@cardor/agent-harness-kit 1.4.4 → 1.5.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.
@@ -6,11 +6,10 @@ description: >
6
6
  and the explorer's analysis. Invoke only after the explorer has completed its action.
7
7
  Never invoke without a lead plan and explorer analysis available in actions.get(taskId).
8
8
  tools:
9
- read: true
10
- write: true
11
- edit: true
12
- bash: true
13
- permissionMode: acceptEdits
9
+ - Read
10
+ - Write
11
+ - Edit
12
+ - Bash
14
13
  ---
15
14
 
16
15
  # Builder Agent — {{projectName}}
@@ -100,7 +99,15 @@ The explorer identified how this codebase works. Use those patterns. Do not intr
100
99
 
101
100
  If tests fail, fix them before completing your action. Do not leave the codebase in a broken state.
102
101
 
103
- ### 6. Record your result
102
+ ### 6. Sync README and docs after codebase changes
103
+
104
+ If your changes affect public APIs, CLI commands, configuration, or any user-facing behavior, update the relevant sections of `README.md` and any files under `./docs/` to reflect the new state.
105
+
106
+ - Do not leave docs describing behavior that no longer exists.
107
+ - Do not add implementation details that belong in code comments, not docs.
108
+ - If no user-facing behavior changed, you may skip this step — but note that explicitly in your result.
109
+
110
+ ### 7. Record your result
104
111
 
105
112
  ```
106
113
  actions.write(actionId, 'result', '<summary of what was implemented>')
@@ -108,7 +115,7 @@ actions.write(actionId, 'result', '<summary of what was implemented>')
108
115
 
109
116
  Include: what was created, what was modified, what was deleted, and any decisions you made.
110
117
 
111
- ### 7. Record blockers if stuck
118
+ ### 8. Record blockers if stuck
112
119
 
113
120
  If you cannot implement something (missing dependency, conflicting pattern, unclear requirement):
114
121
 
@@ -118,7 +125,7 @@ actions.write(actionId, 'blockers', '<specific blocker — what is needed to unb
118
125
 
119
126
  Then complete your action with a blocked status — do not guess through ambiguity.
120
127
 
121
- ### 8. Complete your action
128
+ ### 9. Complete your action
122
129
 
123
130
  ```
124
131
  actions.complete(actionId, 'Implementation done — N files modified, tests passing')
@@ -6,11 +6,8 @@ description: >
6
6
  builder to use. Invoke after the lead has defined a plan and before the builder starts.
7
7
  Never invoke for tasks that require writing or modifying files.
8
8
  tools:
9
- read: true
10
- write: false
11
- edit: false
12
- bash: true
13
- permissionMode: plan
9
+ - Read
10
+ - Bash
14
11
  ---
15
12
 
16
13
  # Explorer Agent — {{projectName}}
@@ -6,10 +6,8 @@ description: >
6
6
  Invoke when starting a new work session, picking up a pending task, or when another agent
7
7
  reports a blocker that requires re-coordination.
8
8
  tools:
9
- read: true
10
- write: false
11
- edit: false
12
- bash: true
9
+ - Read
10
+ - Bash
13
11
  ---
14
12
 
15
13
  # Lead Agent — {{projectName}}
@@ -64,7 +62,7 @@ actions.record_tool(actionId, '<ToolName>', '<args-summary>', '<why>')
64
62
  ```
65
63
 
66
64
  Examples:
67
- - `actions.record_tool(actionId, 'Bash', 'bash health.sh', 'verify codebase health before starting')`
65
+ - `actions.record_tool(actionId, 'Bash', 'bash health.sh', 'verify codebase health before making changes')`
68
66
  - `actions.record_tool(actionId, 'tasks.get', 'pending', 'find next task to claim')`
69
67
  - `actions.record_tool(actionId, 'actions.get', 'taskId=abc123', 'read action history to resume in-progress task')`
70
68
 
@@ -74,7 +72,14 @@ Examples:
74
72
 
75
73
  ## Workflow
76
74
 
77
- ### 1. Orient (always first)
75
+ ### 0. Assess user intent (before running health check)
76
+
77
+ Before running the health check, evaluate whether the user's prompt requires codebase changes:
78
+
79
+ - **If the user is simply asking a question, checking something, or seeking information** (no code changes needed) → skip the health check entirely. Proceed to respond to the query directly.
80
+ - **If the user wants to make changes** (refactor, fix, add feature, modify config, or any codebase modification) → proceed to Step 1 below and run health check.
81
+
82
+ ### 1. Orient (run health check when making changes)
78
83
 
79
84
  ```
