buddy-agent-harness 1.1.0 → 1.2.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 (37) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/.cursor-plugin/plugin.json +1 -1
  4. package/CHANGELOG.md +19 -0
  5. package/dist/cli.mjs +86 -9
  6. package/dist/index.d.mts +10 -3
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.mjs +92 -8
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/skill-scripts/doctor.mjs +27 -27
  11. package/dist/skill-scripts/init.mjs +27 -27
  12. package/dist/skill-scripts/mcp.mjs +27 -27
  13. package/llms.txt +6 -5
  14. package/package.json +2 -1
  15. package/plugin.json +1 -1
  16. package/skills/doctor-buddy-agent-harness/README.md +1 -1
  17. package/skills/doctor-buddy-agent-harness/SKILL.md +9 -2
  18. package/skills/doctor-buddy-agent-harness/references/worktrees.md +17 -0
  19. package/skills/doctor-buddy-agent-harness/scripts/doctor.mjs +27 -27
  20. package/skills/enhance/SKILL.md +18 -10
  21. package/skills/enhance/references/agent-worktrees.md +59 -0
  22. package/skills/enhance/references/references-as-links.history.md +9 -0
  23. package/skills/enhance/references/references-as-links.md +76 -0
  24. package/skills/enhance/scripts/doctor.mjs +27 -27
  25. package/skills/init-buddy-agent-harness/SKILL.md +8 -5
  26. package/skills/init-buddy-agent-harness/references/agent-worktrees.md +59 -0
  27. package/skills/init-buddy-agent-harness/scripts/doctor.mjs +27 -27
  28. package/skills/init-buddy-agent-harness/scripts/init.mjs +27 -27
  29. package/skills/repair/README.md +2 -2
  30. package/skills/repair/SKILL.md +3 -3
  31. package/skills/repair/scripts/doctor.mjs +27 -27
  32. package/skills/repair/scripts/mcp.mjs +27 -27
  33. package/src/diagnose-bridges/doctor-guidance.ts +66 -1
  34. package/src/diagnose-bridges/doctor.command.ts +2 -0
  35. package/src/diagnose-bridges/git-bridge-state.ts +5 -0
  36. package/src/diagnose-worktrees/diagnose-worktrees.ts +69 -0
  37. package/src/index.ts +1 -0
@@ -72,6 +72,12 @@ export type GlobalInstructionProblem =
72
72
  | 'global-instructions-overridden'
73
73
  | 'global-instructions-emptied'
74
74
 
75
+ /**
76
+ * What a repository lacks for the git worktrees Claude Code creates under `.claude/worktrees/`;
77
+ * each is an offer `init` and `enhance` make, never a fault in a file the owner wrote.
78
+ */
79
+ export type WorktreeProblem = 'worktrees-untracked' | 'worktrees-scanned' | 'worktree-base-unset'
80
+
75
81
  /** Everything `doctor` can report against, across every section. */
76
82
  export type DoctorProblem =
77
83
  | BridgeProblem
@@ -80,6 +86,7 @@ export type DoctorProblem =
80
86
  | ConfigurationFault
81
87
  | McpProblem
82
88
  | NonstandardProblem
89
+ | WorktreeProblem
83
90
 
84
91
  /** Alias kept for callers that name the whole set rather than one section. */
85
92
  export type ConfigurationProblem = DoctorProblem
@@ -192,6 +199,7 @@ export const pnpmSkillRef = (invocation: string): string =>
192
199
  `\`${invocation}\` (pnpm install: \`pnpm-${commandInvocation}-${invocation.slice(invocation.indexOf(':') + 1)}\`)`
193
200
 
194
201
  export const initSkillRef: string = pnpmSkillRef(initSkillInvocation)
202
+ export const enhanceSkillRef: string = pnpmSkillRef('/buddy-agent-harness:enhance')
195
203
  export const repairSkillRef: string = pnpmSkillRef(repairSkillInvocation)
196
204
 
197
205
  /** Where the user authors the golden MCP server set, named in every MCP repair that points at it. */
