@kolisachint/hoocode-agent 0.4.43 → 0.4.45
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/CHANGELOG.md +17 -0
- package/README.md +3 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/keybindings.d.ts +5 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +4 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +10 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +4 -2
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/task-store.d.ts +53 -1
- package/dist/core/task-store.d.ts.map +1 -1
- package/dist/core/task-store.js +77 -1
- package/dist/core/task-store.js.map +1 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +34 -3
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/command-executor.d.ts.map +1 -1
- package/dist/modes/interactive/command-executor.js +2 -0
- package/dist/modes/interactive/command-executor.js.map +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts +19 -2
- package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-panel.js +184 -18
- package/dist/modes/interactive/components/task-panel.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +4 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/resources-cli.d.ts +2 -0
- package/dist/resources-cli.d.ts.map +1 -0
- package/dist/resources-cli.js +184 -0
- package/dist/resources-cli.js.map +1 -0
- package/docs/docs.json +4 -0
- package/docs/project-local-resources.md +124 -0
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources-cli.d.ts","sourceRoot":"","sources":["../src/resources-cli.ts"],"names":[],"mappings":"AAuJA,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAoD7E","sourcesContent":["import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { APP_NAME, CONFIG_DIR_NAME, getAgentDir, getHooCodeDir } from \"./config.js\";\nimport { loadAgentRegistry } from \"./core/agent-registry.js\";\nimport { DefaultResourceLoader } from \"./core/resource-loader.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport type { SourceInfo } from \"./core/source-info.js\";\n\ninterface ResourceRow {\n\tname: string;\n\tdetail?: string;\n\torigin: string;\n\tpath: string;\n}\n\n/**\n * Derive a short origin label from a resource path and scope. Distinguishes the\n * four discovery surfaces a user cares about when debugging: project vs user, and\n * the cross-vendor `.agents/` vs Claude Code `.claude/` directories.\n */\nfunction classifyOrigin(path: string, scope: string): string {\n\tif (path.includes(`${join(\"\", \".agents\", \"\")}`) || path.includes(\"/.agents/\")) return `${scope} (.agents)`;\n\tif (path.includes(\"/.claude/\")) return `${scope} (.claude)`;\n\tif (path.includes(`${CONFIG_DIR_NAME}/`)) return `${scope} (${CONFIG_DIR_NAME})`;\n\treturn scope;\n}\n\nfunction originFromSourceInfo(sourceInfo: SourceInfo): string {\n\treturn classifyOrigin(sourceInfo.path, sourceInfo.scope);\n}\n\n/** Map an AgentSource value to the same origin vocabulary used elsewhere. */\nfunction originFromAgentSource(source: string, path: string | undefined): string {\n\tswitch (source) {\n\t\tcase \"builtin\":\n\t\t\treturn \"builtin\";\n\t\tcase \"user\":\n\t\t\treturn path ? classifyOrigin(path, \"user\") : \"user\";\n\t\tcase \"project\":\n\t\t\treturn path ? classifyOrigin(path, \"project\") : \"project\";\n\t\tcase \"claude-user\":\n\t\t\treturn \"user (.claude)\";\n\t\tcase \"claude-project\":\n\t\t\treturn \"project (.claude)\";\n\t\tdefault:\n\t\t\treturn source;\n\t}\n}\n\ninterface DiscoveredMcpServer {\n\tname: string;\n\tpath: string;\n\tscope: \"user\" | \"project\";\n}\n\n/**\n * Read-only replica of the MCP discovery surfaces in\n * `extensions/core/hoo-core.ts` (setupMcpLoader). First-wins by name across all\n * sources. Kept in sync with that loader; it is the source of truth.\n */\nfunction discoverMcpServers(cwd: string): DiscoveredMcpServer[] {\n\tconst servers: DiscoveredMcpServer[] = [];\n\tconst seen = new Set<string>();\n\tconst add = (name: string, path: string, scope: \"user\" | \"project\"): void => {\n\t\tif (!name || seen.has(name)) return;\n\t\tseen.add(name);\n\t\tservers.push({ name, path, scope });\n\t};\n\n\tconst standardFiles: Array<{ path: string; scope: \"user\" | \"project\" }> = [\n\t\t{ path: join(homedir(), \".agents\", \"mcp.json\"), scope: \"user\" },\n\t\t{ path: join(cwd, \".agents\", \"mcp.json\"), scope: \"project\" },\n\t\t{ path: join(homedir(), \".config\", \"claude\", \"mcp.json\"), scope: \"user\" },\n\t];\n\tfor (const { path, scope } of standardFiles) {\n\t\tif (!existsSync(path)) continue;\n\t\ttry {\n\t\t\tconst config = JSON.parse(readFileSync(path, \"utf8\")) as { mcpServers?: Record<string, unknown> };\n\t\t\tfor (const name of Object.keys(config.mcpServers ?? {})) {\n\t\t\t\tadd(name, path, scope);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore malformed config; the loader does the same.\n\t\t}\n\t}\n\n\tconst perServerDirs: Array<{ dir: string; scope: \"user\" | \"project\" }> = [\n\t\t{ dir: join(getHooCodeDir(), \"mcp-servers\"), scope: \"user\" },\n\t\t{ dir: join(cwd, CONFIG_DIR_NAME, \"mcp-servers\"), scope: \"project\" },\n\t];\n\tfor (const { dir, scope } of perServerDirs) {\n\t\tif (!existsSync(dir)) continue;\n\t\tlet files: string[];\n\t\ttry {\n\t\t\tfiles = readdirSync(dir).filter((f) => f.endsWith(\".json\"));\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const file of files) {\n\t\t\tconst path = join(dir, file);\n\t\t\ttry {\n\t\t\t\tconst config = JSON.parse(readFileSync(path, \"utf8\")) as { name?: string };\n\t\t\t\tif (config.name) add(config.name, path, scope);\n\t\t\t} catch {\n\t\t\t\t// Ignore malformed config.\n\t\t\t}\n\t\t}\n\t}\n\n\treturn servers;\n}\n\n/** Collapse a description to a single short line for the table. */\nfunction oneLine(text: string | undefined): string | undefined {\n\tif (!text) return undefined;\n\tconst firstLine = text\n\t\t.split(\"\\n\")\n\t\t.map((l) => l.trim())\n\t\t.find((l) => l.length > 0);\n\tif (!firstLine) return undefined;\n\treturn firstLine.length > 80 ? `${firstLine.slice(0, 77)}...` : firstLine;\n}\n\nfunction printSection(title: string, rows: ResourceRow[]): void {\n\tconsole.log(chalk.bold(`${title} (${rows.length})`));\n\tif (rows.length === 0) {\n\t\tconsole.log(chalk.dim(\" (none)\"));\n\t\tconsole.log();\n\t\treturn;\n\t}\n\tfor (const row of rows) {\n\t\tconst summary = oneLine(row.detail);\n\t\tconst detail = summary ? chalk.dim(` - ${summary}`) : \"\";\n\t\tconsole.log(` ${row.name}${detail} ${chalk.dim(`[${row.origin}]`)}`);\n\t\tconsole.log(chalk.dim(` ${row.path}`));\n\t}\n\tconsole.log();\n}\n\nfunction printHelp(): void {\n\tconsole.log(`${chalk.bold(\"Usage:\")}\n ${APP_NAME} resources\n\nPrint the skills, subagents, slash commands, and MCP servers discovered for the\ncurrent working directory, with their source path and origin. Read-only; makes\nno LLM call.\n`);\n}\n\nexport async function handleResourcesCommand(args: string[]): Promise<boolean> {\n\tif (args[0] !== \"resources\") {\n\t\treturn false;\n\t}\n\n\tif (args.includes(\"-h\") || args.includes(\"--help\")) {\n\t\tprintHelp();\n\t\treturn true;\n\t}\n\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir);\n\tconst resourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });\n\tawait resourceLoader.reload();\n\n\tconst skills = resourceLoader.getSkills().skills.map<ResourceRow>((skill) => ({\n\t\tname: skill.name,\n\t\tdetail: skill.description,\n\t\torigin: originFromSourceInfo(skill.sourceInfo),\n\t\tpath: skill.sourceInfo.path,\n\t}));\n\n\tconst agents = loadAgentRegistry({ cwd, agentDir })\n\t\t.list()\n\t\t.map<ResourceRow>((agent) => ({\n\t\t\tname: agent.name,\n\t\t\tdetail: agent.description,\n\t\t\torigin: originFromAgentSource(agent.source, agent.filePath),\n\t\t\tpath: agent.filePath ?? \"<builtin>\",\n\t\t}));\n\n\tconst slashCommands = resourceLoader.getPrompts().prompts.map<ResourceRow>((prompt) => ({\n\t\tname: `/${prompt.name}`,\n\t\tdetail: prompt.description,\n\t\torigin: originFromSourceInfo(prompt.sourceInfo),\n\t\tpath: prompt.sourceInfo.path,\n\t}));\n\n\tconst mcpServers = discoverMcpServers(cwd).map<ResourceRow>((server) => ({\n\t\tname: server.name,\n\t\torigin: classifyOrigin(server.path, server.scope),\n\t\tpath: server.path,\n\t}));\n\n\tconsole.log(chalk.dim(`Resources discovered for ${cwd}\\n`));\n\tprintSection(\"Skills\", skills);\n\tprintSection(\"Subagents\", agents);\n\tprintSection(\"Slash commands\", slashCommands);\n\tprintSection(\"MCP servers\", mcpServers);\n\n\treturn true;\n}\n"]}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { APP_NAME, CONFIG_DIR_NAME, getAgentDir, getHooCodeDir } from "./config.js";
|
|
6
|
+
import { loadAgentRegistry } from "./core/agent-registry.js";
|
|
7
|
+
import { DefaultResourceLoader } from "./core/resource-loader.js";
|
|
8
|
+
import { SettingsManager } from "./core/settings-manager.js";
|
|
9
|
+
/**
|
|
10
|
+
* Derive a short origin label from a resource path and scope. Distinguishes the
|
|
11
|
+
* four discovery surfaces a user cares about when debugging: project vs user, and
|
|
12
|
+
* the cross-vendor `.agents/` vs Claude Code `.claude/` directories.
|
|
13
|
+
*/
|
|
14
|
+
function classifyOrigin(path, scope) {
|
|
15
|
+
if (path.includes(`${join("", ".agents", "")}`) || path.includes("/.agents/"))
|
|
16
|
+
return `${scope} (.agents)`;
|
|
17
|
+
if (path.includes("/.claude/"))
|
|
18
|
+
return `${scope} (.claude)`;
|
|
19
|
+
if (path.includes(`${CONFIG_DIR_NAME}/`))
|
|
20
|
+
return `${scope} (${CONFIG_DIR_NAME})`;
|
|
21
|
+
return scope;
|
|
22
|
+
}
|
|
23
|
+
function originFromSourceInfo(sourceInfo) {
|
|
24
|
+
return classifyOrigin(sourceInfo.path, sourceInfo.scope);
|
|
25
|
+
}
|
|
26
|
+
/** Map an AgentSource value to the same origin vocabulary used elsewhere. */
|
|
27
|
+
function originFromAgentSource(source, path) {
|
|
28
|
+
switch (source) {
|
|
29
|
+
case "builtin":
|
|
30
|
+
return "builtin";
|
|
31
|
+
case "user":
|
|
32
|
+
return path ? classifyOrigin(path, "user") : "user";
|
|
33
|
+
case "project":
|
|
34
|
+
return path ? classifyOrigin(path, "project") : "project";
|
|
35
|
+
case "claude-user":
|
|
36
|
+
return "user (.claude)";
|
|
37
|
+
case "claude-project":
|
|
38
|
+
return "project (.claude)";
|
|
39
|
+
default:
|
|
40
|
+
return source;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read-only replica of the MCP discovery surfaces in
|
|
45
|
+
* `extensions/core/hoo-core.ts` (setupMcpLoader). First-wins by name across all
|
|
46
|
+
* sources. Kept in sync with that loader; it is the source of truth.
|
|
47
|
+
*/
|
|
48
|
+
function discoverMcpServers(cwd) {
|
|
49
|
+
const servers = [];
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
const add = (name, path, scope) => {
|
|
52
|
+
if (!name || seen.has(name))
|
|
53
|
+
return;
|
|
54
|
+
seen.add(name);
|
|
55
|
+
servers.push({ name, path, scope });
|
|
56
|
+
};
|
|
57
|
+
const standardFiles = [
|
|
58
|
+
{ path: join(homedir(), ".agents", "mcp.json"), scope: "user" },
|
|
59
|
+
{ path: join(cwd, ".agents", "mcp.json"), scope: "project" },
|
|
60
|
+
{ path: join(homedir(), ".config", "claude", "mcp.json"), scope: "user" },
|
|
61
|
+
];
|
|
62
|
+
for (const { path, scope } of standardFiles) {
|
|
63
|
+
if (!existsSync(path))
|
|
64
|
+
continue;
|
|
65
|
+
try {
|
|
66
|
+
const config = JSON.parse(readFileSync(path, "utf8"));
|
|
67
|
+
for (const name of Object.keys(config.mcpServers ?? {})) {
|
|
68
|
+
add(name, path, scope);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Ignore malformed config; the loader does the same.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const perServerDirs = [
|
|
76
|
+
{ dir: join(getHooCodeDir(), "mcp-servers"), scope: "user" },
|
|
77
|
+
{ dir: join(cwd, CONFIG_DIR_NAME, "mcp-servers"), scope: "project" },
|
|
78
|
+
];
|
|
79
|
+
for (const { dir, scope } of perServerDirs) {
|
|
80
|
+
if (!existsSync(dir))
|
|
81
|
+
continue;
|
|
82
|
+
let files;
|
|
83
|
+
try {
|
|
84
|
+
files = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const path = join(dir, file);
|
|
91
|
+
try {
|
|
92
|
+
const config = JSON.parse(readFileSync(path, "utf8"));
|
|
93
|
+
if (config.name)
|
|
94
|
+
add(config.name, path, scope);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Ignore malformed config.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return servers;
|
|
102
|
+
}
|
|
103
|
+
/** Collapse a description to a single short line for the table. */
|
|
104
|
+
function oneLine(text) {
|
|
105
|
+
if (!text)
|
|
106
|
+
return undefined;
|
|
107
|
+
const firstLine = text
|
|
108
|
+
.split("\n")
|
|
109
|
+
.map((l) => l.trim())
|
|
110
|
+
.find((l) => l.length > 0);
|
|
111
|
+
if (!firstLine)
|
|
112
|
+
return undefined;
|
|
113
|
+
return firstLine.length > 80 ? `${firstLine.slice(0, 77)}...` : firstLine;
|
|
114
|
+
}
|
|
115
|
+
function printSection(title, rows) {
|
|
116
|
+
console.log(chalk.bold(`${title} (${rows.length})`));
|
|
117
|
+
if (rows.length === 0) {
|
|
118
|
+
console.log(chalk.dim(" (none)"));
|
|
119
|
+
console.log();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
for (const row of rows) {
|
|
123
|
+
const summary = oneLine(row.detail);
|
|
124
|
+
const detail = summary ? chalk.dim(` - ${summary}`) : "";
|
|
125
|
+
console.log(` ${row.name}${detail} ${chalk.dim(`[${row.origin}]`)}`);
|
|
126
|
+
console.log(chalk.dim(` ${row.path}`));
|
|
127
|
+
}
|
|
128
|
+
console.log();
|
|
129
|
+
}
|
|
130
|
+
function printHelp() {
|
|
131
|
+
console.log(`${chalk.bold("Usage:")}
|
|
132
|
+
${APP_NAME} resources
|
|
133
|
+
|
|
134
|
+
Print the skills, subagents, slash commands, and MCP servers discovered for the
|
|
135
|
+
current working directory, with their source path and origin. Read-only; makes
|
|
136
|
+
no LLM call.
|
|
137
|
+
`);
|
|
138
|
+
}
|
|
139
|
+
export async function handleResourcesCommand(args) {
|
|
140
|
+
if (args[0] !== "resources") {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
144
|
+
printHelp();
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
const cwd = process.cwd();
|
|
148
|
+
const agentDir = getAgentDir();
|
|
149
|
+
const settingsManager = SettingsManager.create(cwd, agentDir);
|
|
150
|
+
const resourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });
|
|
151
|
+
await resourceLoader.reload();
|
|
152
|
+
const skills = resourceLoader.getSkills().skills.map((skill) => ({
|
|
153
|
+
name: skill.name,
|
|
154
|
+
detail: skill.description,
|
|
155
|
+
origin: originFromSourceInfo(skill.sourceInfo),
|
|
156
|
+
path: skill.sourceInfo.path,
|
|
157
|
+
}));
|
|
158
|
+
const agents = loadAgentRegistry({ cwd, agentDir })
|
|
159
|
+
.list()
|
|
160
|
+
.map((agent) => ({
|
|
161
|
+
name: agent.name,
|
|
162
|
+
detail: agent.description,
|
|
163
|
+
origin: originFromAgentSource(agent.source, agent.filePath),
|
|
164
|
+
path: agent.filePath ?? "<builtin>",
|
|
165
|
+
}));
|
|
166
|
+
const slashCommands = resourceLoader.getPrompts().prompts.map((prompt) => ({
|
|
167
|
+
name: `/${prompt.name}`,
|
|
168
|
+
detail: prompt.description,
|
|
169
|
+
origin: originFromSourceInfo(prompt.sourceInfo),
|
|
170
|
+
path: prompt.sourceInfo.path,
|
|
171
|
+
}));
|
|
172
|
+
const mcpServers = discoverMcpServers(cwd).map((server) => ({
|
|
173
|
+
name: server.name,
|
|
174
|
+
origin: classifyOrigin(server.path, server.scope),
|
|
175
|
+
path: server.path,
|
|
176
|
+
}));
|
|
177
|
+
console.log(chalk.dim(`Resources discovered for ${cwd}\n`));
|
|
178
|
+
printSection("Skills", skills);
|
|
179
|
+
printSection("Subagents", agents);
|
|
180
|
+
printSection("Slash commands", slashCommands);
|
|
181
|
+
printSection("MCP servers", mcpServers);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=resources-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources-cli.js","sourceRoot":"","sources":["../src/resources-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { APP_NAME, CONFIG_DIR_NAME, getAgentDir, getHooCodeDir } from \"./config.js\";\nimport { loadAgentRegistry } from \"./core/agent-registry.js\";\nimport { DefaultResourceLoader } from \"./core/resource-loader.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport type { SourceInfo } from \"./core/source-info.js\";\n\ninterface ResourceRow {\n\tname: string;\n\tdetail?: string;\n\torigin: string;\n\tpath: string;\n}\n\n/**\n * Derive a short origin label from a resource path and scope. Distinguishes the\n * four discovery surfaces a user cares about when debugging: project vs user, and\n * the cross-vendor `.agents/` vs Claude Code `.claude/` directories.\n */\nfunction classifyOrigin(path: string, scope: string): string {\n\tif (path.includes(`${join(\"\", \".agents\", \"\")}`) || path.includes(\"/.agents/\")) return `${scope} (.agents)`;\n\tif (path.includes(\"/.claude/\")) return `${scope} (.claude)`;\n\tif (path.includes(`${CONFIG_DIR_NAME}/`)) return `${scope} (${CONFIG_DIR_NAME})`;\n\treturn scope;\n}\n\nfunction originFromSourceInfo(sourceInfo: SourceInfo): string {\n\treturn classifyOrigin(sourceInfo.path, sourceInfo.scope);\n}\n\n/** Map an AgentSource value to the same origin vocabulary used elsewhere. */\nfunction originFromAgentSource(source: string, path: string | undefined): string {\n\tswitch (source) {\n\t\tcase \"builtin\":\n\t\t\treturn \"builtin\";\n\t\tcase \"user\":\n\t\t\treturn path ? classifyOrigin(path, \"user\") : \"user\";\n\t\tcase \"project\":\n\t\t\treturn path ? classifyOrigin(path, \"project\") : \"project\";\n\t\tcase \"claude-user\":\n\t\t\treturn \"user (.claude)\";\n\t\tcase \"claude-project\":\n\t\t\treturn \"project (.claude)\";\n\t\tdefault:\n\t\t\treturn source;\n\t}\n}\n\ninterface DiscoveredMcpServer {\n\tname: string;\n\tpath: string;\n\tscope: \"user\" | \"project\";\n}\n\n/**\n * Read-only replica of the MCP discovery surfaces in\n * `extensions/core/hoo-core.ts` (setupMcpLoader). First-wins by name across all\n * sources. Kept in sync with that loader; it is the source of truth.\n */\nfunction discoverMcpServers(cwd: string): DiscoveredMcpServer[] {\n\tconst servers: DiscoveredMcpServer[] = [];\n\tconst seen = new Set<string>();\n\tconst add = (name: string, path: string, scope: \"user\" | \"project\"): void => {\n\t\tif (!name || seen.has(name)) return;\n\t\tseen.add(name);\n\t\tservers.push({ name, path, scope });\n\t};\n\n\tconst standardFiles: Array<{ path: string; scope: \"user\" | \"project\" }> = [\n\t\t{ path: join(homedir(), \".agents\", \"mcp.json\"), scope: \"user\" },\n\t\t{ path: join(cwd, \".agents\", \"mcp.json\"), scope: \"project\" },\n\t\t{ path: join(homedir(), \".config\", \"claude\", \"mcp.json\"), scope: \"user\" },\n\t];\n\tfor (const { path, scope } of standardFiles) {\n\t\tif (!existsSync(path)) continue;\n\t\ttry {\n\t\t\tconst config = JSON.parse(readFileSync(path, \"utf8\")) as { mcpServers?: Record<string, unknown> };\n\t\t\tfor (const name of Object.keys(config.mcpServers ?? {})) {\n\t\t\t\tadd(name, path, scope);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore malformed config; the loader does the same.\n\t\t}\n\t}\n\n\tconst perServerDirs: Array<{ dir: string; scope: \"user\" | \"project\" }> = [\n\t\t{ dir: join(getHooCodeDir(), \"mcp-servers\"), scope: \"user\" },\n\t\t{ dir: join(cwd, CONFIG_DIR_NAME, \"mcp-servers\"), scope: \"project\" },\n\t];\n\tfor (const { dir, scope } of perServerDirs) {\n\t\tif (!existsSync(dir)) continue;\n\t\tlet files: string[];\n\t\ttry {\n\t\t\tfiles = readdirSync(dir).filter((f) => f.endsWith(\".json\"));\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const file of files) {\n\t\t\tconst path = join(dir, file);\n\t\t\ttry {\n\t\t\t\tconst config = JSON.parse(readFileSync(path, \"utf8\")) as { name?: string };\n\t\t\t\tif (config.name) add(config.name, path, scope);\n\t\t\t} catch {\n\t\t\t\t// Ignore malformed config.\n\t\t\t}\n\t\t}\n\t}\n\n\treturn servers;\n}\n\n/** Collapse a description to a single short line for the table. */\nfunction oneLine(text: string | undefined): string | undefined {\n\tif (!text) return undefined;\n\tconst firstLine = text\n\t\t.split(\"\\n\")\n\t\t.map((l) => l.trim())\n\t\t.find((l) => l.length > 0);\n\tif (!firstLine) return undefined;\n\treturn firstLine.length > 80 ? `${firstLine.slice(0, 77)}...` : firstLine;\n}\n\nfunction printSection(title: string, rows: ResourceRow[]): void {\n\tconsole.log(chalk.bold(`${title} (${rows.length})`));\n\tif (rows.length === 0) {\n\t\tconsole.log(chalk.dim(\" (none)\"));\n\t\tconsole.log();\n\t\treturn;\n\t}\n\tfor (const row of rows) {\n\t\tconst summary = oneLine(row.detail);\n\t\tconst detail = summary ? chalk.dim(` - ${summary}`) : \"\";\n\t\tconsole.log(` ${row.name}${detail} ${chalk.dim(`[${row.origin}]`)}`);\n\t\tconsole.log(chalk.dim(` ${row.path}`));\n\t}\n\tconsole.log();\n}\n\nfunction printHelp(): void {\n\tconsole.log(`${chalk.bold(\"Usage:\")}\n ${APP_NAME} resources\n\nPrint the skills, subagents, slash commands, and MCP servers discovered for the\ncurrent working directory, with their source path and origin. Read-only; makes\nno LLM call.\n`);\n}\n\nexport async function handleResourcesCommand(args: string[]): Promise<boolean> {\n\tif (args[0] !== \"resources\") {\n\t\treturn false;\n\t}\n\n\tif (args.includes(\"-h\") || args.includes(\"--help\")) {\n\t\tprintHelp();\n\t\treturn true;\n\t}\n\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir);\n\tconst resourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });\n\tawait resourceLoader.reload();\n\n\tconst skills = resourceLoader.getSkills().skills.map<ResourceRow>((skill) => ({\n\t\tname: skill.name,\n\t\tdetail: skill.description,\n\t\torigin: originFromSourceInfo(skill.sourceInfo),\n\t\tpath: skill.sourceInfo.path,\n\t}));\n\n\tconst agents = loadAgentRegistry({ cwd, agentDir })\n\t\t.list()\n\t\t.map<ResourceRow>((agent) => ({\n\t\t\tname: agent.name,\n\t\t\tdetail: agent.description,\n\t\t\torigin: originFromAgentSource(agent.source, agent.filePath),\n\t\t\tpath: agent.filePath ?? \"<builtin>\",\n\t\t}));\n\n\tconst slashCommands = resourceLoader.getPrompts().prompts.map<ResourceRow>((prompt) => ({\n\t\tname: `/${prompt.name}`,\n\t\tdetail: prompt.description,\n\t\torigin: originFromSourceInfo(prompt.sourceInfo),\n\t\tpath: prompt.sourceInfo.path,\n\t}));\n\n\tconst mcpServers = discoverMcpServers(cwd).map<ResourceRow>((server) => ({\n\t\tname: server.name,\n\t\torigin: classifyOrigin(server.path, server.scope),\n\t\tpath: server.path,\n\t}));\n\n\tconsole.log(chalk.dim(`Resources discovered for ${cwd}\\n`));\n\tprintSection(\"Skills\", skills);\n\tprintSection(\"Subagents\", agents);\n\tprintSection(\"Slash commands\", slashCommands);\n\tprintSection(\"MCP servers\", mcpServers);\n\n\treturn true;\n}\n"]}
|
package/docs/docs.json
CHANGED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Project-local resources via `.agents/`
|
|
2
|
+
|
|
3
|
+
hoocode discovers project-local resources from four directory conventions. Each resource type (slash commands, subagents, MCP servers, skills) has its own precedence order across these roots.
|
|
4
|
+
|
|
5
|
+
## Discovery roots
|
|
6
|
+
|
|
7
|
+
| Root | Scope | Notes |
|
|
8
|
+
|------|-------|-------|
|
|
9
|
+
| `.hoocode/` | project + user (`~/.hoocode/`) | hoocode-native convention |
|
|
10
|
+
| `.agents/` | project (ancestor-walked) + user (`~/.agents/`) | cross-vendor convention |
|
|
11
|
+
| `.claude/` | project + user (`~/.claude/`) | Claude Code native import |
|
|
12
|
+
| `hoocode.*` manifest fields | installed packages | declared in `package.json` |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Ancestor-walk-to-git-root rule
|
|
17
|
+
|
|
18
|
+
Source: `packages/coding-agent/src/utils/paths.ts` (`collectAgentsAncestorDirs`, `findGitRepoRoot`).
|
|
19
|
+
|
|
20
|
+
For `.agents/<subdir>` only, hoocode walks upward from the current working directory, collecting `<dir>/.agents/<subdir>` at each level, stopping at (and including) the nearest ancestor that contains a `.git` entry (the git repo root). If the cwd is not inside a git repo, the walk continues to the filesystem root.
|
|
21
|
+
|
|
22
|
+
Directories are collected **cwd-first**, so a closer `.agents/` directory beats an ancestor under first-match-wins semantics.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Slash commands / prompt templates
|
|
27
|
+
|
|
28
|
+
Source: `packages/coding-agent/src/core/resource-loader.ts` (`defaultSlashCommandDirs` in `reload()`).
|
|
29
|
+
|
|
30
|
+
**First-match-wins**, highest precedence first:
|
|
31
|
+
|
|
32
|
+
1. `{cwd}/.hoocode/commands`
|
|
33
|
+
2. `{cwd}/.claude/commands`
|
|
34
|
+
3. `.agents/commands` — ancestor-walk from cwd up to git root, cwd-first
|
|
35
|
+
4. `{agentDir}/commands` (i.e. `~/.hoocode/commands`)
|
|
36
|
+
5. `~/.agents/commands`
|
|
37
|
+
6. `~/.claude/commands`
|
|
38
|
+
|
|
39
|
+
Pass `--no-slash-commands` or `--no-prompt-templates` to disable discovery. Explicit `--slash-command`/`--prompt-template` paths still load when discovery is off.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Subagents
|
|
44
|
+
|
|
45
|
+
Source: `packages/coding-agent/src/core/agent-registry.ts` (`loadAgentRegistry`).
|
|
46
|
+
|
|
47
|
+
Registered **lowest precedence first**; later sources override earlier ones by name:
|
|
48
|
+
|
|
49
|
+
1. Built-in agents
|
|
50
|
+
2. Package-manifest agents (`hoocode.agents` in `package.json`)
|
|
51
|
+
3. `~/.claude/agents` (source `claude-user`)
|
|
52
|
+
4. `{agentDir}/agents` i.e. `~/.hoocode/agents` (source `user`)
|
|
53
|
+
5. `.agents/agents` — ancestor-walk from cwd up to git root, registered git-root-first so the cwd-level entry overrides ancestors (source `project`)
|
|
54
|
+
6. `{cwd}/.claude/agents` (source `claude-project`)
|
|
55
|
+
7. `{cwd}/.hoocode/agents` (source `project`)
|
|
56
|
+
8. CLI `--agent` paths (highest precedence)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## MCP servers
|
|
61
|
+
|
|
62
|
+
Source: `packages/coding-agent/src/extensions/core/hoo-core.ts` (`setupMcpLoader`).
|
|
63
|
+
|
|
64
|
+
**First-wins by server name**, in this order:
|
|
65
|
+
|
|
66
|
+
1. `~/.agents/mcp.json` — user, standard format (`mcpServers` object)
|
|
67
|
+
2. `./.agents/mcp.json` — project, standard format
|
|
68
|
+
3. `~/.config/claude/mcp.json` — Claude Desktop, standard format
|
|
69
|
+
4. `~/.hoocode/mcp-servers/*.json` — user, one file per server (each with top-level `name`/`command`)
|
|
70
|
+
5. `./.hoocode/mcp-servers/*.json` — project, per-server format
|
|
71
|
+
|
|
72
|
+
Standard `mcp.json` format:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"mcpServers": {
|
|
77
|
+
"my-server": {
|
|
78
|
+
"command": "npx",
|
|
79
|
+
"args": ["-y", "my-mcp-server"],
|
|
80
|
+
"env": { "API_KEY": "..." }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Per-server format (`.hoocode/mcp-servers/<name>.json`):
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"name": "my-server",
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": ["-y", "my-mcp-server"]
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Debugging discovery
|
|
99
|
+
|
|
100
|
+
Run `hoocode resources` to print everything that was discovered for the current cwd — skills, subagents, slash commands, and MCP servers — with source path and origin label.
|
|
101
|
+
|
|
102
|
+
Source: `packages/coding-agent/src/resources-cli.ts`.
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
$ hoocode resources
|
|
106
|
+
Resources discovered for /path/to/project
|
|
107
|
+
|
|
108
|
+
Skills (0)
|
|
109
|
+
(none)
|
|
110
|
+
|
|
111
|
+
Subagents (1)
|
|
112
|
+
docs - Maintain project documentation [project (.hoocode)]
|
|
113
|
+
/path/to/project/.hoocode/agents/docs.md
|
|
114
|
+
|
|
115
|
+
Slash commands (1)
|
|
116
|
+
/review - Review a diff for risk [project (.hoocode)]
|
|
117
|
+
/path/to/project/.hoocode/commands/review.md
|
|
118
|
+
|
|
119
|
+
MCP servers (1)
|
|
120
|
+
my-db-tools [project (.agents)]
|
|
121
|
+
/path/to/project/.agents/mcp.json
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This is the fastest way to verify why a `.agents/` resource is or is not being loaded.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolisachint/hoocode-agent",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.45",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"hoocodeConfig": {
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"prepublishOnly": "npm run clean && npm run build"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@kolisachint/hoocode-agent-core": "^0.4.
|
|
48
|
-
"@kolisachint/hoocode-ai": "^0.4.
|
|
49
|
-
"@kolisachint/hoocode-tui": "^0.4.
|
|
47
|
+
"@kolisachint/hoocode-agent-core": "^0.4.45",
|
|
48
|
+
"@kolisachint/hoocode-ai": "^0.4.45",
|
|
49
|
+
"@kolisachint/hoocode-tui": "^0.4.45",
|
|
50
50
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
|
51
51
|
"chalk": "^5.5.0",
|
|
52
52
|
"cli-highlight": "^2.1.11",
|