80
85
  bash health.sh
@@ -128,6 +133,7 @@ Think through:
128
133
  - What does the explorer need to map?
129
134
  - What exactly should the builder implement?
130
135
  - What are the acceptance criteria the reviewer will check?
136
+ - If codebase changes are involved: does the builder need to update README or `docs/` files?
131
137
 
132
138
  Record it:
133
139
 
@@ -165,9 +171,16 @@ If the reviewer blocks the task:
165
171
  Once the reviewer approves:
166
172
  ```
167
173
  tasks.update(taskId, 'done')
168
- bash health.sh → must be green before closing
174
+ bash health.sh → must be green before closing (only if changes were made)
175
+ ```
176
+
177
+ Then check for a `graphify-out/` directory:
178
+
179
+ ```bash
180
+ ls graphify-out/ 2>/dev/null
169
181
  ```
170
182
 
183
+ If it exists and contains files, ask the user whether to resync (re-run `/graphify`) before finishing. Do not resync automatically — always ask first.
171
184
 
172
185
  ---
173
186
 
@@ -183,9 +196,10 @@ When creating a PR via the CLI, gather context in this order:
183
196
 
184
197
  - **One task at a time.** Never pick a second task while one is in progress.
185
198
  - **YOU DO NOT MODIFY THE CODEBASE — EVER.** No file writes, no edits, no Bash commands that change state. Delegate ALL implementation to Builder, ALL analysis to Explorer.
186
- - **Bash is read-only.** The only Bash commands you may run are: `bash health.sh`, `git status/log/diff`, `ls`, `cat`, `find`, `grep`. Nothing that writes.
199
+ - **Bash is read-only.** The only Bash commands you may run are: `bash health.sh` (only when making changes), `git status/log/diff`, `ls`, `cat`, `find`, `grep`. Nothing that writes.
187
200
  - **Never mark done without reviewer approval.**
188
201
  - **If blocked and unsure how to proceed:** record a blocker in your action and stop the session cleanly.
202
+ - **Skip health check for informational queries.** If the user is just asking a question, do not run health.sh.
189
203
 
190
204
  ## Anti-patterns to avoid
191
205
 
@@ -6,11 +6,8 @@ description: >
6
6
  changes against each criterion, runs the health check, and either approves or blocks
7
7
  with specific, actionable feedback. Invoke only after the builder has completed its action.
8
8
  tools:
9
- read: true
10
- write: false
11
- edit: false
12
- bash: true
13
- permissionMode: plan
9
+ - Read
10
+ - Bash
14
11
  ---
15
12
 
16
13
  # Reviewer Agent — {{projectName}}
package/dist/cli.js CHANGED
@@ -67,6 +67,47 @@ function mergeClaudeSettingsJson(filePath) {
67
67
  };
68
68
  writeFileSync2(filePath, JSON.stringify(merged, null, 2) + "\n", "utf8");
69
69
  }
