trackops 1.1.0 → 2.0.0

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.
Files changed (51) hide show
  1. package/README.md +194 -230
  2. package/bin/trackops.js +54 -28
  3. package/lib/config.js +14 -10
  4. package/lib/control.js +44 -32
  5. package/lib/env.js +18 -1
  6. package/lib/init.js +40 -6
  7. package/lib/opera-bootstrap.js +825 -273
  8. package/lib/opera.js +360 -110
  9. package/lib/preferences.js +74 -0
  10. package/lib/runtime-state.js +144 -0
  11. package/lib/server.js +155 -25
  12. package/locales/en.json +136 -42
  13. package/locales/es.json +136 -42
  14. package/package.json +2 -1
  15. package/scripts/postinstall-locale.js +21 -0
  16. package/scripts/smoke-tests.js +130 -5
  17. package/scripts/validate-skill.js +2 -1
  18. package/skills/trackops/SKILL.md +57 -32
  19. package/skills/trackops/agents/openai.yaml +1 -1
  20. package/skills/trackops/references/activation.md +50 -16
  21. package/skills/trackops/references/troubleshooting.md +35 -20
  22. package/skills/trackops/references/workflow.md +18 -12
  23. package/skills/trackops/scripts/bootstrap-trackops.js +9 -7
  24. package/skills/trackops/skill.json +4 -4
  25. package/templates/opera/agent.md +10 -9
  26. package/templates/opera/architecture/dependency-graph.md +24 -0
  27. package/templates/opera/architecture/runtime-automation.md +24 -0
  28. package/templates/opera/architecture/runtime-operations.md +34 -0
  29. package/templates/opera/en/agent.md +21 -20
  30. package/templates/opera/en/architecture/dependency-graph.md +24 -0
  31. package/templates/opera/en/architecture/runtime-automation.md +24 -0
  32. package/templates/opera/en/architecture/runtime-operations.md +34 -0
  33. package/templates/opera/en/reviews/delivery-audit.md +18 -0
  34. package/templates/opera/en/reviews/integration-audit.md +18 -0
  35. package/templates/opera/en/router.md +19 -9
  36. package/templates/opera/reviews/delivery-audit.md +18 -0
  37. package/templates/opera/reviews/integration-audit.md +18 -0
  38. package/templates/opera/router.md +15 -5
  39. package/templates/skills/opera-contract-auditor/SKILL.md +38 -0
  40. package/templates/skills/opera-contract-auditor/locales/en/SKILL.md +38 -0
  41. package/templates/skills/opera-policy-guard/SKILL.md +26 -0
  42. package/templates/skills/opera-policy-guard/locales/en/SKILL.md +26 -0
  43. package/templates/skills/project-starter-skill/SKILL.md +89 -164
  44. package/templates/skills/project-starter-skill/locales/en/SKILL.md +104 -24
  45. package/ui/js/views/overview.js +16 -12
  46. package/templates/etapa/agent.md +0 -26
  47. package/templates/etapa/genesis.md +0 -94
  48. package/templates/etapa/references/autonomy-and-recovery.md +0 -117
  49. package/templates/etapa/references/etapa-cycle.md +0 -193
  50. package/templates/etapa/registry.md +0 -28
  51. package/templates/etapa/router.md +0 -39
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const path = require("path");
6
+
7
+ const { detectSystemLocale, isInteractive, normalizeLocale, promptForLocale } = require("./locale");
8
+
9
+ function getRuntimeHome() {
10
+ return process.env.TRACKOPS_BOOTSTRAP_HOME || os.homedir();
11
+ }
12
+
13
+ function getRuntimeDir() {
14
+ return path.join(getRuntimeHome(), ".trackops");
15
+ }
16
+
17
+ function getRuntimeFile() {
18
+ return path.join(getRuntimeDir(), "runtime.json");
19
+ }
20
+
21
+ function defaultRuntimeState() {
22
+ return {
23
+ locale: null,
24
+ localeSource: null,
25
+ skill: null,
26
+ skillVersion: null,
27
+ runtimePackage: "trackops",
28
+ runtimeVersion: null,
29
+ bootstrapPolicy: null,
30
+ supportedAgentsV1: [],
31
+ verifiedAt: null,
32
+ verifiedWith: null,
33
+ executable: null,
34
+ };
35
+ }
36
+
37
+ function normalizeRuntimeState(state) {
38
+ const base = defaultRuntimeState();
39
+ const next = { ...base, ...(state || {}) };
40
+ next.locale = normalizeLocale(next.locale);
41
+ next.localeSource = next.localeSource || null;
42
+ next.supportedAgentsV1 = Array.isArray(next.supportedAgentsV1) ? next.supportedAgentsV1 : [];
43
+ return next;
44
+ }
45
+
46
+ function readRuntimeState() {
47
+ const file = getRuntimeFile();
48
+ if (!fs.existsSync(file)) return defaultRuntimeState();
49
+ try {
50
+ return normalizeRuntimeState(JSON.parse(fs.readFileSync(file, "utf8")));
51
+ } catch (_error) {
52
+ return defaultRuntimeState();
53
+ }
54
+ }
55
+
56
+ function writeRuntimeState(patch) {
57
+ const file = getRuntimeFile();
58
+ const state = normalizeRuntimeState({ ...readRuntimeState(), ...(patch || {}) });
59
+ fs.mkdirSync(path.dirname(file), { recursive: true });
60
+ fs.writeFileSync(file, `${JSON.stringify(state, null, 2)}\n`, "utf8");
61
+ return state;
62
+ }
63
+
64
+ function getGlobalLocale() {
65
+ return readRuntimeState().locale || null;
66
+ }
67
+
68
+ function resolveLocaleState(options = {}) {
69
+ const runtime = readRuntimeState();
70
+ const explicit = normalizeLocale(options.explicitLocale);
71
+ if (explicit) return { locale: explicit, source: "explicit", runtime };
72
+
73
+ const project = normalizeLocale(options.projectLocale);
74
+ if (project) return { locale: project, source: "project", runtime };
75
+
76
+ const global = normalizeLocale(runtime.locale);
77
+ if (global) return { locale: global, source: "global", runtime };
78
+
79
+ const envLocale = normalizeLocale(process.env.TRACKOPS_LOCALE);
80
+ if (envLocale) return { locale: envLocale, source: "env", runtime };
81
+
82
+ const system = detectSystemLocale();
83
+ return { locale: system || "es", source: "system", runtime };
84
+ }
85
+
86
+ async function ensureGlobalLocale(options = {}) {
87
+ const current = readRuntimeState();
88
+ const explicit = normalizeLocale(options.preferredLocale);
89
+ if (explicit) {
90
+ return {
91
+ locale: explicit,
92
+ source: "explicit",
93
+ runtime: writeRuntimeState({ locale: explicit, localeSource: "explicit" }),
94
+ };
95
+ }
96
+
97
+ if (normalizeLocale(current.locale)) {
98
+ return { locale: current.locale, source: "global", runtime: current };
99
+ }
100
+
101
+ let locale = null;
102
+ let source = null;
103
+ if (options.interactive !== false && isInteractive()) {
104
+ locale = await promptForLocale(detectSystemLocale());
105
+ source = "prompt";
106
+ } else {
107
+ locale = detectSystemLocale() || "es";
108
+ source = "system";
109
+ }
110
+
111
+ return {
112
+ locale,
113
+ source,
114
+ runtime: writeRuntimeState({ locale, localeSource: source }),
115
+ };
116
+ }
117
+
118
+ function doctorLocale(projectLocale = null, explicitLocale = null) {
119
+ const resolved = resolveLocaleState({ explicitLocale, projectLocale });
120
+ const runtime = readRuntimeState();
121
+ return {
122
+ effectiveLocale: resolved.locale || "es",
123
+ source: resolved.source,
124
+ projectLocale: normalizeLocale(projectLocale),
125
+ globalLocale: normalizeLocale(runtime.locale),
126
+ envLocale: normalizeLocale(process.env.TRACKOPS_LOCALE),
127
+ systemLocale: detectSystemLocale() || "es",
128
+ runtimeFile: getRuntimeFile(),
129
+ };
130
+ }
131
+
132
+ module.exports = {
133
+ getRuntimeHome,
134
+ getRuntimeDir,
135
+ getRuntimeFile,
136
+ defaultRuntimeState,
137
+ normalizeRuntimeState,
138
+ readRuntimeState,
139
+ writeRuntimeState,
140
+ getGlobalLocale,
141
+ resolveLocaleState,
142
+ ensureGlobalLocale,
143
+ doctorLocale,
144
+ };
package/lib/server.js CHANGED
@@ -11,8 +11,9 @@ const config = require("./config");
11
11
  const control = require("./control");