@@ -589,6 +597,38 @@ const nonstandardTable: Record<NonstandardProblem, RepairRow> = {
589
597
 
590
598
  export const nonstandardRepairs: readonly Repair[] = repairsOf(nonstandardTable)
591
599
 
600
+ /** Who offers every worktree repair, named the same way in each row. */
601
+ const worktreeOffer = `${initSkillRef} offers it, or ${enhanceSkillRef} where the repository already has an AGENTS.md`
602
+
603
+ const worktreeTable: Record<WorktreeProblem, RepairRow> = {
604
+ 'worktrees-untracked': {
605
+ detail:
606
+ '.claude/worktrees/ is not git-ignored, so every agent worktree Claude Code creates there shows up as untracked in git status',
607
+ repair: ({ file }) => ({ command: '', instruction: `add \`.claude/worktrees/\` to ${file} — ${worktreeOffer}` }),
608
+ skillRepair: () => `run ${initSkillRef} or ${enhanceSkillRef}, which offers the .gitignore line`,
609
+ },
610
+ 'worktrees-scanned': {
611
+ detail:
612
+ 'this config does not exclude .claude/worktrees, so the tool walks into each agent worktree, a whole checkout with its own config, and Biome stops on the nested root configuration',
613
+ repair: ({ file }) => ({
614
+ command: '',
615
+ instruction: `add \`"!**/.claude/worktrees"\` to \`files.includes\` in ${file} — ${worktreeOffer}`,
616
+ }),
617
+ skillRepair: () => `run ${initSkillRef} or ${enhanceSkillRef}, which offers the exclusion`,
618
+ },
619
+ 'worktree-base-unset': {
620
+ detail:
621
+ 'worktree.baseRef is unset, so agent worktrees branch from the default branch rather than the current HEAD — work on a feature branch starts without its commits',
622
+ repair: ({ file }) => ({
623
+ command: '',
624
+ instruction: `decide whether agent worktrees should branch from HEAD, and if so set \`"worktree": { "baseRef": "head" }\` in ${file} — ${worktreeOffer}`,
625
+ }),
626
+ skillRepair: () => `run ${initSkillRef} or ${enhanceSkillRef}, which explains the trade-off and offers the setting`,
627
+ },
628
+ }
629
+
630
+ export const worktreeRepairs: readonly Repair[] = repairsOf(worktreeTable)
631
+
592
632
  /**
593
633
  * Annotated over the whole union rather than inferred, so an added problem fails to compile until
594
634
  * its section carries it.
@@ -600,6 +640,7 @@ const doctorTable: Record<DoctorProblem, RepairRow> = {
600
640
  ...configurationTable,
601
641
  ...mcpTable,
602
642
  ...nonstandardTable,
643
+ ...worktreeTable,
603
644
  }
604
645
 
605
646
  export const doctorRepairs: readonly Repair[] = repairsOf(doctorTable)
@@ -612,7 +653,7 @@ export function repairFor(problem: DoctorProblem): Repair {
612
653
  export const doctorSkill = {
613
654
  name: 'doctor-buddy-agent-harness',
614
655
  description:
615
- 'Use this skill when a repository loads no project skills, when skills are missing after a clone, when a harness appears to be ignoring AGENTS.md, or when checking whether the agent configuration bridges into .claude/skills and the other harness files still resolve.',
656
+ "Checks whether a repository's agent configuration bridges into .claude/skills and the other harness files still resolve — when a repository loads no project skills, when skills are missing after a clone, or when a harness appears to be ignoring AGENTS.md.",
616
657
  } as const
617
658
 
618
659
  /** Written into the generated skill so a reader knows not to edit it in place. */
@@ -699,6 +740,7 @@ Every \`problem\` name routes to exactly one page. Load the page for the finding
699
740
  | \`references/configuration.md\` | a finding about the configuration around the bridges rather than a bridge |
700
741
  | \`references/mcp.md\` | any finding whose path is an MCP locator — **always** before acting on a credential finding |
701
742
  | \`references/nonstandard.md\` | a finding about configuration only one harness can read |
743
+ | \`references/worktrees.md\` | a finding about the agent worktrees Claude Code creates under \`.claude/worktrees/\` |
702
744
  | \`references/harnesses/<name>.md\` | the paths and files one named harness uses |
703
745
 
704
746
  ## Rules
@@ -709,6 +751,12 @@ Every \`problem\` name routes to exactly one page. Load the page for the finding
709
751
  - Never repeat a value from a file an \`mcp-literal-secret\` or \`mcp-committed-secret\` finding points at. The report withheld it on purpose, and quoting it back puts it in the transcript anyway.
710
752
  - Never write outside the repository. A \`globalInstructions\` repair is a step for the user to take in their own home directory; hand it over exactly as reported.
711
753
  - Write instructions in \`AGENTS.md\`, never in \`CLAUDE.md\`. Content written there reaches one harness, drifts from the canonical file, and — because Claude Code prefers it — takes \`AGENTS.md\` out of that harness's context entirely.
754
+
755
+ ## Done when
756
+
757
+ - **C1.** The reply names each problem the report found and its cause, or, when the report found none, says the configuration is healthy and calls for no change.
758
+ - **C2.** No bridge, skill, or instruction file changed: no \`init\` ran, and nothing was written to \`CLAUDE.md\`.
759
+ - **C3.** The reply repeats no value that a secret finding withheld.
712
760
  `
713
761
  }
