@jevitate/cli 0.1.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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ai-cli.d.ts +31 -0
  3. package/dist/ai-cli.js +238 -0
  4. package/dist/ai-cli.js.map +1 -0
  5. package/dist/bin.d.ts +2 -0
  6. package/dist/bin.js +50663 -0
  7. package/dist/bin.js.map +1 -0
  8. package/dist/data-dir.d.ts +9 -0
  9. package/dist/data-dir.js +13 -0
  10. package/dist/data-dir.js.map +1 -0
  11. package/dist/envelope.d.ts +11 -0
  12. package/dist/envelope.js +7 -0
  13. package/dist/envelope.js.map +1 -0
  14. package/dist/explore-api.d.ts +187 -0
  15. package/dist/explore-api.js +302 -0
  16. package/dist/explore-api.js.map +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +3 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/init-keys.d.ts +14 -0
  21. package/dist/init-keys.js +18 -0
  22. package/dist/init-keys.js.map +1 -0
  23. package/dist/init-mcp.d.ts +87 -0
  24. package/dist/init-mcp.js +198 -0
  25. package/dist/init-mcp.js.map +1 -0
  26. package/dist/init-skills.d.ts +80 -0
  27. package/dist/init-skills.js +256 -0
  28. package/dist/init-skills.js.map +1 -0
  29. package/dist/journey-api.d.ts +37 -0
  30. package/dist/journey-api.js +55 -0
  31. package/dist/journey-api.js.map +1 -0
  32. package/dist/load-api.d.ts +48 -0
  33. package/dist/load-api.js +84 -0
  34. package/dist/load-api.js.map +1 -0
  35. package/dist/mcp-api.d.ts +116 -0
  36. package/dist/mcp-api.js +359 -0
  37. package/dist/mcp-api.js.map +1 -0
  38. package/dist/mission-api.d.ts +56 -0
  39. package/dist/mission-api.js +65 -0
  40. package/dist/mission-api.js.map +1 -0
  41. package/dist/program.d.ts +77 -0
  42. package/dist/program.js +1938 -0
  43. package/dist/program.js.map +1 -0
  44. package/dist/record-api.d.ts +74 -0
  45. package/dist/record-api.js +93 -0
  46. package/dist/record-api.js.map +1 -0
  47. package/dist/regression-api.d.ts +17 -0
  48. package/dist/regression-api.js +16 -0
  49. package/dist/regression-api.js.map +1 -0
  50. package/dist/self-heal-adapter.d.ts +31 -0
  51. package/dist/self-heal-adapter.js +70 -0
  52. package/dist/self-heal-adapter.js.map +1 -0
  53. package/dist/source-api.d.ts +147 -0
  54. package/dist/source-api.js +208 -0
  55. package/dist/source-api.js.map +1 -0
  56. package/dist/source-run-api.d.ts +57 -0
  57. package/dist/source-run-api.js +91 -0
  58. package/dist/source-run-api.js.map +1 -0
  59. package/dist/ui-api.d.ts +35 -0
  60. package/dist/ui-api.js +369 -0
  61. package/dist/ui-api.js.map +1 -0
  62. package/dist/ux-api.d.ts +75 -0
  63. package/dist/ux-api.js +231 -0
  64. package/dist/ux-api.js.map +1 -0
  65. package/dist/version.d.ts +1 -0
  66. package/dist/version.js +37 -0
  67. package/dist/version.js.map +1 -0
  68. package/package.json +85 -0
  69. package/skills/jevitate-explore/SKILL.md +108 -0
  70. package/skills/jevitate-load-test/SKILL.md +42 -0
  71. package/skills/jevitate-mission-scope/SKILL.md +107 -0
  72. package/skills/jevitate-record/SKILL.md +78 -0
  73. package/skills/jevitate-run-journey/SKILL.md +72 -0
  74. package/skills/jevitate-sources/SKILL.md +88 -0
  75. package/skills/jevitate-ux-review/SKILL.md +68 -0
  76. package/ui/app.js +538 -0
  77. package/ui/index.html +546 -0
