@hi-man/himan 0.1.0 → 0.2.2
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/.nvmrc +1 -0
- package/CHANGELOG.md +65 -0
- package/README.md +75 -66
- package/dist/adapters/git/repo-manager.js +41 -6
- package/dist/adapters/resource/resource-scanner.js +3 -1
- package/dist/adapters/source/git-source-adapter.js +103 -17
- package/dist/bin/himan-project.js +7 -0
- package/dist/bin/himan-resource.js +7 -0
- package/dist/bin/himan-source.js +7 -0
- package/dist/bin/himan.js +7 -0
- package/dist/{index.js → bin/shared.js} +3 -5
- package/dist/cli/agent-commands.js +93 -0
- package/dist/cli/builders.js +72 -0
- package/dist/cli/index.js +3 -318
- package/dist/cli/project-commands.js +127 -0
- package/dist/cli/resource-commands.js +104 -0
- package/dist/cli/shared.js +69 -0
- package/dist/cli/source-commands.js +58 -0
- package/dist/services/index.js +261 -70
- package/dist/state/index-cache-store.js +4 -3
- package/dist/state/project-config-store.js +43 -0
- package/dist/state/project-lock-store.js +4 -0
- package/dist/state/state-store.js +5 -1
- package/dist/utils/agent-configs.js +71 -0
- package/dist/utils/errors.js +2 -0
- package/dist/{version.js → utils/version.js} +1 -1
- package/docs/development.md +83 -0
- package/docs/error-codes.md +132 -0
- package/docs/mvp/README.md +143 -0
- package/docs/mvp/create-resource.md +129 -0
- package/docs/mvp/impl.md +111 -0
- package/package.json +31 -7
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { HimanError, errorCodes } from "../utils/errors.js";
|
|
2
|
+
import { getSupportedAgentNames, normalizeAgent } from "../utils/agent-configs.js";
|
|
3
|
+
import { runAction } from "./shared.js";
|
|
4
|
+
export function registerAgentCommands(command, services) {
|
|
5
|
+
command
|
|
6
|
+
.command("list")
|
|
7
|
+
.option("--json", "output json format")
|
|
8
|
+
.description("List supported agents")
|
|
9
|
+
.action(async (options) => {
|
|
10
|
+
await runAction(async () => {
|
|
11
|
+
const agents = getSupportedAgentNames();
|
|
12
|
+
if (options.json) {
|
|
13
|
+
process.stdout.write(`${JSON.stringify(agents, null, 2)}\n`);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
for (const agent of agents) {
|
|
17
|
+
process.stdout.write(`- ${agent}\n`);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
command
|
|
22
|
+
.command("use")
|
|
23
|
+
.argument("<agent_list>", "agent list, comma separated")
|
|
24
|
+
.option("--global", "save as global default")
|
|
25
|
+
.option("--project", "save as current project default")
|
|
26
|
+
.option("--json", "output json format")
|
|
27
|
+
.description("Set default agents globally or for current project")
|
|
28
|
+
.action(async (agentList, options) => {
|
|
29
|
+
await runAction(async () => {
|
|
30
|
+
const scope = parseScope(options);
|
|
31
|
+
const result = await services.setAgents(parseAgents(agentList), scope, process.cwd());
|
|
32
|
+
if (options.json) {
|
|
33
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
process.stdout.write(`Using agents (${result.scope}): ${result.agents.join(", ")}\n`);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
command
|
|
40
|
+
.command("current")
|
|
41
|
+
.option("--json", "output json format")
|
|
42
|
+
.description("Show configured and effective default agents")
|
|
43
|
+
.action(async (options) => {
|
|
44
|
+
await runAction(async () => {
|
|
45
|
+
const settings = await services.getAgentSettings(process.cwd());
|
|
46
|
+
if (options.json) {
|
|
47
|
+
process.stdout.write(`${JSON.stringify(settings, null, 2)}\n`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
process.stdout.write(`effective: ${settings.effective.join(", ")}\n`);
|
|
51
|
+
process.stdout.write(`project: ${(settings.project ?? []).join(", ") || "-"}\n`);
|
|
52
|
+
process.stdout.write(`global: ${(settings.global ?? []).join(", ") || "-"}\n`);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
command
|
|
56
|
+
.command("clear")
|
|
57
|
+
.option("--global", "clear global default agents")
|
|
58
|
+
.option("--project", "clear current project default agents")
|
|
59
|
+
.option("--json", "output json format")
|
|
60
|
+
.description("Clear configured default agents")
|
|
61
|
+
.action(async (options) => {
|
|
62
|
+
await runAction(async () => {
|
|
63
|
+
const result = await services.clearAgents(parseScope(options), process.cwd());
|
|
64
|
+
if (options.json) {
|
|
65
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
process.stdout.write(`Cleared agents (${result.scope}).\n`);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function parseScope(options) {
|
|
73
|
+
if (options.global && options.project) {
|
|
74
|
+
throw new HimanError(errorCodes.CLI_USAGE, "Use only one of --global or --project.");
|
|
75
|
+
}
|
|
76
|
+
return options.global ? "global" : "project";
|
|
77
|
+
}
|
|
78
|
+
function parseAgents(input) {
|
|
79
|
+
const agents = input
|
|
80
|
+
.split(",")
|
|
81
|
+
.map((item) => item.trim())
|
|
82
|
+
.filter(Boolean);
|
|
83
|
+
if (agents.length === 0) {
|
|
84
|
+
throw new HimanError(errorCodes.INVALID_INPUT, "Agent list cannot be empty.");
|
|
85
|
+
}
|
|
86
|
+
const supported = getSupportedAgentNames();
|
|
87
|
+
for (const agent of agents) {
|
|
88
|
+
if (!normalizeAgent(agent)) {
|
|
89
|
+
throw new HimanError(errorCodes.INVALID_INPUT, `Unsupported agent: ${agent}. Supported agents: ${supported.join(", ")}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return agents;
|
|
93
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ServiceFactory } from "../services/index.js";
|
|
2
|
+
import { registerAgentCommands } from "./agent-commands.js";
|
|
3
|
+
import { registerProjectCommands } from "./project-commands.js";
|
|
4
|
+
import { registerResourceCommands } from "./resource-commands.js";
|
|
5
|
+
import { registerInitCommand, registerSourceCommands } from "./source-commands.js";
|
|
6
|
+
import { createBaseProgram } from "./shared.js";
|
|
7
|
+
export function buildCli() {
|
|
8
|
+
const program = createBaseProgram("himan", "Prompt and agent asset management CLI");
|
|
9
|
+
const services = new ServiceFactory();
|
|
10
|
+
appendCommandGroupsHelp(program);
|
|
11
|
+
registerInitCommand(program, services);
|
|
12
|
+
const sourceCmd = program.command("source").description("Manage source repositories");
|
|
13
|
+
registerSourceCommands(sourceCmd, services, { includeInit: true });
|
|
14
|
+
const resourceCmd = program
|
|
15
|
+
.command("resource")
|
|
16
|
+
.description("Manage resources from current default source");
|
|
17
|
+
registerResourceCommands(resourceCmd, services);
|
|
18
|
+
const projectCmd = program
|
|
19
|
+
.command("project")
|
|
20
|
+
.description("Manage installed resources in current project");
|
|
21
|
+
registerProjectCommands(projectCmd, services);
|
|
22
|
+
const agentCmd = program
|
|
23
|
+
.command("agent")
|
|
24
|
+
.description("Manage default agent configuration");
|
|
25
|
+
registerAgentCommands(agentCmd, services);
|
|
26
|
+
// Backward compatible top-level resource lifecycle commands.
|
|
27
|
+
registerResourceCommands(program, services);
|
|
28
|
+
registerProjectCommands(program, services);
|
|
29
|
+
return program;
|
|
30
|
+
}
|
|
31
|
+
export function buildSourceCli() {
|
|
32
|
+
const program = createBaseProgram("himan-source", "Source repository management CLI");
|
|
33
|
+
const services = new ServiceFactory();
|
|
34
|
+
registerSourceCommands(program, services, { includeInit: true });
|
|
35
|
+
return program;
|
|
36
|
+
}
|
|
37
|
+
export function buildResourceCli() {
|
|
38
|
+
const program = createBaseProgram("himan-resource", "Resource lifecycle management CLI");
|
|
39
|
+
const services = new ServiceFactory();
|
|
40
|
+
registerResourceCommands(program, services);
|
|
41
|
+
const agentCmd = program
|
|
42
|
+
.command("agent")
|
|
43
|
+
.description("Manage default agent configuration");
|
|
44
|
+
registerAgentCommands(agentCmd, services);
|
|
45
|
+
// Backward compatible: keep project lifecycle commands in himan-resource.
|
|
46
|
+
registerProjectCommands(program, services);
|
|
47
|
+
return program;
|
|
48
|
+
}
|
|
49
|
+
export function buildProjectCli() {
|
|
50
|
+
const program = createBaseProgram("himan-project", "Project lifecycle management CLI");
|
|
51
|
+
const services = new ServiceFactory();
|
|
52
|
+
const agentCmd = program
|
|
53
|
+
.command("agent")
|
|
54
|
+
.description("Manage default agent configuration");
|
|
55
|
+
registerAgentCommands(agentCmd, services);
|
|
56
|
+
registerProjectCommands(program, services);
|
|
57
|
+
return program;
|
|
58
|
+
}
|
|
59
|
+
function appendCommandGroupsHelp(program) {
|
|
60
|
+
program.addHelpText("after", `
|
|
61
|
+
Command groups:
|
|
62
|
+
source Data source management (git now, registry reserved)
|
|
63
|
+
init, source init, source add, source use, source list
|
|
64
|
+
resource Source resource discovery and metadata
|
|
65
|
+
list, history, create, resource list, resource history, resource create
|
|
66
|
+
project Resource usage lifecycle in current project
|
|
67
|
+
install, dev, uninstall, publish,
|
|
68
|
+
project install, project dev, project uninstall, project publish
|
|
69
|
+
agent Default agent configuration
|
|
70
|
+
agent list, agent use, agent current, agent clear
|
|
71
|
+
`);
|
|
72
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -1,318 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { PACKAGE_VERSION } from "../version.js";
|
|
5
|
-
export function buildCli() {
|
|
6
|
-
const program = new Command();
|
|
7
|
-
const services = new ServiceFactory();
|
|
8
|
-
program.exitOverride();
|
|
9
|
-
program.configureOutput({
|
|
10
|
-
writeOut: (str) => {
|
|
11
|
-
process.stdout.write(str);
|
|
12
|
-
},
|
|
13
|
-
writeErr: () => {
|
|
14
|
-
// Parse/usage errors are unified by writeCliError().
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
program
|
|
18
|
-
.name("himan")
|
|
19
|
-
.description("Prompt and agent asset management CLI")
|
|
20
|
-
.version(PACKAGE_VERSION);
|
|
21
|
-
appendCommandGroupsHelp(program);
|
|
22
|
-
program
|
|
23
|
-
.command("init")
|
|
24
|
-
.argument("<git_repo>", "Git repository URL")
|
|
25
|
-
.action(async (gitRepo) => {
|
|
26
|
-
await runAction(async () => {
|
|
27
|
-
const result = await services.initSource("git", gitRepo);
|
|
28
|
-
process.stdout.write(`Initialized ${result.sourceType} source: ${result.repo}\n`);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
const sourceCmd = program.command("source").description("Manage source repositories");
|
|
32
|
-
sourceCmd
|
|
33
|
-
.command("add")
|
|
34
|
-
.argument("<name>", "source name (kebab-case)")
|
|
35
|
-
.argument("<git_repo>", "Git repository URL")
|
|
36
|
-
.description("Add a named git source")
|
|
37
|
-
.action(async (name, gitRepo) => {
|
|
38
|
-
await runAction(async () => {
|
|
39
|
-
const result = await services.addSource(name, "git", gitRepo);
|
|
40
|
-
process.stdout.write(`Added source ${result.name}: ${result.type}${result.repo ? ` ${result.repo}` : ""}\n`);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
sourceCmd
|
|
44
|
-
.command("use")
|
|
45
|
-
.argument("<name>", "source name")
|
|
46
|
-
.description("Switch default source")
|
|
47
|
-
.action(async (name) => {
|
|
48
|
-
await runAction(async () => {
|
|
49
|
-
const result = await services.useSource(name);
|
|
50
|
-
process.stdout.write(`Using source: ${result.name}\n`);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
sourceCmd
|
|
54
|
-
.command("list")
|
|
55
|
-
.option("--json", "output json format")
|
|
56
|
-
.description("List configured sources and current default")
|
|
57
|
-
.action(async (options) => {
|
|
58
|
-
await runAction(async () => {
|
|
59
|
-
const sources = await services.listSources();
|
|
60
|
-
if (options.json) {
|
|
61
|
-
process.stdout.write(`${JSON.stringify(sources, null, 2)}\n`);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (sources.length === 0) {
|
|
65
|
-
process.stdout.write("No sources configured.\n");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
for (const source of sources) {
|
|
69
|
-
process.stdout.write(`- ${source.name}${source.isDefault ? " (default)" : ""}: ${source.type}${source.repo ? ` ${source.repo}` : ""}\n`);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
program
|
|
74
|
-
.command("list")
|
|
75
|
-
.argument("[type]", "resource type", "rule")
|
|
76
|
-
.option("--json", "output json format")
|
|
77
|
-
.description("List resources from current default source")
|
|
78
|
-
.action(async (type, options) => {
|
|
79
|
-
await runAction(async () => {
|
|
80
|
-
const resourceType = ensureResourceType(type);
|
|
81
|
-
const resources = await services.list(resourceType);
|
|
82
|
-
if (options.json) {
|
|
83
|
-
process.stdout.write(`${JSON.stringify(resources, null, 2)}\n`);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
if (resources.length === 0) {
|
|
87
|
-
process.stdout.write("No resources found.\n");
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
for (const resource of resources) {
|
|
91
|
-
process.stdout.write(`- ${resource.type}/${resource.name}${resource.description ? `: ${resource.description}` : ""}\n`);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
program
|
|
96
|
-
.command("history")
|
|
97
|
-
.argument("<type>", "resource type")
|
|
98
|
-
.argument("<name>", "resource name")
|
|
99
|
-
.option("--json", "output json format")
|
|
100
|
-
.description("Show resource history")
|
|
101
|
-
.action(async (type, name, options) => {
|
|
102
|
-
await runAction(async () => {
|
|
103
|
-
const resourceType = ensureResourceType(type);
|
|
104
|
-
const versions = await services.history(resourceType, name);
|
|
105
|
-
if (options.json) {
|
|
106
|
-
process.stdout.write(`${JSON.stringify(versions, null, 2)}\n`);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
if (versions.length === 0) {
|
|
110
|
-
process.stdout.write(`No history found for ${resourceType}/${name}.\n`);
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
for (const version of versions) {
|
|
114
|
-
process.stdout.write(`- ${version.raw}\n`);
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
program
|
|
119
|
-
.command("install")
|
|
120
|
-
.argument("[type]", "resource type")
|
|
121
|
-
.argument("[name[@version]]", "resource name with optional @version")
|
|
122
|
-
.description("Install resource, or install from himan.lock")
|
|
123
|
-
.action(async (type, nameVersion) => {
|
|
124
|
-
await runAction(async () => {
|
|
125
|
-
if (!type && !nameVersion) {
|
|
126
|
-
const results = await services.installFromLock(process.cwd());
|
|
127
|
-
if (results.length === 0) {
|
|
128
|
-
process.stdout.write("No resources in lock file.\n");
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
for (const item of results) {
|
|
132
|
-
process.stdout.write(`Installed ${item.type}/${item.name}@${item.version}\n`);
|
|
133
|
-
}
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (!type || !nameVersion) {
|
|
137
|
-
throw new Error("Install usage:\n"
|
|
138
|
-
+ " - himan install # install from himan.lock\n"
|
|
139
|
-
+ " - himan install <type> <name[@version]> # install single resource");
|
|
140
|
-
}
|
|
141
|
-
const resourceType = ensureResourceType(type);
|
|
142
|
-
const { name, version } = parseNameVersion(nameVersion);
|
|
143
|
-
const result = await services.install(resourceType, name, version, process.cwd());
|
|
144
|
-
process.stdout.write(`Installed ${result.type}/${result.name}@${result.version}\n`);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
program
|
|
148
|
-
.command("dev")
|
|
149
|
-
.argument("<type>", "resource type")
|
|
150
|
-
.argument("<name>", "resource name")
|
|
151
|
-
.description("Switch resource to development mode")
|
|
152
|
-
.action(async (type, name) => {
|
|
153
|
-
await runAction(async () => {
|
|
154
|
-
const resourceType = ensureResourceType(type);
|
|
155
|
-
const result = await services.dev(resourceType, name, process.cwd());
|
|
156
|
-
process.stdout.write(`Switched ${result.type}/${result.name} to dev mode: ${result.devPath}\n`);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
program
|
|
160
|
-
.command("uninstall")
|
|
161
|
-
.argument("<type>", "resource type")
|
|
162
|
-
.argument("<name>", "resource name")
|
|
163
|
-
.description("Uninstall resource from project and lock")
|
|
164
|
-
.action(async (type, name) => {
|
|
165
|
-
await runAction(async () => {
|
|
166
|
-
const resourceType = ensureResourceType(type);
|
|
167
|
-
const result = await services.uninstall(resourceType, name, process.cwd());
|
|
168
|
-
process.stdout.write(`Uninstalled ${result.type}/${result.name}\n`);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
program
|
|
172
|
-
.command("publish")
|
|
173
|
-
.argument("<type>", "resource type")
|
|
174
|
-
.argument("<name>", "resource name")
|
|
175
|
-
.option("--patch", "patch release")
|
|
176
|
-
.option("--minor", "minor release")
|
|
177
|
-
.option("--major", "major release")
|
|
178
|
-
.description("Publish resource (default: --patch)")
|
|
179
|
-
.action(async (type, name, options) => {
|
|
180
|
-
await runAction(async () => {
|
|
181
|
-
const resourceType = ensureResourceType(type);
|
|
182
|
-
const releaseType = resolveReleaseType(options);
|
|
183
|
-
const result = await services.publish(resourceType, name, releaseType, process.cwd());
|
|
184
|
-
process.stdout.write(`Published ${result.type}/${result.name}@${result.version}\n`);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
program
|
|
188
|
-
.command("create")
|
|
189
|
-
.argument("<type>", "resource type")
|
|
190
|
-
.argument("<name>", "resource name")
|
|
191
|
-
.option("--description <text>", "resource description")
|
|
192
|
-
.option("--target <list>", "targets list, comma separated")
|
|
193
|
-
.option("--entry <file>", "entry file name")
|
|
194
|
-
.option("--template <name>", "template name", "basic")
|
|
195
|
-
.option("--force", "overwrite existing resource")
|
|
196
|
-
.option("--dry-run", "show files without writing")
|
|
197
|
-
.option("--json", "output json format")
|
|
198
|
-
.description("Create resource scaffold")
|
|
199
|
-
.action(async (type, name, options) => {
|
|
200
|
-
await runAction(async () => {
|
|
201
|
-
const resourceType = ensureCreateResourceType(type);
|
|
202
|
-
const result = await services.create(resourceType, name, {
|
|
203
|
-
description: options.description,
|
|
204
|
-
targets: parseTargets(options.target),
|
|
205
|
-
entry: options.entry,
|
|
206
|
-
template: options.template,
|
|
207
|
-
force: options.force,
|
|
208
|
-
dryRun: options.dryRun,
|
|
209
|
-
});
|
|
210
|
-
if (options.json) {
|
|
211
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
process.stdout.write(`Created ${result.type}/${result.name} at ${result.resourceDir}${result.dryRun ? " (dry-run)" : ""}\n`);
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
return program;
|
|
218
|
-
}
|
|
219
|
-
function ensureResourceType(type) {
|
|
220
|
-
if (type !== "rule" && type !== "command" && type !== "skill") {
|
|
221
|
-
throw new Error(`Unsupported resource type: ${type}`);
|
|
222
|
-
}
|
|
223
|
-
return type;
|
|
224
|
-
}
|
|
225
|
-
function parseNameVersion(input) {
|
|
226
|
-
const idx = input.lastIndexOf("@");
|
|
227
|
-
if (idx <= 0)
|
|
228
|
-
return { name: input };
|
|
229
|
-
return { name: input.slice(0, idx), version: input.slice(idx + 1) };
|
|
230
|
-
}
|
|
231
|
-
async function runAction(action) {
|
|
232
|
-
try {
|
|
233
|
-
await action();
|
|
234
|
-
}
|
|
235
|
-
catch (error) {
|
|
236
|
-
writeCliError(error);
|
|
237
|
-
process.exitCode = 1;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
export function writeCliError(error) {
|
|
241
|
-
const payload = toCliErrorPayload(error);
|
|
242
|
-
if (shouldOutputJsonError()) {
|
|
243
|
-
process.stderr.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
process.stderr.write(`[${payload.error.code}] ${payload.error.message}\n`);
|
|
247
|
-
}
|
|
248
|
-
function toCliErrorPayload(error) {
|
|
249
|
-
if (error instanceof CommanderError) {
|
|
250
|
-
return {
|
|
251
|
-
ok: false,
|
|
252
|
-
error: {
|
|
253
|
-
code: errorCodes.CLI_USAGE,
|
|
254
|
-
message: error.message,
|
|
255
|
-
details: {
|
|
256
|
-
commanderCode: error.code,
|
|
257
|
-
exitCode: error.exitCode,
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
if (error instanceof HimanError) {
|
|
263
|
-
return {
|
|
264
|
-
ok: false,
|
|
265
|
-
error: {
|
|
266
|
-
code: error.code,
|
|
267
|
-
message: error.message,
|
|
268
|
-
details: error.details,
|
|
269
|
-
},
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
return {
|
|
273
|
-
ok: false,
|
|
274
|
-
error: {
|
|
275
|
-
code: "E_UNKNOWN",
|
|
276
|
-
message: error instanceof Error ? error.message : String(error),
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
function shouldOutputJsonError() {
|
|
281
|
-
return process.argv.includes("--json");
|
|
282
|
-
}
|
|
283
|
-
function appendCommandGroupsHelp(program) {
|
|
284
|
-
program.addHelpText("after", `
|
|
285
|
-
Command groups:
|
|
286
|
-
source Data source management (git now, registry reserved)
|
|
287
|
-
init, source add, source use, source list
|
|
288
|
-
resource Source resource discovery and metadata
|
|
289
|
-
list, history, create
|
|
290
|
-
project Resource usage lifecycle in current project
|
|
291
|
-
install, dev, uninstall, publish
|
|
292
|
-
`);
|
|
293
|
-
}
|
|
294
|
-
function resolveReleaseType(options) {
|
|
295
|
-
const selected = [
|
|
296
|
-
options.patch ? "patch" : undefined,
|
|
297
|
-
options.minor ? "minor" : undefined,
|
|
298
|
-
options.major ? "major" : undefined,
|
|
299
|
-
].filter(Boolean);
|
|
300
|
-
if (selected.length > 1) {
|
|
301
|
-
throw new Error("Use only one of --patch, --minor or --major.");
|
|
302
|
-
}
|
|
303
|
-
return selected[0] ?? "patch";
|
|
304
|
-
}
|
|
305
|
-
function ensureCreateResourceType(type) {
|
|
306
|
-
if (type !== "rule" && type !== "command" && type !== "skill") {
|
|
307
|
-
throw new Error(`Unsupported resource type: ${type}`);
|
|
308
|
-
}
|
|
309
|
-
return type;
|
|
310
|
-
}
|
|
311
|
-
function parseTargets(input) {
|
|
312
|
-
if (!input)
|
|
313
|
-
return undefined;
|
|
314
|
-
return input
|
|
315
|
-
.split(",")
|
|
316
|
-
.map((item) => item.trim())
|
|
317
|
-
.filter(Boolean);
|
|
318
|
-
}
|
|
1
|
+
export { buildCli, buildProjectCli, buildResourceCli, buildSourceCli } from "./builders.js";
|
|
2
|
+
export { registerAgentCommands } from "./agent-commands.js";
|
|
3
|
+
export { writeCliError } from "./shared.js";
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { HimanError, errorCodes } from "../utils/errors.js";
|
|
2
|
+
import { getSupportedAgentNames, normalizeAgent } from "../utils/agent-configs.js";
|
|
3
|
+
import { runAction } from "./shared.js";
|
|
4
|
+
export function registerProjectCommands(command, services) {
|
|
5
|
+
command
|
|
6
|
+
.command("install")
|
|
7
|
+
.argument("[type]", "resource type")
|
|
8
|
+
.argument("[name[@version]]", "resource name with optional @version")
|
|
9
|
+
.option("--agent <list>", "install target agents, comma separated")
|
|
10
|
+
.option("--mode <mode>", "install mode: link or copy")
|
|
11
|
+
.description("Install resource, or install from himan.lock")
|
|
12
|
+
.action(async (type, nameVersion, options) => {
|
|
13
|
+
await runAction(async () => {
|
|
14
|
+
const agents = parseAgents(options.agent);
|
|
15
|
+
const mode = parseInstallMode(options.mode);
|
|
16
|
+
if (!type && !nameVersion) {
|
|
17
|
+
const results = await services.installFromLock(process.cwd(), agents, mode);
|
|
18
|
+
if (results.length === 0) {
|
|
19
|
+
process.stdout.write("No resources in lock file.\n");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
for (const item of results) {
|
|
23
|
+
process.stdout.write(`Installed ${item.type}/${item.name}@${item.version}\n`);
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!type || !nameVersion) {
|
|
28
|
+
throw new HimanError(errorCodes.CLI_USAGE, "Install usage:\n"
|
|
29
|
+
+ " - himan install # install from himan.lock\n"
|
|
30
|
+
+ " - himan install <type> <name[@version]> [--mode link|copy] # install single resource");
|
|
31
|
+
}
|
|
32
|
+
const resourceType = ensureResourceType(type);
|
|
33
|
+
const { name, version } = parseNameVersion(nameVersion);
|
|
34
|
+
const result = await services.install(resourceType, name, version, process.cwd(), agents, mode);
|
|
35
|
+
process.stdout.write(`Installed ${result.type}/${result.name}@${result.version}\n`);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
command
|
|
39
|
+
.command("dev")
|
|
40
|
+
.argument("<type>", "resource type")
|
|
41
|
+
.argument("<name>", "resource name")
|
|
42
|
+
.description("Switch resource to development mode")
|
|
43
|
+
.action(async (type, name) => {
|
|
44
|
+
await runAction(async () => {
|
|
45
|
+
const resourceType = ensureResourceType(type);
|
|
46
|
+
const result = await services.dev(resourceType, name, process.cwd());
|
|
47
|
+
process.stdout.write(`Switched ${result.type}/${result.name} to dev mode: ${result.devPath}\n`);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
command
|
|
51
|
+
.command("uninstall")
|
|
52
|
+
.argument("<type>", "resource type")
|
|
53
|
+
.argument("<name>", "resource name")
|
|
54
|
+
.description("Uninstall resource from project and lock")
|
|
55
|
+
.action(async (type, name) => {
|
|
56
|
+
await runAction(async () => {
|
|
57
|
+
const resourceType = ensureResourceType(type);
|
|
58
|
+
const result = await services.uninstall(resourceType, name, process.cwd());
|
|
59
|
+
process.stdout.write(`Uninstalled ${result.type}/${result.name}\n`);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
command
|
|
63
|
+
.command("publish")
|
|
64
|
+
.argument("<type>", "resource type")
|
|
65
|
+
.argument("<name>", "resource name")
|
|
66
|
+
.option("--patch", "patch release")
|
|
67
|
+
.option("--minor", "minor release")
|
|
68
|
+
.option("--major", "major release")
|
|
69
|
+
.description("Publish resource (default: --patch)")
|
|
70
|
+
.action(async (type, name, options) => {
|
|
71
|
+
await runAction(async () => {
|
|
72
|
+
const resourceType = ensureResourceType(type);
|
|
73
|
+
const releaseType = resolveReleaseType(options);
|
|
74
|
+
const result = await services.publish(resourceType, name, releaseType, process.cwd());
|
|
75
|
+
process.stdout.write(`Published ${result.type}/${result.name}@${result.version}\n`);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function ensureResourceType(type) {
|
|
80
|
+
if (type !== "rule" && type !== "command" && type !== "skill") {
|
|
81
|
+
throw new HimanError(errorCodes.UNSUPPORTED_RESOURCE_TYPE, `Unsupported resource type: ${type}`);
|
|
82
|
+
}
|
|
83
|
+
return type;
|
|
84
|
+
}
|
|
85
|
+
function parseNameVersion(input) {
|
|
86
|
+
const idx = input.lastIndexOf("@");
|
|
87
|
+
if (idx <= 0)
|
|
88
|
+
return { name: input };
|
|
89
|
+
return { name: input.slice(0, idx), version: input.slice(idx + 1) };
|
|
90
|
+
}
|
|
91
|
+
function resolveReleaseType(options) {
|
|
92
|
+
const selected = [
|
|
93
|
+
options.patch ? "patch" : undefined,
|
|
94
|
+
options.minor ? "minor" : undefined,
|
|
95
|
+
options.major ? "major" : undefined,
|
|
96
|
+
].filter(Boolean);
|
|
97
|
+
if (selected.length > 1) {
|
|
98
|
+
throw new HimanError(errorCodes.CLI_USAGE, "Use only one of --patch, --minor or --major.");
|
|
99
|
+
}
|
|
100
|
+
return selected[0] ?? "patch";
|
|
101
|
+
}
|
|
102
|
+
function parseInstallMode(input) {
|
|
103
|
+
if (!input)
|
|
104
|
+
return undefined;
|
|
105
|
+
const normalized = input.trim().toLowerCase();
|
|
106
|
+
if (normalized === "link" || normalized === "copy") {
|
|
107
|
+
return normalized;
|
|
108
|
+
}
|
|
109
|
+
throw new HimanError(errorCodes.INVALID_INPUT, `Unsupported install mode: ${input}. Supported modes: link, copy`);
|
|
110
|
+
}
|
|
111
|
+
function parseAgents(input) {
|
|
112
|
+
if (!input)
|
|
113
|
+
return undefined;
|
|
114
|
+
const agents = input
|
|
115
|
+
.split(",")
|
|
116
|
+
.map((item) => item.trim())
|
|
117
|
+
.filter(Boolean);
|
|
118
|
+
if (agents.length === 0)
|
|
119
|
+
return undefined;
|
|
120
|
+
const supported = getSupportedAgentNames();
|
|
121
|
+
for (const agent of agents) {
|
|
122
|
+
if (!normalizeAgent(agent)) {
|
|
123
|
+
throw new HimanError(errorCodes.INVALID_INPUT, `Unsupported agent: ${agent}. Supported agents: ${supported.join(", ")}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return agents;
|
|
127
|
+
}
|