70
+ var MCP_PERMISSIONS = [
71
+ "mcp__agent-harness-kit__actions_start",
72
+ "mcp__agent-harness-kit__actions_write",
73
+ "mcp__agent-harness-kit__actions_complete",
74
+ "mcp__agent-harness-kit__actions_get",
75
+ "mcp__agent-harness-kit__actions_record_file",
76
+ "mcp__agent-harness-kit__actions_record_tool",
77
+ "mcp__agent-harness-kit__tasks_get",
78
+ "mcp__agent-harness-kit__tasks_claim",
79
+ "mcp__agent-harness-kit__tasks_update",
80
+ "mcp__agent-harness-kit__tasks_add",
81
+ "mcp__agent-harness-kit__tasks_acceptance_update",
82
+ "mcp__agent-harness-kit__tasks_edit",
83
+ "mcp__agent-harness-kit__tasks_archive",
84
+ "mcp__agent-harness-kit__tasks_unarchive",
85
+ "mcp__agent-harness-kit__docs_search"
86
+ ];
87
+ function mergeClaudeSettingsLocalJson(filePath) {
88
+ mkdirSync2(dirname(filePath), { recursive: true });
89
+ let existing = {};
90
+ if (existsSync(filePath)) {
91
+ try {
92
+ existing = JSON.parse(readFileSync(filePath, "utf8"));
93
+ } catch {
94
+ }
95
+ }
96
+ const existingPermissions = existing.permissions ?? {};
97
+ const existingAllow = existingPermissions.allow ?? [];
98
+ const existingServers = existing.enabledMcpjsonServers ?? [];
99
+ const mergedAllow = Array.from(/* @__PURE__ */ new Set([...existingAllow, ...MCP_PERMISSIONS]));
100
+ const mergedServers = Array.from(/* @__PURE__ */ new Set([...existingServers, "agent-harness-kit"]));
101
+ const merged = {
102
+ ...existing,
103
+ permissions: {
104
+ ...existingPermissions,
105
+ allow: mergedAllow
106
+ },
107
+ enabledMcpjsonServers: mergedServers
108
+ };
109
+ writeFileSync2(filePath, JSON.stringify(merged, null, 2) + "\n", "utf8");
110
+ }
70
111
  function mergeOpencodeJson(filePath, port) {
71
112
  const folderPath = dirname(filePath);
72
113
  if (!existsSync(folderPath)) {
@@ -153,7 +194,7 @@ var HEALTH_SH = `#!/usr/bin/env bash
153
194
  # health.sh \u2014 project health check for agent-harness-kit
154
195
  #
155
196
  # This script must exit 0 when the project is healthy.
156
- # Agents will run this before starting work.
197
+ # Agents will run this before making codebase changes.
157
198
  #
158
199
  # TODO: implement your project's health checks below.
159
200
  # Examples:
@@ -180,13 +221,13 @@ function agentsMd(config) {
180
221
 
181
222
  **${name}** \u2014 ${description}
182
223
 
183
- ## Health check (run before starting)
224
+ ## Health check (run before making codebase changes)
184
225
 
185
226
  \`\`\`bash
186
227
  bash health.sh
187
228
  \`\`\`
188
229
 
189
- If it exits non-zero, stop and report the issue. Do not proceed with tasks until health is green.
230
+ If it exits non-zero, stop and report the issue. Do not proceed with codebase changes until health is green.
190
231
 
191
232
  ## Harness data (source of truth)
192
233
 
@@ -219,7 +260,7 @@ docs.search query \u2192 search ${docs
219
260
 
220
261
  \`\`\`
221
262
  1. INIT
222
- - Run health.sh \u2192 exit 1 means stop
263
+ - Assess user intent: only run health.sh if changes are needed
223
264
  - tasks.get('in_progress') \u2192 resume if something is in progress
224
265
  - tasks.get('pending') \u2192 pick lowest id
225
266
 
@@ -231,7 +272,7 @@ docs.search query \u2192 search ${docs
231
272
 
232
273
  3. CLOSE
233
274
  - tasks.update(taskId, 'done')
234
- - Run health.sh \u2192 must be green before closing
275
+ - Run health.sh (if changes were made) \u2192 must be green before closing
235
276
  \`\`\`
236
277
 
237
278
  ## Agent roles
@@ -263,13 +304,13 @@ function claudeMd(config) {
263
304
 
264
305
  **${name}** \u2014 ${description}
265
306
 
266
- ## Health check (run before starting)
307
+ ## Health check (run before making codebase changes)
267
308
 
268
309
  \`\`\`bash
269
310
  bash health.sh
270
311
  \`\`\`
271
312
 
272
- If it exits non-zero, stop and report the issue. Do not proceed with tasks until health is green.
313
+ If it exits non-zero, stop and report the issue. Do not proceed with codebase changes until health is green.
273
314
 
274
315
  ## Harness data (source of truth)
275
316
 
@@ -302,7 +343,7 @@ docs.search query \u2192 search ${docs
302
343
 
303
344
  \`\`\`
304
345
  1. INIT
305
- - Run health.sh \u2192 exit 1 means stop
346
+ - Assess user intent: only run health.sh if changes are needed
306
347
  - tasks.get('in_progress') \u2192 resume if something is in progress
307
348
  - tasks.get('pending') \u2192 pick lowest id
308
349
  - No pending tasks? \u2192 ask user, infer fields, call tasks.add, then tasks.claim
@@ -315,7 +356,7 @@ docs.search query \u2192 search ${docs
315
356
 
316
357
  3. CLOSE
317
358
  - tasks.update(taskId, 'done')
318
- - Run health.sh \u2192 must be green before closing
359
+ - Run health.sh (if changes were made) \u2192 must be green before closing
319
360
  \`\`\`
320
361
 
321
362
  ## Agent roles
@@ -450,6 +491,55 @@ function agentReviewerToml(vars) {
450
491
  const { description, body } = stripFrontmatter(loadAgentTemplate("reviewer", vars));
451
492
  return toCodexToml("reviewer", description, body, "read-only");
452
493
  }
494
+ var CLAUDE_CODE_MCP_TOOLS = {
495
+ lead: [
496
+ // 'mcp__agent-harness-kit__actions.start',
497
+ // 'mcp__agent-harness-kit__actions.write',
498
+ // 'mcp__agent-harness-kit__actions.complete',
499
+ // 'mcp__agent-harness-kit__actions.get',
500
+ // 'mcp__agent-harness-kit__actions.record_tool',
501
+ // 'mcp__agent-harness-kit__tasks.get',
502
+ // 'mcp__agent-harness-kit__tasks.claim',
503
+ // 'mcp__agent-harness-kit__tasks.update',
504
+ // 'mcp__agent-harness-kit__tasks.add',
505
+ ],
506
+ explorer: [
507
+ // 'mcp__agent-harness-kit__actions.start',
508
+ // 'mcp__agent-harness-kit__actions.write',
509
+ // 'mcp__agent-harness-kit__actions.complete',
510
+ // 'mcp__agent-harness-kit__actions.get',
511
+ // 'mcp__agent-harness-kit__actions.record_tool',
512
+ // 'mcp__agent-harness-kit__docs.search',
513
+ ],
514
+ builder: [
515
+ // 'mcp__agent-harness-kit__actions.start',
516
+ // 'mcp__agent-harness-kit__actions.write',
517
+ // 'mcp__agent-harness-kit__actions.complete',
518
+ // 'mcp__agent-harness-kit__actions.get',
519
+ // 'mcp__agent-harness-kit__actions.record_tool',
520
+ // 'mcp__agent-harness-kit__actions.record_file',
521
+ ],
522
+ reviewer: [
523
+ // 'mcp__agent-harness-kit__actions.start',
524
+ // 'mcp__agent-harness-kit__actions.write',
525
+ // 'mcp__agent-harness-kit__actions.complete',
526
+ // 'mcp__agent-harness-kit__actions.get',
527
+ // 'mcp__agent-harness-kit__actions.record_tool',
528
+ // 'mcp__agent-harness-kit__tasks.acceptance.update',
529
+ // 'mcp__agent-harness-kit__tasks.update',
530
+ ]
531
+ };
532
+ function translateFrontmatterForClaudeCode(md, agentName) {
533
+ const mcpTools = CLAUDE_CODE_MCP_TOOLS[agentName] ?? [];
534
+ const mcpLines = mcpTools.map((t) => ` - ${t}`).join("\n");
535
+ return md.replace(/(tools:\n(?: - (?!mcp__)[^\n]+\n)+)/, (match) => {
536
+ const trimmed = match.trimEnd();
537
+ return `${trimmed}
538
+ - Task
539
+ ${mcpLines}
540
+ `;
541
+ });
542
+ }
453
543
  var GITIGNORE_ENTRIES = `
454
544
  # agent-harness-kit
455
545
  .harness/harness.db
@@ -504,12 +594,13 @@ No tasks in progress.
504
594
  const projectName = config.project.name;
505
595
  const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
506
596
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
507
- writeAgentFile(cwd2, ".claude/agents/lead.md", agentLead({ projectName }));
508
- writeAgentFile(cwd2, ".claude/agents/explorer.md", agentExplorer({ projectName, allowedPaths }));
509
- writeAgentFile(cwd2, ".claude/agents/builder.md", agentBuilder({ projectName, writablePaths }));
510
- writeAgentFile(cwd2, ".claude/agents/reviewer.md", agentReviewer({ projectName }));
511
- mergeClaudeMcpJson(join4(cwd2, ".claude/mcp.json"), config.tools.mcp.port);
597
+ writeAgentFile(cwd2, ".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead"));
598
+ writeAgentFile(cwd2, ".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer"));
599
+ writeAgentFile(cwd2, ".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder"));
600
+ writeAgentFile(cwd2, ".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer"));
601
+ mergeClaudeMcpJson(join4(cwd2, ".mcp.json"), config.tools.mcp.port);
512
602
  mergeClaudeSettingsJson(join4(cwd2, ".claude/settings.json"));
