One .zip per skill. Inside: SKILL.md with the intent and the frontmatter, and the code that runs in the page.
Settings → Personalize → Skills. This is Cowork's own skill library, shared across every chat.
Click Add and choose the file you just downloaded. Cowork installs it once; no CLAUDE.md, no per-project setup.
The skill stays available in every session. Cowork runs it in your own Chrome, riding the session you are already signed into.
Fill ARCA's RCEL invoice wizard (Factura C and the other C comprobantes) end to end and stop at the summary screen. It never presses Confirmar, so it never generates a CAE.
{"tipoComprobante":"string optional, default \"Factura C\". Also \"Nota de Credito C\", \"Nota de Debito C\", \"Recibo C\"","puntoVenta":"int|string optional, e.g. 1 or \"00001\". Auto-selected when the CUIT has exactly one","fecha":"DD/MM/AAAA optional, defaults to the date RCEL prefills (today)","concepto":"\"productos\" | \"servicios\" | \"ambos\" (default \"servicios\")","periodoDesde":"DD/MM/AAAA, REQUIRED when concepto is servicios or ambos","periodoHasta":"DD/MM/AAAA, same","vtoPago":"DD/MM/AAAA, same","actividad":"string optional, e.g. \"620200\". Only needed when the CUIT has several","receptor":"{condicionIva:\"Consumidor Final\"|..., docTipo:\"DNI\"|\"CUIT\"|..., docNro:string, razonSocial, domicilio, email}","formasDePago":"array of strings optional, e.g. [\"Transferencia Bancaria\"]. Defaults to whatever RCEL prechecks","items":"REQUIRED array of {descripcion, cantidad, unidadMedida, precioUnitario, bonificacion}","referencia":"string optional, Referencia Comercial"}{"step":"string: empresa|menu|ptovta|emision|receptor|operacion|resumen","done":"bool, true ONLY on the summary page","wait":"bool, true means call again","stop":"\"MANUAL_CONFIRM\" on the final call","resumen":"{razonSocial,puntoDeVenta,concepto,periodoFacturado,vtoPago,condicionIva,condicionesVenta,importeTotal}","texto":"string, the full summary screen as plain text","error":"string|null","alerts":"array of RCEL validation messages when error is \"validacion\""}Drives ARCA's RCEL (Regimen de Comprobantes en Linea) wizard at fe.afip.gob.ar/rcel/, filling the four steps from the params and stopping on the Resumen de Datos screen.
IT DOES NOT ISSUE THE COMPROBANTE. The extractor has a hard block on the confirm control (
#btngenerar,onclick="observarOConfirmar()") and refuses to click it or anything wired to that handler. Pressing Confirmar generates a CAE, which is a legal fiscal document: it cannot be undone, edited or deleted, only offset with a Nota de Credito. That press belongs to a human looking at the screen. Report the returnedresumento the user and tell them to click "Confirmar Datos..." themselves.
Unlike the other skills, this one is not a single call. RCEL is a 2008-era JSP app: each step is a full page POST and each navigation destroys the JS context, so the extractor runs once per screen. That is why it lives in localStorage, which survives the navigations.
The extractor is fully synchronous, no await anywhere. Every wait (the AJAX that fills the comprobante types, each page load) is expressed as {wait: true} and resolved by calling again.
ARCA (ex AFIP) keeps its service URLs on
afip.gob.ar. The catalog domain reads ARCA but the tab must be onfe.afip.gob.ar.
Default action: just call (step 1), in a loop. The call reports NEEDS_STORE when the extractor is missing or out of date; only then store (step 2) and resume the loop.
Versioning. Gated by VER (must match version in the frontmatter). When evaluate.js changes, bump both.
done or error)Navigate to https://fe.afip.gob.ar/rcel/jsp/menu_ppal.jsp first, then run this same snippet repeatedly with the same params every time:
await (() => {
const KEY = "arca-prepare-invoice", VER = "1";
const s = localStorage.getItem(KEY);
if (!s || localStorage.getItem(KEY + ":ver") !== VER) return 'NEEDS_STORE';
const fn = eval('(' + s + ')');
return fn(document, {
tipoComprobante: "Factura C",
concepto: "servicios",
periodoDesde: "24/07/2026", periodoHasta: "24/07/2026", vtoPago: "24/07/2026",
receptor: { condicionIva: "Consumidor Final" },
formasDePago: ["Transferencia Bancaria"],
items: [{ descripcion: "Servicios de consultoria en informatica",
cantidad: 1, unidadMedida: "unidades", precioUnitario: 15000 }]
});
})()
Run this snippet verbatim. Never add an await before the eval. Only the outer await is allowed.
The loop. Each call advances one screen and returns {step, done: false, wait: true}. Wait for the page to settle, then call again with identical params. Stop when:
done: true -> you are on the Resumen screen. Finish here. Read resumen back to the user and hand off the Confirmar click.error present -> stop and report it. Do not retry blindly.done -> stop, something is wrong.The extractor is idempotent per screen: it decides what to do from location.pathname, so a repeated call on an unchanged page is harmless.
Typical run for one item: empresa (only if RCEL asks) -> menu -> ptovta (twice, the second waits on the ajax) -> emision -> receptor -> operacion -> resumen. Six to eight calls.
NEEDS_STORE)From a fe.afip.gob.ar tab, evaluate the following with the bridge, replacing <FN> with the contents of evaluate.js (the ( (root, params) => { ... } ) function). Keep VER equal to the call snippet's:
(() => {
const KEY = "arca-prepare-invoice", VER = "1";
const fn = (<FN>);
localStorage.setItem(KEY, String(fn));
localStorage.setItem(KEY + ":ver", VER);
return 'stored';
})()
Returns "stored". Then go back to step 1.
Everything is matched by the visible option text, case and accent insensitive, so write params the way they read on screen. Numeric codes also work as a fallback.
tipoComprobante: Factura C (2), Nota de Debito C (3), Nota de Credito C (4), Recibo C (5), plus the FCE MiPyMEs variants (120/121/122). (These are the EMISSION codes, different from the consulta in arca-issued-invoices where Factura C is 11.)
receptor.condicionIva: Consumidor Final (5), IVA Responsable Inscripto (1), Responsable Monotributo (6), IVA Sujeto Exento (4), Sujeto No Categorizado (7), Cliente del Exterior (9), Proveedor del Exterior (8).
receptor.docTipo: CUIT (80), CUIL (86), CDI (87), DNI (96), Pasaporte (94), LE (89), LC (90).
formasDePago (checkboxes, several at once): Contado (1), Cuenta Corriente (96), Cheque (97), Transferencia Bancaria (91), Otros medios de pago electronico (90, RCEL default), Otra (99). Omit the param to keep RCEL's default.
items[].unidadMedida: unidades is the normal one for services. Also kilogramos, metros, litros, docenas, packs, otras unidades and the rest of the RCEL list.
Same-origin only. RCEL has no CORS and authenticates with JSESSIONID, so the tab must be on fe.afip.gob.ar.
Latin-1. RCEL is served as iso-8859-1, so the browser encodes the form body in Latin-1. The extractor folds curly quotes, long dashes and ellipses to ASCII and strips anything outside Latin-1. Accents and the letter n-tilde are fine. Do not put long dashes or emoji in descripcion.
Selects are matched by text, not value. RCEL reuses option values; the extractor sets selectedIndex, which is what the page's own handlers read.
Totals are computed by the page. After filling a row the extractor calls RCEL's own calcularSubtotalDetalle(n) and verifies #imptotal is greater than zero before submitting.
Validation alerts are captured, not swallowed. validarCampos() reports problems with alert(), which would hang the bridge. The extractor temporarily replaces alert/confirm, collects the messages and returns them as {error: "validacion", alerts: [...]}.
Not supported: foreign currency, credit/debit card payment, comprobantes asociados, and the RG 3749 / RG 4004 datos adicionales. Those must be loaded by hand.
NEEDS_STORE -> extractor missing or version-mismatched; run step 2 once, then retry.session_expired -> ARCA bounced you to login; log in again and restart the loop from the menu.wrong_site -> the tab is not on fe.afip.gob.ar/rcel/; navigate to menu_ppal.jsp.validacion -> RCEL rejected the screen. Read alerts, fix the params, restart from the menu.punto_de_venta_no_encontrado / tipo_comprobante_no_encontrado / condicion_iva_no_encontrada / forma_de_pago_no_encontrada / unidad_de_medida_no_encontrada -> the return includes disponibles with the exact option texts; pick one from there.faltan_fechas_de_servicio -> concepto is servicios or ambos, so periodoDesde, periodoHasta and vtoPago are mandatory.refused_to_click_confirm -> something asked the extractor to press Confirmar. It will not. By design.{ "type": "json", "jsonPath": "done" }