trackops 1.1.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) 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 +67 -45
  19. package/skills/trackops/agents/openai.yaml +5 -1
  20. package/skills/trackops/locales/en/SKILL.md +86 -0
  21. package/skills/trackops/locales/en/references/activation.md +73 -0
  22. package/skills/trackops/locales/en/references/troubleshooting.md +49 -0
  23. package/skills/trackops/locales/en/references/workflow.md +26 -0
  24. package/skills/trackops/references/activation.md +53 -19
  25. package/skills/trackops/references/troubleshooting.md +36 -21
  26. package/skills/trackops/references/workflow.md +21 -15
  27. package/skills/trackops/scripts/bootstrap-trackops.js +9 -7
  28. package/skills/trackops/skill.json +4 -4
  29. package/templates/opera/agent.md +10 -9
  30. package/templates/opera/architecture/dependency-graph.md +24 -0
  31. package/templates/opera/architecture/runtime-automation.md +24 -0
  32. package/templates/opera/architecture/runtime-operations.md +34 -0
  33. package/templates/opera/en/agent.md +21 -20
  34. package/templates/opera/en/architecture/dependency-graph.md +24 -0
  35. package/templates/opera/en/architecture/runtime-automation.md +24 -0
  36. package/templates/opera/en/architecture/runtime-operations.md +34 -0
  37. package/templates/opera/en/reviews/delivery-audit.md +18 -0
  38. package/templates/opera/en/reviews/integration-audit.md +18 -0
  39. package/templates/opera/en/router.md +19 -9
  40. package/templates/opera/reviews/delivery-audit.md +18 -0
  41. package/templates/opera/reviews/integration-audit.md +18 -0
  42. package/templates/opera/router.md +15 -5
  43. package/templates/skills/opera-contract-auditor/SKILL.md +38 -0
  44. package/templates/skills/opera-contract-auditor/locales/en/SKILL.md +38 -0
  45. package/templates/skills/opera-policy-guard/SKILL.md +26 -0
  46. package/templates/skills/opera-policy-guard/locales/en/SKILL.md +26 -0
  47. package/templates/skills/project-starter-skill/SKILL.md +89 -164
  48. package/templates/skills/project-starter-skill/locales/en/SKILL.md +104 -24
  49. package/ui/js/views/overview.js +16 -12
  50. package/templates/etapa/agent.md +0 -26
  51. package/templates/etapa/genesis.md +0 -94
  52. package/templates/etapa/references/autonomy-and-recovery.md +0 -117
  53. package/templates/etapa/references/etapa-cycle.md +0 -193
  54. package/templates/etapa/registry.md +0 -28
  55. 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.1",
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}'.`);
@@ -1,64 +1,86 @@
1
1
  ---
2
2
  name: "trackops"
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
- metadata:
5
- version: "1.1.0"
6
- type: "global"
7
- triggers:
8
- - "install trackops"
9
- - "skills.sh"
10
- - "bootstrap trackops"
11
- - "trackops init"
12
- - "trackops opera install"
3
+ description: "Skill global de TrackOps para instalar y activar la orquestacion local de proyectos con OPERA, entorno y handoff a agentes. Usala cuando el usuario quiera instalar TrackOps desde skills.sh, bootstrapear el runtime con `node scripts/bootstrap-trackops.js`, ejecutar `trackops init`, ejecutar `trackops opera install`, revisar `trackops opera handoff`, o trabajar sobre el flujo operativo de un repositorio."
13
4
  ---
14
5
 
15
6
  # TrackOps
16
7
 
17
- Use this skill in two layers:
8
+ Si la conversacion y el proyecto deben trabajar en ingles, lee `locales/en/SKILL.md` antes de seguir.
18
9
 
19
- 1. Global skill layer
20
- Install it with:
10
+ ## Capa global
21
11
 
22
- ```bash
23
- npx skills add Baxahaun/trackops --skill trackops --full-depth --global --agent codex -y
24
- ```
12
+ Instala la skill del marketplace con:
25
13
 