603
+ mergeClaudeSettingsLocalJson(join4(cwd2, ".claude/settings.local.json"));
513
604
  appendGitignore(cwd2);
514
605
  }
515
606
  async build(config, cwd2) {
@@ -523,12 +614,13 @@ No tasks in progress.
523
614
  const projectName = config.project.name;
524
615
  const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
525
616
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
526
- writeAgentFile(cwd2, ".claude/agents/lead.md", agentLead({ projectName }));
527
- writeAgentFile(cwd2, ".claude/agents/explorer.md", agentExplorer({ projectName, allowedPaths }));
528
- writeAgentFile(cwd2, ".claude/agents/builder.md", agentBuilder({ projectName, writablePaths }));
529
- writeAgentFile(cwd2, ".claude/agents/reviewer.md", agentReviewer({ projectName }));
530
- mergeClaudeMcpJson(join4(cwd2, ".claude/mcp.json"), config.tools.mcp.port);
617
+ writeAgentFile(cwd2, ".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead"));
618
+ writeAgentFile(cwd2, ".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer"));
619
+ writeAgentFile(cwd2, ".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder"));
620
+ writeAgentFile(cwd2, ".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer"));
621
+ mergeClaudeMcpJson(join4(cwd2, ".mcp.json"), config.tools.mcp.port);
531
622
  mergeClaudeSettingsJson(join4(cwd2, ".claude/settings.json"));
623
+ mergeClaudeSettingsLocalJson(join4(cwd2, ".claude/settings.local.json"));
532
624
  }
533
625
  async migrate(config, _to, _cwd) {
534
626
  void config;
@@ -1636,7 +1728,7 @@ async function runHealth(cwd2) {
1636
1728
  function getProviderHealthFiles(provider) {
1637
1729
  switch (provider) {
1638
1730
  case "claude-code":
1639
- return { agentsDir: ".claude/agents", agentExtension: ".md", mcpFile: ".claude/mcp.json" };
1731
+ return { agentsDir: ".claude/agents", agentExtension: ".md", mcpFile: ".mcp.json" };
1640
1732
  case "opencode":
1641
1733
  return { agentsDir: ".opencode/agents", agentExtension: ".md", mcpFile: "opencode.json" };
1642
1734
  case "codex-cli":