greprag 5.76.0 → 5.78.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/app/app.css +342 -0
- package/app/app.js +360 -0
- package/app/favicon.svg +4 -0
- package/app/index.html +54 -0
- package/dist/app-settings.js +162 -0
- package/dist/commands/app-model.js +308 -0
- package/dist/commands/app.js +310 -0
- package/dist/commands/codex-doctor.js +1 -0
- package/dist/commands/delivery-reminder.js +5 -5
- package/dist/commands/init.js +18 -3
- package/dist/commands/opencode-interrupt.js +11 -2
- package/dist/commands/persona-reminder.js +9 -0
- package/dist/commands/persona.js +191 -0
- package/dist/commands/reminder-registry.js +2 -0
- package/dist/commands/status.js +1 -0
- package/dist/hook.js +30 -4
- package/dist/index.js +23 -1
- package/dist/opencode-plugin.bundle.js +360 -279
- package/dist/opencode-plugin.js +45 -2
- package/dist/reminder-injector.js +2 -1
- package/package.json +2 -1
- package/scripts/postinstall.js +2 -1
|
@@ -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 crypto5 = __toESM(require("crypto"));
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
27
|
+
var fs8 = __toESM(require("fs"));
|
|
28
|
+
var os3 = __toESM(require("os"));
|
|
29
|
+
var path8 = __toESM(require("path"));
|
|
30
30
|
|
|
31
31
|
// src/opencode-plugin-helpers.ts
|
|
32
32
|
var crypto = __toESM(require("crypto"));
|
|
@@ -2438,15 +2438,15 @@ var loadoutRegistrarModule = {
|
|
|
2438
2438
|
// src/commands/delivery-reminder.ts
|
|
2439
2439
|
function coordinationLine(platform) {
|
|
2440
2440
|
if (platform === "codex") {
|
|
2441
|
-
return "Codex: at the merge/deploy gate, call codex_app.list_threads unfiltered, filter to peers in the same repo,
|
|
2441
|
+
return "Codex: at the merge/deploy gate, call codex_app.list_threads unfiltered, filter to peers in the same repo, and inspect their current summaries/status. Use codex_app.send_message_to_thread only for visible evidence of an unlanded ready commit, overlapping integration, or a concrete blocker. Peers with nothing actionable stay silent; do not solicit or send negative acknowledgements or routine completion follow-ups. Delivery owner: rename this task `DEPLOY: <existing title>` for operator visibility only; transfer the prefix on handoff; remove it before reporting delivery complete or abandoned.";
|
|
2442
2442
|
}
|
|
2443
2443
|
if (platform === "claude-code") {
|
|
2444
|
-
return "Claude Code: at the merge/deploy gate,
|
|
2444
|
+
return "Claude Code: at the merge/deploy gate, inspect peers in the same repo and send one concise delivery notice only for visible evidence of an unlanded ready commit, overlapping integration, or a concrete blocker. Peers with nothing actionable stay silent; do not solicit or send negative acknowledgements or routine completion follow-ups.";
|
|
2445
2445
|
}
|
|
2446
2446
|
if (platform === "opencode") {
|
|
2447
|
-
return "OpenCode: at the merge/deploy gate, use the GrepRAG project/session registry and send one concise
|
|
2447
|
+
return "OpenCode: at the merge/deploy gate, use the GrepRAG project/session registry and send one concise notice only for visible evidence of an unlanded ready commit, overlapping integration, or a concrete blocker. Peers with nothing actionable stay silent; do not solicit or send negative acknowledgements or routine completion follow-ups.";
|
|
2448
2448
|
}
|
|
2449
|
-
return "At the merge/deploy gate,
|
|
2449
|
+
return "At the merge/deploy gate, inspect peers in the same repo and send one concise notice only for visible evidence of an unlanded ready commit, overlapping integration, or a concrete blocker. Peers with nothing actionable stay silent; do not solicit or send negative acknowledgements or routine completion follow-ups.";
|
|
2450
2450
|
}
|
|
2451
2451
|
function buildDeliveryAnnounce(env) {
|
|
2452
2452
|
if (!env.deliveryRepo)
|
|
@@ -2456,7 +2456,7 @@ function buildDeliveryAnnounce(env) {
|
|
|
2456
2456
|
`[DELIVERY \u2014 ${project}]`,
|
|
2457
2457
|
"Commit useful, passing work.",
|
|
2458
2458
|
coordinationLine(env.platform),
|
|
2459
|
-
"Include ready peer commits and clear known blockers, then merge to the default branch and deploy immediately.
|
|
2459
|
+
"Include visible ready peer commits and clear known blockers, then merge to the default branch and deploy immediately. Silence means nothing actionable and does not block delivery.",
|
|
2460
2460
|
"Use this repo's delivery profile and verify production."
|
|
2461
2461
|
].join("\n");
|
|
2462
2462
|
}
|
|
@@ -2467,6 +2467,14 @@ var deliveryControlModule = {
|
|
|
2467
2467
|
reminder: () => null
|
|
2468
2468
|
};
|
|
2469
2469
|
|
|
2470
|
+
// src/commands/persona-reminder.ts
|
|
2471
|
+
var personaAnnounceModule = {
|
|
2472
|
+
id: "persona-announce",
|
|
2473
|
+
detect: () => ({ tier: "silent" }),
|
|
2474
|
+
announce: (env) => env.personaAnnounce || null,
|
|
2475
|
+
reminder: () => null
|
|
2476
|
+
};
|
|
2477
|
+
|
|
2470
2478
|
// src/commands/reminder-registry.ts
|
|
2471
2479
|
var REGISTRY = [
|
|
2472
2480
|
osPrimerModule,
|
|
@@ -2488,6 +2496,8 @@ var REGISTRY = [
|
|
|
2488
2496
|
// active project procedures, rendered generically from operator-owned installs
|
|
2489
2497
|
deliveryControlModule,
|
|
2490
2498
|
// repo-scoped announce-first Delivery System pilot (all harnesses)
|
|
2499
|
+
personaAnnounceModule,
|
|
2500
|
+
// tenant-scoped Persona, shared across projects/harnesses
|
|
2491
2501
|
loadoutRegistrarModule,
|
|
2492
2502
|
// equipped loadouts: one advisor announce each + per-turn keyword Match (docs/loadout.md)
|
|
2493
2503
|
setupWarningModule,
|
|
@@ -2562,6 +2572,216 @@ function collectAnnounces(env, registry = REGISTRY) {
|
|
|
2562
2572
|
return out;
|
|
2563
2573
|
}
|
|
2564
2574
|
|
|
2575
|
+
// src/app-settings.ts
|
|
2576
|
+
var fs3 = __toESM(require("node:fs"));
|
|
2577
|
+
var os2 = __toESM(require("node:os"));
|
|
2578
|
+
var path3 = __toESM(require("node:path"));
|
|
2579
|
+
|
|
2580
|
+
// src/project-anchor.ts
|
|
2581
|
+
var path2 = __toESM(require("path"));
|
|
2582
|
+
var fs2 = __toESM(require("fs"));
|
|
2583
|
+
var crypto2 = __toESM(require("crypto"));
|
|
2584
|
+
var os = __toESM(require("os"));
|
|
2585
|
+
var ANCHOR_DIR = ".greprag";
|
|
2586
|
+
var ANCHOR_FILE = "project.json";
|
|
2587
|
+
var LEGACY_ANCHOR_DIR = ".claude";
|
|
2588
|
+
function anchorPathIn(dir) {
|
|
2589
|
+
return path2.join(dir, ANCHOR_DIR, ANCHOR_FILE);
|
|
2590
|
+
}
|
|
2591
|
+
function legacyAnchorPathIn(dir) {
|
|
2592
|
+
return path2.join(dir, LEGACY_ANCHOR_DIR, ANCHOR_FILE);
|
|
2593
|
+
}
|
|
2594
|
+
function globalAnchorPath() {
|
|
2595
|
+
return anchorPathIn(os.homedir());
|
|
2596
|
+
}
|
|
2597
|
+
function legacyGlobalAnchorPath() {
|
|
2598
|
+
return legacyAnchorPathIn(os.homedir());
|
|
2599
|
+
}
|
|
2600
|
+
function findExistingAnchor(startDir) {
|
|
2601
|
+
const homeAnchor = globalAnchorPath();
|
|
2602
|
+
const legacyHomeAnchor = legacyGlobalAnchorPath();
|
|
2603
|
+
let dir = path2.resolve(startDir);
|
|
2604
|
+
while (true) {
|
|
2605
|
+
const candidate = anchorPathIn(dir);
|
|
2606
|
+
if (candidate !== homeAnchor && fs2.existsSync(candidate))
|
|
2607
|
+
return candidate;
|
|
2608
|
+
const legacyCandidate = legacyAnchorPathIn(dir);
|
|
2609
|
+
if (legacyCandidate !== legacyHomeAnchor && fs2.existsSync(legacyCandidate))
|
|
2610
|
+
return legacyCandidate;
|
|
2611
|
+
const parent = path2.dirname(dir);
|
|
2612
|
+
if (parent === dir)
|
|
2613
|
+
return null;
|
|
2614
|
+
dir = parent;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
function isEphemeralCwd2(cwd) {
|
|
2618
|
+
const norm = path2.resolve(cwd).replace(/\\/g, "/").toLowerCase();
|
|
2619
|
+
if (norm.includes("/appdata/roaming/claude/local-agent-mode-sessions/"))
|
|
2620
|
+
return true;
|
|
2621
|
+
if (norm.includes("/appdata/local/claude/local-agent-mode-sessions/"))
|
|
2622
|
+
return true;
|
|
2623
|
+
if (norm.startsWith("/tmp/"))
|
|
2624
|
+
return true;
|
|
2625
|
+
if (norm.startsWith("/var/tmp/"))
|
|
2626
|
+
return true;
|
|
2627
|
+
if (norm.startsWith("/private/tmp/"))
|
|
2628
|
+
return true;
|
|
2629
|
+
return false;
|
|
2630
|
+
}
|
|
2631
|
+
function computeGitDerivedProjectId2(workingDir) {
|
|
2632
|
+
try {
|
|
2633
|
+
const out = safeExecSync("git rev-list --max-parents=0 HEAD", {
|
|
2634
|
+
cwd: workingDir,
|
|
2635
|
+
encoding: "utf-8",
|
|
2636
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2637
|
+
});
|
|
2638
|
+
const roots = out.trim().split(/\s+/).filter(Boolean).sort();
|
|
2639
|
+
if (roots.length === 0)
|
|
2640
|
+
return null;
|
|
2641
|
+
const hash = crypto2.createHash("sha256").update(roots.join("\n")).digest("hex");
|
|
2642
|
+
return [
|
|
2643
|
+
hash.slice(0, 8),
|
|
2644
|
+
hash.slice(8, 12),
|
|
2645
|
+
"4" + hash.slice(13, 16),
|
|
2646
|
+
// version 4 nibble
|
|
2647
|
+
"8" + hash.slice(17, 20),
|
|
2648
|
+
// variant nibble
|
|
2649
|
+
hash.slice(20, 32)
|
|
2650
|
+
].join("-");
|
|
2651
|
+
} catch {
|
|
2652
|
+
return null;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
function deterministicProjectId2(workingDir) {
|
|
2656
|
+
const normalized = path2.resolve(workingDir).toLowerCase();
|
|
2657
|
+
const hash = crypto2.createHash("sha256").update(normalized).digest("hex");
|
|
2658
|
+
return [
|
|
2659
|
+
hash.slice(0, 8),
|
|
2660
|
+
hash.slice(8, 12),
|
|
2661
|
+
"4" + hash.slice(13, 16),
|
|
2662
|
+
// version 4 nibble
|
|
2663
|
+
"8" + hash.slice(17, 20),
|
|
2664
|
+
// variant nibble
|
|
2665
|
+
hash.slice(20, 32)
|
|
2666
|
+
].join("-");
|
|
2667
|
+
}
|
|
2668
|
+
function tryReadAnchorFileContents(filePath) {
|
|
2669
|
+
try {
|
|
2670
|
+
const raw = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
2671
|
+
const notifyRaw = raw.inbox_notify;
|
|
2672
|
+
const inboxNotify = notifyRaw === "off" || notifyRaw === "session_start_only" ? notifyRaw : "every_turn";
|
|
2673
|
+
return {
|
|
2674
|
+
projectId: raw.project_id,
|
|
2675
|
+
projectName: raw.project_name,
|
|
2676
|
+
created: raw.created,
|
|
2677
|
+
memoryCapture: raw.memory_capture !== false,
|
|
2678
|
+
sessionStartRecap: raw.session_start_recap !== false,
|
|
2679
|
+
inboxNotify,
|
|
2680
|
+
emailDir: typeof raw.email_dir === "string" ? raw.email_dir : void 0,
|
|
2681
|
+
emailAutosave: raw.email_autosave === true,
|
|
2682
|
+
role: typeof raw.role === "string" ? raw.role : void 0
|
|
2683
|
+
};
|
|
2684
|
+
} catch {
|
|
2685
|
+
return null;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
function readAnchor2(cwd) {
|
|
2689
|
+
const existingPath = findExistingAnchor(cwd);
|
|
2690
|
+
const fileContents = existingPath ? tryReadAnchorFileContents(existingPath) : null;
|
|
2691
|
+
if (existingPath && fileContents && fileContents.projectId && fileContents.projectName) {
|
|
2692
|
+
return {
|
|
2693
|
+
projectId: fileContents.projectId,
|
|
2694
|
+
projectName: fileContents.projectName,
|
|
2695
|
+
initialized: true,
|
|
2696
|
+
source: "file",
|
|
2697
|
+
anchorPath: existingPath,
|
|
2698
|
+
created: fileContents.created,
|
|
2699
|
+
memoryCapture: fileContents.memoryCapture,
|
|
2700
|
+
sessionStartRecap: fileContents.sessionStartRecap,
|
|
2701
|
+
inboxNotify: fileContents.inboxNotify,
|
|
2702
|
+
emailDir: fileContents.emailDir,
|
|
2703
|
+
emailAutosave: fileContents.emailAutosave,
|
|
2704
|
+
role: fileContents.role
|
|
2705
|
+
};
|
|
2706
|
+
}
|
|
2707
|
+
const gitId = computeGitDerivedProjectId2(cwd);
|
|
2708
|
+
if (gitId) {
|
|
2709
|
+
const root2 = path2.resolve(cwd);
|
|
2710
|
+
return {
|
|
2711
|
+
projectId: gitId,
|
|
2712
|
+
projectName: fileContents?.projectName || path2.basename(root2).toLowerCase(),
|
|
2713
|
+
initialized: true,
|
|
2714
|
+
source: "git",
|
|
2715
|
+
anchorPath: existingPath || anchorPathIn(root2),
|
|
2716
|
+
created: fileContents?.created,
|
|
2717
|
+
memoryCapture: fileContents?.memoryCapture ?? true,
|
|
2718
|
+
sessionStartRecap: fileContents?.sessionStartRecap ?? true,
|
|
2719
|
+
inboxNotify: fileContents?.inboxNotify ?? "every_turn",
|
|
2720
|
+
emailDir: fileContents?.emailDir,
|
|
2721
|
+
emailAutosave: fileContents?.emailAutosave,
|
|
2722
|
+
role: fileContents?.role
|
|
2723
|
+
};
|
|
2724
|
+
}
|
|
2725
|
+
if (isEphemeralCwd2(cwd)) {
|
|
2726
|
+
const globalPath = fs2.existsSync(globalAnchorPath()) ? globalAnchorPath() : legacyGlobalAnchorPath();
|
|
2727
|
+
const globalContents = tryReadAnchorFileContents(globalPath);
|
|
2728
|
+
if (globalContents && globalContents.projectId && globalContents.projectName) {
|
|
2729
|
+
return {
|
|
2730
|
+
projectId: globalContents.projectId,
|
|
2731
|
+
projectName: globalContents.projectName,
|
|
2732
|
+
initialized: true,
|
|
2733
|
+
source: "global",
|
|
2734
|
+
anchorPath: globalPath,
|
|
2735
|
+
created: globalContents.created,
|
|
2736
|
+
memoryCapture: globalContents.memoryCapture,
|
|
2737
|
+
sessionStartRecap: globalContents.sessionStartRecap,
|
|
2738
|
+
inboxNotify: globalContents.inboxNotify,
|
|
2739
|
+
emailDir: globalContents.emailDir,
|
|
2740
|
+
emailAutosave: globalContents.emailAutosave,
|
|
2741
|
+
role: globalContents.role
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
const root = path2.resolve(cwd);
|
|
2746
|
+
return {
|
|
2747
|
+
projectId: deterministicProjectId2(root),
|
|
2748
|
+
projectName: fileContents?.projectName || path2.basename(root).toLowerCase(),
|
|
2749
|
+
initialized: false,
|
|
2750
|
+
source: "hash",
|
|
2751
|
+
anchorPath: existingPath || anchorPathIn(root),
|
|
2752
|
+
created: fileContents?.created,
|
|
2753
|
+
memoryCapture: fileContents?.memoryCapture ?? true,
|
|
2754
|
+
sessionStartRecap: fileContents?.sessionStartRecap ?? true,
|
|
2755
|
+
inboxNotify: fileContents?.inboxNotify ?? "every_turn",
|
|
2756
|
+
emailDir: fileContents?.emailDir,
|
|
2757
|
+
emailAutosave: fileContents?.emailAutosave,
|
|
2758
|
+
role: fileContents?.role
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
// src/app-settings.ts
|
|
2763
|
+
function readJson(file) {
|
|
2764
|
+
try {
|
|
2765
|
+
const parsed = JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
2766
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
2767
|
+
} catch {
|
|
2768
|
+
return {};
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
function localAppSettingsPath(homeDir = os2.homedir()) {
|
|
2772
|
+
return path3.join(homeDir, ".greprag", "settings.json");
|
|
2773
|
+
}
|
|
2774
|
+
function readLocalAppSettings(homeDir = os2.homedir()) {
|
|
2775
|
+
const raw = readJson(localAppSettingsPath(homeDir));
|
|
2776
|
+
const interrupts = raw.interrupts && typeof raw.interrupts === "object" ? raw.interrupts : {};
|
|
2777
|
+
const disabled = Array.isArray(interrupts.disabled) ? [...new Set(interrupts.disabled.filter((id) => typeof id === "string" && /^[a-z0-9-]{1,80}$/.test(id)))] : [];
|
|
2778
|
+
return { version: 1, interrupts: { disabled } };
|
|
2779
|
+
}
|
|
2780
|
+
function filterEnabledInterrupts(modules, settings = readLocalAppSettings()) {
|
|
2781
|
+
const disabled = new Set(settings.interrupts.disabled);
|
|
2782
|
+
return modules.filter((module2) => !disabled.has(module2.id));
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2565
2785
|
// src/commands/opencode-interrupt.ts
|
|
2566
2786
|
var opencodeRegistry = harnessModules("opencode");
|
|
2567
2787
|
var OPENCODE_QUOTA_WAITLIST_ANNOUNCE = [
|
|
@@ -2591,19 +2811,23 @@ function buildOpenCodeEnv(params) {
|
|
|
2591
2811
|
corpusApiDocs: params.corpusApiDocs ?? [],
|
|
2592
2812
|
updateAvailable: params.updateAvailable ?? null,
|
|
2593
2813
|
procedureAnnounces: params.procedureAnnounces ?? [],
|
|
2594
|
-
mirroredSkills: params.mirroredSkills
|
|
2814
|
+
mirroredSkills: params.mirroredSkills,
|
|
2815
|
+
personaAnnounce: params.personaAnnounce ?? null
|
|
2595
2816
|
};
|
|
2596
2817
|
}
|
|
2597
2818
|
function getOpenCodeAnnounces(env) {
|
|
2598
|
-
return [...collectAnnounces(env, opencodeRegistry), OPENCODE_QUOTA_WAITLIST_ANNOUNCE];
|
|
2819
|
+
return [...collectAnnounces(env, filterEnabledInterrupts(opencodeRegistry)), OPENCODE_QUOTA_WAITLIST_ANNOUNCE];
|
|
2820
|
+
}
|
|
2821
|
+
function getOpenCodePersistentAnnounces(env) {
|
|
2822
|
+
return collectAnnounces(env, filterEnabledInterrupts(opencodeRegistry.filter((m) => m.id === "persona-announce")));
|
|
2599
2823
|
}
|
|
2600
2824
|
function getOpenCodeReminders(env) {
|
|
2601
|
-
return collectReminders(env, opencodeRegistry);
|
|
2825
|
+
return collectReminders(env, filterEnabledInterrupts(opencodeRegistry));
|
|
2602
2826
|
}
|
|
2603
2827
|
|
|
2604
2828
|
// src/procedure.ts
|
|
2605
|
-
var
|
|
2606
|
-
var
|
|
2829
|
+
var path4 = __toESM(require("path"));
|
|
2830
|
+
var fs4 = __toESM(require("fs"));
|
|
2607
2831
|
|
|
2608
2832
|
// src/delivery-lifecycle.ts
|
|
2609
2833
|
var DELIVERY_LIFECYCLE_VERBS = [
|
|
@@ -2622,14 +2846,14 @@ function isDeliveryLifecycleVerb(verb) {
|
|
|
2622
2846
|
var PROCEDURE_STORE_VERSION = "2";
|
|
2623
2847
|
function stateDir() {
|
|
2624
2848
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
2625
|
-
return
|
|
2849
|
+
return path4.join(home, ".greprag", "state");
|
|
2626
2850
|
}
|
|
2627
2851
|
function procedureStorePath(projectId) {
|
|
2628
|
-
return
|
|
2852
|
+
return path4.join(stateDir(), `procedures-${projectId}.json`);
|
|
2629
2853
|
}
|
|
2630
2854
|
function readProcedureStore(projectId) {
|
|
2631
2855
|
try {
|
|
2632
|
-
const raw =
|
|
2856
|
+
const raw = fs4.readFileSync(procedureStorePath(projectId), "utf-8");
|
|
2633
2857
|
const parsed = JSON.parse(raw);
|
|
2634
2858
|
if (parsed && Array.isArray(parsed.procedures)) {
|
|
2635
2859
|
return normalizeProcedureStore({
|
|
@@ -2862,191 +3086,9 @@ function activeProcedureAnnounces(store) {
|
|
|
2862
3086
|
// src/procedure-runtime.ts
|
|
2863
3087
|
var crypto4 = __toESM(require("crypto"));
|
|
2864
3088
|
|
|
2865
|
-
// src/project-anchor.ts
|
|
2866
|
-
var path3 = __toESM(require("path"));
|
|
2867
|
-
var fs3 = __toESM(require("fs"));
|
|
2868
|
-
var crypto2 = __toESM(require("crypto"));
|
|
2869
|
-
var os = __toESM(require("os"));
|
|
2870
|
-
var ANCHOR_DIR = ".greprag";
|
|
2871
|
-
var ANCHOR_FILE = "project.json";
|
|
2872
|
-
var LEGACY_ANCHOR_DIR = ".claude";
|
|
2873
|
-
function anchorPathIn(dir) {
|
|
2874
|
-
return path3.join(dir, ANCHOR_DIR, ANCHOR_FILE);
|
|
2875
|
-
}
|
|
2876
|
-
function legacyAnchorPathIn(dir) {
|
|
2877
|
-
return path3.join(dir, LEGACY_ANCHOR_DIR, ANCHOR_FILE);
|
|
2878
|
-
}
|
|
2879
|
-
function globalAnchorPath() {
|
|
2880
|
-
return anchorPathIn(os.homedir());
|
|
2881
|
-
}
|
|
2882
|
-
function legacyGlobalAnchorPath() {
|
|
2883
|
-
return legacyAnchorPathIn(os.homedir());
|
|
2884
|
-
}
|
|
2885
|
-
function findExistingAnchor(startDir) {
|
|
2886
|
-
const homeAnchor = globalAnchorPath();
|
|
2887
|
-
const legacyHomeAnchor = legacyGlobalAnchorPath();
|
|
2888
|
-
let dir = path3.resolve(startDir);
|
|
2889
|
-
while (true) {
|
|
2890
|
-
const candidate = anchorPathIn(dir);
|
|
2891
|
-
if (candidate !== homeAnchor && fs3.existsSync(candidate))
|
|
2892
|
-
return candidate;
|
|
2893
|
-
const legacyCandidate = legacyAnchorPathIn(dir);
|
|
2894
|
-
if (legacyCandidate !== legacyHomeAnchor && fs3.existsSync(legacyCandidate))
|
|
2895
|
-
return legacyCandidate;
|
|
2896
|
-
const parent = path3.dirname(dir);
|
|
2897
|
-
if (parent === dir)
|
|
2898
|
-
return null;
|
|
2899
|
-
dir = parent;
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
function isEphemeralCwd2(cwd) {
|
|
2903
|
-
const norm = path3.resolve(cwd).replace(/\\/g, "/").toLowerCase();
|
|
2904
|
-
if (norm.includes("/appdata/roaming/claude/local-agent-mode-sessions/"))
|
|
2905
|
-
return true;
|
|
2906
|
-
if (norm.includes("/appdata/local/claude/local-agent-mode-sessions/"))
|
|
2907
|
-
return true;
|
|
2908
|
-
if (norm.startsWith("/tmp/"))
|
|
2909
|
-
return true;
|
|
2910
|
-
if (norm.startsWith("/var/tmp/"))
|
|
2911
|
-
return true;
|
|
2912
|
-
if (norm.startsWith("/private/tmp/"))
|
|
2913
|
-
return true;
|
|
2914
|
-
return false;
|
|
2915
|
-
}
|
|
2916
|
-
function computeGitDerivedProjectId2(workingDir) {
|
|
2917
|
-
try {
|
|
2918
|
-
const out = safeExecSync("git rev-list --max-parents=0 HEAD", {
|
|
2919
|
-
cwd: workingDir,
|
|
2920
|
-
encoding: "utf-8",
|
|
2921
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
2922
|
-
});
|
|
2923
|
-
const roots = out.trim().split(/\s+/).filter(Boolean).sort();
|
|
2924
|
-
if (roots.length === 0)
|
|
2925
|
-
return null;
|
|
2926
|
-
const hash = crypto2.createHash("sha256").update(roots.join("\n")).digest("hex");
|
|
2927
|
-
return [
|
|
2928
|
-
hash.slice(0, 8),
|
|
2929
|
-
hash.slice(8, 12),
|
|
2930
|
-
"4" + hash.slice(13, 16),
|
|
2931
|
-
// version 4 nibble
|
|
2932
|
-
"8" + hash.slice(17, 20),
|
|
2933
|
-
// variant nibble
|
|
2934
|
-
hash.slice(20, 32)
|
|
2935
|
-
].join("-");
|
|
2936
|
-
} catch {
|
|
2937
|
-
return null;
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
function deterministicProjectId2(workingDir) {
|
|
2941
|
-
const normalized = path3.resolve(workingDir).toLowerCase();
|
|
2942
|
-
const hash = crypto2.createHash("sha256").update(normalized).digest("hex");
|
|
2943
|
-
return [
|
|
2944
|
-
hash.slice(0, 8),
|
|
2945
|
-
hash.slice(8, 12),
|
|
2946
|
-
"4" + hash.slice(13, 16),
|
|
2947
|
-
// version 4 nibble
|
|
2948
|
-
"8" + hash.slice(17, 20),
|
|
2949
|
-
// variant nibble
|
|
2950
|
-
hash.slice(20, 32)
|
|
2951
|
-
].join("-");
|
|
2952
|
-
}
|
|
2953
|
-
function tryReadAnchorFileContents(filePath) {
|
|
2954
|
-
try {
|
|
2955
|
-
const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
2956
|
-
const notifyRaw = raw.inbox_notify;
|
|
2957
|
-
const inboxNotify = notifyRaw === "off" || notifyRaw === "session_start_only" ? notifyRaw : "every_turn";
|
|
2958
|
-
return {
|
|
2959
|
-
projectId: raw.project_id,
|
|
2960
|
-
projectName: raw.project_name,
|
|
2961
|
-
created: raw.created,
|
|
2962
|
-
memoryCapture: raw.memory_capture !== false,
|
|
2963
|
-
sessionStartRecap: raw.session_start_recap !== false,
|
|
2964
|
-
inboxNotify,
|
|
2965
|
-
emailDir: typeof raw.email_dir === "string" ? raw.email_dir : void 0,
|
|
2966
|
-
emailAutosave: raw.email_autosave === true,
|
|
2967
|
-
role: typeof raw.role === "string" ? raw.role : void 0
|
|
2968
|
-
};
|
|
2969
|
-
} catch {
|
|
2970
|
-
return null;
|
|
2971
|
-
}
|
|
2972
|
-
}
|
|
2973
|
-
function readAnchor2(cwd) {
|
|
2974
|
-
const existingPath = findExistingAnchor(cwd);
|
|
2975
|
-
const fileContents = existingPath ? tryReadAnchorFileContents(existingPath) : null;
|
|
2976
|
-
if (existingPath && fileContents && fileContents.projectId && fileContents.projectName) {
|
|
2977
|
-
return {
|
|
2978
|
-
projectId: fileContents.projectId,
|
|
2979
|
-
projectName: fileContents.projectName,
|
|
2980
|
-
initialized: true,
|
|
2981
|
-
source: "file",
|
|
2982
|
-
anchorPath: existingPath,
|
|
2983
|
-
created: fileContents.created,
|
|
2984
|
-
memoryCapture: fileContents.memoryCapture,
|
|
2985
|
-
sessionStartRecap: fileContents.sessionStartRecap,
|
|
2986
|
-
inboxNotify: fileContents.inboxNotify,
|
|
2987
|
-
emailDir: fileContents.emailDir,
|
|
2988
|
-
emailAutosave: fileContents.emailAutosave,
|
|
2989
|
-
role: fileContents.role
|
|
2990
|
-
};
|
|
2991
|
-
}
|
|
2992
|
-
const gitId = computeGitDerivedProjectId2(cwd);
|
|
2993
|
-
if (gitId) {
|
|
2994
|
-
const root2 = path3.resolve(cwd);
|
|
2995
|
-
return {
|
|
2996
|
-
projectId: gitId,
|
|
2997
|
-
projectName: fileContents?.projectName || path3.basename(root2).toLowerCase(),
|
|
2998
|
-
initialized: true,
|
|
2999
|
-
source: "git",
|
|
3000
|
-
anchorPath: existingPath || anchorPathIn(root2),
|
|
3001
|
-
created: fileContents?.created,
|
|
3002
|
-
memoryCapture: fileContents?.memoryCapture ?? true,
|
|
3003
|
-
sessionStartRecap: fileContents?.sessionStartRecap ?? true,
|
|
3004
|
-
inboxNotify: fileContents?.inboxNotify ?? "every_turn",
|
|
3005
|
-
emailDir: fileContents?.emailDir,
|
|
3006
|
-
emailAutosave: fileContents?.emailAutosave,
|
|
3007
|
-
role: fileContents?.role
|
|
3008
|
-
};
|
|
3009
|
-
}
|
|
3010
|
-
if (isEphemeralCwd2(cwd)) {
|
|
3011
|
-
const globalPath = fs3.existsSync(globalAnchorPath()) ? globalAnchorPath() : legacyGlobalAnchorPath();
|
|
3012
|
-
const globalContents = tryReadAnchorFileContents(globalPath);
|
|
3013
|
-
if (globalContents && globalContents.projectId && globalContents.projectName) {
|
|
3014
|
-
return {
|
|
3015
|
-
projectId: globalContents.projectId,
|
|
3016
|
-
projectName: globalContents.projectName,
|
|
3017
|
-
initialized: true,
|
|
3018
|
-
source: "global",
|
|
3019
|
-
anchorPath: globalPath,
|
|
3020
|
-
created: globalContents.created,
|
|
3021
|
-
memoryCapture: globalContents.memoryCapture,
|
|
3022
|
-
sessionStartRecap: globalContents.sessionStartRecap,
|
|
3023
|
-
inboxNotify: globalContents.inboxNotify,
|
|
3024
|
-
emailDir: globalContents.emailDir,
|
|
3025
|
-
emailAutosave: globalContents.emailAutosave,
|
|
3026
|
-
role: globalContents.role
|
|
3027
|
-
};
|
|
3028
|
-
}
|
|
3029
|
-
}
|
|
3030
|
-
const root = path3.resolve(cwd);
|
|
3031
|
-
return {
|
|
3032
|
-
projectId: deterministicProjectId2(root),
|
|
3033
|
-
projectName: fileContents?.projectName || path3.basename(root).toLowerCase(),
|
|
3034
|
-
initialized: false,
|
|
3035
|
-
source: "hash",
|
|
3036
|
-
anchorPath: existingPath || anchorPathIn(root),
|
|
3037
|
-
created: fileContents?.created,
|
|
3038
|
-
memoryCapture: fileContents?.memoryCapture ?? true,
|
|
3039
|
-
sessionStartRecap: fileContents?.sessionStartRecap ?? true,
|
|
3040
|
-
inboxNotify: fileContents?.inboxNotify ?? "every_turn",
|
|
3041
|
-
emailDir: fileContents?.emailDir,
|
|
3042
|
-
emailAutosave: fileContents?.emailAutosave,
|
|
3043
|
-
role: fileContents?.role
|
|
3044
|
-
};
|
|
3045
|
-
}
|
|
3046
|
-
|
|
3047
3089
|
// src/procedure-watch.ts
|
|
3048
|
-
var
|
|
3049
|
-
var
|
|
3090
|
+
var path5 = __toESM(require("path"));
|
|
3091
|
+
var fs5 = __toESM(require("fs"));
|
|
3050
3092
|
var TIER1_LEARN_TRIGGERS = [
|
|
3051
3093
|
{ verb: "deploy", triggers: ["deploy", "deploy the api", "deploy the worker", "redeploy"], steps: "", status: "seeded" },
|
|
3052
3094
|
{ verb: "push", triggers: ["push", "git push", "push to remote", "push it up", "push upstream"], steps: "", status: "seeded", destructive: true },
|
|
@@ -3079,24 +3121,24 @@ function openWatch(verb, phase, openedAt, shadowRunId) {
|
|
|
3079
3121
|
}
|
|
3080
3122
|
function stateDir2() {
|
|
3081
3123
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
3082
|
-
return
|
|
3124
|
+
return path5.join(home, ".greprag", "state");
|
|
3083
3125
|
}
|
|
3084
3126
|
function procedureWatchPath(projectId) {
|
|
3085
|
-
return
|
|
3127
|
+
return path5.join(stateDir2(), `procedure-watch-${projectId}.json`);
|
|
3086
3128
|
}
|
|
3087
3129
|
function hasProcedureWatch(projectId) {
|
|
3088
3130
|
try {
|
|
3089
|
-
return
|
|
3131
|
+
return fs5.existsSync(procedureWatchPath(projectId));
|
|
3090
3132
|
} catch {
|
|
3091
3133
|
return false;
|
|
3092
3134
|
}
|
|
3093
3135
|
}
|
|
3094
3136
|
function writeProcedureWatch(projectId, watch) {
|
|
3095
3137
|
const file = procedureWatchPath(projectId);
|
|
3096
|
-
const dir =
|
|
3097
|
-
if (!
|
|
3098
|
-
|
|
3099
|
-
|
|
3138
|
+
const dir = path5.dirname(file);
|
|
3139
|
+
if (!fs5.existsSync(dir))
|
|
3140
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
3141
|
+
fs5.writeFileSync(file, JSON.stringify(watch, null, 2) + "\n");
|
|
3100
3142
|
}
|
|
3101
3143
|
function openWatchIfIdle(projectId, verb, phase, shadowRunId) {
|
|
3102
3144
|
if (hasProcedureWatch(projectId))
|
|
@@ -3107,20 +3149,20 @@ function openWatchIfIdle(projectId, verb, phase, shadowRunId) {
|
|
|
3107
3149
|
|
|
3108
3150
|
// src/procedure-shadow.ts
|
|
3109
3151
|
var crypto3 = __toESM(require("crypto"));
|
|
3110
|
-
var
|
|
3111
|
-
var
|
|
3152
|
+
var fs6 = __toESM(require("fs"));
|
|
3153
|
+
var path6 = __toESM(require("path"));
|
|
3112
3154
|
function stateDir3() {
|
|
3113
3155
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
3114
|
-
return
|
|
3156
|
+
return path6.join(home, ".greprag", "state");
|
|
3115
3157
|
}
|
|
3116
3158
|
function procedureShadowPath(projectId) {
|
|
3117
|
-
return
|
|
3159
|
+
return path6.join(stateDir3(), `procedure-shadow-${projectId}.jsonl`);
|
|
3118
3160
|
}
|
|
3119
3161
|
function appendShadowEvent(projectId, event) {
|
|
3120
3162
|
try {
|
|
3121
3163
|
const file = procedureShadowPath(projectId);
|
|
3122
|
-
|
|
3123
|
-
|
|
3164
|
+
fs6.mkdirSync(path6.dirname(file), { recursive: true });
|
|
3165
|
+
fs6.appendFileSync(file, JSON.stringify(event) + "\n");
|
|
3124
3166
|
} catch {
|
|
3125
3167
|
}
|
|
3126
3168
|
}
|
|
@@ -3840,29 +3882,29 @@ function recodeMessagesToPng(messages, opts) {
|
|
|
3840
3882
|
}
|
|
3841
3883
|
|
|
3842
3884
|
// src/skill-activation-manifest.ts
|
|
3843
|
-
var
|
|
3844
|
-
var
|
|
3885
|
+
var fs7 = __toESM(require("fs"));
|
|
3886
|
+
var path7 = __toESM(require("path"));
|
|
3845
3887
|
var MAX_NATIVE_SKILL_FILES = 1500;
|
|
3846
3888
|
var MAX_SCAN_DEPTH = 7;
|
|
3847
3889
|
function homeRoots(homeDir, platform) {
|
|
3848
3890
|
if (platform === "claude-code") {
|
|
3849
|
-
return [
|
|
3891
|
+
return [path7.join(homeDir, ".claude", "skills")];
|
|
3850
3892
|
}
|
|
3851
3893
|
if (platform === "codex") {
|
|
3852
3894
|
return [
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3895
|
+
path7.join(homeDir, ".codex", "skills"),
|
|
3896
|
+
path7.join(homeDir, ".agents", "skills"),
|
|
3897
|
+
path7.join(homeDir, ".codex", "plugins", "cache")
|
|
3856
3898
|
];
|
|
3857
3899
|
}
|
|
3858
|
-
return [
|
|
3900
|
+
return [path7.join(homeDir, ".config", "opencode", "skills")];
|
|
3859
3901
|
}
|
|
3860
3902
|
function ancestorDirs(cwd) {
|
|
3861
3903
|
const dirs = [];
|
|
3862
|
-
let current =
|
|
3904
|
+
let current = path7.resolve(cwd);
|
|
3863
3905
|
for (let depth = 0; depth < 16; depth++) {
|
|
3864
3906
|
dirs.push(current);
|
|
3865
|
-
const parent =
|
|
3907
|
+
const parent = path7.dirname(current);
|
|
3866
3908
|
if (parent === current)
|
|
3867
3909
|
break;
|
|
3868
3910
|
current = parent;
|
|
@@ -3872,11 +3914,11 @@ function ancestorDirs(cwd) {
|
|
|
3872
3914
|
function repoRoots(cwd, platform) {
|
|
3873
3915
|
return ancestorDirs(cwd).flatMap((dir) => {
|
|
3874
3916
|
if (platform === "claude-code")
|
|
3875
|
-
return [
|
|
3917
|
+
return [path7.join(dir, ".claude", "skills")];
|
|
3876
3918
|
if (platform === "codex") {
|
|
3877
|
-
return [
|
|
3919
|
+
return [path7.join(dir, ".codex", "skills"), path7.join(dir, ".agents", "skills")];
|
|
3878
3920
|
}
|
|
3879
|
-
return [
|
|
3921
|
+
return [path7.join(dir, ".opencode", "skills")];
|
|
3880
3922
|
});
|
|
3881
3923
|
}
|
|
3882
3924
|
function frontmatterName(content) {
|
|
@@ -3892,14 +3934,14 @@ function readNativeSkillNames(params) {
|
|
|
3892
3934
|
return;
|
|
3893
3935
|
let entries;
|
|
3894
3936
|
try {
|
|
3895
|
-
entries =
|
|
3937
|
+
entries = fs7.readdirSync(dir, { withFileTypes: true });
|
|
3896
3938
|
} catch {
|
|
3897
3939
|
return;
|
|
3898
3940
|
}
|
|
3899
3941
|
for (const entry of entries) {
|
|
3900
3942
|
if (visited >= MAX_NATIVE_SKILL_FILES)
|
|
3901
3943
|
return;
|
|
3902
|
-
const full =
|
|
3944
|
+
const full = path7.join(dir, entry.name);
|
|
3903
3945
|
if (entry.isDirectory()) {
|
|
3904
3946
|
walk(full, depth + 1);
|
|
3905
3947
|
continue;
|
|
@@ -3907,9 +3949,9 @@ function readNativeSkillNames(params) {
|
|
|
3907
3949
|
if (!entry.isFile() || entry.name.toLowerCase() !== "skill.md")
|
|
3908
3950
|
continue;
|
|
3909
3951
|
visited++;
|
|
3910
|
-
names.add(
|
|
3952
|
+
names.add(path7.basename(path7.dirname(full)).toLowerCase());
|
|
3911
3953
|
try {
|
|
3912
|
-
const declared = frontmatterName(
|
|
3954
|
+
const declared = frontmatterName(fs7.readFileSync(full, "utf8"));
|
|
3913
3955
|
if (declared)
|
|
3914
3956
|
names.add(declared.toLowerCase());
|
|
3915
3957
|
} catch {
|
|
@@ -3941,14 +3983,14 @@ function activationFromApiRows(params) {
|
|
|
3941
3983
|
}
|
|
3942
3984
|
|
|
3943
3985
|
// src/opencode-plugin.ts
|
|
3944
|
-
var DEBUG_LOG_PATH =
|
|
3986
|
+
var DEBUG_LOG_PATH = path8.join(os3.homedir(), ".greprag", "opencode-plugin-debug.log");
|
|
3945
3987
|
var _debugLogReady = false;
|
|
3946
3988
|
function dlogInit() {
|
|
3947
3989
|
if (_debugLogReady)
|
|
3948
3990
|
return;
|
|
3949
3991
|
_debugLogReady = true;
|
|
3950
3992
|
try {
|
|
3951
|
-
|
|
3993
|
+
fs8.writeFileSync(DEBUG_LOG_PATH, "");
|
|
3952
3994
|
} catch {
|
|
3953
3995
|
}
|
|
3954
3996
|
}
|
|
@@ -3957,7 +3999,7 @@ function dlog(msg) {
|
|
|
3957
3999
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [greprag-memory] ${msg}
|
|
3958
4000
|
`;
|
|
3959
4001
|
try {
|
|
3960
|
-
|
|
4002
|
+
fs8.appendFileSync(DEBUG_LOG_PATH, line);
|
|
3961
4003
|
} catch {
|
|
3962
4004
|
}
|
|
3963
4005
|
}
|
|
@@ -3965,9 +4007,9 @@ dlog(`module top reached: pid=${process.pid} argv0=${process.argv[0]} distPath=$
|
|
|
3965
4007
|
var API_URL = "https://api.greprag.com";
|
|
3966
4008
|
function loadEnvFile(filePath) {
|
|
3967
4009
|
try {
|
|
3968
|
-
if (!
|
|
4010
|
+
if (!fs8.existsSync(filePath))
|
|
3969
4011
|
return;
|
|
3970
|
-
const raw =
|
|
4012
|
+
const raw = fs8.readFileSync(filePath, "utf-8");
|
|
3971
4013
|
for (const line of raw.split(/\r?\n/)) {
|
|
3972
4014
|
const trimmed = line.trim();
|
|
3973
4015
|
if (!trimmed || trimmed.startsWith("#"))
|
|
@@ -3987,12 +4029,12 @@ function loadEnvFile(filePath) {
|
|
|
3987
4029
|
}
|
|
3988
4030
|
}
|
|
3989
4031
|
function loadGrepragEnv() {
|
|
3990
|
-
loadEnvFile(
|
|
4032
|
+
loadEnvFile(path8.join(HOME, ".greprag", ".env"));
|
|
3991
4033
|
try {
|
|
3992
|
-
const p =
|
|
3993
|
-
if (!
|
|
4034
|
+
const p = path8.join(HOME, ".claude", "settings.json");
|
|
4035
|
+
if (!fs8.existsSync(p))
|
|
3994
4036
|
return;
|
|
3995
|
-
const data = JSON.parse(
|
|
4037
|
+
const data = JSON.parse(fs8.readFileSync(p, "utf-8"));
|
|
3996
4038
|
if (data && data.env && typeof data.env === "object") {
|
|
3997
4039
|
for (const [key, val] of Object.entries(data.env)) {
|
|
3998
4040
|
if (!process.env[key] && typeof val === "string") {
|
|
@@ -4005,11 +4047,11 @@ function loadGrepragEnv() {
|
|
|
4005
4047
|
}
|
|
4006
4048
|
loadGrepragEnv();
|
|
4007
4049
|
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>"}`);
|
|
4008
|
-
var WATCHER_LOCK =
|
|
4050
|
+
var WATCHER_LOCK = path8.join(HOME || os3.homedir(), ".greprag", "opencode-watch.lock");
|
|
4009
4051
|
var WATCHER_STALE_MS = 3e4;
|
|
4010
4052
|
function findGrepragBinary() {
|
|
4011
4053
|
const override = process.env.GREPRAG_BIN;
|
|
4012
|
-
if (override &&
|
|
4054
|
+
if (override && fs8.existsSync(override))
|
|
4013
4055
|
return override;
|
|
4014
4056
|
const binaryName = process.platform === "win32" ? "greprag.cmd" : "greprag";
|
|
4015
4057
|
try {
|
|
@@ -4018,14 +4060,14 @@ function findGrepragBinary() {
|
|
|
4018
4060
|
const candidate = line.trim();
|
|
4019
4061
|
if (!candidate)
|
|
4020
4062
|
continue;
|
|
4021
|
-
if (!
|
|
4063
|
+
if (!fs8.existsSync(candidate))
|
|
4022
4064
|
continue;
|
|
4023
4065
|
if (process.platform === "win32") {
|
|
4024
|
-
const ext =
|
|
4066
|
+
const ext = path8.extname(candidate).toLowerCase();
|
|
4025
4067
|
if (ext === ".cmd" || ext === ".exe" || ext === ".bat")
|
|
4026
4068
|
return candidate;
|
|
4027
4069
|
const cmdSibling = candidate + ".cmd";
|
|
4028
|
-
if (
|
|
4070
|
+
if (fs8.existsSync(cmdSibling))
|
|
4029
4071
|
return cmdSibling;
|
|
4030
4072
|
continue;
|
|
4031
4073
|
}
|
|
@@ -4034,19 +4076,19 @@ function findGrepragBinary() {
|
|
|
4034
4076
|
} catch {
|
|
4035
4077
|
}
|
|
4036
4078
|
const candidates = process.platform === "win32" ? [
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4079
|
+
path8.join(HOME, "AppData", "Roaming", "npm", "greprag.cmd"),
|
|
4080
|
+
path8.join(HOME, "AppData", "Roaming", "npm", "greprag"),
|
|
4081
|
+
path8.join(HOME, "AppData", "Local", "Yarn", "bin", "greprag.cmd"),
|
|
4082
|
+
path8.join(HOME, "AppData", "Local", "pnpm", "bin", "greprag.cmd")
|
|
4041
4083
|
] : [
|
|
4042
4084
|
"/usr/local/bin/greprag",
|
|
4043
4085
|
"/opt/homebrew/bin/greprag",
|
|
4044
|
-
|
|
4045
|
-
|
|
4086
|
+
path8.join(HOME || "", ".local", "bin", "greprag"),
|
|
4087
|
+
path8.join(HOME || "", ".yarn", "bin", "greprag")
|
|
4046
4088
|
];
|
|
4047
4089
|
for (const c of candidates) {
|
|
4048
4090
|
try {
|
|
4049
|
-
if (c &&
|
|
4091
|
+
if (c && fs8.existsSync(c))
|
|
4050
4092
|
return c;
|
|
4051
4093
|
} catch {
|
|
4052
4094
|
}
|
|
@@ -4055,18 +4097,18 @@ function findGrepragBinary() {
|
|
|
4055
4097
|
}
|
|
4056
4098
|
function tryClaimWatcherLock() {
|
|
4057
4099
|
try {
|
|
4058
|
-
const fd =
|
|
4100
|
+
const fd = fs8.openSync(WATCHER_LOCK, "wx");
|
|
4059
4101
|
return { ok: true, fd };
|
|
4060
4102
|
} catch (err) {
|
|
4061
4103
|
if (err.code !== "EEXIST") {
|
|
4062
4104
|
return { ok: false, reason: "lock-create-failed" };
|
|
4063
4105
|
}
|
|
4064
4106
|
try {
|
|
4065
|
-
const stat =
|
|
4107
|
+
const stat = fs8.statSync(WATCHER_LOCK);
|
|
4066
4108
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
4067
4109
|
let pidAlive = false;
|
|
4068
4110
|
try {
|
|
4069
|
-
const pid = parseInt(
|
|
4111
|
+
const pid = parseInt(fs8.readFileSync(WATCHER_LOCK, "utf-8").trim(), 10);
|
|
4070
4112
|
pidAlive = pid > 0 && isPidAlive(pid);
|
|
4071
4113
|
} catch {
|
|
4072
4114
|
}
|
|
@@ -4074,10 +4116,10 @@ function tryClaimWatcherLock() {
|
|
|
4074
4116
|
return { ok: false, reason: "busy" };
|
|
4075
4117
|
}
|
|
4076
4118
|
try {
|
|
4077
|
-
|
|
4119
|
+
fs8.unlinkSync(WATCHER_LOCK);
|
|
4078
4120
|
} catch {
|
|
4079
4121
|
}
|
|
4080
|
-
const fd =
|
|
4122
|
+
const fd = fs8.openSync(WATCHER_LOCK, "wx");
|
|
4081
4123
|
return { ok: true, fd };
|
|
4082
4124
|
} catch (err2) {
|
|
4083
4125
|
return { ok: false, reason: "lock-stale-replace-failed" };
|
|
@@ -4118,11 +4160,11 @@ function startCaptureWatcher() {
|
|
|
4118
4160
|
return;
|
|
4119
4161
|
}
|
|
4120
4162
|
try {
|
|
4121
|
-
|
|
4122
|
-
|
|
4163
|
+
fs8.writeSync(claim.fd, String(process.pid));
|
|
4164
|
+
fs8.closeSync(claim.fd);
|
|
4123
4165
|
} catch {
|
|
4124
4166
|
try {
|
|
4125
|
-
|
|
4167
|
+
fs8.unlinkSync(WATCHER_LOCK);
|
|
4126
4168
|
} catch {
|
|
4127
4169
|
}
|
|
4128
4170
|
return;
|
|
@@ -4138,11 +4180,11 @@ function startCaptureWatcher() {
|
|
|
4138
4180
|
const claim2 = tryClaimWatcherLock();
|
|
4139
4181
|
if (claim2.ok) {
|
|
4140
4182
|
try {
|
|
4141
|
-
|
|
4142
|
-
|
|
4183
|
+
fs8.writeSync(claim2.fd, String(process.pid));
|
|
4184
|
+
fs8.closeSync(claim2.fd);
|
|
4143
4185
|
} catch {
|
|
4144
4186
|
try {
|
|
4145
|
-
|
|
4187
|
+
fs8.unlinkSync(WATCHER_LOCK);
|
|
4146
4188
|
} catch {
|
|
4147
4189
|
}
|
|
4148
4190
|
return false;
|
|
@@ -4185,7 +4227,7 @@ function startCaptureWatcher() {
|
|
|
4185
4227
|
childStartTime = Date.now();
|
|
4186
4228
|
dlog(`startCaptureWatcher: spawned 'greprag opencode watch' pid=${child.pid ?? "<none>"}`);
|
|
4187
4229
|
try {
|
|
4188
|
-
|
|
4230
|
+
fs8.writeFileSync(WATCHER_LOCK, String(child.pid));
|
|
4189
4231
|
} catch {
|
|
4190
4232
|
}
|
|
4191
4233
|
child.stderr?.on("data", (chunk2) => {
|
|
@@ -4197,9 +4239,9 @@ function startCaptureWatcher() {
|
|
|
4197
4239
|
child.on("exit", (code, signal) => {
|
|
4198
4240
|
dlog(`startCaptureWatcher: child pid=${child.pid ?? "<none>"} exited code=${code} signal=${signal || "none"}`);
|
|
4199
4241
|
try {
|
|
4200
|
-
const current =
|
|
4242
|
+
const current = fs8.readFileSync(WATCHER_LOCK, "utf-8").trim();
|
|
4201
4243
|
if (current === String(child.pid))
|
|
4202
|
-
|
|
4244
|
+
fs8.unlinkSync(WATCHER_LOCK);
|
|
4203
4245
|
} catch {
|
|
4204
4246
|
}
|
|
4205
4247
|
currentChild = null;
|
|
@@ -4228,7 +4270,7 @@ function startCaptureWatcher() {
|
|
|
4228
4270
|
`
|
|
4229
4271
|
);
|
|
4230
4272
|
try {
|
|
4231
|
-
|
|
4273
|
+
fs8.unlinkSync(WATCHER_LOCK);
|
|
4232
4274
|
} catch {
|
|
4233
4275
|
}
|
|
4234
4276
|
return;
|
|
@@ -4260,7 +4302,7 @@ function startCaptureWatcher() {
|
|
|
4260
4302
|
}, 2e3).unref();
|
|
4261
4303
|
}
|
|
4262
4304
|
try {
|
|
4263
|
-
|
|
4305
|
+
fs8.unlinkSync(WATCHER_LOCK);
|
|
4264
4306
|
} catch {
|
|
4265
4307
|
}
|
|
4266
4308
|
};
|
|
@@ -4296,6 +4338,8 @@ async function fetchRecapInbox(anchor) {
|
|
|
4296
4338
|
}
|
|
4297
4339
|
var relayArmedBySession = /* @__PURE__ */ new Set();
|
|
4298
4340
|
var announcedBySession = /* @__PURE__ */ new Set();
|
|
4341
|
+
var personaAnnounceBySession = /* @__PURE__ */ new Map();
|
|
4342
|
+
var PERSONA_SESSION_CACHE_MAX = 100;
|
|
4299
4343
|
var turnCountBySession = /* @__PURE__ */ new Map();
|
|
4300
4344
|
var procedurePromptKeyBySession = /* @__PURE__ */ new Map();
|
|
4301
4345
|
var cachedRecap = null;
|
|
@@ -4333,6 +4377,32 @@ async function fetchSkillActivation(apiKey, cwd) {
|
|
|
4333
4377
|
return void 0;
|
|
4334
4378
|
}
|
|
4335
4379
|
}
|
|
4380
|
+
async function fetchPersonaAnnounce(apiKey) {
|
|
4381
|
+
try {
|
|
4382
|
+
const response = await fetch(`${API_URL}/v1/persona`, {
|
|
4383
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
4384
|
+
});
|
|
4385
|
+
if (!response.ok)
|
|
4386
|
+
return null;
|
|
4387
|
+
const data = await response.json();
|
|
4388
|
+
return typeof data.announce === "string" && data.announce.length > 0 ? data.announce : null;
|
|
4389
|
+
} catch {
|
|
4390
|
+
return null;
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4393
|
+
async function personaAnnounceForSession(sessionId, apiKey) {
|
|
4394
|
+
if (personaAnnounceBySession.has(sessionId)) {
|
|
4395
|
+
return personaAnnounceBySession.get(sessionId) ?? null;
|
|
4396
|
+
}
|
|
4397
|
+
const announce = await fetchPersonaAnnounce(apiKey);
|
|
4398
|
+
if (personaAnnounceBySession.size >= PERSONA_SESSION_CACHE_MAX) {
|
|
4399
|
+
const oldest = personaAnnounceBySession.keys().next().value;
|
|
4400
|
+
if (oldest)
|
|
4401
|
+
personaAnnounceBySession.delete(oldest);
|
|
4402
|
+
}
|
|
4403
|
+
personaAnnounceBySession.set(sessionId, announce);
|
|
4404
|
+
return announce;
|
|
4405
|
+
}
|
|
4336
4406
|
function startSessionRelay(sessionId, serverUrl) {
|
|
4337
4407
|
if (process.env.GREPRAG_OPENCODE_RELAY === "0")
|
|
4338
4408
|
return;
|
|
@@ -4369,11 +4439,11 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
4369
4439
|
return;
|
|
4370
4440
|
}
|
|
4371
4441
|
try {
|
|
4372
|
-
|
|
4373
|
-
|
|
4442
|
+
fs8.writeSync(claim.fd, String(process.pid));
|
|
4443
|
+
fs8.closeSync(claim.fd);
|
|
4374
4444
|
} catch {
|
|
4375
4445
|
try {
|
|
4376
|
-
|
|
4446
|
+
fs8.unlinkSync(lockPath);
|
|
4377
4447
|
} catch {
|
|
4378
4448
|
}
|
|
4379
4449
|
return;
|
|
@@ -4388,11 +4458,11 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
4388
4458
|
const claim2 = tryClaimRelayLock(lockPath);
|
|
4389
4459
|
if (claim2.ok) {
|
|
4390
4460
|
try {
|
|
4391
|
-
|
|
4392
|
-
|
|
4461
|
+
fs8.writeSync(claim2.fd, String(process.pid));
|
|
4462
|
+
fs8.closeSync(claim2.fd);
|
|
4393
4463
|
} catch {
|
|
4394
4464
|
try {
|
|
4395
|
-
|
|
4465
|
+
fs8.unlinkSync(lockPath);
|
|
4396
4466
|
} catch {
|
|
4397
4467
|
}
|
|
4398
4468
|
return false;
|
|
@@ -4428,7 +4498,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
4428
4498
|
childStartTime = Date.now();
|
|
4429
4499
|
dlog(`startSessionRelay: spawned 'greprag opencode relay' pid=${child.pid ?? "<none>"}`);
|
|
4430
4500
|
try {
|
|
4431
|
-
|
|
4501
|
+
fs8.writeFileSync(lockPath, String(child.pid));
|
|
4432
4502
|
} catch {
|
|
4433
4503
|
}
|
|
4434
4504
|
child.stderr?.on("data", (chunk2) => {
|
|
@@ -4439,9 +4509,9 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
4439
4509
|
});
|
|
4440
4510
|
child.on("exit", (code, signal) => {
|
|
4441
4511
|
try {
|
|
4442
|
-
const current =
|
|
4512
|
+
const current = fs8.readFileSync(lockPath, "utf-8").trim();
|
|
4443
4513
|
if (current === String(child.pid))
|
|
4444
|
-
|
|
4514
|
+
fs8.unlinkSync(lockPath);
|
|
4445
4515
|
} catch {
|
|
4446
4516
|
}
|
|
4447
4517
|
currentChild = null;
|
|
@@ -4468,7 +4538,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
4468
4538
|
`
|
|
4469
4539
|
);
|
|
4470
4540
|
try {
|
|
4471
|
-
|
|
4541
|
+
fs8.unlinkSync(lockPath);
|
|
4472
4542
|
} catch {
|
|
4473
4543
|
}
|
|
4474
4544
|
return;
|
|
@@ -4500,7 +4570,7 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
4500
4570
|
}, 2e3).unref();
|
|
4501
4571
|
}
|
|
4502
4572
|
try {
|
|
4503
|
-
|
|
4573
|
+
fs8.unlinkSync(lockPath);
|
|
4504
4574
|
} catch {
|
|
4505
4575
|
}
|
|
4506
4576
|
};
|
|
@@ -4667,6 +4737,7 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
|
|
|
4667
4737
|
if (sid8) {
|
|
4668
4738
|
const turnCount = (turnCountBySession.get(sid8) ?? 0) + 1;
|
|
4669
4739
|
turnCountBySession.set(sid8, turnCount);
|
|
4740
|
+
const firstAnnounce = !announcedBySession.has(sid8);
|
|
4670
4741
|
const env = buildOpenCodeEnv({
|
|
4671
4742
|
short: sid8,
|
|
4672
4743
|
turnCount,
|
|
@@ -4682,9 +4753,12 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
|
|
|
4682
4753
|
return [];
|
|
4683
4754
|
}
|
|
4684
4755
|
})(),
|
|
4685
|
-
mirroredSkills:
|
|
4756
|
+
mirroredSkills: firstAnnounce ? await fetchSkillActivation(apiKey, workingDir) : void 0,
|
|
4757
|
+
// output.system is fresh on EVERY transform call. Resolve Persona
|
|
4758
|
+
// once/session, then replay the cached shared-registry announce.
|
|
4759
|
+
personaAnnounce: await personaAnnounceForSession(sid8, apiKey)
|
|
4686
4760
|
});
|
|
4687
|
-
if (
|
|
4761
|
+
if (firstAnnounce) {
|
|
4688
4762
|
announcedBySession.add(sid8);
|
|
4689
4763
|
const announces = getOpenCodeAnnounces(env);
|
|
4690
4764
|
if (announces.length > 0) {
|
|
@@ -4693,6 +4767,13 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
|
|
|
4693
4767
|
}
|
|
4694
4768
|
dlog(`interrupt: pushed ${announces.length} announce(s) for sid=${sid8}`);
|
|
4695
4769
|
}
|
|
4770
|
+
} else {
|
|
4771
|
+
const persistent = getOpenCodePersistentAnnounces(env);
|
|
4772
|
+
for (const line of persistent)
|
|
4773
|
+
pushSystemPrompt(output, line);
|
|
4774
|
+
if (persistent.length > 0) {
|
|
4775
|
+
dlog(`interrupt: replayed ${persistent.length} persistent announce(s) for sid=${sid8}`);
|
|
4776
|
+
}
|
|
4696
4777
|
}
|
|
4697
4778
|
const reminders = getOpenCodeReminders(env);
|
|
4698
4779
|
if (reminders.length > 0) {
|