@nac3/forge-cli 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Turn steering -- incorporar un mensaje del usuario SIN esperar a que
3
+ * termine el turno (Pablo, 2026-09-29).
4
+ *
5
+ * Hasta ahora, si el usuario escribia con un turno en marcha, el mensaje
6
+ * quedaba encolado EN EL NAVEGADOR (`state.queue` del panel) y el servidor
7
+ * ni se enteraba: recien se mandaba cuando el turno anterior terminaba. Para
8
+ * corregir el rumbo a mitad de trabajo habia que apretar Detener y perder
9
+ * todo lo que venia despues.
10
+ *
11
+ * Este modulo es el buzon del medio. El panel deposita el texto mientras el
12
+ * turno corre; el turno lo retira en su proxima frontera de ronda y lo suma
13
+ * a la conversacion como un mensaje mas del usuario. Las dos rutas lo hacen
14
+ * en el punto que les corresponde:
15
+ *
16
+ * - Console / Anthropic API: `claude.ts` ya arma
17
+ * `messages.push({ role:'user', content: toolResultBlocks })` al cerrar
18
+ * cada ronda. La API acepta bloques tool_result y un bloque de texto en
19
+ * el mismo turno de usuario, asi que alcanza con sumar el bloque.
20
+ * - Plan / `claude -p`: el subproceso es dueno del loop, pero con
21
+ * `--input-format stream-json` acepta mensajes nuevos por stdin
22
+ * mientras trabaja. Verificado contra el binario 2.1.268: los archivos
23
+ * creados antes de la inyeccion salieron como decia el primer mensaje y
24
+ * los de despues como decia el segundo.
25
+ *
26
+ * Deliberadamente BEST-EFFORT: si el modelo estaba por responder sin llamar
27
+ * ninguna herramienta mas, no hay proxima frontera y el mensaje no se
28
+ * alcanza a incorporar. En ese caso el que lo deposito tiene que poder
29
+ * recuperarlo y mandarlo como turno nuevo, que es para lo que existe
30
+ * `reclaimSteers`.
31
+ *
32
+ * Todo el estado es del turno: `turnId` lo ata, y al terminar se limpia. Un
33
+ * mensaje depositado para un turno que ya murio nunca se cuela en el
34
+ * siguiente.
35
+ *
36
+ * Puro salvo por el singleton de modulo, para poder testear la logica sin
37
+ * levantar el server.
38
+ *
39
+ * ASCII-only.
40
+ */
41
+ export interface SteerItem {
42
+ text: string;
43
+ at: number;
44
+ }
45
+ /** Tope defensivo: si alguien pega quince mensajes durante un turno, se
46
+ * quedan los primeros y el resto vuelve por la via normal. Un turno con
47
+ * demasiadas correcciones simultaneas no se entiende ni humanamente. */
48
+ export declare const MAX_PENDING = 6;
49
+ /** Tope de texto por mensaje, para no inflar el turno sin control. */
50
+ export declare const MAX_TEXT = 4000;
51
+ /** Abre el buzon para un turno. Lo llama el server justo despues de
52
+ * `beginTurn`. Descarta cualquier buzon viejo. */
53
+ export declare function openSteering(turnId: string): void;
54
+ /** Cierra el buzon del turno y devuelve lo que quedo SIN entregar, para que
55
+ * el que lo deposito lo pueda mandar como turno nuevo. Si el id no es el
56
+ * del buzon abierto, no toca nada (un turno viejo que termina tarde no
57
+ * puede cerrar el buzon del nuevo). */
58
+ export declare function closeSteering(turnId: string): SteerItem[];
59
+ export interface QueueResult {
60
+ ok: boolean;
61
+ /** Por que no se acepto, cuando ok=false. */
62
+ reason?: 'no-active-turn' | 'empty' | 'full';
63
+ /** Cuantos quedan esperando entrega. */
64
+ pending?: number;
65
+ }
66
+ /** Deposita un mensaje para el turno en curso. */
67
+ export declare function queueSteer(turnId: string | null, text: string, now?: number): QueueResult;
68
+ /** Retira todo lo pendiente del turno. Lo llama el loop en su frontera de
69
+ * ronda. Vacia el buzon: lo retirado ya es parte de la conversacion. */
70
+ export declare function drainSteers(turnId: string): SteerItem[];
71
+ /** Cuantos hay esperando, sin retirarlos. */
72
+ export declare function pendingSteers(turnId: string | null): number;
73
+ /** Cuantos se entregaron en este turno. */
74
+ export declare function deliveredSteers(turnId: string | null): number;
75
+ /** El id del turno cuyo buzon esta abierto, o null. */
76
+ export declare function steeringTurnId(): string | null;
77
+ /**
78
+ * Da forma al texto que ve el modelo. No se inyecta el mensaje pelado: se
79
+ * lo enmarca para que el modelo entienda que lo dijo el humano DURANTE el
80
+ * trabajo y que es una correccion de rumbo, no un pedido nuevo que empieza
81
+ * de cero. Sin el marco, un "mejor en mayusculas" suelto entre resultados
82
+ * de herramienta se lee como ruido.
83
+ */
84
+ export declare function formatSteer(items: SteerItem[]): string;
85
+ /** Test hook. */
86
+ export declare function _resetSteering(): void;
87
+ //# sourceMappingURL=turn_steering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn_steering.d.ts","sourceRoot":"","sources":["../../src/chat/turn_steering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;yEAEyE;AACzE,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,sEAAsE;AACtE,eAAO,MAAM,QAAQ,OAAO,CAAC;AAW7B;mDACmD;AACnD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED;;;wCAGwC;AACxC,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAKzD;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,6CAA6C;IAC7C,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAC;IAC7C,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,WAAW,CAO7F;AAED;yEACyE;AACzE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAMvD;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAG3D;AAED,2CAA2C;AAC3C,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAG7D;AAED,uDAAuD;AACvD,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAOtD;AAED,iBAAiB;AACjB,wBAAgB,cAAc,IAAI,IAAI,CAAiB"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Turn steering -- incorporar un mensaje del usuario SIN esperar a que
3
+ * termine el turno (Pablo, 2026-09-29).
4
+ *
5
+ * Hasta ahora, si el usuario escribia con un turno en marcha, el mensaje
6
+ * quedaba encolado EN EL NAVEGADOR (`state.queue` del panel) y el servidor
7
+ * ni se enteraba: recien se mandaba cuando el turno anterior terminaba. Para
8
+ * corregir el rumbo a mitad de trabajo habia que apretar Detener y perder
9
+ * todo lo que venia despues.
10
+ *
11
+ * Este modulo es el buzon del medio. El panel deposita el texto mientras el
12
+ * turno corre; el turno lo retira en su proxima frontera de ronda y lo suma
13
+ * a la conversacion como un mensaje mas del usuario. Las dos rutas lo hacen
14
+ * en el punto que les corresponde:
15
+ *
16
+ * - Console / Anthropic API: `claude.ts` ya arma
17
+ * `messages.push({ role:'user', content: toolResultBlocks })` al cerrar
18
+ * cada ronda. La API acepta bloques tool_result y un bloque de texto en
19
+ * el mismo turno de usuario, asi que alcanza con sumar el bloque.
20
+ * - Plan / `claude -p`: el subproceso es dueno del loop, pero con
21
+ * `--input-format stream-json` acepta mensajes nuevos por stdin
22
+ * mientras trabaja. Verificado contra el binario 2.1.268: los archivos
23
+ * creados antes de la inyeccion salieron como decia el primer mensaje y
24
+ * los de despues como decia el segundo.
25
+ *
26
+ * Deliberadamente BEST-EFFORT: si el modelo estaba por responder sin llamar
27
+ * ninguna herramienta mas, no hay proxima frontera y el mensaje no se
28
+ * alcanza a incorporar. En ese caso el que lo deposito tiene que poder
29
+ * recuperarlo y mandarlo como turno nuevo, que es para lo que existe
30
+ * `reclaimSteers`.
31
+ *
32
+ * Todo el estado es del turno: `turnId` lo ata, y al terminar se limpia. Un
33
+ * mensaje depositado para un turno que ya murio nunca se cuela en el
34
+ * siguiente.
35
+ *
36
+ * Puro salvo por el singleton de modulo, para poder testear la logica sin
37
+ * levantar el server.
38
+ *
39
+ * ASCII-only.
40
+ */
41
+ /** Tope defensivo: si alguien pega quince mensajes durante un turno, se
42
+ * quedan los primeros y el resto vuelve por la via normal. Un turno con
43
+ * demasiadas correcciones simultaneas no se entiende ni humanamente. */
44
+ export const MAX_PENDING = 6;
45
+ /** Tope de texto por mensaje, para no inflar el turno sin control. */
46
+ export const MAX_TEXT = 4000;
47
+ let _box = null;
48
+ /** Abre el buzon para un turno. Lo llama el server justo despues de
49
+ * `beginTurn`. Descarta cualquier buzon viejo. */
50
+ export function openSteering(turnId) {
51
+ _box = { turnId, items: [], delivered: 0 };
52
+ }
53
+ /** Cierra el buzon del turno y devuelve lo que quedo SIN entregar, para que
54
+ * el que lo deposito lo pueda mandar como turno nuevo. Si el id no es el
55
+ * del buzon abierto, no toca nada (un turno viejo que termina tarde no
56
+ * puede cerrar el buzon del nuevo). */
57
+ export function closeSteering(turnId) {
58
+ if (!_box || _box.turnId !== turnId)
59
+ return [];
60
+ const sobrantes = _box.items;
61
+ _box = null;
62
+ return sobrantes;
63
+ }
64
+ /** Deposita un mensaje para el turno en curso. */
65
+ export function queueSteer(turnId, text, now = Date.now()) {
66
+ const t = String(text ?? '').trim().slice(0, MAX_TEXT);
67
+ if (!t)
68
+ return { ok: false, reason: 'empty' };
69
+ if (!_box || !turnId || _box.turnId !== turnId)
70
+ return { ok: false, reason: 'no-active-turn' };
71
+ if (_box.items.length >= MAX_PENDING)
72
+ return { ok: false, reason: 'full' };
73
+ _box.items.push({ text: t, at: now });
74
+ return { ok: true, pending: _box.items.length };
75
+ }
76
+ /** Retira todo lo pendiente del turno. Lo llama el loop en su frontera de
77
+ * ronda. Vacia el buzon: lo retirado ya es parte de la conversacion. */
78
+ export function drainSteers(turnId) {
79
+ if (!_box || _box.turnId !== turnId)
80
+ return [];
81
+ const out = _box.items;
82
+ _box.items = [];
83
+ _box.delivered += out.length;
84
+ return out;
85
+ }
86
+ /** Cuantos hay esperando, sin retirarlos. */
87
+ export function pendingSteers(turnId) {
88
+ if (!_box || !turnId || _box.turnId !== turnId)
89
+ return 0;
90
+ return _box.items.length;
91
+ }
92
+ /** Cuantos se entregaron en este turno. */
93
+ export function deliveredSteers(turnId) {
94
+ if (!_box || !turnId || _box.turnId !== turnId)
95
+ return 0;
96
+ return _box.delivered;
97
+ }
98
+ /** El id del turno cuyo buzon esta abierto, o null. */
99
+ export function steeringTurnId() {
100
+ return _box ? _box.turnId : null;
101
+ }
102
+ /**
103
+ * Da forma al texto que ve el modelo. No se inyecta el mensaje pelado: se
104
+ * lo enmarca para que el modelo entienda que lo dijo el humano DURANTE el
105
+ * trabajo y que es una correccion de rumbo, no un pedido nuevo que empieza
106
+ * de cero. Sin el marco, un "mejor en mayusculas" suelto entre resultados
107
+ * de herramienta se lee como ruido.
108
+ */
109
+ export function formatSteer(items) {
110
+ if (items.length === 0)
111
+ return '';
112
+ const cuerpo = items.map((i) => i.text).join('\n\n');
113
+ return '[El usuario escribio esto MIENTRAS trabajabas, sin esperar a que '
114
+ + 'termines. Tomalo como correccion de rumbo del trabajo en curso y '
115
+ + 'seguí desde donde estas; no reinicies la tarea salvo que lo pida.]\n\n'
116
+ + cuerpo;
117
+ }
118
+ /** Test hook. */
119
+ export function _resetSteering() { _box = null; }
120
+ //# sourceMappingURL=turn_steering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn_steering.js","sourceRoot":"","sources":["../../src/chat/turn_steering.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -2,5 +2,5 @@
2
2
  * Yujin Forge CLI version. Kept in sync with package.json by the npm
