@kolisachint/hoocode-agent 0.4.21 → 0.4.24

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 (128) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cli/args.d.ts +1 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +5 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/config.d.ts +2 -6
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +5 -9
  9. package/dist/config.js.map +1 -1
  10. package/dist/core/agent-frontmatter.d.ts +3 -0
  11. package/dist/core/agent-frontmatter.d.ts.map +1 -1
  12. package/dist/core/agent-frontmatter.js +41 -1
  13. package/dist/core/agent-frontmatter.js.map +1 -1
  14. package/dist/core/agent-manifest-paths.d.ts +17 -0
  15. package/dist/core/agent-manifest-paths.d.ts.map +1 -0
  16. package/dist/core/agent-manifest-paths.js +27 -0
  17. package/dist/core/agent-manifest-paths.js.map +1 -0
  18. package/dist/core/agent-registry.d.ts +14 -7
  19. package/dist/core/agent-registry.d.ts.map +1 -1
  20. package/dist/core/agent-registry.js +114 -8
  21. package/dist/core/agent-registry.js.map +1 -1
  22. package/dist/core/agent-session.d.ts.map +1 -1
  23. package/dist/core/agent-session.js +23 -0
  24. package/dist/core/agent-session.js.map +1 -1
  25. package/dist/core/extensions/index.d.ts +1 -1
  26. package/dist/core/extensions/index.d.ts.map +1 -1
  27. package/dist/core/extensions/index.js.map +1 -1
  28. package/dist/core/extensions/runner.d.ts.map +1 -1
  29. package/dist/core/extensions/runner.js +1 -0
  30. package/dist/core/extensions/runner.js.map +1 -1
  31. package/dist/core/extensions/types.d.ts +26 -0
  32. package/dist/core/extensions/types.d.ts.map +1 -1
  33. package/dist/core/extensions/types.js.map +1 -1
  34. package/dist/core/keybindings.d.ts +8 -0
  35. package/dist/core/keybindings.d.ts.map +1 -1
  36. package/dist/core/keybindings.js +2 -0
  37. package/dist/core/keybindings.js.map +1 -1
  38. package/dist/core/package-manager.d.ts +2 -1
  39. package/dist/core/package-manager.d.ts.map +1 -1
  40. package/dist/core/package-manager.js +38 -9
  41. package/dist/core/package-manager.js.map +1 -1
  42. package/dist/core/provider-health.d.ts +36 -0
  43. package/dist/core/provider-health.d.ts.map +1 -0
  44. package/dist/core/provider-health.js +54 -0
  45. package/dist/core/provider-health.js.map +1 -0
  46. package/dist/core/resource-loader.d.ts +14 -0
  47. package/dist/core/resource-loader.d.ts.map +1 -1
  48. package/dist/core/resource-loader.js +12 -0
  49. package/dist/core/resource-loader.js.map +1 -1
  50. package/dist/core/sdk.d.ts +2 -0
  51. package/dist/core/sdk.d.ts.map +1 -1
  52. package/dist/core/sdk.js +1 -1
  53. package/dist/core/sdk.js.map +1 -1
  54. package/dist/core/skills.d.ts +9 -0
  55. package/dist/core/skills.d.ts.map +1 -1
  56. package/dist/core/skills.js +32 -1
  57. package/dist/core/skills.js.map +1 -1
  58. package/dist/core/source-info.d.ts +1 -1
  59. package/dist/core/source-info.d.ts.map +1 -1
  60. package/dist/core/source-info.js.map +1 -1
  61. package/dist/core/subagent-pool-instance.d.ts +7 -0
  62. package/dist/core/subagent-pool-instance.d.ts.map +1 -1
  63. package/dist/core/subagent-pool-instance.js +14 -1
  64. package/dist/core/subagent-pool-instance.js.map +1 -1
  65. package/dist/core/subagent-pool.d.ts +16 -0
  66. package/dist/core/subagent-pool.d.ts.map +1 -1
  67. package/dist/core/subagent-pool.js +42 -2
  68. package/dist/core/subagent-pool.js.map +1 -1
  69. package/dist/core/subagent-result.d.ts.map +1 -1
  70. package/dist/core/subagent-result.js +32 -2
  71. package/dist/core/subagent-result.js.map +1 -1
  72. package/dist/core/system-prompt.d.ts +7 -0
  73. package/dist/core/system-prompt.d.ts.map +1 -1
  74. package/dist/core/system-prompt.js +15 -3
  75. package/dist/core/system-prompt.js.map +1 -1
  76. package/dist/core/tools/bash.d.ts +10 -0
  77. package/dist/core/tools/bash.d.ts.map +1 -1
  78. package/dist/core/tools/bash.js +34 -0
  79. package/dist/core/tools/bash.js.map +1 -1
  80. package/dist/core/tools/subagent.d.ts.map +1 -1
  81. package/dist/core/tools/subagent.js +26 -0
  82. package/dist/core/tools/subagent.js.map +1 -1
  83. package/dist/extensions/core/hoo-core.d.ts +10 -3
  84. package/dist/extensions/core/hoo-core.d.ts.map +1 -1
  85. package/dist/extensions/core/hoo-core.js +254 -13
  86. package/dist/extensions/core/hoo-core.js.map +1 -1
  87. package/dist/init-templates.generated.d.ts.map +1 -1
  88. package/dist/init-templates.generated.js +5 -4
  89. package/dist/init-templates.generated.js.map +1 -1
  90. package/dist/init.d.ts.map +1 -1
  91. package/dist/init.js +6 -2
  92. package/dist/init.js.map +1 -1
  93. package/dist/main.d.ts.map +1 -1
  94. package/dist/main.js +4 -0
  95. package/dist/main.js.map +1 -1
  96. package/dist/modes/interactive/components/ask-options.d.ts +44 -0
  97. package/dist/modes/interactive/components/ask-options.d.ts.map +1 -0
  98. package/dist/modes/interactive/components/ask-options.js +202 -0
  99. package/dist/modes/interactive/components/ask-options.js.map +1 -0
  100. package/dist/modes/interactive/components/config-selector.d.ts +1 -1
  101. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  102. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  103. package/dist/modes/interactive/components/index.d.ts +1 -0
  104. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  105. package/dist/modes/interactive/components/index.js +1 -0
  106. package/dist/modes/interactive/components/index.js.map +1 -1
  107. package/dist/modes/interactive/components/task-panel.d.ts +15 -4
  108. package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
  109. package/dist/modes/interactive/components/task-panel.js +178 -63
  110. package/dist/modes/interactive/components/task-panel.js.map +1 -1
  111. package/dist/modes/interactive/interactive-mode.d.ts +10 -0
  112. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  113. package/dist/modes/interactive/interactive-mode.js +50 -1
  114. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  115. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  116. package/dist/modes/rpc/rpc-mode.js +26 -0
  117. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  118. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  119. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  120. package/examples/extensions/sandbox/package.json +1 -1
  121. package/examples/extensions/with-deps/package.json +1 -1
  122. package/examples/sdk/12-full-control.ts +2 -0
  123. package/package.json +4 -4
  124. package/templates/agents/doc.md +1 -1
  125. package/templates/agents/edit.md +1 -0
  126. package/templates/agents/explore.md +3 -3
  127. package/templates/agents/general-purpose.md +37 -0
  128. package/templates/agents/review.md +2 -2
