@hi-man/himan 0.3.1 → 0.3.3
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 +21 -0
- package/README.md +12 -6
- package/dist/cli/builders.js +7 -6
- package/dist/cli/installed-resource-list.js +46 -0
- package/dist/cli/project-commands.js +42 -4
- package/dist/cli/resource-commands.js +93 -10
- package/dist/services/index.js +57 -12
- package/dist/utils/agent-configs.js +7 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@ The format is based on Keep a Changelog, and this project follows semver for the
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Changed project guidance to require changelog updates for user-visible CLI behavior changes.
|
|
12
|
+
|
|
13
|
+
## [0.3.3] - 2026-05-11
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added `himan project list` and `himan list --installed` to show resources recorded in the current project's `himan.lock`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Changed `himan list` without a resource type to group all source resources by `rule`, `command`, and `skill`.
|
|
22
|
+
- Added `himan list --brief` to hide resource descriptions in concise list output.
|
|
23
|
+
|
|
24
|
+
## [0.3.2] - 2026-05-08
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Added `himan install <type> <name[@version]> --global` to install a resource into the matching user-level agent directory, reusing the current project's resource agent when available and without writing the project lock file.
|
|
29
|
+
|
|
9
30
|
## [0.3.1] - 2026-05-07
|
|
10
31
|
|
|
11
32
|
### Added
|
package/README.md
CHANGED
|
@@ -56,11 +56,16 @@ himan publish rule my-rule --patch
|
|
|
56
56
|
- `claude-code` -> `.claude/{rules|commands|skills}/<name>`
|
|
57
57
|
- `codex` -> `.agents/{rules|commands|skills}/<name>`
|
|
58
58
|
- `openclaw` -> `.openclaw/{rules|commands|skills}/<name>`
|
|
59
|
+
- 加 `--global` 时会安装到用户级 agent 目录,并仍按当前项目生效的 agent 选择目标:
|
|
60
|
+
- `cursor` -> `~/.cursor/{rules|commands|skills}/<name>`
|
|
61
|
+
- `claude-code` -> `~/.claude/{rules|commands|skills}/<name>`
|
|
62
|
+
- `codex` -> `~/.agents/{rules|commands|skills}/<name>`
|
|
63
|
+
- `openclaw` -> `~/.openclaw/{rules|commands|skills}/<name>`
|
|
59
64
|
- 开发态目录:
|
|
60
65
|
- `rule` -> `.himan/dev/rule/<name>`
|
|
61
66
|
- `command` -> `.himan/dev/command/<name>`
|
|
62
67
|
- `skill` -> `.himan/dev/skill/<name>`
|
|
63
|
-
- lock
|
|
68
|
+
- lock 文件:项目安装 `install <type> <name[@version]>` 会写入 `himan.lock`,记录 source、精确版本、agent 和安装模式;`himan install`(无参数)会按 lock 记录的 source 批量恢复安装,不受当前 default source 切换影响。`--global` 安装不写当前项目的 `himan.lock`。
|
|
64
69
|
- 安装模式:默认 `--mode link` 使用软链;也可用 `--mode copy` 将资源复制到目标 agent 目录,lock 会记录并复现该模式。
|
|
65
70
|
- 默认 agent:`agent use <agent>` 默认写当前项目 `.himan/config.json`;加 `--global` 写入 `~/.himan/config.json`。当前项目配置优先于全局配置。
|
|
66
71
|
|
|
@@ -128,7 +133,7 @@ your-himan-source/
|
|
|
128
133
|
|
|
129
134
|
| 命令 | 说明 |
|
|
130
135
|
| -------------------------------- | ----------------------------------------------------------------------------------- |
|
|
131
|
-
| `list [type] [--agent a,b] [--json]` |
|
|
136
|
+
| `list [type] [--agent a,b] [--brief] [--installed] [--json]` | 默认列出当前 default source 的资源;未传 `type` 时按 `rule`/`command`/`skill` 分组展示全部资源;可按 agent 过滤;默认显示描述,`--brief` 可隐藏描述;`--installed` 改为查看当前项目 `himan.lock` 中的已安装资源 |
|
|
132
137
|
| `history <type> <name> [--json]` | 按 tag 查看版本历史 |
|
|
133
138
|
| `create <type> <name>` | 脚手架;常用选项:`--description`、`--agent a,b`、`--dry-run`、`--force`、`--json` |
|
|
134
139
|
|
|
@@ -136,7 +141,8 @@ your-himan-source/
|
|
|
136
141
|
|
|
137
142
|
| 命令 | 说明 |
|
|
138
143
|
| --------------------------------- | --------------------------------------------------------- |
|
|
139
|
-
| `
|
|
144
|
+
| `list [type] [--agent a,b] [--json]` | 查看当前项目 `himan.lock` 中记录的已安装资源;未传 `type` 时按 `rule`/`command`/`skill` 分组展示 |
|
|
145
|
+
| `install [type] [name[@version]] [--global] [--agent a,b] [--mode link\|copy]` | 有参数时从当前 default source 安装指定资源;**无参数**时按 `himan.lock` 记录的 source 批量安装;加 `--global` 时安装到用户级 agent 目录且不写项目 lock;可覆盖安装目标 agent 或安装模式 |
|
|
140
146
|
| `dev <type> <name>` | 切换到开发态,并按安装模式将项目目标指向或复制自 `.himan/dev/...` |
|
|
141
147
|
| `uninstall <type> <name>` | 从项目移除安装目标,并同步删除 `himan.lock` 条目 |
|
|
142
148
|
| `publish <type> <name>` | 默认 `--patch`;可选 `--minor` / `--major`(勿同时使用多个) |
|
|
@@ -154,12 +160,12 @@ your-himan-source/
|
|
|
154
160
|
|
|
155
161
|
- `himan resource list|history|create ...`
|
|
156
162
|
- `himan-resource list|history|create ...`(兼容保留:也可执行 install/dev/uninstall/publish)
|
|
157
|
-
- `himan project install|dev|uninstall|publish ...`
|
|
158
|
-
- `himan-project install|dev|uninstall|publish ...`
|
|
163
|
+
- `himan project list|install|dev|uninstall|publish ...`
|
|
164
|
+
- `himan-project list|install|dev|uninstall|publish ...`
|
|
159
165
|
- `himan agent list|use|current|clear ...`
|
|
160
166
|
|
|
161
167
|
说明:资源与项目相关命令统一使用 `--agent` 指定目标 Agent。
|
|
162
|
-
若未显式传 `--agent`,`create` / `install` 会使用当前项目默认 agent、全局默认 agent、资源 metadata 或内置默认 `cursor` 中最合适的一项;`dev` 会优先使用 lock 中记录的 agent
|
|
168
|
+
若未显式传 `--agent`,`create` / `install` 会使用当前项目默认 agent、全局默认 agent、资源 metadata 或内置默认 `cursor` 中最合适的一项;`dev` 会优先使用 lock 中记录的 agent。`install --global` 会优先复用当前项目 lock 里该资源的 agent,未命中时再使用默认 install 解析顺序,但目标根目录是用户 home 下对应 agent 目录。
|
|
163
169
|
|
|
164
170
|
`publish` 优先使用项目里 `.himan/dev` 对应目录,否则用源仓库里对应目录。若资源目录包含 `himan.yaml`,发布前会校验元数据与入口文件;若没有 `himan.yaml`,则按默认入口推断最小元数据并发布,不会强制创建 `himan.yaml`。发布需要可推送的 Git 权限。发布 commit 会包含资源目录以及自动维护的 source 根目录 `README.md` / `CHANGELOG.md`。发布成功后会从新版本 store 以 `copy` 模式重新安装到项目目标、更新 lock,并删除对应 `.himan/dev/<type>/<name>` 开发目录。
|
|
165
171
|
|
package/dist/cli/builders.js
CHANGED
|
@@ -25,7 +25,7 @@ export function buildCli() {
|
|
|
25
25
|
registerAgentCommands(agentCmd, services);
|
|
26
26
|
// Backward compatible top-level resource lifecycle commands.
|
|
27
27
|
registerResourceCommands(program, services);
|
|
28
|
-
registerProjectCommands(program, services);
|
|
28
|
+
registerProjectCommands(program, services, { includeList: false });
|
|
29
29
|
return program;
|
|
30
30
|
}
|
|
31
31
|
export function buildSourceCli() {
|
|
@@ -43,7 +43,7 @@ export function buildResourceCli() {
|
|
|
43
43
|
.description("Manage default agent configuration");
|
|
44
44
|
registerAgentCommands(agentCmd, services);
|
|
45
45
|
// Backward compatible: keep project lifecycle commands in himan-resource.
|
|
46
|
-
registerProjectCommands(program, services);
|
|
46
|
+
registerProjectCommands(program, services, { includeList: false });
|
|
47
47
|
return program;
|
|
48
48
|
}
|
|
49
49
|
export function buildProjectCli() {
|
|
@@ -62,10 +62,11 @@ Command groups:
|
|
|
62
62
|
source Data source management (git now, registry reserved)
|
|
63
63
|
init, source init, source add, source use, source list, source init-docs
|
|
64
64
|
resource Source resource discovery and metadata
|
|
65
|
-
list,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
list, list --installed, history, create,
|
|
66
|
+
resource list, resource history, resource create
|
|
67
|
+
project Resource usage lifecycle in current project or user-level agent dirs
|
|
68
|
+
list, install, dev, uninstall, publish,
|
|
69
|
+
project list, project install, project dev, project uninstall, project publish
|
|
69
70
|
agent Default agent configuration
|
|
70
71
|
agent list, agent use, agent current, agent clear
|
|
71
72
|
`);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const RESOURCE_TYPES = ["rule", "command", "skill"];
|
|
2
|
+
export async function listInstalledResourceGroups(services, projectDir, agents) {
|
|
3
|
+
const resources = await services.listInstalled(projectDir, undefined, agents);
|
|
4
|
+
return groupInstalledResources(resources);
|
|
5
|
+
}
|
|
6
|
+
export function groupInstalledResources(resources) {
|
|
7
|
+
return {
|
|
8
|
+
rule: resources.filter((resource) => resource.type === "rule"),
|
|
9
|
+
command: resources.filter((resource) => resource.type === "command"),
|
|
10
|
+
skill: resources.filter((resource) => resource.type === "skill"),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function writeInstalledResourceGroups(groups) {
|
|
14
|
+
const hasResources = RESOURCE_TYPES.some((type) => groups[type].length > 0);
|
|
15
|
+
if (!hasResources) {
|
|
16
|
+
process.stdout.write("No installed resources found.\n");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
for (const type of RESOURCE_TYPES) {
|
|
20
|
+
const resources = groups[type];
|
|
21
|
+
if (resources.length === 0)
|
|
22
|
+
continue;
|
|
23
|
+
process.stdout.write(`${formatGroupTitle(type)}:\n`);
|
|
24
|
+
writeInstalledResources(resources);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function writeInstalledResources(resources) {
|
|
28
|
+
if (resources.length === 0) {
|
|
29
|
+
process.stdout.write("No installed resources found.\n");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const resource of resources) {
|
|
33
|
+
process.stdout.write(`- ${formatInstalledResource(resource)}\n`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function formatInstalledResource(resource) {
|
|
37
|
+
const agents = resource.agents.length > 0 ? ` [${resource.agents.join(", ")}]` : "";
|
|
38
|
+
return `${resource.type}/${resource.name}@${resource.version}${agents} (${resource.mode})`;
|
|
39
|
+
}
|
|
40
|
+
function formatGroupTitle(type) {
|
|
41
|
+
if (type === "rule")
|
|
42
|
+
return "Rules";
|
|
43
|
+
if (type === "command")
|
|
44
|
+
return "Commands";
|
|
45
|
+
return "Skills";
|
|
46
|
+
}
|
|
@@ -1,19 +1,54 @@
|
|
|
1
1
|
import { HimanError, errorCodes } from "../utils/errors.js";
|
|
2
2
|
import { getSupportedAgentNames, normalizeAgent } from "../utils/agent-configs.js";
|
|
3
|
+
import { listInstalledResourceGroups, writeInstalledResourceGroups, writeInstalledResources, } from "./installed-resource-list.js";
|
|
3
4
|
import { runAction } from "./shared.js";
|
|
4
|
-
export function registerProjectCommands(command, services) {
|
|
5
|
+
export function registerProjectCommands(command, services, options = {}) {
|
|
6
|
+
if (options.includeList !== false) {
|
|
7
|
+
command
|
|
8
|
+
.command("list")
|
|
9
|
+
.argument("[type]", "resource type")
|
|
10
|
+
.option("--agent <list>", "agent list filter, comma separated")
|
|
11
|
+
.option("--json", "output json format")
|
|
12
|
+
.description("List resources installed in current project")
|
|
13
|
+
.action(async (type, commandOptions) => {
|
|
14
|
+
await runAction(async () => {
|
|
15
|
+
const agents = parseAgents(commandOptions.agent);
|
|
16
|
+
if (!type) {
|
|
17
|
+
const groups = await listInstalledResourceGroups(services, process.cwd(), agents);
|
|
18
|
+
if (commandOptions.json) {
|
|
19
|
+
process.stdout.write(`${JSON.stringify(groups, null, 2)}\n`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
writeInstalledResourceGroups(groups);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const resourceType = ensureResourceType(type);
|
|
26
|
+
const resources = await services.listInstalled(process.cwd(), resourceType, agents);
|
|
27
|
+
if (commandOptions.json) {
|
|
28
|
+
process.stdout.write(`${JSON.stringify(resources, null, 2)}\n`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
writeInstalledResources(resources);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
5
35
|
command
|
|
6
36
|
.command("install")
|
|
7
37
|
.argument("[type]", "resource type")
|
|
8
38
|
.argument("[name[@version]]", "resource name with optional @version")
|
|
9
39
|
.option("--agent <list>", "install target agents, comma separated")
|
|
10
40
|
.option("--mode <mode>", "install mode: link or copy")
|
|
41
|
+
.option("--global", "install into user-level agent directories")
|
|
11
42
|
.description("Install resource, or install from himan.lock")
|
|
12
43
|
.action(async (type, nameVersion, options) => {
|
|
13
44
|
await runAction(async () => {
|
|
14
45
|
const agents = parseAgents(options.agent);
|
|
15
46
|
const mode = parseInstallMode(options.mode);
|
|
16
47
|
if (!type && !nameVersion) {
|
|
48
|
+
if (options.global) {
|
|
49
|
+
throw new HimanError(errorCodes.CLI_USAGE, "Global install requires a resource:\n"
|
|
50
|
+
+ " - himan install <type> <name[@version]> --global [--mode link|copy]");
|
|
51
|
+
}
|
|
17
52
|
const results = await services.installFromLock(process.cwd(), agents, mode);
|
|
18
53
|
if (results.length === 0) {
|
|
19
54
|
process.stdout.write("No resources in lock file.\n");
|
|
@@ -27,12 +62,15 @@ export function registerProjectCommands(command, services) {
|
|
|
27
62
|
if (!type || !nameVersion) {
|
|
28
63
|
throw new HimanError(errorCodes.CLI_USAGE, "Install usage:\n"
|
|
29
64
|
+ " - himan install # install from himan.lock\n"
|
|
30
|
-
+ " - himan install <type> <name[@version]> [--mode link|copy] # install single resource"
|
|
65
|
+
+ " - himan install <type> <name[@version]> [--mode link|copy] # install single resource\n"
|
|
66
|
+
+ " - himan install <type> <name[@version]> --global [--mode link|copy] # install single resource globally");
|
|
31
67
|
}
|
|
32
68
|
const resourceType = ensureResourceType(type);
|
|
33
69
|
const { name, version } = parseNameVersion(nameVersion);
|
|
34
|
-
const result =
|
|
35
|
-
|
|
70
|
+
const result = options.global
|
|
71
|
+
? await services.installGlobal(resourceType, name, version, process.cwd(), agents, mode)
|
|
72
|
+
: await services.install(resourceType, name, version, process.cwd(), agents, mode);
|
|
73
|
+
process.stdout.write(`Installed ${options.global ? "global " : ""}${result.type}/${result.name}@${result.version}\n`);
|
|
36
74
|
});
|
|
37
75
|
});
|
|
38
76
|
command
|
|
@@ -1,28 +1,41 @@
|
|
|
1
1
|
import { HimanError, errorCodes } from "../utils/errors.js";
|
|
2
2
|
import { getSupportedAgentNames, normalizeAgent } from "../utils/agent-configs.js";
|
|
3
|
+
import { listInstalledResourceGroups, writeInstalledResourceGroups, writeInstalledResources, } from "./installed-resource-list.js";
|
|
3
4
|
import { runAction } from "./shared.js";
|
|
5
|
+
const RESOURCE_TYPES = ["rule", "command", "skill"];
|
|
4
6
|
export function registerResourceCommands(command, services) {
|
|
5
7
|
command
|
|
6
8
|
.command("list")
|
|
7
|
-
.argument("[type]", "resource type"
|
|
9
|
+
.argument("[type]", "resource type")
|
|
8
10
|
.option("--agent <list>", "agent list filter, comma separated")
|
|
11
|
+
.option("--brief", "hide resource descriptions")
|
|
12
|
+
.option("--installed", "list resources installed in current project")
|
|
9
13
|
.option("--json", "output json format")
|
|
10
|
-
.description("List resources from current default source")
|
|
14
|
+
.description("List resources from current default source or project installs")
|
|
11
15
|
.action(async (type, options) => {
|
|
12
16
|
await runAction(async () => {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
if (options.
|
|
16
|
-
|
|
17
|
+
const agents = parseAgents(options.agent);
|
|
18
|
+
const showDescription = !options.brief;
|
|
19
|
+
if (options.installed) {
|
|
20
|
+
await writeInstalledList(services, type, agents, Boolean(options.json));
|
|
17
21
|
return;
|
|
18
22
|
}
|
|
19
|
-
if (
|
|
20
|
-
|
|
23
|
+
if (!type) {
|
|
24
|
+
const groups = await listGroupedResources(services, agents);
|
|
25
|
+
if (options.json) {
|
|
26
|
+
process.stdout.write(`${JSON.stringify(formatResourceGroups(groups, showDescription), null, 2)}\n`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
writeGroupedResources(groups, showDescription);
|
|
21
30
|
return;
|
|
22
31
|
}
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
const resourceType = ensureResourceType(type);
|
|
33
|
+
const resources = await services.list(resourceType, agents);
|
|
34
|
+
if (options.json) {
|
|
35
|
+
process.stdout.write(`${JSON.stringify(formatResources(resources, showDescription), null, 2)}\n`);
|
|
36
|
+
return;
|
|
25
37
|
}
|
|
38
|
+
writeResourceList(resources, showDescription);
|
|
26
39
|
});
|
|
27
40
|
});
|
|
28
41
|
command
|
|
@@ -79,12 +92,82 @@ export function registerResourceCommands(command, services) {
|
|
|
79
92
|
});
|
|
80
93
|
});
|
|
81
94
|
}
|
|
95
|
+
async function writeInstalledList(services, type, agents, json) {
|
|
96
|
+
if (!type) {
|
|
97
|
+
const groups = await listInstalledResourceGroups(services, process.cwd(), agents);
|
|
98
|
+
if (json) {
|
|
99
|
+
process.stdout.write(`${JSON.stringify(groups, null, 2)}\n`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
writeInstalledResourceGroups(groups);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const resourceType = ensureResourceType(type);
|
|
106
|
+
const resources = await services.listInstalled(process.cwd(), resourceType, agents);
|
|
107
|
+
if (json) {
|
|
108
|
+
process.stdout.write(`${JSON.stringify(resources, null, 2)}\n`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
writeInstalledResources(resources);
|
|
112
|
+
}
|
|
82
113
|
function ensureResourceType(type) {
|
|
83
114
|
if (type !== "rule" && type !== "command" && type !== "skill") {
|
|
84
115
|
throw new HimanError(errorCodes.UNSUPPORTED_RESOURCE_TYPE, `Unsupported resource type: ${type}`);
|
|
85
116
|
}
|
|
86
117
|
return type;
|
|
87
118
|
}
|
|
119
|
+
async function listGroupedResources(services, agents) {
|
|
120
|
+
return {
|
|
121
|
+
rule: await services.list("rule", agents),
|
|
122
|
+
command: await services.list("command", agents),
|
|
123
|
+
skill: await services.list("skill", agents),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function formatResourceGroups(groups, showDescription) {
|
|
127
|
+
return {
|
|
128
|
+
rule: formatResources(groups.rule, showDescription),
|
|
129
|
+
command: formatResources(groups.command, showDescription),
|
|
130
|
+
skill: formatResources(groups.skill, showDescription),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function formatResources(resources, showDescription) {
|
|
134
|
+
if (showDescription)
|
|
135
|
+
return resources;
|
|
136
|
+
return resources.map((resource) => {
|
|
137
|
+
const { description: _description, ...withoutDescription } = resource;
|
|
138
|
+
return withoutDescription;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function writeGroupedResources(groups, showDescription) {
|
|
142
|
+
const hasResources = RESOURCE_TYPES.some((type) => groups[type].length > 0);
|
|
143
|
+
if (!hasResources) {
|
|
144
|
+
process.stdout.write("No resources found.\n");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
for (const type of RESOURCE_TYPES) {
|
|
148
|
+
const resources = groups[type];
|
|
149
|
+
if (resources.length === 0)
|
|
150
|
+
continue;
|
|
151
|
+
process.stdout.write(`${formatGroupTitle(type)}:\n`);
|
|
152
|
+
writeResourceList(resources, showDescription);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function writeResourceList(resources, showDescription) {
|
|
156
|
+
if (resources.length === 0) {
|
|
157
|
+
process.stdout.write("No resources found.\n");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
for (const resource of resources) {
|
|
161
|
+
process.stdout.write(`- ${resource.type}/${resource.name}${showDescription && resource.description ? `: ${resource.description}` : ""}\n`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function formatGroupTitle(type) {
|
|
165
|
+
if (type === "rule")
|
|
166
|
+
return "Rules";
|
|
167
|
+
if (type === "command")
|
|
168
|
+
return "Commands";
|
|
169
|
+
return "Skills";
|
|
170
|
+
}
|
|
88
171
|
function parseAgents(input) {
|
|
89
172
|
if (!input)
|
|
90
173
|
return undefined;
|
package/dist/services/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ProjectLockStore, } from "../state/project-lock-store.js";
|
|
|
6
6
|
import { PathResolver } from "../utils/path-resolver.js";
|
|
7
7
|
import { toRepoId } from "../utils/repo-id.js";
|
|
8
8
|
import { HimanError, errorCodes } from "../utils/errors.js";
|
|
9
|
-
import { getProjectResourcePaths, getSupportedAgentNames, normalizeAgents, } from "../utils/agent-configs.js";
|
|
9
|
+
import { getGlobalResourcePaths, getProjectResourcePaths, getSupportedAgentNames, normalizeAgents, } from "../utils/agent-configs.js";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import { promises as fs } from "node:fs";
|
|
12
12
|
import { VersionResolver } from "../adapters/version/version-resolver.js";
|
|
@@ -157,6 +157,28 @@ export class ServiceFactory {
|
|
|
157
157
|
const selected = normalizeAgents(agents);
|
|
158
158
|
return resources.filter((resource) => normalizeAgents(resource.agents).some((agent) => selected.includes(agent)));
|
|
159
159
|
}
|
|
160
|
+
async listInstalled(projectDir, type, agents) {
|
|
161
|
+
const { lock, state } = await this.lockStore.loadWithState(projectDir);
|
|
162
|
+
if (state === "invalid") {
|
|
163
|
+
throw new HimanError(errorCodes.LOCK_INVALID, `Lock file is invalid: ${this.lockStore.getLockPath(projectDir)}`);
|
|
164
|
+
}
|
|
165
|
+
if (state === "missing" || !lock) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
const selectedAgents = agents?.length ? normalizeAgents(agents) : undefined;
|
|
169
|
+
return lock.resources
|
|
170
|
+
.filter((resource) => !type || resource.type === type)
|
|
171
|
+
.map((resource) => ({
|
|
172
|
+
type: resource.type,
|
|
173
|
+
name: resource.name,
|
|
174
|
+
version: resource.version,
|
|
175
|
+
agents: normalizeAgents(resource.agents),
|
|
176
|
+
mode: this.resolveInstallMode(resource.mode),
|
|
177
|
+
updatedAt: resource.updatedAt,
|
|
178
|
+
}))
|
|
179
|
+
.filter((resource) => !selectedAgents ||
|
|
180
|
+
resource.agents.some((agent) => selectedAgents.includes(agent)));
|
|
181
|
+
}
|
|
160
182
|
async history(type, name) {
|
|
161
183
|
const source = await this.loadSourceFromConfig();
|
|
162
184
|
return source.history(type, name);
|
|
@@ -165,6 +187,10 @@ export class ServiceFactory {
|
|
|
165
187
|
const { source, sourceInfo } = await this.loadSourceWithInfoFromConfig();
|
|
166
188
|
return this.installWithSource(source, sourceInfo, type, name, version, projectDir, agents, mode);
|
|
167
189
|
}
|
|
190
|
+
async installGlobal(type, name, version, projectDir, agents, mode = "link") {
|
|
191
|
+
const source = await this.loadSourceFromConfig();
|
|
192
|
+
return this.installWithSource(source, undefined, type, name, version, projectDir, agents, mode, "global");
|
|
193
|
+
}
|
|
168
194
|
async dev(type, name, projectDir) {
|
|
169
195
|
const installInfo = await this.resolveInstalledResource(projectDir, type, name);
|
|
170
196
|
const installedPath = installInfo.installedPath;
|
|
@@ -263,12 +289,12 @@ export class ServiceFactory {
|
|
|
263
289
|
const lockSourceInfo = this.normalizeLockSourceInfo(lock.source);
|
|
264
290
|
const lockedSource = await this.loadSourceFromLock(lockSourceInfo);
|
|
265
291
|
for (const item of lock.resources) {
|
|
266
|
-
const result = await this.installWithSource(lockedSource, lockSourceInfo, item.type, item.name, item.version, projectDir, agents ?? item.agents, mode ?? this.resolveInstallMode(item.mode));
|
|
292
|
+
const result = await this.installWithSource(lockedSource, lockSourceInfo, item.type, item.name, item.version, projectDir, agents ?? item.agents, mode ?? this.resolveInstallMode(item.mode), "project");
|
|
267
293
|
results.push(result);
|
|
268
294
|
}
|
|
269
295
|
return results;
|
|
270
296
|
}
|
|
271
|
-
async installWithSource(source, sourceInfo, type, name, version, projectDir, agents, mode) {
|
|
297
|
+
async installWithSource(source, sourceInfo, type, name, version, projectDir, agents, mode, scope = "project") {
|
|
272
298
|
const history = await source.history(type, name);
|
|
273
299
|
if (history.length === 0) {
|
|
274
300
|
throw new HimanError(errorCodes.RESOURCE_NOT_FOUND, `Resource not found: ${type}/${name}`);
|
|
@@ -279,18 +305,27 @@ export class ServiceFactory {
|
|
|
279
305
|
await source.pull(type, name, resolvedVersion, storePath);
|
|
280
306
|
}
|
|
281
307
|
const resourceMeta = await this.readResourceMetaFromDir(storePath, type);
|
|
282
|
-
const effectiveTargets =
|
|
283
|
-
|
|
308
|
+
const effectiveTargets = scope === "global"
|
|
309
|
+
? await this.resolveGlobalInstallAgents(projectDir, type, name, agents, resourceMeta?.agents)
|
|
310
|
+
: await this.resolveEffectiveAgents(projectDir, agents, resourceMeta?.agents);
|
|
311
|
+
const linkPaths = scope === "global"
|
|
312
|
+
? getGlobalResourcePaths(this.paths.getHomeDir(), type, name, effectiveTargets)
|
|
313
|
+
: getProjectResourcePaths(projectDir, type, name, effectiveTargets);
|
|
284
314
|
for (const linkPath of linkPaths) {
|
|
285
315
|
await this.materializeResource(storePath, linkPath, mode);
|
|
286
316
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
317
|
+
if (scope === "project") {
|
|
318
|
+
if (!sourceInfo) {
|
|
319
|
+
throw new Error("Project install requires source lock information.");
|
|
320
|
+
}
|
|
321
|
+
await this.lockStore.upsertResource(projectDir, sourceInfo, {
|
|
322
|
+
type,
|
|
323
|
+
name,
|
|
324
|
+
version: resolvedVersion,
|
|
325
|
+
agents: effectiveTargets,
|
|
326
|
+
mode,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
294
329
|
return { type, name, version: resolvedVersion, linkPath: linkPaths[0], mode };
|
|
295
330
|
}
|
|
296
331
|
async loadSourceFromConfig() {
|
|
@@ -467,6 +502,16 @@ export class ServiceFactory {
|
|
|
467
502
|
}
|
|
468
503
|
return normalizeAgents(fallbackAgents);
|
|
469
504
|
}
|
|
505
|
+
async resolveGlobalInstallAgents(projectDir, type, name, explicitAgents, fallbackAgents) {
|
|
506
|
+
if (explicitAgents?.length) {
|
|
507
|
+
return normalizeAgents(explicitAgents);
|
|
508
|
+
}
|
|
509
|
+
const locked = await this.getLockedResource(projectDir, type, name);
|
|
510
|
+
if (locked?.agents?.length) {
|
|
511
|
+
return normalizeAgents(locked.agents);
|
|
512
|
+
}
|
|
513
|
+
return this.resolveEffectiveAgents(projectDir, undefined, fallbackAgents);
|
|
514
|
+
}
|
|
470
515
|
async getConfiguredAgents(projectDir) {
|
|
471
516
|
const [globalConfig, projectConfig] = await Promise.all([
|
|
472
517
|
this.stateStore.loadConfig(),
|
|
@@ -46,8 +46,14 @@ export function normalizeAgent(input) {
|
|
|
46
46
|
return AGENT_ALIASES.get(input.trim().toLowerCase());
|
|
47
47
|
}
|
|
48
48
|
export function getProjectResourcePaths(projectDir, type, name, agents) {
|
|
49
|
+
return getResourcePaths(projectDir, type, name, agents);
|
|
50
|
+
}
|
|
51
|
+
export function getGlobalResourcePaths(homeDir, type, name, agents) {
|
|
52
|
+
return getResourcePaths(homeDir, type, name, agents);
|
|
53
|
+
}
|
|
54
|
+
function getResourcePaths(rootDir, type, name, agents) {
|
|
49
55
|
const typeDir = getTypeDir(type);
|
|
50
|
-
return normalizeAgents(agents).map((agent) => path.join(
|
|
56
|
+
return normalizeAgents(agents).map((agent) => path.join(rootDir, getAgentBaseDir(agent), typeDir, name));
|
|
51
57
|
}
|
|
52
58
|
export function getSupportedAgentNames() {
|
|
53
59
|
return AGENT_CONFIGS.map((config) => config.name);
|