@hi-man/himan 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,20 +1,25 @@
1
1
  import { GitSourceAdapter } from "../adapters/source/git-source-adapter.js";
2
2
  import { RegistrySourceAdapter } from "../adapters/source/registry-source-adapter.js";
3
3
  import { StateStore } from "../state/state-store.js";
4
+ import { ProjectConfigStore } from "../state/project-config-store.js";
4
5
  import { ProjectLockStore } from "../state/project-lock-store.js";
5
6
  import { PathResolver } from "../utils/path-resolver.js";
6
7
  import { toRepoId } from "../utils/repo-id.js";
7
8
  import { HimanError, errorCodes } from "../utils/errors.js";
9
+ import { getProjectResourcePaths, getSupportedAgentNames, normalizeAgents, } from "../utils/agent-configs.js";
8
10
  import path from "node:path";
9
11
  import { promises as fs } from "node:fs";
10
12
  import { VersionResolver } from "../adapters/version/version-resolver.js";
13
+ import YAML from "yaml";
11
14
  export class ServiceFactory {
12
15
  stateStore = new StateStore();
16
+ projectConfigStore = new ProjectConfigStore();
13
17
  lockStore = new ProjectLockStore();
14
18
  paths = new PathResolver();
15
19
  versions = new VersionResolver();
16
20
  async initSource(type, repo) {
17
21
  await this.stateStore.ensureBaseDirs();
22
+ const current = await this.stateStore.loadConfig();
18
23
  const sourceConfig = this.buildSourceConfig(type, repo);
19
24
  const source = this.createSource(type);
20
25
  await source.init(sourceConfig);
@@ -29,6 +34,7 @@ export class ServiceFactory {
29
34
  default: "default",
30
35
  items: { default: stateSource },
31
36
  },
37
+ agents: current?.agents,
32
38
  });
33
39
  return {
34
40
  sourceType: type,
@@ -60,6 +66,7 @@ export class ServiceFactory {
60
66
  default: defaultName,
61
67
  items,
62
68
  },
69
+ agents: current?.agents,
63
70
  });
64
71
  return { name, type, repo: sourceConfig.repo, repoId: sourceConfig.repoId };
65
72
  }
@@ -78,6 +85,7 @@ export class ServiceFactory {
78
85
  default: name,
79
86
  items: config.sources.items,
80
87
  },
88
+ agents: config.agents,
81
89
  });
82
90
  return { name };
83
91
  }
@@ -93,15 +101,63 @@ export class ServiceFactory {
93
101
  isDefault: name === config.sources?.default,
94
102
  }));
95
103
  }