714
762
 
@@ -988,6 +1036,23 @@ ${repairTable(nonstandardRepairs)}
988
1036
  A harness directory that is a **symlink** is a projection someone already made, not configuration authored here. An artifact is reported whether or not its harness is enabled: a \`.cursorrules\` in a repository nobody opens in Cursor is still instruction content \`AGENTS.md\` does not carry, and filtering by the enabled set would hide exactly the drift worth converting.
989
1037
 
990
1038
  Hooks, LSP settings, and output styles are not covered yet. Their event names and shapes differ by harness with no safe projection, and half-reporting them would be worse than the silence.
1039
+ `,
1040
+ },
1041
+ {
1042
+ path: 'references/worktrees.md',
1043
+ content: `${generatedSkillWarning}
1044
+
1045
+ # Agent worktree findings
1046
+
1047
+ Claude Code runs a session started with \`--worktree\`, and a subagent isolated in a worktree, in a git worktree under \`.claude/worktrees/<name>/\` inside the repository. That place is by design, and no setting moves it. These findings are what a repository needs so the worktrees there do no harm. \`doctor\` checks them only where a \`.claude\` directory exists.
1048
+
1049
+ ${repairTable(worktreeRepairs)}
1050
+
1051
+ None of these is a fault in a file the owner wrote, and none is a \`repair\` correction. Each is an offer the \`init-buddy-agent-harness\` and \`enhance\` skills make, one at a time, and write only on approval.
1052
+
1053
+ \`worktree-base-unset\` is a choice, not an error. The default branch suits agents given unrelated tasks; \`"head"\` suits agents asked to build on the branch in front of them. Put the trade-off to the owner, and report an explicit \`"fresh"\` as their answer rather than as a finding — \`doctor\` already does.
1054
+
1055
+ \`worktrees-scanned\` is checked for Biome only, the one tool with a confirmed failure. A repository whose ESLint, Prettier, knip, or TypeScript configuration walks the tree can hit the same thing; the skills look for those when they make the offer.
991
1056
  `,
992
1057
  },
993
1058
  ...harnessRegistry.map((harness) => harnessPage(harness, initReferences.has(harness.name))),
@@ -7,6 +7,7 @@ import { collapseHome, displayBinPath, fail, parseHarnessOption } from '../comma
7
7
  import { type ConfigurationFinding, diagnoseConfiguration } from '../diagnose-configuration/diagnose-configuration.ts'
8
8
  import { diagnoseMcp } from '../diagnose-mcp/diagnose-mcp.ts'
9
9
  import { diagnoseNonstandard } from '../diagnose-nonstandard/diagnose-nonstandard.ts'
10
+ import { diagnoseWorktrees } from '../diagnose-worktrees/diagnose-worktrees.ts'
10
11
  import { referenceLayers } from '../references/references.ts'
11
12
  import { type DiagnoseResult, diagnoseBridges } from './diagnose-bridges.ts'
12
13
  import { commandInvocation, type DoctorProblem, type RepairAction } from './doctor-guidance.ts'