@@ -0,0 +1,87 @@
1
+ import type { RuntimeId, DetectionDeps } from "./init-skills.js";
2
+ /**
3
+ * Registers the `jevitate mcp` stdio server into each agent harness's own MCP
4
+ * config, idempotently and NEVER destructively. This mirrors `init-skills.ts`'s
5
+ * safety contract: a user's existing config is never clobbered without
6
+ * `--force`, and when a target's on-disk content conflicts or cannot be parsed
7
+ * we PRINT the snippet + instruction rather than risk corrupting it. All fs
8
+ * seams are injected so the logic is unit-testable without touching the machine.
9
+ *
10
+ * The harness conventions were verified (Sep 2026):
11
+ * - Claude Code: `claude mcp add jevitate -- jevitate mcp`, or an mcpServers
12
+ * entry in `~/.claude.json` (user) / a project `.mcp.json`. We write the
13
+ * project-scoped `.mcp.json` (a dedicated, team-shareable MCP file).
14
+ * - Cursor: `.cursor/mcp.json` (project) / `~/.cursor/mcp.json` (global),
15
+ * `{ "mcpServers": { ... } }`.
16
+ * - Codex: `~/.codex/config.toml`, a `[mcp_servers.jevitate]` TOML table.
17
+ */
18
+ /** The MCP server key every harness registers us under. */
19
+ export declare const MCP_SERVER_NAME = "jevitate";
20
+ /** The canonical stdio launch config: run the `jevitate mcp` subcommand. */
21
+ export interface JevitateServerConfig {
22
+ command: string;
23
+ args: string[];
24
+ }
25
+ export declare function jevitateServerConfig(): JevitateServerConfig;
26
+ /** The `{ mcpServers: { jevitate: {...} } }` object Claude Code / Cursor use. */
27
+ export declare function mcpServersObject(): {
28
+ mcpServers: Record<string, JevitateServerConfig>;
29
+ };
30
+ export type McpHarness = "claude" | "cursor" | "codex" | "json";
31
+ /**
32
+ * Renders the exact registration snippet for one harness — the universal escape
33
+ * hatch behind `jevitate mcp --print-config <harness>`. Prints only; writes
34
+ * nothing. `json` is the bare mcpServers object so it can be piped/round-tripped.
35
+ */
36
+ export declare function renderPrintConfig(harness: McpHarness): string;
37
+ export interface McpTargetPaths {
38
+ claudeMcpJson: string;
39
+ cursorMcpJson: string;
40
+ codexConfigToml: string;
41
+ }
42
+ export declare function resolveMcpTargetPaths(deps?: DetectionDeps): McpTargetPaths;
43
+ export type McpInstallAction = "create" | "update" | "unchanged" | "skip-conflict" | "force-update" | "skip-unparsable";
44
+ export interface MergeResult {
45
+ action: McpInstallAction;
46
+ /** The bytes to write, present ONLY for create/update/force-update. */
47
+ content?: string;
48
+ }
49
+ /**
50
+ * Plans a merge into an `mcpServers` JSON config (Claude Code / Cursor). Other
51
+ * servers and unrelated top-level keys are preserved. A pre-existing, DIFFERENT
52
+ * `jevitate` entry is never overwritten without `force`; an unparseable file is
53
+ * never rewritten at all — both fall back to "print the snippet."
54
+ */
55
+ export declare function mergeMcpJson(existingRaw: string | undefined, opts: {
56
+ force?: boolean;
57
+ }): MergeResult;
58
+ /**
59
+ * Plans a merge into Codex's `~/.codex/config.toml`. When our table is absent we
60
+ * append it (prior bytes preserved). A pre-existing, DIFFERENT `jevitate` table
61
+ * is surfaced for printing unless `force`, in which case ONLY that one table is
62
+ * replaced in place — neighbouring tables/keys are left intact.
63
+ */
64
+ export declare function mergeCodexToml(existingRaw: string | undefined, opts: {
65
+ force?: boolean;
66
+ }): MergeResult;
67
+ /** MCP registration applies only to harnesses with a real MCP-config convention. */
68
+ type McpRuntime = "claude-code" | "cursor" | "codex";
69
+ export interface McpInstallReport {
70
+ target: McpRuntime;
71
+ path: string;
72
+ action: McpInstallAction;
73
+ /** For skip-conflict / skip-unparsable: the snippet + guidance to show the
74
+ * user, so a refusal is honest and actionable (never a silent no-op). */
75
+ instruction?: string;
76
+ }
77
+ /**
78
+ * Registers the MCP server into every applicable runtime in `runtimes`
79
+ * (claude-code, cursor, codex). `generic` has no MCP-config convention and is
80
+ * ignored. Idempotent; never overwrites a user's conflicting/unparseable config
81
+ * without `--force`, attaching a printable instruction when it declines.
82
+ */
83
+ export declare function registerMcp(runtimes: RuntimeId[], paths: McpTargetPaths, opts?: {
84
+ force?: boolean;
85
+ dryRun?: boolean;
86
+ }): Promise<McpInstallReport[]>;
87
+ export {};
@@ -0,0 +1,198 @@
1
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
2
+ import { homedir as realHomedir } from "node:os";
3
+ import { join, dirname } from "node:path";
4
+ /**
5
+ * Registers the `jevitate mcp` stdio server into each agent harness's own MCP
6
+ * config, idempotently and NEVER destructively. This mirrors `init-skills.ts`'s
7
+ * safety contract: a user's existing config is never clobbered without
8
+ * `--force`, and when a target's on-disk content conflicts or cannot be parsed
9
+ * we PRINT the snippet + instruction rather than risk corrupting it. All fs
10
+ * seams are injected so the logic is unit-testable without touching the machine.
11
+ *
12
+ * The harness conventions were verified (Sep 2026):
13
+ * - Claude Code: `claude mcp add jevitate -- jevitate mcp`, or an mcpServers
14
+ * entry in `~/.claude.json` (user) / a project `.mcp.json`. We write the
15
+ * project-scoped `.mcp.json` (a dedicated, team-shareable MCP file).
16
+ * - Cursor: `.cursor/mcp.json` (project) / `~/.cursor/mcp.json` (global),
17
+ * `{ "mcpServers": { ... } }`.
18
+ * - Codex: `~/.codex/config.toml`, a `[mcp_servers.jevitate]` TOML table.
19
+ */
20
+ /** The MCP server key every harness registers us under. */
21
+ export const MCP_SERVER_NAME = "jevitate";
22
+ export function jevitateServerConfig() {
23
+ return { command: "jevitate", args: ["mcp"] };
24
+ }
25
+ /** The `{ mcpServers: { jevitate: {...} } }` object Claude Code / Cursor use. */
26
+ export function mcpServersObject() {
27
+ return { mcpServers: { [MCP_SERVER_NAME]: jevitateServerConfig() } };
28
+ }
29
+ const JSON_SNIPPET = JSON.stringify(mcpServersObject(), null, 2);
30
+ const CODEX_TOML_BLOCK = `[mcp_servers.jevitate]\ncommand = "jevitate"\nargs = ["mcp"]`;
31
+ /**
32
+ * Renders the exact registration snippet for one harness — the universal escape
33
+ * hatch behind `jevitate mcp --print-config <harness>`. Prints only; writes
34
+ * nothing. `json` is the bare mcpServers object so it can be piped/round-tripped.
35
+ */
36
+ export function renderPrintConfig(harness) {
37
+ switch (harness) {
38
+ case "json":
39
+ return JSON_SNIPPET;
40
+ case "claude":
41
+ return [
42
+ "# Claude Code — option A: register with one command",
43
+ "claude mcp add jevitate -- jevitate mcp",
44
+ "",
45
+ "# Claude Code — option B: add this to ~/.claude.json (user scope)",
46
+ "# or a project .mcp.json (project scope):",
47
+ JSON_SNIPPET,
48
+ ].join("\n");
49
+ case "cursor":
50
+ return [
51
+ "# Cursor — add this to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):",
52
+ JSON_SNIPPET,
53
+ ].join("\n");
54
+ case "codex":
55
+ return ["# Codex — add this to ~/.codex/config.toml:", CODEX_TOML_BLOCK].join("\n");
56
+ default:
57
+ throw new Error(`unknown MCP harness '${harness}' (expected claude | cursor | codex | json)`);
58
+ }
59
+ }
60
+ export function resolveMcpTargetPaths(deps = {}) {
61
+ const homedir = deps.homedir ?? realHomedir;
62
+ const cwd = deps.cwd ?? process.cwd;
63
+ return {
64
+ claudeMcpJson: join(cwd(), ".mcp.json"),
65
+ cursorMcpJson: join(cwd(), ".cursor", "mcp.json"),
66
+ codexConfigToml: join(homedir(), ".codex", "config.toml"),
67
+ };
68
+ }
69
+ function isEquivalentServer(value) {
70
+ if (typeof value !== "object" || value === null || Array.isArray(value))
71
+ return false;
72
+ const rec = value;
73
+ const keys = Object.keys(rec).sort();
74
+ if (keys.length !== 2 || keys[0] !== "args" || keys[1] !== "command")
75
+ return false;
76
+ if (rec.command !== "jevitate")
77
+ return false;
78
+ return Array.isArray(rec.args) && rec.args.length === 1 && rec.args[0] === "mcp";
79
+ }
80
+ /**
81
+ * Plans a merge into an `mcpServers` JSON config (Claude Code / Cursor). Other
82
+ * servers and unrelated top-level keys are preserved. A pre-existing, DIFFERENT
83
+ * `jevitate` entry is never overwritten without `force`; an unparseable file is
84
+ * never rewritten at all — both fall back to "print the snippet."
85
+ */
86
+ export function mergeMcpJson(existingRaw, opts) {
87
+ if (existingRaw === undefined || existingRaw.trim() === "") {
88
+ return { action: "create", content: `${JSON.stringify(mcpServersObject(), null, 2)}\n` };
89
+ }
90
+ let parsed;
91
+ try {
92
+ parsed = JSON.parse(existingRaw);
93
+ }
94
+ catch {
95
+ return { action: "skip-unparsable" };
96
+ }
97
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
98
+ return { action: "skip-unparsable" };
99
+ }
100
+ const root = parsed;
101
+ const servers = typeof root.mcpServers === "object" && root.mcpServers !== null && !Array.isArray(root.mcpServers)
102
+ ? root.mcpServers
103
+ : {};
104
+ const existingEntry = servers[MCP_SERVER_NAME];
105
+ if (existingEntry !== undefined) {
106
+ if (isEquivalentServer(existingEntry))
107
+ return { action: "unchanged" };
108
+ if (!opts.force)
109
+ return { action: "skip-conflict" };
110
+ const next = { ...root, mcpServers: { ...servers, [MCP_SERVER_NAME]: jevitateServerConfig() } };
111
+ return { action: "force-update", content: `${JSON.stringify(next, null, 2)}\n` };
112
+ }
113
+ const next = { ...root, mcpServers: { ...servers, [MCP_SERVER_NAME]: jevitateServerConfig() } };
114
+ return { action: "update", content: `${JSON.stringify(next, null, 2)}\n` };
115
+ }
116
+ const CODEX_HEADER_RE = /^[ \t]*\[mcp_servers\.jevitate\][ \t]*$/m;
117
+ /** Slices out the `[mcp_servers.jevitate]` table (header through the line before
118
+ * the next top-level `[...]` header, or EOF). Returns null when absent. */
119
+ function codexTableRegion(raw) {
120
+ const headerMatch = CODEX_HEADER_RE.exec(raw);
121
+ if (!headerMatch)
122
+ return null;
123
+ const start = headerMatch.index;
124
+ const afterHeader = start + headerMatch[0].length;
125
+ const nextHeader = /^[ \t]*\[/m.exec(raw.slice(afterHeader));
126
+ const end = nextHeader ? afterHeader + nextHeader.index : raw.length;
127
+ return { start, end, body: raw.slice(afterHeader, end) };
128
+ }
129
+ function codexBodyIsOurs(body) {
130
+ const command = /^[ \t]*command[ \t]*=[ \t]*"jevitate"[ \t]*$/m.test(body);
131
+ const args = /^[ \t]*args[ \t]*=[ \t]*\[[ \t]*"mcp"[ \t]*\][ \t]*$/m.test(body);
132
+ return command && args;
133
+ }
134
+ /**
135
+ * Plans a merge into Codex's `~/.codex/config.toml`. When our table is absent we
136
+ * append it (prior bytes preserved). A pre-existing, DIFFERENT `jevitate` table
137
+ * is surfaced for printing unless `force`, in which case ONLY that one table is
138
+ * replaced in place — neighbouring tables/keys are left intact.
139
+ */
140
+ export function mergeCodexToml(existingRaw, opts) {
141
+ if (existingRaw === undefined || existingRaw.trim() === "") {
142
+ return { action: "create", content: `${CODEX_TOML_BLOCK}\n` };
143
+ }
144
+ const region = codexTableRegion(existingRaw);
145
+ if (region) {
146
+ if (codexBodyIsOurs(region.body))
147
+ return { action: "unchanged" };
148
+ if (!opts.force)
149
+ return { action: "skip-conflict" };
150
+ const before = existingRaw.slice(0, region.start);
151
+ const after = existingRaw.slice(region.end);
152
+ const replaced = `${before}${CODEX_TOML_BLOCK}\n${after.startsWith("\n") ? after.slice(1) : after}`;
153
+ return { action: "force-update", content: replaced };
154
+ }
155
+ const sep = existingRaw.endsWith("\n\n") ? "" : existingRaw.endsWith("\n") ? "\n" : "\n\n";
156
+ return { action: "update", content: `${existingRaw}${sep}${CODEX_TOML_BLOCK}\n` };
157
+ }
158
+ /**
159
+ * Registers the MCP server into every applicable runtime in `runtimes`
160
+ * (claude-code, cursor, codex). `generic` has no MCP-config convention and is
161
+ * ignored. Idempotent; never overwrites a user's conflicting/unparseable config
162
+ * without `--force`, attaching a printable instruction when it declines.
163
+ */
164
+ export async function registerMcp(runtimes, paths, opts = {}) {
165
+ const runtimeSet = new Set(runtimes);
166
+ const units = [];
167
+ if (runtimeSet.has("claude-code")) {
168
+ units.push({ target: "claude-code", path: paths.claudeMcpJson, harness: "claude", plan: mergeMcpJson });
169
+ }
170
+ if (runtimeSet.has("cursor")) {
171
+ units.push({ target: "cursor", path: paths.cursorMcpJson, harness: "cursor", plan: mergeMcpJson });
172
+ }
173
+ if (runtimeSet.has("codex")) {
174
+ units.push({ target: "codex", path: paths.codexConfigToml, harness: "codex", plan: mergeCodexToml });
175
+ }
176
+ const report = [];
177
+ for (const unit of units) {
178
+ let existing;
179
+ try {
180
+ existing = await readFile(unit.path, "utf8");
181
+ }
182
+ catch {
183
+ existing = undefined;
184
+ }
185
+ const result = unit.plan(existing, { force: opts.force });
186
+ const entry = { target: unit.target, path: unit.path, action: result.action };
187
+ if (result.action === "skip-conflict" || result.action === "skip-unparsable") {
188
+ entry.instruction = renderPrintConfig(unit.harness);
189
+ }
190
+ if (!opts.dryRun && result.content !== undefined) {
191
+ await mkdir(dirname(unit.path), { recursive: true });
192
+ await writeFile(unit.path, result.content, "utf8");
193
+ }
194
+ report.push(entry);
195
+ }
196
+ return report;
197
+ }
198
+ //# sourceMappingURL=init-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-mcp.js","sourceRoot":"","sources":["../src/init-mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import type { ResolvedSkill } from "@jevitate/skills";
2
+ export type RuntimeId = "claude-code" | "codex" | "cursor" | "generic";
3
+ export interface DetectionDeps {
4
+ existsSync?: (path: string) => boolean;
5
+ homedir?: () => string;
6
+ cwd?: () => string;
7
+ }
8
+ /**
9
+ * Detects which agent runtimes to install into. Claude Code and Codex are
10
+ * user-global (`~/.claude`, `~/.codex`); Cursor is project-local (`<cwd>/.cursor`);
11
+ * `generic` is always appended (the tool-agnostic fallback). Never returns an
12
+ * empty array (Guardrail 4). Detection is fail-closed toward "don't install
13
+ * where not detected" for the three gated targets — a caller opts a target in
14
+ * explicitly via `--targets` when it isn't auto-detected.
15
+ */
16
+ export declare function detectRuntimes(deps?: DetectionDeps): RuntimeId[];
17
+ export interface InstallTargetPaths {
18
+ claudeSkillsDir: string;
19
+ codexAgentsFile: string;
20
+ cursorRulesDir: string;
21
+ genericAgentsFile: string;
22
+ genericSkillsDir: string;
23
+ }
24
+ export declare function resolveInstallTargetPaths(deps?: DetectionDeps): InstallTargetPaths;
25
+ export type InstallAction = "create" | "update" | "unchanged" | "skip-user-modified" | "force-update";
26
+ /**
27
+ * Decides what to do with one whole-file target. Never writes. A target whose
28
+ * current on-disk hash does not match the hash `init` last recorded (or was
29
+ * never recorded) is treated as user-modified and SKIPPED — a reported,
30
+ * non-error branch, never a silent no-op — unless `force` is set.
31
+ */
32
+ export declare function planFileInstall(targetPath: string, newContent: string, lastInstalledHash: string | undefined, opts?: {
33
+ force?: boolean;
34
+ }): Promise<InstallAction>;
35
+ export declare function applyFileInstall(targetPath: string, content: string, action: InstallAction): Promise<void>;
36
+ export declare const SKILLS_BLOCK_BEGIN = "<!-- BEGIN JEVITATE SKILLS v1 -->";
37
+ export declare const SKILLS_BLOCK_END = "<!-- END JEVITATE SKILLS v1 -->";
38
+ type BlockSkill = Pick<ResolvedSkill, "id" | "name" | "description" | "body">;
39
+ export interface RenderBlockOptions {
40
+ mode: "inline" | "reference";
41
+ /** Required for reference mode: where the Claude-Code copies live. */
42
+ claudeSkillsDir?: string;
43
+ }
44
+ /**
45
+ * Renders the marked block that Codex/generic `AGENTS.md` carry. In `inline`
46
+ * mode each skill's full body is embedded (used when no other on-disk copy
47
+ * exists, avoiding a dangling pointer); in `reference` mode it points at the
48
+ * installed Claude-Code path instead.
49
+ */
50
+ export declare function renderSkillsBlock(skills: BlockSkill[], opts: RenderBlockOptions): string;
51
+ /**
52
+ * Splices `newBlock` (marker-wrapped) into `existing`. If markers are present,
53
+ * only the marked region is replaced; everything before the opening marker and
54
+ * after the closing marker is preserved byte-for-byte. Otherwise the block is
55
+ * appended after a blank-line separator. Idempotent on repeat with the same block.
56
+ */
57
+ export declare function mergeBlock(existing: string, newBlock: string): string;
58
+ /** sha256 of just the block's inner text (between the markers), or undefined
59
+ * when no block is present — so user edits OUTSIDE the block never register as
60
+ * a modification and edits INSIDE it do. */
61
+ export declare function extractBlockHash(content: string): string | undefined;
62
+ export interface InstallReport {
63
+ target: RuntimeId;
64
+ skillId: string;
65
+ path: string;
66
+ action: InstallAction;
67
+ }
68
+ /**
69
+ * Detect/plan/install the six skills into every runtime passed in (the caller
70
+ * decides the runtime set: `detectRuntimes()` ∪ nothing, or a `--targets`
71
+ * override). Idempotent via a small state file keyed by target path. Never
72
+ * overwrites a user-modified file/block without `force`; every skip is reported.
73
+ * State is keyed by absolute path (a superset of the plan's `<target>:<skillId>`
74
+ * key that also disambiguates the generic target's two write locations).
75
+ */
76
+ export declare function installSkills(runtimes: RuntimeId[], skills: ResolvedSkill[], paths: InstallTargetPaths, statePath: string, opts?: {
77
+ force?: boolean;
78
+ dryRun?: boolean;
79
+ }): Promise<InstallReport[]>;
80
+ export {};
@@ -0,0 +1,256 @@
1
+ import { existsSync as realExistsSync } from "node:fs";
2
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
3
+ import { homedir as realHomedir } from "node:os";
4
+ import { join, dirname } from "node:path";
5
+ import { createHash } from "node:crypto";
6
+ /**
7
+ * Detects which agent runtimes to install into. Claude Code and Codex are
8
+ * user-global (`~/.claude`, `~/.codex`); Cursor is project-local (`<cwd>/.cursor`);
9
+ * `generic` is always appended (the tool-agnostic fallback). Never returns an
10
+ * empty array (Guardrail 4). Detection is fail-closed toward "don't install
11
+ * where not detected" for the three gated targets — a caller opts a target in
12
+ * explicitly via `--targets` when it isn't auto-detected.
13
+ */
14
+ export function detectRuntimes(deps = {}) {
15
+ const existsSync = deps.existsSync ?? realExistsSync;
16
+ const homedir = deps.homedir ?? realHomedir;
17
+ const cwd = deps.cwd ?? process.cwd;
18
+ const detected = [];
19
+ if (existsSync(join(homedir(), ".claude")))
20
+ detected.push("claude-code");
21
+ if (existsSync(join(homedir(), ".codex")))
22
+ detected.push("codex");
23
+ if (existsSync(join(cwd(), ".cursor")))
24
+ detected.push("cursor");
25
+ detected.push("generic"); // always-on fallback target — see Decision 3 / Guardrail 4
26
+ return detected;
27
+ }
28
+ export function resolveInstallTargetPaths(deps = {}) {
29
+ const homedir = deps.homedir ?? realHomedir;
30
+ const cwd = deps.cwd ?? process.cwd;
31
+ return {
32
+ claudeSkillsDir: join(homedir(), ".claude", "skills"),
33
+ codexAgentsFile: join(homedir(), ".codex", "AGENTS.md"),
34
+ cursorRulesDir: join(cwd(), ".cursor", "rules"),
35
+ genericAgentsFile: join(cwd(), "AGENTS.md"),
36
+ genericSkillsDir: join(cwd(), ".agent", "skills"),
37
+ };
38
+ }
39
+ function sha256(s) {
40
+ return createHash("sha256").update(s, "utf8").digest("hex");
41
+ }
42
+ /**
43
+ * Decides what to do with one whole-file target. Never writes. A target whose
44
+ * current on-disk hash does not match the hash `init` last recorded (or was
45
+ * never recorded) is treated as user-modified and SKIPPED — a reported,
46
+ * non-error branch, never a silent no-op — unless `force` is set.
47
+ */
48
+ export async function planFileInstall(targetPath, newContent, lastInstalledHash, opts = {}) {
49
+ let current;
50
+ try {
51
+ current = await readFile(targetPath, "utf8");
52
+ }
53
+ catch {
54
+ return "create"; // absent (or unreadable) — fail toward creating fresh; a write error surfaces at apply time
55
+ }
56
+ const currentHash = sha256(current);
57
+ const userModified = lastInstalledHash === undefined || currentHash !== lastInstalledHash;
58
+ if (userModified && !opts.force)
59
+ return "skip-user-modified";
60
+ if (userModified && opts.force)
61
+ return "force-update";
62
+ return currentHash === sha256(newContent) ? "unchanged" : "update";
63
+ }
64
+ export async function applyFileInstall(targetPath, content, action) {
65
+ if (action === "unchanged" || action === "skip-user-modified")
66
+ return;
67
+ await mkdir(dirname(targetPath), { recursive: true });
68
+ await writeFile(targetPath, content, "utf8");
69
+ }
70
+ // ---- marked-block targets (Codex + generic AGENTS.md) ----
71
+ export const SKILLS_BLOCK_BEGIN = "<!-- BEGIN JEVITATE SKILLS v1 -->";
72
+ export const SKILLS_BLOCK_END = "<!-- END JEVITATE SKILLS v1 -->";
73
+ /**
74
+ * Renders the marked block that Codex/generic `AGENTS.md` carry. In `inline`
75
+ * mode each skill's full body is embedded (used when no other on-disk copy
76
+ * exists, avoiding a dangling pointer); in `reference` mode it points at the
77
+ * installed Claude-Code path instead.
78
+ */
79
+ export function renderSkillsBlock(skills, opts) {
80
+ const parts = [SKILLS_BLOCK_BEGIN, "", "# Jevitate skills", ""];
81
+ for (const s of skills) {
82
+ parts.push(`## ${s.name}`, "", s.description, "");
83
+ if (opts.mode === "reference" && opts.claudeSkillsDir) {
84
+ parts.push(`See ${join(opts.claudeSkillsDir, s.id, "SKILL.md")}`, "");
85
+ }
86
+ else {
87
+ parts.push(s.body.trimEnd(), "");
88
+ }
89
+ }
90
+ parts.push(SKILLS_BLOCK_END);
91
+ return parts.join("\n");
92
+ }
93
+ /**
94
+ * Splices `newBlock` (marker-wrapped) into `existing`. If markers are present,
95
+ * only the marked region is replaced; everything before the opening marker and
96
+ * after the closing marker is preserved byte-for-byte. Otherwise the block is
97
+ * appended after a blank-line separator. Idempotent on repeat with the same block.
98
+ */
99
+ export function mergeBlock(existing, newBlock) {
100
+ const start = existing.indexOf(SKILLS_BLOCK_BEGIN);
101
+ const endMarker = existing.indexOf(SKILLS_BLOCK_END, start === -1 ? 0 : start);
102
+ if (start !== -1 && endMarker !== -1) {
103
+ const before = existing.slice(0, start);
104
+ const after = existing.slice(endMarker + SKILLS_BLOCK_END.length);
105
+ return before + newBlock + after;
106
+ }
107
+ if (existing === "")
108
+ return newBlock;
109
+ const sep = existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
110
+ return existing + sep + newBlock;
111
+ }
112
+ /** sha256 of just the block's inner text (between the markers), or undefined
113
+ * when no block is present — so user edits OUTSIDE the block never register as
114
+ * a modification and edits INSIDE it do. */
115
+ export function extractBlockHash(content) {
116
+ const start = content.indexOf(SKILLS_BLOCK_BEGIN);
117
+ if (start === -1)
118
+ return undefined;
119
+ const endMarker = content.indexOf(SKILLS_BLOCK_END, start);
120
+ if (endMarker === -1)
121
+ return undefined;
122
+ const inner = content.slice(start + SKILLS_BLOCK_BEGIN.length, endMarker);
123
+ return sha256(inner);
124
+ }
125
+ async function planBlockInstall(targetPath, newBlock, lastInstalledHash, opts = {}) {
126
+ let current;
127
+ try {
128
+ current = await readFile(targetPath, "utf8");
129
+ }
130
+ catch {
131
+ return "create";
132
+ }
133
+ const currentBlockHash = extractBlockHash(current);
134
+ if (currentBlockHash === undefined)
135
+ return "create"; // file exists but has no block yet — append one
136
+ const userModified = lastInstalledHash === undefined || currentBlockHash !== lastInstalledHash;
137
+ if (userModified && !opts.force)
138
+ return "skip-user-modified";
139
+ if (userModified && opts.force)
140
+ return "force-update";
141
+ const newInnerHash = extractBlockHash(newBlock);
142
+ return currentBlockHash === newInnerHash ? "unchanged" : "update";
143
+ }
144
+ async function applyBlockInstall(targetPath, newBlock, action) {
145
+ if (action === "unchanged" || action === "skip-user-modified")
146
+ return;
147
+ let existing = "";
148
+ try {
149
+ existing = await readFile(targetPath, "utf8");
150
+ }
151
+ catch {
152
+ existing = "";
153
+ }
154
+ await mkdir(dirname(targetPath), { recursive: true });
155
+ await writeFile(targetPath, mergeBlock(existing, newBlock), "utf8");
156
+ }
157
+ // ---- Cursor .mdc rendering (project-scoped rule) ----
158
+ /**
159
+ * Renders a Cursor rule (`.mdc`): Cursor's own frontmatter (`description`,
160
+ * `globs`, `alwaysApply`) followed by the skill body. NOTE: Cursor's `.mdc`
161
+ * convention was inferred; the install mechanics (idempotent per-file write)
162
+ * do not depend on the exact frontmatter shape.
163
+ */
164
+ function renderCursorRule(skill) {
165
+ return `---\ndescription: ${skill.description}\nglobs:\nalwaysApply: false\n---\n\n${skill.body}`;
166
+ }
167
+ async function loadState(statePath) {
168
+ try {
169
+ const raw = await readFile(statePath, "utf8");
170
+ return { state: JSON.parse(raw), raw };
171
+ }
172
+ catch {
173
+ return { state: {}, raw: undefined }; // no existing file yet — start fresh (matches realSecureIO().persist's pattern)
174
+ }
175
+ }
176
+ /**
177
+ * Detect/plan/install the six skills into every runtime passed in (the caller
178
+ * decides the runtime set: `detectRuntimes()` ∪ nothing, or a `--targets`
179
+ * override). Idempotent via a small state file keyed by target path. Never
180
+ * overwrites a user-modified file/block without `force`; every skip is reported.
181
+ * State is keyed by absolute path (a superset of the plan's `<target>:<skillId>`
182
+ * key that also disambiguates the generic target's two write locations).
183
+ */
184
+ export async function installSkills(runtimes, skills, paths, statePath, opts = {}) {
185
+ const { state, raw: originalStateRaw } = await loadState(statePath);
186
+ const runtimeSet = new Set(runtimes);
187
+ const blockMode = runtimeSet.has("claude-code") ? "reference" : "inline";
188
+ const fileUnits = [];
189
+ const blockUnits = [];
190
+ for (const skill of skills) {
191
+ const source = await readFile(skill.filePath, "utf8");
192
+ if (runtimeSet.has("claude-code")) {
193
+ fileUnits.push({
194
+ target: "claude-code",
195
+ skillId: skill.id,
196
+ path: join(paths.claudeSkillsDir, skill.id, "SKILL.md"),
197
+ content: source,
198
+ });
199
+ }
200
+ if (runtimeSet.has("cursor")) {
201
+ fileUnits.push({
202
+ target: "cursor",
203
+ skillId: skill.id,
204
+ path: join(paths.cursorRulesDir, `jevitate-${skill.id}.mdc`),
205
+ content: renderCursorRule(skill),
206
+ });
207
+ }
208
+ if (runtimeSet.has("generic")) {
209
+ fileUnits.push({
210
+ target: "generic",
211
+ skillId: skill.id,
212
+ path: join(paths.genericSkillsDir, skill.id, "SKILL.md"),
213
+ content: source,
214
+ });
215
+ }
216
+ }
217
+ const block = renderSkillsBlock(skills, { mode: blockMode, claudeSkillsDir: paths.claudeSkillsDir });
218
+ if (runtimeSet.has("codex"))
219
+ blockUnits.push({ target: "codex", path: paths.codexAgentsFile, block });
220
+ if (runtimeSet.has("generic"))
221
+ blockUnits.push({ target: "generic", path: paths.genericAgentsFile, block });
222
+ const report = [];
223
+ for (const unit of fileUnits) {
224
+ const action = await planFileInstall(unit.path, unit.content, state[unit.path], { force: opts.force });
225
+ if (!opts.dryRun) {
226
+ await applyFileInstall(unit.path, unit.content, action);
227
+ if (action !== "skip-user-modified")
228
+ state[unit.path] = sha256(unit.content);
229
+ }
230
+ report.push({ target: unit.target, skillId: unit.skillId, path: unit.path, action });
231
+ }
232
+ for (const unit of blockUnits) {
233
+ const action = await planBlockInstall(unit.path, unit.block, state[unit.path], { force: opts.force });
234
+ if (!opts.dryRun) {
235
+ await applyBlockInstall(unit.path, unit.block, action);
236
+ if (action !== "skip-user-modified") {
237
+ const written = await readFile(unit.path, "utf8").catch(() => "");
238
+ const hash = extractBlockHash(written);
239
+ if (hash !== undefined)
240
+ state[unit.path] = hash;
241
+ }
242
+ }
243
+ report.push({ target: unit.target, skillId: "*", path: unit.path, action });
244
+ }
245
+ // Only touch the state file when it actually changed, so a clean re-run
246
+ // (everything "unchanged") performs zero writes.
247
+ if (!opts.dryRun) {
248
+ const nextStateRaw = JSON.stringify(state, null, 2);
249
+ if (nextStateRaw !== originalStateRaw) {
250
+ await mkdir(dirname(statePath), { recursive: true });
251
+ await writeFile(statePath, nextStateRaw, "utf8");
252
+ }
253
+ }
254
+ return report;
255
+ }
256
+ //# sourceMappingURL=init-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-skills.js","sourceRoot":"","sources":["../src/init-skills.ts"],"names":[],"mappings":"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"}