@@ -6,10 +6,13 @@
6
6
  * increasing order of precedence:
7
7
  *
8
8
  * 1. builtin embedded templates (EMBEDDED_AGENT_PROMPTS)
9
- * 2. claude-user ~/.claude/agents/*.md (D7 native import)
10
- * 3. user ~/.hoocode/agent/agents/*.md
11
- * 4. claude-project <cwd>/.claude/agents/*.md (D7 native import)
12
- * 5. project <cwd>/.hoocode/agents/*.md
9
+ * 2. package-manifest paths from hoocode.agents in package.json
10
+ * 3. claude-user ~/.claude/agents/*.md (D7 native import)
11
+ * 4. user ~/.hoocode/agents/*.md
12
+ * 5. ancestor-walk <git-root..cwd>/.agents/agents/*.md
13
+ * 6. claude-project <cwd>/.claude/agents/*.md (D7 native import)
14
+ * 7. project <cwd>/.hoocode/agents/*.md
15
+ * 8. cli paths injected via --agent <path>
13
16
  *
14
17
  * Higher-precedence sources override lower ones by name. Overrides are recorded
15
18
  * as collision diagnostics. Loading never throws; problems surface as
@@ -17,10 +20,11 @@
17
20
  */
18
21
  import { existsSync, readdirSync, readFileSync, statSync } from "fs";