@@ -116,6 +117,7 @@ export async function diagnoseReport(args: {
116
117
  ...diagnoseConfiguration({ root, git, cli: commandInvocation }),
117
118
  ...diagnoseMcp({ root, git, cli: commandInvocation }),
118
119
  ...diagnoseNonstandard({ root, cli: commandInvocation }),
120
+ ...diagnoseWorktrees({ root, git, cli: commandInvocation }),
119
121
  ]
120
122
  const { layers: rows } = listReferences(
121
123
  await referenceLayers({
@@ -37,6 +37,11 @@ export class GitBridgeState {
37
37
  this.repoRootPrefix = git(root, ['rev-parse', '--show-prefix'])?.trim()
38
38
  }
39
39
 
40
+ /** Whether `root` is inside a git work tree at all; a check that needs git's answer skips when not. */
41
+ isRepository(): boolean {
42
+ return this.repoRootPrefix !== undefined
43
+ }
44
+
40
45
  /** The skip-worktree bit is a hint some checkouts clear, so it is verified rather than assumed. */
41
46
  trackingOf(path: string): TrackingState {
42
47
  if (this.repoRootPrefix === undefined) return 'untracked'
@@ -0,0 +1,69 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { type RepairAction, repairFor, type WorktreeProblem } from '../diagnose-bridges/doctor-guidance.ts'
4
+ import type { GitBridgeState } from '../diagnose-bridges/git-bridge-state.ts'
5
+ import { isRecord } from '../is-record/is-record.ts'
6
+
7
+ /**
8
+ * The agent-worktree half of `doctor`: what a repository lacks for the git worktrees Claude Code
9
+ * creates inside it under `.claude/worktrees/`. Read-only.
10
+ */
11
+ export type WorktreeFinding = {
12
+ /** Repository-relative path the finding is about. */
13
+ path: string
14
+ problem: WorktreeProblem
15
+ detail: string
16
+ /** Always an empty `command`: each repair is an offer the owner approves. */
17
+ repair: RepairAction
18
+ }
19
+
20
+ export type DiagnoseWorktreesOptions = {
21
+ root: string
22
+ git: GitBridgeState
23
+ /** How to name this tool in the repair commands. */
24
+ cli: string
25
+ }
26
+
27
+ /** The trailing slash matters: `git check-ignore` matches a directory rule only against a directory path. */
28
+ export const worktreeDirectory = '.claude/worktrees/'
29
+
30
+ const biomeConfigs = ['biome.json', 'biome.jsonc']
31
+ const claudeSettings = '.claude/settings.json'
32
+
33
+ /** `undefined` when the file is missing or does not parse; either way Claude Code reads no base from it. */
34
+ function readJson(path: string): unknown {
35
+ try {
36
+ return JSON.parse(readFileSync(path, 'utf8'))
37
+ } catch {
38
+ return undefined
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Checked only where `.claude/` exists, the sign the repository is worked in with Claude Code.
44
+ * Biome is the one tool checked: it is the confirmed failure, and the others are offered by the
45
+ * skills without a finding.
46
+ */
47
+ export function diagnoseWorktrees({ root, git, cli }: DiagnoseWorktreesOptions): WorktreeFinding[] {
48
+ if (!existsSync(join(root, '.claude'))) return []
49
+
50
+ const findings: WorktreeFinding[] = []
51
+ const add = (path: string, problem: WorktreeProblem) => {
52
+ const { detail, repair } = repairFor(problem)
53
+ findings.push({ path, problem, detail, repair: repair({ file: path }, cli) })
54
+ }
55
+
56
+ if (git.isRepository() && !git.isIgnored(worktreeDirectory)) add('.gitignore', 'worktrees-untracked')
57
+
58
+ for (const config of biomeConfigs) {
59
+ const path = join(root, config)
60
+ if (existsSync(path) && !readFileSync(path, 'utf8').includes('.claude/worktrees')) add(config, 'worktrees-scanned')
61
+ }
62
+
63
+ const settings = readJson(join(root, claudeSettings))
64
+ const worktree = isRecord(settings) ? settings['worktree'] : undefined
65
+ // An explicit `"fresh"` is the owner's choice; only an unset key is reported.
66
+ if (!isRecord(worktree) || worktree['baseRef'] === undefined) add(claudeSettings, 'worktree-base-unset')
67
+
68
+ return findings
69
+ }
package/src/index.ts CHANGED
@@ -56,6 +56,7 @@ export type {
56
56
  InstructionProblem,
57
57
  McpProblem,
58
58
  Repair,
59
+ WorktreeProblem,
59
60
  } from './diagnose-bridges/doctor-guidance.ts'
60
61
  export {
61
62
  bridgeRepairs,