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
package/locales/es.json CHANGED
@@ -101,8 +101,58 @@
101
101
  "cli.next.stream": "stream",
102
102
  "cli.next.summary": "resumen",
103
103
 
104
- "cli.help.title": "Control operativo del proyecto",
105
- "cli.help.usage": "Uso:",
104
+ "cli.help.title": "Control operativo del proyecto",
105
+ "cli.help.usage": "Uso:",
106
+ "cli.help.commands": "Comandos:",
107
+ "cli.help.init.desc": "Inicializa TrackOps en el directorio actual.",
108
+ "cli.help.workspace.desc": "Muestra o migra el layout actual del workspace.",
109
+ "cli.help.env.desc": "Audita o sincroniza el contrato .env del workspace.",
110
+ "cli.help.release.desc": "Publica el snapshot configurado de app/.",
111
+ "cli.help.version.desc": "Imprime la version instalada de TrackOps.",
112
+ "cli.help.status.desc": "Muestra el estado del proyecto: foco, fase activa, tareas listas, bloqueadores y repo.",
113
+ "cli.help.next.desc": "Cola priorizada de siguientes tareas ejecutables.",
114
+ "cli.help.sync.desc": "Regenera task_plan.md, progress.md y findings.md desde project_control.json.",
115
+ "cli.help.dashboard.desc": "Lanza el dashboard web local en un puerto libre e imprime URLs local/red.",
116
+ "cli.help.refreshRepo.desc": "Actualiza el snapshot runtime del repo con el estado git.",
117
+ "cli.help.installHooks.desc": "Configura git core.hooksPath para usar el directorio de hooks de TrackOps.",
118
+ "cli.help.register.desc": "Registra el proyecto actual en el portfolio multiproyecto.",
119
+ "cli.help.projects.desc": "Lista los proyectos registrados.",
120
+ "cli.help.task.desc": "Acciones: start, review, complete, block, pending, cancel, note.",
121
+ "cli.help.opera.desc": "Gestiona OPERA.",
122
+ "cli.help.opera.upgradeHint": "Upgrade: trackops opera upgrade --stable [--reset]",
123
+ "cli.help.locale.desc": "Muestra o actualiza el idioma global de TrackOps.",
124
+ "cli.help.doctor.desc": "Explica el origen efectivo del idioma para esta maquina/proyecto.",
125
+ "cli.help.skill.desc": "Gestiona skills.",
126
+ "cli.help.help.desc": "Muestra esta ayuda.",
127
+ "cli.help.globalWorkflow": "Flujo global del agente:",
128
+ "cli.help.globalWorkflow.line1": "Instala con 'npx skills add Baxahaun/trackops'",
129
+ "cli.help.globalWorkflow.line2": "y los flags globales/de agente que necesites; despues usa 'trackops init' y 'trackops opera install' de forma explicita dentro de cada proyecto.",
130
+ "cli.usage.workspace": "Uso: trackops workspace <status|migrate>",
131
+ "cli.usage.env": "Uso: trackops env <status|sync>",
132
+ "cli.usage.opera": "Uso: trackops opera <install|bootstrap|handoff|status|configure|upgrade>",
133
+ "cli.usage.skill": "Uso: trackops skill <install|list|remove|catalog> [name]",
134
+ "cli.usage.locale": "Uso: trackops locale <get|set> [es|en]",
135
+ "cli.usage.doctor": "Uso: trackops doctor locale",
136
+ "cli.error.unknownCommand": "Comando desconocido: {command}",
137
+ "cli.error.runHelp": "Ejecuta 'trackops help' para ver el uso.",
138
+ "cli.error.noWorkspace": "No se encontro ningun workspace TrackOps en este directorio ni en sus padres.",
139
+ "locale.effective": "Idioma efectivo",
140
+ "locale.source": "Origen",
141
+ "locale.global": "Idioma global",
142
+ "locale.project": "Idioma del proyecto",
143
+ "locale.env": "Idioma por entorno",
144
+ "locale.system": "Idioma del sistema",
145
+ "locale.runtimeFile": "Archivo runtime",
146
+ "locale.none": "ninguno",
147
+ "locale.invalid": "Idioma invalido: {value}.",
148
+ "locale.updated": "Idioma global actualizado a {locale}.",
149
+ "locale.source.explicit": "flag explicito",
150
+ "locale.source.project": "proyecto",
151
+ "locale.source.global": "global",
152
+ "locale.source.env": "entorno",
153
+ "locale.source.system": "sistema",
154
+ "locale.source.prompt": "prompt",
155
+ "locale.source.manual": "manual",
106
156
 
107
157
  "server.ready": "Ops dashboard listo en http://{host}:{port}",
108
158
  "server.bannerTitle": "Serving!",
@@ -138,11 +188,34 @@
138
188
  "init.defaultTaskTitle": "Configurar proyecto con trackops",
