hirmos 1.3.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.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # HIRMOS CLI
2
+
3
+ Product-facing HIRMOS CLI.
4
+
5
+ Implemented command:
6
+
7
+ ```bash
8
+ hirmos init [project-path] [--integration <ids>] [--source <path>]
9
+ ```
10
+
11
+ The CLI makes an installed HIRMOS project discoverable to selected agent-native tools by generating thin bootstrap integration files from `_hirmos/integrations/agent-tools/`.
12
+
13
+ It does not invoke agents, run HIRMOS workflows, install extensions, download remote packages, or generate slash-command packs.
14
+
15
+ ## Usage
16
+
17
+ From a project that already contains `_hirmos/`:
18
+
19
+ ```bash
20
+ hirmos init
21
+ ```
22
+
23
+ This defaults to:
24
+
25
+ ```text
26
+ project-path = .
27
+ integration = agents
28
+ source = existing _hirmos/ in the project
29
+ ```
30
+
31
+ Generate specific integrations:
32
+
33
+ ```bash
34
+ hirmos init --integration claude,cursor,copilot
35
+ ```
36
+
37
+ Initialize another project path:
38
+
39
+ ```bash
40
+ hirmos init /path/to/project --integration agents,claude
41
+ ```
42
+
43
+ Install `_hirmos/` from a local source folder or release zip when the target project does not already contain `_hirmos/`:
44
+
45
+ ```bash
46
+ hirmos init /path/to/project --source /path/to/hirmos-framework.zip --integration agents
47
+ ```
48
+
49
+ ## Supported integrations
50
+
51
+ ```text
52
+ agents
53
+ claude
54
+ cursor
55
+ copilot
56
+ codex
57
+ opencode
58
+ gemini
59
+ windsurf
60
+ kiro
61
+ ```
62
+
63
+ Some integrations share the same target file. For example, `agents`, `codex`, and `opencode` all use `AGENTS.md` as the safe generic bootstrap target.
64
+
65
+ ## Managed-block behavior
66
+
67
+ For existing files, the CLI updates only the HIRMOS managed block:
68
+
69
+ ```md
70
+ <!-- HIRMOS:START -->
71
+ ...
72
+ <!-- HIRMOS:END -->
73
+ ```
74
+
75
+ If the file exists and has no HIRMOS block, the CLI appends one. User-owned content outside the block is preserved.
76
+
77
+ ## Project config behavior
78
+
79
+ The CLI merges selected integration IDs into:
80
+
81
+ ```text
82
+ _hirmos/project.json
83
+ ```
84
+
85
+ under:
86
+
87
+ ```json
88
+ {
89
+ "integrations": {
90
+ "installed": []
91
+ }
92
+ }
93
+ ```
94
+
95
+ Re-running `hirmos init --integration ...` is additive and does not remove previously recorded integrations.
96
+
97
+ ## Workflow commands are not CLI commands
98
+
99
+ These are HIRMOS workflow commands to use inside the AI tool after HIRMOS Core has loaded:
100
+
101
+ ```text
102
+ hirmos requirements
103
+ hirmos system-design
104
+ hirmos implementation
105
+ ```
106
+
107
+ The CLI currently implements only `hirmos init`.
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runInit = runInit;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const filesystem_1 = require("../lib/filesystem");
10
+ const integration_registry_1 = require("../lib/integration-registry");
11
+ const managed_blocks_1 = require("../lib/managed-blocks");
12
+ const project_config_1 = require("../lib/project-config");
13
+ const source_install_1 = require("../lib/source-install");
14
+ const validation_1 = require("../lib/validation");
15
+ function runInit(options) {
16
+ const projectRoot = path_1.default.resolve(options.projectPath);
17
+ (0, validation_1.validateProjectRoot)(projectRoot);
18
+ (0, source_install_1.ensureHirmosInstalled)(projectRoot, options.source);
19
+ (0, validation_1.validateHirmosFramework)(projectRoot);
20
+ const hirmosDir = path_1.default.join(projectRoot, "_hirmos");
21
+ const registry = (0, integration_registry_1.loadRegistry)(hirmosDir);
22
+ const selected = (0, integration_registry_1.normalizeSelectedIntegrations)(options.integrations, registry);
23
+ const plannedTargets = (0, integration_registry_1.planTargetUpdates)(projectRoot, hirmosDir, selected, registry);
24
+ const currentConfig = (0, project_config_1.loadProjectConfig)(projectRoot);
25
+ const mergeResult = (0, project_config_1.mergeProjectConfig)(currentConfig, selected, registry);
26
+ const renderedWrites = plannedTargets.map((target) => {
27
+ (0, filesystem_1.assertInsideProject)(projectRoot, target.targetPath);
28
+ const existing = fs_1.default.existsSync(target.targetPath) ? (0, filesystem_1.readText)(target.targetPath) : null;
29
+ const nextContent = (0, managed_blocks_1.updateManagedBlock)(existing, target.templateContent, registry.managed_block, target.target);
30
+ return {
31
+ path: target.targetPath,
32
+ relative: target.target,
33
+ content: nextContent,
34
+ integrationIds: target.integrationIds
35
+ };
36
+ });
37
+ for (const write of renderedWrites) {
38
+ (0, filesystem_1.writeText)(write.path, write.content);
39
+ }
40
+ (0, project_config_1.writeProjectConfig)(projectRoot, mergeResult.config);
41
+ return formatInitSummary(mergeResult.wasFirstInitialization, selected, mergeResult.added, mergeResult.alreadyInstalled, renderedWrites.map((item) => item.relative));
42
+ }
43
+ function formatInitSummary(first, selected, added, alreadyInstalled, targets) {
44
+ const lines = [];
45
+ if (first) {
46
+ lines.push("HIRMOS initialized.", "");
47
+ lines.push("Installed integrations:");
48
+ for (const id of selected)
49
+ lines.push(`- ${id}`);
50
+ lines.push("", "Generated or updated files:");
51
+ for (const target of targets)
52
+ lines.push(`- ${target}`);
53
+ lines.push("", "Next step:");
54
+ lines.push("Open your AI coding tool and ask it to read and follow _hirmos/HIRMOS_CORE.md.");
55
+ lines.push("", "To add more integrations later:");
56
+ lines.push("hirmos init --integration claude,cursor,copilot");
57
+ return `${lines.join("\n")}\n`;
58
+ }
59
+ lines.push("HIRMOS integrations updated.", "");
60
+ if (added.length > 0) {
61
+ lines.push("Added:");
62
+ for (const id of added)
63
+ lines.push(`- ${id}`);
64
+ lines.push("");
65
+ }
66
+ if (alreadyInstalled.length > 0) {
67
+ lines.push("Already installed:");
68
+ for (const id of alreadyInstalled)
69
+ lines.push(`- ${id}`);
70
+ lines.push("");
71
+ }
72
+ lines.push("Generated or updated files:");
73
+ for (const target of targets)
74
+ lines.push(`- ${target}`);
75
+ return `${lines.join("\n").replace(/\n+$/u, "")}\n`;
76
+ }
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const args_1 = require("./lib/args");
5
+ const errors_1 = require("./lib/errors");
6
+ const init_1 = require("./commands/init");
7
+ function main() {
8
+ try {
9
+ const parsed = (0, args_1.parseArgs)(process.argv.slice(2));
10
+ if (parsed.command === "help") {
11
+ process.stdout.write((0, args_1.helpText)());
12
+ return;
13
+ }
14
+ if (parsed.command === "init") {
15
+ const summary = (0, init_1.runInit)({
16
+ projectPath: parsed.projectPath,
17
+ integrations: parsed.integrations,
18
+ source: parsed.source
19
+ });
20
+ process.stdout.write(summary);
21
+ return;
22
+ }
23
+ }
24
+ catch (error) {
25
+ if (error instanceof errors_1.HirmosCliError) {
26
+ process.stderr.write(`ERROR: ${error.message}\n`);
27
+ process.exitCode = 1;
28
+ return;
29
+ }
30
+ process.stderr.write(`ERROR: ${error?.message ?? String(error)}\n`);
31
+ process.exitCode = 1;
32
+ }
33
+ }
34
+ if (require.main === module) {
35
+ main();
36
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseArgs = parseArgs;
7
+ exports.parseIntegrationList = parseIntegrationList;
8
+ exports.helpText = helpText;
9
+ const path_1 = __importDefault(require("path"));
10
+ const errors_1 = require("./errors");
11
+ function parseArgs(argv) {
12
+ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
13
+ return { command: "help" };
14
+ }
15
+ const command = argv[0];
16
+ if (command !== "init") {
17
+ (0, errors_1.fail)(`Unsupported command: ${command}. DP-3B implements only: hirmos init`);
18
+ }
19
+ let projectPath = ".";
20
+ let projectPathSet = false;
21
+ let integrationValue;
22
+ let source;
23
+ for (let i = 1; i < argv.length; i += 1) {
24
+ const arg = argv[i];
25
+ if (arg === "--integration") {
26
+ const value = argv[++i];
27
+ if (!value)
28
+ (0, errors_1.fail)("Missing value for --integration");
29
+ integrationValue = value;
30
+ continue;
31
+ }
32
+ if (arg.startsWith("--integration=")) {
33
+ integrationValue = arg.slice("--integration=".length);
34
+ continue;
35
+ }
36
+ if (arg === "--source") {
37
+ const value = argv[++i];
38
+ if (!value)
39
+ (0, errors_1.fail)("Missing value for --source");
40
+ source = value;
41
+ continue;
42
+ }
43
+ if (arg.startsWith("--source=")) {
44
+ source = arg.slice("--source=".length);
45
+ continue;
46
+ }
47
+ if (arg === "--help" || arg === "-h") {
48
+ return { command: "help" };
49
+ }
50
+ if (arg.startsWith("-")) {
51
+ (0, errors_1.fail)(`Unsupported option: ${arg}`);
52
+ }
53
+ if (projectPathSet) {
54
+ (0, errors_1.fail)(`Unexpected extra positional argument: ${arg}`);
55
+ }
56
+ projectPath = arg;
57
+ projectPathSet = true;
58
+ }
59
+ const integrations = parseIntegrationList(integrationValue ?? "agents");
60
+ return {
61
+ command: "init",
62
+ projectPath: path_1.default.resolve(projectPath),
63
+ integrations,
64
+ source: source ? path_1.default.resolve(source) : undefined
65
+ };
66
+ }
67
+ function parseIntegrationList(value) {
68
+ const ids = value
69
+ .split(",")
70
+ .map((item) => item.trim())
71
+ .filter(Boolean);
72
+ if (ids.length === 0) {
73
+ (0, errors_1.fail)("--integration must include at least one integration id");
74
+ }
75
+ return ids;
76
+ }
77
+ function helpText() {
78
+ return `HIRMOS CLI\n\nUsage:\n hirmos init [project-path] [--integration <ids>] [--source <path>]\n\nDefaults:\n project-path: .\n integration: agents\n source: existing _hirmos/ in the project\n\nDP-3B implements only hirmos init. Workflow commands such as hirmos requirements are interpreted by an agent after HIRMOS Core is loaded.\n`;
79
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HirmosCliError = void 0;
4
+ exports.fail = fail;
5
+ class HirmosCliError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "HirmosCliError";
9
+ }
10
+ }
11
+ exports.HirmosCliError = HirmosCliError;
12
+ function fail(message) {
13
+ throw new HirmosCliError(message);
14
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ensureDir = ensureDir;
7
+ exports.readText = readText;
8
+ exports.writeText = writeText;
9
+ exports.readJson = readJson;
10
+ exports.writeJson = writeJson;
11
+ exports.assertInsideProject = assertInsideProject;
12
+ exports.pathExists = pathExists;
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const errors_1 = require("./errors");
16
+ function ensureDir(dirPath) {
17
+ fs_1.default.mkdirSync(dirPath, { recursive: true });
18
+ }
19
+ function readText(filePath) {
20
+ return fs_1.default.readFileSync(filePath, "utf8");
21
+ }
22
+ function writeText(filePath, content) {
23
+ ensureDir(path_1.default.dirname(filePath));
24
+ fs_1.default.writeFileSync(filePath, content, "utf8");
25
+ }
26
+ function readJson(filePath, label) {
27
+ try {
28
+ return JSON.parse(readText(filePath));
29
+ }
30
+ catch (error) {
31
+ (0, errors_1.fail)(`Invalid ${label} JSON at ${filePath}: ${error.message}`);
32
+ }
33
+ }
34
+ function writeJson(filePath, value) {
35
+ writeText(filePath, `${JSON.stringify(value, null, 2)}\n`);
36
+ }
37
+ function assertInsideProject(projectRoot, targetPath) {
38
+ const relative = path_1.default.relative(projectRoot, targetPath);
39
+ if (relative.startsWith("..") || path_1.default.isAbsolute(relative)) {
40
+ (0, errors_1.fail)(`Refusing to write outside the project root: ${targetPath}`);
41
+ }
42
+ }
43
+ function pathExists(filePath) {
44
+ return fs_1.default.existsSync(filePath);
45
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadRegistry = loadRegistry;
7
+ exports.registryOrder = registryOrder;
8
+ exports.normalizeSelectedIntegrations = normalizeSelectedIntegrations;
9
+ exports.planTargetUpdates = planTargetUpdates;
10
+ const path_1 = __importDefault(require("path"));
11
+ const errors_1 = require("./errors");
12
+ const filesystem_1 = require("./filesystem");
13
+ const managed_blocks_1 = require("./managed-blocks");
14
+ function loadRegistry(hirmosDir) {
15
+ const registryPath = path_1.default.join(hirmosDir, "integrations/agent-tools", "registry.json");
16
+ const registry = (0, filesystem_1.readJson)(registryPath, "agent integration registry");
17
+ if (!registry || typeof registry !== "object")
18
+ (0, errors_1.fail)("Agent integration registry is malformed.");
19
+ if (registry.schema_version !== 1)
20
+ (0, errors_1.fail)(`Unsupported agent integration registry schema_version: ${registry.schema_version}`);
21
+ if (!registry.managed_block?.start || !registry.managed_block?.end)
22
+ (0, errors_1.fail)("Agent integration registry is missing managed block markers.");
23
+ if (!registry.integrations || typeof registry.integrations !== "object")
24
+ (0, errors_1.fail)("Agent integration registry is missing integrations.");
25
+ return registry;
26
+ }
27
+ function registryOrder(registry) {
28
+ return Object.keys(registry.integrations);
29
+ }
30
+ function normalizeSelectedIntegrations(selected, registry) {
31
+ const selectedSet = new Set();
32
+ for (const id of selected) {
33
+ if (!registry.integrations[id]) {
34
+ (0, errors_1.fail)(`Unknown integration id: ${id}. Valid integrations: ${registryOrder(registry).join(", ")}`);
35
+ }
36
+ selectedSet.add(id);
37
+ }
38
+ return registryOrder(registry).filter((id) => selectedSet.has(id));
39
+ }
40
+ function planTargetUpdates(projectRoot, hirmosDir, selected, registry) {
41
+ const grouped = new Map();
42
+ for (const id of selected) {
43
+ const integration = registry.integrations[id];
44
+ if (integration.mode !== "managed_block")
45
+ (0, errors_1.fail)(`Unsupported integration mode for ${id}: ${integration.mode}`);
46
+ const templatePath = path_1.default.join(hirmosDir, "integrations/agent-tools", integration.template);
47
+ const templateContent = (0, filesystem_1.readText)(templatePath);
48
+ (0, managed_blocks_1.validateTemplateManagedBlock)(templateContent, registry.managed_block, integration.template);
49
+ const existing = grouped.get(integration.target);
50
+ if (existing) {
51
+ if (existing.templateContent !== templateContent) {
52
+ (0, errors_1.fail)(`Integrations sharing target ${integration.target} use different templates. Refusing ambiguous update.`);
53
+ }
54
+ existing.integrationIds.push(id);
55
+ continue;
56
+ }
57
+ grouped.set(integration.target, {
58
+ target: integration.target,
59
+ targetPath: path_1.default.resolve(projectRoot, integration.target),
60
+ templatePath,
61
+ templateContent,
62
+ integrationIds: [id]
63
+ });
64
+ }
65
+ return Array.from(grouped.values());
66
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateTemplateManagedBlock = validateTemplateManagedBlock;
4
+ exports.updateManagedBlock = updateManagedBlock;
5
+ const errors_1 = require("./errors");
6
+ function countOccurrences(content, needle) {
7
+ if (!needle)
8
+ return 0;
9
+ let count = 0;
10
+ let index = 0;
11
+ while (true) {
12
+ const found = content.indexOf(needle, index);
13
+ if (found === -1)
14
+ return count;
15
+ count += 1;
16
+ index = found + needle.length;
17
+ }
18
+ }
19
+ function validateTemplateManagedBlock(template, markers, label) {
20
+ const starts = countOccurrences(template, markers.start);
21
+ const ends = countOccurrences(template, markers.end);
22
+ if (starts !== 1 || ends !== 1) {
23
+ (0, errors_1.fail)(`Template ${label} must contain exactly one complete HIRMOS managed block.`);
24
+ }
25
+ if (template.indexOf(markers.start) > template.indexOf(markers.end)) {
26
+ (0, errors_1.fail)(`Template ${label} has malformed HIRMOS managed block markers.`);
27
+ }
28
+ }
29
+ function updateManagedBlock(existing, renderedTemplate, markers, targetLabel) {
30
+ validateTemplateManagedBlock(renderedTemplate, markers, targetLabel);
31
+ if (existing === null) {
32
+ return ensureTrailingNewline(renderedTemplate);
33
+ }
34
+ const starts = countOccurrences(existing, markers.start);
35
+ const ends = countOccurrences(existing, markers.end);
36
+ if (starts !== ends) {
37
+ (0, errors_1.fail)(`Target ${targetLabel} has malformed HIRMOS managed block markers.`);
38
+ }
39
+ if (starts > 1) {
40
+ (0, errors_1.fail)(`Target ${targetLabel} contains multiple HIRMOS managed blocks. Refusing to modify it.`);
41
+ }
42
+ if (starts === 1) {
43
+ const startIndex = existing.indexOf(markers.start);
44
+ const endIndex = existing.indexOf(markers.end, startIndex);
45
+ if (endIndex === -1 || startIndex > endIndex) {
46
+ (0, errors_1.fail)(`Target ${targetLabel} has malformed HIRMOS managed block markers.`);
47
+ }
48
+ const afterEnd = endIndex + markers.end.length;
49
+ return ensureTrailingNewline(`${existing.slice(0, startIndex)}${trimTrailingNewline(renderedTemplate)}${existing.slice(afterEnd)}`);
50
+ }
51
+ const separator = existing.trim().length === 0 ? "" : "\n\n";
52
+ return ensureTrailingNewline(`${trimTrailingNewline(existing)}${separator}${trimTrailingNewline(renderedTemplate)}`);
53
+ }
54
+ function trimTrailingNewline(content) {
55
+ return content.replace(/\n+$/u, "");
56
+ }
57
+ function ensureTrailingNewline(content) {
58
+ return `${trimTrailingNewline(content)}\n`;
59
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadProjectConfig = loadProjectConfig;
7
+ exports.mergeProjectConfig = mergeProjectConfig;
8
+ exports.writeProjectConfig = writeProjectConfig;
9
+ const path_1 = __importDefault(require("path"));
10
+ const filesystem_1 = require("./filesystem");
11
+ const integration_registry_1 = require("./integration-registry");
12
+ function loadProjectConfig(projectRoot) {
13
+ return (0, filesystem_1.readJson)(path_1.default.join(projectRoot, "_hirmos", "project.json"), "_hirmos/project.json");
14
+ }
15
+ function mergeProjectConfig(config, selected, registry) {
16
+ const previousInstalled = Array.isArray(config.integrations?.installed)
17
+ ? config.integrations.installed.filter((id) => typeof id === "string")
18
+ : [];
19
+ const previousSet = new Set(previousInstalled);
20
+ const selectedSet = new Set(selected);
21
+ const added = selected.filter((id) => !previousSet.has(id));
22
+ const alreadyInstalled = selected.filter((id) => previousSet.has(id));
23
+ const knownOrder = (0, integration_registry_1.registryOrder)(registry);
24
+ const allKnown = knownOrder.filter((id) => previousSet.has(id) || selectedSet.has(id));
25
+ const unknownExisting = previousInstalled.filter((id) => !registry.integrations[id]);
26
+ const installed = [...allKnown, ...unknownExisting];
27
+ const nextConfig = {
28
+ ...config,
29
+ integrations: {
30
+ ...(config.integrations ?? {}),
31
+ installed
32
+ }
33
+ };
34
+ return {
35
+ config: nextConfig,
36
+ previousInstalled,
37
+ added,
38
+ alreadyInstalled,
39
+ installed,
40
+ wasFirstInitialization: previousInstalled.length === 0
41
+ };
42
+ }
43
+ function writeProjectConfig(projectRoot, config) {
44
+ (0, filesystem_1.writeJson)(path_1.default.join(projectRoot, "_hirmos", "project.json"), config);
45
+ }
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ensureHirmosInstalled = ensureHirmosInstalled;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const child_process_1 = require("child_process");
11
+ const errors_1 = require("./errors");
12
+ const filesystem_1 = require("./filesystem");
13
+ function ensureHirmosInstalled(projectRoot, source) {
14
+ const hirmosDir = path_1.default.join(projectRoot, "_hirmos");
15
+ if ((0, filesystem_1.pathExists)(hirmosDir)) {
16
+ return;
17
+ }
18
+ if (!source) {
19
+ (0, errors_1.fail)(`Missing _hirmos/ in ${projectRoot}. Provide --source <path-to-hirmos-framework.zip-or-folder>.`);
20
+ }
21
+ if (!(0, filesystem_1.pathExists)(source)) {
22
+ (0, errors_1.fail)(`Source path does not exist: ${source}`);
23
+ }
24
+ const stat = fs_1.default.statSync(source);
25
+ if (stat.isDirectory()) {
26
+ installFromDirectory(projectRoot, source);
27
+ return;
28
+ }
29
+ if (stat.isFile() && source.toLowerCase().endsWith(".zip")) {
30
+ installFromZip(projectRoot, source);
31
+ return;
32
+ }
33
+ (0, errors_1.fail)(`Unsupported --source. Expected a .zip, a folder containing _hirmos, or an _hirmos folder: ${source}`);
34
+ }
35
+ function installFromDirectory(projectRoot, sourceDir) {
36
+ const sourceHirmos = path_1.default.basename(sourceDir) === "_hirmos" ? sourceDir : path_1.default.join(sourceDir, "_hirmos");
37
+ if (!(0, filesystem_1.pathExists)(sourceHirmos) || !fs_1.default.statSync(sourceHirmos).isDirectory()) {
38
+ (0, errors_1.fail)(`Source folder does not contain _hirmos/: ${sourceDir}`);
39
+ }
40
+ copyHirmos(projectRoot, sourceHirmos);
41
+ }
42
+ function installFromZip(projectRoot, zipPath) {
43
+ const tmp = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), "hirmos-cli-"));
44
+ try {
45
+ try {
46
+ extractZip(zipPath, tmp);
47
+ }
48
+ catch (error) {
49
+ (0, errors_1.fail)(`Unable to extract --source zip. Ensure the zip is valid and an extraction command is available: ${error.message}`);
50
+ }
51
+ const sourceHirmos = findHirmosDir(tmp);
52
+ if (!sourceHirmos) {
53
+ (0, errors_1.fail)(`Source zip does not contain an _hirmos/ folder: ${zipPath}`);
54
+ }
55
+ copyHirmos(projectRoot, sourceHirmos);
56
+ }
57
+ finally {
58
+ fs_1.default.rmSync(tmp, { recursive: true, force: true });
59
+ }
60
+ }
61
+ function findHirmosDir(root) {
62
+ const direct = path_1.default.join(root, "_hirmos");
63
+ if ((0, filesystem_1.pathExists)(direct) && fs_1.default.statSync(direct).isDirectory())
64
+ return direct;
65
+ const entries = fs_1.default.readdirSync(root, { withFileTypes: true });
66
+ for (const entry of entries) {
67
+ if (!entry.isDirectory())
68
+ continue;
69
+ const candidate = path_1.default.join(root, entry.name, "_hirmos");
70
+ if ((0, filesystem_1.pathExists)(candidate) && fs_1.default.statSync(candidate).isDirectory())
71
+ return candidate;
72
+ }
73
+ return null;
74
+ }
75
+ function copyHirmos(projectRoot, sourceHirmos) {
76
+ const target = path_1.default.join(projectRoot, "_hirmos");
77
+ if ((0, filesystem_1.pathExists)(target)) {
78
+ (0, errors_1.fail)(`Refusing to overwrite existing _hirmos/: ${target}`);
79
+ }
80
+ (0, filesystem_1.ensureDir)(projectRoot);
81
+ fs_1.default.cpSync(sourceHirmos, target, { recursive: true, errorOnExist: true });
82
+ }
83
+ function extractZip(zipPath, destination) {
84
+ if (process.platform === "win32") {
85
+ (0, child_process_1.execFileSync)("powershell", ["-NoProfile", "-Command", "Expand-Archive", "-LiteralPath", zipPath, "-DestinationPath", destination, "-Force"], { stdio: "ignore" });
86
+ return;
87
+ }
88
+ (0, child_process_1.execFileSync)("unzip", ["-q", zipPath, "-d", destination], { stdio: "ignore" });
89
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateProjectRoot = validateProjectRoot;
7
+ exports.validateHirmosFramework = validateHirmosFramework;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const errors_1 = require("./errors");
11
+ const filesystem_1 = require("./filesystem");
12
+ function validateProjectRoot(projectRoot) {
13
+ if (!(0, filesystem_1.pathExists)(projectRoot)) {
14
+ (0, errors_1.fail)(`Project path does not exist: ${projectRoot}`);
15
+ }
16
+ if (!fs_1.default.statSync(projectRoot).isDirectory()) {
17
+ (0, errors_1.fail)(`Project path is not a directory: ${projectRoot}`);
18
+ }
19
+ }
20
+ function validateHirmosFramework(projectRoot) {
21
+ const required = [
22
+ "_hirmos/HIRMOS_CORE.md",
23
+ "_hirmos/project.json",
24
+ "_hirmos/integrations/agent-tools/registry.json"
25
+ ];
26
+ for (const relative of required) {
27
+ const absolute = path_1.default.join(projectRoot, relative);
28
+ if (!(0, filesystem_1.pathExists)(absolute)) {
29
+ (0, errors_1.fail)(`Required HIRMOS file is missing: ${relative}`);
30
+ }
31
+ }
32
+ const templatesDir = path_1.default.join(projectRoot, "_hirmos", "integrations/agent-tools", "templates");
33
+ if (!(0, filesystem_1.pathExists)(templatesDir) || !fs_1.default.statSync(templatesDir).isDirectory()) {
34
+ (0, errors_1.fail)("Required HIRMOS directory is missing: _hirmos/integrations/agent-tools/templates/");
35
+ }
36
+ }
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "hirmos",
3
+ "version": "1.3.0",
4
+ "description": "HIRMOS product-facing CLI.",
5
+ "license": "Apache-2.0",
6
+ "type": "commonjs",
7
+ "bin": {
8
+ "hirmos": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc -p tsconfig.json",
12
+ "test": "npm run build && node --test test/*.test.js",
13
+ "prepack": "npm run build"
14
+ },
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.0.0"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "package.json"
25
+ ]
26
+ }