3
3
  * "version" lifecycle (scripts/sync_version.mjs) so the two never drift.
4
4
  */
5
- export declare const VERSION = "1.6.0";
5
+ export declare const VERSION = "1.7.1";
6
6
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  * Yujin Forge CLI version. Kept in sync with package.json by the npm
3
3
  * "version" lifecycle (scripts/sync_version.mjs) so the two never drift.
4
4
  */
5
- export const VERSION = '1.6.0';
5
+ export const VERSION = '1.7.1';
6
6
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,117 +1,115 @@
1
- {
2
- "name": "@nac3/forge-cli",
3
- "version": "1.6.0",
4
- "description": "Yujin Forge -- voice-first NAC-3 React development framework. CLI + chat panel + spec ingest + 10-format document reader + voice loop.",
5
- "license": "SEE LICENSE IN LICENSE",
6
- "author": "Pablo Kuschnirof <pablo@rpaforce.com>",
7
- "homepage": "https://github.com/yujinapp/yujin-forge#readme",
8
- "bugs": {
9
- "url": "https://github.com/yujinapp/yujin-forge/issues"
10
- },
11
- "bin": {
12
- "yf": "./dist/bin/yf.js"
13
- },
14
- "type": "module",
15
- "exports": {
16
- ".": "./dist/index.js"
17
- },
18
- "scripts": {
19
- "version": "node scripts/sync_version.mjs && git add src/version.ts",
20
- "build": "tsc && node scripts/copy_qa_assets.mjs",
21
- "dev": "tsx watch src/bin/yf.ts",
22
- "typecheck": "tsc --noEmit",
23
- "typecheck:all": "pnpm typecheck && pnpm --filter @yujin/hito4-server typecheck && pnpm --filter @yujin/forge-mobile typecheck",
24
- "test": "vitest run",
25
- "test:watch": "vitest",
26
- "lint": "tsc --noEmit",
27
- "yf": "tsx src/bin/yf.ts",
28
- "catalog:lint": "node scripts/catalog_lint.mjs",
29
- "pairing:check": "node scripts/check_pairing_sync.mjs",
30
- "brain-matrix:gate": "node scripts/brain_matrix_gate.mjs",
31
- "hito4:lint": "node scripts/hito4_lint_bundle.mjs",
32
- "publish-alpha-emergency": "node scripts/publish_alpha_emergency.mjs",
33
- "prepublishOnly": "pnpm run build && pnpm run typecheck && pnpm run catalog:lint && pnpm run test && pnpm run brain-matrix:gate && pnpm run hito4:lint"
34
- },
35
- "dependencies": {
36
- "@anthropic-ai/sdk": "^0.32.1",
37
- "@aws-sdk/client-ecs": "^3.1053.0",
38
- "@noble/ciphers": "^2.2.0",
39
- "@noble/hashes": "^2.2.0",
40
- "@scure/base": "^2.2.0",
41
- "better-sqlite3": "^12.10.0",
42
- "commander": "12.1.0",
43
- "cross-spawn": "^7.0.6",
44
- "dotenv": "^17.4.2",
45
- "kleur": "4.1.5",
46
- "mammoth": "^1.8.0",
47
- "node-html-parser": "6.1.13",
48
- "pdfjs-dist": "^4.10.38",
49
- "prompts": "2.4.2",
50
- "qrcode": "^1.5.4",
51
- "typescript": "5.7.2",
52
- "xlsx": "npm:@e965/xlsx@0.20.3"
53
- },
54
- "devDependencies": {
55
- "@playwright/test": "^1.60.0",
56
- "@types/better-sqlite3": "^7.6.13",
57
- "@types/cross-spawn": "^6.0.6",
58
- "@types/node": "22.10.2",
59
- "@types/prompts": "2.4.9",
60
- "@types/qrcode": "^1.5.6",
61
- "acorn": "^8.16.0",
62
- "pixelmatch": "^7.2.0",
63
- "pngjs": "^7.0.0",
64
- "tsx": "4.19.2",
65
- "vitest": "2.1.8"
66
- },
67
- "engines": {
68
- "node": ">=18.17",
69
- "pnpm": ">=9"
70
- },
71
- "packageManager": "pnpm@11.3.0",
72
- "files": [
73
- "dist",
74
- "templates",
75
- "docs/manuals",
76
- "docs/SQ.md",
77
- "docs/SQ_BY_STEP.yaml",
78
- "docs/doctrine",
79
- "src/i18n/catalogs",
80
- "yujin-design-system",
81
- "yujin-design-system-artdeco",
82
- "yujin-design-system-moderna",
83
- "yujin-design-system-marina",
84
- "README.md",
85
- "LICENSE"
86
- ],
87
- "keywords": [
88
- "nac",
89
- "nac-3",
90
- "react",
91
- "framework",
92
- "claude-code",
93
- "yujin",
94
- "voice",
95
- "whisper",
96
- "elevenlabs",
97
- "tts",
98
- "stt",
99
- "document-reader",
100
- "pdf",
101
- "docx",
102
- "epub",
103
- "spec-ingest",
104
- "scaffolding",
105
- "accessibility",
106
- "voice-first",
107
- "adip"
108
- ],
109
- "repository": {
110
- "type": "git",
111
- "url": "git+https://github.com/yujinapp/yujin-forge.git"
112
- },
113
- "publishConfig": {
114
- "access": "public",
115
- "tag": "latest"
116
- }
117
- }
1
+ {
2
+ "name": "@nac3/forge-cli",
3
+ "version": "1.7.1",
4
+ "description": "Yujin Forge -- voice-first NAC-3 React development framework. CLI + chat panel + spec ingest + 10-format document reader + voice loop.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "author": "Pablo Kuschnirof <pablo@rpaforce.com>",
7
+ "homepage": "https://github.com/yujinapp/yujin-forge#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/yujinapp/yujin-forge/issues"
10
+ },
11
+ "bin": {
12
+ "yf": "./dist/bin/yf.js"
13
+ },
14
+ "type": "module",
15
+ "exports": {
16
+ ".": "./dist/index.js"
17
+ },
18
+ "dependencies": {
19
+ "@anthropic-ai/sdk": "^0.32.1",
20
+ "@aws-sdk/client-ecs": "^3.1053.0",
21
+ "@noble/ciphers": "^2.2.0",
22
+ "@noble/hashes": "^2.2.0",
23
+ "@scure/base": "^2.2.0",
24
+ "better-sqlite3": "^12.10.0",
25
+ "commander": "12.1.0",
26
+ "cross-spawn": "^7.0.6",
27
+ "dotenv": "^17.4.2",
28
+ "kleur": "4.1.5",
29
+ "mammoth": "^1.8.0",
30
+ "node-html-parser": "6.1.13",
31
+ "pdfjs-dist": "^4.10.38",
32
+ "prompts": "2.4.2",
33
+ "qrcode": "^1.5.4",
34
+ "typescript": "5.7.2",
35
+ "xlsx": "npm:@e965/xlsx@0.20.3"
36
+ },
37
+ "devDependencies": {
38
+ "@playwright/test": "^1.60.0",
39
+ "@types/better-sqlite3": "^7.6.13",
40
+ "@types/cross-spawn": "^6.0.6",
41
+ "@types/node": "22.10.2",
42
+ "@types/prompts": "2.4.9",
43
+ "@types/qrcode": "^1.5.6",
44
+ "acorn": "^8.16.0",
45
+ "pixelmatch": "^7.2.0",
46
+ "pngjs": "^7.0.0",
47
+ "tsx": "4.19.2",
48
+ "vitest": "2.1.8"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.17",
52
+ "pnpm": ">=9"
53
+ },
54
+ "files": [
55
+ "dist",
56
+ "templates",
57
+ "docs/manuals",
58
+ "docs/SQ.md",
59
+ "docs/SQ_BY_STEP.yaml",
60
+ "docs/doctrine",
61
+ "src/i18n/catalogs",
62
+ "yujin-design-system",
63
+ "yujin-design-system-artdeco",
64
+ "yujin-design-system-moderna",
65
+ "yujin-design-system-marina",
66
+ "README.md",
67
+ "LICENSE"
68
+ ],
69
+ "keywords": [
70
+ "nac",
71
+ "nac-3",
72
+ "react",
73
+ "framework",
74
+ "claude-code",
75
+ "yujin",
76
+ "voice",
77
+ "whisper",
78
+ "elevenlabs",
79
+ "tts",
80
+ "stt",
81
+ "document-reader",
82
+ "pdf",
83
+ "docx",
84
+ "epub",
85
+ "spec-ingest",
86
+ "scaffolding",
87
+ "accessibility",
88
+ "voice-first",
89
+ "adip"
90
+ ],
91
+ "repository": {
92
+ "type": "git",
93
+ "url": "git+https://github.com/yujinapp/yujin-forge.git"
94
+ },
95
+ "publishConfig": {
96
+ "access": "public",
97
+ "tag": "latest"
98
+ },
99
+ "scripts": {
100
+ "version": "node scripts/sync_version.mjs && git add src/version.ts",
101
+ "build": "tsc && node scripts/copy_qa_assets.mjs",
102
+ "dev": "tsx watch src/bin/yf.ts",
103
+ "typecheck": "tsc --noEmit",
104
+ "typecheck:all": "pnpm typecheck && pnpm --filter @yujin/hito4-server typecheck && pnpm --filter @yujin/forge-mobile typecheck",
105
+ "test": "vitest run",
106
+ "test:watch": "vitest",
107
+ "lint": "tsc --noEmit",
108
+ "yf": "tsx src/bin/yf.ts",
109
+ "catalog:lint": "node scripts/catalog_lint.mjs",
110
+ "pairing:check": "node scripts/check_pairing_sync.mjs",
111
+ "brain-matrix:gate": "node scripts/brain_matrix_gate.mjs",
112
+ "hito4:lint": "node scripts/hito4_lint_bundle.mjs",
113
+ "publish-alpha-emergency": "node scripts/publish_alpha_emergency.mjs"
114
+ }
115
+ }