139
189
  "init.defaultTaskSummary": "Verificar estructura inicial, ajustar fases y confirmar integracion operativa.",
140
190
 
141
- "opera.installed": "Metodologia OPERA instalada (v{version}).",
142
- "opera.alreadyInstalled": "OPERA ya esta instalado en este proyecto (v{version}).",
143
- "opera.notInstalled": "OPERA no esta instalado en este proyecto.",
144
- "opera.upgraded": "OPERA actualizado a v{version}.",
145
- "opera.primitiveDetected": "Detectada instalacion OPERA existente (sin gestion por Ops).",
191
+ "opera.installed": "Metodologia OPERA instalada (v{version}).",
192
+ "opera.alreadyInstalled": "OPERA ya esta instalado en este proyecto (v{version}).",
193
+ "opera.notInstalled": "OPERA no esta instalado en este proyecto.",
194
+ "opera.upgraded": "OPERA actualizado a v{version}.",
195
+ "opera.primitiveDetected": "Detectada instalacion OPERA existente (sin gestion por Ops).",
196
+ "opera.status.version": "OPERA v{version}",
197
+ "opera.status.installed": " Instalado: {value}",
198
+ "opera.status.skills": " Skills: {value}",
199
+ "opera.status.locale": " Idioma: {locale} ({source})",
200
+ "opera.status.legacy": " Legacy: {value}",
201
+ "opera.status.contractVersion": " Version de contrato: {value}",
202
+ "opera.status.contractReadiness": " Readiness del contrato: {value}",
203
+ "opera.status.bootstrap": " Bootstrap: {value}",
204
+ "opera.status.mode": " Modo: {value}",
205
+ "opera.status.route": " Ruta: {value}",
206
+ "opera.status.ownership": " Ownership: {value}",
207
+ "opera.status.missing": " Faltan: {value}",
208
+ "opera.status.resume": " Reanudar: trackops opera bootstrap --resume",
209
+ "opera.status.handoff": " Handoff: {value}",
210
+ "opera.status.qualityReport": " Quality report: {value}",
211
+ "opera.status.structure": " Estructura:",
212
+ "opera.configure.invalidPhases": "JSON de fases invalido.",
213
+ "opera.configure.updated": "Configuracion actualizada.",
214
+ "opera.upgrade.runInstallFirst": "Ejecuta 'trackops opera install' primero.",
215
+ "opera.upgrade.usage": "Uso: trackops opera upgrade --stable [--reset]",
216
+ "opera.upgrade.legacyUnsupported": "Proyecto OPERA legacy detectado. Ejecuta 'trackops opera upgrade --stable --reset' para moverlo al modelo estable actual.",
217
+ "opera.upgrade.backup": "Backup: {path}",
218
+ "postinstall.localeSet": "TrackOps ha fijado el idioma en '{locale}' ({source}).",
146
219
 
147
220
  "skill.installed": "Skill '{name}' instalada.",
148
221
  "skill.removed": "Skill '{name}' desinstalada.",
@@ -154,35 +227,47 @@
154
227
 
155
228
  "cli.status.bootstrap": "Bootstrap: {status} | Idioma: {locale}",
156
229
 
157
- "bootstrap.header": "Bootstrap OPERA",
158
- "bootstrap.subtitle": "Confirma el contexto real del proyecto para convertir OPERA en un framework operativo de verdad.",
159
- "bootstrap.question.desiredOutcome": "Resultado singular deseado",
160
- "bootstrap.question.externalServices": "Servicios externos (separados por comas)",
161
- "bootstrap.question.sourceOfTruth": "Fuente de la verdad",
162
- "bootstrap.question.payload": "Payload / objetivo de entrega",
163
- "bootstrap.question.behaviorRules": "Reglas de comportamiento (separadas por punto y coma)",
230
+ "bootstrap.header": "Bootstrap OPERA",
231
+ "bootstrap.subtitle": "Primero clasifica al usuario y el estado del proyecto. TrackOps decidira si continuar por terminal o derivar el arranque al agente.",
232
+ "bootstrap.question.technicalLevel": "Nivel tecnico del usuario",
233
+ "bootstrap.question.projectState": "Estado actual del proyecto",
234
+ "bootstrap.question.docsState": "Documentacion disponible",
235
+ "bootstrap.question.decisionOwnership": "Quien debe tomar las decisiones clave",
236
+ "bootstrap.question.problemStatement": "Problema principal que se quiere resolver",
237
+ "bootstrap.question.targetUser": "Usuario objetivo principal",
238
+ "bootstrap.question.desiredOutcome": "Resultado singular deseado",
239
+ "bootstrap.question.externalServices": "Servicios externos (separados por comas)",
240
+ "bootstrap.question.sourceOfTruth": "Fuente de la verdad",
241
+ "bootstrap.question.payload": "Payload / objetivo de entrega",
242
+ "bootstrap.question.behaviorRules": "Reglas de comportamiento (separadas por punto y coma)",
164
243
  "bootstrap.question.inputSchema": "Schema JSON de entrada",
