trackops 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +695 -640
- package/bin/trackops.js +116 -116
- package/lib/config.js +326 -326
- package/lib/control.js +208 -208
- package/lib/env.js +244 -244
- package/lib/init.js +325 -325
- package/lib/locale.js +41 -41
- package/lib/opera-bootstrap.js +942 -936
- package/lib/opera.js +495 -486
- package/lib/preferences.js +74 -74
- package/lib/registry.js +214 -214
- package/lib/release.js +56 -56
- package/lib/runtime-state.js +144 -144
- package/lib/skills.js +74 -57
- package/lib/workspace.js +260 -260
- package/locales/en.json +192 -170
- package/locales/es.json +192 -170
- package/package.json +61 -58
- package/scripts/postinstall-locale.js +21 -21
- package/scripts/skills-marketplace-smoke.js +124 -124
- package/scripts/smoke-tests.js +558 -554
- package/scripts/sync-skill-version.js +21 -21
- package/scripts/validate-skill.js +103 -103
- package/skills/trackops/SKILL.md +126 -122
- package/skills/trackops/agents/openai.yaml +7 -7
- package/skills/trackops/locales/en/SKILL.md +126 -122
- package/skills/trackops/locales/en/references/activation.md +94 -90
- package/skills/trackops/locales/en/references/troubleshooting.md +73 -67
- package/skills/trackops/locales/en/references/workflow.md +55 -32
- package/skills/trackops/references/activation.md +94 -90
- package/skills/trackops/references/troubleshooting.md +73 -67
- package/skills/trackops/references/workflow.md +55 -32
- package/skills/trackops/skill.json +29 -29
- package/templates/hooks/post-checkout +2 -2
- package/templates/hooks/post-commit +2 -2
- package/templates/hooks/post-merge +2 -2
- package/templates/opera/agent.md +28 -27
- package/templates/opera/architecture/dependency-graph.md +24 -24
- package/templates/opera/architecture/runtime-automation.md +24 -24
- package/templates/opera/architecture/runtime-operations.md +34 -34
- package/templates/opera/en/agent.md +22 -21
- package/templates/opera/en/architecture/dependency-graph.md +24 -24
- package/templates/opera/en/architecture/runtime-automation.md +24 -24
- package/templates/opera/en/architecture/runtime-operations.md +34 -34
- package/templates/opera/en/reviews/delivery-audit.md +18 -18
- package/templates/opera/en/reviews/integration-audit.md +18 -18
- package/templates/opera/en/router.md +24 -19
- package/templates/opera/references/autonomy-and-recovery.md +117 -117
- package/templates/opera/references/opera-cycle.md +193 -193
- package/templates/opera/registry.md +28 -28
- package/templates/opera/reviews/delivery-audit.md +18 -18
- package/templates/opera/reviews/integration-audit.md +18 -18
- package/templates/opera/router.md +54 -49
- package/templates/skills/changelog-updater/SKILL.md +69 -69
- package/templates/skills/commiter/SKILL.md +99 -99
- package/templates/skills/opera-contract-auditor/SKILL.md +38 -38
- package/templates/skills/opera-contract-auditor/locales/en/SKILL.md +38 -38
- package/templates/skills/opera-policy-guard/SKILL.md +26 -26
- package/templates/skills/opera-policy-guard/locales/en/SKILL.md +26 -26
- package/templates/skills/opera-skill/SKILL.md +279 -0
- package/templates/skills/opera-skill/locales/en/SKILL.md +279 -0
- package/templates/skills/opera-skill/locales/en/references/phase-dod.md +138 -0
- package/templates/skills/opera-skill/references/phase-dod.md +138 -0
- package/templates/skills/project-starter-skill/SKILL.md +150 -131
- package/templates/skills/project-starter-skill/locales/en/SKILL.md +143 -105
- package/templates/skills/project-starter-skill/references/opera-cycle.md +195 -193
- package/ui/css/base.css +284 -284
- package/ui/css/charts.css +425 -425
- package/ui/css/components.css +1107 -1107
- package/ui/css/onboarding.css +133 -133
- package/ui/css/terminal.css +125 -125
- package/ui/css/timeline.css +58 -58
- package/ui/css/tokens.css +284 -284
- package/ui/favicon.svg +5 -5
- package/ui/index.html +99 -99
- package/ui/js/charts.js +526 -526
- package/ui/js/console-logger.js +172 -172
- package/ui/js/filters.js +247 -247
- package/ui/js/icons.js +129 -129
- package/ui/js/keyboard.js +229 -229
- package/ui/js/router.js +142 -142
- package/ui/js/theme.js +100 -100
- package/ui/js/time-tracker.js +248 -248
- package/ui/js/views/dashboard.js +870 -870
- package/ui/js/views/flash.js +47 -47
- package/ui/js/views/projects.js +745 -745
- package/ui/js/views/scrum.js +476 -476
- package/ui/js/views/settings.js +331 -331
- package/ui/js/views/timeline.js +265 -265
package/lib/control.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const { spawnSync } = require("child_process");
|
|
6
|
-
|
|
7
|
-
const config = require("./config");
|
|
8
|
-
const env = require("./env");
|
|
9
|
-
const { t, setLocale, getLocale } = require("./i18n");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { spawnSync } = require("child_process");
|
|
6
|
+
|
|
7
|
+
const config = require("./config");
|
|
8
|
+
const env = require("./env");
|
|
9
|
+
const { t, setLocale, getLocale } = require("./i18n");
|
|
10
10
|
|
|
11
11
|
const PRIORITY_ORDER = ["P0", "P1", "P2", "P3"];
|
|
12
12
|
const STATUS_ORDER = ["in_progress", "in_review", "pending", "blocked", "completed", "cancelled"];
|
|
@@ -51,14 +51,14 @@ function statusLabel(status) {
|
|
|
51
51
|
|
|
52
52
|
/* ── repo snapshot ── */
|
|
53
53
|
|
|
54
|
-
function getRepoSnapshot(contextOrRoot) {
|
|
55
|
-
const context = config.ensureContext(contextOrRoot);
|
|
56
|
-
const repoRoot = context.workspaceRoot;
|
|
57
|
-
const branch = git(["branch", "--show-current"], repoRoot) || "detached";
|
|
58
|
-
const status = git(["status", "--short"], repoRoot) || "";
|
|
59
|
-
const lines = status.split(/\r?\n/).filter(Boolean);
|
|
60
|
-
const lastCommitRaw = git(["log", "-1", "--pretty=format:%H%n%cs%n%s"], repoRoot);
|
|
61
|
-
const divergenceRaw = git(["rev-list", "--left-right", "--count", "@{upstream}...HEAD"], repoRoot);
|
|
54
|
+
function getRepoSnapshot(contextOrRoot) {
|
|
55
|
+
const context = config.ensureContext(contextOrRoot);
|
|
56
|
+
const repoRoot = context.workspaceRoot;
|
|
57
|
+
const branch = git(["branch", "--show-current"], repoRoot) || "detached";
|
|
58
|
+
const status = git(["status", "--short"], repoRoot) || "";
|
|
59
|
+
const lines = status.split(/\r?\n/).filter(Boolean);
|
|
60
|
+
const lastCommitRaw = git(["log", "-1", "--pretty=format:%H%n%cs%n%s"], repoRoot);
|
|
61
|
+
const divergenceRaw = git(["rev-list", "--left-right", "--count", "@{upstream}...HEAD"], repoRoot);
|
|
62
62
|
|
|
63
63
|
let staged = 0;
|
|
64
64
|
let unstaged = 0;
|
|
@@ -87,17 +87,17 @@ function getRepoSnapshot(contextOrRoot) {
|
|
|
87
87
|
return { generatedAt: nowIso(), branch, clean: lines.length === 0, staged, unstaged, untracked, ahead, behind, lastCommit };
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
function refreshRepoRuntime(root, options = {}) {
|
|
91
|
-
const context = config.ensureContext(root);
|
|
92
|
-
const runtimeFile = config.runtimeFilePath(context);
|
|
93
|
-
fs.mkdirSync(path.dirname(runtimeFile), { recursive: true });
|
|
94
|
-
const snapshot = getRepoSnapshot(context);
|
|
95
|
-
writeJson(runtimeFile, snapshot);
|
|
96
|
-
if (!options.quiet) {
|
|
97
|
-
console.log(t("cli.runtimeUpdated", { path: path.relative(context.workspaceRoot, runtimeFile) }));
|
|
98
|
-
}
|
|
99
|
-
return snapshot;
|
|
100
|
-
}
|
|
90
|
+
function refreshRepoRuntime(root, options = {}) {
|
|
91
|
+
const context = config.ensureContext(root);
|
|
92
|
+
const runtimeFile = config.runtimeFilePath(context);
|
|
93
|
+
fs.mkdirSync(path.dirname(runtimeFile), { recursive: true });
|
|
94
|
+
const snapshot = getRepoSnapshot(context);
|
|
95
|
+
writeJson(runtimeFile, snapshot);
|
|
96
|
+
if (!options.quiet) {
|
|
97
|
+
console.log(t("cli.runtimeUpdated", { path: path.relative(context.workspaceRoot, runtimeFile) }));
|
|
98
|
+
}
|
|
99
|
+
return snapshot;
|
|
100
|
+
}
|
|
101
101
|
|
|
102
102
|
/* ── derive ── */
|
|
103
103
|
|
|
@@ -115,11 +115,11 @@ function compareTasks(a, b, phases) {
|
|
|
115
115
|
return a.title.localeCompare(b.title, getLocale());
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function derive(control) {
|
|
119
|
-
const phases = config.getPhases(control);
|
|
120
|
-
const tasks = [...control.tasks].sort((a, b) => compareTasks(a, b, phases));
|
|
121
|
-
const completedIds = new Set(tasks.filter((t) => t.status === "completed").map((t) => t.id));
|
|
122
|
-
const closedStatuses = new Set(["completed", "cancelled"]);
|
|
118
|
+
function derive(control) {
|
|
119
|
+
const phases = config.getPhases(control);
|
|
120
|
+
const tasks = [...control.tasks].sort((a, b) => compareTasks(a, b, phases));
|
|
121
|
+
const completedIds = new Set(tasks.filter((t) => t.status === "completed").map((t) => t.id));
|
|
122
|
+
const closedStatuses = new Set(["completed", "cancelled"]);
|
|
123
123
|
|
|
124
124
|
const readyTasks = tasks
|
|
125
125
|
.filter((task) => {
|
|
@@ -144,12 +144,12 @@ function derive(control) {
|
|
|
144
144
|
phases.find((p) => requiredOpenTasks.some((t) => t.phase === p.id)) ||
|
|
145
145
|
phases[phases.length - 1];
|
|
146
146
|
|
|
147
|
-
const phaseStats = phases.map((phase) => {
|
|
148
|
-
const phaseTasks = tasks.filter((t) => t.phase === phase.id && t.required !== false);
|
|
149
|
-
const completed = phaseTasks.filter((t) => t.status === "completed").length;
|
|
150
|
-
const closed = phaseTasks.filter((t) => closedStatuses.has(t.status)).length;
|
|
151
|
-
return { ...phase, total: phaseTasks.length, completed, closed, remaining: phaseTasks.length - closed };
|
|
152
|
-
});
|
|
147
|
+
const phaseStats = phases.map((phase) => {
|
|
148
|
+
const phaseTasks = tasks.filter((t) => t.phase === phase.id && t.required !== false);
|
|
149
|
+
const completed = phaseTasks.filter((t) => t.status === "completed").length;
|
|
150
|
+
const closed = phaseTasks.filter((t) => closedStatuses.has(t.status)).length;
|
|
151
|
+
return { ...phase, total: phaseTasks.length, completed, closed, remaining: phaseTasks.length - closed };
|
|
152
|
+
});
|
|
153
153
|
|
|
154
154
|
const nextTask = activeTasks[0] || readyTasks[0] || blockers[0] || openTasks[0] || null;
|
|
155
155
|
|
|
@@ -359,11 +359,11 @@ function buildDocMap(control) {
|
|
|
359
359
|
return { taskPlan: renderTaskPlan(control), progress: renderProgress(control), findings: renderFindings(control) };
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
-
function getDocDrift(root, control) {
|
|
363
|
-
const context = config.ensureContext(root);
|
|
364
|
-
const docs = buildDocMap(control);
|
|
365
|
-
const docFiles = config.docFilePaths(context);
|
|
366
|
-
return Object.entries({ task_plan: [docFiles.taskPlan, docs.taskPlan], progress: [docFiles.progress, docs.progress], findings: [docFiles.findings, docs.findings] })
|
|
362
|
+
function getDocDrift(root, control) {
|
|
363
|
+
const context = config.ensureContext(root);
|
|
364
|
+
const docs = buildDocMap(control);
|
|
365
|
+
const docFiles = config.docFilePaths(context);
|
|
366
|
+
return Object.entries({ task_plan: [docFiles.taskPlan, docs.taskPlan], progress: [docFiles.progress, docs.progress], findings: [docFiles.findings, docs.findings] })
|
|
367
367
|
.filter(([, [filePath, expected]]) => {
|
|
368
368
|
if (!fs.existsSync(filePath)) return true;
|
|
369
369
|
return fs.readFileSync(filePath, "utf8").replace(/\r\n/g, "\n") !== `${expected}\n`;
|
|
@@ -371,21 +371,21 @@ function getDocDrift(root, control) {
|
|
|
371
371
|
.map(([name]) => name);
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
-
function syncDocs(root, control) {
|
|
375
|
-
const context = config.ensureContext(root);
|
|
376
|
-
const docs = buildDocMap(control);
|
|
377
|
-
const docFiles = config.docFilePaths(context);
|
|
378
|
-
writeText(docFiles.taskPlan, `${docs.taskPlan}\n`);
|
|
379
|
-
writeText(docFiles.progress, `${docs.progress}\n`);
|
|
380
|
-
writeText(docFiles.findings, `${docs.findings}\n`);
|
|
381
|
-
}
|
|
374
|
+
function syncDocs(root, control) {
|
|
375
|
+
const context = config.ensureContext(root);
|
|
376
|
+
const docs = buildDocMap(control);
|
|
377
|
+
const docFiles = config.docFilePaths(context);
|
|
378
|
+
writeText(docFiles.taskPlan, `${docs.taskPlan}\n`);
|
|
379
|
+
writeText(docFiles.progress, `${docs.progress}\n`);
|
|
380
|
+
writeText(docFiles.findings, `${docs.findings}\n`);
|
|
381
|
+
}
|
|
382
382
|
|
|
383
383
|
/* ── task management ── */
|
|
384
384
|
|
|
385
|
-
function updateTask(root, control, action, taskId, note) {
|
|
386
|
-
const context = config.ensureContext(root);
|
|
387
|
-
const task = control.tasks.find((item) => item.id === taskId);
|
|
388
|
-
if (!task) throw new Error(t("cli.taskNotFound", { taskId }));
|
|
385
|
+
function updateTask(root, control, action, taskId, note) {
|
|
386
|
+
const context = config.ensureContext(root);
|
|
387
|
+
const task = control.tasks.find((item) => item.id === taskId);
|
|
388
|
+
if (!task) throw new Error(t("cli.taskNotFound", { taskId }));
|
|
389
389
|
|
|
390
390
|
const actionMap = { start: "in_progress", review: "in_review", complete: "completed", done: "completed", block: "blocked", pending: "pending", cancel: "cancelled" };
|
|
391
391
|
|
|
@@ -406,49 +406,49 @@ function updateTask(root, control, action, taskId, note) {
|
|
|
406
406
|
task.history.push({ at: nowIso(), action, note: note || "" });
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
-
config.saveControl(context, control);
|
|
410
|
-
syncDocs(context, control);
|
|
411
|
-
refreshRepoRuntime(context, { quiet: true });
|
|
412
|
-
}
|
|
409
|
+
config.saveControl(context, control);
|
|
410
|
+
syncDocs(context, control);
|
|
411
|
+
refreshRepoRuntime(context, { quiet: true });
|
|
412
|
+
}
|
|
413
413
|
|
|
414
414
|
/* ── CLI commands ── */
|
|
415
415
|
|
|
416
|
-
function initLocale(root) {
|
|
417
|
-
try {
|
|
418
|
-
const control = config.loadControl(config.ensureContext(root));
|
|
419
|
-
setLocale(config.getLocale(control));
|
|
420
|
-
} catch (_err) {
|
|
421
|
-
setLocale("es");
|
|
416
|
+
function initLocale(root) {
|
|
417
|
+
try {
|
|
418
|
+
const control = config.loadControl(config.ensureContext(root));
|
|
419
|
+
setLocale(config.getLocale(control));
|
|
420
|
+
} catch (_err) {
|
|
421
|
+
setLocale("es");
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
-
function cmdStatus(root) {
|
|
426
|
-
const context = config.ensureContext(root);
|
|
427
|
-
initLocale(context);
|
|
428
|
-
const control = config.loadControl(context);
|
|
429
|
-
const state = derive(control);
|
|
430
|
-
const phases = config.getPhases(control);
|
|
431
|
-
const repo = refreshRepoRuntime(context, { quiet: true });
|
|
432
|
-
const drift = getDocDrift(context, control);
|
|
433
|
-
const envAudit = env.auditEnvironment(context, control);
|
|
434
|
-
|
|
435
|
-
console.log(t("cli.status.title", { projectName: control.meta.projectName }));
|
|
436
|
-
console.log(t("cli.status.focus", { focus: control.meta.currentFocus }));
|
|
437
|
-
console.log(t("cli.status.activePhase", { phaseId: state.activePhase.id, phaseLabel: state.activePhase.label }));
|
|
438
|
-
console.log(`Layout: ${context.layout} | Workspace: ${context.workspaceRoot}`);
|
|
439
|
-
if (context.layout === "split") {
|
|
440
|
-
console.log(`App: ${context.appRoot}`);
|
|
441
|
-
console.log(`Ops: ${context.opsRoot}`);
|
|
442
|
-
}
|
|
443
|
-
if (control.meta?.opera?.bootstrap?.status) {
|
|
444
|
-
console.log(t("cli.status.bootstrap", { status: control.meta.opera.bootstrap.status, locale: config.getLocale(control) }));
|
|
445
|
-
if (control.meta.opera.bootstrap.mode) {
|
|
446
|
-
console.log(`Bootstrap mode: ${control.meta.opera.bootstrap.mode}`);
|
|
447
|
-
}
|
|
448
|
-
if (control.meta.opera.bootstrap.routeReason) {
|
|
449
|
-
console.log(`Bootstrap route: ${control.meta.opera.bootstrap.routeReason}`);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
425
|
+
function cmdStatus(root) {
|
|
426
|
+
const context = config.ensureContext(root);
|
|
427
|
+
initLocale(context);
|
|
428
|
+
const control = config.loadControl(context);
|
|
429
|
+
const state = derive(control);
|
|
430
|
+
const phases = config.getPhases(control);
|
|
431
|
+
const repo = refreshRepoRuntime(context, { quiet: true });
|
|
432
|
+
const drift = getDocDrift(context, control);
|
|
433
|
+
const envAudit = env.auditEnvironment(context, control);
|
|
434
|
+
|
|
435
|
+
console.log(t("cli.status.title", { projectName: control.meta.projectName }));
|
|
436
|
+
console.log(t("cli.status.focus", { focus: control.meta.currentFocus }));
|
|
437
|
+
console.log(t("cli.status.activePhase", { phaseId: state.activePhase.id, phaseLabel: state.activePhase.label }));
|
|
438
|
+
console.log(`Layout: ${context.layout} | Workspace: ${context.workspaceRoot}`);
|
|
439
|
+
if (context.layout === "split") {
|
|
440
|
+
console.log(`App: ${context.appRoot}`);
|
|
441
|
+
console.log(`Ops: ${context.opsRoot}`);
|
|
442
|
+
}
|
|
443
|
+
if (control.meta?.opera?.bootstrap?.status) {
|
|
444
|
+
console.log(t("cli.status.bootstrap", { status: control.meta.opera.bootstrap.status, locale: config.getLocale(control) }));
|
|
445
|
+
if (control.meta.opera.bootstrap.mode) {
|
|
446
|
+
console.log(`Bootstrap mode: ${control.meta.opera.bootstrap.mode}`);
|
|
447
|
+
}
|
|
448
|
+
if (control.meta.opera.bootstrap.routeReason) {
|
|
449
|
+
console.log(`Bootstrap route: ${control.meta.opera.bootstrap.routeReason}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
452
|
console.log(t("cli.status.tasks", {
|
|
453
453
|
completed: state.totals.completed, inProgress: state.totals.inProgress,
|
|
454
454
|
inReview: state.totals.inReview, pending: state.totals.pending, blocked: state.totals.blocked,
|
|
@@ -485,22 +485,22 @@ function cmdStatus(root) {
|
|
|
485
485
|
}
|
|
486
486
|
if (repo.ahead || repo.behind) {
|
|
487
487
|
console.log(`- ${t("cli.status.divergence", { ahead: repo.ahead, behind: repo.behind })}`);
|
|
488
|
-
}
|
|
489
|
-
console.log(`- ${t("cli.status.runtime", { path: path.relative(context.workspaceRoot, config.runtimeFilePath(context)) })}`);
|
|
490
|
-
console.log(`- Env present: ${envAudit.presentKeys.length ? envAudit.presentKeys.join(", ") : "none"}`);
|
|
491
|
-
console.log(`- Env missing: ${envAudit.missingKeys.length ? envAudit.missingKeys.join(", ") : "none"}`);
|
|
492
|
-
console.log("");
|
|
493
|
-
const syncStatus = drift.length
|
|
494
|
-
? t("cli.status.docsSyncedNo", { files: drift.join(", ") })
|
|
488
|
+
}
|
|
489
|
+
console.log(`- ${t("cli.status.runtime", { path: path.relative(context.workspaceRoot, config.runtimeFilePath(context)) })}`);
|
|
490
|
+
console.log(`- Env present: ${envAudit.presentKeys.length ? envAudit.presentKeys.join(", ") : "none"}`);
|
|
491
|
+
console.log(`- Env missing: ${envAudit.missingKeys.length ? envAudit.missingKeys.join(", ") : "none"}`);
|
|
492
|
+
console.log("");
|
|
493
|
+
const syncStatus = drift.length
|
|
494
|
+
? t("cli.status.docsSyncedNo", { files: drift.join(", ") })
|
|
495
495
|
: t("cli.status.docsSyncedYes");
|
|
496
496
|
console.log(t("cli.status.docsSynced", { status: syncStatus }));
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
-
function cmdNext(root) {
|
|
500
|
-
const context = config.ensureContext(root);
|
|
501
|
-
initLocale(context);
|
|
502
|
-
const control = config.loadControl(context);
|
|
503
|
-
const ready = derive(control).readyTasks.slice(0, 10);
|
|
499
|
+
function cmdNext(root) {
|
|
500
|
+
const context = config.ensureContext(root);
|
|
501
|
+
initLocale(context);
|
|
502
|
+
const control = config.loadControl(context);
|
|
503
|
+
const ready = derive(control).readyTasks.slice(0, 10);
|
|
504
504
|
if (!ready.length) {
|
|
505
505
|
console.log(t("cli.noReadyTasks"));
|
|
506
506
|
return;
|
|
@@ -513,114 +513,114 @@ function cmdNext(root) {
|
|
|
513
513
|
});
|
|
514
514
|
}
|
|
515
515
|
|
|
516
|
-
function cmdSync(root) {
|
|
517
|
-
const context = config.ensureContext(root);
|
|
518
|
-
initLocale(context);
|
|
519
|
-
const control = config.loadControl(context);
|
|
520
|
-
env.syncEnvironment(context, control);
|
|
521
|
-
syncDocs(context, control);
|
|
522
|
-
refreshRepoRuntime(context, { quiet: true });
|
|
523
|
-
console.log(t("cli.docsSynced"));
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function cmdRefreshRepo(root, args) {
|
|
527
|
-
refreshRepoRuntime(config.ensureContext(root), { quiet: (args || []).includes("--quiet") });
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
function cmdTask(root, args) {
|
|
531
|
-
const context = config.ensureContext(root);
|
|
532
|
-
initLocale(context);
|
|
533
|
-
const [action, taskId, ...noteParts] = args || [];
|
|
534
|
-
if (!action || !taskId) throw new Error(t("cli.mustProvideActionAndId"));
|
|
535
|
-
const control = config.loadControl(context);
|
|
536
|
-
updateTask(context, control, action, taskId, noteParts.join(" ").trim());
|
|
537
|
-
console.log(t("cli.taskUpdated", { taskId, action }));
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
function cmdInstallHooks(root) {
|
|
541
|
-
const context = config.ensureContext(root);
|
|
542
|
-
initLocale(context);
|
|
543
|
-
const hooksPath = context.layout === "split" ? "ops/.githooks" : ".githooks";
|
|
544
|
-
const result = spawnSync("git", ["config", "core.hooksPath", hooksPath], { cwd: context.workspaceRoot, encoding: "utf8" });
|
|
545
|
-
if (result.error || result.status !== 0) throw new Error(t("cli.hooksError"));
|
|
546
|
-
console.log(t("cli.hooksInstalled"));
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function cmdHelp() {
|
|
550
|
-
console.log(`trackops — ${t("cli.help.title")}`);
|
|
551
|
-
console.log("");
|
|
552
|
-
console.log(`${t("cli.help.usage")} trackops <command> [args]`);
|
|
553
|
-
console.log("");
|
|
554
|
-
console.log(t("cli.help.commands"));
|
|
555
|
-
console.log(" init [--with-opera] [--legacy-layout] [--locale es|en] [--name \"...\"] [--no-bootstrap]");
|
|
556
|
-
console.log(" [--bootstrap-mode auto|direct|handoff] [--technical-level low|medium|high|senior]");
|
|
557
|
-
console.log(" [--project-state idea|draft|existing_repo|advanced] [--docs-state none|notes|sos|spec_dossier|repo_docs]");
|
|
558
|
-
console.log(" [--decision-ownership user|shared|agent]");
|
|
559
|
-
console.log(` ${t("cli.help.init.desc")}`);
|
|
560
|
-
console.log(" workspace status|migrate");
|
|
561
|
-
console.log(` ${t("cli.help.workspace.desc")}`);
|
|
562
|
-
console.log(" env status|sync");
|
|
563
|
-
console.log(` ${t("cli.help.env.desc")}`);
|
|
564
|
-
console.log(" release [--push]");
|
|
565
|
-
console.log(` ${t("cli.help.release.desc")}`);
|
|
566
|
-
console.log(" version");
|
|
567
|
-
console.log(` ${t("cli.help.version.desc")}`);
|
|
568
|
-
console.log(" status");
|
|
569
|
-
console.log(` ${t("cli.help.status.desc")}`);
|
|
570
|
-
console.log(" next");
|
|
571
|
-
console.log(` ${t("cli.help.next.desc")}`);
|
|
572
|
-
console.log(" sync");
|
|
573
|
-
console.log(` ${t("cli.help.sync.desc")}`);
|
|
574
|
-
console.log(" dashboard [--port N] [--host HOST] [--public] [--strict-port]");
|
|
575
|
-
console.log(` ${t("cli.help.dashboard.desc")}`);
|
|
576
|
-
console.log(" refresh-repo [--quiet]");
|
|
577
|
-
console.log(` ${t("cli.help.refreshRepo.desc")}`);
|
|
578
|
-
console.log(" install-hooks");
|
|
579
|
-
console.log(` ${t("cli.help.installHooks.desc")}`);
|
|
580
|
-
console.log(" register");
|
|
581
|
-
console.log(` ${t("cli.help.register.desc")}`);
|
|
582
|
-
console.log(" projects");
|
|
583
|
-
console.log(` ${t("cli.help.projects.desc")}`);
|
|
584
|
-
console.log(" task <action> <id> [note]");
|
|
585
|
-
console.log(` ${t("cli.help.task.desc")}`);
|
|
586
|
-
console.log(" opera install|bootstrap|handoff|status|configure|upgrade");
|
|
587
|
-
console.log(` ${t("cli.help.opera.desc")}`);
|
|
588
|
-
console.log(` ${t("cli.help.opera.upgradeHint")}`);
|
|
589
|
-
console.log(" locale get|set [es|en]");
|
|
590
|
-
console.log(` ${t("cli.help.locale.desc")}`);
|
|
591
|
-
console.log(" doctor locale");
|
|
592
|
-
console.log(` ${t("cli.help.doctor.desc")}`);
|
|
593
|
-
console.log(" skill install|list|remove|catalog <name>");
|
|
594
|
-
console.log(` ${t("cli.help.skill.desc")}`);
|
|
595
|
-
console.log(" help");
|
|
596
|
-
console.log(` ${t("cli.help.help.desc")}`);
|
|
597
|
-
console.log("");
|
|
598
|
-
console.log(t("cli.help.globalWorkflow"));
|
|
599
|
-
console.log(` ${t("cli.help.globalWorkflow.line1")}`);
|
|
600
|
-
console.log(` ${t("cli.help.globalWorkflow.line2")}`);
|
|
601
|
-
}
|
|
516
|
+
function cmdSync(root) {
|
|
517
|
+
const context = config.ensureContext(root);
|
|
518
|
+
initLocale(context);
|
|
519
|
+
const control = config.loadControl(context);
|
|
520
|
+
env.syncEnvironment(context, control);
|
|
521
|
+
syncDocs(context, control);
|
|
522
|
+
refreshRepoRuntime(context, { quiet: true });
|
|
523
|
+
console.log(t("cli.docsSynced"));
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function cmdRefreshRepo(root, args) {
|
|
527
|
+
refreshRepoRuntime(config.ensureContext(root), { quiet: (args || []).includes("--quiet") });
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function cmdTask(root, args) {
|
|
531
|
+
const context = config.ensureContext(root);
|
|
532
|
+
initLocale(context);
|
|
533
|
+
const [action, taskId, ...noteParts] = args || [];
|
|
534
|
+
if (!action || !taskId) throw new Error(t("cli.mustProvideActionAndId"));
|
|
535
|
+
const control = config.loadControl(context);
|
|
536
|
+
updateTask(context, control, action, taskId, noteParts.join(" ").trim());
|
|
537
|
+
console.log(t("cli.taskUpdated", { taskId, action }));
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function cmdInstallHooks(root) {
|
|
541
|
+
const context = config.ensureContext(root);
|
|
542
|
+
initLocale(context);
|
|
543
|
+
const hooksPath = context.layout === "split" ? "ops/.githooks" : ".githooks";
|
|
544
|
+
const result = spawnSync("git", ["config", "core.hooksPath", hooksPath], { cwd: context.workspaceRoot, encoding: "utf8" });
|
|
545
|
+
if (result.error || result.status !== 0) throw new Error(t("cli.hooksError"));
|
|
546
|
+
console.log(t("cli.hooksInstalled"));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function cmdHelp() {
|
|
550
|
+
console.log(`trackops — ${t("cli.help.title")}`);
|
|
551
|
+
console.log("");
|
|
552
|
+
console.log(`${t("cli.help.usage")} trackops <command> [args]`);
|
|
553
|
+
console.log("");
|
|
554
|
+
console.log(t("cli.help.commands"));
|
|
555
|
+
console.log(" init [--with-opera] [--legacy-layout] [--locale es|en] [--name \"...\"] [--no-bootstrap]");
|
|
556
|
+
console.log(" [--bootstrap-mode auto|direct|handoff] [--technical-level low|medium|high|senior]");
|
|
557
|
+
console.log(" [--project-state idea|draft|existing_repo|advanced] [--docs-state none|notes|sos|spec_dossier|repo_docs]");
|
|
558
|
+
console.log(" [--decision-ownership user|shared|agent]");
|
|
559
|
+
console.log(` ${t("cli.help.init.desc")}`);
|
|
560
|
+
console.log(" workspace status|migrate");
|
|
561
|
+
console.log(` ${t("cli.help.workspace.desc")}`);
|
|
562
|
+
console.log(" env status|sync");
|
|
563
|
+
console.log(` ${t("cli.help.env.desc")}`);
|
|
564
|
+
console.log(" release [--push]");
|
|
565
|
+
console.log(` ${t("cli.help.release.desc")}`);
|
|
566
|
+
console.log(" version");
|
|
567
|
+
console.log(` ${t("cli.help.version.desc")}`);
|
|
568
|
+
console.log(" status");
|
|
569
|
+
console.log(` ${t("cli.help.status.desc")}`);
|
|
570
|
+
console.log(" next");
|
|
571
|
+
console.log(` ${t("cli.help.next.desc")}`);
|
|
572
|
+
console.log(" sync");
|
|
573
|
+
console.log(` ${t("cli.help.sync.desc")}`);
|
|
574
|
+
console.log(" dashboard [--port N] [--host HOST] [--public] [--strict-port]");
|
|
575
|
+
console.log(` ${t("cli.help.dashboard.desc")}`);
|
|
576
|
+
console.log(" refresh-repo [--quiet]");
|
|
577
|
+
console.log(` ${t("cli.help.refreshRepo.desc")}`);
|
|
578
|
+
console.log(" install-hooks");
|
|
579
|
+
console.log(` ${t("cli.help.installHooks.desc")}`);
|
|
580
|
+
console.log(" register");
|
|
581
|
+
console.log(` ${t("cli.help.register.desc")}`);
|
|
582
|
+
console.log(" projects");
|
|
583
|
+
console.log(` ${t("cli.help.projects.desc")}`);
|
|
584
|
+
console.log(" task <action> <id> [note]");
|
|
585
|
+
console.log(` ${t("cli.help.task.desc")}`);
|
|
586
|
+
console.log(" opera install|bootstrap|handoff|status|configure|upgrade");
|
|
587
|
+
console.log(` ${t("cli.help.opera.desc")}`);
|
|
588
|
+
console.log(` ${t("cli.help.opera.upgradeHint")}`);
|
|
589
|
+
console.log(" locale get|set [es|en]");
|
|
590
|
+
console.log(` ${t("cli.help.locale.desc")}`);
|
|
591
|
+
console.log(" doctor locale");
|
|
592
|
+
console.log(` ${t("cli.help.doctor.desc")}`);
|
|
593
|
+
console.log(" skill install|list|remove|catalog <name>");
|
|
594
|
+
console.log(` ${t("cli.help.skill.desc")}`);
|
|
595
|
+
console.log(" help");
|
|
596
|
+
console.log(` ${t("cli.help.help.desc")}`);
|
|
597
|
+
console.log("");
|
|
598
|
+
console.log(t("cli.help.globalWorkflow"));
|
|
599
|
+
console.log(` ${t("cli.help.globalWorkflow.line1")}`);
|
|
600
|
+
console.log(` ${t("cli.help.globalWorkflow.line2")}`);
|
|
601
|
+
}
|
|
602
602
|
|
|
603
603
|
/* ── project-scoped API (used by server) ── */
|
|
604
604
|
|
|
605
|
-
function forProject(root) {
|
|
606
|
-
const context = config.ensureContext(root);
|
|
607
|
-
initLocale(context);
|
|
608
|
-
return {
|
|
609
|
-
loadControl: () => config.loadControl(context),
|
|
610
|
-
saveControl: (ctrl) => config.saveControl(context, ctrl),
|
|
611
|
-
derive,
|
|
612
|
-
buildDocMap,
|
|
613
|
-
getDocDrift: (ctrl) => getDocDrift(context, ctrl),
|
|
614
|
-
syncDocs: (ctrl) => syncDocs(context, ctrl),
|
|
615
|
-
updateTask: (ctrl, action, id, note) => updateTask(context, ctrl, action, id, note),
|
|
616
|
-
getRepoSnapshot: () => getRepoSnapshot(context),
|
|
617
|
-
refreshRepoRuntime: (opts) => refreshRepoRuntime(context, opts),
|
|
618
|
-
getPhases: (ctrl) => config.getPhases(ctrl),
|
|
619
|
-
getLocale: (ctrl) => config.getLocale(ctrl),
|
|
620
|
-
statusLabel,
|
|
621
|
-
context,
|
|
622
|
-
};
|
|
623
|
-
}
|
|
605
|
+
function forProject(root) {
|
|
606
|
+
const context = config.ensureContext(root);
|
|
607
|
+
initLocale(context);
|
|
608
|
+
return {
|
|
609
|
+
loadControl: () => config.loadControl(context),
|
|
610
|
+
saveControl: (ctrl) => config.saveControl(context, ctrl),
|
|
611
|
+
derive,
|
|
612
|
+
buildDocMap,
|
|
613
|
+
getDocDrift: (ctrl) => getDocDrift(context, ctrl),
|
|
614
|
+
syncDocs: (ctrl) => syncDocs(context, ctrl),
|
|
615
|
+
updateTask: (ctrl, action, id, note) => updateTask(context, ctrl, action, id, note),
|
|
616
|
+
getRepoSnapshot: () => getRepoSnapshot(context),
|
|
617
|
+
refreshRepoRuntime: (opts) => refreshRepoRuntime(context, opts),
|
|
618
|
+
getPhases: (ctrl) => config.getPhases(ctrl),
|
|
619
|
+
getLocale: (ctrl) => config.getLocale(ctrl),
|
|
620
|
+
statusLabel,
|
|
621
|
+
context,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
624
|
|
|
625
625
|
module.exports = {
|
|
626
626
|
buildDocMap, derive, getDocDrift, getRepoSnapshot, refreshRepoRuntime, syncDocs, updateTask,
|