greprag 5.84.0 → 5.85.1
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/dist/commands/agent-doctrine-file.js +5 -128
- package/dist/commands/agents.js +125 -846
- package/dist/commands/init.js +17 -29
- package/dist/commands/load.js +14 -36
- package/dist/commands/parity.js +1 -1
- package/dist/commands/skill-mirror-reminder.js +1 -18
- package/dist/commands/skill.js +137 -566
- package/dist/hook.js +30 -54
- package/dist/index.js +571 -573
- package/dist/native-adapter-refresh.js +15 -18
- package/dist/native-skill-mirror.js +11 -92
- package/dist/opencode-plugin.bundle.js +81 -283
- package/dist/skill-activation-manifest.js +14 -37
- package/dist/skill-git-ingest.js +300 -0
- package/dist/skill-landing.js +7 -173
- package/dist/skill-mirror-client.js +4 -280
- package/dist/skill-staleness.js +3 -6
- package/package.json +1 -1
- package/skill/mechanic/docs/skill-fix-conventions.md +1 -1
- package/skill/templates/os.md +1 -1
- package/skill/templates/prompt-audit.md +26 -49
- package/skill/templates/skill-change.md +15 -13
|
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
|
|
25
25
|
// src/opencode-plugin.ts
|
|
26
26
|
var crypto6 = __toESM(require("crypto"));
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
27
|
+
var fs11 = __toESM(require("fs"));
|
|
28
|
+
var os4 = __toESM(require("os"));
|
|
29
|
+
var path12 = __toESM(require("path"));
|
|
30
30
|
|
|
31
31
|
// src/opencode-plugin-helpers.ts
|
|
32
32
|
var crypto = __toESM(require("crypto"));
|
|
@@ -2179,12 +2179,15 @@ var fs3 = __toESM(require("fs"));
|
|
|
2179
2179
|
var path4 = __toESM(require("path"));
|
|
2180
2180
|
|
|
2181
2181
|
// src/skill-landing.ts
|
|
2182
|
-
var
|
|
2182
|
+
var path2 = __toESM(require("path"));
|
|
2183
|
+
var BUNDLED_SKILLS = ["greprag", "commander", "content-advisor", "mechanic"];
|
|
2184
|
+
var LEARNED_DIR_RELPATH = path2.join("docs", "learned");
|
|
2185
|
+
var LEARNINGS_RELPATH = path2.join("docs", "learnings.md");
|
|
2183
2186
|
|
|
2184
2187
|
// src/skill-mirror-files.ts
|
|
2185
2188
|
var crypto2 = __toESM(require("node:crypto"));
|
|
2186
2189
|
var fs2 = __toESM(require("node:fs"));
|
|
2187
|
-
var
|
|
2190
|
+
var path3 = __toESM(require("node:path"));
|
|
2188
2191
|
function computeSkillFileHash(files) {
|
|
2189
2192
|
const hash = crypto2.createHash("sha256");
|
|
2190
2193
|
for (const file of [...files].sort((a, b) => a.path.localeCompare(b.path))) {
|
|
@@ -2208,13 +2211,13 @@ function isLinkedSkillFolder(dir) {
|
|
|
2208
2211
|
function isSkillFolderOwnedElsewhere(dir) {
|
|
2209
2212
|
if (isLinkedSkillFolder(dir))
|
|
2210
2213
|
return true;
|
|
2211
|
-
const root =
|
|
2212
|
-
const harnessDir =
|
|
2213
|
-
if (
|
|
2214
|
+
const root = path3.dirname(path3.resolve(dir));
|
|
2215
|
+
const harnessDir = path3.basename(path3.dirname(root));
|
|
2216
|
+
if (path3.basename(root) !== "skills" || !INSTALLED_SKILL_HARNESS_DIRS.includes(harnessDir))
|
|
2214
2217
|
return false;
|
|
2215
2218
|
let skillMd = "";
|
|
2216
2219
|
try {
|
|
2217
|
-
skillMd = fs2.readFileSync(
|
|
2220
|
+
skillMd = fs2.readFileSync(path3.join(dir, "SKILL.md"), "utf8");
|
|
2218
2221
|
} catch {
|
|
2219
2222
|
}
|
|
2220
2223
|
if (isGeneratedSkillAdapter(skillMd))
|
|
@@ -2225,8 +2228,8 @@ function isSkillFolderOwnedElsewhere(dir) {
|
|
|
2225
2228
|
if (harnessDir !== ".claude" && harnessDir !== ".agents")
|
|
2226
2229
|
return false;
|
|
2227
2230
|
try {
|
|
2228
|
-
const lock = JSON.parse(fs2.readFileSync(
|
|
2229
|
-
return Boolean(lock?.skills && Object.prototype.hasOwnProperty.call(lock.skills,
|
|
2231
|
+
const lock = JSON.parse(fs2.readFileSync(path3.join(path3.dirname(path3.dirname(root)), ".agents", ".skill-lock.json"), "utf8"));
|
|
2232
|
+
return Boolean(lock?.skills && Object.prototype.hasOwnProperty.call(lock.skills, path3.basename(dir)));
|
|
2230
2233
|
} catch {
|
|
2231
2234
|
return false;
|
|
2232
2235
|
}
|
|
@@ -2237,11 +2240,6 @@ function isSafeMirroredSkillFile(file) {
|
|
|
2237
2240
|
return /^docs\/(?!.*(?:^|\/)\.\.(?:\/|$))[A-Za-z0-9._\/-]+\.md$/.test(file.path);
|
|
2238
2241
|
}
|
|
2239
2242
|
|
|
2240
|
-
// src/skill-landing.ts
|
|
2241
|
-
var BUNDLED_SKILLS = ["greprag", "commander", "content-advisor", "mechanic"];
|
|
2242
|
-
var LEARNED_DIR_RELPATH = path3.join("docs", "learned");
|
|
2243
|
-
var LEARNINGS_RELPATH = path3.join("docs", "learnings.md");
|
|
2244
|
-
|
|
2245
2243
|
// src/skill-activation-manifest.ts
|
|
2246
2244
|
var MAX_NATIVE_SKILL_FILES = 1500;
|
|
2247
2245
|
var MAX_SCAN_DEPTH = 7;
|
|
@@ -2348,19 +2346,15 @@ function readNativeSkillNames(params) {
|
|
|
2348
2346
|
}
|
|
2349
2347
|
function nativeAdapterGap(platform, mirrored, native) {
|
|
2350
2348
|
const missing = [];
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
for (const { skillName, scope } of mirrored) {
|
|
2349
|
+
if (platform !== "codex" && platform !== "opencode")
|
|
2350
|
+
return { missing };
|
|
2351
|
+
for (const { skillName } of mirrored) {
|
|
2355
2352
|
if (!skillName || BUNDLED_SKILLS.includes(skillName))
|
|
2356
2353
|
continue;
|
|
2357
|
-
|
|
2358
|
-
if (!kind)
|
|
2354
|
+
if (!native.get(skillName.toLowerCase()))
|
|
2359
2355
|
missing.push(skillName);
|
|
2360
|
-
else if (kind === "local" && platform === "claude-code" && scope !== "project")
|
|
2361
|
-
localCopies.push(skillName);
|
|
2362
2356
|
}
|
|
2363
|
-
return { missing
|
|
2357
|
+
return { missing };
|
|
2364
2358
|
}
|
|
2365
2359
|
function buildMirroredSkillActivation(entries, native, platform) {
|
|
2366
2360
|
const valid = entries.filter((entry) => entry.skillName && entry.triggerDescription);
|
|
@@ -2377,16 +2371,7 @@ function buildMirroredSkillActivation(entries, native, platform) {
|
|
|
2377
2371
|
function nativeAdapterInstallCommand(platform, missingNames) {
|
|
2378
2372
|
if (missingNames.length === 0)
|
|
2379
2373
|
return null;
|
|
2380
|
-
|
|
2381
|
-
return `greprag skill mirror sync claude-code --only ${[...missingNames].sort().join(",")}`;
|
|
2382
|
-
}
|
|
2383
|
-
if (platform === "codex" || platform === "opencode") {
|
|
2384
|
-
return `greprag skill mirror sync ${platform}`;
|
|
2385
|
-
}
|
|
2386
|
-
return null;
|
|
2387
|
-
}
|
|
2388
|
-
function localCopyCutoverCommand(platform, localCopies) {
|
|
2389
|
-
return localCopies.length > 0 ? nativeAdapterInstallCommand(platform, ["<name>"]) : null;
|
|
2374
|
+
return platform === "codex" || platform === "opencode" ? `greprag skill mirror sync ${platform}` : null;
|
|
2390
2375
|
}
|
|
2391
2376
|
function activationFromApiRows(params) {
|
|
2392
2377
|
const entries = params.rows.filter((row) => typeof row.skillName === "string" && !!row.skillName && typeof row.triggerDescription === "string" && !!row.triggerDescription).map((row) => ({
|
|
@@ -2409,20 +2394,9 @@ function renderEntry(skill) {
|
|
|
2409
2394
|
function buildSkillMirrorAnnounce(m, platform) {
|
|
2410
2395
|
if (!m || m.count <= 0)
|
|
2411
2396
|
return null;
|
|
2412
|
-
const cutoverCommand = localCopyCutoverCommand(platform || "", m.localCopies);
|
|
2413
|
-
const copies = m.localCopies.length === 1 ? "one mirrored skill" : `${m.localCopies.length} mirrored skills`;
|
|
2414
|
-
const copySection = cutoverCommand ? [
|
|
2415
|
-
"### Hand-Kept Copies",
|
|
2416
|
-
`This harness lists ${copies} from a hand-kept copy instead of an adapter, so it keeps running that copy when the mirrored skill changes: ${m.localCopies.join(", ")}. Replacing a copy with its adapter is the user's call, one skill at a time (the old SKILL.md is backed up first):`,
|
|
2417
|
-
"",
|
|
2418
|
-
` ${cutoverCommand}`,
|
|
2419
|
-
"",
|
|
2420
|
-
"Offer that command when the user asks why a skill behaves out of date, or when you needed one of them this session.",
|
|
2421
|
-
""
|
|
2422
|
-
] : [];
|
|
2423
2397
|
if (m.skills.length === 0) {
|
|
2424
2398
|
const line = "[greprag skills: slash skills are available. When discussing skills with the user, say use/create/update/refresh `/skill`; keep storage mechanics internal unless diagnosing a failure or the user asks how it works. Use `greprag load <skill>` to read skill instructions.]";
|
|
2425
|
-
return
|
|
2399
|
+
return line;
|
|
2426
2400
|
}
|
|
2427
2401
|
const installCommand = nativeAdapterInstallCommand(platform || "", m.missing);
|
|
2428
2402
|
const gapSection = installCommand ? [
|
|
@@ -2440,7 +2414,6 @@ function buildSkillMirrorAnnounce(m, platform) {
|
|
|
2440
2414
|
"When discussing skills with the user, say use/create/update/refresh `/skill`; keep storage mechanics internal unless diagnosing a failure or the user asks how it works.",
|
|
2441
2415
|
"",
|
|
2442
2416
|
...gapSection,
|
|
2443
|
-
...copySection,
|
|
2444
2417
|
"### Trigger Rules",
|
|
2445
2418
|
"If the user names a skill (with or without `/`) OR the request clearly matches a skill's description below, you MUST activate that skill for the current turn. Match meaning, not exact keywords.",
|
|
2446
2419
|
"",
|
|
@@ -4176,11 +4149,6 @@ function recodeMessagesToPng(messages, opts) {
|
|
|
4176
4149
|
return stats;
|
|
4177
4150
|
}
|
|
4178
4151
|
|
|
4179
|
-
// src/claude-lifecycle-cutover.ts
|
|
4180
|
-
var fs11 = __toESM(require("fs"));
|
|
4181
|
-
var os4 = __toESM(require("os"));
|
|
4182
|
-
var path12 = __toESM(require("path"));
|
|
4183
|
-
|
|
4184
4152
|
// src/native-skill-mirror.ts
|
|
4185
4153
|
var fs10 = __toESM(require("node:fs"));
|
|
4186
4154
|
var os3 = __toESM(require("node:os"));
|
|
@@ -4422,126 +4390,44 @@ function canonicalContentHash(skill) {
|
|
|
4422
4390
|
function canonicalLookupArgs(skill) {
|
|
4423
4391
|
return `--skill-id ${canonicalSkillId(skill)}`;
|
|
4424
4392
|
}
|
|
4425
|
-
function
|
|
4426
|
-
|
|
4427
|
-
return "";
|
|
4428
|
-
const replaces = (skill.replaces || []).map((id) => ` --replaces ${id}`).join("");
|
|
4429
|
-
return `--scope project --project-id ${skill.projectId}${replaces}`;
|
|
4430
|
-
}
|
|
4431
|
-
function canonicalEditArgs(skill) {
|
|
4432
|
-
return [canonicalLookupArgs(skill), canonicalWriteArgs(skill)].filter(Boolean).join(" ");
|
|
4433
|
-
}
|
|
4434
|
-
function nativeCreationLocations(platform) {
|
|
4435
|
-
if (platform === "codex") {
|
|
4436
|
-
return "`~/.codex/skills/<new-name>/SKILL.md` (global) or `<repo>/.agents/skills/<new-name>/SKILL.md` (project)";
|
|
4437
|
-
}
|
|
4438
|
-
if (platform === "opencode") {
|
|
4439
|
-
return "`~/.config/opencode/skills/<new-name>/SKILL.md` (global) or `<repo>/.opencode/skills/<new-name>/SKILL.md` (project)";
|
|
4440
|
-
}
|
|
4441
|
-
return "`~/.claude/skills/<new-name>/SKILL.md` (global) or `<repo>/.claude/skills/<new-name>/SKILL.md` (project)";
|
|
4442
|
-
}
|
|
4443
|
-
function renderChangeSkillLine(params) {
|
|
4444
|
-
const exportCommand = `greprag skill mirror export ${params.skillName} ${params.identityArg} --to <dir>`;
|
|
4445
|
-
const applyCommand = `greprag skill mirror apply <dir> --skill ${params.skillName} ${params.editArgs} --expected-hash <exported-hash> --reason "<why>"`;
|
|
4446
|
-
if (params.platform === "codex") {
|
|
4447
|
-
return `- **Change this skill:** run \`greprag load skill-change\`, then \`${exportCommand}\`; patch the exported Markdown files non-interactively; run \`${applyCommand}\`; then \`${params.syncCommand}\`; then verify with \`${params.loadCommand}\`.`;
|
|
4448
|
-
}
|
|
4449
|
-
return `- **Change this skill:** run \`greprag load skill-change\`, then \`greprag skill edit ${params.skillName} ${params.editArgs} --reason "<why>"\`, then \`${params.syncCommand}\`, then verify with \`${params.loadCommand}\`.`;
|
|
4450
|
-
}
|
|
4451
|
-
function renderEditingSection(platform, params) {
|
|
4452
|
-
const exportCommand = `greprag skill mirror export ${params.skillName} ${params.identityArg} --to <dir>`;
|
|
4453
|
-
const applyCommand = `greprag skill mirror apply <dir> --skill ${params.skillName} ${params.editArgs} --expected-hash <exported-hash> --reason "<why>"`;
|
|
4454
|
-
if (platform === "codex") {
|
|
4455
|
-
return `## Editing
|
|
4456
|
-
|
|
4457
|
-
Codex agents must use the non-interactive mirror path:
|
|
4458
|
-
|
|
4459
|
-
\`\`\`bash
|
|
4460
|
-
${exportCommand}
|
|
4461
|
-
# patch the exported Markdown files with normal file edits
|
|
4462
|
-
${applyCommand}
|
|
4463
|
-
\`\`\`
|
|
4464
|
-
|
|
4465
|
-
Do not launch an interactive editor from Codex. If using \`greprag skill edit\` as an export helper, include \`--no-open\` and run the printed apply command after patching.
|
|
4466
|
-
`;
|
|
4467
|
-
}
|
|
4468
|
-
return `## Editing
|
|
4469
|
-
|
|
4470
|
-
To edit this skill, run the guarded editor flow:
|
|
4471
|
-
|
|
4472
|
-
\`\`\`bash
|
|
4473
|
-
greprag skill edit ${params.skillName} ${params.editArgs} --reason "<why>"
|
|
4474
|
-
\`\`\`
|
|
4475
|
-
|
|
4476
|
-
Manual path:
|
|
4477
|
-
|
|
4478
|
-
\`\`\`bash
|
|
4479
|
-
${exportCommand}
|
|
4480
|
-
${applyCommand}
|
|
4481
|
-
\`\`\`
|
|
4482
|
-
`;
|
|
4393
|
+
function gitSkillFolder(skill) {
|
|
4394
|
+
return skill.scope === "project" ? `\`<repo>/.claude/skills/${skill.skillName}/\`${skill.projectName ? ` in the ${skill.projectName} repo` : ""}` : `\`~/.claude/skills/${skill.skillName}/\` in the dotclaude repo`;
|
|
4483
4395
|
}
|
|
4484
4396
|
function renderAdapterBody(platform, skill) {
|
|
4485
4397
|
const skillName = skill.skillName;
|
|
4486
4398
|
const skillId = canonicalSkillId(skill);
|
|
4487
4399
|
const identityArg = canonicalLookupArgs(skill);
|
|
4488
|
-
const editArgs = canonicalEditArgs(skill);
|
|
4489
|
-
const creationLocations = nativeCreationLocations(platform);
|
|
4490
4400
|
const syncCommand = nativeAdapterInstallCommand(platform, [skillName]);
|
|
4491
|
-
const creationLine = platform === "claude-code" ? "- **Create another skill:** use the normal native creation path and leave it unmanaged until a separate Claude cutover is approved." : `- **Create another skill:** create a normal native skill at ${creationLocations}; run \`${syncCommand} --dry-run\` and confirm it says \`adopt\`; then run \`${syncCommand}\`. GrepRAG adopts it as canon and replaces the native package with an adapter.`;
|
|
4492
4401
|
const companions = skill.files.map((file) => file.path).filter((relative) => relative !== "SKILL.md").sort();
|
|
4493
4402
|
const harnessArg = `--harness ${platform}`;
|
|
4494
4403
|
const loadCommand = `greprag load ${skillName} ${identityArg} ${harnessArg}`;
|
|
4495
|
-
const
|
|
4496
|
-
platform,
|
|
4497
|
-
skillName,
|
|
4498
|
-
editArgs,
|
|
4499
|
-
identityArg,
|
|
4500
|
-
syncCommand,
|
|
4501
|
-
loadCommand
|
|
4502
|
-
});
|
|
4503
|
-
const editingSection = renderEditingSection(platform, { skillName, editArgs, identityArg });
|
|
4504
|
-
const companionLines = companions.length ? companions.map((relative) => `- \`greprag load ${skillName} ${relative} ${identityArg} ${harnessArg}\``).join("\n") : "- No companion docs are mirrored into this native adapter.";
|
|
4404
|
+
const companionLines = companions.length ? companions.map((relative) => `- \`greprag load ${skillName} ${relative} ${identityArg} ${harnessArg}\``).join("\n") : "- No companion docs are mirrored into this launcher.";
|
|
4505
4405
|
return `# ${skillName}
|
|
4506
4406
|
|
|
4507
4407
|
## GENERATED ADAPTER \u2014 NEVER EDIT
|
|
4508
4408
|
|
|
4509
|
-
This is a disposable GrepRAG launcher for ${platform}, not
|
|
4510
|
-
|
|
4511
|
-
Do not add or maintain project-state/doc pointers here. Current project state belongs in repo docs and the GrepRAG doc mirror; \`greprag load ${skillName} ${identityArg} ${harnessArg}\` discovers relevant state docs dynamically.
|
|
4409
|
+
This is a disposable GrepRAG launcher for ${platform}, not skill content. The skill lives in git at ${gitSkillFolder(skill)}; GrepRAG mirrors the committed files read-only and this launcher loads them.
|
|
4512
4410
|
|
|
4513
4411
|
Required action chains:
|
|
4514
4412
|
|
|
4515
4413
|
- **Use this skill:** run \`${loadCommand}\` before acting.
|
|
4516
|
-
${
|
|
4517
|
-
|
|
4414
|
+
- **Change this skill:** edit ${gitSkillFolder(skill)} with normal file edits, commit, then run \`${syncCommand}\` (session start does it too) and verify with \`${loadCommand}\`. Uncommitted edits are never served. History and rollback are git log / git revert on that folder.
|
|
4415
|
+
- **Create another skill:** create \`~/.claude/skills/<new-name>/SKILL.md\` (dotclaude repo, global) or \`<repo>/.claude/skills/<new-name>/SKILL.md\` (project), commit, then run \`${syncCommand}\`.
|
|
4518
4416
|
|
|
4519
4417
|
Authority: ${skillId}
|
|
4520
4418
|
Source binding: ${sourceBindingState(skill)}
|
|
4521
4419
|
Canonical content hash: ${canonicalContentHash(skill)}
|
|
4522
4420
|
${typeof skill.revision === "number" ? `Mirror revision: ${skill.revision}` : "Mirror revision: unknown"}
|
|
4523
4421
|
|
|
4524
|
-
${editingSection}
|
|
4525
|
-
|
|
4526
|
-
History and rollback (every canon write keeps its bytes and reason):
|
|
4527
|
-
|
|
4528
|
-
\`\`\`bash
|
|
4529
|
-
greprag skill history ${skillName} ${identityArg}
|
|
4530
|
-
greprag skill diff ${skillName} <revA> [<revB>] ${identityArg}
|
|
4531
|
-
greprag skill revert ${skillName} --to <rev> ${identityArg} --reason "<why>" --expected-hash <current-hash>
|
|
4532
|
-
greprag skill mirror snapshot list ${skillName} ${identityArg}
|
|
4533
|
-
greprag skill mirror snapshot promote ${skillName} --name <snapshot> ${editArgs} --expected-hash <current-hash> --reason "<why>"
|
|
4534
|
-
\`\`\`
|
|
4535
|
-
|
|
4536
4422
|
## Mandatory Invocation
|
|
4537
4423
|
|
|
4538
4424
|
When this skill activates, run:
|
|
4539
4425
|
|
|
4540
4426
|
\`\`\`bash
|
|
4541
|
-
|
|
4427
|
+
${loadCommand}
|
|
4542
4428
|
\`\`\`
|
|
4543
4429
|
|
|
4544
|
-
Read and follow that
|
|
4430
|
+
Read and follow that payload before responding or taking action.
|
|
4545
4431
|
|
|
4546
4432
|
## Companion Docs
|
|
4547
4433
|
|
|
@@ -4805,94 +4691,6 @@ async function fetchNativeMirroredSkill(apiUrl, apiKey, row) {
|
|
|
4805
4691
|
} : null;
|
|
4806
4692
|
}
|
|
4807
4693
|
|
|
4808
|
-
// src/claude-lifecycle-cutover.ts
|
|
4809
|
-
var CLAUDE_LIFECYCLE_SKILLS = ["commit", "deploy", "release"];
|
|
4810
|
-
function adapterFor(skill) {
|
|
4811
|
-
const files = buildNativeSkillAdapter("claude-code", skill, { allowClaudeCode: true });
|
|
4812
|
-
if (!files || files.length !== 1 || files[0].path !== "SKILL.md") {
|
|
4813
|
-
throw new Error(`Claude lifecycle adapter is invalid for ${skill.skillName}.`);
|
|
4814
|
-
}
|
|
4815
|
-
return files;
|
|
4816
|
-
}
|
|
4817
|
-
function safeTimestamp(now) {
|
|
4818
|
-
return now.toISOString().replace(/[:.]/g, "-");
|
|
4819
|
-
}
|
|
4820
|
-
function requiredSkills(skills, names) {
|
|
4821
|
-
const byName = new Map(skills.filter((skill) => skill.scope !== "project").map((skill) => [skill.skillName, skill]));
|
|
4822
|
-
return names.map((name) => {
|
|
4823
|
-
const skill = byName.get(name);
|
|
4824
|
-
if (!skill)
|
|
4825
|
-
throw new Error(`Canonical Claude lifecycle skill missing: ${name}.`);
|
|
4826
|
-
return skill;
|
|
4827
|
-
});
|
|
4828
|
-
}
|
|
4829
|
-
function applyClaudeLifecycleCutover(params) {
|
|
4830
|
-
const homeDir = params.homeDir || os4.homedir();
|
|
4831
|
-
const root = nativeSkillRoot("claude-code", homeDir);
|
|
4832
|
-
const state = readState(root);
|
|
4833
|
-
const names = params.names?.length ? params.names : CLAUDE_LIFECYCLE_SKILLS;
|
|
4834
|
-
const prepared = requiredSkills(params.skills, names).map((skill) => {
|
|
4835
|
-
const target = path12.join(root, skill.skillName);
|
|
4836
|
-
if (isSkillFolderOwnedElsewhere(target)) {
|
|
4837
|
-
throw new Error(`Claude skill folder is owned elsewhere (a link or another installer's package): ${skill.skillName}; not written.`);
|
|
4838
|
-
}
|
|
4839
|
-
const skillMd = path12.join(target, "SKILL.md");
|
|
4840
|
-
const adapterFiles = adapterFor(skill);
|
|
4841
|
-
const nextHash = computeSkillFileHash(adapterFiles);
|
|
4842
|
-
const managed = state.skills[skill.skillName];
|
|
4843
|
-
let currentContent;
|
|
4844
|
-
if (fs11.existsSync(skillMd))
|
|
4845
|
-
currentContent = fs11.readFileSync(skillMd, "utf8");
|
|
4846
|
-
if (managed) {
|
|
4847
|
-
const currentFiles = currentContent === void 0 ? null : [{ path: "SKILL.md", content: currentContent }];
|
|
4848
|
-
if (!currentFiles || computeSkillFileHash(currentFiles) !== managed.hash) {
|
|
4849
|
-
throw new Error(`Claude lifecycle adapter diverged: ${skill.skillName}.`);
|
|
4850
|
-
}
|
|
4851
|
-
} else if (fs11.existsSync(target) && currentContent === void 0) {
|
|
4852
|
-
throw new Error(`Claude lifecycle package has no SKILL.md: ${skill.skillName}.`);
|
|
4853
|
-
}
|
|
4854
|
-
return { skill, target, skillMd, adapterFiles, nextHash, managed, currentContent };
|
|
4855
|
-
});
|
|
4856
|
-
const replacements = prepared.filter((item) => !item.managed && item.currentContent !== void 0);
|
|
4857
|
-
let backupDir;
|
|
4858
|
-
if (replacements.length > 0) {
|
|
4859
|
-
backupDir = path12.join(
|
|
4860
|
-
homeDir,
|
|
4861
|
-
".greprag",
|
|
4862
|
-
"backups",
|
|
4863
|
-
"claude-lifecycle-cutover",
|
|
4864
|
-
safeTimestamp(params.now || /* @__PURE__ */ new Date())
|
|
4865
|
-
);
|
|
4866
|
-
for (const item of replacements) {
|
|
4867
|
-
const destination = path12.join(backupDir, item.skill.skillName, "SKILL.md");
|
|
4868
|
-
fs11.mkdirSync(path12.dirname(destination), { recursive: true });
|
|
4869
|
-
fs11.writeFileSync(destination, item.currentContent, "utf8");
|
|
4870
|
-
}
|
|
4871
|
-
}
|
|
4872
|
-
const result = {
|
|
4873
|
-
installed: 0,
|
|
4874
|
-
updated: 0,
|
|
4875
|
-
current: 0,
|
|
4876
|
-
blocked: 0,
|
|
4877
|
-
backupDir
|
|
4878
|
-
};
|
|
4879
|
-
for (const item of prepared) {
|
|
4880
|
-
if (item.managed && item.managed.hash === item.nextHash) {
|
|
4881
|
-
result.current++;
|
|
4882
|
-
continue;
|
|
4883
|
-
}
|
|
4884
|
-
fs11.mkdirSync(item.target, { recursive: true });
|
|
4885
|
-
fs11.writeFileSync(item.skillMd, item.adapterFiles[0].content, "utf8");
|
|
4886
|
-
state.skills[item.skill.skillName] = { hash: item.nextHash, files: ["SKILL.md"] };
|
|
4887
|
-
if (item.managed)
|
|
4888
|
-
result.updated++;
|
|
4889
|
-
else
|
|
4890
|
-
result.installed++;
|
|
4891
|
-
}
|
|
4892
|
-
writeState(root, state);
|
|
4893
|
-
return result;
|
|
4894
|
-
}
|
|
4895
|
-
|
|
4896
4694
|
// src/native-adapter-refresh.ts
|
|
4897
4695
|
function readAdapterStamps(content) {
|
|
4898
4696
|
const frontmatter = /^---\n([\s\S]*?)\n---/.exec(content.replace(/\r\n?/g, "\n"))?.[1] || "";
|
|
@@ -4908,7 +4706,7 @@ function readAdapterStamps(content) {
|
|
|
4908
4706
|
function staleNativeAdapters(params) {
|
|
4909
4707
|
const globalRoot = nativeSkillRoot(params.platform, params.homeDir);
|
|
4910
4708
|
const roots = [globalRoot];
|
|
4911
|
-
const projectRoot = params.projectDir
|
|
4709
|
+
const projectRoot = params.projectDir ? writerOwnedProjectSkillRoot(params.platform, params.projectDir, params.homeDir) : null;
|
|
4912
4710
|
if (projectRoot)
|
|
4913
4711
|
roots.push(projectRoot);
|
|
4914
4712
|
const byId = /* @__PURE__ */ new Map();
|
|
@@ -4942,7 +4740,7 @@ function staleNativeAdapters(params) {
|
|
|
4942
4740
|
}
|
|
4943
4741
|
async function refreshNativeAdapters(params) {
|
|
4944
4742
|
const platform = params.platform;
|
|
4945
|
-
if (platform !== "
|
|
4743
|
+
if (platform !== "codex" && platform !== "opencode") {
|
|
4946
4744
|
return { refreshed: 0, blocked: 0 };
|
|
4947
4745
|
}
|
|
4948
4746
|
const stale = staleNativeAdapters({ ...params, platform });
|
|
@@ -4953,7 +4751,7 @@ async function refreshNativeAdapters(params) {
|
|
|
4953
4751
|
if (!skill)
|
|
4954
4752
|
continue;
|
|
4955
4753
|
try {
|
|
4956
|
-
const result =
|
|
4754
|
+
const result = applyNativeSkillMirror({
|
|
4957
4755
|
platform,
|
|
4958
4756
|
skills: [skill],
|
|
4959
4757
|
homeDir: params.homeDir,
|
|
@@ -4970,14 +4768,14 @@ async function refreshNativeAdapters(params) {
|
|
|
4970
4768
|
}
|
|
4971
4769
|
|
|
4972
4770
|
// src/opencode-plugin.ts
|
|
4973
|
-
var DEBUG_LOG_PATH =
|
|
4771
|
+
var DEBUG_LOG_PATH = path12.join(os4.homedir(), ".greprag", "opencode-plugin-debug.log");
|
|
4974
4772
|
var _debugLogReady = false;
|
|
4975
4773
|
function dlogInit() {
|
|
4976
4774
|
if (_debugLogReady)
|
|
4977
4775
|
return;
|
|
4978
4776
|
_debugLogReady = true;
|
|
4979
4777
|
try {
|
|
4980
|
-
|
|
4778
|
+
fs11.writeFileSync(DEBUG_LOG_PATH, "");
|
|
4981
4779
|
} catch {
|
|
4982
4780
|
}
|
|
4983
4781
|
}
|
|
@@ -4986,7 +4784,7 @@ function dlog(msg) {
|
|
|
4986
4784
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [greprag-memory] ${msg}
|
|
4987
4785
|
`;
|
|
4988
4786
|
try {
|
|
4989
|
-
|
|
4787
|
+
fs11.appendFileSync(DEBUG_LOG_PATH, line);
|
|
4990
4788
|
} catch {
|
|
4991
4789
|
}
|
|
4992
4790
|
}
|
|
@@ -4994,9 +4792,9 @@ dlog(`module top reached: pid=${process.pid} argv0=${process.argv[0]} distPath=$
|
|
|
4994
4792
|
var API_URL = "https://api.greprag.com";
|
|
4995
4793
|
function loadEnvFile(filePath2) {
|
|
4996
4794
|
try {
|
|
4997
|
-
if (!
|
|
4795
|
+
if (!fs11.existsSync(filePath2))
|
|
4998
4796
|
return;
|
|
4999
|
-
const raw =
|
|
4797
|
+
const raw = fs11.readFileSync(filePath2, "utf-8");
|
|
5000
4798
|
for (const line of raw.split(/\r?\n/)) {
|
|
5001
4799
|
const trimmed = line.trim();
|
|
5002
4800
|
if (!trimmed || trimmed.startsWith("#"))
|
|
@@ -5016,12 +4814,12 @@ function loadEnvFile(filePath2) {
|
|
|
5016
4814
|
}
|
|
5017
4815
|
}
|
|
5018
4816
|
function loadGrepragEnv() {
|
|
5019
|
-
loadEnvFile(
|
|
4817
|
+
loadEnvFile(path12.join(HOME, ".greprag", ".env"));
|
|
5020
4818
|
try {
|
|
5021
|
-
const p =
|
|
5022
|
-
if (!
|
|
4819
|
+
const p = path12.join(HOME, ".claude", "settings.json");
|
|
4820
|
+
if (!fs11.existsSync(p))
|
|
5023
4821
|
return;
|
|
5024
|
-
const data = JSON.parse(
|
|
4822
|
+
const data = JSON.parse(fs11.readFileSync(p, "utf-8"));
|
|
5025
4823
|
if (data && data.env && typeof data.env === "object") {
|
|
5026
4824
|
for (const [key, val] of Object.entries(data.env)) {
|
|
5027
4825
|
if (!process.env[key] && typeof val === "string") {
|
|
@@ -5034,11 +4832,11 @@ function loadGrepragEnv() {
|
|
|
5034
4832
|
}
|
|
5035
4833
|
loadGrepragEnv();
|
|
5036
4834
|
dlog(`env loaded: MEMORY_HOOK_ENABLED=${process.env.MEMORY_HOOK_ENABLED || "<unset>"} GREPRAG_API_KEY=${process.env.GREPRAG_API_KEY ? "set" : "<unset>"} GREPRAG_OPENCODE_CAPTURE=${process.env.GREPRAG_OPENCODE_CAPTURE || "<unset>"}`);
|
|
5037
|
-
var WATCHER_LOCK =
|
|
4835
|
+
var WATCHER_LOCK = path12.join(HOME || os4.homedir(), ".greprag", "opencode-watch.lock");
|
|
5038
4836
|
var WATCHER_STALE_MS = 3e4;
|
|
5039
4837
|
function findGrepragBinary() {
|
|
5040
4838
|
const override = process.env.GREPRAG_BIN;
|
|
5041
|
-
if (override &&
|
|
4839
|
+
if (override && fs11.existsSync(override))
|
|
5042
4840
|
return override;
|
|
5043
4841
|
const binaryName = process.platform === "win32" ? "greprag.cmd" : "greprag";
|
|
5044
4842
|
try {
|
|
@@ -5047,14 +4845,14 @@ function findGrepragBinary() {
|
|
|
5047
4845
|
const candidate = line.trim();
|
|
5048
4846
|
if (!candidate)
|
|
5049
4847
|
continue;
|
|
5050
|
-
if (!
|
|
4848
|
+
if (!fs11.existsSync(candidate))
|
|
5051
4849
|
continue;
|
|
5052
4850
|
if (process.platform === "win32") {
|
|
5053
|
-
const ext =
|
|
4851
|
+
const ext = path12.extname(candidate).toLowerCase();
|
|
5054
4852
|
if (ext === ".cmd" || ext === ".exe" || ext === ".bat")
|
|
5055
4853
|
return candidate;
|
|
5056
4854
|
const cmdSibling = candidate + ".cmd";
|
|
5057
|
-
if (
|
|
4855
|
+
if (fs11.existsSync(cmdSibling))
|
|
5058
4856
|
return cmdSibling;
|
|
5059
4857
|
continue;
|
|
5060
4858
|
}
|
|
@@ -5063,19 +4861,19 @@ function findGrepragBinary() {
|
|
|
5063
4861
|
} catch {
|
|
5064
4862
|
}
|
|
5065
4863
|
const candidates = process.platform === "win32" ? [
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
4864
|
+
path12.join(HOME, "AppData", "Roaming", "npm", "greprag.cmd"),
|
|
4865
|
+
path12.join(HOME, "AppData", "Roaming", "npm", "greprag"),
|
|
4866
|
+
path12.join(HOME, "AppData", "Local", "Yarn", "bin", "greprag.cmd"),
|
|
4867
|
+
path12.join(HOME, "AppData", "Local", "pnpm", "bin", "greprag.cmd")
|
|
5070
4868
|
] : [
|
|
5071
4869
|
"/usr/local/bin/greprag",
|
|
5072
4870
|
"/opt/homebrew/bin/greprag",
|
|
5073
|
-
|
|
5074
|
-
|
|
4871
|
+
path12.join(HOME || "", ".local", "bin", "greprag"),
|
|
4872
|
+
path12.join(HOME || "", ".yarn", "bin", "greprag")
|
|
5075
4873
|
];
|
|
5076
4874
|
for (const c of candidates) {
|
|
5077
4875
|
try {
|
|
5078
|
-
if (c &&
|
|
4876
|
+
if (c && fs11.existsSync(c))
|
|
5079
4877
|
return c;
|
|
5080
4878
|
} catch {
|
|
5081
4879
|
}
|
|
@@ -5084,18 +4882,18 @@ function findGrepragBinary() {
|
|
|
5084
4882
|
}
|
|
5085
4883
|
function tryClaimWatcherLock() {
|
|
5086
4884
|
try {
|
|
5087
|
-
const fd =
|
|
4885
|
+
const fd = fs11.openSync(WATCHER_LOCK, "wx");
|
|
5088
4886
|
return { ok: true, fd };
|
|
5089
4887
|
} catch (err) {
|
|
5090
4888
|
if (err.code !== "EEXIST") {
|
|
5091
4889
|
return { ok: false, reason: "lock-create-failed" };
|
|
5092
4890
|
}
|
|
5093
4891
|
try {
|
|
5094
|
-
const stat =
|
|
4892
|
+
const stat = fs11.statSync(WATCHER_LOCK);
|
|
5095
4893
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
5096
4894
|
let pidAlive = false;
|
|
5097
4895
|
try {
|
|
5098
|
-
const pid = parseInt(
|
|
4896
|
+
const pid = parseInt(fs11.readFileSync(WATCHER_LOCK, "utf-8").trim(), 10);
|
|
5099
4897
|
pidAlive = pid > 0 && isPidAlive(pid);
|
|
5100
4898
|
} catch {
|
|
5101
4899
|
}
|
|
@@ -5103,10 +4901,10 @@ function tryClaimWatcherLock() {
|
|
|
5103
4901
|
return { ok: false, reason: "busy" };
|
|
5104
4902
|
}
|
|
5105
4903
|
try {
|
|
5106
|
-
|
|
4904
|
+
fs11.unlinkSync(WATCHER_LOCK);
|
|
5107
4905
|
} catch {
|
|
5108
4906
|
}
|
|
5109
|
-
const fd =
|
|
4907
|
+
const fd = fs11.openSync(WATCHER_LOCK, "wx");
|
|
5110
4908
|
return { ok: true, fd };
|
|
5111
4909
|
} catch (err2) {
|
|
5112
4910
|
return { ok: false, reason: "lock-stale-replace-failed" };
|
|
@@ -5147,11 +4945,11 @@ function startCaptureWatcher() {
|
|
|
5147
4945
|
return;
|
|
5148
4946
|
}
|
|
5149
4947
|
try {
|
|
5150
|
-
|
|
5151
|
-
|
|
4948
|
+
fs11.writeSync(claim.fd, String(process.pid));
|
|
4949
|
+
fs11.closeSync(claim.fd);
|
|
5152
4950
|
} catch {
|
|
5153
4951
|
try {
|
|
5154
|
-
|
|
4952
|
+
fs11.unlinkSync(WATCHER_LOCK);
|
|
5155
4953
|
} catch {
|
|
5156
4954
|
}
|
|
5157
4955
|
return;
|
|
@@ -5167,11 +4965,11 @@ function startCaptureWatcher() {
|
|
|
5167
4965
|
const claim2 = tryClaimWatcherLock();
|
|
5168
4966
|
if (claim2.ok) {
|
|
5169
4967
|
try {
|
|
5170
|
-
|
|
5171
|
-
|
|
4968
|
+
fs11.writeSync(claim2.fd, String(process.pid));
|
|
4969
|
+
fs11.closeSync(claim2.fd);
|
|
5172
4970
|
} catch {
|
|
5173
4971
|
try {
|
|
5174
|
-
|
|
4972
|
+
fs11.unlinkSync(WATCHER_LOCK);
|
|
5175
4973
|
} catch {
|
|
5176
4974
|
}
|
|
5177
4975
|
return false;
|
|
@@ -5214,7 +5012,7 @@ function startCaptureWatcher() {
|
|
|
5214
5012
|
childStartTime = Date.now();
|
|
5215
5013
|
dlog(`startCaptureWatcher: spawned 'greprag opencode watch' pid=${child.pid ?? "<none>"}`);
|
|
5216
5014
|
try {
|
|
5217
|
-
|
|
5015
|
+
fs11.writeFileSync(WATCHER_LOCK, String(child.pid));
|
|
5218
5016
|
} catch {
|
|
5219
5017
|
}
|
|
5220
5018
|
child.stderr?.on("data", (chunk2) => {
|
|
@@ -5226,9 +5024,9 @@ function startCaptureWatcher() {
|
|
|
5226
5024
|
child.on("exit", (code, signal) => {
|
|
5227
5025
|
dlog(`startCaptureWatcher: child pid=${child.pid ?? "<none>"} exited code=${code} signal=${signal || "none"}`);
|
|
5228
5026
|
try {
|
|
5229
|
-
const current =
|
|
5027
|
+
const current = fs11.readFileSync(WATCHER_LOCK, "utf-8").trim();
|
|
5230
5028
|
if (current === String(child.pid))
|
|
5231
|
-
|
|
5029
|
+
fs11.unlinkSync(WATCHER_LOCK);
|
|
5232
5030
|
} catch {
|
|
5233
5031
|
}
|
|
5234
5032
|
currentChild = null;
|
|
@@ -5257,7 +5055,7 @@ function startCaptureWatcher() {
|
|
|
5257
5055
|
`
|
|
5258
5056
|
);
|
|
5259
5057
|
try {
|
|
5260
|
-
|
|
5058
|
+
fs11.unlinkSync(WATCHER_LOCK);
|
|
5261
5059
|
} catch {
|
|
5262
5060
|
}
|
|
5263
5061
|
return;
|
|
@@ -5289,7 +5087,7 @@ function startCaptureWatcher() {
|
|
|
5289
5087
|
}, 2e3).unref();
|
|
5290
5088
|
}
|
|
5291
5089
|
try {
|
|
5292
|
-
|
|
5090
|
+
fs11.unlinkSync(WATCHER_LOCK);
|
|
5293
5091
|
} catch {
|
|
5294
5092
|
}
|
|
5295
5093
|
};
|
|
@@ -5359,7 +5157,7 @@ async function fetchSkillActivation(apiKey, cwd) {
|
|
|
5359
5157
|
rows,
|
|
5360
5158
|
apiUrl: API_URL,
|
|
5361
5159
|
apiKey,
|
|
5362
|
-
homeDir:
|
|
5160
|
+
homeDir: os4.homedir(),
|
|
5363
5161
|
projectDir: cwd
|
|
5364
5162
|
}).catch(() => void 0);
|
|
5365
5163
|
return activationFromApiRows({
|
|
@@ -5434,11 +5232,11 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
5434
5232
|
return;
|
|
5435
5233
|
}
|
|
5436
5234
|
try {
|
|
5437
|
-
|
|
5438
|
-
|
|
5235
|
+
fs11.writeSync(claim.fd, String(process.pid));
|
|
5236
|
+
fs11.closeSync(claim.fd);
|
|
5439
5237
|
} catch {
|
|
5440
5238
|
try {
|
|
5441
|
-
|
|
5239
|
+
fs11.unlinkSync(lockPath);
|
|
5442
5240
|
} catch {
|
|
5443
5241
|
}
|
|
5444
5242
|
return;
|
|
@@ -5453,11 +5251,11 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
5453
5251
|
const claim2 = tryClaimRelayLock(lockPath);
|
|
5454
5252
|
if (claim2.ok) {
|
|
5455
5253
|
try {
|
|
5456
|
-
|
|
5457
|
-
|
|
5254
|
+
fs11.writeSync(claim2.fd, String(process.pid));
|
|
5255
|
+
fs11.closeSync(claim2.fd);
|
|
5458
5256
|
} catch {
|
|
5459
5257
|
try {
|
|
5460
|
-
|
|
5258
|
+
fs11.unlinkSync(lockPath);
|
|
5461
5259
|
} catch {
|
|
5462
5260
|
}
|
|
5463
5261
|
return false;
|
|
@@ -5493,7 +5291,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
5493
5291
|
childStartTime = Date.now();
|
|
5494
5292
|
dlog(`startSessionRelay: spawned 'greprag opencode relay' pid=${child.pid ?? "<none>"}`);
|
|
5495
5293
|
try {
|
|
5496
|
-
|
|
5294
|
+
fs11.writeFileSync(lockPath, String(child.pid));
|
|
5497
5295
|
} catch {
|
|
5498
5296
|
}
|
|
5499
5297
|
child.stderr?.on("data", (chunk2) => {
|
|
@@ -5504,9 +5302,9 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
5504
5302
|
});
|
|
5505
5303
|
child.on("exit", (code, signal) => {
|
|
5506
5304
|
try {
|
|
5507
|
-
const current =
|
|
5305
|
+
const current = fs11.readFileSync(lockPath, "utf-8").trim();
|
|
5508
5306
|
if (current === String(child.pid))
|
|
5509
|
-
|
|
5307
|
+
fs11.unlinkSync(lockPath);
|
|
5510
5308
|
} catch {
|
|
5511
5309
|
}
|
|
5512
5310
|
currentChild = null;
|
|
@@ -5533,7 +5331,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
5533
5331
|
`
|
|
5534
5332
|
);
|
|
5535
5333
|
try {
|
|
5536
|
-
|
|
5334
|
+
fs11.unlinkSync(lockPath);
|
|
5537
5335
|
} catch {
|
|
5538
5336
|
}
|
|
5539
5337
|
return;
|
|
@@ -5565,7 +5363,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
5565
5363
|
}, 2e3).unref();
|
|
5566
5364
|
}
|
|
5567
5365
|
try {
|
|
5568
|
-
|
|
5366
|
+
fs11.unlinkSync(lockPath);
|
|
5569
5367
|
} catch {
|
|
5570
5368
|
}
|
|
5571
5369
|
};
|