coding-agent-harness 1.0.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.
Files changed (139) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +141 -0
  4. package/SKILL.md +423 -0
  5. package/docs-release/README.md +30 -0
  6. package/docs-release/architecture/overview.md +52 -0
  7. package/docs-release/guides/agent-installation.md +139 -0
  8. package/examples/minimal-project/.harness-capabilities.json +8 -0
  9. package/examples/minimal-project/AGENTS.md +4 -0
  10. package/examples/minimal-project/CLAUDE.md +3 -0
  11. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/execution_strategy.md +10 -0
  12. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +11 -0
  13. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/review.md +27 -0
  14. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/task_plan.md +14 -0
  15. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/visual_roadmap.md +11 -0
  16. package/examples/minimal-project/docs/Harness-Ledger.md +6 -0
  17. package/package.json +34 -0
  18. package/references/adversarial-review-standard.md +173 -0
  19. package/references/agents-md-pattern.md +140 -0
  20. package/references/cadence-ledger.md +55 -0
  21. package/references/ci-cd-standard.md +90 -0
  22. package/references/delivery-operating-model-standard.md +145 -0
  23. package/references/docs-directory-standard.md +125 -0
  24. package/references/harness-ledger.md +148 -0
  25. package/references/lessons-governance.md +157 -0
  26. package/references/long-running-task-standard.md +209 -0
  27. package/references/module-parallel-standard.md +292 -0
  28. package/references/planning-loop.md +192 -0
  29. package/references/project-onboarding-audit.md +167 -0
  30. package/references/regression-system.md +89 -0
  31. package/references/repo-governance-standard.md +131 -0
  32. package/references/review-routing-standard.md +103 -0
  33. package/references/ssot-governance.md +111 -0
  34. package/references/walkthrough-closeout.md +135 -0
  35. package/references/worktree-parallel.md +184 -0
  36. package/scripts/check-harness.mjs +728 -0
  37. package/scripts/harness.mjs +201 -0
  38. package/scripts/lib/dashboard-writer.mjs +95 -0
  39. package/scripts/lib/harness-core.mjs +1318 -0
  40. package/scripts/smoke-dashboard.mjs +70 -0
  41. package/scripts/test-harness.mjs +482 -0
  42. package/templates/AGENTS.md.template +82 -0
  43. package/templates/CLAUDE.md.template +12 -0
  44. package/templates/dashboard/assets/app.css +399 -0
  45. package/templates/dashboard/assets/app.js +435 -0
  46. package/templates/dashboard/assets/i18n.js +47 -0
  47. package/templates/dashboard/assets/markdown-reader.js +116 -0
  48. package/templates/dashboard/assets/mermaid-renderer.js +59 -0
  49. package/templates/dashboard/index.html +18 -0
  50. package/templates/ledger/Harness-Ledger.md +39 -0
  51. package/templates/lessons/lesson-arch-process-change.md +47 -0
  52. package/templates/lessons/lesson-new-doc.md +50 -0
  53. package/templates/lessons/lesson-ref-change.md +45 -0
  54. package/templates/planning/execution_strategy.md +40 -0
  55. package/templates/planning/findings.md +24 -0
  56. package/templates/planning/long-running-task-contract.md +69 -0
  57. package/templates/planning/module_plan.md +36 -0
  58. package/templates/planning/module_session_prompt.md +39 -0
  59. package/templates/planning/optional/artifacts/INDEX.md +12 -0
  60. package/templates/planning/optional/references/INDEX.md +13 -0
  61. package/templates/planning/optional/slices/_slice-template/brief.md +27 -0
  62. package/templates/planning/optional/slices/_slice-template/evidence.md +9 -0
  63. package/templates/planning/optional/slices/_slice-template/review.md +31 -0
  64. package/templates/planning/progress.md +33 -0
  65. package/templates/planning/review.md +48 -0
  66. package/templates/planning/task_plan.md +86 -0
  67. package/templates/planning/visual_roadmap.md +28 -0
  68. package/templates/reference/adversarial-review-standard.md +28 -0
  69. package/templates/reference/ci-cd-standard.md +28 -0
  70. package/templates/reference/delivery-operating-model-standard.md +28 -0
  71. package/templates/reference/docs-library-standard.md +28 -0
  72. package/templates/reference/engineering-standard.md +29 -0
  73. package/templates/reference/execution-workflow-standard.md +29 -0
  74. package/templates/reference/harness-ledger-standard.md +26 -0
  75. package/templates/reference/long-running-task-standard.md +28 -0
  76. package/templates/reference/regression-ssot-governance.md +28 -0
  77. package/templates/reference/repo-governance-standard.md +29 -0
  78. package/templates/reference/review-routing-standard.md +29 -0
  79. package/templates/reference/testing-standard.md +28 -0
  80. package/templates/reference/walkthrough-standard.md +28 -0
  81. package/templates/reference/worktree-standard.md +28 -0
  82. package/templates/regression/Cadence-Ledger.md +41 -0
  83. package/templates/ssot/Delivery-SSoT.md +43 -0
  84. package/templates/ssot/Feature-SSoT.md +43 -0
  85. package/templates/ssot/Lessons-SSoT.md +44 -0
  86. package/templates/ssot/Module-Registry.md +43 -0
  87. package/templates/ssot/Regression-SSoT.md +51 -0
  88. package/templates/verifier/verifier-output.md +43 -0
  89. package/templates/walkthrough/Closeout-SSoT.md +43 -0
  90. package/templates/walkthrough/walkthrough-template.md +63 -0
  91. package/templates-zh-CN/AGENTS.md.template +92 -0
  92. package/templates-zh-CN/CLAUDE.md.template +12 -0
  93. package/templates-zh-CN/dashboard/assets/app.css +399 -0
  94. package/templates-zh-CN/dashboard/assets/app.js +435 -0
  95. package/templates-zh-CN/dashboard/assets/i18n.js +47 -0
  96. package/templates-zh-CN/dashboard/assets/markdown-reader.js +116 -0
  97. package/templates-zh-CN/dashboard/assets/mermaid-renderer.js +59 -0
  98. package/templates-zh-CN/dashboard/index.html +18 -0
  99. package/templates-zh-CN/ledger/Harness-Ledger.md +50 -0
  100. package/templates-zh-CN/lessons/lesson-arch-process-change.md +47 -0
  101. package/templates-zh-CN/lessons/lesson-new-doc.md +49 -0
  102. package/templates-zh-CN/lessons/lesson-ref-change.md +59 -0
  103. package/templates-zh-CN/planning/execution_strategy.md +37 -0
  104. package/templates-zh-CN/planning/findings.md +24 -0
  105. package/templates-zh-CN/planning/long-running-task-contract.md +118 -0
  106. package/templates-zh-CN/planning/module_plan.md +43 -0
  107. package/templates-zh-CN/planning/module_session_prompt.md +70 -0
  108. package/templates-zh-CN/planning/optional/artifacts/INDEX.md +13 -0
  109. package/templates-zh-CN/planning/optional/references/INDEX.md +13 -0
  110. package/templates-zh-CN/planning/optional/slices/_slice-template/brief.md +35 -0
  111. package/templates-zh-CN/planning/optional/slices/_slice-template/evidence.md +12 -0
  112. package/templates-zh-CN/planning/optional/slices/_slice-template/review.md +37 -0
  113. package/templates-zh-CN/planning/progress.md +29 -0
  114. package/templates-zh-CN/planning/review.md +69 -0
  115. package/templates-zh-CN/planning/task_plan.md +116 -0
  116. package/templates-zh-CN/planning/visual_roadmap.md +24 -0
  117. package/templates-zh-CN/reference/adversarial-review-standard.md +89 -0
  118. package/templates-zh-CN/reference/ci-cd-standard.md +72 -0
  119. package/templates-zh-CN/reference/delivery-operating-model-standard.md +79 -0
  120. package/templates-zh-CN/reference/docs-library-standard.md +59 -0
  121. package/templates-zh-CN/reference/engineering-standard.md +80 -0
  122. package/templates-zh-CN/reference/execution-workflow-standard.md +81 -0
  123. package/templates-zh-CN/reference/harness-ledger-standard.md +91 -0
  124. package/templates-zh-CN/reference/long-running-task-standard.md +156 -0
  125. package/templates-zh-CN/reference/regression-ssot-governance.md +82 -0
  126. package/templates-zh-CN/reference/repo-governance-standard.md +84 -0
  127. package/templates-zh-CN/reference/review-routing-standard.md +82 -0
  128. package/templates-zh-CN/reference/testing-standard.md +72 -0
  129. package/templates-zh-CN/reference/walkthrough-standard.md +83 -0
  130. package/templates-zh-CN/reference/worktree-standard.md +116 -0
  131. package/templates-zh-CN/regression/Cadence-Ledger.md +48 -0
  132. package/templates-zh-CN/ssot/Delivery-SSoT.md +60 -0
  133. package/templates-zh-CN/ssot/Feature-SSoT.md +49 -0
  134. package/templates-zh-CN/ssot/Lessons-SSoT.md +49 -0
  135. package/templates-zh-CN/ssot/Module-Registry.md +48 -0
  136. package/templates-zh-CN/ssot/Regression-SSoT.md +51 -0
  137. package/templates-zh-CN/verifier/verifier-output.md +38 -0
  138. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +42 -0
  139. package/templates-zh-CN/walkthrough/walkthrough-template.md +62 -0
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { createInterface } from "node:readline/promises";
6
+ import {
7
+ addCapability,
8
+ buildStatus,
9
+ doctorUserSkill,
10
+ installUserSkill,
11
+ renderDashboard,
12
+ normalizeLocale,
13
+ writeDashboardFolder,
14
+ writeInitFiles,
15
+ } from "./lib/harness-core.mjs";
16
+
17
+ const args = process.argv.slice(2);
18
+ const command = args.shift() || "help";
19
+
20
+ function takeFlag(name, fallback = false) {
21
+ const index = args.indexOf(name);
22
+ if (index < 0) return fallback;
23
+ args.splice(index, 1);
24
+ return true;
25
+ }
26
+
27
+ function takeOption(name, fallback = "") {
28
+ const index = args.indexOf(name);
29
+ if (index < 0) return fallback;
30
+ const value = args[index + 1] || fallback;
31
+ args.splice(index, 2);
32
+ return value;
33
+ }
34
+
35
+ async function resolveInitLocale(requestedLocale) {
36
+ if (requestedLocale) return normalizeLocale(requestedLocale);
37
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return "en-US";
38
+
39
+ const prompt = [
40
+ "Select harness language / 选择初始化语言:",
41
+ " 1. 中文 (zh-CN)",
42
+ " 2. English (en-US)",
43
+ "Language [1/2, default 2]: ",
44
+ ].join("\n");
45
+ const reader = createInterface({ input: process.stdin, output: process.stdout });
46
+ try {
47
+ const answer = (await reader.question(prompt)).trim().toLowerCase();
48
+ if (["1", "zh", "zh-cn", "cn", "中文"].includes(answer)) return "zh-CN";
49
+ if (["2", "en", "en-us", "english", "英文", ""].includes(answer)) return "en-US";
50
+ console.error(`Unknown language selection: ${answer}. Falling back to en-US.`);
51
+ return "en-US";
52
+ } finally {
53
+ reader.close();
54
+ }
55
+ }
56
+
57
+ async function confirmUserInstall({ yes = false, dryRun = false, agent = "codex" } = {}) {
58
+ if (yes || dryRun) return true;
59
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
60
+ const reader = createInterface({ input: process.stdin, output: process.stdout });
61
+ try {
62
+ const answer = (await reader.question(`Install Coding Agent Harness into user skill directory for ${agent}? [y/N] `)).trim().toLowerCase();
63
+ return ["y", "yes"].includes(answer);
64
+ } finally {
65
+ reader.close();
66
+ }
67
+ }
68
+
69
+ function targetArg() {
70
+ return args[args.length - 1] && !args[args.length - 1].startsWith("-") ? args[args.length - 1] : ".";
71
+ }
72
+
73
+ function printHelp() {
74
+ console.log(`Coding Agent Harness
75
+
76
+ Usage:
77
+ harness check [--profile source-package|private-harness|target-project] [target]
78
+ harness status [--json] [--strict] [target]
79
+ harness dashboard [--out file.html] [--out-dir folder] [target]
80
+ harness init [--dry-run] [--locale zh-CN|en-US] [--capabilities core,dashboard] [target]
81
+ harness add-capability <name> [--dry-run] [--locale zh-CN|en-US] [target]
82
+ harness install-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir] [--dry-run] [--force] [--yes]
83
+ harness doctor-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir]
84
+
85
+ If init runs in an interactive terminal and --locale is omitted, it asks for a
86
+ language. Non-interactive init defaults to en-US.
87
+ `);
88
+ }
89
+
90
+ function exitWithReport(report) {
91
+ for (const warning of report.warnings || []) console.log(`Warning: ${warning}`);
92
+ for (const failure of report.failures || []) console.error(`Failure: ${failure}`);
93
+ process.exit((report.failures || []).length > 0 ? 1 : 0);
94
+ }
95
+
96
+ if (command === "help" || command === "--help" || command === "-h") {
97
+ printHelp();
98
+ } else if (command === "check") {
99
+ const profile = takeOption("--profile", "target-project");
100
+ const strict = takeFlag("--strict");
101
+ const target = targetArg();
102
+ const failures = [];
103
+ const warnings = [];
104
+
105
+ if (profile === "source-package") {
106
+ for (const required of ["package.json", "scripts/harness.mjs", "scripts/check-harness.mjs", "templates/planning/task_plan.md"]) {
107
+ if (!fs.existsSync(path.resolve(target, required))) failures.push(`missing source package file: ${required}`);
108
+ }
109
+ }
110
+
111
+ const status = buildStatus(target, { skipLegacyCheck: profile === "source-package", strictLegacy: strict, strict });
112
+ failures.push(...status.checkState.details.failures);
113
+ warnings.push(...status.checkState.details.warnings);
114
+
115
+ if (!["source-package", "private-harness", "target-project"].includes(profile)) failures.push(`unknown profile: ${profile}`);
116
+ if (failures.length === 0) console.log(`Harness check passed (${profile}): ${path.resolve(target)}`);
117
+ exitWithReport({ failures: [...new Set(failures)], warnings: [...new Set(warnings)] });
118
+ } else if (command === "status") {
119
+ const json = takeFlag("--json");
120
+ const strict = takeFlag("--strict");
121
+ const status = buildStatus(targetArg(), { strictLegacy: strict, strict });
122
+ if (json) {
123
+ console.log(JSON.stringify(status, null, 2));
124
+ } else {
125
+ console.log(`${status.project.name}: ${status.checkState.status} (${status.checkState.failures} failures, ${status.checkState.warnings} warnings)`);
126
+ console.log(`mode: ${status.mode}`);
127
+ console.log(`capabilities: ${status.capabilities.map((capability) => `${capability.name}:${capability.state}`).join(", ")}`);
128
+ console.log(`tasks: ${status.tasks.length}`);
129
+ }
130
+ process.exit(status.checkState.status === "fail" ? 1 : 0);
131
+ } else if (command === "dashboard") {
132
+ const out = takeOption("--out", "harness-dashboard.html");
133
+ const outDir = takeOption("--out-dir", "");
134
+ if (outDir) {
135
+ console.log(writeDashboardFolder(outDir, targetArg()));
136
+ } else {
137
+ const status = buildStatus(targetArg());
138
+ const html = renderDashboard(status);
139
+ fs.mkdirSync(path.dirname(path.resolve(out)), { recursive: true });
140
+ fs.writeFileSync(path.resolve(out), html);
141
+ console.log(path.resolve(out));
142
+ }
143
+ process.exit(0);
144
+ } else if (command === "init") {
145
+ const dryRun = takeFlag("--dry-run");
146
+ const locale = await resolveInitLocale(takeOption("--locale", ""));
147
+ const capabilities = takeOption("--capabilities", "core").split(",").map((item) => item.trim()).filter(Boolean);
148
+ try {
149
+ const result = writeInitFiles(targetArg(), capabilities, { dryRun, locale });
150
+ console.log(JSON.stringify({ dryRun, locale: result.locale, capabilities: result.capabilities, changes: result.changes, report: result.report }, null, 2));
151
+ } catch (error) {
152
+ console.error(error.message);
153
+ process.exit(1);
154
+ }
155
+ } else if (command === "add-capability") {
156
+ const dryRun = takeFlag("--dry-run");
157
+ const locale = normalizeLocale(takeOption("--locale", ""));
158
+ const capability = args.shift();
159
+ if (!capability) {
160
+ console.error("Missing capability name");
161
+ process.exit(2);
162
+ }
163
+ try {
164
+ const result = addCapability(targetArg(), capability, { dryRun, locale });
165
+ console.log(JSON.stringify({ dryRun, registry: result.registry, changes: result.changes, report: result.report }, null, 2));
166
+ } catch (error) {
167
+ console.error(error.message);
168
+ process.exit(1);
169
+ }
170
+ } else if (command === "install-user") {
171
+ const dryRun = takeFlag("--dry-run");
172
+ const force = takeFlag("--force");
173
+ const yes = takeFlag("--yes") || takeFlag("-y");
174
+ takeFlag("--global");
175
+ const agent = takeOption("--agent", "codex");
176
+ const home = takeOption("--home", "");
177
+ if (!(await confirmUserInstall({ yes, dryRun, agent }))) {
178
+ console.error("Refusing to write user skill files without confirmation. Re-run with --yes or --dry-run.");
179
+ process.exit(2);
180
+ }
181
+ try {
182
+ console.log(JSON.stringify(installUserSkill({ agent, home, dryRun, force }), null, 2));
183
+ } catch (error) {
184
+ console.error(error.message);
185
+ process.exit(1);
186
+ }
187
+ } else if (command === "doctor-user") {
188
+ const agent = takeOption("--agent", "codex");
189
+ const home = takeOption("--home", "");
190
+ try {
191
+ const report = doctorUserSkill({ agent, home });
192
+ console.log(JSON.stringify(report, null, 2));
193
+ process.exit(report.status === "pass" ? 0 : 1);
194
+ } catch (error) {
195
+ console.error(error.message);
196
+ process.exit(1);
197
+ }
198
+ } else {
199
+ printHelp();
200
+ process.exit(2);
201
+ }
@@ -0,0 +1,95 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), "../..");
5
+ const dashboardTemplateRoot = path.join(repoRoot, "templates/dashboard");
6
+ const dashboardMarker = ".harness-dashboard";
7
+
8
+ export function writeJsonFile(filePath, value) {
9
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
10
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
11
+ }
12
+
13
+ export function copyDashboardAssets(outDir, options = {}) {
14
+ copyDirectory(dashboardTemplateRootForLocale(options.locale), outDir);
15
+ }
16
+
17
+ export function writeDashboardDirectory(outDir, bundle, options = {}) {
18
+ const target = path.resolve(outDir);
19
+ assertSafeDashboardTarget(target, options);
20
+ if (fs.existsSync(target)) fs.rmSync(target, { recursive: true, force: true });
21
+ copyDashboardAssets(target, options);
22
+ fs.writeFileSync(path.join(target, dashboardMarker), "generated dashboard directory\n");
23
+ writeJsonFile(path.join(target, "data/status.json"), bundle.status);
24
+ writeJsonFile(path.join(target, "data/tables.json"), bundle.tables);
25
+ writeJsonFile(path.join(target, "data/documents.json"), bundle.documents);
26
+ writeJsonFile(path.join(target, "data/graph.json"), bundle.graph);
27
+ writeJsonFile(path.join(target, "data/adoption.json"), bundle.adoption);
28
+ fs.writeFileSync(
29
+ path.join(target, "assets/dashboard-data.js"),
30
+ `window.__HARNESS_DASHBOARD__ = ${JSON.stringify(bundle, null, 2)};\n`,
31
+ );
32
+ fs.writeFileSync(
33
+ path.join(target, "README.md"),
34
+ [
35
+ "# Harness Dashboard",
36
+ "",
37
+ "This is a read-only static snapshot generated by `harness dashboard --out-dir`.",
38
+ "Open `index.html` directly in a browser. No target project files are read at browser runtime.",
39
+ "",
40
+ ].join("\n"),
41
+ );
42
+ return target;
43
+ }
44
+
45
+ function assertSafeDashboardTarget(target, options) {
46
+ const localizedDashboardTemplateRoot = dashboardTemplateRootForLocale(options.locale);
47
+ const protectedRoots = [
48
+ path.parse(target).root,
49
+ process.env.HOME,
50
+ options.repoRoot,
51
+ options.projectRoot,
52
+ options.docsRoot,
53
+ dashboardTemplateRoot,
54
+ localizedDashboardTemplateRoot,
55
+ ].filter(Boolean).map((item) => path.resolve(item));
56
+ if (protectedRoots.includes(target)) {
57
+ throw new Error(`Refusing unsafe dashboard output directory: ${target}`);
58
+ }
59
+ for (const root of [options.docsRoot, dashboardTemplateRoot, localizedDashboardTemplateRoot].filter(Boolean).map((item) => path.resolve(item))) {
60
+ if (isPathInside(target, root)) {
61
+ throw new Error(`Refusing dashboard output inside protected directory: ${target}`);
62
+ }
63
+ }
64
+ if (fs.existsSync(target)) {
65
+ const entries = fs.readdirSync(target);
66
+ const hasMarker = fs.existsSync(path.join(target, dashboardMarker));
67
+ if (entries.length > 0 && !hasMarker) {
68
+ throw new Error(`Refusing to overwrite non-dashboard directory: ${target}`);
69
+ }
70
+ }
71
+ }
72
+
73
+ function dashboardTemplateRootForLocale(locale = "en-US") {
74
+ const localized = locale === "zh-CN" ? path.join(repoRoot, "templates-zh-CN/dashboard") : dashboardTemplateRoot;
75
+ return fs.existsSync(localized) ? localized : dashboardTemplateRoot;
76
+ }
77
+
78
+ function isPathInside(candidate, parent) {
79
+ const relative = path.relative(parent, candidate);
80
+ return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
81
+ }
82
+
83
+ function copyDirectory(from, to) {
84
+ if (!fs.existsSync(from)) throw new Error(`Missing dashboard template directory: ${from}`);
85
+ fs.mkdirSync(to, { recursive: true });
86
+ for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
87
+ const source = path.join(from, entry.name);
88
+ const destination = path.join(to, entry.name);
89
+ if (entry.isDirectory()) {
90
+ copyDirectory(source, destination);
91
+ } else {
92
+ fs.copyFileSync(source, destination);
93
+ }
94
+ }
95
+ }