19
22
  import { homedir } from "os";
20
- import { join, resolve } from "path";
23
+ import { dirname, join, resolve } from "path";
21
24
  import { CONFIG_DIR_NAME, getAgentDir } from "../config.js";
22
25
  import { EMBEDDED_AGENT_PROMPTS } from "../init-templates.generated.js";
23
26
  import { parseAgentDefinition } from "./agent-frontmatter.js";
27
+ import { getAgentCliPaths, getAgentManifestPaths } from "./agent-manifest-paths.js";
24
28
  /** Registry of agent definitions keyed by name. */
25
29
  export class AgentRegistry {
26
30
  agents = new Map();
@@ -107,9 +111,53 @@ function registerDir(registry, dir, source) {
107
111
  }
108
112
  }
109
113
  }
110
- /**
111
- * Build an AgentRegistry from all configured locations, applying precedence.
112
- */
114
+ function findGitRepoRoot(startDir) {
115
+ let dir = resolve(startDir);
116
+ while (true) {
117
+ if (existsSync(join(dir, ".git")))
118
+ return dir;
119
+ const parent = dirname(dir);
120
+ if (parent === dir)
121
+ return null;
122
+ dir = parent;
123
+ }
124
+ }
125
+ /** Collect `.agents/agents/` dirs from cwd up to the git root (cwd-first order). */
126
+ function collectAncestorAgentsDirs(startDir) {
127
+ const dirs = [];
128
+ const resolvedStart = resolve(startDir);
129
+ const gitRoot = findGitRepoRoot(resolvedStart);
130
+ let dir = resolvedStart;
131
+ while (true) {
132
+ dirs.push(join(dir, ".agents", "agents"));
133
+ if (gitRoot && dir === gitRoot)
134
+ break;
135
+ const parent = dirname(dir);
136
+ if (parent === dir)
137
+ break;
138
+ dir = parent;
139
+ }
140
+ return dirs;
141
+ }
142
+ /** Register a single file as an agent. */
143
+ function registerFile(registry, filePath, source) {
144
+ if (!existsSync(filePath))
145
+ return;
146
+ try {
147
+ if (!statSync(filePath).isFile())
148
+ return;
149
+ const raw = readFileSync(filePath, "utf-8");
150
+ const { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });
151
+ registry.addDiagnostics(diagnostics);
152
+ if (agent)
153
+ registry.register(agent);
154
+ }
155
+ catch (error) {
156
+ const message = error instanceof Error ? error.message : "failed to read agent file";
157
+ registry.addDiagnostics([{ type: "warning", message, path: filePath }]);
158
+ }
159
+ }
160
+ /** Build an AgentRegistry from all configured locations, applying precedence. */
113
161
  export function loadAgentRegistry(options) {
114
162
  const { cwd, includeBuiltins = true, includeClaude = true } = options;
115
163
  const userAgentDir = options.agentDir ?? getAgentDir();
@@ -118,14 +166,72 @@ export function loadAgentRegistry(options) {
118
166
  if (includeBuiltins) {
119
167
  registerBuiltins(registry);
120
168
  }
169
+ // Package-manifest agents (declared via `hoocode.agents` in package.json).
170
+ for (const filePath of getAgentManifestPaths()) {
171
+ registerFile(registry, filePath, "user");
172
+ }
121
173
  if (includeClaude) {
122
174
  registerDir(registry, join(homedir(), ".claude", "agents"), "claude-user");
123
175
  }
124
176
  registerDir(registry, join(userAgentDir, "agents"), "user");
177
+ // Ancestor-walk .agents/agents/ dirs (git-root first so cwd-level overrides ancestors).
178
+ for (const dir of collectAncestorAgentsDirs(cwd).reverse()) {
179
+ registerDir(registry, dir, "project");
180
+ }
125
181
  if (includeClaude) {
126
182
  registerDir(registry, resolve(cwd, ".claude", "agents"), "claude-project");
127
183
  }
128
184
  registerDir(registry, resolve(cwd, CONFIG_DIR_NAME, "agents"), "project");
185
+ // CLI-injected paths have highest precedence (support both files and dirs).
186
+ for (const p of getAgentCliPaths()) {
187
+ if (!existsSync(p)) {
188
+ registry.addDiagnostics([{ type: "warning", message: `Agent path does not exist: ${p}`, path: p }]);
189
+ continue;
190
+ }
191
+ if (statSync(p).isDirectory()) {
192
+ registerDir(registry, p, "user");
193
+ }
194
+ else {
195
+ registerFile(registry, p, "user");
196
+ }
197
+ }
129
198
  return registry;
130
199
  }
200
+ /**
201
+ * Format a list of agent definitions as an XML block for inclusion in a system
202
+ * prompt, mirroring the `<available_skills>` format used by formatSkillsForPrompt.
203
+ * Only intended for display when the Task tool is active.
204
+ */
205
+ export function formatAgentsForPrompt(agents) {
206
+ if (agents.length === 0)
207
+ return "";
208
+ const lines = [
209
+ "\n\nThe following specialized agents are available for delegation via the Task tool.",
210
+ "Choose the agent whose description best matches the task and pass it as `subagent_type`.",
211
+ "",
212
+ "<available_agents>",
213
+ ];
214
+ for (const agent of agents) {
215
+ lines.push(" <agent>");
216
+ lines.push(` <name>${escapeXml(agent.name)}</name>`);
217
+ lines.push(` <description>${escapeXml(agent.description)}</description>`);
218
+ if (agent.tools && agent.tools.length > 0) {
219
+ lines.push(` <tools>${escapeXml(agent.tools.join(", "))}</tools>`);
220
+ }
221
+ if (agent.model) {
222
+ lines.push(` <model>${escapeXml(agent.model)}</model>`);
223
+ }
224
+ lines.push(" </agent>");
225
+ }
226
+ lines.push("</available_agents>");
227
+ return lines.join("\n");
228
+ }
229
+ function escapeXml(str) {
230
+ return str
231
+ .replace(/&/g, "&amp;")
232
+ .replace(/</g, "&lt;")
233
+ .replace(/>/g, "&gt;")
234
+ .replace(/"/g, "&quot;")
235
+ .replace(/'/g, "&apos;");
236
+ }
131
237
  //# sourceMappingURL=agent-registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-registry.js","sourceRoot":"","sources":["../../src/core/agent-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent registry: data-driven loading of subagent definitions.\n *\n * Replaces the hardcoded `SubagentMode` enum + `MODE_TOOLS` map with frontmatter\n * `.md` files (see agent-frontmatter.ts). Definitions are discovered from, in\n * increasing order of precedence:\n *\n * 1. builtin embedded templates (EMBEDDED_AGENT_PROMPTS)\n * 2. claude-user ~/.claude/agents/*.md (D7 native import)\n * 3. user ~/.hoocode/agent/agents/*.md\n * 4. claude-project <cwd>/.claude/agents/*.md (D7 native import)\n * 5. project <cwd>/.hoocode/agents/*.md\n *\n * Higher-precedence sources override lower ones by name. Overrides are recorded\n * as collision diagnostics. Loading never throws; problems surface as\n * diagnostics, matching skills.ts.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport { EMBEDDED_AGENT_PROMPTS } from \"../init-templates.generated.js\";\nimport { type AgentDefinition, type AgentSource, parseAgentDefinition } from \"./agent-frontmatter.js\";\nimport type { ResourceDiagnostic } from \"./diagnostics.js\";\n\n/** Registry of agent definitions keyed by name. */\nexport class AgentRegistry {\n\tprivate agents = new Map<string, AgentDefinition>();\n\tprivate diagnostics: ResourceDiagnostic[] = [];\n\n\t/**\n\t * Add or override a definition. Later registrations win (used both by the\n\t * loader for precedence and as an escape hatch for programmatic agents).\n\t * Overriding an existing name records a collision diagnostic.\n\t */\n\tregister(def: AgentDefinition): void {\n\t\tconst existing = this.agents.get(def.name);\n\t\tif (existing) {\n\t\t\tthis.diagnostics.push({\n\t\t\t\ttype: \"collision\",\n\t\t\t\tmessage: `agent \"${def.name}\" from ${def.source} overrides ${existing.source}`,\n\t\t\t\tpath: def.filePath,\n\t\t\t\tcollision: {\n\t\t\t\t\tresourceType: \"skill\",\n\t\t\t\t\tname: def.name,\n\t\t\t\t\twinnerPath: def.filePath ?? `<${def.source}>`,\n\t\t\t\t\tloserPath: existing.filePath ?? `<${existing.source}>`,\n\t\t\t\t\twinnerSource: def.source,\n\t\t\t\t\tloserSource: existing.source,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tthis.agents.set(def.name, def);\n\t}\n\n\tget(name: string): AgentDefinition | undefined {\n\t\treturn this.agents.get(name);\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this.agents.has(name);\n\t}\n\n\tlist(): AgentDefinition[] {\n\t\treturn Array.from(this.agents.values());\n\t}\n\n\t/** Diagnostics accumulated during loading/registration. */\n\tgetDiagnostics(): ResourceDiagnostic[] {\n\t\treturn this.diagnostics;\n\t}\n\n\t/** Append externally-produced diagnostics (e.g. from a parse step). */\n\taddDiagnostics(diagnostics: ResourceDiagnostic[]): void {\n\t\tthis.diagnostics.push(...diagnostics);\n\t}\n}\n\n/** Load and register every built-in (embedded) agent definition. */\nfunction registerBuiltins(registry: AgentRegistry): void {\n\tfor (const [key, raw] of Object.entries(EMBEDDED_AGENT_PROMPTS)) {\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source: \"builtin\", fallbackName: key });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t}\n}\n\n/** Load flat `*.md` agent files from a directory. Non-`.md` entries and\n * subdirectories are skipped (so runtime dispatch dirs are ignored). */\nfunction registerDir(registry: AgentRegistry, dir: string, source: AgentSource): void {\n\tif (!existsSync(dir)) return;\n\tlet entries: string[];\n\ttry {\n\t\tentries = readdirSync(dir);\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const entry of entries) {\n\t\tif (entry.startsWith(\".\") || !entry.endsWith(\".md\")) continue;\n\t\tconst filePath = join(dir, entry);\n\t\ttry {\n\t\t\tif (!statSync(filePath).isFile()) continue;\n\t\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\t\tregistry.addDiagnostics(diagnostics);\n\t\t\tif (agent) registry.register(agent);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t\t}\n\t}\n}\n\nexport interface LoadAgentRegistryOptions {\n\t/** Working directory for project-local agents. */\n\tcwd: string;\n\t/** User agent config directory (parent of `agents/`). Defaults to getAgentDir(). */\n\tagentDir?: string;\n\t/** Include embedded built-in agents. Defaults to true. */\n\tincludeBuiltins?: boolean;\n\t/** Discover `.claude/agents/` directories for native Claude Code import (D7). Defaults to true. */\n\tincludeClaude?: boolean;\n}\n\n/**\n * Build an AgentRegistry from all configured locations, applying precedence.\n */\nexport function loadAgentRegistry(options: LoadAgentRegistryOptions): AgentRegistry {\n\tconst { cwd, includeBuiltins = true, includeClaude = true } = options;\n\tconst userAgentDir = options.agentDir ?? getAgentDir();\n\tconst registry = new AgentRegistry();\n\n\t// Lowest precedence first; later sources override earlier ones by name.\n\tif (includeBuiltins) {\n\t\tregisterBuiltins(registry);\n\t}\n\tif (includeClaude) {\n\t\tregisterDir(registry, join(homedir(), \".claude\", \"agents\"), \"claude-user\");\n\t}\n\tregisterDir(registry, join(userAgentDir, \"agents\"), \"user\");\n\tif (includeClaude) {\n\t\tregisterDir(registry, resolve(cwd, \".claude\", \"agents\"), \"claude-project\");\n\t}\n\tregisterDir(registry, resolve(cwd, CONFIG_DIR_NAME, \"agents\"), \"project\");\n\n\treturn registry;\n}\n"]}
1
+ {"version":3,"file":"agent-registry.js","sourceRoot":"","sources":["../../src/core/agent-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent registry: data-driven loading of subagent definitions.\n *\n * Replaces the hardcoded `SubagentMode` enum + `MODE_TOOLS` map with frontmatter\n * `.md` files (see agent-frontmatter.ts). Definitions are discovered from, in\n * increasing order of precedence:\n *\n * 1. builtin embedded templates (EMBEDDED_AGENT_PROMPTS)\n * 2. package-manifest paths from hoocode.agents in package.json\n * 3. claude-user ~/.claude/agents/*.md (D7 native import)\n * 4. user ~/.hoocode/agents/*.md\n * 5. ancestor-walk <git-root..cwd>/.agents/agents/*.md\n * 6. claude-project <cwd>/.claude/agents/*.md (D7 native import)\n * 7. project <cwd>/.hoocode/agents/*.md\n * 8. cli paths injected via --agent <path>\n *\n * Higher-precedence sources override lower ones by name. Overrides are recorded\n * as collision diagnostics. Loading never throws; problems surface as\n * diagnostics, matching skills.ts.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport { EMBEDDED_AGENT_PROMPTS } from \"../init-templates.generated.js\";\nimport { type AgentDefinition, type AgentSource, parseAgentDefinition } from \"./agent-frontmatter.js\";\nimport { getAgentCliPaths, getAgentManifestPaths } from \"./agent-manifest-paths.js\";\nimport type { ResourceDiagnostic } from \"./diagnostics.js\";\n\n/** Registry of agent definitions keyed by name. */\nexport class AgentRegistry {\n\tprivate agents = new Map<string, AgentDefinition>();\n\tprivate diagnostics: ResourceDiagnostic[] = [];\n\n\t/**\n\t * Add or override a definition. Later registrations win (used both by the\n\t * loader for precedence and as an escape hatch for programmatic agents).\n\t * Overriding an existing name records a collision diagnostic.\n\t */\n\tregister(def: AgentDefinition): void {\n\t\tconst existing = this.agents.get(def.name);\n\t\tif (existing) {\n\t\t\tthis.diagnostics.push({\n\t\t\t\ttype: \"collision\",\n\t\t\t\tmessage: `agent \"${def.name}\" from ${def.source} overrides ${existing.source}`,\n\t\t\t\tpath: def.filePath,\n\t\t\t\tcollision: {\n\t\t\t\t\tresourceType: \"skill\",\n\t\t\t\t\tname: def.name,\n\t\t\t\t\twinnerPath: def.filePath ?? `<${def.source}>`,\n\t\t\t\t\tloserPath: existing.filePath ?? `<${existing.source}>`,\n\t\t\t\t\twinnerSource: def.source,\n\t\t\t\t\tloserSource: existing.source,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tthis.agents.set(def.name, def);\n\t}\n\n\tget(name: string): AgentDefinition | undefined {\n\t\treturn this.agents.get(name);\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this.agents.has(name);\n\t}\n\n\tlist(): AgentDefinition[] {\n\t\treturn Array.from(this.agents.values());\n\t}\n\n\t/** Diagnostics accumulated during loading/registration. */\n\tgetDiagnostics(): ResourceDiagnostic[] {\n\t\treturn this.diagnostics;\n\t}\n\n\t/** Append externally-produced diagnostics (e.g. from a parse step). */\n\taddDiagnostics(diagnostics: ResourceDiagnostic[]): void {\n\t\tthis.diagnostics.push(...diagnostics);\n\t}\n}\n\n/** Load and register every built-in (embedded) agent definition. */\nfunction registerBuiltins(registry: AgentRegistry): void {\n\tfor (const [key, raw] of Object.entries(EMBEDDED_AGENT_PROMPTS)) {\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source: \"builtin\", fallbackName: key });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t}\n}\n\n/** Load flat `*.md` agent files from a directory. Non-`.md` entries and\n * subdirectories are skipped (so runtime dispatch dirs are ignored). */\nfunction registerDir(registry: AgentRegistry, dir: string, source: AgentSource): void {\n\tif (!existsSync(dir)) return;\n\tlet entries: string[];\n\ttry {\n\t\tentries = readdirSync(dir);\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const entry of entries) {\n\t\tif (entry.startsWith(\".\") || !entry.endsWith(\".md\")) continue;\n\t\tconst filePath = join(dir, entry);\n\t\ttry {\n\t\t\tif (!statSync(filePath).isFile()) continue;\n\t\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\t\tregistry.addDiagnostics(diagnostics);\n\t\t\tif (agent) registry.register(agent);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t\t}\n\t}\n}\n\nfunction findGitRepoRoot(startDir: string): string | null {\n\tlet dir = resolve(startDir);\n\twhile (true) {\n\t\tif (existsSync(join(dir, \".git\"))) return dir;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Collect `.agents/agents/` dirs from cwd up to the git root (cwd-first order). */\nfunction collectAncestorAgentsDirs(startDir: string): string[] {\n\tconst dirs: string[] = [];\n\tconst resolvedStart = resolve(startDir);\n\tconst gitRoot = findGitRepoRoot(resolvedStart);\n\tlet dir = resolvedStart;\n\twhile (true) {\n\t\tdirs.push(join(dir, \".agents\", \"agents\"));\n\t\tif (gitRoot && dir === gitRoot) break;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn dirs;\n}\n\n/** Register a single file as an agent. */\nfunction registerFile(registry: AgentRegistry, filePath: string, source: AgentSource): void {\n\tif (!existsSync(filePath)) return;\n\ttry {\n\t\tif (!statSync(filePath).isFile()) return;\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t}\n}\n\nexport interface LoadAgentRegistryOptions {\n\t/** Working directory for project-local agents. */\n\tcwd: string;\n\t/** User agent config directory (contains `agents/` subdir). Defaults to getAgentDir(). */\n\tagentDir?: string;\n\t/** Include embedded built-in agents. Defaults to true. */\n\tincludeBuiltins?: boolean;\n\t/** Discover `.claude/agents/` directories for native Claude Code import (D7). Defaults to true. */\n\tincludeClaude?: boolean;\n}\n\n/** Build an AgentRegistry from all configured locations, applying precedence. */\nexport function loadAgentRegistry(options: LoadAgentRegistryOptions): AgentRegistry {\n\tconst { cwd, includeBuiltins = true, includeClaude = true } = options;\n\tconst userAgentDir = options.agentDir ?? getAgentDir();\n\tconst registry = new AgentRegistry();\n\n\t// Lowest precedence first; later sources override earlier ones by name.\n\tif (includeBuiltins) {\n\t\tregisterBuiltins(registry);\n\t}\n\n\t// Package-manifest agents (declared via `hoocode.agents` in package.json).\n\tfor (const filePath of getAgentManifestPaths()) {\n\t\tregisterFile(registry, filePath, \"user\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, join(homedir(), \".claude\", \"agents\"), \"claude-user\");\n\t}\n\tregisterDir(registry, join(userAgentDir, \"agents\"), \"user\");\n\n\t// Ancestor-walk .agents/agents/ dirs (git-root first so cwd-level overrides ancestors).\n\tfor (const dir of collectAncestorAgentsDirs(cwd).reverse()) {\n\t\tregisterDir(registry, dir, \"project\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, resolve(cwd, \".claude\", \"agents\"), \"claude-project\");\n\t}\n\tregisterDir(registry, resolve(cwd, CONFIG_DIR_NAME, \"agents\"), \"project\");\n\n\t// CLI-injected paths have highest precedence (support both files and dirs).\n\tfor (const p of getAgentCliPaths()) {\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"user\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"user\");\n\t\t}\n\t}\n\n\treturn registry;\n}\n\n/**\n * Format a list of agent definitions as an XML block for inclusion in a system\n * prompt, mirroring the `<available_skills>` format used by formatSkillsForPrompt.\n * Only intended for display when the Task tool is active.\n */\nexport function formatAgentsForPrompt(agents: AgentDefinition[]): string {\n\tif (agents.length === 0) return \"\";\n\n\tconst lines = [\n\t\t\"\\n\\nThe following specialized agents are available for delegation via the Task tool.\",\n\t\t\"Choose the agent whose description best matches the task and pass it as `subagent_type`.\",\n\t\t\"\",\n\t\t\"<available_agents>\",\n\t];\n\n\tfor (const agent of agents) {\n\t\tlines.push(\" <agent>\");\n\t\tlines.push(` <name>${escapeXml(agent.name)}</name>`);\n\t\tlines.push(` <description>${escapeXml(agent.description)}</description>`);\n\t\tif (agent.tools && agent.tools.length > 0) {\n\t\t\tlines.push(` <tools>${escapeXml(agent.tools.join(\", \"))}</tools>`);\n\t\t}\n\t\tif (agent.model) {\n\t\t\tlines.push(` <model>${escapeXml(agent.model)}</model>`);\n\t\t}\n\t\tlines.push(\" </agent>\");\n\t}\n\n\tlines.push(\"</available_agents>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n"]}