165
244
  "bootstrap.question.outputSchema": "Schema JSON de salida",
166
245
  "bootstrap.question.invariants": "Invariantes arquitectonicas (separadas por punto y coma)",
167
246
  "bootstrap.question.pipeline": "Pasos del pipeline (separados por punto y coma)",
168
- "bootstrap.question.templates": "Archivos template (separados por comas)",
169
- "bootstrap.question.repoTasks": "¿Incluir tareas opcionales de repo? [y/n]",
170
- "bootstrap.completed": "Bootstrap OPERA completado.",
171
- "bootstrap.pending": "Bootstrap OPERA guardado como pendiente. Reanuda con 'trackops opera bootstrap --resume'.",
172
- "bootstrap.infer.envSourceHint": "Detectados archivos de entorno",
173
- "bootstrap.noneDefined": "Aun no definido.",
174
- "bootstrap.pendingValue": "Pendiente de definir.",
175
- "bootstrap.servicePending": "pendiente",
176
- "bootstrap.defaultFocus": "Completar bootstrap OPERA",
177
- "bootstrap.defaultTarget": "Entrega objetivo pendiente de definir",
178
- "bootstrap.blocker.missingData": "Todavia faltan datos clave del bootstrap.",
179
- "bootstrap.history.seeded": "Sembrada por bootstrap OPERA.",
180
- "bootstrap.acceptance.discovery": "Preguntas de descubrimiento respondidas.",
181
- "bootstrap.acceptance.schema": "Schema de entrada/salida definido en genesis.md.",
182
- "bootstrap.acceptance.rules": "Reglas de comportamiento documentadas.",
183
- "bootstrap.acceptance.plan": "Plan revisado y aceptado.",
184
- "bootstrap.acceptance.env": "Credenciales requeridas verificadas.",
185
- "bootstrap.acceptance.tests": "Checks de conectividad pasando.",
247
+ "bootstrap.question.templates": "Archivos template (separados por comas)",
248
+ "bootstrap.question.repoTasks": "¿Incluir tareas opcionales de repo? [y/n]",
249
+ "bootstrap.completed": "Bootstrap OPERA completado.",
250
+ "bootstrap.pending": "Bootstrap OPERA guardado como pendiente. Reanuda con 'trackops opera bootstrap --resume'.",
251
+ "bootstrap.awaitingAgent": "Bootstrap OPERA derivado al agente. Completa el handoff y reanuda con 'trackops opera bootstrap --resume'.",
252
+ "bootstrap.handoffFile": "Archivo de handoff",
253
+ "bootstrap.infer.envSourceHint": "Detectados archivos de entorno",
254
+ "bootstrap.noneDefined": "Aun no definido.",
255
+ "bootstrap.pendingValue": "Pendiente de definir.",
256
+ "bootstrap.servicePending": "pendiente",
257
+ "bootstrap.defaultFocus": "Completar bootstrap OPERA",
258
+ "bootstrap.defaultTarget": "Entrega objetivo pendiente de definir",
259
+ "bootstrap.blocker.missingData": "Todavia faltan datos clave del bootstrap.",
260
+ "bootstrap.blocker.awaitingAgent": "Esperando el resultado del handoff al agente.",
261
+ "bootstrap.history.seeded": "Sembrada por bootstrap OPERA.",
262
+ "bootstrap.acceptance.discovery": "Preguntas de descubrimiento respondidas.",
263
+ "bootstrap.acceptance.schema": "Schema de entrada/salida definido en genesis.md.",
264
+ "bootstrap.acceptance.rules": "Reglas de comportamiento documentadas.",
265
+ "bootstrap.acceptance.plan": "Plan revisado y aceptado.",
266
+ "bootstrap.acceptance.intake": "El agente genero ops/bootstrap/intake.json.",
267
+ "bootstrap.acceptance.specDossier": "El agente genero ops/bootstrap/spec-dossier.md.",
268
+ "bootstrap.acceptance.resume": "OPERA pudo ingerir el contexto y continuar.",
269
+ "bootstrap.acceptance.env": "Credenciales requeridas verificadas.",
270
+ "bootstrap.acceptance.tests": "Checks de conectividad pasando.",
186
271
  "bootstrap.acceptance.shape": "Shapes externos validados contra genesis.md.",
187
272
  "bootstrap.acceptance.findings": "Hallazgos documentados.",
188
273
  "bootstrap.acceptance.sops": "SOPs documentados.",
@@ -196,9 +281,10 @@
196
281
  "bootstrap.acceptance.deploy": "Despliegue completado.",
197
282
  "bootstrap.acceptance.triggers": "Triggers configurados.",