96
- async list(type) {
104
+ async setAgents(agents, scope, projectDir) {
105
+ const normalized = normalizeAgents(agents);
106
+ if (scope === "project") {
107
+ await this.projectConfigStore.saveAgents(projectDir, normalized);
108
+ return { scope, agents: normalized };
109
+ }
110
+ await this.stateStore.ensureBaseDirs();
111
+ const current = await this.stateStore.loadConfig();
112
+ await this.stateStore.saveConfig({
113
+ ...(current ?? {}),
114
+ agents: normalized,
115
+ });
116
+ return { scope, agents: normalized };
117
+ }
118
+ async getAgentSettings(projectDir) {
119
+ const [globalConfig, projectConfig] = await Promise.all([
120
+ this.stateStore.loadConfig(),
121
+ this.projectConfigStore.load(projectDir),
122
+ ]);
123
+ const global = globalConfig?.agents?.length
124
+ ? normalizeAgents(globalConfig.agents)
125
+ : undefined;
126
+ const project = projectConfig?.agents?.length
127
+ ? normalizeAgents(projectConfig.agents)
128
+ : undefined;
129
+ return {
130
+ global,
131
+ project,
132
+ effective: project ?? global ?? normalizeAgents(),
133
+ supported: getSupportedAgentNames(),
134
+ };
135
+ }
136
+ async clearAgents(scope, projectDir) {
137
+ if (scope === "project") {
138
+ await this.projectConfigStore.clearAgents(projectDir);
139
+ return { scope };
140
+ }
141
+ const current = await this.stateStore.loadConfig();
142
+ await this.stateStore.saveConfig({
143
+ ...(current ?? {}),
144
+ agents: undefined,
145
+ });
146
+ return { scope };
147
+ }
148
+ async list(type, agents) {
97
149
  const source = await this.loadSourceFromConfig();
98
- return source.list(type);
150
+ const resources = await source.list(type);
151
+ if (!agents?.length)
152
+ return resources;
153
+ const selected = normalizeAgents(agents);
154
+ return resources.filter((resource) => normalizeAgents(resource.agents).some((agent) => selected.includes(agent)));
99
155
  }
100
156
  async history(type, name) {
101
157
  const source = await this.loadSourceFromConfig();
102
158
  return source.history(type, name);
103
159
  }
104
- async install(type, name, version, projectDir) {
160
+ async install(type, name, version, projectDir, agents, mode = "link") {
105
161
  const source = await this.loadSourceFromConfig();
106
162
  const sourceInfo = await this.getLockSourceInfo();
107
163
  const history = await source.history(type, name);
@@ -110,37 +166,53 @@ export class ServiceFactory {
110
166
  }
111
167
  const resolvedVersion = this.resolveVersion(history, version);
112
168
  const storePath = this.getStorePath(type, name, resolvedVersion);
113
- const linkPath = this.getProjectResourcePath(projectDir, type, name);
114
169
  if (!(await this.exists(storePath))) {
115
170
  await source.pull(type, name, resolvedVersion, storePath);
116
171
  }
117
- await this.switchSymlink(storePath, linkPath);
172
+ const resourceMeta = await this.readResourceMetaFromDir(storePath);
173
+ const effectiveTargets = await this.resolveEffectiveAgents(projectDir, agents, resourceMeta?.agents);
174
+ const linkPaths = getProjectResourcePaths(projectDir, type, name, effectiveTargets);
175
+ for (const linkPath of linkPaths) {
176
+ await this.materializeResource(storePath, linkPath, mode);
177
+ }
118
178
  await this.lockStore.upsertResource(projectDir, sourceInfo, {
119
179
  type,
120
180
  name,
121
181
  version: resolvedVersion,
182
+ agents: effectiveTargets,
183
+ mode,
122
184
  });
123
- return { type, name, version: resolvedVersion, linkPath };
185
+ return { type, name, version: resolvedVersion, linkPath: linkPaths[0], mode };
124
186
  }
125
187
  async dev(type, name, projectDir) {
126
- const linkPath = this.getProjectResourcePath(projectDir, type, name);
127
- const installedPath = await this.readInstalledPath(linkPath);
188
+ const installInfo = await this.resolveInstalledResource(projectDir, type, name);
189
+ const installedPath = installInfo.installedPath;
128
190
  const devPath = this.getProjectDevPath(projectDir, type, name);
129
191
  if (!(await this.exists(devPath))) {
130
192
  await fs.mkdir(path.dirname(devPath), { recursive: true });
131
193
  await fs.cp(installedPath, devPath, { recursive: true });
132
194
  }
133
- await this.switchSymlink(devPath, linkPath);
134
- return { type, name, devPath, linkPath };
195
+ for (const linkPath of installInfo.linkPaths) {
196
+ await this.materializeResource(devPath, linkPath, installInfo.mode);
197
+ }
198
+ return {
199
+ type,
200
+ name,
201
+ devPath,
202
+ linkPath: installInfo.linkPaths[0],
203
+ mode: installInfo.mode,
204
+ };
135
205
  }
136
206
  async uninstall(type, name, projectDir) {
137
- const linkPath = this.getProjectResourcePath(projectDir, type, name);
138
- if (!(await this.exists(linkPath))) {
139
- throw new HimanError(errorCodes.INSTALL_NOT_FOUND, `Installed resource link not found: ${linkPath}.`);
207
+ const installInfo = await this.resolveInstalledResource(projectDir, type, name);
208
+ if (installInfo.linkPaths.length === 0) {
209
+ throw new HimanError(errorCodes.INSTALL_NOT_FOUND, `Installed resource link not found for ${type}/${name}.`);
210
+ }
211
+ for (const linkPath of installInfo.linkPaths) {
212
+ await fs.rm(linkPath, { recursive: true, force: true });
140
213
  }
141
- await fs.rm(linkPath, { recursive: true, force: true });
142
214
  await this.lockStore.removeResource(projectDir, { type, name });
143
- return { type, name, linkPath };
215
+ return { type, name, linkPath: installInfo.linkPaths[0] };
144
216
  }
145
217
  async publish(type, name, releaseType, projectDir) {
146
218
  const source = await this.loadSourceFromConfig();
@@ -155,33 +227,43 @@ export class ServiceFactory {
155
227
  if (!(await this.exists(storePath))) {
156
228
  await source.pull(type, name, nextVersion, storePath);
157
229
  }
158
- const linkPath = this.getProjectResourcePath(projectDir, type, name);
159
- if (await this.exists(linkPath)) {
160
- await this.switchSymlink(storePath, linkPath);
230
+ const agentsFromMeta = normalizeAgents((await this.readResourceMetaFromDir(storePath))?.agents);
231
+ const locked = await this.getLockedResource(projectDir, type, name);
232
+ const nextAgents = locked?.agents?.length
233
+ ? normalizeAgents(locked.agents)
234
+ : agentsFromMeta;
235
+ const installMode = this.resolveInstallMode(locked?.mode);
236
+ const linkPaths = getProjectResourcePaths(projectDir, type, name, nextAgents);
237
+ for (const linkPath of linkPaths) {
238
+ if (await this.exists(linkPath)) {
239
+ await this.materializeResource(storePath, linkPath, installMode);
240
+ }
161
241
  }
162
- if (await this.isResourceLocked(projectDir, type, name)) {
242
+ if (locked) {
163
243
  const sourceInfo = await this.getLockSourceInfo();
164
244
  await this.lockStore.upsertResource(projectDir, sourceInfo, {
165
245
  type,
166
246
  name,
167
247
  version: nextVersion,
248
+ agents: nextAgents,
249
+ mode: installMode,
168
250
  });
169
251
  }
170
252
  return { type, name, version: result.version, tag: result.tag };
171
253
  }
172
- async create(type, name, options) {
254
+ async create(type, name, options, projectDir) {
173
255
  this.validateCreateInput(type, name, options);
174
256
  const source = await this.loadSourceFromConfig();
175
257
  return source.create(type, name, {
176
258
  description: options.description,
177
- targets: options.targets,
259
+ agents: await this.resolveEffectiveAgents(projectDir, options.agents),
178
260
  entry: options.entry,
179
261
  template: options.template ?? "basic",
180
262
  force: options.force,
181
263
  dryRun: options.dryRun,
182
264
  });
183
265
  }
184
- async installFromLock(projectDir) {
266
+ async installFromLock(projectDir, agents, mode) {
185
267
  const { lock, state } = await this.lockStore.loadWithState(projectDir);
186
268
  if (state === "missing") {
187
269
  throw new HimanError(errorCodes.LOCK_NOT_FOUND, `Lock file not found: ${this.lockStore.getLockPath(projectDir)}`);
@@ -194,14 +276,14 @@ export class ServiceFactory {
194
276
  }
195
277
  const results = [];
196
278
  for (const item of lock.resources) {
197
- const result = await this.install(item.type, item.name, item.version, projectDir);
279
+ const result = await this.install(item.type, item.name, item.version, projectDir, agents ?? item.agents, mode ?? this.resolveInstallMode(item.mode));
198
280
  results.push(result);
199
281
  }
200
282
  return results;
201
283
  }
202
284
  async loadSourceFromConfig() {
203
285
  const config = await this.stateStore.loadConfig();
204
- if (!config) {
286
+ if (!config?.source) {
205
287
  throw new HimanError(errorCodes.CONFIG_NOT_FOUND, "Source config not found. Please run `himan init <git_repo>` first.");
206
288
  }
207
289
  const sourceConfig = this.buildSourceConfig(config.source.type, config.source.repo, config.source.repoId);
@@ -216,7 +298,7 @@ export class ServiceFactory {
216
298
  }
217
299
  async getLockSourceInfo() {
218
300
  const config = await this.stateStore.loadConfig();
219
- if (!config) {
301
+ if (!config?.source) {
220
302
  throw new HimanError(errorCodes.CONFIG_NOT_FOUND, "Source config not found. Please run `himan init <git_repo>` first.");
221
303
  }
222
304
  return {
@@ -225,11 +307,11 @@ export class ServiceFactory {
225
307
  repoId: config.source.repoId,
226
308
  };
227
309
  }
228
- async isResourceLocked(projectDir, type, name) {
310
+ async getLockedResource(projectDir, type, name) {
229
311
  const lock = await this.lockStore.load(projectDir);
230
312
  if (!lock)
231
- return false;
232
- return lock.resources.some((item) => item.type === type && item.name === name);
313
+ return undefined;
314
+ return lock.resources.find((item) => item.type === type && item.name === name);
233
315
  }
234
316
  buildSourceConfig(type, repo, repoId) {
235
317
  if (type === "registry") {
@@ -258,26 +340,100 @@ export class ServiceFactory {
258
340
  getStorePath(type, name, version) {
259
341
  return path.join(this.paths.getStoreDir(), type, name, version);
260
342
  }
261
- getProjectResourcePath(projectDir, type, name) {
262
- if (type === "rule")
263
- return path.join(projectDir, ".cursor", "rules", name);
264
- if (type === "command")
265
- return path.join(projectDir, ".cursor", "commands", name);
266
- return path.join(projectDir, ".cursor", "skills", name);
267
- }
268
343
  getProjectDevPath(projectDir, type, name) {
269
344
  return path.join(projectDir, ".himan", "dev", type, name);
270
345
  }
271
- async switchSymlink(targetPath, linkPath) {
272
- await fs.mkdir(path.dirname(linkPath), { recursive: true });
273
- await fs.rm(linkPath, { recursive: true, force: true });
274
- await fs.symlink(targetPath, linkPath, "dir");
346
+ async materializeResource(sourcePath, targetPath, mode) {
347
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
348
+ await fs.rm(targetPath, { recursive: true, force: true });
349
+ if (mode === "copy") {
350
+ await fs.cp(sourcePath, targetPath, { recursive: true });
351
+ return;
352
+ }
353
+ await fs.symlink(sourcePath, targetPath, "dir");
354
+ }
355
+ resolveInstallMode(mode) {
356
+ return mode === "copy" ? "copy" : "link";
357
+ }
358
+ async resolveInstalledResource(projectDir, type, name) {
359
+ const locked = await this.getLockedResource(projectDir, type, name);
360
+ const configuredAgents = await this.getConfiguredAgents(projectDir);
361
+ const lockedTargets = locked?.agents?.length
362
+ ? normalizeAgents(locked.agents)
363
+ : configuredAgents ?? normalizeAgents();
364
+ const expectedFromLock = getProjectResourcePaths(projectDir, type, name, lockedTargets);
365
+ const existingFromLock = [];
366
+ for (const candidate of expectedFromLock) {
367
+ if (await this.exists(candidate))
368
+ existingFromLock.push(candidate);
369
+ }
370
+ if (existingFromLock.length > 0) {
371
+ const installedPath = await fs.realpath(existingFromLock[0]);
372
+ return {
373
+ installedPath,
374
+ agents: lockedTargets,
375
+ linkPaths: getProjectResourcePaths(projectDir, type, name, lockedTargets),
376
+ mode: this.resolveInstallMode(locked?.mode),
377
+ };
378
+ }
379
+ const allCandidates = getSupportedAgentNames().map((agent) => ({
380
+ agent,
381
+ path: getProjectResourcePaths(projectDir, type, name, [agent])[0],
382
+ }));
383
+ const existingCandidates = [];
384
+ for (const candidate of allCandidates) {
385
+ if (await this.exists(candidate.path))
386
+ existingCandidates.push(candidate);
387
+ }
388
+ if (existingCandidates.length === 0) {
389
+ throw new HimanError(errorCodes.INSTALL_NOT_FOUND, `Installed resource link not found for ${type}/${name}. Run install first.`);
390
+ }
391
+ const installedPath = await fs.realpath(existingCandidates[0].path);
392
+ const resourceMeta = await this.readResourceMetaFromDir(installedPath);
393
+ const agentsFromMeta = resourceMeta?.agents?.length
394
+ ? normalizeAgents(resourceMeta.agents)
395
+ : undefined;
396
+ const existingAgents = normalizeAgents(existingCandidates.map((candidate) => candidate.agent));
397
+ const effectiveAgents = configuredAgents ?? agentsFromMeta ?? existingAgents;
398
+ return {
399
+ installedPath,
400
+ agents: effectiveAgents,
401
+ linkPaths: getProjectResourcePaths(projectDir, type, name, effectiveAgents),
402
+ mode: "link",
403
+ };
404
+ }
405
+ async resolveEffectiveAgents(projectDir, explicitAgents, fallbackAgents) {
406
+ if (explicitAgents?.length) {
407
+ return normalizeAgents(explicitAgents);
408
+ }
409
+ const configuredAgents = await this.getConfiguredAgents(projectDir);
410
+ if (configuredAgents?.length) {
411
+ return configuredAgents;
412
+ }
413
+ return normalizeAgents(fallbackAgents);
275
414
  }
276
- async readInstalledPath(linkPath) {
277
- if (!(await this.exists(linkPath))) {
278
- throw new HimanError(errorCodes.INSTALL_NOT_FOUND, `Installed resource link not found: ${linkPath}. Run install first.`);
415
+ async getConfiguredAgents(projectDir) {
416
+ const [globalConfig, projectConfig] = await Promise.all([
417
+ this.stateStore.loadConfig(),
418
+ this.projectConfigStore.load(projectDir),
419
+ ]);
420
+ if (projectConfig?.agents?.length) {
421
+ return normalizeAgents(projectConfig.agents);
279
422
  }
280
- return fs.realpath(linkPath);
423
+ if (globalConfig?.agents?.length) {
424
+ return normalizeAgents(globalConfig.agents);
425
+ }
426
+ return undefined;
427
+ }
428
+ async readResourceMetaFromDir(resourceDir) {
429
+ const yamlPath = path.join(resourceDir, "himan.yaml");
430
+ if (!(await this.exists(yamlPath)))
431
+ return null;
432
+ const raw = await fs.readFile(yamlPath, "utf8");
433
+ const parsed = YAML.parse(raw) ?? null;
434
+ if (!parsed)
435
+ return null;
436
+ return { agents: parsed.agents ?? parsed.targets };
281
437
  }
282
438
  async exists(targetPath) {
283
439
  try {
@@ -301,7 +457,7 @@ export class ServiceFactory {
301
457
  }
302
458
  async getRepoResourceDir(type, name) {
303
459
  const config = await this.stateStore.loadConfig();
304
- if (!config) {
460
+ if (!config?.source) {
305
461
  throw new HimanError(errorCodes.CONFIG_NOT_FOUND, "Source config not found. Please run `himan init <git_repo>` first.");
306
462
  }
307
463
  const sourceConfig = this.buildSourceConfig(config.source.type, config.source.repo, config.source.repoId);
@@ -0,0 +1,43 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ export class ProjectConfigStore {
4
+ getConfigPath(projectDir) {
5
+ return path.join(projectDir, ".himan", "config.json");
6
+ }
7
+ async load(projectDir) {
8
+ try {
9
+ const raw = await fs.readFile(this.getConfigPath(projectDir), "utf8");
10
+ const parsed = JSON.parse(raw);
11
+ if (parsed.version !== 1)
12
+ return null;
13
+ return parsed;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ async saveAgents(projectDir, agents) {
20
+ const now = new Date().toISOString();
21
+ const existing = await this.load(projectDir);
22
+ const config = {
23
+ version: 1,
24
+ ...existing,
25
+ agents,
26
+ updatedAt: now,
27
+ };
28
+ await fs.mkdir(path.dirname(this.getConfigPath(projectDir)), { recursive: true });
29
+ await fs.writeFile(this.getConfigPath(projectDir), JSON.stringify(config, null, 2), "utf8");
30
+ return config;
31
+ }
32
+ async clearAgents(projectDir) {
33
+ const existing = await this.load(projectDir);
34
+ if (!existing)
35
+ return;
36
+ const config = {
37
+ ...existing,
38
+ agents: undefined,
39
+ updatedAt: new Date().toISOString(),
40
+ };
41
+ await fs.writeFile(this.getConfigPath(projectDir), JSON.stringify(config, null, 2), "utf8");
42
+ }
43
+ }
@@ -40,6 +40,8 @@ export class ProjectLockStore {
40
40
  const found = lock.resources.find((item) => item.type === resource.type && item.name === resource.name);
41
41
  if (found) {
42
42
  found.version = resource.version;
43
+ found.agents = resource.agents;
44
+ found.mode = resource.mode;
43
45
  found.updatedAt = now;
44
46
  }
45
47
  else {
@@ -47,6 +49,8 @@ export class ProjectLockStore {
47
49
  type: resource.type,
48
50
  name: resource.name,
49
51
  version: resource.version,
52
+ agents: resource.agents,
53
+ mode: resource.mode,
50
54
  updatedAt: now,
51
55
  });
52
56
  }
@@ -35,12 +35,15 @@ export class StateStore {
35
35
  default: defaultName,
36
36
  items: input.sources.items,
37
37
  },
38
+ agents: input.agents,
38
39
  };
39
40
  }
40
41
  }
41
42
  const fallback = input.source;
42
43
  if (!fallback) {
43
- throw new Error("Invalid config: source is required.");
44
+ return {
45
+ agents: input.agents,
46
+ };
44
47
  }
45
48
  return {
46
49
  source: fallback,
@@ -50,6 +53,7 @@ export class StateStore {
50
53
  default: fallback,
51
54
  },
52
55
  },
56
+ agents: input.agents,
53
57
  };
54
58
  }
55
59
  }
@@ -0,0 +1,71 @@
1
+ import path from "node:path";
2
+ const AGENT_CONFIGS = [
3
+ {
4
+ name: "cursor",
5
+ aliases: ["cursor"],
6
+ baseDir: ".cursor",
7
+ },
8
+ {
9
+ name: "claude-code",
10
+ aliases: ["claude", "claude-code", "claude code", "claude_code"],
11
+ baseDir: ".claude",
12
+ },
13
+ {
14
+ name: "codex",
15
+ aliases: ["codex"],
16
+ baseDir: ".agents",
17
+ },
18
+ {
19
+ name: "openclaw",
20
+ aliases: ["openclaw", "open-claw", "open claw"],
21
+ baseDir: ".openclaw",
22
+ },
23
+ ];
24
+ const DEFAULT_AGENT = "cursor";
25
+ const AGENT_ALIASES = buildAgentAliases();
26
+ function getTypeDir(type) {
27
+ if (type === "rule")
28
+ return "rules";
29
+ if (type === "command")
30
+ return "commands";
31
+ return "skills";
32
+ }
33
+ function getAgentBaseDir(agent) {
34
+ return getAgentConfig(agent).baseDir;
35
+ }
36
+ export function normalizeAgents(agents) {
37
+ const normalized = (agents ?? [])
38
+ .map((item) => normalizeAgent(item))
39
+ .filter((item) => Boolean(item));
40
+ if (normalized.length === 0) {
41
+ return [DEFAULT_AGENT];
42
+ }
43
+ return [...new Set(normalized)];
44
+ }
45
+ export function normalizeAgent(input) {
46
+ return AGENT_ALIASES.get(input.trim().toLowerCase());
47
+ }
48
+ export function getProjectResourcePaths(projectDir, type, name, agents) {
49
+ const typeDir = getTypeDir(type);
50
+ return normalizeAgents(agents).map((agent) => path.join(projectDir, getAgentBaseDir(agent), typeDir, name));
51
+ }
52
+ export function getSupportedAgentNames() {
53
+ return AGENT_CONFIGS.map((config) => config.name);
54
+ }
55
+ function getAgentConfig(agent) {
56
+ const config = AGENT_CONFIGS.find((item) => item.name === agent);
57
+ if (!config) {
58
+ throw new Error(`Unsupported agent config: ${agent}`);
59
+ }
60
+ return config;
61
+ }
62
+ function buildAgentAliases() {
63
+ const aliases = new Map();
64
+ for (const config of AGENT_CONFIGS) {
65
+ aliases.set(config.name, config.name);
66
+ for (const alias of config.aliases) {
67
+ aliases.set(alias, config.name);
68
+ }
69
+ }
70
+ return aliases;
71
+ }
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  const moduleDir = dirname(fileURLToPath(import.meta.url));
5
- const pkgPath = join(moduleDir, "../package.json");
5
+ const pkgPath = join(moduleDir, "../../package.json");
6
6
  const parsed = JSON.parse(readFileSync(pkgPath, "utf8"));
7
7
  if (typeof parsed.version !== "string" || parsed.version.length === 0) {
8
8
  throw new Error("Invalid or missing version in package.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hi-man/himan",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Prompt and agent asset management CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,11 +23,14 @@
23
23
  "registry": "https://registry.npmjs.org/"
24
24
  },
25
25
  "bin": {
26
- "himan": "dist/index.js"
26
+ "himan": "dist/bin/himan.js",
27
+ "himan-source": "dist/bin/himan-source.js",
28
+ "himan-resource": "dist/bin/himan-resource.js",
29
+ "himan-project": "dist/bin/himan-project.js"
27
30
  },
28
31
  "scripts": {
29
32
  "build": "tsc -p tsconfig.json",
30
- "dev": "tsx src/index.ts",
33
+ "dev": "tsx src/bin/himan.ts",
31
34
  "typecheck": "tsc -p tsconfig.json --noEmit",
32
35
  "test": "vitest run",
33
36
  "test:watch": "vitest",