@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.
- package/LICENSE +21 -0
- package/dist/ai-cli.d.ts +31 -0
- package/dist/ai-cli.js +238 -0
- package/dist/ai-cli.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +50663 -0
- package/dist/bin.js.map +1 -0
- package/dist/data-dir.d.ts +9 -0
- package/dist/data-dir.js +13 -0
- package/dist/data-dir.js.map +1 -0
- package/dist/envelope.d.ts +11 -0
- package/dist/envelope.js +7 -0
- package/dist/envelope.js.map +1 -0
- package/dist/explore-api.d.ts +187 -0
- package/dist/explore-api.js +302 -0
- package/dist/explore-api.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/init-keys.d.ts +14 -0
- package/dist/init-keys.js +18 -0
- package/dist/init-keys.js.map +1 -0
- package/dist/init-mcp.d.ts +87 -0
- package/dist/init-mcp.js +198 -0
- package/dist/init-mcp.js.map +1 -0
- package/dist/init-skills.d.ts +80 -0
- package/dist/init-skills.js +256 -0
- package/dist/init-skills.js.map +1 -0
- package/dist/journey-api.d.ts +37 -0
- package/dist/journey-api.js +55 -0
- package/dist/journey-api.js.map +1 -0
- package/dist/load-api.d.ts +48 -0
- package/dist/load-api.js +84 -0
- package/dist/load-api.js.map +1 -0
- package/dist/mcp-api.d.ts +116 -0
- package/dist/mcp-api.js +359 -0
- package/dist/mcp-api.js.map +1 -0
- package/dist/mission-api.d.ts +56 -0
- package/dist/mission-api.js +65 -0
- package/dist/mission-api.js.map +1 -0
- package/dist/program.d.ts +77 -0
- package/dist/program.js +1938 -0
- package/dist/program.js.map +1 -0
- package/dist/record-api.d.ts +74 -0
- package/dist/record-api.js +93 -0
- package/dist/record-api.js.map +1 -0
- package/dist/regression-api.d.ts +17 -0
- package/dist/regression-api.js +16 -0
- package/dist/regression-api.js.map +1 -0
- package/dist/self-heal-adapter.d.ts +31 -0
- package/dist/self-heal-adapter.js +70 -0
- package/dist/self-heal-adapter.js.map +1 -0
- package/dist/source-api.d.ts +147 -0
- package/dist/source-api.js +208 -0
- package/dist/source-api.js.map +1 -0
- package/dist/source-run-api.d.ts +57 -0
- package/dist/source-run-api.js +91 -0
- package/dist/source-run-api.js.map +1 -0
- package/dist/ui-api.d.ts +35 -0
- package/dist/ui-api.js +369 -0
- package/dist/ui-api.js.map +1 -0
- package/dist/ux-api.d.ts +75 -0
- package/dist/ux-api.js +231 -0
- package/dist/ux-api.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +37 -0
- package/dist/version.js.map +1 -0
- package/package.json +85 -0
- package/skills/jevitate-explore/SKILL.md +108 -0
- package/skills/jevitate-load-test/SKILL.md +42 -0
- package/skills/jevitate-mission-scope/SKILL.md +107 -0
- package/skills/jevitate-record/SKILL.md +78 -0
- package/skills/jevitate-run-journey/SKILL.md +72 -0
- package/skills/jevitate-sources/SKILL.md +88 -0
- package/skills/jevitate-ux-review/SKILL.md +68 -0
- package/ui/app.js +538 -0
- 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 {};
|
package/dist/init-mcp.js
ADDED
|
@@ -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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"}
|