198
283
  "bootstrap.acceptance.smoke": "Smoke test pasando.",
199
- "bootstrap.task.bootstrap.title": "Completar bootstrap OPERA",
200
- "bootstrap.task.bootstrap.summary": "Convertir la estructura OPERA instalada en un modelo operativo especifico del proyecto.",
201
- "bootstrap.task.prove.title": "Validar integraciones",
284
+ "bootstrap.task.bootstrap.title": "Completar bootstrap OPERA",
285
+ "bootstrap.task.bootstrap.summary": "Convertir la estructura OPERA instalada en un modelo operativo especifico del proyecto.",
286
+ "bootstrap.task.bootstrap.handoffSummary": "Preparar el handoff al agente para convertir una idea o especificacion parcial en contexto operativo estructurado.",
287
+ "bootstrap.task.prove.title": "Validar integraciones",
202
288
  "bootstrap.task.prove.summary": "Verificar credenciales, conectividad y shape de respuestas antes de seguir construyendo.",
203
289
  "bootstrap.task.structure.title": "Estructurar el sistema",
204
290
  "bootstrap.task.structure.summary": "Documentar SOPs, implementar herramientas y capturar el grafo de dependencias.",
@@ -214,13 +300,21 @@
214
300
  "bootstrap.task.repoChangelog.summary": "Decidir como se mantendra CHANGELOG.md en este proyecto.",
215
301
  "bootstrap.task.repoGithub.title": "Revisar tareas de gobernanza GitHub",
216
302
  "bootstrap.task.repoGithub.summary": "Comprobar si deben activarse tareas de automatizacion o gobernanza del repositorio.",
217
- "bootstrap.field.desiredOutcome": "Resultado deseado",
218
- "bootstrap.field.sourceOfTruth": "Fuente de la verdad",
219
- "bootstrap.field.payload": "Payload",
220
- "bootstrap.field.inputSchema": "Schema de entrada",
221
- "bootstrap.field.outputSchema": "Schema de salida",
222
- "bootstrap.decisionImpact": "Necesario para completar el bootstrap OPERA.",
223
- "bootstrap.finding.genesisConflictTitle": "Genesis requiere revision manual",
303
+ "bootstrap.field.singularDesiredOutcome": "Resultado deseado",
304
+ "bootstrap.field.desiredOutcome": "Resultado deseado",
305
+ "bootstrap.field.problemStatement": "Problema principal",
306
+ "bootstrap.field.targetUser": "Usuario objetivo",
307
+ "bootstrap.field.decisionOwnership": "Propiedad de decision",
308
+ "bootstrap.field.sourceOfTruth": "Fuente de la verdad",
309
+ "bootstrap.field.payload": "Payload",
310
+ "bootstrap.field.inputSchema": "Schema de entrada",
311
+ "bootstrap.field.outputSchema": "Schema de salida",
312
+ "bootstrap.field.intakeJson": "Archivo intake.json",
313
+ "bootstrap.field.specDossier": "Archivo spec-dossier.md",
314
+ "bootstrap.decisionImpact": "Necesario para completar el bootstrap OPERA.",
315
+ "bootstrap.pendingDecision.handoff": "Enviar el handoff de TrackOps al agente",
316
+ "bootstrap.pendingDecision.handoffImpact": "Necesario para que el agente genere intake.json y spec-dossier.md.",
317
+ "bootstrap.finding.genesisConflictTitle": "Genesis requiere revision manual",
224
318
  "bootstrap.finding.genesisConflictDetail": "TrackOps detecto un genesis.md no plantilla y detuvo la sobreescritura automatica.",
225
319
  "bootstrap.finding.genesisConflictImpact": "El bootstrap no puede completarse hasta revisar la constitucion existente.",
226
320
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trackops",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Operational project control with task management, document generation, multi-project dashboard, and optional OPERA methodology",
5
5
  "main": "lib/control.js",
6
6
  "bin": {
@@ -47,6 +47,7 @@
47
47
  "node": ">=18"
48
48
  },
