greprag 5.74.0 → 5.74.2
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/codex-chip-hooks.js +2 -2
- package/dist/codex-chip-hooks.js.map +1 -1
- package/dist/commands/codex-chip/prompt.js +1 -1
- package/dist/commands/codex-chip/prompt.js.map +1 -1
- package/dist/commands/collision-check.js +3 -2
- package/dist/commands/collision-check.js.map +1 -1
- package/dist/commands/fix.js +2 -2
- package/dist/commands/fix.js.map +1 -1
- package/dist/commands/friction-reminder.d.ts +1 -1
- package/dist/commands/friction-reminder.js +4 -4
- package/dist/commands/friction-reminder.js.map +1 -1
- package/dist/commands/init.js +2 -2
- package/dist/commands/mechanic-spawn.d.ts +2 -1
- package/dist/commands/mechanic-spawn.js +11 -9
- package/dist/commands/mechanic-spawn.js.map +1 -1
- package/dist/commands/mechanic.js +1 -1
- package/dist/commands/opencode-chip-mission.js +1 -1
- package/dist/commands/opencode-chip-mission.js.map +1 -1
- package/dist/commands/os-primer-reminder.js +1 -1
- package/dist/commands/os-primer-reminder.js.map +1 -1
- package/dist/hook.js +21 -5
- package/dist/hook.js.map +1 -1
- package/dist/native-skill-mirror.js +12 -0
- package/dist/native-skill-mirror.js.map +1 -1
- package/dist/opencode-plugin.bundle.js +629 -104
- package/dist/opencode-plugin.js +33 -0
- package/dist/opencode-plugin.js.map +1 -1
- package/dist/procedure-runtime.d.ts +10 -0
- package/dist/procedure-runtime.js +67 -0
- package/dist/procedure-runtime.js.map +1 -1
- package/dist/procedure.d.ts +4 -5
- package/dist/procedure.js +27 -34
- package/dist/procedure.js.map +1 -1
- package/package.json +1 -1
- package/skill/greprag/SKILL.md +1 -1
- package/skill/greprag/docs/codex-chip.md +5 -3
- package/skill/mechanic/SKILL.md +13 -11
- package/skill/templates/chip-spawn.md +2 -2
- package/skill/templates/codex-chip-spawn.md +7 -3
|
@@ -23,10 +23,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/opencode-plugin.ts
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
26
|
+
var crypto5 = __toESM(require("crypto"));
|
|
27
|
+
var fs7 = __toESM(require("fs"));
|
|
28
|
+
var os2 = __toESM(require("os"));
|
|
29
|
+
var path7 = __toESM(require("path"));
|
|
30
30
|
|
|
31
31
|
// src/opencode-plugin-helpers.ts
|
|
32
32
|
var crypto = __toESM(require("crypto"));
|
|
@@ -66,13 +66,13 @@ function readAnchorFile(filePath) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
function findExistingAnchorFile(startDir) {
|
|
69
|
-
const
|
|
70
|
-
const
|
|
69
|
+
const globalAnchorPath2 = path.join(HOME, ".greprag", "project.json");
|
|
70
|
+
const legacyGlobalAnchorPath2 = path.join(HOME, ".claude", "project.json");
|
|
71
71
|
let dir = path.resolve(startDir);
|
|
72
72
|
while (true) {
|
|
73
73
|
for (const subdir of [".greprag", ".claude", ".opencode"]) {
|
|
74
74
|
const candidate = path.join(dir, subdir, "project.json");
|
|
75
|
-
if (candidate !==
|
|
75
|
+
if (candidate !== globalAnchorPath2 && candidate !== legacyGlobalAnchorPath2 && fs.existsSync(candidate)) {
|
|
76
76
|
return candidate;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -1753,7 +1753,7 @@ function buildOsPrimer(env) {
|
|
|
1753
1753
|
'\u2022 Doctrine vs state: methods ship in the CLI (`greprag load`); live state lives in the repo. A skill that depends on repo state carries a "STATE \u2014 read these first" block naming exact paths.',
|
|
1754
1754
|
"\u2022 Discoverability: every durable artifact must be findable next session \u2014 docs auto-register, skills auto-mirror, decisions get a dated ADR/decision-log entry, everything else gets its path named in the owning skill/doc. If nothing points at it, you didn't finish.",
|
|
1755
1755
|
"\u2022 Pull before derive: `greprag memory search` / `corpus search` / `load` BEFORE asking the operator or re-deriving what the project already knows. Named person/org/project/repo/customer/handle or unexplained proper noun \u2192 search Memory before guessing, unless fully defined in-turn.",
|
|
1756
|
-
`\u2022 Friction \u21D2 fix NOW: type by durable repair surface (\`harness|doctrine|injection|env|code\`) \u21D2 \`greprag fix spawn --type <type> "<unit>"\`. It PRINTS a fix-chip mission \u2014 YOU then create the visible child task with that mission as its first message (how: \`greprag load ${spawnEntry}\`, or your harness's native task tool). Repo write = isolated worktree; data-only row/diagnosis = no repo write. One unit per chip; the chip fixes
|
|
1756
|
+
`\u2022 Friction \u21D2 fix NOW: type by durable repair surface (\`harness|doctrine|injection|env|code\`) \u21D2 \`greprag fix spawn --type <type> "<unit>"\`. It PRINTS a fix-chip mission \u2014 YOU then create the visible child task with that mission as its first message (how: \`greprag load ${spawnEntry}\`, or your harness's native task tool). Repo write = isolated worktree; data-only row/diagnosis = no repo write. One unit per chip; the chip fixes, verifies, checkpoints, and hands it to the mission delivery owner. Never queue friction.`,
|
|
1757
1757
|
"\u2022 Teach the system, not the chat: explained twice by the operator \u21D2 it belongs in a skill / load entry / STATE block / ADR, not the conversation."
|
|
1758
1758
|
].join("\n");
|
|
1759
1759
|
}
|
|
@@ -2028,7 +2028,7 @@ function buildMechanicLiveNotice(_env) {
|
|
|
2028
2028
|
function buildFrictionReminder(tier, env) {
|
|
2029
2029
|
switch (tier) {
|
|
2030
2030
|
case "nag":
|
|
2031
|
-
return `\u26A0 FRICTION HIGH (live signal: repetition / errors / churn) \u2014 STOP pushing through. Name the ONE unit, choose type by repair surface, and spawn now: \`${FIX_SPAWN}\` (chip fixes
|
|
2031
|
+
return `\u26A0 FRICTION HIGH (live signal: repetition / errors / churn) \u2014 STOP pushing through. Name the ONE unit, choose type by repair surface, and spawn now: \`${FIX_SPAWN}\` (chip fixes, verifies, checkpoints, and hands it to the mission delivery owner). No friction? say "clear" + continue.`;
|
|
2032
2032
|
case "nudge":
|
|
2033
2033
|
return `\u26A0 Friction detected (live signal) \u2014 repeated yourself, fought a tool, or hit a wall? Choose type by repair surface and spawn: \`${FIX_SPAWN}\`, then keep working. Otherwise say "clear" + continue.`;
|
|
2034
2034
|
default:
|
|
@@ -2041,7 +2041,7 @@ function buildBootstrapFrictionReminder(env) {
|
|
|
2041
2041
|
return null;
|
|
2042
2042
|
const repeats = b.repeats && b.repeats > 1 ? ` repeated ${b.repeats}x` : "";
|
|
2043
2043
|
const error = b.error ? ` Last error: ${b.error}` : "";
|
|
2044
|
-
return `\u26A0 BOOTSTRAP FRICTION detected${repeats}: setup/toolchain failure (${b.signal || "setup"}) around \`${b.command || "unknown command"}\`.${error} If this is not caused by the code you just edited, spawn a fix chip now: \`greprag fix spawn --type env "${bootstrapSummary(env)}"\`. The chip owns the root-cause repair,
|
|
2044
|
+
return `\u26A0 BOOTSTRAP FRICTION detected${repeats}: setup/toolchain failure (${b.signal || "setup"}) around \`${b.command || "unknown command"}\`.${error} If this is not caused by the code you just edited, spawn a fix chip now: \`greprag fix spawn --type env "${bootstrapSummary(env)}"\`. The chip owns the root-cause repair, verifies it, and hands it to the mission delivery owner so future sessions stop hitting it.`;
|
|
2045
2045
|
}
|
|
2046
2046
|
function buildMechanicAnnounce() {
|
|
2047
2047
|
return [
|
|
@@ -2049,7 +2049,7 @@ function buildMechanicAnnounce() {
|
|
|
2049
2049
|
"WHEN friction happens \u2014 repeated yourself, fought a tool, got corrected >=2x on the same thing, rediscovered something already known, or hit a setup/toolchain failure not caused by code you just edited \u2014 spawn its fix chip NOW, then keep working. One unit = one chip; adjacent friction gets its own spawn. Never queue friction for later.",
|
|
2050
2050
|
'TYPE = durable repair surface: harness=hooks/watchers/task dispatch; doctrine=greprag load/skills/AGENTS; injection=recap/Capture/doc-pointer/stateful injection; env=bootstrap/deps/scripts/worktree setup; code=product/source behavior. Use: `greprag fix spawn --type <type> "<one unit>"`.',
|
|
2051
2051
|
"WORKSPACE ROUTING: `fix spawn` detects usable Git history before dispatch \u2014 Git uses an isolated worktree; non-Git, unavailable Git, or no commit uses the project-local task with serialized writes. No fail-then-fallback attempt.",
|
|
2052
|
-
"THE CHIP'S CONTRACT (it self-enforces; you just spawn): identify the exact friction \u2192 make the smallest durable root-cause fix \u2192 explain the
|
|
2052
|
+
"THE CHIP'S CONTRACT (it self-enforces; you just spawn): identify the exact friction \u2192 make the smallest durable root-cause fix \u2192 explain and verify it \u2192 checkpoint \u2192 hand it to the mission delivery owner. With no live parent and a full-goal mission, the chip becomes delivery owner and follows the repo profile. No second lifecycle approval.",
|
|
2053
2053
|
"Every repair is ROOT-CAUSE \u2014 fix the pattern that makes the friction class possible, never a guard on today's trigger.",
|
|
2054
2054
|
'A per-turn reminder fires ONLY when live friction is DETECTED (the stress signal \u2014 repetition / errors / churn), never on a timer \u2014 act on it, or say "clear" and continue.'
|
|
2055
2055
|
].join("\n");
|
|
@@ -2648,59 +2648,160 @@ function effectiveInstall(store, proc) {
|
|
|
2648
2648
|
function defaultProcedureSettings(proc) {
|
|
2649
2649
|
return Object.fromEntries(Object.entries(proc.settingsSchema ?? {}).map(([key, spec]) => [key, spec.default]));
|
|
2650
2650
|
}
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2651
|
+
function triggerHits(promptLower, triggerLower) {
|
|
2652
|
+
if (!triggerLower)
|
|
2653
|
+
return false;
|
|
2654
|
+
if (/\s/.test(triggerLower))
|
|
2655
|
+
return promptLower.includes(triggerLower);
|
|
2656
|
+
const re = new RegExp(`(^|[^a-z0-9])${escapeRegex(triggerLower)}([^a-z0-9]|$)`);
|
|
2657
|
+
return re.test(promptLower);
|
|
2658
|
+
}
|
|
2659
|
+
function escapeRegex(s) {
|
|
2660
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2661
|
+
}
|
|
2662
|
+
var INTENT_NEGATIONS = [
|
|
2663
|
+
"don't",
|
|
2664
|
+
"dont",
|
|
2665
|
+
"not",
|
|
2666
|
+
"no",
|
|
2667
|
+
"never",
|
|
2668
|
+
"without",
|
|
2669
|
+
"avoid",
|
|
2670
|
+
"won't",
|
|
2671
|
+
"wont",
|
|
2672
|
+
"didn't",
|
|
2673
|
+
"didnt",
|
|
2674
|
+
"cannot",
|
|
2675
|
+
"can't",
|
|
2676
|
+
"cant",
|
|
2677
|
+
"stop"
|
|
2678
|
+
];
|
|
2679
|
+
var INTENT_COLLISION_PHRASES = [
|
|
2680
|
+
"push back",
|
|
2681
|
+
"push for",
|
|
2682
|
+
"push notification",
|
|
2683
|
+
"push notifications",
|
|
2684
|
+
"pushover",
|
|
2685
|
+
"merge conflict",
|
|
2686
|
+
"merge conflicts",
|
|
2687
|
+
"merge request",
|
|
2688
|
+
"merge sort",
|
|
2689
|
+
"press release",
|
|
2690
|
+
"release notes",
|
|
2691
|
+
"release candidate",
|
|
2692
|
+
"release date",
|
|
2693
|
+
"commit message",
|
|
2694
|
+
"commit hash",
|
|
2695
|
+
"commit history",
|
|
2696
|
+
"commit sha"
|
|
2697
|
+
];
|
|
2698
|
+
function intentSuppressed(promptLower, triggerLower) {
|
|
2699
|
+
for (const phrase of INTENT_COLLISION_PHRASES) {
|
|
2700
|
+
if (phrase.includes(triggerLower) && promptLower.includes(phrase)) {
|
|
2701
|
+
const stripped = promptLower.split(phrase).join(" \xB7 ");
|
|
2702
|
+
if (!triggerHits(stripped, triggerLower))
|
|
2703
|
+
return true;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
const idx = promptLower.search(new RegExp(`(^|[^a-z0-9])${escapeRegex(triggerLower)}([^a-z0-9]|$)`));
|
|
2707
|
+
if (idx >= 0) {
|
|
2708
|
+
const before = promptLower.slice(0, idx).split(/[^a-z']+/).filter(Boolean);
|
|
2709
|
+
const window = " " + before.slice(-3).join(" ") + " ";
|
|
2710
|
+
if (INTENT_NEGATIONS.some((neg) => window.includes(" " + neg + " ")))
|
|
2711
|
+
return true;
|
|
2712
|
+
}
|
|
2713
|
+
return false;
|
|
2714
|
+
}
|
|
2715
|
+
function matchProcedure(prompt, store, opts = {}) {
|
|
2716
|
+
if (!prompt)
|
|
2717
|
+
return null;
|
|
2718
|
+
const promptLower = prompt.toLowerCase();
|
|
2719
|
+
let best = null;
|
|
2720
|
+
let bestLen = -1;
|
|
2721
|
+
for (const procedure of store.procedures) {
|
|
2722
|
+
if (!opts.includeInactive && effectiveInstall(store, procedure).state !== "active")
|
|
2723
|
+
continue;
|
|
2724
|
+
for (const trigger of procedure.triggers) {
|
|
2725
|
+
const t = trigger.toLowerCase();
|
|
2726
|
+
if (!triggerHits(promptLower, t))
|
|
2727
|
+
continue;
|
|
2728
|
+
if (!opts.ignoreIntentGuard && intentSuppressed(promptLower, t))
|
|
2729
|
+
continue;
|
|
2730
|
+
if (t.length > bestLen) {
|
|
2731
|
+
best = { procedure, trigger };
|
|
2732
|
+
bestLen = t.length;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
return best;
|
|
2737
|
+
}
|
|
2738
|
+
function buildProcedureInjection(proc) {
|
|
2739
|
+
const lines = [];
|
|
2740
|
+
const tag = proc.destructive ? " \u26A0 DESTRUCTIVE" : "";
|
|
2741
|
+
lines.push(`[Procedure: ${proc.verb} \u2014 ${proc.status}${tag}] This repo has a known recipe for "${proc.verb}".`);
|
|
2742
|
+
if (proc.preconditions)
|
|
2743
|
+
lines.push(` PRECONDITIONS: ${proc.preconditions}`);
|
|
2744
|
+
if (proc.destructive && proc.caveats) {
|
|
2745
|
+
lines.push(` \u26D4 GATE: ${proc.caveats}`);
|
|
2746
|
+
lines.push(` STEPS (only after approval): ${proc.steps}`);
|
|
2747
|
+
} else {
|
|
2748
|
+
lines.push(` STEPS: ${proc.steps}`);
|
|
2749
|
+
if (proc.caveats)
|
|
2750
|
+
lines.push(` GATE: ${proc.caveats}`);
|
|
2751
|
+
}
|
|
2752
|
+
if (proc.endpoint)
|
|
2753
|
+
lines.push(` ENDPOINT (done when): ${proc.endpoint}`);
|
|
2754
|
+
lines.push(
|
|
2755
|
+
proc.destructive ? ` Follow the procedure's gate for effects outside ordinary repo delivery. For commit/merge/push/deploy/release, the current full-goal mission and Delivery Profile are the authority; Procedure adds no second approval.` : ` Use this recipe. If it no longer works, say so \u2014 that's a stale procedure to re-learn.`
|
|
2756
|
+
);
|
|
2757
|
+
return lines.join("\n");
|
|
2758
|
+
}
|
|
2759
|
+
var LIFECYCLE_SHADOW_PRECONDITION = "Shadow evidence only. The Delivery Profile and canonical lifecycle skills own active repo-boundary behavior.";
|
|
2659
2760
|
var GIT_SEED_SET = [
|
|
2660
2761
|
{
|
|
2661
2762
|
verb: "commit",
|
|
2662
2763
|
triggers: ["commit", "check in", "check it in", "checkpoint this", "land it"],
|
|
2663
|
-
preconditions:
|
|
2664
|
-
steps:
|
|
2665
|
-
caveats: "
|
|
2666
|
-
endpoint: "
|
|
2764
|
+
preconditions: LIFECYCLE_SHADOW_PRECONDITION,
|
|
2765
|
+
steps: "Shadow capture only. Active behavior resolves the repo Delivery Profile and canonical commit skill.",
|
|
2766
|
+
caveats: "No lifecycle guidance is injected from Procedure while the announce-first pilot is active.",
|
|
2767
|
+
endpoint: "observed checkpoint/integration span is no longer live",
|
|
2667
2768
|
status: "seeded"
|
|
2668
2769
|
},
|
|
2669
2770
|
{
|
|
2670
2771
|
verb: "merge",
|
|
2671
2772
|
triggers: ["merge", "merge the branch", "merge to main", "merge into main"],
|
|
2672
|
-
preconditions:
|
|
2673
|
-
steps: "
|
|
2674
|
-
caveats: "
|
|
2675
|
-
endpoint: "
|
|
2773
|
+
preconditions: LIFECYCLE_SHADOW_PRECONDITION,
|
|
2774
|
+
steps: "Shadow capture only. Active behavior resolves the profile integration strategy and coordinates peers through the harness adapter.",
|
|
2775
|
+
caveats: "Procedure records evidence; it does not choose a branch or merge strategy.",
|
|
2776
|
+
endpoint: "observed integration span is no longer live",
|
|
2676
2777
|
status: "seeded"
|
|
2677
2778
|
},
|
|
2678
2779
|
{
|
|
2679
2780
|
verb: "push",
|
|
2680
2781
|
triggers: ["push", "git push", "push to remote", "push it up", "push upstream"],
|
|
2681
|
-
preconditions:
|
|
2682
|
-
steps: "
|
|
2683
|
-
caveats: "
|
|
2684
|
-
endpoint: "
|
|
2782
|
+
preconditions: LIFECYCLE_SHADOW_PRECONDITION,
|
|
2783
|
+
steps: "Shadow capture only. Active behavior follows the repo profile's remote, default branch, and push/deploy coupling.",
|
|
2784
|
+
caveats: "Seed-only destructive vocabulary; never learn or inject a push command.",
|
|
2785
|
+
endpoint: "observed push span is no longer live",
|
|
2685
2786
|
status: "seeded",
|
|
2686
2787
|
destructive: true
|
|
2687
2788
|
},
|
|
2688
2789
|
{
|
|
2689
2790
|
verb: "deploy",
|
|
2690
2791
|
triggers: ["deploy", "deploy the api", "deploy the worker", "redeploy"],
|
|
2691
|
-
preconditions:
|
|
2692
|
-
steps: "
|
|
2693
|
-
caveats: "
|
|
2694
|
-
endpoint: "
|
|
2792
|
+
preconditions: LIFECYCLE_SHADOW_PRECONDITION,
|
|
2793
|
+
steps: "Shadow capture only. Active behavior resolves `.greprag/delivery.json` and its declared deploy document.",
|
|
2794
|
+
caveats: "Procedure does not infer, learn, or inject provider commands during the pilot.",
|
|
2795
|
+
endpoint: "observed deploy/verification span is no longer live",
|
|
2695
2796
|
status: "seeded"
|
|
2696
2797
|
},
|
|
2697
2798
|
{
|
|
2698
2799
|
verb: "release",
|
|
2699
2800
|
triggers: ["release", "ship it", "ship a release", "cut a release", "publish the release", "do a release"],
|
|
2700
|
-
preconditions:
|
|
2701
|
-
steps: "
|
|
2702
|
-
caveats: "
|
|
2703
|
-
endpoint: "
|
|
2801
|
+
preconditions: LIFECYCLE_SHADOW_PRECONDITION,
|
|
2802
|
+
steps: "Shadow capture only. Active behavior follows the repo profile's release document and canonical release skill.",
|
|
2803
|
+
caveats: "Procedure does not infer package, tag, registry, provider, or version policy.",
|
|
2804
|
+
endpoint: "observed release/verification span is no longer live",
|
|
2704
2805
|
status: "seeded"
|
|
2705
2806
|
}
|
|
2706
2807
|
];
|
|
@@ -2728,6 +2829,404 @@ function activeProcedureAnnounces(store) {
|
|
|
2728
2829
|
return lines;
|
|
2729
2830
|
}
|
|
2730
2831
|
|
|
2832
|
+
// src/procedure-runtime.ts
|
|
2833
|
+
var crypto4 = __toESM(require("crypto"));
|
|
2834
|
+
|
|
2835
|
+
// src/project-anchor.ts
|
|
2836
|
+
var path3 = __toESM(require("path"));
|
|
2837
|
+
var fs3 = __toESM(require("fs"));
|
|
2838
|
+
var crypto2 = __toESM(require("crypto"));
|
|
2839
|
+
var os = __toESM(require("os"));
|
|
2840
|
+
var ANCHOR_DIR = ".greprag";
|
|
2841
|
+
var ANCHOR_FILE = "project.json";
|
|
2842
|
+
var LEGACY_ANCHOR_DIR = ".claude";
|
|
2843
|
+
function anchorPathIn(dir) {
|
|
2844
|
+
return path3.join(dir, ANCHOR_DIR, ANCHOR_FILE);
|
|
2845
|
+
}
|
|
2846
|
+
function legacyAnchorPathIn(dir) {
|
|
2847
|
+
return path3.join(dir, LEGACY_ANCHOR_DIR, ANCHOR_FILE);
|
|
2848
|
+
}
|
|
2849
|
+
function globalAnchorPath() {
|
|
2850
|
+
return anchorPathIn(os.homedir());
|
|
2851
|
+
}
|
|
2852
|
+
function legacyGlobalAnchorPath() {
|
|
2853
|
+
return legacyAnchorPathIn(os.homedir());
|
|
2854
|
+
}
|
|
2855
|
+
function findExistingAnchor(startDir) {
|
|
2856
|
+
const homeAnchor = globalAnchorPath();
|
|
2857
|
+
const legacyHomeAnchor = legacyGlobalAnchorPath();
|
|
2858
|
+
let dir = path3.resolve(startDir);
|
|
2859
|
+
while (true) {
|
|
2860
|
+
const candidate = anchorPathIn(dir);
|
|
2861
|
+
if (candidate !== homeAnchor && fs3.existsSync(candidate))
|
|
2862
|
+
return candidate;
|
|
2863
|
+
const legacyCandidate = legacyAnchorPathIn(dir);
|
|
2864
|
+
if (legacyCandidate !== legacyHomeAnchor && fs3.existsSync(legacyCandidate))
|
|
2865
|
+
return legacyCandidate;
|
|
2866
|
+
const parent = path3.dirname(dir);
|
|
2867
|
+
if (parent === dir)
|
|
2868
|
+
return null;
|
|
2869
|
+
dir = parent;
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
function isEphemeralCwd2(cwd) {
|
|
2873
|
+
const norm = path3.resolve(cwd).replace(/\\/g, "/").toLowerCase();
|
|
2874
|
+
if (norm.includes("/appdata/roaming/claude/local-agent-mode-sessions/"))
|
|
2875
|
+
return true;
|
|
2876
|
+
if (norm.includes("/appdata/local/claude/local-agent-mode-sessions/"))
|
|
2877
|
+
return true;
|
|
2878
|
+
if (norm.startsWith("/tmp/"))
|
|
2879
|
+
return true;
|
|
2880
|
+
if (norm.startsWith("/var/tmp/"))
|
|
2881
|
+
return true;
|
|
2882
|
+
if (norm.startsWith("/private/tmp/"))
|
|
2883
|
+
return true;
|
|
2884
|
+
return false;
|
|
2885
|
+
}
|
|
2886
|
+
function computeGitDerivedProjectId2(workingDir) {
|
|
2887
|
+
try {
|
|
2888
|
+
const out = safeExecSync("git rev-list --max-parents=0 HEAD", {
|
|
2889
|
+
cwd: workingDir,
|
|
2890
|
+
encoding: "utf-8",
|
|
2891
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2892
|
+
});
|
|
2893
|
+
const roots = out.trim().split(/\s+/).filter(Boolean).sort();
|
|
2894
|
+
if (roots.length === 0)
|
|
2895
|
+
return null;
|
|
2896
|
+
const hash = crypto2.createHash("sha256").update(roots.join("\n")).digest("hex");
|
|
2897
|
+
return [
|
|
2898
|
+
hash.slice(0, 8),
|
|
2899
|
+
hash.slice(8, 12),
|
|
2900
|
+
"4" + hash.slice(13, 16),
|
|
2901
|
+
// version 4 nibble
|
|
2902
|
+
"8" + hash.slice(17, 20),
|
|
2903
|
+
// variant nibble
|
|
2904
|
+
hash.slice(20, 32)
|
|
2905
|
+
].join("-");
|
|
2906
|
+
} catch {
|
|
2907
|
+
return null;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
function deterministicProjectId2(workingDir) {
|
|
2911
|
+
const normalized = path3.resolve(workingDir).toLowerCase();
|
|
2912
|
+
const hash = crypto2.createHash("sha256").update(normalized).digest("hex");
|
|
2913
|
+
return [
|
|
2914
|
+
hash.slice(0, 8),
|
|
2915
|
+
hash.slice(8, 12),
|
|
2916
|
+
"4" + hash.slice(13, 16),
|
|
2917
|
+
// version 4 nibble
|
|
2918
|
+
"8" + hash.slice(17, 20),
|
|
2919
|
+
// variant nibble
|
|
2920
|
+
hash.slice(20, 32)
|
|
2921
|
+
].join("-");
|
|
2922
|
+
}
|
|
2923
|
+
function tryReadAnchorFileContents(filePath) {
|
|
2924
|
+
try {
|
|
2925
|
+
const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
2926
|
+
const notifyRaw = raw.inbox_notify;
|
|
2927
|
+
const inboxNotify = notifyRaw === "off" || notifyRaw === "session_start_only" ? notifyRaw : "every_turn";
|
|
2928
|
+
return {
|
|
2929
|
+
projectId: raw.project_id,
|
|
2930
|
+
projectName: raw.project_name,
|
|
2931
|
+
created: raw.created,
|
|
2932
|
+
memoryCapture: raw.memory_capture !== false,
|
|
2933
|
+
sessionStartRecap: raw.session_start_recap !== false,
|
|
2934
|
+
inboxNotify,
|
|
2935
|
+
emailDir: typeof raw.email_dir === "string" ? raw.email_dir : void 0,
|
|
2936
|
+
emailAutosave: raw.email_autosave === true,
|
|
2937
|
+
role: typeof raw.role === "string" ? raw.role : void 0
|
|
2938
|
+
};
|
|
2939
|
+
} catch {
|
|
2940
|
+
return null;
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
function readAnchor2(cwd) {
|
|
2944
|
+
const existingPath = findExistingAnchor(cwd);
|
|
2945
|
+
const fileContents = existingPath ? tryReadAnchorFileContents(existingPath) : null;
|
|
2946
|
+
if (existingPath && fileContents && fileContents.projectId && fileContents.projectName) {
|
|
2947
|
+
return {
|
|
2948
|
+
projectId: fileContents.projectId,
|
|
2949
|
+
projectName: fileContents.projectName,
|
|
2950
|
+
initialized: true,
|
|
2951
|
+
source: "file",
|
|
2952
|
+
anchorPath: existingPath,
|
|
2953
|
+
created: fileContents.created,
|
|
2954
|
+
memoryCapture: fileContents.memoryCapture,
|
|
2955
|
+
sessionStartRecap: fileContents.sessionStartRecap,
|
|
2956
|
+
inboxNotify: fileContents.inboxNotify,
|
|
2957
|
+
emailDir: fileContents.emailDir,
|
|
2958
|
+
emailAutosave: fileContents.emailAutosave,
|
|
2959
|
+
role: fileContents.role
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
const gitId = computeGitDerivedProjectId2(cwd);
|
|
2963
|
+
if (gitId) {
|
|
2964
|
+
const root2 = path3.resolve(cwd);
|
|
2965
|
+
return {
|
|
2966
|
+
projectId: gitId,
|
|
2967
|
+
projectName: fileContents?.projectName || path3.basename(root2).toLowerCase(),
|
|
2968
|
+
initialized: true,
|
|
2969
|
+
source: "git",
|
|
2970
|
+
anchorPath: existingPath || anchorPathIn(root2),
|
|
2971
|
+
created: fileContents?.created,
|
|
2972
|
+
memoryCapture: fileContents?.memoryCapture ?? true,
|
|
2973
|
+
sessionStartRecap: fileContents?.sessionStartRecap ?? true,
|
|
2974
|
+
inboxNotify: fileContents?.inboxNotify ?? "every_turn",
|
|
2975
|
+
emailDir: fileContents?.emailDir,
|
|
2976
|
+
emailAutosave: fileContents?.emailAutosave,
|
|
2977
|
+
role: fileContents?.role
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
if (isEphemeralCwd2(cwd)) {
|
|
2981
|
+
const globalPath = fs3.existsSync(globalAnchorPath()) ? globalAnchorPath() : legacyGlobalAnchorPath();
|
|
2982
|
+
const globalContents = tryReadAnchorFileContents(globalPath);
|
|
2983
|
+
if (globalContents && globalContents.projectId && globalContents.projectName) {
|
|
2984
|
+
return {
|
|
2985
|
+
projectId: globalContents.projectId,
|
|
2986
|
+
projectName: globalContents.projectName,
|
|
2987
|
+
initialized: true,
|
|
2988
|
+
source: "global",
|
|
2989
|
+
anchorPath: globalPath,
|
|
2990
|
+
created: globalContents.created,
|
|
2991
|
+
memoryCapture: globalContents.memoryCapture,
|
|
2992
|
+
sessionStartRecap: globalContents.sessionStartRecap,
|
|
2993
|
+
inboxNotify: globalContents.inboxNotify,
|
|
2994
|
+
emailDir: globalContents.emailDir,
|
|
2995
|
+
emailAutosave: globalContents.emailAutosave,
|
|
2996
|
+
role: globalContents.role
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
const root = path3.resolve(cwd);
|
|
3001
|
+
return {
|
|
3002
|
+
projectId: deterministicProjectId2(root),
|
|
3003
|
+
projectName: fileContents?.projectName || path3.basename(root).toLowerCase(),
|
|
3004
|
+
initialized: false,
|
|
3005
|
+
source: "hash",
|
|
3006
|
+
anchorPath: existingPath || anchorPathIn(root),
|
|
3007
|
+
created: fileContents?.created,
|
|
3008
|
+
memoryCapture: fileContents?.memoryCapture ?? true,
|
|
3009
|
+
sessionStartRecap: fileContents?.sessionStartRecap ?? true,
|
|
3010
|
+
inboxNotify: fileContents?.inboxNotify ?? "every_turn",
|
|
3011
|
+
emailDir: fileContents?.emailDir,
|
|
3012
|
+
emailAutosave: fileContents?.emailAutosave,
|
|
3013
|
+
role: fileContents?.role
|
|
3014
|
+
};
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
// src/procedure-watch.ts
|
|
3018
|
+
var path4 = __toESM(require("path"));
|
|
3019
|
+
var fs4 = __toESM(require("fs"));
|
|
3020
|
+
var TIER1_LEARN_TRIGGERS = [
|
|
3021
|
+
{ verb: "deploy", triggers: ["deploy", "deploy the api", "deploy the worker", "redeploy"], steps: "", status: "seeded" },
|
|
3022
|
+
{ verb: "push", triggers: ["push", "git push", "push to remote", "push it up", "push upstream"], steps: "", status: "seeded", destructive: true },
|
|
3023
|
+
{ verb: "release", triggers: ["release", "ship", "ship it", "ship a release", "cut a release", "publish the release", "do a release"], steps: "", status: "seeded" },
|
|
3024
|
+
{ verb: "build", triggers: ["build", "rebuild", "compile", "run the build"], steps: "", status: "seeded" },
|
|
3025
|
+
{ verb: "test", triggers: ["test", "tests", "run the tests", "run tests"], steps: "", status: "seeded" }
|
|
3026
|
+
];
|
|
3027
|
+
function matchLearnTrigger(prompt) {
|
|
3028
|
+
return matchProcedure(prompt, {
|
|
3029
|
+
version: PROCEDURE_STORE_VERSION,
|
|
3030
|
+
procedures: TIER1_LEARN_TRIGGERS
|
|
3031
|
+
});
|
|
3032
|
+
}
|
|
3033
|
+
function isDestructiveVerb(verb, store) {
|
|
3034
|
+
if (store.procedures.some((p) => p.verb === verb && p.destructive))
|
|
3035
|
+
return true;
|
|
3036
|
+
return TIER1_LEARN_TRIGGERS.some((p) => p.verb === verb && p.destructive);
|
|
3037
|
+
}
|
|
3038
|
+
function openWatch(verb, phase, openedAt, shadowRunId) {
|
|
3039
|
+
return {
|
|
3040
|
+
verb,
|
|
3041
|
+
phase,
|
|
3042
|
+
shadowRunId,
|
|
3043
|
+
openedAt: openedAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
3044
|
+
turnCount: 0,
|
|
3045
|
+
observations: [],
|
|
3046
|
+
lastLiveTurn: 0,
|
|
3047
|
+
offStreak: 0
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
function stateDir2() {
|
|
3051
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
3052
|
+
return path4.join(home, ".greprag", "state");
|
|
3053
|
+
}
|
|
3054
|
+
function procedureWatchPath(projectId) {
|
|
3055
|
+
return path4.join(stateDir2(), `procedure-watch-${projectId}.json`);
|
|
3056
|
+
}
|
|
3057
|
+
function hasProcedureWatch(projectId) {
|
|
3058
|
+
try {
|
|
3059
|
+
return fs4.existsSync(procedureWatchPath(projectId));
|
|
3060
|
+
} catch {
|
|
3061
|
+
return false;
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
function writeProcedureWatch(projectId, watch) {
|
|
3065
|
+
const file = procedureWatchPath(projectId);
|
|
3066
|
+
const dir = path4.dirname(file);
|
|
3067
|
+
if (!fs4.existsSync(dir))
|
|
3068
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
3069
|
+
fs4.writeFileSync(file, JSON.stringify(watch, null, 2) + "\n");
|
|
3070
|
+
}
|
|
3071
|
+
function openWatchIfIdle(projectId, verb, phase, shadowRunId) {
|
|
3072
|
+
if (hasProcedureWatch(projectId))
|
|
3073
|
+
return false;
|
|
3074
|
+
writeProcedureWatch(projectId, openWatch(verb, phase, void 0, shadowRunId));
|
|
3075
|
+
return true;
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
// src/procedure-shadow.ts
|
|
3079
|
+
var crypto3 = __toESM(require("crypto"));
|
|
3080
|
+
var fs5 = __toESM(require("fs"));
|
|
3081
|
+
var path5 = __toESM(require("path"));
|
|
3082
|
+
function stateDir3() {
|
|
3083
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
3084
|
+
return path5.join(home, ".greprag", "state");
|
|
3085
|
+
}
|
|
3086
|
+
function procedureShadowPath(projectId) {
|
|
3087
|
+
return path5.join(stateDir3(), `procedure-shadow-${projectId}.jsonl`);
|
|
3088
|
+
}
|
|
3089
|
+
function appendShadowEvent(projectId, event) {
|
|
3090
|
+
try {
|
|
3091
|
+
const file = procedureShadowPath(projectId);
|
|
3092
|
+
fs5.mkdirSync(path5.dirname(file), { recursive: true });
|
|
3093
|
+
fs5.appendFileSync(file, JSON.stringify(event) + "\n");
|
|
3094
|
+
} catch {
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
function createProcedureShadowRunId() {
|
|
3098
|
+
return crypto3.randomUUID();
|
|
3099
|
+
}
|
|
3100
|
+
function procedureFingerprint(proc) {
|
|
3101
|
+
const payload = JSON.stringify({
|
|
3102
|
+
verb: proc.verb,
|
|
3103
|
+
triggers: proc.triggers,
|
|
3104
|
+
preconditions: proc.preconditions,
|
|
3105
|
+
steps: proc.steps,
|
|
3106
|
+
caveats: proc.caveats,
|
|
3107
|
+
endpoint: proc.endpoint,
|
|
3108
|
+
definitionVersion: proc.definitionVersion ?? 1
|
|
3109
|
+
});
|
|
3110
|
+
return crypto3.createHash("sha256").update(payload).digest("hex");
|
|
3111
|
+
}
|
|
3112
|
+
function recordProcedureShadowTrigger(params) {
|
|
3113
|
+
appendShadowEvent(params.projectId, {
|
|
3114
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3115
|
+
kind: "would-have-fired",
|
|
3116
|
+
runId: params.runId,
|
|
3117
|
+
projectId: params.projectId,
|
|
3118
|
+
verb: params.match.procedure.verb,
|
|
3119
|
+
trigger: params.match.trigger,
|
|
3120
|
+
procedureStatus: params.match.procedure.status,
|
|
3121
|
+
definitionVersion: params.match.procedure.definitionVersion ?? 1,
|
|
3122
|
+
configuredState: params.configuredState,
|
|
3123
|
+
runtimeState: "shadow",
|
|
3124
|
+
schemaFingerprint: procedureFingerprint(params.match.procedure),
|
|
3125
|
+
watchOpened: params.watchOpened,
|
|
3126
|
+
sessionId: params.sessionId || void 0
|
|
3127
|
+
});
|
|
3128
|
+
}
|
|
3129
|
+
function recordMissingLifecycleShadowTrigger(params) {
|
|
3130
|
+
appendShadowEvent(params.projectId, {
|
|
3131
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3132
|
+
kind: "would-have-fired",
|
|
3133
|
+
runId: params.runId,
|
|
3134
|
+
projectId: params.projectId,
|
|
3135
|
+
verb: params.verb,
|
|
3136
|
+
trigger: params.trigger,
|
|
3137
|
+
configuredState: "missing",
|
|
3138
|
+
runtimeState: "shadow",
|
|
3139
|
+
watchOpened: false,
|
|
3140
|
+
sessionId: params.sessionId || void 0
|
|
3141
|
+
});
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
// src/procedure-runtime.ts
|
|
3145
|
+
function extractLatestOpenCodeProcedurePrompt(messages) {
|
|
3146
|
+
let userCount = 0;
|
|
3147
|
+
let latest = null;
|
|
3148
|
+
for (const raw of messages) {
|
|
3149
|
+
if (!raw || typeof raw !== "object")
|
|
3150
|
+
continue;
|
|
3151
|
+
const msg = raw;
|
|
3152
|
+
if (msg.info?.role !== "user" || !Array.isArray(msg.parts))
|
|
3153
|
+
continue;
|
|
3154
|
+
userCount++;
|
|
3155
|
+
const parts = msg.parts;
|
|
3156
|
+
const prompt = parts.filter((part) => !!part && typeof part === "object").filter((part) => part.type === "text" && typeof part.text === "string" && part.synthetic !== true && part.ignored !== true).map((part) => String(part.text)).join("\n").trim();
|
|
3157
|
+
if (prompt)
|
|
3158
|
+
latest = { prompt, parts };
|
|
3159
|
+
}
|
|
3160
|
+
if (!latest)
|
|
3161
|
+
return null;
|
|
3162
|
+
const digest = crypto4.createHash("sha256").update(latest.prompt).digest("hex");
|
|
3163
|
+
return { ...latest, key: `${userCount}:${digest}` };
|
|
3164
|
+
}
|
|
3165
|
+
function procedureRuntimeState(configuredState, proc) {
|
|
3166
|
+
if (configuredState === "disabled")
|
|
3167
|
+
return "disabled";
|
|
3168
|
+
return isDeliveryLifecycleVerb(proc.verb) ? "shadow" : configuredState;
|
|
3169
|
+
}
|
|
3170
|
+
function runProcedurePromptCheck(params) {
|
|
3171
|
+
try {
|
|
3172
|
+
if (!params.prompt.trim())
|
|
3173
|
+
return { mode: "none" };
|
|
3174
|
+
const anchor = readAnchor2(params.cwd);
|
|
3175
|
+
if (!anchor.projectId)
|
|
3176
|
+
return { mode: "none" };
|
|
3177
|
+
const store = readProcedureStore(anchor.projectId);
|
|
3178
|
+
const raw = store.procedures.length ? matchProcedure(params.prompt, store, { includeInactive: true }) : null;
|
|
3179
|
+
if (raw) {
|
|
3180
|
+
const configuredState = effectiveInstall(store, raw.procedure).state;
|
|
3181
|
+
const runtimeState = procedureRuntimeState(configuredState, raw.procedure);
|
|
3182
|
+
if (runtimeState === "disabled") {
|
|
3183
|
+
return { mode: "disabled", verb: raw.procedure.verb };
|
|
3184
|
+
}
|
|
3185
|
+
if (runtimeState === "shadow") {
|
|
3186
|
+
const runId = createProcedureShadowRunId();
|
|
3187
|
+
const watchOpened = raw.procedure.status !== "stale" ? openWatchIfIdle(anchor.projectId, raw.procedure.verb, "SHADOW", runId) : false;
|
|
3188
|
+
recordProcedureShadowTrigger({
|
|
3189
|
+
projectId: anchor.projectId,
|
|
3190
|
+
runId,
|
|
3191
|
+
match: raw,
|
|
3192
|
+
configuredState,
|
|
3193
|
+
watchOpened,
|
|
3194
|
+
sessionId: params.sessionId
|
|
3195
|
+
});
|
|
3196
|
+
return { mode: "shadow", verb: raw.procedure.verb };
|
|
3197
|
+
}
|
|
3198
|
+
if (raw.procedure.status !== "stale") {
|
|
3199
|
+
openWatchIfIdle(anchor.projectId, raw.procedure.verb, "REPLAY");
|
|
3200
|
+
return {
|
|
3201
|
+
mode: "active",
|
|
3202
|
+
verb: raw.procedure.verb,
|
|
3203
|
+
additionalContext: buildProcedureInjection(raw.procedure)
|
|
3204
|
+
};
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
const learned = raw || matchLearnTrigger(params.prompt);
|
|
3208
|
+
if (!learned)
|
|
3209
|
+
return { mode: "none" };
|
|
3210
|
+
if (isDeliveryLifecycleVerb(learned.procedure.verb)) {
|
|
3211
|
+
recordMissingLifecycleShadowTrigger({
|
|
3212
|
+
projectId: anchor.projectId,
|
|
3213
|
+
runId: createProcedureShadowRunId(),
|
|
3214
|
+
verb: learned.procedure.verb,
|
|
3215
|
+
trigger: learned.trigger,
|
|
3216
|
+
sessionId: params.sessionId
|
|
3217
|
+
});
|
|
3218
|
+
return { mode: "shadow", verb: learned.procedure.verb };
|
|
3219
|
+
}
|
|
3220
|
+
if (isDestructiveVerb(learned.procedure.verb, store)) {
|
|
3221
|
+
return { mode: "disabled", verb: learned.procedure.verb };
|
|
3222
|
+
}
|
|
3223
|
+
openWatchIfIdle(anchor.projectId, learned.procedure.verb, "LEARN");
|
|
3224
|
+
return { mode: "learn", verb: learned.procedure.verb };
|
|
3225
|
+
} catch {
|
|
3226
|
+
return { mode: "none" };
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
|
|
2731
3230
|
// src/archive/glyph-font.ts
|
|
2732
3231
|
var GLYPH_W = 8;
|
|
2733
3232
|
var GLYPH_H = 8;
|
|
@@ -3311,29 +3810,29 @@ function recodeMessagesToPng(messages, opts) {
|
|
|
3311
3810
|
}
|
|
3312
3811
|
|
|
3313
3812
|
// src/skill-activation-manifest.ts
|
|
3314
|
-
var
|
|
3315
|
-
var
|
|
3813
|
+
var fs6 = __toESM(require("fs"));
|
|
3814
|
+
var path6 = __toESM(require("path"));
|
|
3316
3815
|
var MAX_NATIVE_SKILL_FILES = 1500;
|
|
3317
3816
|
var MAX_SCAN_DEPTH = 7;
|
|
3318
3817
|
function homeRoots(homeDir, platform) {
|
|
3319
3818
|
if (platform === "claude-code") {
|
|
3320
|
-
return [
|
|
3819
|
+
return [path6.join(homeDir, ".claude", "skills")];
|
|
3321
3820
|
}
|
|
3322
3821
|
if (platform === "codex") {
|
|
3323
3822
|
return [
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3823
|
+
path6.join(homeDir, ".codex", "skills"),
|
|
3824
|
+
path6.join(homeDir, ".agents", "skills"),
|
|
3825
|
+
path6.join(homeDir, ".codex", "plugins", "cache")
|
|
3327
3826
|
];
|
|
3328
3827
|
}
|
|
3329
|
-
return [
|
|
3828
|
+
return [path6.join(homeDir, ".config", "opencode", "skills")];
|
|
3330
3829
|
}
|
|
3331
3830
|
function ancestorDirs(cwd) {
|
|
3332
3831
|
const dirs = [];
|
|
3333
|
-
let current =
|
|
3832
|
+
let current = path6.resolve(cwd);
|
|
3334
3833
|
for (let depth = 0; depth < 16; depth++) {
|
|
3335
3834
|
dirs.push(current);
|
|
3336
|
-
const parent =
|
|
3835
|
+
const parent = path6.dirname(current);
|
|
3337
3836
|
if (parent === current)
|
|
3338
3837
|
break;
|
|
3339
3838
|
current = parent;
|
|
@@ -3343,11 +3842,11 @@ function ancestorDirs(cwd) {
|
|
|
3343
3842
|
function repoRoots(cwd, platform) {
|
|
3344
3843
|
return ancestorDirs(cwd).flatMap((dir) => {
|
|
3345
3844
|
if (platform === "claude-code")
|
|
3346
|
-
return [
|
|
3845
|
+
return [path6.join(dir, ".claude", "skills")];
|
|
3347
3846
|
if (platform === "codex") {
|
|
3348
|
-
return [
|
|
3847
|
+
return [path6.join(dir, ".codex", "skills"), path6.join(dir, ".agents", "skills")];
|
|
3349
3848
|
}
|
|
3350
|
-
return [
|
|
3849
|
+
return [path6.join(dir, ".opencode", "skills")];
|
|
3351
3850
|
});
|
|
3352
3851
|
}
|
|
3353
3852
|
function frontmatterName(content) {
|
|
@@ -3363,14 +3862,14 @@ function readNativeSkillNames(params) {
|
|
|
3363
3862
|
return;
|
|
3364
3863
|
let entries;
|
|
3365
3864
|
try {
|
|
3366
|
-
entries =
|
|
3865
|
+
entries = fs6.readdirSync(dir, { withFileTypes: true });
|
|
3367
3866
|
} catch {
|
|
3368
3867
|
return;
|
|
3369
3868
|
}
|
|
3370
3869
|
for (const entry of entries) {
|
|
3371
3870
|
if (visited >= MAX_NATIVE_SKILL_FILES)
|
|
3372
3871
|
return;
|
|
3373
|
-
const full =
|
|
3872
|
+
const full = path6.join(dir, entry.name);
|
|
3374
3873
|
if (entry.isDirectory()) {
|
|
3375
3874
|
walk(full, depth + 1);
|
|
3376
3875
|
continue;
|
|
@@ -3378,9 +3877,9 @@ function readNativeSkillNames(params) {
|
|
|
3378
3877
|
if (!entry.isFile() || entry.name.toLowerCase() !== "skill.md")
|
|
3379
3878
|
continue;
|
|
3380
3879
|
visited++;
|
|
3381
|
-
names.add(
|
|
3880
|
+
names.add(path6.basename(path6.dirname(full)).toLowerCase());
|
|
3382
3881
|
try {
|
|
3383
|
-
const declared = frontmatterName(
|
|
3882
|
+
const declared = frontmatterName(fs6.readFileSync(full, "utf8"));
|
|
3384
3883
|
if (declared)
|
|
3385
3884
|
names.add(declared.toLowerCase());
|
|
3386
3885
|
} catch {
|
|
@@ -3412,14 +3911,14 @@ function activationFromApiRows(params) {
|
|
|
3412
3911
|
}
|
|
3413
3912
|
|
|
3414
3913
|
// src/opencode-plugin.ts
|
|
3415
|
-
var DEBUG_LOG_PATH =
|
|
3914
|
+
var DEBUG_LOG_PATH = path7.join(os2.homedir(), ".greprag", "opencode-plugin-debug.log");
|
|
3416
3915
|
var _debugLogReady = false;
|
|
3417
3916
|
function dlogInit() {
|
|
3418
3917
|
if (_debugLogReady)
|
|
3419
3918
|
return;
|
|
3420
3919
|
_debugLogReady = true;
|
|
3421
3920
|
try {
|
|
3422
|
-
|
|
3921
|
+
fs7.writeFileSync(DEBUG_LOG_PATH, "");
|
|
3423
3922
|
} catch {
|
|
3424
3923
|
}
|
|
3425
3924
|
}
|
|
@@ -3428,7 +3927,7 @@ function dlog(msg) {
|
|
|
3428
3927
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [greprag-memory] ${msg}
|
|
3429
3928
|
`;
|
|
3430
3929
|
try {
|
|
3431
|
-
|
|
3930
|
+
fs7.appendFileSync(DEBUG_LOG_PATH, line);
|
|
3432
3931
|
} catch {
|
|
3433
3932
|
}
|
|
3434
3933
|
}
|
|
@@ -3436,9 +3935,9 @@ dlog(`module top reached: pid=${process.pid} argv0=${process.argv[0]} distPath=$
|
|
|
3436
3935
|
var API_URL = "https://api.greprag.com";
|
|
3437
3936
|
function loadEnvFile(filePath) {
|
|
3438
3937
|
try {
|
|
3439
|
-
if (!
|
|
3938
|
+
if (!fs7.existsSync(filePath))
|
|
3440
3939
|
return;
|
|
3441
|
-
const raw =
|
|
3940
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
3442
3941
|
for (const line of raw.split(/\r?\n/)) {
|
|
3443
3942
|
const trimmed = line.trim();
|
|
3444
3943
|
if (!trimmed || trimmed.startsWith("#"))
|
|
@@ -3458,12 +3957,12 @@ function loadEnvFile(filePath) {
|
|
|
3458
3957
|
}
|
|
3459
3958
|
}
|
|
3460
3959
|
function loadGrepragEnv() {
|
|
3461
|
-
loadEnvFile(
|
|
3960
|
+
loadEnvFile(path7.join(HOME, ".greprag", ".env"));
|
|
3462
3961
|
try {
|
|
3463
|
-
const p =
|
|
3464
|
-
if (!
|
|
3962
|
+
const p = path7.join(HOME, ".claude", "settings.json");
|
|
3963
|
+
if (!fs7.existsSync(p))
|
|
3465
3964
|
return;
|
|
3466
|
-
const data = JSON.parse(
|
|
3965
|
+
const data = JSON.parse(fs7.readFileSync(p, "utf-8"));
|
|
3467
3966
|
if (data && data.env && typeof data.env === "object") {
|
|
3468
3967
|
for (const [key, val] of Object.entries(data.env)) {
|
|
3469
3968
|
if (!process.env[key] && typeof val === "string") {
|
|
@@ -3476,11 +3975,11 @@ function loadGrepragEnv() {
|
|
|
3476
3975
|
}
|
|
3477
3976
|
loadGrepragEnv();
|
|
3478
3977
|
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>"}`);
|
|
3479
|
-
var WATCHER_LOCK =
|
|
3978
|
+
var WATCHER_LOCK = path7.join(HOME || os2.homedir(), ".greprag", "opencode-watch.lock");
|
|
3480
3979
|
var WATCHER_STALE_MS = 3e4;
|
|
3481
3980
|
function findGrepragBinary() {
|
|
3482
3981
|
const override = process.env.GREPRAG_BIN;
|
|
3483
|
-
if (override &&
|
|
3982
|
+
if (override && fs7.existsSync(override))
|
|
3484
3983
|
return override;
|
|
3485
3984
|
const binaryName = process.platform === "win32" ? "greprag.cmd" : "greprag";
|
|
3486
3985
|
try {
|
|
@@ -3489,14 +3988,14 @@ function findGrepragBinary() {
|
|
|
3489
3988
|
const candidate = line.trim();
|
|
3490
3989
|
if (!candidate)
|
|
3491
3990
|
continue;
|
|
3492
|
-
if (!
|
|
3991
|
+
if (!fs7.existsSync(candidate))
|
|
3493
3992
|
continue;
|
|
3494
3993
|
if (process.platform === "win32") {
|
|
3495
|
-
const ext =
|
|
3994
|
+
const ext = path7.extname(candidate).toLowerCase();
|
|
3496
3995
|
if (ext === ".cmd" || ext === ".exe" || ext === ".bat")
|
|
3497
3996
|
return candidate;
|
|
3498
3997
|
const cmdSibling = candidate + ".cmd";
|
|
3499
|
-
if (
|
|
3998
|
+
if (fs7.existsSync(cmdSibling))
|
|
3500
3999
|
return cmdSibling;
|
|
3501
4000
|
continue;
|
|
3502
4001
|
}
|
|
@@ -3505,19 +4004,19 @@ function findGrepragBinary() {
|
|
|
3505
4004
|
} catch {
|
|
3506
4005
|
}
|
|
3507
4006
|
const candidates = process.platform === "win32" ? [
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
4007
|
+
path7.join(HOME, "AppData", "Roaming", "npm", "greprag.cmd"),
|
|
4008
|
+
path7.join(HOME, "AppData", "Roaming", "npm", "greprag"),
|
|
4009
|
+
path7.join(HOME, "AppData", "Local", "Yarn", "bin", "greprag.cmd"),
|
|
4010
|
+
path7.join(HOME, "AppData", "Local", "pnpm", "bin", "greprag.cmd")
|
|
3512
4011
|
] : [
|
|
3513
4012
|
"/usr/local/bin/greprag",
|
|
3514
4013
|
"/opt/homebrew/bin/greprag",
|
|
3515
|
-
|
|
3516
|
-
|
|
4014
|
+
path7.join(HOME || "", ".local", "bin", "greprag"),
|
|
4015
|
+
path7.join(HOME || "", ".yarn", "bin", "greprag")
|
|
3517
4016
|
];
|
|
3518
4017
|
for (const c of candidates) {
|
|
3519
4018
|
try {
|
|
3520
|
-
if (c &&
|
|
4019
|
+
if (c && fs7.existsSync(c))
|
|
3521
4020
|
return c;
|
|
3522
4021
|
} catch {
|
|
3523
4022
|
}
|
|
@@ -3526,18 +4025,18 @@ function findGrepragBinary() {
|
|
|
3526
4025
|
}
|
|
3527
4026
|
function tryClaimWatcherLock() {
|
|
3528
4027
|
try {
|
|
3529
|
-
const fd =
|
|
4028
|
+
const fd = fs7.openSync(WATCHER_LOCK, "wx");
|
|
3530
4029
|
return { ok: true, fd };
|
|
3531
4030
|
} catch (err) {
|
|
3532
4031
|
if (err.code !== "EEXIST") {
|
|
3533
4032
|
return { ok: false, reason: "lock-create-failed" };
|
|
3534
4033
|
}
|
|
3535
4034
|
try {
|
|
3536
|
-
const stat =
|
|
4035
|
+
const stat = fs7.statSync(WATCHER_LOCK);
|
|
3537
4036
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
3538
4037
|
let pidAlive = false;
|
|
3539
4038
|
try {
|
|
3540
|
-
const pid = parseInt(
|
|
4039
|
+
const pid = parseInt(fs7.readFileSync(WATCHER_LOCK, "utf-8").trim(), 10);
|
|
3541
4040
|
pidAlive = pid > 0 && isPidAlive(pid);
|
|
3542
4041
|
} catch {
|
|
3543
4042
|
}
|
|
@@ -3545,10 +4044,10 @@ function tryClaimWatcherLock() {
|
|
|
3545
4044
|
return { ok: false, reason: "busy" };
|
|
3546
4045
|
}
|
|
3547
4046
|
try {
|
|
3548
|
-
|
|
4047
|
+
fs7.unlinkSync(WATCHER_LOCK);
|
|
3549
4048
|
} catch {
|
|
3550
4049
|
}
|
|
3551
|
-
const fd =
|
|
4050
|
+
const fd = fs7.openSync(WATCHER_LOCK, "wx");
|
|
3552
4051
|
return { ok: true, fd };
|
|
3553
4052
|
} catch (err2) {
|
|
3554
4053
|
return { ok: false, reason: "lock-stale-replace-failed" };
|
|
@@ -3589,11 +4088,11 @@ function startCaptureWatcher() {
|
|
|
3589
4088
|
return;
|
|
3590
4089
|
}
|
|
3591
4090
|
try {
|
|
3592
|
-
|
|
3593
|
-
|
|
4091
|
+
fs7.writeSync(claim.fd, String(process.pid));
|
|
4092
|
+
fs7.closeSync(claim.fd);
|
|
3594
4093
|
} catch {
|
|
3595
4094
|
try {
|
|
3596
|
-
|
|
4095
|
+
fs7.unlinkSync(WATCHER_LOCK);
|
|
3597
4096
|
} catch {
|
|
3598
4097
|
}
|
|
3599
4098
|
return;
|
|
@@ -3609,11 +4108,11 @@ function startCaptureWatcher() {
|
|
|
3609
4108
|
const claim2 = tryClaimWatcherLock();
|
|
3610
4109
|
if (claim2.ok) {
|
|
3611
4110
|
try {
|
|
3612
|
-
|
|
3613
|
-
|
|
4111
|
+
fs7.writeSync(claim2.fd, String(process.pid));
|
|
4112
|
+
fs7.closeSync(claim2.fd);
|
|
3614
4113
|
} catch {
|
|
3615
4114
|
try {
|
|
3616
|
-
|
|
4115
|
+
fs7.unlinkSync(WATCHER_LOCK);
|
|
3617
4116
|
} catch {
|
|
3618
4117
|
}
|
|
3619
4118
|
return false;
|
|
@@ -3656,7 +4155,7 @@ function startCaptureWatcher() {
|
|
|
3656
4155
|
childStartTime = Date.now();
|
|
3657
4156
|
dlog(`startCaptureWatcher: spawned 'greprag opencode watch' pid=${child.pid ?? "<none>"}`);
|
|
3658
4157
|
try {
|
|
3659
|
-
|
|
4158
|
+
fs7.writeFileSync(WATCHER_LOCK, String(child.pid));
|
|
3660
4159
|
} catch {
|
|
3661
4160
|
}
|
|
3662
4161
|
child.stderr?.on("data", (chunk2) => {
|
|
@@ -3668,9 +4167,9 @@ function startCaptureWatcher() {
|
|
|
3668
4167
|
child.on("exit", (code, signal) => {
|
|
3669
4168
|
dlog(`startCaptureWatcher: child pid=${child.pid ?? "<none>"} exited code=${code} signal=${signal || "none"}`);
|
|
3670
4169
|
try {
|
|
3671
|
-
const current =
|
|
4170
|
+
const current = fs7.readFileSync(WATCHER_LOCK, "utf-8").trim();
|
|
3672
4171
|
if (current === String(child.pid))
|
|
3673
|
-
|
|
4172
|
+
fs7.unlinkSync(WATCHER_LOCK);
|
|
3674
4173
|
} catch {
|
|
3675
4174
|
}
|
|
3676
4175
|
currentChild = null;
|
|
@@ -3699,7 +4198,7 @@ function startCaptureWatcher() {
|
|
|
3699
4198
|
`
|
|
3700
4199
|
);
|
|
3701
4200
|
try {
|
|
3702
|
-
|
|
4201
|
+
fs7.unlinkSync(WATCHER_LOCK);
|
|
3703
4202
|
} catch {
|
|
3704
4203
|
}
|
|
3705
4204
|
return;
|
|
@@ -3731,7 +4230,7 @@ function startCaptureWatcher() {
|
|
|
3731
4230
|
}, 2e3).unref();
|
|
3732
4231
|
}
|
|
3733
4232
|
try {
|
|
3734
|
-
|
|
4233
|
+
fs7.unlinkSync(WATCHER_LOCK);
|
|
3735
4234
|
} catch {
|
|
3736
4235
|
}
|
|
3737
4236
|
};
|
|
@@ -3768,6 +4267,7 @@ async function fetchRecapInbox(anchor) {
|
|
|
3768
4267
|
var relayArmedBySession = /* @__PURE__ */ new Set();
|
|
3769
4268
|
var announcedBySession = /* @__PURE__ */ new Set();
|
|
3770
4269
|
var turnCountBySession = /* @__PURE__ */ new Map();
|
|
4270
|
+
var procedurePromptKeyBySession = /* @__PURE__ */ new Map();
|
|
3771
4271
|
var cachedRecap = null;
|
|
3772
4272
|
var RECAP_CACHE_TTL_MS = 5 * 6e4;
|
|
3773
4273
|
var cachedSkillRows = null;
|
|
@@ -3839,11 +4339,11 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
3839
4339
|
return;
|
|
3840
4340
|
}
|
|
3841
4341
|
try {
|
|
3842
|
-
|
|
3843
|
-
|
|
4342
|
+
fs7.writeSync(claim.fd, String(process.pid));
|
|
4343
|
+
fs7.closeSync(claim.fd);
|
|
3844
4344
|
} catch {
|
|
3845
4345
|
try {
|
|
3846
|
-
|
|
4346
|
+
fs7.unlinkSync(lockPath);
|
|
3847
4347
|
} catch {
|
|
3848
4348
|
}
|
|
3849
4349
|
return;
|
|
@@ -3858,11 +4358,11 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
3858
4358
|
const claim2 = tryClaimRelayLock(lockPath);
|
|
3859
4359
|
if (claim2.ok) {
|
|
3860
4360
|
try {
|
|
3861
|
-
|
|
3862
|
-
|
|
4361
|
+
fs7.writeSync(claim2.fd, String(process.pid));
|
|
4362
|
+
fs7.closeSync(claim2.fd);
|
|
3863
4363
|
} catch {
|
|
3864
4364
|
try {
|
|
3865
|
-
|
|
4365
|
+
fs7.unlinkSync(lockPath);
|
|
3866
4366
|
} catch {
|
|
3867
4367
|
}
|
|
3868
4368
|
return false;
|
|
@@ -3898,7 +4398,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
3898
4398
|
childStartTime = Date.now();
|
|
3899
4399
|
dlog(`startSessionRelay: spawned 'greprag opencode relay' pid=${child.pid ?? "<none>"}`);
|
|
3900
4400
|
try {
|
|
3901
|
-
|
|
4401
|
+
fs7.writeFileSync(lockPath, String(child.pid));
|
|
3902
4402
|
} catch {
|
|
3903
4403
|
}
|
|
3904
4404
|
child.stderr?.on("data", (chunk2) => {
|
|
@@ -3909,9 +4409,9 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
3909
4409
|
});
|
|
3910
4410
|
child.on("exit", (code, signal) => {
|
|
3911
4411
|
try {
|
|
3912
|
-
const current =
|
|
4412
|
+
const current = fs7.readFileSync(lockPath, "utf-8").trim();
|
|
3913
4413
|
if (current === String(child.pid))
|
|
3914
|
-
|
|
4414
|
+
fs7.unlinkSync(lockPath);
|
|
3915
4415
|
} catch {
|
|
3916
4416
|
}
|
|
3917
4417
|
currentChild = null;
|
|
@@ -3938,7 +4438,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
3938
4438
|
`
|
|
3939
4439
|
);
|
|
3940
4440
|
try {
|
|
3941
|
-
|
|
4441
|
+
fs7.unlinkSync(lockPath);
|
|
3942
4442
|
} catch {
|
|
3943
4443
|
}
|
|
3944
4444
|
return;
|
|
@@ -3970,7 +4470,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
3970
4470
|
}, 2e3).unref();
|
|
3971
4471
|
}
|
|
3972
4472
|
try {
|
|
3973
|
-
|
|
4473
|
+
fs7.unlinkSync(lockPath);
|
|
3974
4474
|
} catch {
|
|
3975
4475
|
}
|
|
3976
4476
|
};
|
|
@@ -4030,7 +4530,7 @@ function runPngRecode(input, msgs) {
|
|
|
4030
4530
|
}
|
|
4031
4531
|
function makeCcrStash() {
|
|
4032
4532
|
return (original, sourceType) => {
|
|
4033
|
-
const hash =
|
|
4533
|
+
const hash = crypto5.createHash("sha256").update(original, "utf8").digest("hex").slice(0, 16);
|
|
4034
4534
|
const bin = grepragBinCached();
|
|
4035
4535
|
if (bin) {
|
|
4036
4536
|
try {
|
|
@@ -4181,6 +4681,31 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
|
|
|
4181
4681
|
// never opencode's persisted session history. adr:
|
|
4182
4682
|
// adr/opencode-context-compressor.md
|
|
4183
4683
|
"experimental.chat.messages.transform": async (input, output) => {
|
|
4684
|
+
try {
|
|
4685
|
+
const prompt = extractLatestOpenCodeProcedurePrompt(output.messages);
|
|
4686
|
+
if (prompt) {
|
|
4687
|
+
const sid = input.sessionID || "<no-session>";
|
|
4688
|
+
if (procedurePromptKeyBySession.get(sid) !== prompt.key) {
|
|
4689
|
+
procedurePromptKeyBySession.set(sid, prompt.key);
|
|
4690
|
+
const sessionContext = input.sessionID ? await resolveSessionContext(input.sessionID) : { anchor: fallbackAnchor, workingDir: fallbackWorkingDir };
|
|
4691
|
+
const procedure = runProcedurePromptCheck({
|
|
4692
|
+
cwd: sessionContext.workingDir,
|
|
4693
|
+
prompt: prompt.prompt,
|
|
4694
|
+
sessionId: input.sessionID
|
|
4695
|
+
});
|
|
4696
|
+
if (procedure.additionalContext) {
|
|
4697
|
+
prompt.parts.push({
|
|
4698
|
+
type: "text",
|
|
4699
|
+
text: procedure.additionalContext,
|
|
4700
|
+
synthetic: true
|
|
4701
|
+
});
|
|
4702
|
+
}
|
|
4703
|
+
dlog(`procedure: ${procedure.mode}${procedure.verb ? ` ${procedure.verb}` : ""} sid=${sid}`);
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
} catch (e) {
|
|
4707
|
+
dlog(`messages.transform procedure: swallowed error (turn unaffected): ${e.message}`);
|
|
4708
|
+
}
|
|
4184
4709
|
try {
|
|
4185
4710
|
if (pngRecodeEnabled()) {
|
|
4186
4711
|
const msgs0 = output && output.messages;
|