trackops 2.0.5 → 2.1.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.
- package/README.md +319 -695
- package/bin/trackops.js +52 -23
- package/lib/cli-format.js +118 -0
- package/lib/config.js +277 -44
- package/lib/control.js +1052 -352
- package/lib/env.js +40 -28
- package/lib/i18n.js +5 -4
- package/lib/init.js +194 -56
- package/lib/opera-bootstrap.js +326 -106
- package/lib/opera-phase-dod.js +485 -0
- package/lib/opera.js +243 -78
- package/lib/plans.js +1329 -0
- package/lib/quality-assert.js +49 -0
- package/lib/quality.js +1759 -0
- package/lib/release.js +18 -11
- package/lib/server.js +504 -192
- package/lib/skills.js +43 -35
- package/lib/workspace.js +32 -21
- package/locales/en.json +431 -75
- package/locales/es.json +432 -76
- package/package.json +6 -5
- package/scripts/quality-unit-tests.js +130 -0
- package/scripts/smoke-tests.js +438 -96
- package/skills/trackops/skill.json +29 -29
- package/templates/skills/opera-quality-guard/SKILL.md +26 -0
- package/templates/skills/opera-quality-guard/locales/en/SKILL.md +26 -0
- package/templates/skills/opera-skill/SKILL.md +8 -0
- package/templates/skills/opera-skill/locales/en/SKILL.md +8 -0
- package/ui/js/api.js +93 -26
- package/ui/js/app.js +13 -7
- package/ui/js/filters.js +49 -29
- package/ui/js/time-tracker.js +41 -28
- package/ui/js/views/board.js +22 -14
- package/ui/js/views/dashboard.js +206 -49
- package/ui/js/views/execution.js +7 -3
- package/ui/js/views/plans.js +284 -0
- package/ui/js/views/scrum.js +25 -13
- package/ui/js/views/sidebar.js +9 -8
- package/ui/js/views/tasks.js +238 -134
package/lib/opera.js
CHANGED
|
@@ -10,19 +10,52 @@ const { promptForLocale, maybePromptForLocale, resolveLocale } = require("./loca
|
|
|
10
10
|
const { resolveLocalizedFile, resolveLocalizedDir, resolveSkillFile } = require("./resources");
|
|
11
11
|
const bootstrap = require("./opera-bootstrap");
|
|
12
12
|
const runtimeState = require("./runtime-state");
|
|
13
|
+
const fmt = require("./cli-format");
|
|
13
14
|
|
|
14
15
|
const TEMPLATES_DIR = path.join(__dirname, "..", "templates", "opera");
|
|
15
16
|
const SKILLS_TEMPLATES_DIR = path.join(__dirname, "..", "templates", "skills");
|
|
16
17
|
const OPERA_VERSION = require("../package.json").version;
|
|
17
|
-
const AUXILIARY_SKILLS = ["opera-skill", "project-starter-skill", "opera-contract-auditor", "opera-policy-guard"];
|
|
18
|
+
const AUXILIARY_SKILLS = ["opera-skill", "opera-quality-guard", "project-starter-skill", "opera-contract-auditor", "opera-policy-guard"];
|
|
18
19
|
|
|
19
20
|
function nowIso() {
|
|
20
21
|
return new Date().toISOString();
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
function formatLocaleSource(source) {
|
|
24
|
-
return t(`locale.source.${String(source || "").trim()}`) || source || t("locale.none");
|
|
25
|
-
}
|
|
24
|
+
function formatLocaleSource(source) {
|
|
25
|
+
return t(`locale.source.${String(source || "").trim()}`) || source || t("locale.none");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function formatBootstrapStatus(status) {
|
|
29
|
+
return t(`bootstrap.status.${String(status || "").trim()}`) || status || t("locale.none");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function formatBootstrapMode(mode) {
|
|
33
|
+
return t(`bootstrap.mode.${String(mode || "").trim()}`) || mode || t("locale.none");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function formatBootstrapReason(reason) {
|
|
37
|
+
return t(`bootstrap.reason.${String(reason || "").trim()}`) || reason || t("locale.none");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function formatDecisionOwnership(value) {
|
|
41
|
+
return t(`bootstrap.ownership.${String(value || "").trim()}`) || value || t("locale.none");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function formatContractReadiness(value) {
|
|
45
|
+
return t(`bootstrap.readiness.${String(value || "").trim()}`) || value || t("locale.none");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatLegacyStatus(value) {
|
|
49
|
+
return t(`bootstrap.legacy.${String(value || "").trim()}`) || value || t("locale.none");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function formatMissingFields(fields) {
|
|
53
|
+
return (fields || []).map((field) => t(`bootstrap.field.${field}`) || field).join(", ");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function relativePathExists(context, relativePath) {
|
|
57
|
+
return Boolean(relativePath) && fs.existsSync(path.join(context.workspaceRoot, relativePath));
|
|
58
|
+
}
|
|
26
59
|
|
|
27
60
|
function readText(filePath) {
|
|
28
61
|
return fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "";
|
|
@@ -88,7 +121,10 @@ function resolveOperaLocale(control, options = {}) {
|
|
|
88
121
|
|
|
89
122
|
function seedAuxiliarySkill(skillName, locale, skillsDir, options = {}) {
|
|
90
123
|
const templateDir = path.join(SKILLS_TEMPLATES_DIR, skillName);
|
|
91
|
-
if (!fs.existsSync(templateDir))
|
|
124
|
+
if (!fs.existsSync(templateDir)) {
|
|
125
|
+
process.stderr.write(`[opera] Warning: skill template not found: ${skillName} (${templateDir})\n`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
92
128
|
const targetDir = path.join(skillsDir, skillName);
|
|
93
129
|
seedDirRecursive(templateDir, targetDir, {
|
|
94
130
|
overwrite: options.rewriteLocalizedTemplates === true,
|
|
@@ -222,22 +258,36 @@ async function install(root, options = {}) {
|
|
|
222
258
|
config.saveControl(context, control);
|
|
223
259
|
env.syncEnvironment(context, control);
|
|
224
260
|
|
|
261
|
+
fmt.blank();
|
|
225
262
|
if (!alreadyInstalled) {
|
|
226
|
-
|
|
263
|
+
fmt.success(t("opera.installed", { version: OPERA_VERSION }));
|
|
227
264
|
} else {
|
|
228
|
-
|
|
265
|
+
fmt.info(t("opera.alreadyInstalled", { version: config.getOperaVersion(control) || OPERA_VERSION }));
|
|
229
266
|
}
|
|
230
267
|
|
|
231
268
|
const skills = require("./skills");
|
|
232
269
|
for (const skillName of ["commiter", "changelog-updater"]) {
|
|
233
270
|
try {
|
|
234
271
|
skills.installSkill(context, skillName, { locale });
|
|
235
|
-
} catch (
|
|
236
|
-
|
|
272
|
+
} catch (error) {
|
|
273
|
+
fmt.warn(t("skill.installError", { name: skillName, error: error.message }));
|
|
237
274
|
}
|
|
238
275
|
}
|
|
239
276
|
skills.updateRegistry(context);
|
|
240
277
|
|
|
278
|
+
if (options.bootstrap !== false && options.interactive !== false) {
|
|
279
|
+
const readline = require("readline");
|
|
280
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
281
|
+
const answer = await new Promise((resolve) => {
|
|
282
|
+
rl.question(` ${t("opera.install.bootstrapConfirm")} `, (ans) => { rl.close(); resolve(ans); });
|
|
283
|
+
});
|
|
284
|
+
const normalizedAnswer = String(answer || "").trim().toLowerCase();
|
|
285
|
+
if (!["y", "yes", "s", "si"].includes(normalizedAnswer)) {
|
|
286
|
+
fmt.info(t("opera.install.bootstrapSkipped"));
|
|
287
|
+
fmt.blank();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
241
291
|
if (options.bootstrap !== false) {
|
|
242
292
|
await runBootstrap(context, {
|
|
243
293
|
locale,
|
|
@@ -302,37 +352,85 @@ async function runBootstrap(root, options = {}) {
|
|
|
302
352
|
if ((options.resume || options.forceResume) && control.meta?.opera?.bootstrap) {
|
|
303
353
|
const resumed = bootstrap.resumeBootstrap(context, control);
|
|
304
354
|
if (resumed.resumed) {
|
|
305
|
-
const
|
|
355
|
+
const profile = resumed.profile;
|
|
356
|
+
const updatedControl = bootstrap.applyBootstrap(context, control, profile);
|
|
306
357
|
env.syncEnvironment(context, updatedControl, { requiredKeys: env.inferRequiredKeys(updatedControl, context) });
|
|
307
358
|
const ops = require("./control");
|
|
308
359
|
ops.syncDocs(context, updatedControl);
|
|
309
360
|
ops.refreshRepoRuntime(context, { quiet: true });
|
|
310
|
-
|
|
311
|
-
|
|
361
|
+
fmt.blank();
|
|
362
|
+
|
|
363
|
+
if (profile.status === "completed") {
|
|
364
|
+
fmt.success(t("bootstrap.completed"));
|
|
365
|
+
fmt.blank();
|
|
366
|
+
fmt.step(t("bootstrap.next.label"));
|
|
367
|
+
fmt.hint(t("bootstrap.next.directCompleted"));
|
|
368
|
+
} else if (profile.status === "blocked") {
|
|
369
|
+
fmt.warn(t("bootstrap.resumeBlocked"));
|
|
370
|
+
for (const field of profile.missingFields || []) {
|
|
371
|
+
fmt.bullet(t(`bootstrap.field.${field}`) || field);
|
|
372
|
+
}
|
|
373
|
+
fmt.blank();
|
|
374
|
+
fmt.hint(t("bootstrap.next.directPending"));
|
|
375
|
+
} else if (profile.status === "needs_review") {
|
|
376
|
+
fmt.info(t("bootstrap.resumeNeedsReview"));
|
|
377
|
+
for (const field of profile.missingFields || []) {
|
|
378
|
+
fmt.bullet(t(`bootstrap.field.${field}`) || field);
|
|
379
|
+
}
|
|
380
|
+
fmt.blank();
|
|
381
|
+
fmt.hint(t("bootstrap.next.directPending"));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
fmt.blank();
|
|
385
|
+
return profile;
|
|
386
|
+
}
|
|
387
|
+
fmt.blank();
|
|
388
|
+
if (resumed.reason === "missing_agent_artifacts") {
|
|
389
|
+
fmt.warn(t("bootstrap.resumeAwaitingArtifacts"));
|
|
390
|
+
} else if (resumed.reason === "empty_intake_and_spec") {
|
|
391
|
+
fmt.warn(t("bootstrap.resumeEmptyArtifacts"));
|
|
392
|
+
} else {
|
|
393
|
+
fmt.info(t("bootstrap.awaitingAgent"));
|
|
312
394
|
}
|
|
313
|
-
|
|
395
|
+
fmt.hint(t("bootstrap.next.handoff"));
|
|
396
|
+
fmt.blank();
|
|
314
397
|
return control.meta.opera.bootstrap;
|
|
315
398
|
}
|
|
316
399
|
|
|
317
|
-
const profile = await bootstrap.collectBootstrapProfile(context, control, options);
|
|
400
|
+
const profile = await bootstrap.collectBootstrapProfile(context, control, options);
|
|
401
|
+
if (profile.mode && profile.routeReason) {
|
|
402
|
+
fmt.blank();
|
|
403
|
+
fmt.info(t("bootstrap.routingMode", {
|
|
404
|
+
mode: formatBootstrapMode(profile.mode),
|
|
405
|
+
reason: formatBootstrapReason(profile.routeReason),
|
|
406
|
+
}));
|
|
407
|
+
}
|
|
318
408
|
const updatedControl = bootstrap.applyBootstrap(context, control, profile);
|
|
319
409
|
env.syncEnvironment(context, updatedControl, { requiredKeys: env.inferRequiredKeys(updatedControl, context) });
|
|
320
410
|
const ops = require("./control");
|
|
321
411
|
ops.syncDocs(context, updatedControl);
|
|
322
412
|
ops.refreshRepoRuntime(context, { quiet: true });
|
|
323
413
|
|
|
414
|
+
fmt.header(t("bootstrap.resultHeader"));
|
|
415
|
+
|
|
324
416
|
if (profile.mode === "agent_handoff") {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
417
|
+
fmt.info(t("bootstrap.awaitingAgent"));
|
|
418
|
+
fmt.blank();
|
|
419
|
+
fmt.step(t("bootstrap.handoffFile"), profile.handoffFiles?.markdown || bootstrap.bootstrapRelativePaths(context).markdown);
|
|
420
|
+
fmt.blank();
|
|
421
|
+
fmt.step(t("bootstrap.next.label"));
|
|
422
|
+
fmt.hint(t("bootstrap.next.handoff"));
|
|
328
423
|
} else {
|
|
329
|
-
|
|
330
|
-
|
|
424
|
+
fmt.success(profile.status === "completed" ? t("bootstrap.completed") : t("bootstrap.pending"));
|
|
425
|
+
fmt.blank();
|
|
426
|
+
fmt.step(t("bootstrap.next.label"));
|
|
427
|
+
fmt.hint(
|
|
331
428
|
profile.status === "completed"
|
|
332
429
|
? t("bootstrap.next.directCompleted")
|
|
333
430
|
: t("bootstrap.next.directPending"),
|
|
334
431
|
);
|
|
335
432
|
}
|
|
433
|
+
fmt.blank();
|
|
336
434
|
return profile;
|
|
337
435
|
}
|
|
338
436
|
|
|
@@ -346,39 +444,59 @@ function status(root) {
|
|
|
346
444
|
return;
|
|
347
445
|
}
|
|
348
446
|
|
|
349
|
-
const opera = control.meta.opera;
|
|
350
|
-
const bootstrapState = opera.bootstrap || bootstrap.detectLegacyBootstrap(context, control);
|
|
351
|
-
const localeDoctor = runtimeState.doctorLocale(control.meta?.locale || null);
|
|
352
|
-
console.log(t("opera.status.version", { version: opera.version }));
|
|
353
|
-
console.log(t("opera.status.installed", { value: opera.installedAt }));
|
|
354
|
-
console.log(t("opera.status.skills", { value: (opera.skills || []).join(", ") || t("locale.none") }));
|
|
355
|
-
console.log(t("opera.status.locale", { locale: config.getLocale(control), source: formatLocaleSource(localeDoctor.source) }));
|
|
356
|
-
console.log(t("opera.status.legacy", { value: opera.legacyStatus || bootstrapState?.status || "supported" }));
|
|
357
|
-
console.log(t("opera.status.contractVersion", { value: opera.contractVersion || t("locale.none") }));
|
|
358
|
-
console.log(t("opera.status.contractReadiness", { value: opera.contractReadiness || "hypothesis" }));
|
|
359
|
-
|
|
360
|
-
if (bootstrapState) {
|
|
361
|
-
console.log(t("opera.status.bootstrap", { value: bootstrapState.status }));
|
|
362
|
-
if (bootstrapState.mode) {
|
|
363
|
-
console.log(t("opera.status.mode", { value: bootstrapState.mode }));
|
|
364
|
-
}
|
|
365
|
-
if (bootstrapState.routeReason) {
|
|
366
|
-
console.log(t("opera.status.route", { value: bootstrapState.routeReason }));
|
|
367
|
-
}
|
|
368
|
-
if (bootstrapState.decisionOwnership) {
|
|
369
|
-
console.log(t("opera.status.ownership", { value: bootstrapState.decisionOwnership }));
|
|
370
|
-
}
|
|
371
|
-
if ((bootstrapState.missingFields || []).length) {
|
|
372
|
-
console.log(t("opera.status.missing", { value: bootstrapState.missingFields
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
447
|
+
const opera = control.meta.opera;
|
|
448
|
+
const bootstrapState = opera.bootstrap || bootstrap.detectLegacyBootstrap(context, control);
|
|
449
|
+
const localeDoctor = runtimeState.doctorLocale(control.meta?.locale || null);
|
|
450
|
+
console.log(t("opera.status.version", { version: opera.version }));
|
|
451
|
+
console.log(t("opera.status.installed", { value: opera.installedAt }));
|
|
452
|
+
console.log(t("opera.status.skills", { value: (opera.skills || []).join(", ") || t("locale.none") }));
|
|
453
|
+
console.log(t("opera.status.locale", { locale: config.getLocale(control), source: formatLocaleSource(localeDoctor.source) }));
|
|
454
|
+
console.log(t("opera.status.legacy", { value: formatLegacyStatus(opera.legacyStatus || bootstrapState?.status || "supported") }));
|
|
455
|
+
console.log(t("opera.status.contractVersion", { value: opera.contractVersion || t("locale.none") }));
|
|
456
|
+
console.log(t("opera.status.contractReadiness", { value: formatContractReadiness(opera.contractReadiness || "hypothesis") }));
|
|
457
|
+
|
|
458
|
+
if (bootstrapState) {
|
|
459
|
+
console.log(t("opera.status.bootstrap", { value: formatBootstrapStatus(bootstrapState.status) }));
|
|
460
|
+
if (bootstrapState.mode) {
|
|
461
|
+
console.log(t("opera.status.mode", { value: formatBootstrapMode(bootstrapState.mode) }));
|
|
462
|
+
}
|
|
463
|
+
if (bootstrapState.routeReason) {
|
|
464
|
+
console.log(t("opera.status.route", { value: formatBootstrapReason(bootstrapState.routeReason) }));
|
|
465
|
+
}
|
|
466
|
+
if (bootstrapState.decisionOwnership) {
|
|
467
|
+
console.log(t("opera.status.ownership", { value: formatDecisionOwnership(bootstrapState.decisionOwnership) }));
|
|
468
|
+
}
|
|
469
|
+
if ((bootstrapState.missingFields || []).length) {
|
|
470
|
+
console.log(t("opera.status.missing", { value: formatMissingFields(bootstrapState.missingFields) }));
|
|
471
|
+
if (bootstrapState.mode === "agent_handoff") {
|
|
472
|
+
console.log(t("opera.status.awaitingAgentExplanation"));
|
|
473
|
+
console.log(t("opera.status.awaitingAgentAction"));
|
|
474
|
+
} else {
|
|
475
|
+
console.log(t("opera.status.directExplanation"));
|
|
476
|
+
console.log(t("opera.status.directAction", {
|
|
477
|
+
intake: bootstrapState.intakeFiles?.json || bootstrap.bootstrapRelativePaths(context).intakeJson,
|
|
478
|
+
spec: bootstrapState.intakeFiles?.specDossier || bootstrap.bootstrapRelativePaths(context).specDossier,
|
|
479
|
+
}));
|
|
480
|
+
}
|
|
481
|
+
console.log(t("opera.status.contractNotGenerated"));
|
|
482
|
+
console.log(t("opera.status.resume"));
|
|
483
|
+
}
|
|
484
|
+
if (bootstrapState.mode === "agent_handoff" && relativePathExists(context, bootstrapState.handoffFiles?.markdown)) {
|
|
485
|
+
console.log(t("opera.status.handoff", { value: bootstrapState.handoffFiles.markdown }));
|
|
486
|
+
}
|
|
487
|
+
if (relativePathExists(context, bootstrapState.intakeFiles?.json)) {
|
|
488
|
+
console.log(t("opera.status.intake", { value: bootstrapState.intakeFiles.json }));
|
|
489
|
+
}
|
|
490
|
+
if (relativePathExists(context, bootstrapState.intakeFiles?.specDossier)) {
|
|
491
|
+
console.log(t("opera.status.specDossier", { value: bootstrapState.intakeFiles.specDossier }));
|
|
492
|
+
}
|
|
493
|
+
if (relativePathExists(context, bootstrapState.reviewFiles?.openQuestions)) {
|
|
494
|
+
console.log(t("opera.status.openQuestions", { value: bootstrapState.reviewFiles.openQuestions }));
|
|
495
|
+
}
|
|
496
|
+
if (relativePathExists(context, bootstrapState.reviewFiles?.qualityReport)) {
|
|
497
|
+
console.log(t("opera.status.qualityReport", { value: bootstrapState.reviewFiles.qualityReport }));
|
|
498
|
+
}
|
|
499
|
+
}
|
|
382
500
|
|
|
383
501
|
const checks = [
|
|
384
502
|
[context.layout === "split" ? "ops/.agent/hub/agent.md" : ".agent/hub/agent.md", fs.existsSync(path.join(context.paths.agentHubDir, "agent.md"))],
|
|
@@ -389,11 +507,11 @@ function status(root) {
|
|
|
389
507
|
[context.layout === "split" ? "ops/policy/autonomy.json" : "policy/autonomy.json", fs.existsSync(context.paths.autonomyPolicyFile)],
|
|
390
508
|
];
|
|
391
509
|
|
|
392
|
-
console.log(t("opera.status.structure"));
|
|
393
|
-
for (const [file, exists] of checks) {
|
|
394
|
-
console.log(` ${exists
|
|
395
|
-
}
|
|
396
|
-
}
|
|
510
|
+
console.log(t("opera.status.structure"));
|
|
511
|
+
for (const [file, exists] of checks) {
|
|
512
|
+
console.log(` ${fmt.boolToken(exists)} ${file}`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
397
515
|
|
|
398
516
|
function configure(root, args) {
|
|
399
517
|
const context = config.ensureContext(root);
|
|
@@ -467,10 +585,13 @@ function upgrade(root, args = []) {
|
|
|
467
585
|
removePath(context.paths.bootstrapDir);
|
|
468
586
|
}
|
|
469
587
|
|
|
470
|
-
installStructure(context, control, locale, { rewriteLocalizedTemplates: true });
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
588
|
+
installStructure(context, control, locale, { rewriteLocalizedTemplates: true });
|
|
589
|
+
require("./quality").backfillBootstrapMetadata(context, control, {
|
|
590
|
+
persist: true,
|
|
591
|
+
});
|
|
592
|
+
control.meta.opera = {
|
|
593
|
+
...(control.meta.opera || {}),
|
|
594
|
+
installed: true,
|
|
474
595
|
model: "v3",
|
|
475
596
|
stableTag: "stable",
|
|
476
597
|
version: OPERA_VERSION,
|
|
@@ -574,33 +695,77 @@ async function cmdBootstrap(root, args) {
|
|
|
574
695
|
return runBootstrap(root, options);
|
|
575
696
|
}
|
|
576
697
|
|
|
577
|
-
function cmdHandoff(root, args) {
|
|
698
|
+
function cmdHandoff(root, args) {
|
|
578
699
|
const context = config.ensureContext(root);
|
|
579
700
|
const control = config.loadControl(context);
|
|
580
701
|
const state = bootstrap.getBootstrapState(control, context) || bootstrap.detectLegacyBootstrap(context, control);
|
|
581
702
|
if (!state) {
|
|
582
703
|
throw new Error("OPERA bootstrap is not initialized.");
|
|
583
704
|
}
|
|
584
|
-
const files = bootstrap.bootstrapFilePaths(context);
|
|
585
|
-
const printMode = (args || []).includes("--print");
|
|
586
|
-
const jsonMode = (args || []).includes("--json");
|
|
587
|
-
if (
|
|
588
|
-
const payload =
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
705
|
+
const files = bootstrap.bootstrapFilePaths(context);
|
|
706
|
+
const printMode = (args || []).includes("--print");
|
|
707
|
+
const jsonMode = (args || []).includes("--json");
|
|
708
|
+
if (state.mode !== "agent_handoff") {
|
|
709
|
+
const payload = {
|
|
710
|
+
mode: state.mode,
|
|
711
|
+
status: state.status,
|
|
712
|
+
files: {
|
|
713
|
+
intakeJson: state.intakeFiles?.json || bootstrap.bootstrapRelativePaths(context).intakeJson,
|
|
714
|
+
specDossier: state.intakeFiles?.specDossier || bootstrap.bootstrapRelativePaths(context).specDossier,
|
|
715
|
+
openQuestions: state.reviewFiles?.openQuestions || bootstrap.bootstrapRelativePaths(context).openQuestions,
|
|
716
|
+
qualityReport: state.reviewFiles?.qualityReport || bootstrap.bootstrapRelativePaths(context).qualityReport,
|
|
717
|
+
},
|
|
718
|
+
nextStep: t("opera.handoff.directNext"),
|
|
719
|
+
};
|
|
720
|
+
if (jsonMode) {
|
|
721
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (printMode) {
|
|
725
|
+
process.stdout.write([
|
|
726
|
+
`# ${t("opera.handoff.directTitle")}`,
|
|
727
|
+
"",
|
|
728
|
+
`- ${t("opera.handoff.directStatus")}: ${formatBootstrapStatus(state.status)}`,
|
|
729
|
+
`- ${t("opera.handoff.directIntake")}: ${payload.files.intakeJson}`,
|
|
730
|
+
`- ${t("opera.handoff.directSpec")}: ${payload.files.specDossier}`,
|
|
731
|
+
`- ${t("opera.handoff.directQuestions")}: ${payload.files.openQuestions}`,
|
|
732
|
+
`- ${t("opera.handoff.directQuality")}: ${payload.files.qualityReport}`,
|
|
733
|
+
"",
|
|
734
|
+
t("opera.handoff.directNext"),
|
|
735
|
+
"",
|
|
736
|
+
].join("\n"));
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
console.log(t("opera.handoff.directSummary", { status: formatBootstrapStatus(state.status) }));
|
|
740
|
+
console.log(t("opera.status.intake", { value: payload.files.intakeJson }));
|
|
741
|
+
console.log(t("opera.status.specDossier", { value: payload.files.specDossier }));
|
|
742
|
+
if (relativePathExists(context, payload.files.openQuestions)) {
|
|
743
|
+
console.log(t("opera.status.openQuestions", { value: payload.files.openQuestions }));
|
|
744
|
+
}
|
|
745
|
+
if (relativePathExists(context, payload.files.qualityReport)) {
|
|
746
|
+
console.log(t("opera.status.qualityReport", { value: payload.files.qualityReport }));
|
|
747
|
+
}
|
|
748
|
+
console.log(t("opera.status.resume"));
|
|
749
|
+
console.log(t("opera.handoff.directNext"));
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (jsonMode) {
|
|
753
|
+
const payload = readText(files.json);
|
|
754
|
+
process.stdout.write(payload || "{}\n");
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
592
757
|
if (printMode) {
|
|
593
758
|
process.stdout.write(readText(files.markdown) || "");
|
|
594
759
|
return;
|
|
595
760
|
}
|
|
596
|
-
console.log(
|
|
597
|
-
console.log(
|
|
598
|
-
console.log(
|
|
599
|
-
console.log(
|
|
600
|
-
if (state.reviewFiles?.openQuestions) {
|
|
601
|
-
console.log(
|
|
602
|
-
}
|
|
603
|
-
}
|
|
761
|
+
console.log(t("opera.handoff.summary", { status: formatBootstrapStatus(state.status) }));
|
|
762
|
+
console.log(t("opera.handoff.mode", { mode: formatBootstrapMode(state.mode) }));
|
|
763
|
+
console.log(t("opera.handoff.markdown", { value: state.handoffFiles?.markdown || bootstrap.bootstrapRelativePaths(context).markdown }));
|
|
764
|
+
console.log(t("opera.handoff.json", { value: state.handoffFiles?.json || bootstrap.bootstrapRelativePaths(context).json }));
|
|
765
|
+
if (relativePathExists(context, state.reviewFiles?.openQuestions)) {
|
|
766
|
+
console.log(t("opera.status.openQuestions", { value: state.reviewFiles.openQuestions }));
|
|
767
|
+
}
|
|
768
|
+
}
|
|
604
769
|
|
|
605
770
|
module.exports = {
|
|
606
771
|
installStructure,
|