49
49
  "scripts": {
50
+ "postinstall": "node scripts/postinstall-locale.js",
50
51
  "test": "node scripts/smoke-tests.js",
51
52
  "test:smoke": "node scripts/smoke-tests.js",
52
53
  "skill:sync-version": "node scripts/sync-skill-version.js",
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+
3
+ const runtimeState = require("../lib/runtime-state");
4
+ const { setLocale, t } = require("../lib/i18n");
5
+
6
+ function isGlobalInstall() {
7
+ return String(process.env.npm_config_global || "").toLowerCase() === "true";
8
+ }
9
+
10
+ async function main() {
11
+ if (!isGlobalInstall()) return;
12
+ const result = await runtimeState.ensureGlobalLocale({ interactive: true });
13
+ if (!result?.locale) return;
14
+ setLocale(result.locale);
15
+ process.stdout.write(`${t("postinstall.localeSet", { locale: result.locale, source: t(`locale.source.${result.source}`) })}\n`);
16
+ }
17
+
18
+ main().catch((error) => {
19
+ process.stderr.write(`${error.message}\n`);
20
+ process.exit(0);
21
+ });
@@ -231,6 +231,7 @@ async function main() {
231
231
  assert.strictEqual(runtimeStamp.runtimeVersion, packageVersion);
232
232
  assert.strictEqual(runtimeStamp.skill, "trackops");
233
233
  assert.strictEqual(runtimeStamp.bootstrapPolicy, "first_use");
234
+ assert.ok(["es", "en"].includes(runtimeStamp.locale), "el bootstrap global debe fijar un idioma");
234
235
 
235
236
  const installedCli = path.join(bootstrapPrefix, "node_modules", "trackops", "bin", "trackops.js");
236
237
  assert.ok(fs.existsSync(installedCli), "el runtime instalado debe existir dentro del prefijo aislado");
@@ -256,9 +257,15 @@ async function main() {
256
257
  const versionOutput = runNode([BIN, "--version"], ROOT);
257
258
  assert.strictEqual(versionOutput.trim(), packageVersion);
258
259
 
260
+ runNode([BIN, "locale", "set", "en"], tempRoot, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
261
+ const localeGet = runNode([BIN, "locale", "get"], tempRoot, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
262
+ assert.match(localeGet, /Effective language: en|Idioma efectivo: en/);
263
+ const localeDoctor = runNode([BIN, "doctor", "locale"], tempRoot, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
264
+ assert.match(localeDoctor, /Source: global|Origen: global/);
265
+
259
266
  const splitProject = path.join(tempRoot, "split-demo");
260
267
  fs.mkdirSync(splitProject, { recursive: true });
261
- runNode([BIN, "init"], splitProject);
268
+ runNode([BIN, "init", "--locale", "es"], splitProject, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
262
269
 
263
270
  assert.ok(fs.existsSync(path.join(splitProject, ".trackops-workspace.json")));
264
271
  assert.ok(fs.existsSync(path.join(splitProject, ".env")));
@@ -273,6 +280,11 @@ async function main() {
273
280
  const splitControl = readJson(path.join(splitProject, "ops", "project_control.json"));
274
281
  assert.strictEqual(splitControl.meta.workspace.layout, "split");
275
282
  assert.strictEqual(splitControl.meta.environment.rootEnvFile, ".env");
283
+ assert.strictEqual(splitControl.meta.locale, "es");
284
+
285
+ const projectLocaleDoctor = runNode([BIN, "doctor", "locale"], splitProject, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
286
+ assert.match(projectLocaleDoctor, /Project language: es|Idioma del proyecto: es/);
287
+ assert.match(projectLocaleDoctor, /Source: project|Origen: proyecto/);
276
288
 
277
289
  const statusFromWorkspace = runNode([BIN, "status"], splitProject);
278
290
  const statusFromApp = runNode([BIN, "status"], path.join(splitProject, "app"));
@@ -311,27 +323,113 @@ async function main() {
311
323
  assert.ok(fs.existsSync(path.join(splitProject, "ops", "genesis.md")));
312
324
  assert.ok(fs.existsSync(path.join(splitProject, "ops", ".agent", "hub", "agent.md")));
313
325
  assert.ok(fs.existsSync(path.join(splitProject, "ops", ".agents", "skills", "_registry.md")));
326
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "bootstrap", "agent-handoff.md")));
327
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "bootstrap", "agent-handoff.json")));
328
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "bootstrap", "open-questions.md")));
329
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "architecture", "runtime-operations.md")));
330
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "architecture", "dependency-graph.md")));
331
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "architecture", "runtime-automation.md")));
332
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "policy", "autonomy.json")));
333
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "reviews", "integration-audit.md")));
334
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "reviews", "delivery-audit.md")));
314
335
  assert.ok(!fs.existsSync(path.join(splitProject, "genesis.md")));
315
336
 
316
337
  const operaControl = readJson(path.join(splitProject, "ops", "project_control.json"));
317
338
  assert.strictEqual(operaControl.meta.locale, "en");
318
339
  assert.strictEqual(operaControl.meta.opera.installed, true);
340
+ assert.strictEqual(operaControl.meta.opera.bootstrap.mode, "agent_handoff");
341
+ assert.strictEqual(operaControl.meta.opera.bootstrap.status, "awaiting_agent");
342
+ assert.ok(operaControl.meta.opera.skills.includes("project-starter-skill"));
343
+ assert.ok(operaControl.meta.opera.skills.includes("opera-contract-auditor"));
344
+ assert.ok(operaControl.meta.opera.skills.includes("opera-policy-guard"));
319
345
  assert.ok(operaControl.meta.environment.requiredKeys.includes("OPENAI_API_KEY"));
