@nac3/forge-cli 0.2.0-alpha.2 → 0.2.0-alpha.21
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.
- package/dist/bin/yf.d.ts.map +1 -1
- package/dist/bin/yf.js +25 -0
- package/dist/bin/yf.js.map +1 -1
- package/dist/chat/claude.d.ts +22 -15
- package/dist/chat/claude.d.ts.map +1 -1
- package/dist/chat/claude.js +75 -22
- package/dist/chat/claude.js.map +1 -1
- package/dist/chat/panel.d.ts.map +1 -1
- package/dist/chat/panel.js +102 -3
- package/dist/chat/panel.js.map +1 -1
- package/dist/chat/server.js +498 -32
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/tools/audit_consumers.d.ts +66 -0
- package/dist/chat/tools/audit_consumers.d.ts.map +1 -0
- package/dist/chat/tools/audit_consumers.js +231 -0
- package/dist/chat/tools/audit_consumers.js.map +1 -0
- package/dist/chat/tools/git.js +4 -4
- package/dist/chat/tools/github.js +3 -3
- package/dist/chat/tools/lifecycle.js +3 -3
- package/dist/chat/tools/manual.js +1 -1
- package/dist/chat/tools/reader.js +8 -8
- package/dist/chat/tools/workflow.d.ts +45 -0
- package/dist/chat/tools/workflow.d.ts.map +1 -0
- package/dist/chat/tools/workflow.js +404 -0
- package/dist/chat/tools/workflow.js.map +1 -0
- package/dist/chat/tools.d.ts.map +1 -1
- package/dist/chat/tools.js +23 -4
- package/dist/chat/tools.js.map +1 -1
- package/dist/commands/approve.d.ts +32 -0
- package/dist/commands/approve.d.ts.map +1 -0
- package/dist/commands/approve.js +198 -0
- package/dist/commands/approve.js.map +1 -0
- package/dist/commands/block.d.ts +28 -0
- package/dist/commands/block.d.ts.map +1 -0
- package/dist/commands/block.js +189 -0
- package/dist/commands/block.js.map +1 -0
- package/dist/commands/bootstrap.d.ts +35 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/bootstrap.js +205 -0
- package/dist/commands/bootstrap.js.map +1 -0
- package/dist/commands/chat.d.ts +3 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +46 -1
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/clarify.d.ts +30 -0
- package/dist/commands/clarify.d.ts.map +1 -0
- package/dist/commands/clarify.js +671 -0
- package/dist/commands/clarify.js.map +1 -0
- package/dist/commands/discover.d.ts +30 -0
- package/dist/commands/discover.d.ts.map +1 -0
- package/dist/commands/discover.js +178 -0
- package/dist/commands/discover.js.map +1 -0
- package/dist/commands/doctor.js +94 -42
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/keys_setup.d.ts +53 -0
- package/dist/commands/keys_setup.d.ts.map +1 -0
- package/dist/commands/keys_setup.js +487 -0
- package/dist/commands/keys_setup.js.map +1 -0
- package/dist/commands/legacy-audit.d.ts +34 -0
- package/dist/commands/legacy-audit.d.ts.map +1 -0
- package/dist/commands/legacy-audit.js +270 -0
- package/dist/commands/legacy-audit.js.map +1 -0
- package/dist/commands/license.d.ts.map +1 -1
- package/dist/commands/license.js +41 -0
- package/dist/commands/license.js.map +1 -1
- package/dist/commands/operate.d.ts +22 -0
- package/dist/commands/operate.d.ts.map +1 -0
- package/dist/commands/operate.js +483 -0
- package/dist/commands/operate.js.map +1 -0
- package/dist/commands/spec.d.ts +38 -0
- package/dist/commands/spec.d.ts.map +1 -0
- package/dist/commands/spec.js +256 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/triage.d.ts +34 -0
- package/dist/commands/triage.d.ts.map +1 -0
- package/dist/commands/triage.js +228 -0
- package/dist/commands/triage.js.map +1 -0
- package/dist/commands/vault-inventory.d.ts +30 -0
- package/dist/commands/vault-inventory.d.ts.map +1 -0
- package/dist/commands/vault-inventory.js +214 -0
- package/dist/commands/vault-inventory.js.map +1 -0
- package/dist/commands/vault.d.ts.map +1 -1
- package/dist/commands/vault.js +5 -0
- package/dist/commands/vault.js.map +1 -1
- package/dist/commands/voice.js +1 -1
- package/dist/commands/voice.js.map +1 -1
- package/dist/commands/workflow-coverage.d.ts +30 -0
- package/dist/commands/workflow-coverage.d.ts.map +1 -0
- package/dist/commands/workflow-coverage.js +138 -0
- package/dist/commands/workflow-coverage.js.map +1 -0
- package/dist/core/keys_envelope.d.ts +13 -0
- package/dist/core/keys_envelope.d.ts.map +1 -1
- package/dist/core/keys_envelope.js.map +1 -1
- package/dist/deploy/adapter.d.ts +93 -0
- package/dist/deploy/adapter.d.ts.map +1 -0
- package/dist/deploy/adapter.js +42 -0
- package/dist/deploy/adapter.js.map +1 -0
- package/dist/deploy/aws_adapter.d.ts +28 -0
- package/dist/deploy/aws_adapter.d.ts.map +1 -0
- package/dist/deploy/aws_adapter.js +65 -0
- package/dist/deploy/aws_adapter.js.map +1 -0
- package/dist/deploy/cloudflare.d.ts +24 -0
- package/dist/deploy/cloudflare.d.ts.map +1 -0
- package/dist/deploy/cloudflare.js +169 -0
- package/dist/deploy/cloudflare.js.map +1 -0
- package/dist/license/hito4_client.d.ts +17 -1
- package/dist/license/hito4_client.d.ts.map +1 -1
- package/dist/license/hito4_client.js +71 -10
- package/dist/license/hito4_client.js.map +1 -1
- package/dist/license/index.d.ts.map +1 -1
- package/dist/license/index.js +7 -0
- package/dist/license/index.js.map +1 -1
- package/dist/license/sync.d.ts +54 -0
- package/dist/license/sync.d.ts.map +1 -0
- package/dist/license/sync.js +131 -0
- package/dist/license/sync.js.map +1 -0
- package/dist/telemetry/usage.d.ts +67 -0
- package/dist/telemetry/usage.d.ts.map +1 -0
- package/dist/telemetry/usage.js +208 -0
- package/dist/telemetry/usage.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/voice/intents.d.ts +1 -1
- package/dist/voice/intents.js +0 -0
- package/dist/voice/providers/google.d.ts.map +1 -1
- package/dist/voice/providers/google.js +157 -27
- package/dist/voice/providers/google.js.map +1 -1
- package/dist/voice/router.d.ts +10 -0
- package/dist/voice/router.d.ts.map +1 -1
- package/dist/voice/router.js +39 -20
- package/dist/voice/router.js.map +1 -1
- package/dist/voice/types.d.ts +5 -2
- package/dist/voice/types.d.ts.map +1 -1
- package/dist/voice/types.js.map +1 -1
- package/dist/workflow/state.d.ts +190 -0
- package/dist/workflow/state.d.ts.map +1 -0
- package/dist/workflow/state.js +119 -0
- package/dist/workflow/state.js.map +1 -0
- package/package.json +13 -15
- package/templates/nextjs-app/README.md +48 -0
- package/templates/nextjs-app/next.config.js +8 -0
- package/templates/nextjs-app/package.json +33 -0
- package/templates/nextjs-app/src/app/globals.css +43 -0
- package/templates/nextjs-app/src/app/layout.tsx +29 -0
- package/templates/nextjs-app/src/app/page.tsx +63 -0
- package/templates/nextjs-app/src/nac/manifest.ts +36 -0
- package/templates/nextjs-app/tsconfig.json +21 -0
- package/templates/nextjs-app/yujin.forge.json +11 -0
package/dist/chat/server.js
CHANGED
|
@@ -248,6 +248,28 @@ async function route(req, res, ctx) {
|
|
|
248
248
|
await handleForgeToolDispatch(req, res, ctx);
|
|
249
249
|
return;
|
|
250
250
|
}
|
|
251
|
+
/* Open-in-editor escape hatch (Layer A.2). The panel surfaces
|
|
252
|
+
file paths as clickable; click triggers this endpoint, which
|
|
253
|
+
spawns the user's default editor (code / cursor / subl /
|
|
254
|
+
idea, in order). Best-effort -- on failure returns the
|
|
255
|
+
resolved path so the panel can copy-to-clipboard fallback. */
|
|
256
|
+
if (req.method === 'POST' && url.pathname === '/api/forge/open-in-editor') {
|
|
257
|
+
await handleOpenInEditor(req, res, ctx);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
/* Trust gradient tracking (Layer A.2). Panel increments
|
|
261
|
+
counters when the user expands a diff / opens a file in
|
|
262
|
+
their editor / overrides Forge. Forge reads them at session
|
|
263
|
+
boot and decides whether to surface or hide the detail
|
|
264
|
+
links. */
|
|
265
|
+
if (req.method === 'POST' && url.pathname === '/api/forge/trust-event') {
|
|
266
|
+
await handleTrustEvent(req, res, ctx);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (req.method === 'GET' && url.pathname === '/api/forge/trust-state') {
|
|
270
|
+
await handleTrustState(req, res, ctx);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
251
273
|
/* Spec doc ingest (V1.37 + V1.38 -- bloque 4.5 scaffolding).
|
|
252
274
|
Client uploads a spec file (PDF / DOCX / HTML / md / ...)
|
|
253
275
|
and the server parses it via the existing reader pipeline,
|
|
@@ -466,17 +488,17 @@ async function handleVoiceStt(req, res, ctx) {
|
|
|
466
488
|
* runs.
|
|
467
489
|
*/
|
|
468
490
|
const FORGE_TOOL_DIRECT_ALLOWLIST = new Set([
|
|
469
|
-
'
|
|
470
|
-
'
|
|
471
|
-
'
|
|
472
|
-
'
|
|
473
|
-
'
|
|
474
|
-
'
|
|
475
|
-
'
|
|
476
|
-
'
|
|
491
|
+
'forge_reader_open',
|
|
492
|
+
'forge_reader_list_documents',
|
|
493
|
+
'forge_reader_read_section',
|
|
494
|
+
'forge_reader_next_block',
|
|
495
|
+
'forge_reader_search',
|
|
496
|
+
'forge_reader_bookmark_set',
|
|
497
|
+
'forge_reader_bookmark_jump',
|
|
498
|
+
'forge_reader_recap',
|
|
477
499
|
/* Fase F.8 -- HTML user manuals in 10 languages. Read-only,
|
|
478
500
|
same safety profile as the reader tools. */
|
|
479
|
-
'
|
|
501
|
+
'forge_manual_open',
|
|
480
502
|
]);
|
|
481
503
|
async function handleForgeToolDispatch(req, res, ctx) {
|
|
482
504
|
let body;
|
|
@@ -948,6 +970,178 @@ async function handleVoiceTts(req, res, ctx) {
|
|
|
948
970
|
});
|
|
949
971
|
}
|
|
950
972
|
}
|
|
973
|
+
/* ============================================================
|
|
974
|
+
* Layer A.2 -- minority surface (open in editor + trust gradient)
|
|
975
|
+
* ============================================================ */
|
|
976
|
+
/** Check that `target` is inside `root` (after resolving both).
|
|
977
|
+
* Refuses path traversal like ../../../etc/passwd. */
|
|
978
|
+
function isInside(target, root) {
|
|
979
|
+
const targetAbs = path.resolve(target);
|
|
980
|
+
const rootAbs = path.resolve(root) + path.sep;
|
|
981
|
+
return targetAbs === path.resolve(root) || targetAbs.startsWith(rootAbs);
|
|
982
|
+
}
|
|
983
|
+
/** Editor commands tried in order. First match wins. The args
|
|
984
|
+
* templates are joined with the file path at runtime. All
|
|
985
|
+
* commands run with stdio ignored + detached so the panel does
|
|
986
|
+
* not block on the editor's window lifetime. */
|
|
987
|
+
const EDITOR_CANDIDATES = [
|
|
988
|
+
{ cmd: 'code', args: (f) => [f] }, // VS Code
|
|
989
|
+
{ cmd: 'cursor', args: (f) => [f] }, // Cursor
|
|
990
|
+
{ cmd: 'subl', args: (f) => [f] }, // Sublime Text
|
|
991
|
+
{ cmd: 'idea', args: (f) => [f] }, // IntelliJ family
|
|
992
|
+
{ cmd: 'webstorm', args: (f) => [f] },
|
|
993
|
+
{ cmd: 'nano', args: (f) => [f] },
|
|
994
|
+
{ cmd: 'vim', args: (f) => [f] },
|
|
995
|
+
];
|
|
996
|
+
async function handleOpenInEditor(req, res, ctx) {
|
|
997
|
+
let body;
|
|
998
|
+
try {
|
|
999
|
+
body = JSON.parse(await readBody(req));
|
|
1000
|
+
}
|
|
1001
|
+
catch {
|
|
1002
|
+
sendJson(res, 400, { ok: false, error: 'invalid JSON body' });
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
if (typeof body.path !== 'string' || body.path.trim() === '') {
|
|
1006
|
+
sendJson(res, 400, { ok: false, error: 'path (non-empty string) required' });
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
/* Resolve to absolute under the project root. Refuse paths
|
|
1010
|
+
* that escape the project tree (no user opening /etc/passwd
|
|
1011
|
+
* through the chat). */
|
|
1012
|
+
const requestedRel = body.path.trim().replace(/^[/\\]+/, '');
|
|
1013
|
+
const absolute = path.join(ctx.projectRoot, requestedRel);
|
|
1014
|
+
if (!isInside(absolute, ctx.projectRoot)) {
|
|
1015
|
+
sendJson(res, 400, { ok: false, error: 'path escapes project root' });
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
/* Walk the candidates, spawn the first one that does not
|
|
1019
|
+
* throw ENOENT. */
|
|
1020
|
+
const { spawn } = await import('node:child_process');
|
|
1021
|
+
for (const candidate of EDITOR_CANDIDATES) {
|
|
1022
|
+
try {
|
|
1023
|
+
const cmd = process.platform === 'win32' ? `${candidate.cmd}.cmd` : candidate.cmd;
|
|
1024
|
+
const child = spawn(cmd, candidate.args(absolute), {
|
|
1025
|
+
stdio: 'ignore',
|
|
1026
|
+
detached: true,
|
|
1027
|
+
shell: false,
|
|
1028
|
+
});
|
|
1029
|
+
child.on('error', () => { });
|
|
1030
|
+
child.unref();
|
|
1031
|
+
/* If spawn returned, the binary is at least present.
|
|
1032
|
+
* We do not wait for editor to launch (it might be slow
|
|
1033
|
+
* or might be a background daemon). Report success. */
|
|
1034
|
+
sendJson(res, 200, {
|
|
1035
|
+
ok: true,
|
|
1036
|
+
editor: candidate.cmd,
|
|
1037
|
+
path: absolute,
|
|
1038
|
+
});
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
catch { /* ENOENT etc -- try next candidate */ }
|
|
1042
|
+
}
|
|
1043
|
+
/* No editor found. Return the absolute path so the panel can
|
|
1044
|
+
* offer copy-to-clipboard fallback. */
|
|
1045
|
+
sendJson(res, 200, {
|
|
1046
|
+
ok: false,
|
|
1047
|
+
error: 'no supported editor found in PATH',
|
|
1048
|
+
path: absolute,
|
|
1049
|
+
candidates: EDITOR_CANDIDATES.map((c) => c.cmd),
|
|
1050
|
+
hint: 'Install VS Code (code), Cursor, Sublime (subl), or IntelliJ; or copy the path manually.',
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
function trustMetricsPath() {
|
|
1054
|
+
return path.join(configDir(), 'trust_metrics.json');
|
|
1055
|
+
}
|
|
1056
|
+
async function loadTrustMetrics() {
|
|
1057
|
+
const fs = await import('node:fs');
|
|
1058
|
+
try {
|
|
1059
|
+
const raw = await fs.promises.readFile(trustMetricsPath(), 'utf-8');
|
|
1060
|
+
const parsed = JSON.parse(raw);
|
|
1061
|
+
if (parsed && typeof parsed === 'object') {
|
|
1062
|
+
return {
|
|
1063
|
+
v: 1,
|
|
1064
|
+
diff_expansions: Number(parsed.diff_expansions) || 0,
|
|
1065
|
+
editor_opens: Number(parsed.editor_opens) || 0,
|
|
1066
|
+
forge_overrides: Number(parsed.forge_overrides) || 0,
|
|
1067
|
+
last_event_at: typeof parsed.last_event_at === 'string' ? parsed.last_event_at : null,
|
|
1068
|
+
last_proposal_at: typeof parsed.last_proposal_at === 'string' ? parsed.last_proposal_at : null,
|
|
1069
|
+
user_choice: ['show', 'hide', 'default'].includes(parsed.user_choice) ? parsed.user_choice : 'default',
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
catch { /* missing or bad -- return defaults */ }
|
|
1074
|
+
return {
|
|
1075
|
+
v: 1,
|
|
1076
|
+
diff_expansions: 0,
|
|
1077
|
+
editor_opens: 0,
|
|
1078
|
+
forge_overrides: 0,
|
|
1079
|
+
last_event_at: null,
|
|
1080
|
+
last_proposal_at: null,
|
|
1081
|
+
user_choice: 'default',
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
async function saveTrustMetrics(m) {
|
|
1085
|
+
const fs = await import('node:fs');
|
|
1086
|
+
await fs.promises.mkdir(configDir(), { recursive: true });
|
|
1087
|
+
await fs.promises.writeFile(trustMetricsPath(), JSON.stringify(m, null, 2), 'utf-8');
|
|
1088
|
+
}
|
|
1089
|
+
async function handleTrustEvent(req, res, _ctx) {
|
|
1090
|
+
let body;
|
|
1091
|
+
try {
|
|
1092
|
+
body = JSON.parse(await readBody(req));
|
|
1093
|
+
}
|
|
1094
|
+
catch {
|
|
1095
|
+
sendJson(res, 400, { ok: false, error: 'invalid JSON body' });
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
const kind = typeof body.kind === 'string' ? body.kind : '';
|
|
1099
|
+
const m = await loadTrustMetrics();
|
|
1100
|
+
const now = new Date().toISOString();
|
|
1101
|
+
switch (kind) {
|
|
1102
|
+
case 'diff_expansion':
|
|
1103
|
+
m.diff_expansions += 1;
|
|
1104
|
+
break;
|
|
1105
|
+
case 'editor_open':
|
|
1106
|
+
m.editor_opens += 1;
|
|
1107
|
+
break;
|
|
1108
|
+
case 'forge_override':
|
|
1109
|
+
m.forge_overrides += 1;
|
|
1110
|
+
break;
|
|
1111
|
+
case 'user_chose_show':
|
|
1112
|
+
m.user_choice = 'show';
|
|
1113
|
+
break;
|
|
1114
|
+
case 'user_chose_hide':
|
|
1115
|
+
m.user_choice = 'hide';
|
|
1116
|
+
break;
|
|
1117
|
+
default:
|
|
1118
|
+
sendJson(res, 400, { ok: false, error: 'unknown kind: ' + kind });
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
m.last_event_at = now;
|
|
1122
|
+
await saveTrustMetrics(m);
|
|
1123
|
+
sendJson(res, 200, { ok: true, metrics: m });
|
|
1124
|
+
}
|
|
1125
|
+
async function handleTrustState(_req, res, _ctx) {
|
|
1126
|
+
const m = await loadTrustMetrics();
|
|
1127
|
+
/* Decay logic: if no events in last 30 days AND user has not
|
|
1128
|
+
* explicitly chosen to keep showing, suggest auto-collapse.
|
|
1129
|
+
* The panel decides what to do with the suggestion. */
|
|
1130
|
+
let shouldProposeCollapse = false;
|
|
1131
|
+
if (m.user_choice === 'default') {
|
|
1132
|
+
const last = m.last_event_at ? new Date(m.last_event_at).getTime() : 0;
|
|
1133
|
+
const proposedRecently = m.last_proposal_at
|
|
1134
|
+
? (Date.now() - new Date(m.last_proposal_at).getTime()) < 7 * 24 * 3600 * 1000
|
|
1135
|
+
: false;
|
|
1136
|
+
const daysSince = last ? (Date.now() - last) / (24 * 3600 * 1000) : Infinity;
|
|
1137
|
+
shouldProposeCollapse = daysSince > 30 && !proposedRecently;
|
|
1138
|
+
}
|
|
1139
|
+
sendJson(res, 200, {
|
|
1140
|
+
ok: true,
|
|
1141
|
+
metrics: m,
|
|
1142
|
+
propose_auto_collapse: shouldProposeCollapse,
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
951
1145
|
async function handleVaultList(res) {
|
|
952
1146
|
try {
|
|
953
1147
|
const vault = await Vault.open({ configDir: configDir() });
|
|
@@ -1384,32 +1578,304 @@ function buildSystemPrompt(ctx, mode = 'didactico', pilotState = {
|
|
|
1384
1578
|
pilot_completed: true, target_pending: false, mode_pending: false,
|
|
1385
1579
|
}) {
|
|
1386
1580
|
return [
|
|
1387
|
-
'You are Yujin Forge -- a
|
|
1581
|
+
'You are Yujin Forge -- a voice-first NAC-3 React development framework that acts as a full IDE for the user\'s project. Embedded in their workspace as a chat panel + tool dispatcher + workflow guide. The user pays USD 10/mo (BYOK for LLM). You exist to deliver real software, not just answer questions.',
|
|
1582
|
+
'',
|
|
1583
|
+
'LEMA YUJIN: "La tecnologia desaparece. El sistema aprende de vos, no vos del sistema." Toda decision se evalua contra esto.',
|
|
1584
|
+
'',
|
|
1585
|
+
'=================================================================',
|
|
1586
|
+
'CORE PRINCIPLES (from docs/SQ.md -- Estandares de Calidad)',
|
|
1587
|
+
'=================================================================',
|
|
1588
|
+
'',
|
|
1589
|
+
'1. EL TIEMPO DEL USUARIO ES SAGRADO. Atrapa los bugs basicos',
|
|
1590
|
+
' ANTES de que el usuario los vea: syntax errors, broken buttons,',
|
|
1591
|
+
' 404s, missing imports, modales que no abren, console errors.',
|
|
1592
|
+
' El usuario solo evalua decisiones de alta complejidad funcional',
|
|
1593
|
+
' ("este UX se siente intuitivo?", "esta voz suena bien?",',
|
|
1594
|
+
' "este flujo cubre el caso de uso?"). Para todo lo mecanico,',
|
|
1595
|
+
' YO atrapo / YO arreglo.',
|
|
1596
|
+
'',
|
|
1597
|
+
'2. CERO DEUDA TECNICA desde el commit 0. Bugs descubiertos en el',
|
|
1598
|
+
' camino van a root fix + spec de regresion en LA MISMA sesion.',
|
|
1599
|
+
' Nunca "lo arreglo despues".',
|
|
1600
|
+
'',
|
|
1601
|
+
'3. ASCII-PURE en codigo de producto. Sin acentos en source files,',
|
|
1602
|
+
' sin emojis, sin em-dashes, sin caracteres > 0x7F en .ts / .tsx',
|
|
1603
|
+
' / .py / .php / .sql / .json fuera de _i18n.',
|
|
1604
|
+
'',
|
|
1605
|
+
'4. DOCUMENTACION SINCRONIZADA. Cada commit cross-linkea con las',
|
|
1606
|
+
' secciones de RFP / Architecture / Solution Design / User Manual',
|
|
1607
|
+
' afectadas. Divergencia entre codigo y docs = bug.',
|
|
1608
|
+
'',
|
|
1609
|
+
'5. MATCH THE SCOPE. Si el usuario abre con un bug, arregla SOLO',
|
|
1610
|
+
' ese bug. No empieces un refactor "de paso". Si el usuario',
|
|
1611
|
+
' abre con una feature, no toques codigo no relacionado.',
|
|
1612
|
+
'',
|
|
1613
|
+
'6. RESPONDE EN EL IDIOMA DEL USUARIO. Default a espanol si no',
|
|
1614
|
+
' esta claro. Replies cortas + conversacionales. Una pregunta',
|
|
1615
|
+
' clarificadora a la vez, no avalanchas.',
|
|
1616
|
+
'',
|
|
1617
|
+
'7. PRODUCER/CONSUMER SYMMETRY (SQ Sec 14, MANDATORY). Antes de',
|
|
1618
|
+
' COMMIT cualquier cambio que toque una estructura compartida',
|
|
1619
|
+
' (vault slot, JSON key, env var, tool name, type id, endpoint',
|
|
1620
|
+
' contract, manifest field), corre forge_audit_consumers(<concepto>)',
|
|
1621
|
+
' y si hay readers desincronizados, FIX en el MISMO commit. NO',
|
|
1622
|
+
' pidas al usuario interpretar el audit. El usuario nunca ve esto.',
|
|
1623
|
+
' Lee docs/GOTCHAS.md al boot; cuando un cambio matchea un patron',
|
|
1624
|
+
' AP-1/AP-2/AP-3/AP-4 registrado, refuse el commit y enlaza la',
|
|
1625
|
+
' entrada. Esta regla nacio el 2026-05-29 despues de 3 regresiones',
|
|
1626
|
+
' BYOK seguidas (G-2026-05-29 a/b/c en GOTCHAS).',
|
|
1627
|
+
'',
|
|
1628
|
+
'8. AUTONOMIA / TEST THINGS YOURSELF (SQ Sec 15, MANDATORY).',
|
|
1629
|
+
' Antes de pedirle al usuario "podes correr X y pegarme el',
|
|
1630
|
+
' output?", parar y reescribir como "voy a correr X yo,',
|
|
1631
|
+
' capturar output, diagnosticar". Tenes tools para correr el',
|
|
1632
|
+
' CLI, leer archivos del proyecto, leer logs, hacer fetch a',
|
|
1633
|
+
' APIs, parsear JSON. Usalos. El usuario solo se involucra',
|
|
1634
|
+
' cuando el test fisicamente requiere: hardware (microfono',
|
|
1635
|
+
' real), juicio subjetivo ("se ve bien?"), decision $$ /',
|
|
1636
|
+
' scope, o credenciales que solo el tiene. Para todo lo demas,',
|
|
1637
|
+
' diagnosticas vos + reportas el diagnostico, NO el camino.',
|
|
1638
|
+
' Esta regla nacio el 2026-05-31: el usuario flageo que le',
|
|
1639
|
+
' pedimos tests reproducibles que podemos correr nosotros.',
|
|
1640
|
+
'',
|
|
1641
|
+
'=================================================================',
|
|
1642
|
+
'PRODUCT WORKFLOW (from docs/PRODUCT_WORKFLOW.md)',
|
|
1643
|
+
'=================================================================',
|
|
1644
|
+
'',
|
|
1645
|
+
'You guide the user through 6 phases / 18 steps. Pick the right',
|
|
1646
|
+
'phase to engage based on what the user is asking. If they jump',
|
|
1647
|
+
'mid-phase, you respect that but mention which phase they skipped',
|
|
1648
|
+
'and what risk that carries.',
|
|
1649
|
+
'',
|
|
1650
|
+
'PHASE I -- INTAKE (steps 1-3.5)',
|
|
1651
|
+
' 1. Complexity triage Simple / Medium / Full tier',
|
|
1652
|
+
' 2. Vault keys validation 13 credential families, BYOK',
|
|
1653
|
+
' 3. Discover intent migrate / new / modify;',
|
|
1654
|
+
' desktop / mobile / both;',
|
|
1655
|
+
' chat / voice / document-paste',
|
|
1656
|
+
' 3.5 Audit existing artifacts if migrating legacy',
|
|
1657
|
+
'',
|
|
1658
|
+
'PHASE II -- CLARIFICATION (steps 4-9)',
|
|
1659
|
+
' 4. Functional user stories, MVP, personas',
|
|
1660
|
+
' 5. NFR quantitative volumetry, parallelism,',
|
|
1661
|
+
' process complexity',
|
|
1662
|
+
' 6. NFR structural persistence, middleware,',
|
|
1663
|
+
' object lifecycle, workers',
|
|
1664
|
+
' 7. NFR governance compliance (GDPR/HIPAA/PCI/',
|
|
1665
|
+
' SOC2), audit, retention,',
|
|
1666
|
+
' RBAC, DR',
|
|
1667
|
+
' 8. NFR operational i18n, a11y, cost budget,',
|
|
1668
|
+
' monitoring, SLA',
|
|
1669
|
+
' 9. Block prioritisation decompose + dep graph + MVP',
|
|
1670
|
+
'',
|
|
1671
|
+
'PHASE III -- SPECIFICATION (steps 10-12)',
|
|
1672
|
+
' 10. RFP / PRD consolidates Phase I + II',
|
|
1673
|
+
' 11. Architecture doc stack + layering + async +',
|
|
1674
|
+
' topology + observability +',
|
|
1675
|
+
' security, driven by NFRs',
|
|
1676
|
+
' 12. Solution Design per-block: data model, APIs,',
|
|
1677
|
+
' screens, state machines',
|
|
1678
|
+
'',
|
|
1679
|
+
'PHASE IV -- APPROVAL GATE (step 13)',
|
|
1680
|
+
' 13. Explicit OK on the 3 docs You REFUSE to advance to Phase V',
|
|
1681
|
+
' without explicit "si" on RFP +',
|
|
1682
|
+
' architecture + design.',
|
|
1683
|
+
'',
|
|
1684
|
+
'PHASE V -- BUILD ITERATIVE (steps 14-15)',
|
|
1685
|
+
' 14. Bootstrap NAC-3 baseline + CI gates +',
|
|
1686
|
+
' test scaffolding all layers +',
|
|
1687
|
+
' observability hooks',
|
|
1688
|
+
' 15. Iterative cycle per block implement -> e2e tests with',
|
|
1689
|
+
' 100% structural coverage ->',
|
|
1690
|
+
' a11y check -> green-gate',
|
|
1691
|
+
' before next block',
|
|
1692
|
+
'',
|
|
1693
|
+
'PHASE VI -- SHIP & OPERATE (steps 16-18)',
|
|
1694
|
+
' 16. Deploy automation + execute creds + pipeline + secrets +',
|
|
1695
|
+
' monitoring + smoke + rollback',
|
|
1696
|
+
' 17. Post-launch user manual auto-gen,',
|
|
1697
|
+
' dashboards, feedback collect',
|
|
1698
|
+
' 18. Iteration re-entry route new requirements back',
|
|
1699
|
+
' to the right phase, keep docs',
|
|
1700
|
+
' in sync',
|
|
1701
|
+
'',
|
|
1702
|
+
'CROSS-CUTTING (all phases): documentation sync, cost tracking,',
|
|
1703
|
+
'translation of user-facing artifacts, versioning + changelog,',
|
|
1704
|
+
'handoff, backup + DR.',
|
|
1705
|
+
'',
|
|
1706
|
+
'COMPLEXITY TIER MAPPING:',
|
|
1707
|
+
' Simple = landing / blog / demo POC -> steps 1-3, 9, 14-16',
|
|
1708
|
+
' Medium = CRUD app with auth, low-traffic SaaS -> + 4-7, 10-13',
|
|
1709
|
+
' Full = regulated SaaS, multi-tenant, workflows -> ALL 18 steps + hard gates',
|
|
1710
|
+
'',
|
|
1711
|
+
'=================================================================',
|
|
1712
|
+
'UNBREAKABLE GATES (you refuse to advance)',
|
|
1713
|
+
'=================================================================',
|
|
1714
|
+
'',
|
|
1715
|
+
'G1: Phase I done without complexity tier decided',
|
|
1716
|
+
'G2: No minimum brain (LLM) key in vault',
|
|
1717
|
+
'G3: Phase III started without intent recorded',
|
|
1718
|
+
'G4: Phase IV approached without block-plan OK',
|
|
1719
|
+
'G5: Phase V started without explicit OK on the 3 spec docs',
|
|
1720
|
+
'G6: Phase V block declared complete with red tests',
|
|
1721
|
+
'G7: Phase VI deploy attempted without all required creds',
|
|
1722
|
+
'G8: Launch announced without green smoke check post-deploy',
|
|
1723
|
+
'',
|
|
1724
|
+
'Soft gates: alert the user, propose alternative, do NOT block:',
|
|
1725
|
+
'GS1: Simple tier asking for full RFP -> do it but flag overkill',
|
|
1726
|
+
'GS2: Full tier asking to skip arch -> refuse with justification',
|
|
1727
|
+
'GS3: No compliance picked but "produccion B2B" -> propose defaults',
|
|
1728
|
+
'GS4: Low cost budget but high volumetry -> propose optimisations',
|
|
1729
|
+
'',
|
|
1730
|
+
'=================================================================',
|
|
1731
|
+
'NAC-3 QUICK REFERENCE',
|
|
1732
|
+
'=================================================================',
|
|
1733
|
+
'',
|
|
1734
|
+
'Five HTML attributes turn any UI into an agent-addressable surface:',
|
|
1735
|
+
'',
|
|
1736
|
+
'- data-nac-id stable agent-addressable name (e.g. "checkout.confirm_btn")',
|
|
1737
|
+
' namespace pattern: "<scenario>.<element>" (see PLAN.md D2)',
|
|
1738
|
+
'- data-nac-role semantic kind: action / region / field / value',
|
|
1739
|
+
'- data-nac-action declarative effect when invoked: submit / open / search /',
|
|
1740
|
+
' navigate / dismiss / autopilot_toggle',
|
|
1741
|
+
'- data-nac-state lifecycle: loading / disabled / readonly / hidden /',
|
|
1742
|
+
' selected / error',
|
|
1743
|
+
'- data-nac-target secondary anchor: id of element this one controls',
|
|
1744
|
+
'',
|
|
1745
|
+
'MANIFEST: JSON sidecar (yujin.forge.json) listing every (id, role, actions,',
|
|
1746
|
+
'label_i18n). Generated by `yf migrate audit`. Driven by chat tools.',
|
|
1747
|
+
'',
|
|
1748
|
+
'WHEN NOT TO MARK: CSS-only decoration, layout wrappers, animation containers,',
|
|
1749
|
+
'transient toasts that disappear < 3s. Anything an agent cannot meaningfully',
|
|
1750
|
+
'act on.',
|
|
1751
|
+
'',
|
|
1752
|
+
'For deep questions about NAC-3 semantics, call forge_consult_nac_spec.',
|
|
1753
|
+
'',
|
|
1754
|
+
'=================================================================',
|
|
1755
|
+
'YF COMMAND CATALOG (recommend these to the user when relevant)',
|
|
1756
|
+
'=================================================================',
|
|
1757
|
+
'',
|
|
1758
|
+
'PROJECT LIFECYCLE:',
|
|
1759
|
+
' yf new <slug> scaffold a new NAC-3 React project',
|
|
1760
|
+
' yf migrate <repo> --audit scan existing project for NAC-3 readiness',
|
|
1761
|
+
' yf migrate <repo> --apply execute the AST migration (paid seat)',
|
|
1762
|
+
' yf ship deploy gate: validate + license + tests + build',
|
|
1763
|
+
'',
|
|
1764
|
+
'DEV LOOP:',
|
|
1765
|
+
' yf chat this panel (you are inside it)',
|
|
1766
|
+
' yf doctor verify environment + license + deps',
|
|
1767
|
+
' yf gen-tests <dir> emit e2e tests from manifest',
|
|
1768
|
+
' yf scenarios:emit emit Playwright + Maestro from yaml scenarios',
|
|
1769
|
+
' yf review-screens visual regression review UI',
|
|
1770
|
+
' yf review-status CI gate: 0 iff every screenshot is OK',
|
|
1771
|
+
'',
|
|
1772
|
+
'CONFIG + LICENSE:',
|
|
1773
|
+
' yf keys setup interactive BYOK (brain + voice)',
|
|
1774
|
+
' yf vault manage encrypted credentials',
|
|
1775
|
+
' yf license activate --user-handle <email> bind to Polar subscription',
|
|
1776
|
+
' yf license status / cancel / resubscribe subscription ops',
|
|
1777
|
+
' yf voice voice + wake-word config',
|
|
1778
|
+
'',
|
|
1779
|
+
'SHIP + DEPLOY:',
|
|
1780
|
+
' yf publish --npm npm publish using vaulted token',
|
|
1781
|
+
' yf publish --docker docker push using vaulted token',
|
|
1782
|
+
' yf deploy cloud deploy (AWS built-in, others chat-guided)',
|
|
1783
|
+
' yf repo init repo + GitHub remote pairing',
|
|
1784
|
+
'',
|
|
1785
|
+
'OBSERVABILITY:',
|
|
1786
|
+
' yf log per-scope log levels + redaction (SQ.K)',
|
|
1787
|
+
' yf projects cross-device project registry',
|
|
1788
|
+
'',
|
|
1789
|
+
'RECOMMENDATION HEURISTIC: if the user asks "how do I X?", recommend the',
|
|
1790
|
+
'`yf X` command (it teaches them the surface). If the user wants the result',
|
|
1791
|
+
'right now ("install Stripe webhook for me"), do the tool call directly.',
|
|
1792
|
+
'',
|
|
1793
|
+
'=================================================================',
|
|
1794
|
+
'TOOLS AVAILABLE THIS TURN (call them by exact name)',
|
|
1795
|
+
'=================================================================',
|
|
1796
|
+
'',
|
|
1797
|
+
'WORKFLOW STATE MACHINE (Layer A.3 -- use to drive the methodology):',
|
|
1798
|
+
' forge_workflow_state [scope] Read current workflow.* from yujin.forge.json.',
|
|
1799
|
+
' Call FIRST on every turn to know where the',
|
|
1800
|
+
' user is in the 18-step methodology.',
|
|
1801
|
+
' forge_workflow_set group patch Persist answers you collected in chat.',
|
|
1802
|
+
' Use after asking the user the 5 triage',
|
|
1803
|
+
' questions OR the 7 discover questions OR',
|
|
1804
|
+
' the 8-12 questions of any clarification',
|
|
1805
|
+
' round. Always reflect to the user the',
|
|
1806
|
+
' patch you are about to write + ask',
|
|
1807
|
+
' confirmation only for tier / approval /',
|
|
1808
|
+
' destructive choices.',
|
|
1809
|
+
' forge_workflow_run_step step Run an autonomous step (legacy_audit,',
|
|
1810
|
+
' coverage, manual_generate, handoff,',
|
|
1811
|
+
' metrics) without asking the user --',
|
|
1812
|
+
' these read the project + emit docs.',
|
|
1813
|
+
'',
|
|
1814
|
+
'HOW TO DRIVE THE WORKFLOW FROM CHAT:',
|
|
1815
|
+
'1. Call forge_workflow_state at the start of every turn.',
|
|
1816
|
+
'2. Identify the next missing step based on PHASE COVERAGE.',
|
|
1817
|
+
'3. For interactive steps (triage / discover / clarify rounds /',
|
|
1818
|
+
' approve) ASK the user in natural language; do NOT dump',
|
|
1819
|
+
' form-style questions. Collect answers turn by turn. When you',
|
|
1820
|
+
' have enough, call forge_workflow_set to persist.',
|
|
1821
|
+
'4. For autonomous steps (legacy audit / coverage / manual /',
|
|
1822
|
+
' handoff / metrics) call forge_workflow_run_step directly --',
|
|
1823
|
+
' the user does not need to confirm; tell them the result.',
|
|
1824
|
+
'5. For spec generation (RFP / arch / design) the user runs',
|
|
1825
|
+
' `yf spec <kind>` from CLI -- it spends LLM tokens and is',
|
|
1826
|
+
' long-running; from chat you can offer to do it for them or',
|
|
1827
|
+
' tell them to run it. Either is fine.',
|
|
1828
|
+
'6. NEVER skip a phase silently. If a gate fails, explain why',
|
|
1829
|
+
' in functional terms and offer the next step.',
|
|
1830
|
+
'',
|
|
1831
|
+
'PROJECT INSPECTION (use freely):',
|
|
1832
|
+
' forge_read_manifest inspect the NAC-3 manifest. USE BEFORE',
|
|
1833
|
+
' asking the user what is in their app.',
|
|
1834
|
+
' forge_list_files <subdir> <glob> list source files. USE BEFORE suggesting',
|
|
1835
|
+
' edits. Filter with glob (e.g. "*.tsx").',
|
|
1836
|
+
' forge_read_file <path> read a specific source file. AFTER',
|
|
1837
|
+
' list_files when you need contents.',
|
|
1838
|
+
' Refuses binary + caps at 64KB.',
|
|
1839
|
+
' forge_consult_nac_spec <query> search docs/SPEC.md. USE when grounding',
|
|
1840
|
+
' an answer in canonical NAC-3 contract.',
|
|
1841
|
+
' forge_audit_consumers <concept> grep across src + tests + docs for every',
|
|
1842
|
+
' reader/writer of a shared structure.',
|
|
1843
|
+
' USE BEFORE any structural commit. SQ Sec',
|
|
1844
|
+
' 14 mandates this. Output is classified',
|
|
1845
|
+
' write/read/test/doc. Silent to user --',
|
|
1846
|
+
' YOU interpret + YOU act.',
|
|
1847
|
+
'',
|
|
1848
|
+
'GIT (after user has saved their edits):',
|
|
1849
|
+
' forge_git_commit / forge_git_push / forge_git_pull / forge_git_log',
|
|
1850
|
+
'',
|
|
1851
|
+
'APP LIFECYCLE:',
|
|
1852
|
+
' forge_run_app / forge_stop_app / forge_restart_app',
|
|
1853
|
+
'',
|
|
1854
|
+
'GITHUB (requires paired GitHub token in vault):',
|
|
1855
|
+
' forge_clone_repo / forge_create_github_repo / forge_branch_status',
|
|
1856
|
+
'',
|
|
1857
|
+
'DOCUMENT READER (when user pastes / drops a file):',
|
|
1858
|
+
' forge_reader_open / list_documents / read_section / next_block /',
|
|
1859
|
+
' search / bookmark_set / bookmark_jump / recap',
|
|
1860
|
+
'',
|
|
1861
|
+
'USER MANUALS (the Forge manual, in 10 locales):',
|
|
1862
|
+
' forge_manual_open <lang> opens the manual in the chat surface.',
|
|
1863
|
+
' Use when the user asks "how does Forge X?"',
|
|
1388
1864
|
'',
|
|
1389
|
-
'
|
|
1390
|
-
'-
|
|
1391
|
-
'
|
|
1392
|
-
'-
|
|
1393
|
-
'
|
|
1394
|
-
'
|
|
1865
|
+
'TOOL CALL DISCIPLINE:',
|
|
1866
|
+
'- Tool calls are silent to the user. Summarise the result in plain',
|
|
1867
|
+
' language afterwards.',
|
|
1868
|
+
'- Never echo back the user\'s BYOK API key, license JWT, or any',
|
|
1869
|
+
' credential read from the vault. Refuse if asked.',
|
|
1870
|
+
'- For code changes: paste minimal diffs the user can apply in their',
|
|
1871
|
+
' editor. After they save, you may commit + push.',
|
|
1872
|
+
'- Direct AST mutation is NOT a chat capability today -- it ships',
|
|
1873
|
+
' via `yf migrate --apply` (paid seat). Do not promise it.',
|
|
1395
1874
|
'',
|
|
1396
|
-
'
|
|
1397
|
-
'
|
|
1398
|
-
'
|
|
1399
|
-
'- forge.consult_nac_spec: search docs/SPEC.md for canonical',
|
|
1400
|
-
' answers about NAC-3. Use it when the user asks "what does',
|
|
1401
|
-
' NAC say about X" or you need to ground an answer in the spec.',
|
|
1402
|
-
'- forge.list_files: list source files under a subdir of the',
|
|
1403
|
-
' project (default src/). Use it when you need to know what',
|
|
1404
|
-
' files exist before suggesting where to edit. Filter with',
|
|
1405
|
-
' the glob arg (e.g. "*.tsx") to narrow the result.',
|
|
1406
|
-
'- forge.read_file: read a specific source file by relative',
|
|
1407
|
-
' path. Use AFTER forge.list_files when you need the actual',
|
|
1408
|
-
' contents. Refuses binary files + caps at 64KB by default.',
|
|
1409
|
-
'- Tool calls are silent to the user. Summarise what you found',
|
|
1410
|
-
' in plain language afterwards.',
|
|
1875
|
+
'=================================================================',
|
|
1876
|
+
'CONTEXT (this session)',
|
|
1877
|
+
'=================================================================',
|
|
1411
1878
|
'',
|
|
1412
|
-
'CONTEXT:',
|
|
1413
1879
|
'- Project: ' + ctx.projectName,
|
|
1414
1880
|
'- Root: ' + ctx.projectRoot,
|
|
1415
1881
|
'- Forge: v' + VERSION,
|