coding-agent-harness 1.0.4 → 1.0.6
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/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +2 -2
- package/LICENSE +661 -21
- package/LICENSE-EXCEPTION.md +37 -0
- package/README.md +96 -4
- package/README.zh-CN.md +75 -4
- package/SKILL.md +52 -51
- package/dist/build-dist.mjs +189 -0
- package/dist/check-dist-observation.mjs +428 -0
- package/dist/check-harness.mjs +489 -0
- package/dist/check-import-graph.mjs +511 -0
- package/dist/check-runtime-emit.mjs +304 -0
- package/dist/check-type-boundaries.mjs +139 -0
- package/dist/commands/dashboard-command.mjs +80 -0
- package/dist/commands/migration-command.mjs +152 -0
- package/dist/commands/preset-command.mjs +91 -0
- package/dist/commands/task-command.mjs +324 -0
- package/dist/harness.mjs +304 -0
- package/dist/lib/capability-registry.mjs +643 -0
- package/dist/lib/check-module-parallel.mjs +227 -0
- package/dist/lib/check-profiles.mjs +414 -0
- package/dist/lib/check-task-contracts.mjs +54 -0
- package/dist/lib/core-shared.mjs +254 -0
- package/dist/lib/dashboard-data.mjs +608 -0
- package/dist/lib/dashboard-workbench.mjs +334 -0
- package/dist/lib/dashboard-writer.mjs +200 -0
- package/dist/lib/git-status-summary.mjs +45 -0
- package/dist/lib/governance-index-generator.mjs +236 -0
- package/dist/lib/governance-sync.mjs +617 -0
- package/dist/lib/governance-table-boundary.mjs +161 -0
- package/{scripts → dist}/lib/harness-core.mjs +3 -0
- package/dist/lib/harness-paths.mjs +338 -0
- package/dist/lib/lesson-maintenance.mjs +139 -0
- package/dist/lib/markdown-utils.mjs +193 -0
- package/dist/lib/migration-planner.mjs +439 -0
- package/dist/lib/migration-support.mjs +317 -0
- package/dist/lib/phase-kind.mjs +46 -0
- package/dist/lib/preset-audit-contracts.mjs +40 -0
- package/dist/lib/preset-engine.mjs +516 -0
- package/dist/lib/preset-registry.mjs +831 -0
- package/dist/lib/preset-resource-contracts.mjs +83 -0
- package/dist/lib/review-confirm-git-gate.mjs +244 -0
- package/dist/lib/status-builder.mjs +87 -0
- package/{scripts → dist}/lib/status-dashboard-renderer.mjs +48 -47
- package/dist/lib/structure-migration.mjs +404 -0
- package/dist/lib/subagent-authorization-audit.mjs +198 -0
- package/dist/lib/task-audit-metadata.mjs +376 -0
- package/dist/lib/task-audit-migration.mjs +355 -0
- package/dist/lib/task-completion-consistency.mjs +29 -0
- package/dist/lib/task-index.mjs +133 -0
- package/dist/lib/task-lesson-candidates.mjs +239 -0
- package/dist/lib/task-lesson-sedimentation.mjs +300 -0
- package/dist/lib/task-lifecycle/create-task-helpers.mjs +84 -0
- package/dist/lib/task-lifecycle/phase-sync.mjs +82 -0
- package/dist/lib/task-lifecycle/review-confirm.mjs +93 -0
- package/dist/lib/task-lifecycle/review-gates.mjs +62 -0
- package/dist/lib/task-lifecycle/review-submission.mjs +52 -0
- package/dist/lib/task-lifecycle/scaffold-provenance.mjs +54 -0
- package/dist/lib/task-lifecycle/template-files.mjs +52 -0
- package/dist/lib/task-lifecycle/text-utils.mjs +26 -0
- package/dist/lib/task-lifecycle.mjs +611 -0
- package/dist/lib/task-metadata.mjs +116 -0
- package/dist/lib/task-review-model.mjs +474 -0
- package/dist/lib/task-scanner.mjs +439 -0
- package/dist/lib/task-tombstone-commands.mjs +125 -0
- package/dist/postinstall.mjs +14 -0
- package/dist/run-built-tests.mjs +84 -0
- package/docs-release/README.md +1 -0
- package/docs-release/architecture/overview.md +13 -13
- package/docs-release/architecture/overview.zh-CN.md +13 -13
- package/docs-release/architecture/system-explainer/01-system-overview.md +218 -0
- package/docs-release/architecture/system-explainer/02-module-dependency.md +257 -0
- package/docs-release/architecture/system-explainer/03-task-lifecycle.md +304 -0
- package/docs-release/architecture/system-explainer/04-check-and-governance.md +241 -0
- package/docs-release/architecture/system-explainer/05-data-flow.md +276 -0
- package/docs-release/architecture/system-explainer/06-preset-and-migration.md +300 -0
- package/docs-release/architecture/system-explainer/README.md +67 -0
- package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +227 -0
- package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +263 -0
- package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +319 -0
- package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +252 -0
- package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +290 -0
- package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +320 -0
- package/docs-release/architecture/system-explainer/en-US/README.md +70 -0
- package/docs-release/guides/agent-installation.en-US.md +22 -15
- package/docs-release/guides/agent-installation.md +23 -15
- package/docs-release/guides/contributing.md +3 -3
- package/docs-release/guides/contributing.zh-CN.md +3 -3
- package/docs-release/guides/document-audience-and-surfaces.en-US.md +10 -10
- package/docs-release/guides/document-audience-and-surfaces.md +10 -10
- package/docs-release/guides/legacy-migration-agent-prompt.md +25 -2
- package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +25 -2
- package/docs-release/guides/migration-playbook.en-US.md +63 -1
- package/docs-release/guides/migration-playbook.md +59 -1
- package/docs-release/guides/parent-control-repository-pattern.en-US.md +25 -25
- package/docs-release/guides/parent-control-repository-pattern.md +25 -25
- package/docs-release/guides/preset-development.md +28 -4
- package/docs-release/guides/repository-operating-models.en-US.md +21 -21
- package/docs-release/guides/repository-operating-models.md +21 -21
- package/docs-release/guides/task-state-machine.en-US.md +35 -18
- package/docs-release/guides/task-state-machine.md +35 -18
- package/docs-release/guides/typescript-runtime-migration-closeout.md +96 -0
- package/examples/minimal-project/AGENTS.md +2 -2
- package/examples/minimal-project/coding-agent-harness/harness.yaml +14 -0
- package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/INDEX.md +60 -0
- package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/progress.md +11 -0
- package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/review.md +1 -1
- package/package.json +22 -13
- package/presets/legacy-migration/preset.yaml +5 -5
- package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
- package/presets/lesson-sedimentation/preset.yaml +3 -3
- package/presets/module/preset.yaml +2 -2
- package/presets/module/templates/execution_strategy.append.md +1 -1
- package/presets/module/templates/task_plan.append.md +3 -3
- package/presets/standard-task/preset.yaml +2 -2
- package/references/adversarial-review-standard.md +2 -2
- package/references/agents-md-pattern.md +14 -14
- package/references/cadence-ledger.md +1 -1
- package/references/ci-cd-standard.md +1 -1
- package/references/delivery-operating-model-standard.md +4 -4
- package/references/docs-directory-standard.md +65 -159
- package/references/external-source-intake-standard.md +10 -10
- package/references/harness-ledger.md +6 -6
- package/references/legacy-12-phase-bootstrap.md +2 -2
- package/references/lessons-governance.md +15 -15
- package/references/long-running-task-standard.md +6 -6
- package/references/module-parallel-standard.md +34 -34
- package/references/planning-loop.md +6 -6
- package/references/project-onboarding-audit.md +4 -4
- package/references/regression-system.md +2 -2
- package/references/repo-governance-standard.md +4 -4
- package/references/review-routing-standard.md +1 -1
- package/references/ssot-governance.md +19 -19
- package/references/taskr-gap-analysis.md +5 -5
- package/references/walkthrough-closeout.md +14 -14
- package/references/worktree-parallel.md +3 -3
- package/skills/preset-creator/references/complex-task-skeleton/brief.md +11 -0
- package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +1 -1
- package/skills/preset-creator/references/preset-package-skeleton.md +5 -5
- package/templates/AGENTS.md.template +31 -29
- package/templates/architecture/README.md +4 -4
- package/templates/architecture/service-catalog.md +2 -2
- package/templates/architecture/services/service-template.md +1 -1
- package/templates/dashboard/assets/app-src/00-state.js +12 -0
- package/templates/dashboard/assets/app-src/10-router.js +3 -0
- package/templates/dashboard/assets/app-src/20-overview.js +13 -3
- package/templates/dashboard/assets/app-src/35-task-detail.js +46 -6
- package/templates/dashboard/assets/app-src/40-modules.js +1 -1
- package/templates/dashboard/assets/app-src/55-presets.js +375 -0
- package/templates/dashboard/assets/app-src/60-shared.js +3 -1
- package/templates/dashboard/assets/app-src/90-bindings.js +131 -0
- package/templates/dashboard/assets/app.css +583 -0
- package/templates/dashboard/assets/app.css.manifest.json +1 -0
- package/templates/dashboard/assets/app.js +585 -11
- package/templates/dashboard/assets/app.manifest.json +1 -0
- package/templates/dashboard/assets/css-src/00-foundation.css +4 -0
- package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +62 -0
- package/templates/dashboard/assets/css-src/45-presets.css +516 -0
- package/templates/dashboard/assets/i18n.js +144 -4
- package/templates/development/README.md +10 -10
- package/templates/development/cross-repo-debugging.md +3 -3
- package/templates/development/external-context/service-template.md +2 -2
- package/templates/development/external-source-packs/README.md +4 -4
- package/templates/integrations/README.md +4 -4
- package/templates/integrations/api-contract.md +2 -2
- package/templates/integrations/event-contract.md +2 -2
- package/templates/integrations/third-party/vendor-template.md +2 -2
- package/templates/integrations/webhook-contract.md +2 -2
- package/templates/ledger/Harness-Ledger.md +1 -1
- package/templates/planning/INDEX.md +88 -0
- package/templates/planning/brief.md +1 -1
- package/templates/planning/module_session_prompt.md +2 -1
- package/templates/planning/review.md +0 -18
- package/templates/planning/task_plan.md +5 -44
- package/templates/planning/visual_map.md +13 -9
- package/templates/planning/visual_map.simple.md +52 -0
- package/templates/planning/walkthrough.md +47 -0
- package/templates/reference/docs-library-standard.md +8 -8
- package/templates/reference/execution-workflow-standard.md +29 -2
- package/templates/reference/external-source-intake-standard.md +15 -15
- package/templates/reference/repo-governance-standard.md +1 -1
- package/templates/ssot/Module-Registry.md +1 -1
- package/templates/walkthrough/walkthrough-template.md +2 -2
- package/templates-zh-CN/AGENTS.md.template +31 -29
- package/templates-zh-CN/CLAUDE.md.template +1 -1
- package/templates-zh-CN/architecture/README.md +4 -4
- package/templates-zh-CN/architecture/service-catalog.md +2 -2
- package/templates-zh-CN/architecture/services/service-template.md +1 -1
- package/templates-zh-CN/development/README.md +10 -10
- package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
- package/templates-zh-CN/development/external-context/service-template.md +2 -2
- package/templates-zh-CN/development/external-source-packs/README.md +4 -4
- package/templates-zh-CN/integrations/README.md +4 -4
- package/templates-zh-CN/integrations/api-contract.md +2 -2
- package/templates-zh-CN/integrations/event-contract.md +2 -2
- package/templates-zh-CN/integrations/third-party/vendor-template.md +2 -2
- package/templates-zh-CN/integrations/webhook-contract.md +2 -2
- package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
- package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
- package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
- package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
- package/templates-zh-CN/planning/INDEX.md +87 -0
- package/templates-zh-CN/planning/brief.md +1 -1
- package/templates-zh-CN/planning/module_session_prompt.md +12 -11
- package/templates-zh-CN/planning/review.md +0 -18
- package/templates-zh-CN/planning/task_plan.md +3 -63
- package/templates-zh-CN/planning/visual_map.md +14 -7
- package/templates-zh-CN/planning/visual_map.simple.md +48 -0
- package/templates-zh-CN/planning/walkthrough.md +47 -0
- package/templates-zh-CN/reference/adversarial-review-standard.md +2 -2
- package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
- package/templates-zh-CN/reference/docs-library-standard.md +28 -28
- package/templates-zh-CN/reference/execution-workflow-standard.md +32 -7
- package/templates-zh-CN/reference/external-source-intake-standard.md +16 -16
- package/templates-zh-CN/reference/harness-ledger-standard.md +6 -6
- package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
- package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
- package/templates-zh-CN/reference/review-routing-standard.md +1 -1
- package/templates-zh-CN/reference/walkthrough-standard.md +7 -7
- package/templates-zh-CN/reference/worktree-standard.md +1 -1
- package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
- package/templates-zh-CN/ssot/Delivery-SSoT.md +3 -3
- package/templates-zh-CN/ssot/Module-Registry.md +3 -3
- package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
- package/templates-zh-CN/walkthrough/walkthrough-template.md +5 -5
- package/tsconfig.dist.json +16 -0
- package/tsconfig.json +25 -0
- package/tsconfig.runtime.json +24 -0
- package/examples/minimal-project/.harness-capabilities.json +0 -8
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +0 -11
- package/scripts/check-harness.mjs +0 -508
- package/scripts/commands/dashboard-command.mjs +0 -67
- package/scripts/commands/migration-command.mjs +0 -96
- package/scripts/commands/preset-command.mjs +0 -73
- package/scripts/commands/task-command.mjs +0 -327
- package/scripts/harness.mjs +0 -287
- package/scripts/lib/capability-registry.mjs +0 -591
- package/scripts/lib/check-module-parallel.mjs +0 -237
- package/scripts/lib/check-profiles.mjs +0 -418
- package/scripts/lib/check-task-contracts.mjs +0 -47
- package/scripts/lib/core-shared.mjs +0 -196
- package/scripts/lib/dashboard-data.mjs +0 -412
- package/scripts/lib/dashboard-workbench.mjs +0 -257
- package/scripts/lib/dashboard-writer.mjs +0 -198
- package/scripts/lib/git-status-summary.mjs +0 -46
- package/scripts/lib/governance-index-generator.mjs +0 -174
- package/scripts/lib/governance-sync.mjs +0 -514
- package/scripts/lib/governance-table-boundary.mjs +0 -175
- package/scripts/lib/lesson-maintenance.mjs +0 -152
- package/scripts/lib/markdown-utils.mjs +0 -158
- package/scripts/lib/migration-planner.mjs +0 -478
- package/scripts/lib/migration-support.mjs +0 -312
- package/scripts/lib/preset-audit-contracts.mjs +0 -37
- package/scripts/lib/preset-engine.mjs +0 -497
- package/scripts/lib/preset-registry.mjs +0 -627
- package/scripts/lib/preset-resource-contracts.mjs +0 -83
- package/scripts/lib/review-confirm-git-gate.mjs +0 -248
- package/scripts/lib/subagent-authorization-audit.mjs +0 -196
- package/scripts/lib/task-completion-consistency.mjs +0 -16
- package/scripts/lib/task-index.mjs +0 -93
- package/scripts/lib/task-lesson-candidates.mjs +0 -242
- package/scripts/lib/task-lesson-sedimentation.mjs +0 -326
- package/scripts/lib/task-lifecycle/review-confirm.mjs +0 -101
- package/scripts/lib/task-lifecycle/review-gates.mjs +0 -70
- package/scripts/lib/task-lifecycle/text-utils.mjs +0 -24
- package/scripts/lib/task-lifecycle.mjs +0 -649
- package/scripts/lib/task-review-model.mjs +0 -469
- package/scripts/lib/task-scanner.mjs +0 -576
- package/scripts/lib/task-tombstone-commands.mjs +0 -140
- package/scripts/postinstall.mjs +0 -14
- package/templates/walkthrough/Closeout-SSoT.md +0 -43
- package/templates-zh-CN/walkthrough/Closeout-SSoT.md +0 -42
- /package/examples/minimal-project/{docs → coding-agent-harness/governance/generated}/Harness-Ledger.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/brief.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/execution_strategy.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/findings.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/lesson_candidates.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/task_plan.md +0 -0
- /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/visual_map.md +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { normalizeLocale, normalizeTarget, readJsonSafe, toPosix, } from "./core-shared.mjs";
|
|
5
|
+
import { legacyModuleRoot, legacyPath, v2HarnessRoot, } from "./harness-paths.mjs";
|
|
6
|
+
const legacyMappings = [
|
|
7
|
+
["03-ARCHITECTURE", `${v2HarnessRoot}/context/architecture`],
|
|
8
|
+
["04-DEVELOPMENT", `${v2HarnessRoot}/context/development`],
|
|
9
|
+
["06-INTEGRATIONS", `${v2HarnessRoot}/context/integrations`],
|
|
10
|
+
["05-TEST-QA", `${v2HarnessRoot}/governance/regression`],
|
|
11
|
+
["11-REFERENCE", `${v2HarnessRoot}/governance/standards`],
|
|
12
|
+
["09-PLANNING/TASKS", `${v2HarnessRoot}/planning/tasks`],
|
|
13
|
+
["09-PLANNING/MODULES", `${v2HarnessRoot}/planning/modules`],
|
|
14
|
+
["09-PLANNING/Module-Registry.md", `${v2HarnessRoot}/planning/modules/Module-Registry.md`],
|
|
15
|
+
["10-WALKTHROUGH", `${v2HarnessRoot}/governance/archive/legacy-walkthrough`],
|
|
16
|
+
["Harness-Ledger.md", `${v2HarnessRoot}/governance/archive/legacy-governance/Harness-Ledger.md`],
|
|
17
|
+
];
|
|
18
|
+
export function planStructureMigration(targetInput = ".") {
|
|
19
|
+
const target = normalizeTarget(targetInput);
|
|
20
|
+
const legacyDocsRoot = path.join(target.projectRoot, "docs");
|
|
21
|
+
const manifestPath = path.join(target.projectRoot, v2HarnessRoot, "harness.yaml");
|
|
22
|
+
const capabilities = readLegacyCapabilities(target.projectRoot);
|
|
23
|
+
const actions = [];
|
|
24
|
+
if (!fs.existsSync(legacyDocsRoot)) {
|
|
25
|
+
actions.push({
|
|
26
|
+
action: fs.existsSync(manifestPath) ? "already-v2" : "create-v2-manifest",
|
|
27
|
+
source: "",
|
|
28
|
+
destination: toPosix(path.relative(target.projectRoot, manifestPath)),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
for (const [legacyRelative, v2Relative] of legacyMappings) {
|
|
32
|
+
const source = path.join(legacyDocsRoot, legacyRelative);
|
|
33
|
+
if (!fs.existsSync(source))
|
|
34
|
+
continue;
|
|
35
|
+
actions.push({
|
|
36
|
+
action: "move",
|
|
37
|
+
source: toPosix(path.relative(target.projectRoot, source)),
|
|
38
|
+
destination: v2Relative,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const archiveDestination = `${v2HarnessRoot}/governance/archive/legacy-docs`;
|
|
42
|
+
if (fs.existsSync(legacyDocsRoot)) {
|
|
43
|
+
actions.push({
|
|
44
|
+
action: "archive-source-root",
|
|
45
|
+
source: "docs",
|
|
46
|
+
destination: archiveDestination,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
for (const relative of generatedTemplateDirs()) {
|
|
50
|
+
const legacyEquivalent = relative
|
|
51
|
+
.replace(`${v2HarnessRoot}/planning/tasks`, "09-PLANNING/TASKS")
|
|
52
|
+
.replace(`${v2HarnessRoot}/planning/modules`, "09-PLANNING/MODULES");
|
|
53
|
+
if (fs.existsSync(path.join(target.projectRoot, relative)) || fs.existsSync(path.join(legacyDocsRoot, legacyEquivalent))) {
|
|
54
|
+
actions.push({
|
|
55
|
+
action: "remove-generated-template-dir",
|
|
56
|
+
source: "",
|
|
57
|
+
destination: relative,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const legacyRegistry = path.join(target.projectRoot, ".harness-capabilities.json");
|
|
62
|
+
if (fs.existsSync(legacyRegistry)) {
|
|
63
|
+
actions.push({
|
|
64
|
+
action: "archive-legacy-registry",
|
|
65
|
+
source: ".harness-capabilities.json",
|
|
66
|
+
destination: `${v2HarnessRoot}/governance/archive/legacy-governance/.harness-capabilities.json`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
operation: "migrate-structure",
|
|
71
|
+
target: target.projectRoot,
|
|
72
|
+
mode: fs.existsSync(manifestPath) ? "v2-present" : "legacy-source",
|
|
73
|
+
manifest: toPosix(path.relative(target.projectRoot, manifestPath)),
|
|
74
|
+
capabilities,
|
|
75
|
+
actions,
|
|
76
|
+
summary: {
|
|
77
|
+
actions: actions.length,
|
|
78
|
+
moves: actions.filter((action) => action.action === "move").length,
|
|
79
|
+
willArchiveLegacyDocs: actions.some((action) => action.action === "archive-source-root"),
|
|
80
|
+
canApply: actions.length > 0,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export function applyStructureMigration(targetInput = ".", { force = false } = {}) {
|
|
85
|
+
const plan = planStructureMigration(targetInput);
|
|
86
|
+
const targetRoot = plan.target;
|
|
87
|
+
const manifestPath = path.join(targetRoot, plan.manifest);
|
|
88
|
+
const applied = [];
|
|
89
|
+
preflightStructureMigration(plan, { force });
|
|
90
|
+
if (!fs.existsSync(manifestPath) || force) {
|
|
91
|
+
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
92
|
+
fs.writeFileSync(manifestPath, renderHarnessManifest({ locale: plan.capabilities.locale, capabilities: plan.capabilities.names }));
|
|
93
|
+
applied.push({ action: "write-manifest", destination: plan.manifest });
|
|
94
|
+
}
|
|
95
|
+
for (const action of plan.actions.filter((entry) => entry.action === "move")) {
|
|
96
|
+
const source = path.join(targetRoot, action.source);
|
|
97
|
+
const destination = path.join(targetRoot, action.destination);
|
|
98
|
+
if (!fs.existsSync(source))
|
|
99
|
+
continue;
|
|
100
|
+
if (fs.existsSync(destination) && !force) {
|
|
101
|
+
throw new Error(`Refusing to overwrite existing v2 destination: ${action.destination}`);
|
|
102
|
+
}
|
|
103
|
+
if (fs.existsSync(destination))
|
|
104
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
105
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
106
|
+
fs.cpSync(source, destination, { recursive: true });
|
|
107
|
+
applied.push({ action: "copy", source: action.source, destination: action.destination });
|
|
108
|
+
}
|
|
109
|
+
const docsRoot = path.join(targetRoot, "docs");
|
|
110
|
+
if (fs.existsSync(docsRoot)) {
|
|
111
|
+
const archiveRoot = uniqueArchiveRoot(targetRoot);
|
|
112
|
+
fs.mkdirSync(path.dirname(archiveRoot), { recursive: true });
|
|
113
|
+
fs.renameSync(docsRoot, archiveRoot);
|
|
114
|
+
applied.push({ action: "archive-source-root", source: "docs", destination: toPosix(path.relative(targetRoot, archiveRoot)) });
|
|
115
|
+
}
|
|
116
|
+
archiveLegacyCapabilityRegistry(targetRoot, applied);
|
|
117
|
+
normalizeMigratedModuleTasks(targetRoot, applied, { force });
|
|
118
|
+
scaffoldMissingTaskWalkthroughs(targetRoot, applied);
|
|
119
|
+
removeGeneratedTemplateDirectories(targetRoot, applied);
|
|
120
|
+
return {
|
|
121
|
+
...plan,
|
|
122
|
+
applied: true,
|
|
123
|
+
actionsApplied: applied,
|
|
124
|
+
summary: {
|
|
125
|
+
...plan.summary,
|
|
126
|
+
applied: applied.length,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function removeGeneratedTemplateDirectories(targetRoot, applied) {
|
|
131
|
+
for (const relative of generatedTemplateDirs()) {
|
|
132
|
+
const directory = path.join(targetRoot, relative);
|
|
133
|
+
if (!fs.existsSync(directory))
|
|
134
|
+
continue;
|
|
135
|
+
fs.rmSync(directory, { recursive: true, force: true });
|
|
136
|
+
applied.push({
|
|
137
|
+
action: "remove-generated-template-dir",
|
|
138
|
+
destination: relative,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function generatedTemplateDirs() {
|
|
143
|
+
return [
|
|
144
|
+
`${v2HarnessRoot}/planning/tasks/_task-template`,
|
|
145
|
+
`${v2HarnessRoot}/planning/modules/_task-template`,
|
|
146
|
+
`${v2HarnessRoot}/planning/modules/_module-template`,
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
function preflightStructureMigration(plan, { force = false } = {}) {
|
|
150
|
+
if (force)
|
|
151
|
+
return;
|
|
152
|
+
const conflicts = [];
|
|
153
|
+
for (const action of plan.actions.filter((entry) => entry.action === "move")) {
|
|
154
|
+
const source = path.join(plan.target, action.source);
|
|
155
|
+
const destination = path.join(plan.target, action.destination);
|
|
156
|
+
if (fs.existsSync(source) && fs.existsSync(destination))
|
|
157
|
+
conflicts.push(action.destination);
|
|
158
|
+
}
|
|
159
|
+
conflicts.push(...moduleTaskNormalizationConflicts(plan.target));
|
|
160
|
+
if (conflicts.length) {
|
|
161
|
+
throw new Error(`Refusing to overwrite existing v2 destination(s): ${conflicts.join(", ")}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function moduleTaskNormalizationConflicts(targetRoot) {
|
|
165
|
+
const modulesRoot = path.join(targetRoot, legacyPath(legacyModuleRoot));
|
|
166
|
+
if (!fs.existsSync(modulesRoot))
|
|
167
|
+
return [];
|
|
168
|
+
const conflicts = [];
|
|
169
|
+
for (const moduleName of fs.readdirSync(modulesRoot)) {
|
|
170
|
+
if (moduleName.startsWith("_"))
|
|
171
|
+
continue;
|
|
172
|
+
const moduleDir = path.join(modulesRoot, moduleName);
|
|
173
|
+
if (!fs.statSync(moduleDir).isDirectory())
|
|
174
|
+
continue;
|
|
175
|
+
const legacyTasksRoot = path.join(moduleDir, "TASKS");
|
|
176
|
+
const normalizedTasksRoot = path.join(moduleDir, "tasks");
|
|
177
|
+
if (!hasExactChild(moduleDir, "TASKS") || !hasExactChild(moduleDir, "tasks"))
|
|
178
|
+
continue;
|
|
179
|
+
for (const taskName of fs.readdirSync(legacyTasksRoot)) {
|
|
180
|
+
if (fs.existsSync(path.join(normalizedTasksRoot, taskName))) {
|
|
181
|
+
conflicts.push(`${v2HarnessRoot}/planning/modules/${moduleName}/tasks/${taskName}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return conflicts;
|
|
186
|
+
}
|
|
187
|
+
function hasExactChild(parentDir, childName) {
|
|
188
|
+
return fs.existsSync(parentDir) && fs.readdirSync(parentDir).includes(childName);
|
|
189
|
+
}
|
|
190
|
+
function archiveLegacyCapabilityRegistry(targetRoot, applied) {
|
|
191
|
+
const registry = path.join(targetRoot, ".harness-capabilities.json");
|
|
192
|
+
if (!fs.existsSync(registry))
|
|
193
|
+
return;
|
|
194
|
+
const destination = uniqueArchiveFile(targetRoot, `${v2HarnessRoot}/governance/archive/legacy-governance/.harness-capabilities.json`);
|
|
195
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
196
|
+
fs.renameSync(registry, destination);
|
|
197
|
+
applied.push({
|
|
198
|
+
action: "archive-legacy-registry",
|
|
199
|
+
source: ".harness-capabilities.json",
|
|
200
|
+
destination: toPosix(path.relative(targetRoot, destination)),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function readLegacyCapabilities(projectRoot) {
|
|
204
|
+
const raw = readJsonSafe(path.join(projectRoot, ".harness-capabilities.json"), null);
|
|
205
|
+
const names = new Set(["core"]);
|
|
206
|
+
let locale = "en-US";
|
|
207
|
+
if (raw) {
|
|
208
|
+
locale = normalizeLocale(raw.locale);
|
|
209
|
+
for (const entry of raw.capabilities || [])
|
|
210
|
+
names.add(typeof entry === "string" ? entry : entry.name);
|
|
211
|
+
}
|
|
212
|
+
return { locale, names: [...names].filter(Boolean) };
|
|
213
|
+
}
|
|
214
|
+
function renderHarnessManifest({ locale, capabilities }) {
|
|
215
|
+
return [
|
|
216
|
+
"version: 2",
|
|
217
|
+
`locale: ${normalizeLocale(locale)}`,
|
|
218
|
+
"capabilities:",
|
|
219
|
+
...[...new Set(capabilities)].map((capability) => ` - ${capability}`),
|
|
220
|
+
"structure:",
|
|
221
|
+
` harnessRoot: ${v2HarnessRoot}`,
|
|
222
|
+
` planningRoot: ${v2HarnessRoot}/planning`,
|
|
223
|
+
` tasksRoot: ${v2HarnessRoot}/planning/tasks`,
|
|
224
|
+
` modulesRoot: ${v2HarnessRoot}/planning/modules`,
|
|
225
|
+
` externalRoot: ${v2HarnessRoot}/planning/external`,
|
|
226
|
+
` governanceRoot: ${v2HarnessRoot}/governance`,
|
|
227
|
+
` generatedRoot: ${v2HarnessRoot}/governance/generated`,
|
|
228
|
+
"",
|
|
229
|
+
].join("\n");
|
|
230
|
+
}
|
|
231
|
+
function uniqueArchiveRoot(targetRoot) {
|
|
232
|
+
const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(".", "-");
|
|
233
|
+
const base = path.join(targetRoot, v2HarnessRoot, "governance/archive/legacy-docs", stamp);
|
|
234
|
+
for (let index = 0; index < 100; index += 1) {
|
|
235
|
+
const candidate = index === 0 ? base : `${base}-${String(index).padStart(2, "0")}`;
|
|
236
|
+
if (!fs.existsSync(candidate))
|
|
237
|
+
return candidate;
|
|
238
|
+
}
|
|
239
|
+
throw new Error("Unable to allocate legacy docs archive directory");
|
|
240
|
+
}
|
|
241
|
+
function uniqueArchiveFile(targetRoot, relativeFile) {
|
|
242
|
+
const parsed = path.parse(path.join(targetRoot, relativeFile));
|
|
243
|
+
const base = path.join(parsed.dir, parsed.name);
|
|
244
|
+
for (let index = 0; index < 100; index += 1) {
|
|
245
|
+
const suffix = index === 0 ? "" : `-${String(index).padStart(2, "0")}`;
|
|
246
|
+
const candidate = `${base}${suffix}${parsed.ext}`;
|
|
247
|
+
if (!fs.existsSync(candidate))
|
|
248
|
+
return candidate;
|
|
249
|
+
}
|
|
250
|
+
throw new Error(`Unable to allocate legacy archive file: ${relativeFile}`);
|
|
251
|
+
}
|
|
252
|
+
function normalizeMigratedModuleTasks(targetRoot, applied, { force = false } = {}) {
|
|
253
|
+
const modulesRoot = path.join(targetRoot, v2HarnessRoot, "planning/modules");
|
|
254
|
+
if (!fs.existsSync(modulesRoot))
|
|
255
|
+
return;
|
|
256
|
+
for (const moduleName of fs.readdirSync(modulesRoot)) {
|
|
257
|
+
if (moduleName.startsWith("_"))
|
|
258
|
+
continue;
|
|
259
|
+
const moduleDir = path.join(modulesRoot, moduleName);
|
|
260
|
+
if (!fs.statSync(moduleDir).isDirectory())
|
|
261
|
+
continue;
|
|
262
|
+
const legacyTasksRoot = path.join(moduleDir, "TASKS");
|
|
263
|
+
if (!fs.existsSync(legacyTasksRoot))
|
|
264
|
+
continue;
|
|
265
|
+
const stagingRoot = uniqueModuleTaskStagingRoot(moduleDir);
|
|
266
|
+
fs.renameSync(legacyTasksRoot, stagingRoot);
|
|
267
|
+
const tasksRoot = path.join(moduleDir, "tasks");
|
|
268
|
+
fs.mkdirSync(tasksRoot, { recursive: true });
|
|
269
|
+
for (const taskName of fs.readdirSync(stagingRoot)) {
|
|
270
|
+
const source = path.join(stagingRoot, taskName);
|
|
271
|
+
const destination = path.join(tasksRoot, taskName);
|
|
272
|
+
if (fs.existsSync(destination) && !force) {
|
|
273
|
+
throw new Error(`Refusing to overwrite existing migrated module task: ${toPosix(path.relative(targetRoot, destination))}`);
|
|
274
|
+
}
|
|
275
|
+
if (fs.existsSync(destination))
|
|
276
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
277
|
+
fs.renameSync(source, destination);
|
|
278
|
+
applied.push({
|
|
279
|
+
action: "move",
|
|
280
|
+
source: toPosix(path.relative(targetRoot, source)),
|
|
281
|
+
destination: toPosix(path.relative(targetRoot, destination)),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
fs.rmSync(stagingRoot, { recursive: true, force: true });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function uniqueModuleTaskStagingRoot(moduleDir) {
|
|
288
|
+
for (let index = 0; index < 100; index += 1) {
|
|
289
|
+
const suffix = index === 0 ? "" : `-${String(index).padStart(2, "0")}`;
|
|
290
|
+
const candidate = path.join(moduleDir, `.TASKS-migration-${process.pid}${suffix}`);
|
|
291
|
+
if (!fs.existsSync(candidate))
|
|
292
|
+
return candidate;
|
|
293
|
+
}
|
|
294
|
+
throw new Error(`Unable to allocate module task staging directory: ${moduleDir}`);
|
|
295
|
+
}
|
|
296
|
+
function scaffoldMissingTaskWalkthroughs(targetRoot, applied) {
|
|
297
|
+
for (const taskDir of migratedTaskDirectories(targetRoot)) {
|
|
298
|
+
const taskId = path.basename(taskDir);
|
|
299
|
+
const index = path.join(taskDir, "INDEX.md");
|
|
300
|
+
if (!fs.existsSync(index)) {
|
|
301
|
+
fs.writeFileSync(index, renderMigratedTaskIndex(taskId));
|
|
302
|
+
applied.push({
|
|
303
|
+
action: "create",
|
|
304
|
+
destination: toPosix(path.relative(targetRoot, index)),
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
const walkthrough = path.join(taskDir, "walkthrough.md");
|
|
308
|
+
if (!fs.existsSync(walkthrough)) {
|
|
309
|
+
fs.writeFileSync(walkthrough, "# Walkthrough\n\nPending migrated closeout.\n");
|
|
310
|
+
applied.push({
|
|
311
|
+
action: "create",
|
|
312
|
+
destination: toPosix(path.relative(targetRoot, walkthrough)),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
const visualMap = path.join(taskDir, "visual_map.md");
|
|
316
|
+
if (!fs.existsSync(visualMap)) {
|
|
317
|
+
fs.writeFileSync(visualMap, renderMigratedVisualMap());
|
|
318
|
+
applied.push({
|
|
319
|
+
action: "create",
|
|
320
|
+
destination: toPosix(path.relative(targetRoot, visualMap)),
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function renderMigratedVisualMap() {
|
|
326
|
+
return `# Visual Map
|
|
327
|
+
|
|
328
|
+
Visual Map Contract: v1.0
|
|
329
|
+
|
|
330
|
+
| Phase ID | Kind | Depends On | State | Completion | Output | Required Evidence | Exit Command | Actor | Evidence Status | Blocking Risk | Owner / Handoff |
|
|
331
|
+
| --- | --- | --- | --- | ---: | --- | --- | --- | --- | --- | --- | --- |
|
|
332
|
+
| MIG-01 | execution | none | planned | 0 | migrated task validation | migrated task materials | n/a | agent | present | none | coordinator |
|
|
333
|
+
`;
|
|
334
|
+
}
|
|
335
|
+
function migratedTaskDirectories(targetRoot) {
|
|
336
|
+
const taskDirs = [];
|
|
337
|
+
const tasksRoot = path.join(targetRoot, v2HarnessRoot, "planning/tasks");
|
|
338
|
+
if (fs.existsSync(tasksRoot)) {
|
|
339
|
+
for (const entry of fs.readdirSync(tasksRoot)) {
|
|
340
|
+
const taskDir = path.join(tasksRoot, entry);
|
|
341
|
+
if (fs.existsSync(path.join(taskDir, "task_plan.md")))
|
|
342
|
+
taskDirs.push(taskDir);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const modulesRoot = path.join(targetRoot, v2HarnessRoot, "planning/modules");
|
|
346
|
+
if (!fs.existsSync(modulesRoot))
|
|
347
|
+
return taskDirs;
|
|
348
|
+
for (const moduleName of fs.readdirSync(modulesRoot)) {
|
|
349
|
+
if (moduleName.startsWith("_"))
|
|
350
|
+
continue;
|
|
351
|
+
const moduleTasksRoot = path.join(modulesRoot, moduleName, "tasks");
|
|
352
|
+
if (!fs.existsSync(moduleTasksRoot))
|
|
353
|
+
continue;
|
|
354
|
+
for (const entry of fs.readdirSync(moduleTasksRoot)) {
|
|
355
|
+
const taskDir = path.join(moduleTasksRoot, entry);
|
|
356
|
+
if (fs.existsSync(path.join(taskDir, "task_plan.md")))
|
|
357
|
+
taskDirs.push(taskDir);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return taskDirs;
|
|
361
|
+
}
|
|
362
|
+
function renderMigratedTaskIndex(taskId) {
|
|
363
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
364
|
+
return `# ${taskId} - Task Package Index
|
|
365
|
+
|
|
366
|
+
Task Contract: harness-task/v1
|
|
367
|
+
|
|
368
|
+
## Task Identity
|
|
369
|
+
|
|
370
|
+
| Field | Value |
|
|
371
|
+
| --- | --- |
|
|
372
|
+
| Task ID | \`${taskId}\` |
|
|
373
|
+
| Budget | \`simple\` |
|
|
374
|
+
| Walkthrough Path | \`walkthrough.md\` |
|
|
375
|
+
|
|
376
|
+
## Task Audit Metadata
|
|
377
|
+
|
|
378
|
+
| Field | Value |
|
|
379
|
+
| --- | --- |
|
|
380
|
+
| Created By | historical-backfill |
|
|
381
|
+
| Created At | ${today} |
|
|
382
|
+
| Command Shape | harness migrate-structure --apply |
|
|
383
|
+
| Budget | simple |
|
|
384
|
+
| Template Source | structure-migration |
|
|
385
|
+
| Task Creator | migration |
|
|
386
|
+
| Task Creator Source | git-unavailable |
|
|
387
|
+
| Human Review Status | not-confirmed |
|
|
388
|
+
| Confirmation ID | n/a |
|
|
389
|
+
| Confirmed At | n/a |
|
|
390
|
+
| Reviewer | n/a |
|
|
391
|
+
| Reviewer Email | n/a |
|
|
392
|
+
| Confirm Text | n/a |
|
|
393
|
+
| Evidence Checked | n/a |
|
|
394
|
+
| Review Commit SHA | n/a |
|
|
395
|
+
| Audit Source | native-index |
|
|
396
|
+
| Audit Status | created |
|
|
397
|
+
| Exception Reason | n/a |
|
|
398
|
+
| Message | v2 structure migration backfill |
|
|
399
|
+
| Migration Status | migrated |
|
|
400
|
+
| Migrated From | docs |
|
|
401
|
+
| Legacy Extra Fields | {} |
|
|
402
|
+
| Migration Notes | task index created during hard-cutover structure migration |
|
|
403
|
+
`;
|
|
404
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readFileSafe, toPosix, walkFiles, isArchivedHarnessPath, } from "./core-shared.mjs";
|
|
4
|
+
import { firstColumn, splitMarkdownRow, } from "./markdown-utils.mjs";
|
|
5
|
+
export function validateSubagentAuthorization(target, { strict = true } = {}) {
|
|
6
|
+
const failures = [];
|
|
7
|
+
const warnings = [];
|
|
8
|
+
const report = (message) => {
|
|
9
|
+
if (strict)
|
|
10
|
+
failures.push(message);
|
|
11
|
+
else
|
|
12
|
+
warnings.push(`adoption-needed: ${message}`);
|
|
13
|
+
};
|
|
14
|
+
const strategyPaths = walkFiles(target.docsRoot)
|
|
15
|
+
.filter((file) => file.endsWith("execution_strategy.md"))
|
|
16
|
+
.filter((file) => !file.includes(`${path.sep}_task-template${path.sep}`))
|
|
17
|
+
.filter((file) => !file.includes(`${path.sep}_optional-structures${path.sep}`))
|
|
18
|
+
.filter((file) => !isArchivedHarnessPath(file));
|
|
19
|
+
for (const strategyPath of strategyPaths) {
|
|
20
|
+
const relative = toPosix(path.relative(target.projectRoot, strategyPath));
|
|
21
|
+
const content = readFileSafe(strategyPath);
|
|
22
|
+
const rows = subagentAuthorizationRows(content);
|
|
23
|
+
for (const row of rows.filter((candidate) => /worker/i.test(candidate.role))) {
|
|
24
|
+
if (!isWorkerAuthorizedStatus(row.status))
|
|
25
|
+
continue;
|
|
26
|
+
const missing = [];
|
|
27
|
+
for (const [label, value] of [
|
|
28
|
+
["Authorized By", row.authorizedBy],
|
|
29
|
+
["Authorized At", row.authorizedAt],
|
|
30
|
+
["Scope", row.scope],
|
|
31
|
+
["Worktree / Branch", row.worktreeBranch],
|
|
32
|
+
]) {
|
|
33
|
+
if (!isConcreteAuthorizationValue(value))
|
|
34
|
+
missing.push(label);
|
|
35
|
+
}
|
|
36
|
+
if (missing.length > 0)
|
|
37
|
+
report(`${relative} worker subagent authorization is incomplete: ${missing.join(", ")}`);
|
|
38
|
+
}
|
|
39
|
+
const delegation = subagentDelegationRows(content).find((row) => /worker/i.test(row.question));
|
|
40
|
+
if (delegation && normalizeDecision(delegation.decision) === "ask-user") {
|
|
41
|
+
const userDecision = userAuthorizationRows(content).reverse().find((row) => /worker/i.test(row.gate) && isResolvedWorkerGateState(row.state));
|
|
42
|
+
if (!userDecision) {
|
|
43
|
+
report(`${relative} worker subagent ask-user decision is unresolved: missing User Authorization Decision`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const missing = missingUserDecisionFields(userDecision);
|
|
47
|
+
if (missing.length > 0)
|
|
48
|
+
report(`${relative} worker subagent authorization decision is incomplete: ${missing.join(", ")}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { failures, warnings };
|
|
53
|
+
}
|
|
54
|
+
function subagentAuthorizationRows(content) {
|
|
55
|
+
const section = markdownSection(content, "Subagent Authorization");
|
|
56
|
+
if (!section)
|
|
57
|
+
return [];
|
|
58
|
+
const lines = section.split(/\r?\n/);
|
|
59
|
+
for (let index = 0; index < lines.length - 1; index += 1) {
|
|
60
|
+
if (!lines[index].trim().startsWith("|"))
|
|
61
|
+
continue;
|
|
62
|
+
const header = splitMarkdownRow(lines[index]);
|
|
63
|
+
const separator = splitMarkdownRow(lines[index + 1]);
|
|
64
|
+
if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
|
|
65
|
+
continue;
|
|
66
|
+
const roleIndex = firstColumn(header, ["Role"]);
|
|
67
|
+
const statusIndex = firstColumn(header, ["Status"]);
|
|
68
|
+
if (roleIndex < 0 || statusIndex < 0)
|
|
69
|
+
continue;
|
|
70
|
+
const indexes = {
|
|
71
|
+
role: roleIndex,
|
|
72
|
+
status: statusIndex,
|
|
73
|
+
authorizedBy: firstColumn(header, ["Authorized By"]),
|
|
74
|
+
authorizedAt: firstColumn(header, ["Authorized At"]),
|
|
75
|
+
scope: firstColumn(header, ["Scope"]),
|
|
76
|
+
worktreeBranch: firstColumn(header, ["Worktree / Branch", "Worktree", "Branch"]),
|
|
77
|
+
};
|
|
78
|
+
return lines
|
|
79
|
+
.slice(index + 2)
|
|
80
|
+
.filter((line) => line.trim().startsWith("|"))
|
|
81
|
+
.map(splitMarkdownRow)
|
|
82
|
+
.filter((row) => row.length === header.length)
|
|
83
|
+
.map((row) => Object.fromEntries(Object.entries(indexes).map(([key, column]) => [key, column >= 0 ? row[column] || "" : ""])));
|
|
84
|
+
}
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
function subagentDelegationRows(content) {
|
|
88
|
+
const section = markdownSection(content, "Subagent Delegation Decision");
|
|
89
|
+
if (!section)
|
|
90
|
+
return [];
|
|
91
|
+
return parseFirstTable(section, ["Question", "Decision"]).map((row) => ({
|
|
92
|
+
question: row.Question || "",
|
|
93
|
+
decision: row.Decision || "",
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
function userAuthorizationRows(content) {
|
|
97
|
+
return parseMatchingTables(content, ["Gate", "State"]).map((row) => ({
|
|
98
|
+
gate: row.Gate || "",
|
|
99
|
+
state: row.State || "",
|
|
100
|
+
decidedBy: row["Decided By"] || "",
|
|
101
|
+
decidedAt: row["Decided At"] || "",
|
|
102
|
+
scope: row.Scope || "",
|
|
103
|
+
worktreeBranch: row["Worktree / Branch"] || row.Worktree || row.Branch || "",
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
function parseMatchingTables(content, requiredColumns) {
|
|
107
|
+
const lines = String(content || "").split(/\r?\n/);
|
|
108
|
+
const rows = [];
|
|
109
|
+
for (let index = 0; index < lines.length - 1; index += 1) {
|
|
110
|
+
if (!lines[index].trim().startsWith("|"))
|
|
111
|
+
continue;
|
|
112
|
+
const header = splitMarkdownRow(lines[index]);
|
|
113
|
+
const separator = splitMarkdownRow(lines[index + 1]);
|
|
114
|
+
if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
|
|
115
|
+
continue;
|
|
116
|
+
if (requiredColumns.some((column) => firstColumn(header, [column]) < 0))
|
|
117
|
+
continue;
|
|
118
|
+
let rowIndex = index + 2;
|
|
119
|
+
while (rowIndex < lines.length && lines[rowIndex].trim().startsWith("|")) {
|
|
120
|
+
const row = splitMarkdownRow(lines[rowIndex]);
|
|
121
|
+
if (row.length === header.length)
|
|
122
|
+
rows.push(Object.fromEntries(header.map((column, columnIndex) => [column, row[columnIndex] || ""])));
|
|
123
|
+
rowIndex += 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return rows;
|
|
127
|
+
}
|
|
128
|
+
function parseFirstTable(content, requiredColumns) {
|
|
129
|
+
const lines = String(content || "").split(/\r?\n/);
|
|
130
|
+
for (let index = 0; index < lines.length - 1; index += 1) {
|
|
131
|
+
if (!lines[index].trim().startsWith("|"))
|
|
132
|
+
continue;
|
|
133
|
+
const header = splitMarkdownRow(lines[index]);
|
|
134
|
+
const separator = splitMarkdownRow(lines[index + 1]);
|
|
135
|
+
if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
|
|
136
|
+
continue;
|
|
137
|
+
if (requiredColumns.some((column) => firstColumn(header, [column]) < 0))
|
|
138
|
+
continue;
|
|
139
|
+
return lines
|
|
140
|
+
.slice(index + 2)
|
|
141
|
+
.filter((line) => line.trim().startsWith("|"))
|
|
142
|
+
.map(splitMarkdownRow)
|
|
143
|
+
.filter((row) => row.length === header.length)
|
|
144
|
+
.map((row) => Object.fromEntries(header.map((column, columnIndex) => [column, row[columnIndex] || ""])));
|
|
145
|
+
}
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
function markdownSection(content, heading) {
|
|
149
|
+
const lines = String(content || "").split(/\r?\n/);
|
|
150
|
+
const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
151
|
+
const start = lines.findIndex((line) => new RegExp(`^##\\s+${escaped}\\s*$`, "i").test(line.trim()));
|
|
152
|
+
if (start < 0)
|
|
153
|
+
return "";
|
|
154
|
+
const end = lines.findIndex((line, index) => index > start && /^##\s+/.test(line));
|
|
155
|
+
return lines.slice(start + 1, end < 0 ? undefined : end).join("\n");
|
|
156
|
+
}
|
|
157
|
+
function isConcreteAuthorizationValue(value) {
|
|
158
|
+
const raw = String(value || "").replace(/`/g, "").trim();
|
|
159
|
+
return Boolean(raw) && !/^\[.*\]$/.test(raw) && !/^(pending|n\/a|na|none|-|—|–|待授权|待定|无)$/i.test(raw);
|
|
160
|
+
}
|
|
161
|
+
function isWorkerAuthorizedStatus(value) {
|
|
162
|
+
const raw = String(value || "")
|
|
163
|
+
.replace(/`/g, "")
|
|
164
|
+
.trim()
|
|
165
|
+
.toLowerCase()
|
|
166
|
+
.replaceAll("_", "-")
|
|
167
|
+
.replace(/\s+/g, "-");
|
|
168
|
+
if (!raw || /^(not-authorized|unauthorized|pending|no|false|未授权|待授权)$/.test(raw))
|
|
169
|
+
return false;
|
|
170
|
+
return /(^|\b)(authorized|used|active|approved)(\b|$)|已授权|已使用/.test(raw);
|
|
171
|
+
}
|
|
172
|
+
function normalizeDecision(value) {
|
|
173
|
+
return String(value || "")
|
|
174
|
+
.replace(/`/g, "")
|
|
175
|
+
.trim()
|
|
176
|
+
.toLowerCase()
|
|
177
|
+
.replaceAll("_", "-")
|
|
178
|
+
.replace(/\s+/g, "-");
|
|
179
|
+
}
|
|
180
|
+
function isResolvedWorkerGateState(value) {
|
|
181
|
+
const raw = normalizeDecision(value);
|
|
182
|
+
return /^(authorized|approved|denied|rejected|not-needed|not-authorized|no|yes|无需|拒绝|已授权)$/.test(raw);
|
|
183
|
+
}
|
|
184
|
+
function missingUserDecisionFields(row) {
|
|
185
|
+
const state = normalizeDecision(row.state);
|
|
186
|
+
const required = state === "authorized" || state === "approved" || state === "yes" || state === "已授权"
|
|
187
|
+
? [
|
|
188
|
+
["Decided By", row.decidedBy],
|
|
189
|
+
["Decided At", row.decidedAt],
|
|
190
|
+
["Scope", row.scope],
|
|
191
|
+
["Worktree / Branch", row.worktreeBranch],
|
|
192
|
+
]
|
|
193
|
+
: [
|
|
194
|
+
["Decided By", row.decidedBy],
|
|
195
|
+
["Decided At", row.decidedAt],
|
|
196
|
+
];
|
|
197
|
+
return required.filter(([, value]) => !isConcreteAuthorizationValue(value)).map(([label]) => label);
|
|
198
|
+
}
|