coding-agent-harness 1.0.5 → 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/CONTRIBUTING.md +2 -2
- package/README.md +63 -3
- package/README.zh-CN.md +52 -3
- package/SKILL.md +43 -43
- 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 +2 -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 +44 -46
- 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 +12 -12
- package/docs-release/architecture/overview.zh-CN.md +12 -12
- package/docs-release/architecture/system-explainer/01-system-overview.md +15 -14
- package/docs-release/architecture/system-explainer/02-module-dependency.md +8 -8
- package/docs-release/architecture/system-explainer/03-task-lifecycle.md +3 -3
- package/docs-release/architecture/system-explainer/04-check-and-governance.md +9 -7
- package/docs-release/architecture/system-explainer/05-data-flow.md +5 -5
- package/docs-release/architecture/system-explainer/06-preset-and-migration.md +1 -4
- package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +15 -14
- package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +8 -8
- package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +3 -3
- package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +10 -8
- package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
- package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +1 -4
- package/docs-release/guides/agent-installation.en-US.md +14 -8
- package/docs-release/guides/agent-installation.md +14 -8
- 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 +2 -2
- 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 +5 -5
- package/docs-release/guides/task-state-machine.md +5 -5
- 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/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 +20 -12
- 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 +5 -5
- 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/task_plan.md +1 -1
- package/skills/preset-creator/references/preset-package-skeleton.md +5 -5
- package/templates/AGENTS.md.template +26 -26
- 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/20-overview.js +11 -5
- package/templates/dashboard/assets/app-src/40-modules.js +1 -1
- package/templates/dashboard/assets/app.js +12 -6
- package/templates/dashboard/assets/i18n.js +4 -2
- 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 +1 -0
- package/templates/planning/module_session_prompt.md +1 -1
- package/templates/planning/task_plan.md +1 -1
- package/templates/planning/walkthrough.md +47 -0
- package/templates/reference/docs-library-standard.md +8 -8
- 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 +26 -26
- 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/module_session_prompt.md +11 -11
- 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 +1 -1
- 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 -126
- package/scripts/commands/preset-command.mjs +0 -73
- package/scripts/commands/task-command.mjs +0 -328
- package/scripts/harness.mjs +0 -291
- package/scripts/lib/capability-registry.mjs +0 -587
- package/scripts/lib/check-module-parallel.mjs +0 -230
- package/scripts/lib/check-profiles.mjs +0 -372
- package/scripts/lib/check-task-contracts.mjs +0 -55
- package/scripts/lib/core-shared.mjs +0 -249
- package/scripts/lib/dashboard-data.mjs +0 -520
- package/scripts/lib/dashboard-workbench.mjs +0 -336
- package/scripts/lib/dashboard-writer.mjs +0 -202
- 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 -611
- package/scripts/lib/governance-table-boundary.mjs +0 -175
- package/scripts/lib/lesson-maintenance.mjs +0 -152
- package/scripts/lib/markdown-utils.mjs +0 -191
- package/scripts/lib/migration-planner.mjs +0 -476
- package/scripts/lib/migration-support.mjs +0 -312
- package/scripts/lib/phase-kind.mjs +0 -50
- package/scripts/lib/preset-audit-contracts.mjs +0 -37
- package/scripts/lib/preset-engine.mjs +0 -494
- package/scripts/lib/preset-registry.mjs +0 -776
- package/scripts/lib/preset-resource-contracts.mjs +0 -83
- package/scripts/lib/review-confirm-git-gate.mjs +0 -248
- package/scripts/lib/status-builder.mjs +0 -88
- package/scripts/lib/subagent-authorization-audit.mjs +0 -196
- package/scripts/lib/task-audit-metadata.mjs +0 -385
- package/scripts/lib/task-audit-migration.mjs +0 -350
- package/scripts/lib/task-completion-consistency.mjs +0 -26
- 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/create-task-helpers.mjs +0 -67
- package/scripts/lib/task-lifecycle/phase-sync.mjs +0 -88
- package/scripts/lib/task-lifecycle/review-confirm.mjs +0 -112
- package/scripts/lib/task-lifecycle/review-gates.mjs +0 -73
- package/scripts/lib/task-lifecycle/review-submission.mjs +0 -63
- package/scripts/lib/task-lifecycle/scaffold-provenance.mjs +0 -49
- package/scripts/lib/task-lifecycle/template-files.mjs +0 -53
- package/scripts/lib/task-lifecycle/text-utils.mjs +0 -24
- package/scripts/lib/task-lifecycle.mjs +0 -616
- package/scripts/lib/task-metadata.mjs +0 -118
- package/scripts/lib/task-review-model.mjs +0 -455
- package/scripts/lib/task-scanner.mjs +0 -503
- 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/INDEX.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,54 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Task contract checks depend on dynamic task scan metadata until checker domain types are modeled.
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { lessonCandidatesFile, readFileSafe, toPosix, visualMapFile, } from "./core-shared.mjs";
|
|
6
|
+
import { listTaskPlanPaths, parseTaskBudget, parseTaskContractInfo, } from "./task-scanner.mjs";
|
|
7
|
+
import { parseTaskAuditMetadata } from "./task-audit-metadata.mjs";
|
|
8
|
+
export function validatePlanContracts(target, { strict = true, taskPlanPaths } = {}) {
|
|
9
|
+
const failures = [];
|
|
10
|
+
const warnings = [];
|
|
11
|
+
const report = (message) => {
|
|
12
|
+
if (strict)
|
|
13
|
+
failures.push(message);
|
|
14
|
+
else
|
|
15
|
+
warnings.push(`adoption-needed: ${message}`);
|
|
16
|
+
};
|
|
17
|
+
for (const taskPlanPath of taskPlanPaths || listTaskPlanPaths(target)) {
|
|
18
|
+
const taskDir = path.dirname(taskPlanPath);
|
|
19
|
+
const relativeDir = toPosix(path.relative(target.projectRoot, taskDir));
|
|
20
|
+
const taskPlanContent = readFileSafe(taskPlanPath);
|
|
21
|
+
const indexContent = readFileSafe(path.join(taskDir, "INDEX.md"));
|
|
22
|
+
const budget = parseTaskBudget(taskPlanContent);
|
|
23
|
+
const taskContract = parseTaskContractInfo(taskPlanContent);
|
|
24
|
+
const taskAudit = parseTaskAuditMetadata(indexContent, { required: strict && taskContract.generated });
|
|
25
|
+
if (!taskContract.generated) {
|
|
26
|
+
warnings.push(`adoption-needed: ${relativeDir} missing Task Contract: harness-task/v1 marker`);
|
|
27
|
+
}
|
|
28
|
+
for (const issue of taskAudit.issues) {
|
|
29
|
+
if (taskContract.generated || taskAudit.present)
|
|
30
|
+
failures.push(`${relativeDir}/INDEX.md ${issue.message}`);
|
|
31
|
+
else
|
|
32
|
+
report(`${relativeDir}/INDEX.md ${issue.message}`);
|
|
33
|
+
}
|
|
34
|
+
const indexRequired = /^Task Package Index\s*[::]\s*(required|yes|true|必需|必须|required)\s*$/im.test(taskPlanContent);
|
|
35
|
+
for (const fileName of requiredTaskFilesForBudget(budget, { indexRequired })) {
|
|
36
|
+
if (!fs.existsSync(path.join(taskDir, fileName))) {
|
|
37
|
+
if (taskContract.generated)
|
|
38
|
+
failures.push(`${relativeDir} missing ${fileName}`);
|
|
39
|
+
else
|
|
40
|
+
report(`${relativeDir} missing ${fileName}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { failures, warnings };
|
|
45
|
+
}
|
|
46
|
+
function requiredTaskFilesForBudget(budget, { indexRequired = false } = {}) {
|
|
47
|
+
const simpleFiles = [...(indexRequired ? ["INDEX.md"] : []), "brief.md", "task_plan.md", visualMapFile, "progress.md"];
|
|
48
|
+
if (budget === "simple")
|
|
49
|
+
return simpleFiles;
|
|
50
|
+
const standardFiles = [...simpleFiles, "execution_strategy.md", "findings.md", lessonCandidatesFile, "review.md"];
|
|
51
|
+
if (budget === "complex")
|
|
52
|
+
return [...standardFiles, "references/INDEX.md", "artifacts/INDEX.md"];
|
|
53
|
+
return standardFiles;
|
|
54
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { resolveHarnessPaths } from "./harness-paths.mjs";
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
export const repoRoot = path.resolve(__dirname, "../..");
|
|
9
|
+
export const legacyChecker = path.join(repoRoot, "dist/check-harness.mjs");
|
|
10
|
+
export const bundledCheckScript = legacyChecker;
|
|
11
|
+
export const visualMapFile = "visual_map.md";
|
|
12
|
+
export const legacyVisualRoadmapFile = "visual_roadmap.md";
|
|
13
|
+
export const lessonCandidatesFile = "lesson_candidates.md";
|
|
14
|
+
export const longRunningTaskContractFile = "long-running-task-contract.md";
|
|
15
|
+
export const taskContractMarker = "Task Contract: harness-task/v1";
|
|
16
|
+
export const builtinPresetRoot = path.join(repoRoot, "presets");
|
|
17
|
+
export function userPresetRootForHome(home = "") {
|
|
18
|
+
return path.join(path.resolve(home || os.homedir()), ".coding-agent-harness/presets");
|
|
19
|
+
}
|
|
20
|
+
export const userPresetRoot = userPresetRootForHome();
|
|
21
|
+
export const supportedLocales = new Set(["zh-CN", "en-US"]);
|
|
22
|
+
export const allowedReviewDispositions = new Set([
|
|
23
|
+
"open",
|
|
24
|
+
"mitigated",
|
|
25
|
+
"closed",
|
|
26
|
+
"deferred",
|
|
27
|
+
"accepted-risk",
|
|
28
|
+
"not-reproducible",
|
|
29
|
+
"out-of-scope",
|
|
30
|
+
]);
|
|
31
|
+
export const allowedTaskStates = new Set(["not_started", "planned", "in_progress", "review", "blocked", "done"]);
|
|
32
|
+
export const allowedTaskBudgets = new Set(["simple", "standard", "complex"]);
|
|
33
|
+
export const allowedPhaseStates = new Set(["planned", "in_progress", "review", "blocked", "done", "skipped"]);
|
|
34
|
+
export const allowedEvidenceStatus = new Set(["missing", "partial", "present", "waived"]);
|
|
35
|
+
export function normalizeTarget(input = ".") {
|
|
36
|
+
const target = path.resolve(input);
|
|
37
|
+
const siblingV2Manifest = path.join(path.dirname(target), "coding-agent-harness", "harness.yaml");
|
|
38
|
+
const isDocsRoot = path.basename(target) === "docs" &&
|
|
39
|
+
(fs.existsSync(path.join(target, "09-PLANNING")) || fs.existsSync(path.join(target, "11-REFERENCE")) || fs.existsSync(siblingV2Manifest));
|
|
40
|
+
const isHarnessRoot = path.basename(target) === "coding-agent-harness" && fs.existsSync(path.join(target, "harness.yaml"));
|
|
41
|
+
const normalized = {
|
|
42
|
+
input: target,
|
|
43
|
+
projectRoot: isDocsRoot || isHarnessRoot ? path.dirname(target) : target,
|
|
44
|
+
docsRoot: isDocsRoot ? target : path.join(target, "docs"),
|
|
45
|
+
docsOnly: isDocsRoot,
|
|
46
|
+
};
|
|
47
|
+
const paths = resolveHarnessPaths(normalized);
|
|
48
|
+
const harnessRootRelative = toPosix(path.relative(paths.projectRoot, paths.harnessRoot)) || ".";
|
|
49
|
+
return {
|
|
50
|
+
...normalized,
|
|
51
|
+
...paths,
|
|
52
|
+
docsRoot: paths.version === 2 ? paths.harnessRoot : normalized.docsRoot,
|
|
53
|
+
harnessRootRelative,
|
|
54
|
+
structureVersion: paths.version,
|
|
55
|
+
structureState: paths.version === 2 ? "v2" : "legacy",
|
|
56
|
+
harness: paths,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function projectPresetRoot(targetInput = ".") {
|
|
60
|
+
return path.join(normalizeTarget(targetInput).projectRoot, ".coding-agent-harness/presets");
|
|
61
|
+
}
|
|
62
|
+
export function toPosix(value) {
|
|
63
|
+
return value.split(path.sep).join("/");
|
|
64
|
+
}
|
|
65
|
+
export function exists(target, relativePath) {
|
|
66
|
+
return fs.existsSync(path.join(target.projectRoot, relativePath));
|
|
67
|
+
}
|
|
68
|
+
export function existsInDocs(target, relativePath) {
|
|
69
|
+
return fs.existsSync(path.join(target.docsRoot, relativePath));
|
|
70
|
+
}
|
|
71
|
+
export function readFileSafe(filePath) {
|
|
72
|
+
try {
|
|
73
|
+
return fs.readFileSync(filePath, "utf8");
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function readJsonSafe(filePath, fallback = null, { onError } = {}) {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (typeof onError === "function")
|
|
85
|
+
onError(error);
|
|
86
|
+
return fallback;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function readBundledTemplate(source) {
|
|
90
|
+
const sourcePath = path.join(repoRoot, source);
|
|
91
|
+
if (!fs.existsSync(sourcePath))
|
|
92
|
+
throw new Error(`Bundled template missing: ${source}`);
|
|
93
|
+
const content = fs.readFileSync(sourcePath, "utf8");
|
|
94
|
+
if (!content.trim())
|
|
95
|
+
throw new Error(`Bundled template is empty: ${source}`);
|
|
96
|
+
return content;
|
|
97
|
+
}
|
|
98
|
+
export function walkFiles(root, options = {}) {
|
|
99
|
+
const results = [];
|
|
100
|
+
if (!fs.existsSync(root))
|
|
101
|
+
return results;
|
|
102
|
+
const dirFilter = typeof options.dirFilter === "function" ? options.dirFilter : () => true;
|
|
103
|
+
function walk(dir) {
|
|
104
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
105
|
+
const full = path.join(dir, entry);
|
|
106
|
+
const stat = fs.statSync(full);
|
|
107
|
+
if (stat.isDirectory()) {
|
|
108
|
+
if ([".git", "node_modules", "tmp"].includes(entry))
|
|
109
|
+
continue;
|
|
110
|
+
if (!dirFilter(entry, full))
|
|
111
|
+
continue;
|
|
112
|
+
walk(full);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
results.push(full);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
walk(root);
|
|
120
|
+
return results;
|
|
121
|
+
}
|
|
122
|
+
export function isArchivedHarnessPath(filePath) {
|
|
123
|
+
const normalized = `/${toPosix(filePath)}/`;
|
|
124
|
+
return normalized.includes("/_archive/") || normalized.includes("/governance/archive/");
|
|
125
|
+
}
|
|
126
|
+
export function normalizeLocale(locale = "en-US") {
|
|
127
|
+
return supportedLocales.has(locale) ? locale : "en-US";
|
|
128
|
+
}
|
|
129
|
+
export function inferProjectLocale(target, fallback = "en-US") {
|
|
130
|
+
const candidates = [
|
|
131
|
+
path.join(target.projectRoot, "AGENTS.md"),
|
|
132
|
+
path.join(target.projectRoot, "CLAUDE.md"),
|
|
133
|
+
path.join(target.docsRoot, "AGENTS.md"),
|
|
134
|
+
path.join(target.docsRoot, "Harness-Ledger.md"),
|
|
135
|
+
];
|
|
136
|
+
for (const file of candidates) {
|
|
137
|
+
const content = readFileSafe(file);
|
|
138
|
+
if (/\p{Script=Han}/u.test(content))
|
|
139
|
+
return "zh-CN";
|
|
140
|
+
}
|
|
141
|
+
return normalizeLocale(fallback);
|
|
142
|
+
}
|
|
143
|
+
export function slug(value) {
|
|
144
|
+
return String(value || "item")
|
|
145
|
+
.toLowerCase()
|
|
146
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
147
|
+
.replace(/^-+|-+$/g, "")
|
|
148
|
+
.slice(0, 80) || "item";
|
|
149
|
+
}
|
|
150
|
+
export function prefixedPath(target, filePath) {
|
|
151
|
+
return `TARGET:${toPosix(path.relative(target.projectRoot, filePath))}`;
|
|
152
|
+
}
|
|
153
|
+
export function sanitizeText(value) {
|
|
154
|
+
return String(value ?? "")
|
|
155
|
+
.replace(/file:\/\/\/[^\s)"'`<>\]]+/g, "LOCAL_FILE_URL_REDACTED")
|
|
156
|
+
.replaceAll("file://", "LOCAL_FILE_URL_REDACTED")
|
|
157
|
+
.replace(/\/Users\/[^/\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
|
|
158
|
+
.replace(/\/Volumes\/[^\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
|
|
159
|
+
.replace(/\/(?:private\/)?tmp\/[^\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
|
|
160
|
+
.replace(/\/var\/folders\/[^\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
|
|
161
|
+
.replace(/\/home\/[^/\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
|
|
162
|
+
.replace(/[A-Za-z]:\\[^\s)"'`<>\]]+(?:\\[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED");
|
|
163
|
+
}
|
|
164
|
+
export function sanitizeDeep(value) {
|
|
165
|
+
if (typeof value === "string")
|
|
166
|
+
return sanitizeText(value);
|
|
167
|
+
if (Array.isArray(value))
|
|
168
|
+
return value.map(sanitizeDeep);
|
|
169
|
+
if (value && typeof value === "object") {
|
|
170
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, sanitizeDeep(entry)]));
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
export function titleFromMarkdown(content, fallback) {
|
|
175
|
+
const match = content.match(/^#\s+(.+)$/m);
|
|
176
|
+
return match ? match[1].trim() : fallback;
|
|
177
|
+
}
|
|
178
|
+
export function localizedTemplateSource(source, locale) {
|
|
179
|
+
const localeSource = normalizeLocale(locale) === "zh-CN" ? source.replace(/^templates\//, "templates-zh-CN/") : source;
|
|
180
|
+
return fs.existsSync(path.join(repoRoot, localeSource)) ? localeSource : source;
|
|
181
|
+
}
|
|
182
|
+
export function todayDate() {
|
|
183
|
+
return localDate();
|
|
184
|
+
}
|
|
185
|
+
export function localDate() {
|
|
186
|
+
const now = new Date();
|
|
187
|
+
const year = now.getFullYear();
|
|
188
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
189
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
190
|
+
return `${year}-${month}-${day}`;
|
|
191
|
+
}
|
|
192
|
+
export const datePrefix = /^\d{4}-\d{2}-\d{2}-/;
|
|
193
|
+
export function nowTimestamp() {
|
|
194
|
+
return new Date().toISOString().replace("T", " ").slice(0, 16);
|
|
195
|
+
}
|
|
196
|
+
export function normalizeTaskId(value) {
|
|
197
|
+
return slug(value || "task");
|
|
198
|
+
}
|
|
199
|
+
export function renderTaskTemplate(content, { taskId, title, locale, budget = "standard", moduleKey = "", preset = "none", presetVersion = "", evidenceBundle = "", longRunning = false, scaffoldProvenance = {}, taskAudit = {} }) {
|
|
200
|
+
const date = todayDate();
|
|
201
|
+
const provenance = {
|
|
202
|
+
createdBy: scaffoldProvenance.createdBy || "harness new-task",
|
|
203
|
+
command: scaffoldProvenance.command || "harness new-task [task-id] <target>",
|
|
204
|
+
createdAt: scaffoldProvenance.createdAt || date,
|
|
205
|
+
budget: scaffoldProvenance.budget || budget,
|
|
206
|
+
templateSource: scaffoldProvenance.templateSource || "templates/planning/brief.md",
|
|
207
|
+
exceptionReason: scaffoldProvenance.exceptionReason || "n/a",
|
|
208
|
+
};
|
|
209
|
+
return String(content)
|
|
210
|
+
.replaceAll("{{TASK_ID}}", taskId)
|
|
211
|
+
.replaceAll("{{TASK_TITLE}}", title)
|
|
212
|
+
.replaceAll("{{DATE}}", date)
|
|
213
|
+
.replaceAll("{{LOCALE}}", normalizeLocale(locale))
|
|
214
|
+
.replaceAll("{{TASK_BUDGET}}", budget)
|
|
215
|
+
.replaceAll("{{TASK_MODULE}}", moduleKey || "n/a")
|
|
216
|
+
.replaceAll("{{TASK_PRESET}}", preset || "none")
|
|
217
|
+
.replaceAll("{{TASK_PRESET_VERSION}}", presetVersion || "n/a")
|
|
218
|
+
.replaceAll("{{TASK_EVIDENCE_BUNDLE}}", evidenceBundle || "n/a")
|
|
219
|
+
.replaceAll("{{TASK_LONG_RUNNING}}", longRunning ? "yes" : "no")
|
|
220
|
+
.replaceAll("{{SCAFFOLD_CREATED_BY}}", provenance.createdBy)
|
|
221
|
+
.replaceAll("{{SCAFFOLD_COMMAND}}", provenance.command)
|
|
222
|
+
.replaceAll("{{SCAFFOLD_CREATED_AT}}", provenance.createdAt)
|
|
223
|
+
.replaceAll("{{SCAFFOLD_BUDGET}}", provenance.budget)
|
|
224
|
+
.replaceAll("{{SCAFFOLD_TEMPLATE_SOURCE}}", provenance.templateSource)
|
|
225
|
+
.replaceAll("{{SCAFFOLD_EXCEPTION_REASON}}", provenance.exceptionReason)
|
|
226
|
+
.replaceAll("{{TASK_AUDIT_CREATED_BY}}", taskAudit["Created By"] || provenance.createdBy)
|
|
227
|
+
.replaceAll("{{TASK_AUDIT_CREATED_AT}}", taskAudit["Created At"] || provenance.createdAt)
|
|
228
|
+
.replaceAll("{{TASK_AUDIT_COMMAND_SHAPE}}", taskAudit["Command Shape"] || provenance.command)
|
|
229
|
+
.replaceAll("{{TASK_AUDIT_BUDGET}}", taskAudit.Budget || provenance.budget)
|
|
230
|
+
.replaceAll("{{TASK_AUDIT_TEMPLATE_SOURCE}}", taskAudit["Template Source"] || provenance.templateSource)
|
|
231
|
+
.replaceAll("{{TASK_AUDIT_TASK_CREATOR}}", taskAudit["Task Creator"] || "n/a")
|
|
232
|
+
.replaceAll("{{TASK_AUDIT_TASK_CREATOR_SOURCE}}", taskAudit["Task Creator Source"] || "git-unavailable")
|
|
233
|
+
.replaceAll("{{TASK_AUDIT_HUMAN_REVIEW_STATUS}}", taskAudit["Human Review Status"] || "not-confirmed")
|
|
234
|
+
.replaceAll("{{TASK_AUDIT_CONFIRMATION_ID}}", taskAudit["Confirmation ID"] || "n/a")
|
|
235
|
+
.replaceAll("{{TASK_AUDIT_CONFIRMED_AT}}", taskAudit["Confirmed At"] || "n/a")
|
|
236
|
+
.replaceAll("{{TASK_AUDIT_REVIEWER}}", taskAudit.Reviewer || "n/a")
|
|
237
|
+
.replaceAll("{{TASK_AUDIT_REVIEWER_EMAIL}}", taskAudit["Reviewer Email"] || "n/a")
|
|
238
|
+
.replaceAll("{{TASK_AUDIT_CONFIRM_TEXT}}", taskAudit["Confirm Text"] || "n/a")
|
|
239
|
+
.replaceAll("{{TASK_AUDIT_EVIDENCE_CHECKED}}", taskAudit["Evidence Checked"] || "n/a")
|
|
240
|
+
.replaceAll("{{TASK_AUDIT_REVIEW_COMMIT_SHA}}", taskAudit["Review Commit SHA"] || "n/a")
|
|
241
|
+
.replaceAll("{{TASK_AUDIT_AUDIT_SOURCE}}", taskAudit["Audit Source"] || "native-index")
|
|
242
|
+
.replaceAll("{{TASK_AUDIT_AUDIT_STATUS}}", taskAudit["Audit Status"] || "created")
|
|
243
|
+
.replaceAll("{{TASK_AUDIT_EXCEPTION_REASON}}", taskAudit["Exception Reason"] || provenance.exceptionReason)
|
|
244
|
+
.replaceAll("{{TASK_AUDIT_MESSAGE}}", taskAudit.Message || "n/a")
|
|
245
|
+
.replaceAll("{{TASK_AUDIT_MIGRATION_STATUS}}", taskAudit["Migration Status"] || "native")
|
|
246
|
+
.replaceAll("{{TASK_AUDIT_MIGRATED_FROM}}", taskAudit["Migrated From"] || "n/a")
|
|
247
|
+
.replaceAll("{{TASK_AUDIT_LEGACY_EXTRA_FIELDS}}", taskAudit["Legacy Extra Fields"] || "{}")
|
|
248
|
+
.replaceAll("{{TASK_AUDIT_MIGRATION_NOTES}}", taskAudit["Migration Notes"] || "n/a")
|
|
249
|
+
.replaceAll("[simple / standard / complex]", budget)
|
|
250
|
+
.replaceAll("[simple / standard / long-running / module-parallel]", budget)
|
|
251
|
+
.replaceAll("[simple / complex]", budget)
|
|
252
|
+
.replaceAll("[Task Name]", title)
|
|
253
|
+
.replaceAll("[任务名称]", title);
|
|
254
|
+
}
|