26
- Replace `codex` with any supported target: `antigravity`, `claude-code`, `codex`, `cursor`, `gemini-cli`, `github-copilot`, or `kiro-cli`.
14
+ ```bash
15
+ npx skills add Baxahaun/trackops
16
+ ```
27
17
 
28
- Before relying on the CLI, run:
18
+ Antes de depender del CLI, ejecuta el script empaquetado de la skill:
29
19
 
30
- ```bash
31
- node scripts/bootstrap-trackops.js
32
- ```
20
+ ```bash
21
+ node scripts/bootstrap-trackops.js
22
+ ```
33
23
 
34
- 2. Local project layer
35
- Activate TrackOps inside the current repository with:
24
+ Ese bootstrap:
36
25
 
37
- ```bash
38
- trackops init
39
- ```
26
+ - asegura el runtime npm de `trackops`
27
+ - valida que el binario global sea ejecutable
28
+ - registra estado en `~/.trackops/runtime.json`
40
29
 
41
- Add OPERA only when explicitly requested:
30
+ No debe crear archivos dentro de un repositorio por si solo.
42
31
 
43
- ```bash
44
- trackops opera install
45
- ```
32
+ ## Capa local del proyecto
46
33
 
47
- Core rules:
34
+ Dentro del repositorio:
48
35
 
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.
36
+ ```bash
37
+ trackops init
38
+ trackops opera install
39
+ ```
56
40
 
57
- Read references only when needed:
41
+ Reglas base:
58
42
 
59
- - `references/activation.md`
60
- for install and activation flow
61
- - `references/workflow.md`
62
- for day-to-day repo operation
63
- - `references/troubleshooting.md`
64
- for bootstrap or environment issues
43
+ - trata la instalacion global como no invasiva
44
+ - usa `ops/contract/operating-contract.json` como contrato de maquina cuando exista
45
+ - usa `ops/project_control.json` como fuente de verdad operativa
46
+ - usa `ops/policy/autonomy.json` antes de acciones sensibles
47
+ - usa `/.env` y `/.env.example` como contrato de entorno
48
+ - deja la documentacion operativa generada dentro de `ops/`
49
+ - usa `trackops locale get|set` y `trackops doctor locale` cuando el idioma importe
50
+
51
+ ## Onboarding de OPERA
52
+
53
+ OPERA ya no asume que todo usuario es tecnico.
54
+
55
+ TrackOps clasifica:
56
+
57
+ - nivel tecnico del usuario
58
+ - estado actual del proyecto
59
+ - documentacion disponible
60
+
61
+ Luego elige una de dos rutas:
62
+
63
+ - `direct bootstrap`
64
+ para usuarios tecnicos y repos ya definidos
65
+ - `agent handoff`
66
+ para ideas tempranas, usuarios no tecnicos o documentacion debil
67
+
68
+ Si TrackOps deriva al agente:
69
+
70
+ - lee `ops/bootstrap/agent-handoff.md`
71
+ - o imprimelo con `trackops opera handoff --print`
72
+ - exige como salida:
73
+ - `ops/bootstrap/intake.json`
74
+ - `ops/bootstrap/spec-dossier.md`
75
+ - `ops/bootstrap/open-questions.md` cuando queden huecos importantes
76
+ - reanuda con:
77
+
78
+ ```bash
79
+ trackops opera bootstrap --resume
80
+ ```
81
+
82
+ ## Que referencia leer y cuando
83
+
84
+ - lee `references/activation.md` solo para instalacion, primer uso, locale bootstrap y activacion de un repo
85
+ - lee `references/workflow.md` solo cuando TrackOps ya esta activo y haga falta operar el dia a dia del repositorio
86
+ - lee `references/troubleshooting.md` solo cuando fallen la instalacion, el bootstrap, el resume o el contrato de entorno
@@ -1,3 +1,7 @@
1
1
  interface:
2
2
  display_name: "TrackOps"
3
- short_description: "Global TrackOps skill for local project orchestration and operational automation"
3
+ short_description: "Bootstrap TrackOps and activate OPERA"
4
+ default_prompt: "Use $trackops to bootstrap the TrackOps runtime and activate OPERA in this repository."
5
+
6
+ policy:
7
+ allow_implicit_invocation: true