320
346
  const envRootText = fs.readFileSync(path.join(splitProject, ".env"), "utf8");
321
347
  assert.match(envRootText, /OPENAI_API_KEY=/);
322
348
 
323
- const defaultPortFree = await isPortFree(4173);
349
+ const handoffPrint = runNode([BIN, "opera", "handoff", "--print"], splitProject);
350
+ assert.match(handoffPrint, /project-starter-skill/);
351
+ const handoffJson = JSON.parse(runNode([BIN, "opera", "handoff", "--json"], splitProject));
352
+ assert.strictEqual(handoffJson.skill, "project-starter-skill");
353
+
354
+ const directProject = path.join(tempRoot, "direct-demo");
355
+ fs.mkdirSync(directProject, { recursive: true });
356
+ runNode([BIN, "init", "--locale", "en"], directProject, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
357
+ writeJson(path.join(directProject, "app", "package.json"), { name: "direct-demo", version: "1.0.0" });
358
+ runNode([
359
+ BIN,
360
+ "opera",
361
+ "install",
362
+ "--locale",
363
+ "en",
364
+ "--non-interactive",
365
+ "--bootstrap-mode",
366
+ "direct",
367
+ "--technical-level",
368
+ "senior",
369
+ "--project-state",
370
+ "existing_repo",
371
+ "--docs-state",
372
+ "spec_dossier",
373
+ "--decision-ownership",
374
+ "user",
375
+ ], directProject, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
376
+ const directControl = readJson(path.join(directProject, "ops", "project_control.json"));
377
+ assert.strictEqual(directControl.meta.opera.bootstrap.mode, "direct_cli");
378
+ assert.strictEqual(directControl.meta.opera.bootstrap.status, "awaiting_intake");
379
+
380
+ writeJson(path.join(splitProject, "ops", "bootstrap", "intake.json"), {
381
+ version: 1,
382
+ technicalLevel: "low",
383
+ projectState: "idea",
384
+ documentationState: "none",
385
+ decisionOwnership: "agent",
386
+ problemStatement: "users need a simple way to book and pay online",
387
+ targetUser: "small studio owners",
388
+ singularDesiredOutcome: "let users create and pay for bookings",
389
+ userLanguage: "en",
390
+ needsPlainLanguage: true,
391
+ recommendedStack: ["nextjs"],
392
+ externalServices: ["OpenAI", "Stripe"],
393
+ sourceOfTruth: "primary bookings database",
394
+ payload: "bookings dashboard",
395
+ behaviorRules: ["keep explanations simple"],
396
+ architecturalInvariants: ["keep app and ops separated"],
397
+ inputSchema: { booking: { email: "string" } },
398
+ outputSchema: { confirmation: { id: "string" } },
399
+ pipeline: ["create booking", "confirm payment"],
400
+ templates: ["booking-confirmation"],
401
+ });
402
+ fs.writeFileSync(
403
+ path.join(splitProject, "ops", "bootstrap", "spec-dossier.md"),
404
+ "# Spec dossier\n\n## Problem statement\nusers need a simple way to book and pay online\n\n## Target user\nsmall studio owners\n\n## Singular desired outcome\nlet users create and pay for bookings\n\n## Delivery target\nbookings dashboard\n\n## Source of truth\nprimary bookings database\n",
405
+ "utf8",
406
+ );
407
+ runNode([BIN, "opera", "bootstrap", "--resume"], splitProject);
408
+ const resumedControl = readJson(path.join(splitProject, "ops", "project_control.json"));
409
+ assert.strictEqual(resumedControl.meta.opera.bootstrap.status, "completed");
410
+ assert.strictEqual(resumedControl.meta.currentFocus, "let users create and pay for bookings");
411
+ assert.ok(resumedControl.meta.environment.requiredKeys.includes("STRIPE_SECRET_KEY"));
412
+ assert.ok(fs.existsSync(path.join(splitProject, "ops", "contract", "operating-contract.json")));
413
+ const operatingContract = readJson(path.join(splitProject, "ops", "contract", "operating-contract.json"));
414
+ assert.strictEqual(operatingContract.version, 3);
415
+ assert.strictEqual(operatingContract.userModel.language, "en");
416
+ assert.strictEqual(operatingContract.userModel.decisionOwnership, "agent");
417
+
324
418
  const defaultDashboard = startDashboard(splitProject);
325
419
 
326
420
  try {
327
421
  const ready = await waitForDashboard(defaultDashboard);
328
422
  const state = await get(ready.port, "/api/state");
329
423
  const envPayload = await get(ready.port, "/api/env");
424
+ const operaBootstrapPayload = await get(ready.port, "/api/opera/bootstrap");
425
+ const operaHandoffPayload = await get(ready.port, "/api/opera/handoff");
330
426
  const localSkills = await get(ready.port, "/api/skills/local");
331
427
  const discoverSkills = await get(ready.port, "/api/skills/discover");
332
428
 
333
429
  assert.strictEqual(state.status, 200);
334
430
  assert.strictEqual(envPayload.status, 200);
431
+ assert.strictEqual(operaBootstrapPayload.status, 200);
432
+ assert.strictEqual(operaHandoffPayload.status, 200);
335
433
  assert.strictEqual(localSkills.status, 200);
336
434
  assert.strictEqual(discoverSkills.status, 200);
337
435
 
@@ -347,9 +445,14 @@ async function main() {
347
445
  assert.ok(envState.requiredKeys.includes("OPENAI_API_KEY"));
348
446
  assert.ok(envState.missingKeys.includes("OPENAI_API_KEY"));
349
447
 
350
- if (defaultPortFree) {
351
- assert.strictEqual(ready.port, 4173, `se esperaba usar 4173 cuando estaba libre:\n${ready.output}`);
352
- }
448
+ const bootstrapState = JSON.parse(operaBootstrapPayload.body);
449
+ assert.strictEqual(bootstrapState.status, "completed");
450
+ assert.strictEqual(bootstrapState.contractVersion, 3);
451
+ assert.strictEqual(bootstrapState.contractReadiness, "verified");
452
+ const handoffState = JSON.parse(operaHandoffPayload.body);
453
+ assert.ok(handoffState.markdown.includes("project-starter-skill"));
454
+ assert.ok(handoffState.openQuestionsFile.endsWith("open-questions.md"));
455
+
353
456
  } finally {
354
457
  await stopDashboard(defaultDashboard);
355
458
  }
@@ -420,6 +523,28 @@ async function main() {
420
523
  assert.ok(!migratedPackage.scripts || !migratedPackage.scripts["ops:status"]);
421
524
  assert.match(git(["branch", "--list"], legacyProject), /backup\/trackops-workspace-/);
422
525
 
526
+ const legacyUnsupportedProject = path.join(tempRoot, "legacy-unsupported");
527
+ fs.mkdirSync(legacyUnsupportedProject, { recursive: true });
528
+ initGitRepo(legacyUnsupportedProject);
529
+ runNode([BIN, "init", "--locale", "en"], legacyUnsupportedProject, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
530
+ const legacyUnsupportedControlPath = path.join(legacyUnsupportedProject, "ops", "project_control.json");
531
+ const legacyUnsupportedControl = readJson(legacyUnsupportedControlPath);
532
+ legacyUnsupportedControl.meta.opera = {
533
+ installed: true,
534
+ version: "0.9.0",
535
+ };
536
+ writeJson(legacyUnsupportedControlPath, legacyUnsupportedControl);
537
+ const legacyStatusOutput = runNode([BIN, "opera", "status"], legacyUnsupportedProject, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
538
+ assert.match(legacyStatusOutput, /legacy_unsupported/);
539
+ const upgradeWithoutReset = runNodeResult([BIN, "opera", "upgrade", "--stable"], legacyUnsupportedProject, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
540
+ assert.strictEqual(upgradeWithoutReset.status, 0);
541
+ assert.match(`${upgradeWithoutReset.stdout}\n${upgradeWithoutReset.stderr}`, /legacy/i);
542
+ runNode([BIN, "opera", "upgrade", "--stable", "--reset"], legacyUnsupportedProject, { TRACKOPS_BOOTSTRAP_HOME: bootstrapHome });
543
+ const upgradedLegacyControl = readJson(legacyUnsupportedControlPath);
544
+ assert.strictEqual(upgradedLegacyControl.meta.opera.legacyStatus, "supported");
545
+ assert.strictEqual(upgradedLegacyControl.meta.opera.stableTag, "stable");
546
+ assert.ok(fs.existsSync(path.join(legacyUnsupportedProject, "ops", ".tmp", "upgrade-backups")));
547
+
423
548
  const releaseProject = path.join(tempRoot, "release-demo");
424
549
  fs.mkdirSync(releaseProject, { recursive: true });
425
550
  initGitRepo(releaseProject);
@@ -72,10 +72,11 @@ function main() {
72
72
  }
73
73
 
74
74
  for (const requiredPhrase of [
75
- "npx skills add Baxahaun/trackops --skill trackops --full-depth",
75
+ "npx skills add Baxahaun/trackops",
76
76
  "node scripts/bootstrap-trackops.js",
77
77
  "trackops init",
78
78
  "trackops opera install",
79
+ "trackops opera bootstrap --resume",
79
80
  ]) {
80
81
  if (!skillMd.includes(requiredPhrase)) {
81
82
  fail(`skills/trackops/SKILL.md must mention '${requiredPhrase}'.`);
@@ -2,63 +2,88 @@
2
2
  name: "trackops"
3
3
  description: "Global TrackOps skill that prepares your agent for local project orchestration and operational automation, ensures the runtime on first use, and guides per-project activation with optional OPERA."
4
4
  metadata:
5
- version: "1.1.0"
5
+ version: "2.0.0"
6
6
  type: "global"
7
7
  triggers:
8
8
  - "install trackops"
9
9
  - "skills.sh"
10
- - "bootstrap trackops"
11
10
  - "trackops init"
12
11
  - "trackops opera install"
12
+ - "opera handoff"
13
13
  ---
14
14
 
15
15
  # TrackOps
16
16
 
17
- Use this skill in two layers:
17
+ Use this skill in two layers.
18
18
 
19
- 1. Global skill layer
20
- Install it with:
19
+ ## 1. Global skill layer
21
20
 
22
- ```bash
23
- npx skills add Baxahaun/trackops --skill trackops --full-depth --global --agent codex -y
24
- ```
21
+ Install it with:
25
22
 
26
- Replace `codex` with any supported target: `antigravity`, `claude-code`, `codex`, `cursor`, `gemini-cli`, `github-copilot`, or `kiro-cli`.
23
+ ```bash
24
+ npx skills add Baxahaun/trackops
25
+ ```
27
26
 
28
- Before relying on the CLI, run:
27
+ Before relying on the CLI, run:
29
28
 
30
- ```bash
31
- node scripts/bootstrap-trackops.js
32
- ```
29
+ ```bash
30
+ node scripts/bootstrap-trackops.js
31
+ ```
33
32
 
34
- 2. Local project layer
35
- Activate TrackOps inside the current repository with:
33
+ That bootstrap ensures the `trackops` runtime and records state in `~/.trackops/runtime.json`.
36
34
 
37
- ```bash
38
- trackops init
39
- ```
35
+ ## 2. Local project layer
40
36
 
41
- Add OPERA only when explicitly requested:
37
+ Inside a repository:
42
38
 
43
- ```bash
44
- trackops opera install
45
- ```
39
+ ```bash
40
+ trackops init
41
+ trackops opera install
42
+ ```
46
43
 
47
44
  Core rules:
48
45
 
49
- - Treat the global skill install as non-invasive.
50
- - In split workspaces, use `ops/project_control.json` as the operational source of truth.
51
- - In legacy repos, use `project_control.json` at the repository root.
52
- - Prefer `trackops status`, `trackops next`, and `trackops sync` over hand-editing generated docs.
53
- - Treat `trackops init --with-opera` as a shortcut, not as the primary mental model.
54
- - TrackOps manages `/.env` and `/.env.example` at workspace root. Do not print or persist secret values.
55
- - Remember that skills installs from committed Git state.
46
+ - treat the global skill install as non-invasive
47
+ - use `ops/contract/operating-contract.json` as the machine contract when it exists
48
+ - use `ops/project_control.json` as the operational source of truth for backlog and state
49
+ - use `ops/policy/autonomy.json` before approval-sensitive actions
50
+ - use `/.env` and `/.env.example` as the environment contract
51
+ - keep generated operational docs under `ops/`
52
+ - support `trackops locale get|set` and `trackops doctor locale` when language matters
53
+
54
+ ## OPERA onboarding
55
+
56
+ OPERA no longer assumes every user is technical.
57
+
58
+ When OPERA starts, TrackOps classifies:
59
+
60
+ - user technical level
61
+ - current project state
62
+ - available documentation
63
+
64
+ Then it chooses one of two routes:
65
+
66
+ - `direct bootstrap`
67
+ for technical users and already-defined repositories
68
+ - `agent handoff`
69
+ for early ideas, non-technical users, or weak documentation
70
+
71
+ If TrackOps routes bootstrap to the agent:
72
+
73
+ - read `ops/bootstrap/agent-handoff.md`
74
+ - or print it with `trackops opera handoff --print`
75
+ - the agent must produce:
76
+ - `ops/bootstrap/intake.json`
77
+ - `ops/bootstrap/spec-dossier.md`
78
+ - `ops/bootstrap/open-questions.md` when important gaps remain
79
+ - then resume with:
80
+
81
+ ```bash
82
+ trackops opera bootstrap --resume
83
+ ```
56
84
 
57
85
  Read references only when needed:
58
86
 
59
87
  - `references/activation.md`
60
- for install and activation flow
61
88
  - `references/workflow.md`
62
- for day-to-day repo operation
63
89
  - `references/troubleshooting.md`
64
- for bootstrap or environment issues
@@ -1,3 +1,3 @@
1
1
  interface:
2
2
  display_name: "TrackOps"
3
- short_description: "Global TrackOps skill for local project orchestration and operational automation"
3
+ short_description: "Global TrackOps skill for local project orchestration, bilingual onboarding, agent coordination, and operational automation"