12
12
  const env = require("./env");
13
13
  const registry = require("./registry");
14
- const { t, setLocale, getMessages } = require("./i18n");
15
- const { normalizeLocale } = require("./locale");
14
+ const { t, setLocale, getMessages } = require("./i18n");
15
+ const { normalizeLocale } = require("./locale");
16
+ const runtimeState = require("./runtime-state");
16
17
 
17
18
  const UI_DIR = path.join(__dirname, "..", "ui");
18
19
  const DEFAULT_HOST = "127.0.0.1";
@@ -41,10 +42,19 @@ function sendJson(res, statusCode, payload) {
41
42
  res.end(JSON.stringify(payload));
42
43
  }
43
44
 
44
- function sendText(res, statusCode, message) {
45
- res.writeHead(statusCode, { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" });
46
- res.end(message);
47
- }
45
+ function sendText(res, statusCode, message) {
46
+ res.writeHead(statusCode, { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" });
47
+ res.end(message);
48
+ }
49
+
50
+ function readJsonFileSafe(filePath) {
51
+ if (!filePath || !fs.existsSync(filePath)) return null;
52
+ try {
53
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
54
+ } catch (_error) {
55
+ return null;
56
+ }
57
+ }
48
58
 
49
59
  function parseBody(req) {
50
60
  return new Promise((resolve, reject) => {
@@ -355,22 +365,50 @@ function loadControlApi(projectRoot) {
355
365
  return control.forProject(projectRoot);
356
366
  }
357
367
 
358
- function buildI18nPayload(controlState) {
368
+ function buildI18nPayload(controlState) {
359
369
  const phases = config.getPhases(controlState);
360
370
  const locale = config.getLocale(controlState);
361
371
  const statusLabels = {};
362
372
  for (const s of control.STATUS_ORDER) {
363
373
  statusLabels[s] = control.statusLabel(s);
364
374
  }
365
- return { locale, statusLabels, phases, messages: getMessages(locale) };
366
- }
367
-
375
+ return { locale, statusLabels, phases, messages: getMessages(locale) };
376
+ }
377
+
378
+ function buildOperaState(projectRoot, controlState) {
379
+ const context = config.ensureContext(projectRoot);
380
+ const operaBootstrap = require("./opera-bootstrap");
381
+ const bootstrapState =
382
+ controlState.meta?.opera?.bootstrap ||
383
+ operaBootstrap.detectLegacyBootstrap(context, controlState) ||
384
+ operaBootstrap.createAwaitingBootstrapState(context);
385
+ const qualityReport = readJsonFileSafe(path.join(context.paths.bootstrapDir, "quality-report.json"));
386
+ const localeDoctor = runtimeState.doctorLocale(controlState.meta?.locale || null);
387
+
388
+ return {
389
+ installed: config.isOperaInstalled(controlState),
390
+ version: controlState.meta?.opera?.version || null,
391
+ model: controlState.meta?.opera?.model || null,
392
+ stableTag: controlState.meta?.opera?.stableTag || null,
393
+ contractVersion: controlState.meta?.opera?.contractVersion || null,
394
+ contractReadiness: controlState.meta?.opera?.contractReadiness || qualityReport?.contractReadiness || "hypothesis",
395
+ qualityStatus: controlState.meta?.opera?.qualityStatus || qualityReport?.status || null,
396
+ qualityReport,
397
+ legacyStatus: controlState.meta?.opera?.legacyStatus || bootstrapState?.status || "supported",
398
+ localeSource: localeDoctor.source,
399
+ bootstrap: bootstrapState,
400
+ contractFile: context.paths.contractFile,
401
+ policyFile: context.paths.autonomyPolicyFile,
402
+ };
403
+ }
404
+
368
405
  function getStatePayload(projectRef) {
369
406
  const project = resolveProjectEntry(projectRef);
370
407
  const api = loadControlApi(project.root);
371
408
  const controlState = api.loadControl();
372
409
  const runtime = api.refreshRepoRuntime({ quiet: true });
373
410
  const envState = env.auditEnvironment(project.root, controlState);
411
+ const operaState = buildOperaState(project.root, controlState);
374
412
 
375
413
  return {
376
414
  project,
@@ -378,6 +416,7 @@ function getStatePayload(projectRef) {
378
416
  derived: api.derive(controlState),
379
417
  runtime,
380
418
  env: envState,
419
+ opera: operaState,
381
420
  docsDirty: api.getDocDrift(controlState),
382
421
  i18n: buildI18nPayload(controlState),
383
422
  generatedAt: new Date().toISOString(),
@@ -611,18 +650,23 @@ async function handleApi(req, res, url) {
611
650
  if (!body.root) { sendJson(res, 400, { ok: false, error: "Project path required." }); return; }
612
651
  try {
613
652
  const initMod = require("./init");
614
- const result = initMod.initProject(body.root, { locale: body.locale || null });
615
- if (body.withOpera) {
616
- const opera = require("./opera");
617
- await opera.install(result.root, {
618
- locale: body.locale || null,
619
- bootstrap: body.bootstrap !== false,
620
- interactive: false,
621
- answers: body.bootstrapAnswers || {},
622
- });
623
- }
624
- const project = registry.registerProject(result.root);
625
- sendJson(res, 201, { ok: true, project, projects: registry.listProjects() });
653
+ const result = initMod.initProject(body.root, { locale: body.locale || null });
654
+ if (body.withOpera) {
655
+ const opera = require("./opera");
656
+ await opera.install(result.root, {
657
+ locale: body.locale || null,
658
+ bootstrap: body.bootstrap !== false,
659
+ interactive: false,
660
+ answers: body.bootstrapAnswers || {},
661
+ bootstrapMode: body.bootstrapMode || "auto",
662
+ technicalLevel: body.technicalLevel || null,
663
+ projectState: body.projectState || null,
664
+ docsState: body.docsState || null,
665
+ decisionOwnership: body.decisionOwnership || null,
666
+ });
667
+ }
668
+ const project = registry.registerProject(result.root);
669
+ sendJson(res, 201, { ok: true, project, projects: registry.listProjects() });
626
670
  } catch (err) {
627
671
  sendJson(res, 500, { ok: false, error: err.message });
628
672
  }
@@ -706,8 +750,94 @@ async function handleApi(req, res, url) {
706
750
  sendJson(res, 200, result);
707
751
  return;
708
752
  }
709
-
710
- if (req.method === "POST" && url.pathname === "/api/commands") {
753
+
754
+ if (req.method === "GET" && url.pathname === "/api/opera/bootstrap") {
755
+ const project = resolveProjectEntry(url.searchParams.get("project"));
756
+ const api = loadControlApi(project.root);
757
+ const controlState = api.loadControl();
758
+ const operaState = buildOperaState(project.root, controlState);
759
+ const bootstrap = operaState.bootstrap;
760
+ sendJson(res, 200, {
761
+ ok: true,
762
+ mode: bootstrap.mode || null,
763
+ status: bootstrap.status || "awaiting_intake",
764
+ technicalLevel: bootstrap.technicalLevel || null,
765
+ projectState: bootstrap.projectState || null,
766
+ documentationState: bootstrap.documentationState || null,
767
+ decisionOwnership: bootstrap.decisionOwnership || null,
768
+ handoffFiles: bootstrap.handoffFiles || null,
769
+ intakeFiles: bootstrap.intakeFiles || null,
770
+ reviewFiles: bootstrap.reviewFiles || null,
771
+ contractVersion: operaState.contractVersion,
772
+ contractReadiness: operaState.contractReadiness,
773
+ legacyStatus: operaState.legacyStatus,
774
+ qualityReport: operaState.qualityReport,
775
+ });
776
+ return;
777
+ }
778
+
779
+ if (req.method === "GET" && url.pathname === "/api/opera/handoff") {
780
+ const project = resolveProjectEntry(url.searchParams.get("project"));
781
+ const context = config.ensureContext(project.root);
782
+ const operaBootstrap = require("./opera-bootstrap");
783
+ const files = operaBootstrap.bootstrapFilePaths(context);
784
+ let handoffJson = null;
785
+ if (fs.existsSync(files.json)) {
786
+ try {
787
+ handoffJson = JSON.parse(fs.readFileSync(files.json, "utf8"));
788
+ } catch (_error) {
789
+ handoffJson = null;
790
+ }
791
+ }
792
+ sendJson(res, 200, {
793
+ ok: true,
794
+ markdownFile: files.markdown,
795
+ jsonFile: files.json,
796
+ openQuestionsFile: files.openQuestions,
797
+ qualityReportFile: files.qualityReport,
798
+ markdown: fs.existsSync(files.markdown) ? fs.readFileSync(files.markdown, "utf8") : "",
799
+ json: handoffJson,
800
+ openQuestions: fs.existsSync(files.openQuestions) ? fs.readFileSync(files.openQuestions, "utf8") : "",
801
+ qualityReport: readJsonFileSafe(files.qualityReport),
802
+ });
803
+ return;
804
+ }
805
+
806
+ if (req.method === "POST" && url.pathname === "/api/opera/bootstrap/intake") {
807
+ const body = await parseBody(req);
808
+ const project = resolveProjectEntry(body.projectId || body.project || url.searchParams.get("project"));
809
+ const opera = require("./opera");
810
+ const profile = await opera.runBootstrap(project.root, {
811
+ interactive: false,
812
+ bootstrapMode: body.bootstrapMode || "auto",
813
+ technicalLevel: body.technicalLevel || null,
814
+ projectState: body.projectState || null,
815
+ docsState: body.documentationState || body.docsState || null,
816
+ decisionOwnership: body.decisionOwnership || null,
817
+ answers: body.answers || {},
818
+ });
819
+ sendJson(res, 200, { ok: true, profile, state: getStatePayload(project.id) });
820
+ return;
821
+ }
822
+
823
+ if (req.method === "GET" && url.pathname === "/api/opera/status") {
824
+ const project = resolveProjectEntry(url.searchParams.get("project"));
825
+ const api = loadControlApi(project.root);
826
+ const controlState = api.loadControl();
827
+ sendJson(res, 200, { ok: true, ...buildOperaState(project.root, controlState) });
828
+ return;
829
+ }
830
+
831
+ if (req.method === "POST" && url.pathname === "/api/opera/bootstrap/resume") {
832
+ const body = await parseBody(req);
833
+ const project = resolveProjectEntry(body.projectId || body.project || url.searchParams.get("project"));
834
+ const opera = require("./opera");
835
+ const profile = await opera.runBootstrap(project.root, { interactive: false, resume: true });
836
+ sendJson(res, 200, { ok: true, profile, state: getStatePayload(project.id) });
837
+ return;
838
+ }
839
+
840
+ if (req.method === "POST" && url.pathname === "/api/commands") {
711
841
  const body = await parseBody(req);
712
842
  const project = resolveProjectEntry(body.projectId || body.project || url.searchParams.get("project"));
713
843
  const commandText = String(body.command || "").trim();
@@ -831,7 +961,7 @@ async function handleApi(req, res, url) {
831
961
  const catalog = [
832
962
  { id: "changelog-updater", title: "Changelog Updater", description: "Mantiene automatizado el CHANGELOG basado en commits.", url: "https://skills.sh/changelog-updater.md" },
833
963
  { id: "commiter", title: "Git Commiter", description: "Genera mensajes de commit strictos siguiendo Conventional Commits y Emojis.", url: "https://skills.sh/commiter.md" },
834
- { id: "project-starter-skill", title: "Project Starter", description: "Skill para inicializar proyectos usando el protocolo O.P.E.R.A.", url: "https://skills.sh/project-starter.md" },
964
+ { id: "project-starter-skill", title: "Project Starter", description: "Skill para discovery y estructuracion inicial guiada con TrackOps y OPERA.", url: "https://skills.sh/project-starter.md" },
835
965
  { id: "tdd-master", title: "TDD Master", description: "Fuerza el ciclo Red-Green-Refactor en las implementaciones.", url: "https://skills.sh/tdd-master.md" },
836
966
  { id: "e2e-tester", title: "E2E Tester", description: "Plantillas y comandos para frameworks de Test End-to-End.", url: "https://skills.sh/e2e-tester.md" }
837
967
  ];
package/locales/en.json CHANGED
@@ -101,8 +101,58 @@
101
101
  "cli.next.stream": "stream",
102
102
  "cli.next.summary": "summary",
103
103
 
104
- "cli.help.title": "Project operational control",
105
- "cli.help.usage": "Usage:",
104
+ "cli.help.title": "Project operational control",
105
+ "cli.help.usage": "Usage:",
106
+ "cli.help.commands": "Commands:",
107
+ "cli.help.init.desc": "Initialize TrackOps in the current directory.",
108
+ "cli.help.workspace.desc": "Show or migrate the current workspace layout.",
109
+ "cli.help.env.desc": "Audit or sync the workspace .env contract.",
110
+ "cli.help.release.desc": "Publish the configured app/ branch snapshot.",
111
+ "cli.help.version.desc": "Print the installed TrackOps version.",
112
+ "cli.help.status.desc": "Show project state: focus, active phase, ready tasks, blockers, repo.",
113
+ "cli.help.next.desc": "Prioritized queue of next executable tasks.",
114
+ "cli.help.sync.desc": "Regenerate task_plan.md, progress.md and findings.md from project_control.json.",
115
+ "cli.help.dashboard.desc": "Launch the local web dashboard on a free port and print local/network URLs.",
116
+ "cli.help.refreshRepo.desc": "Update the repo runtime snapshot with git state.",
117
+ "cli.help.installHooks.desc": "Configure git core.hooksPath to use the TrackOps hooks directory.",
118
+ "cli.help.register.desc": "Register the current project in the multi-project portfolio.",
119
+ "cli.help.projects.desc": "List registered projects.",
120
+ "cli.help.task.desc": "Actions: start, review, complete, block, pending, cancel, note.",
121
+ "cli.help.opera.desc": "Manage OPERA.",
122
+ "cli.help.opera.upgradeHint": "Upgrade: trackops opera upgrade --stable [--reset]",
123
+ "cli.help.locale.desc": "Show or update the global TrackOps language.",
124
+ "cli.help.doctor.desc": "Explain the effective language source for this machine/project.",
125
+ "cli.help.skill.desc": "Manage skills.",
126
+ "cli.help.help.desc": "Show this help.",
127
+ "cli.help.globalWorkflow": "Global agent workflow:",
128
+ "cli.help.globalWorkflow.line1": "Install with 'npx skills add Baxahaun/trackops'",
129
+ "cli.help.globalWorkflow.line2": "and the global/agent flags you need; then use 'trackops init' and 'trackops opera install' explicitly inside each project.",
130
+ "cli.usage.workspace": "Usage: trackops workspace <status|migrate>",
131
+ "cli.usage.env": "Usage: trackops env <status|sync>",
132
+ "cli.usage.opera": "Usage: trackops opera <install|bootstrap|handoff|status|configure|upgrade>",
133
+ "cli.usage.skill": "Usage: trackops skill <install|list|remove|catalog> [name]",
134
+ "cli.usage.locale": "Usage: trackops locale <get|set> [es|en]",
135
+ "cli.usage.doctor": "Usage: trackops doctor locale",
136
+ "cli.error.unknownCommand": "Unknown command: {command}",
137
+ "cli.error.runHelp": "Run 'trackops help' for usage.",
138
+ "cli.error.noWorkspace": "No TrackOps workspace found in this directory or any parent.",
139
+ "locale.effective": "Effective language",
140
+ "locale.source": "Source",
141
+ "locale.global": "Global language",
142
+ "locale.project": "Project language",
143
+ "locale.env": "Environment language",
144
+ "locale.system": "System language",
145
+ "locale.runtimeFile": "Runtime file",
146
+ "locale.none": "none",
147
+ "locale.invalid": "Invalid locale: {value}.",
148
+ "locale.updated": "Global language updated to {locale}.",
149
+ "locale.source.explicit": "explicit flag",
150
+ "locale.source.project": "project",
151
+ "locale.source.global": "global",
152
+ "locale.source.env": "environment",
153
+ "locale.source.system": "system",
154
+ "locale.source.prompt": "prompt",
155
+ "locale.source.manual": "manual",
106
156
 
107
157
  "server.ready": "Ops dashboard ready at http://{host}:{port}",
108
158
  "server.bannerTitle": "Serving!",
@@ -138,11 +188,34 @@
138
188
  "init.defaultTaskTitle": "Set up project with trackops",
139
189
  "init.defaultTaskSummary": "Verify initial structure, adjust phases and confirm operational integration.",
140
190
 
141
- "opera.installed": "OPERA methodology installed (v{version}).",
142
- "opera.alreadyInstalled": "OPERA is already installed in this project (v{version}).",
143
- "opera.notInstalled": "OPERA is not installed in this project.",
144
- "opera.upgraded": "OPERA upgraded to v{version}.",
145
- "opera.primitiveDetected": "Existing OPERA installation detected (not managed by Ops).",
191
+ "opera.installed": "OPERA methodology installed (v{version}).",
192
+ "opera.alreadyInstalled": "OPERA is already installed in this project (v{version}).",
193
+ "opera.notInstalled": "OPERA is not installed in this project.",
194
+ "opera.upgraded": "OPERA upgraded to v{version}.",
195
+ "opera.primitiveDetected": "Existing OPERA installation detected (not managed by Ops).",
196
+ "opera.status.version": "OPERA v{version}",
197
+ "opera.status.installed": " Installed: {value}",
198
+ "opera.status.skills": " Skills: {value}",
199
+ "opera.status.locale": " Locale: {locale} ({source})",
200
+ "opera.status.legacy": " Legacy: {value}",
201
+ "opera.status.contractVersion": " Contract version: {value}",
202
+ "opera.status.contractReadiness": " Contract readiness: {value}",
203
+ "opera.status.bootstrap": " Bootstrap: {value}",
204
+ "opera.status.mode": " Mode: {value}",
205
+ "opera.status.route": " Route: {value}",
206
+ "opera.status.ownership": " Ownership: {value}",
207
+ "opera.status.missing": " Missing: {value}",
208
+ "opera.status.resume": " Resume: trackops opera bootstrap --resume",
209
+ "opera.status.handoff": " Handoff: {value}",
210
+ "opera.status.qualityReport": " Quality report: {value}",
211
+ "opera.status.structure": " Structure:",
212
+ "opera.configure.invalidPhases": "Invalid phases JSON.",
213
+ "opera.configure.updated": "Configuration updated.",
214
+ "opera.upgrade.runInstallFirst": "Run 'trackops opera install' first.",
215
+ "opera.upgrade.usage": "Usage: trackops opera upgrade --stable [--reset]",
216
+ "opera.upgrade.legacyUnsupported": "Legacy OPERA project detected. Run 'trackops opera upgrade --stable --reset' to move it to the current stable model.",
217
+ "opera.upgrade.backup": "Backup: {path}",
218
+ "postinstall.localeSet": "TrackOps set the language to '{locale}' ({source}).",
146
219
 
147
220
  "skill.installed": "Skill '{name}' installed.",
148
221
  "skill.removed": "Skill '{name}' uninstalled.",
@@ -154,35 +227,47 @@
154
227
 
155
228
  "cli.status.bootstrap": "Bootstrap: {status} | Locale: {locale}",
156
229
 
157
- "bootstrap.header": "OPERA bootstrap",
158
- "bootstrap.subtitle": "Confirm the core project context to turn OPERA from templates into a real operating framework.",
159
- "bootstrap.question.desiredOutcome": "Desired outcome",
160
- "bootstrap.question.externalServices": "External services (comma separated)",
161
- "bootstrap.question.sourceOfTruth": "Source of truth",
162
- "bootstrap.question.payload": "Delivery payload / target",
163
- "bootstrap.question.behaviorRules": "Behavior rules (semicolon separated)",
230
+ "bootstrap.header": "OPERA bootstrap",
231
+ "bootstrap.subtitle": "Classify the user and the project first. TrackOps will decide whether to continue in the terminal or route discovery to the agent.",
232
+ "bootstrap.question.technicalLevel": "User technical level",
233
+ "bootstrap.question.projectState": "Current project state",
234
+ "bootstrap.question.docsState": "Available documentation",
235
+ "bootstrap.question.decisionOwnership": "Who should own key decisions",
236
+ "bootstrap.question.problemStatement": "Main problem to solve",
237
+ "bootstrap.question.targetUser": "Primary target user",
238
+ "bootstrap.question.desiredOutcome": "Desired outcome",
239
+ "bootstrap.question.externalServices": "External services (comma separated)",
240
+ "bootstrap.question.sourceOfTruth": "Source of truth",
241
+ "bootstrap.question.payload": "Delivery payload / target",
242
+ "bootstrap.question.behaviorRules": "Behavior rules (semicolon separated)",
164
243
  "bootstrap.question.inputSchema": "Input schema JSON",
165
244
  "bootstrap.question.outputSchema": "Output schema JSON",
166
245
  "bootstrap.question.invariants": "Architectural invariants (semicolon separated)",
167
246
  "bootstrap.question.pipeline": "Pipeline steps (semicolon separated)",
168
- "bootstrap.question.templates": "Template files (comma separated)",
169
- "bootstrap.question.repoTasks": "Include optional repo tasks? [y/n]",
170
- "bootstrap.completed": "OPERA bootstrap completed.",
171
- "bootstrap.pending": "OPERA bootstrap saved as pending. Resume with 'trackops opera bootstrap --resume'.",
172
- "bootstrap.infer.envSourceHint": "Environment files detected",
173
- "bootstrap.noneDefined": "Not defined yet.",
174
- "bootstrap.pendingValue": "Pending definition.",
175
- "bootstrap.servicePending": "pending",
176
- "bootstrap.defaultFocus": "Complete OPERA bootstrap",
177
- "bootstrap.defaultTarget": "Target delivery pending definition",
178
- "bootstrap.blocker.missingData": "Core bootstrap data is still missing.",
179
- "bootstrap.history.seeded": "Seeded by OPERA bootstrap.",
180
- "bootstrap.acceptance.discovery": "Discovery questions answered.",
181
- "bootstrap.acceptance.schema": "Input/output schema defined in genesis.md.",
182
- "bootstrap.acceptance.rules": "Behavior rules documented.",
183
- "bootstrap.acceptance.plan": "Plan reviewed and accepted.",
184
- "bootstrap.acceptance.env": "Required credentials verified.",
185
- "bootstrap.acceptance.tests": "Connectivity checks passing.",
247
+ "bootstrap.question.templates": "Template files (comma separated)",
248
+ "bootstrap.question.repoTasks": "Include optional repo tasks? [y/n]",
249
+ "bootstrap.completed": "OPERA bootstrap completed.",
250
+ "bootstrap.pending": "OPERA bootstrap saved as pending. Resume with 'trackops opera bootstrap --resume'.",
251
+ "bootstrap.awaitingAgent": "OPERA bootstrap has been routed to the agent. Complete the handoff and resume with 'trackops opera bootstrap --resume'.",
252
+ "bootstrap.handoffFile": "Handoff file",
253
+ "bootstrap.infer.envSourceHint": "Environment files detected",
254
+ "bootstrap.noneDefined": "Not defined yet.",
255
+ "bootstrap.pendingValue": "Pending definition.",
256
+ "bootstrap.servicePending": "pending",
257
+ "bootstrap.defaultFocus": "Complete OPERA bootstrap",
258
+ "bootstrap.defaultTarget": "Target delivery pending definition",
259
+ "bootstrap.blocker.missingData": "Core bootstrap data is still missing.",
260
+ "bootstrap.blocker.awaitingAgent": "Waiting for the agent handoff output.",
261
+ "bootstrap.history.seeded": "Seeded by OPERA bootstrap.",
262
+ "bootstrap.acceptance.discovery": "Discovery questions answered.",
263
+ "bootstrap.acceptance.schema": "Input/output schema defined in genesis.md.",
264
+ "bootstrap.acceptance.rules": "Behavior rules documented.",
265
+ "bootstrap.acceptance.plan": "Plan reviewed and accepted.",
266
+ "bootstrap.acceptance.intake": "The agent generated ops/bootstrap/intake.json.",
267
+ "bootstrap.acceptance.specDossier": "The agent generated ops/bootstrap/spec-dossier.md.",
268
+ "bootstrap.acceptance.resume": "OPERA ingested the context and continued.",
269
+ "bootstrap.acceptance.env": "Required credentials verified.",
270
+ "bootstrap.acceptance.tests": "Connectivity checks passing.",
186
271
  "bootstrap.acceptance.shape": "External shapes validated against genesis.md.",
187
272
  "bootstrap.acceptance.findings": "Findings documented.",
188
273
  "bootstrap.acceptance.sops": "SOPs documented.",
@@ -196,9 +281,10 @@
196
281
  "bootstrap.acceptance.deploy": "Deployment completed.",
197
282
  "bootstrap.acceptance.triggers": "Triggers configured.",
198
283
  "bootstrap.acceptance.smoke": "Smoke test passing.",
199
- "bootstrap.task.bootstrap.title": "Complete OPERA bootstrap",
200
- "bootstrap.task.bootstrap.summary": "Turn the installed OPERA structure into a project-specific operating model.",
201
- "bootstrap.task.prove.title": "Validate integrations",
284
+ "bootstrap.task.bootstrap.title": "Complete OPERA bootstrap",
285
+ "bootstrap.task.bootstrap.summary": "Turn the installed OPERA structure into a project-specific operating model.",
286
+ "bootstrap.task.bootstrap.handoffSummary": "Prepare the agent handoff so an idea or partial specification becomes structured operational context.",
287
+ "bootstrap.task.prove.title": "Validate integrations",
202
288
  "bootstrap.task.prove.summary": "Verify credentials, connectivity, and response shapes before building further.",
203
289
  "bootstrap.task.structure.title": "Structure the system",
204
290
  "bootstrap.task.structure.summary": "Document SOPs, implement tools, and capture the dependency graph.",
@@ -214,13 +300,21 @@
214
300
  "bootstrap.task.repoChangelog.summary": "Decide how CHANGELOG.md should be maintained in this project.",
215
301
  "bootstrap.task.repoGithub.title": "Review GitHub governance tasks",
216
302
  "bootstrap.task.repoGithub.summary": "Check whether repository automation or governance tasks should be enabled.",
217
- "bootstrap.field.desiredOutcome": "Desired outcome",
218
- "bootstrap.field.sourceOfTruth": "Source of truth",
219
- "bootstrap.field.payload": "Payload",
220
- "bootstrap.field.inputSchema": "Input schema",
221
- "bootstrap.field.outputSchema": "Output schema",
222
- "bootstrap.decisionImpact": "Required to complete the OPERA bootstrap.",
223
- "bootstrap.finding.genesisConflictTitle": "Genesis requires manual review",
303
+ "bootstrap.field.singularDesiredOutcome": "Desired outcome",
304
+ "bootstrap.field.desiredOutcome": "Desired outcome",
305
+ "bootstrap.field.problemStatement": "Problem statement",
306
+ "bootstrap.field.targetUser": "Target user",
307
+ "bootstrap.field.decisionOwnership": "Decision ownership",
308
+ "bootstrap.field.sourceOfTruth": "Source of truth",
309
+ "bootstrap.field.payload": "Payload",
310
+ "bootstrap.field.inputSchema": "Input schema",
311
+ "bootstrap.field.outputSchema": "Output schema",
312
+ "bootstrap.field.intakeJson": "intake.json file",
313
+ "bootstrap.field.specDossier": "spec-dossier.md file",
314
+ "bootstrap.decisionImpact": "Required to complete the OPERA bootstrap.",
315
+ "bootstrap.pendingDecision.handoff": "Send the TrackOps handoff to the agent",
316
+ "bootstrap.pendingDecision.handoffImpact": "Required so the agent can generate intake.json and spec-dossier.md.",
317
+ "bootstrap.finding.genesisConflictTitle": "Genesis requires manual review",
224
318
  "bootstrap.finding.genesisConflictDetail": "TrackOps detected a non-template genesis.md and stopped automatic overwrite.",
225
319
  "bootstrap.finding.genesisConflictImpact": "Bootstrap cannot be completed until the existing constitution is reviewed.",
226
320