greprag 5.32.0 → 5.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/codex-hook-events.d.ts +20 -0
  2. package/dist/codex-hook-events.js +156 -0
  3. package/dist/codex-hook-events.js.map +1 -0
  4. package/dist/commands/codex-app-server.d.ts +1 -0
  5. package/dist/commands/codex-app-server.js +179 -0
  6. package/dist/commands/codex-app-server.js.map +1 -0
  7. package/dist/commands/codex-doctor.js +3 -1
  8. package/dist/commands/codex-doctor.js.map +1 -1
  9. package/dist/commands/codex.js +6 -0
  10. package/dist/commands/codex.js.map +1 -1
  11. package/dist/commands/corpus/index.d.ts +1 -0
  12. package/dist/commands/corpus/index.js +5 -0
  13. package/dist/commands/corpus/index.js.map +1 -1
  14. package/dist/commands/corpus/refresh.d.ts +1 -0
  15. package/dist/commands/corpus/refresh.js +60 -0
  16. package/dist/commands/corpus/refresh.js.map +1 -1
  17. package/dist/commands/desk-line.d.ts +36 -0
  18. package/dist/commands/desk-line.js +248 -0
  19. package/dist/commands/desk-line.js.map +1 -0
  20. package/dist/commands/inbox-spool.d.ts +27 -0
  21. package/dist/commands/inbox-spool.js +151 -0
  22. package/dist/commands/inbox-spool.js.map +1 -0
  23. package/dist/commands/inbox-watch.js +59 -8
  24. package/dist/commands/inbox-watch.js.map +1 -1
  25. package/dist/commands/init.js +75 -7
  26. package/dist/commands/init.js.map +1 -1
  27. package/dist/commands/opencode-relay.d.ts +136 -0
  28. package/dist/commands/opencode-relay.js +529 -0
  29. package/dist/commands/opencode-relay.js.map +1 -0
  30. package/dist/commands/opencode-watch.d.ts +17 -0
  31. package/dist/commands/opencode-watch.js +493 -0
  32. package/dist/commands/opencode-watch.js.map +1 -0
  33. package/dist/commands/status.d.ts +2 -0
  34. package/dist/commands/status.js +5 -1
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/commands/watcher-registry.d.ts +8 -0
  37. package/dist/commands/watcher-registry.js +19 -0
  38. package/dist/commands/watcher-registry.js.map +1 -1
  39. package/dist/hook.js +54 -83
  40. package/dist/hook.js.map +1 -1
  41. package/dist/index.js +220 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/opencode-plugin-helpers.d.ts +200 -0
  44. package/dist/opencode-plugin-helpers.js +512 -0
  45. package/dist/opencode-plugin-helpers.js.map +1 -0
  46. package/dist/opencode-plugin.d.ts +37 -134
  47. package/dist/opencode-plugin.js +648 -364
  48. package/dist/opencode-plugin.js.map +1 -1
  49. package/dist/session-id.d.ts +8 -6
  50. package/dist/session-id.js +10 -9
  51. package/dist/session-id.js.map +1 -1
  52. package/package.json +8 -4
@@ -0,0 +1,200 @@
1
+ /** GrepRAG opencode plugin — test-friendly helpers.
2
+ *
3
+ * Pure functions and type definitions extracted from `opencode-plugin.ts` so
4
+ * the deployed plugin file can use a single `export = { id, server }` shape
5
+ * (no `__esModule: true` wrapper, no `__test` helper export). The opencode
6
+ * legacy plugin loader iterates `Object.values(mod)` and rejects any value
7
+ * that isn't a function or `{ server: function }`; an `__esModule: true`
8
+ * boolean in that iteration throws "Plugin export is not a function" and the
9
+ * plugin registers zero hooks. See `adr/opencode-monitor-relay.md` 2026-06-06
10
+ * entries (e) and (f) for the full diagnosis.
11
+ *
12
+ * Three callers, all out-of-band of opencode's loader:
13
+ * - `opencode-plugin.ts` — the deployed plugin source imports these for use
14
+ * inside the V1 server function (recap renderer + anchor + lockfile).
15
+ * - `hook.ts` — Claude/Codex SessionStart hook imports the recap renderer
16
+ * so the recap body pushed to the system prompt is identical between
17
+ * harnesses. The hook keeps its own preamble (setup warning, checkpoint
18
+ * hint) — those are session-start UX concerns, not recap content.
19
+ * - `tests/test-opencode-plugin.cjs` and `tests/test-opencode-relay-spawn.cjs`
20
+ * — unit tests pull the same helpers by `require()` to exercise envelope
21
+ * construction, anchor resolution, and lockfile dedup without booting
22
+ * opencode.
23
+ *
24
+ * opencode never loads this file directly. Its presence at
25
+ * `~/.config/opencode/plugins/greprag-memory-helpers.js` is incidental to the
26
+ * deploy step; only `greprag-memory.js` is registered in the user's
27
+ * `opencode.json` plugin list.
28
+ */
29
+ declare const HOME: string;
30
+ interface TextPart {
31
+ type: 'text';
32
+ text: string;
33
+ synthetic?: boolean;
34
+ ignored?: boolean;
35
+ }
36
+ type ToolState = {
37
+ status: 'pending';
38
+ input: Record<string, unknown>;
39
+ } | {
40
+ status: 'running';
41
+ input: Record<string, unknown>;
42
+ title?: string;
43
+ } | {
44
+ status: 'completed';
45
+ input: Record<string, unknown>;
46
+ output: string;
47
+ title: string;
48
+ } | {
49
+ status: 'error';
50
+ input: Record<string, unknown>;
51
+ error: string;
52
+ };
53
+ interface ToolPart {
54
+ type: 'tool';
55
+ callID: string;
56
+ tool: string;
57
+ state: ToolState;
58
+ }
59
+ interface GenericPart {
60
+ type: string;
61
+ [key: string]: unknown;
62
+ }
63
+ type Part = TextPart | ToolPart | GenericPart;
64
+ interface ProjectAnchor {
65
+ projectId: string;
66
+ projectName: string;
67
+ memoryCapture: boolean;
68
+ sessionStartRecap: boolean;
69
+ inboxNotify: 'every_turn' | 'session_start_only' | 'off';
70
+ }
71
+ /** Parsed `.greprag/project.json` contents. `project_id` and `project_name` are
72
+ * both optional after v0.7 — a file may carry per-project settings only and
73
+ * let identity flow from git derivation. */
74
+ interface AnchorFile {
75
+ projectId?: string;
76
+ projectName?: string;
77
+ memoryCapture: boolean;
78
+ sessionStartRecap: boolean;
79
+ inboxNotify: 'every_turn' | 'session_start_only' | 'off';
80
+ }
81
+ declare function readAnchorFile(filePath: string): AnchorFile | null;
82
+ /** Walk up from cwd looking for an anchor file. Checks canonical `.greprag/`
83
+ * first, then legacy `.claude/` and `.opencode/` at each level. Returns the
84
+ * first hit. Skips home-level globals; those are reserved for the
85
+ * ephemeral-cwd path of the cascade. */
86
+ declare function findExistingAnchorFile(startDir: string): string | null;
87
+ /** Format the SHA-256 of an input string into a deterministic UUID v4-shape
88
+ * string. Must match the algorithm in packages/cli/src/project-anchor.ts so
89
+ * the plugin resolves identical IDs to the Claude Code CLI. */
90
+ declare function formatUuid(hashHex: string): string;
91
+ /** Derive a stable project_id from the repo's root commit SHA. Sorted +
92
+ * hashed so disjoint-history merges produce one deterministic id everywhere.
93
+ * Returns null when cwd isn't a git repo or has no commits yet. */
94
+ declare function computeGitDerivedProjectId(cwd: string): string | null;
95
+ /** Hash the cwd path into a stable UUID. Last-resort fallback — when no git
96
+ * history and no anchor file exist, this keeps capture flowing under a
97
+ * per-path identity that `greprag doctor` can later consolidate. */
98
+ declare function deterministicProjectId(cwd: string): string;
99
+ /** True when cwd is an ephemeral session path (Cowork, tmp dirs). The
100
+ * deterministic-hash fallback would mint a fresh id every session in those
101
+ * paths, so we prefer the global anchor instead. */
102
+ declare function isEphemeralCwd(cwd: string): boolean;
103
+ /** Resolve the project anchor for `worktree` using the same 4-level cascade
104
+ * the Claude Code CLI uses (packages/cli/src/project-anchor.ts). Settings
105
+ * always come from the nearest repo-level file when one exists, regardless
106
+ * of which identity level resolved.
107
+ *
108
+ * 1. Anchor file with explicit `project_id` → file-based identity
109
+ * 2. Git repo with at least one commit → root-commit-derived UUID
110
+ * 3. Ephemeral cwd + ~/.greprag/project.json exists → global anchor
111
+ * 4. Path-hash fallback (never returns null; lets capture flow until
112
+ * `greprag init` runs and `greprag doctor` consolidates) */
113
+ declare function readAnchor(worktree: string): ProjectAnchor;
114
+ declare function extractText(parts: Part[]): string;
115
+ interface ToolCallSummary {
116
+ name: string;
117
+ target?: string;
118
+ brief?: string;
119
+ }
120
+ /** Pull a one-line summary out of each tool call: name + a target string + an
121
+ * optional brief for shell commands. Mirrors the shape the Claude Code hook
122
+ * posts so server-side compaction sees identical structure regardless of
123
+ * client origin. */
124
+ declare function extractToolCalls(parts: Part[]): ToolCallSummary[];
125
+ declare function extractFilesTouched(parts: Part[]): string[];
126
+ type TurnProvenance = 'session-turn' | 'skill-injection' | 'continuation-summary' | 'chip-prompt';
127
+ declare function classifyUserText(text: string): TurnProvenance;
128
+ declare function provenanceElisionMarker(p: TurnProvenance, text: string): string;
129
+ interface Envelope {
130
+ userPrompt: string;
131
+ agentResponse: string;
132
+ toolCalls: ToolCallSummary[];
133
+ filesTouched: string[];
134
+ status: 'completed' | 'errored';
135
+ provenance: TurnProvenance;
136
+ }
137
+ declare function buildEnvelope(userParts: Part[], assistantParts: Part[], errored: boolean): Envelope;
138
+ /** True when the OS reports `pid` as a running process. Used to decide whether
139
+ * a stale-lockfile check should trust the recorded PID or treat the file as
140
+ * abandoned. */
141
+ export declare function isPidAlive(pid: number): boolean;
142
+ declare function relayLockPath(sessionId: string): string;
143
+ declare function tryClaimRelayLock(lockPath: string): {
144
+ ok: true;
145
+ fd: number;
146
+ } | {
147
+ ok: false;
148
+ reason: string;
149
+ };
150
+ /** One row from `/v1/memory/by-period`. Mirrors hook.ts ByPeriodRow — kept
151
+ * in sync intentionally so the two paths fetch the same shape. */
152
+ interface ByPeriodRow {
153
+ id: string;
154
+ projectId: string | null;
155
+ projectName: string | null;
156
+ crystallization: string;
157
+ content: string;
158
+ wordCount: number;
159
+ windowStart: string | null;
160
+ windowEnd: string | null;
161
+ branch: string | null;
162
+ artifactType: string | null;
163
+ artifactId: string | null;
164
+ artifactUrl: string | null;
165
+ createdAt: string;
166
+ }
167
+ /** Cap on the number of hourlies surfaced in the recap body. 3 — the hook's
168
+ * established value; if the user wants more they pull on demand via
169
+ * `greprag memory recap`. adr: adr/memory-recap-pyramid.md. */
170
+ declare const HOURLY_CAP = 3;
171
+ /** Strip noise blocks from compacted content before injection.
172
+ * Compactor contract: numbered items end with a trailer ("Open: ..." for
173
+ * hourly, "Shipped: ..." for daily) which may span multiple lines. Trailers
174
+ * always come AFTER the numbered list — so we drop from the trigger line
175
+ * through end-of-string.
176
+ *
177
+ * - daily: drop the deterministic "Shipped: <UUID-list>" trailer (useful for
178
+ * /greprag deep-dive, but a wall of IDs at session start)
179
+ * - hourly: drop the per-hour "Open:" block (stale snapshot of open items
180
+ * that were resolved or superseded in later hours) */
181
+ declare function stripRecapNoise(content: string, type: 'daily' | 'hourly'): string;
182
+ /** Render an hourly window's recency: "5 hours ago", "1 hour ago",
183
+ * "30 min ago", "just now". Reference is the end of the window — when the
184
+ * hour wrapped up. */
185
+ declare function fmtHoursAgo(windowEndIso: string, now: Date): string;
186
+ /** Build the recap body — hourlies-only render. Recap is hourlies-only by
187
+ * design: daily summaries used to render here (one prose paragraph per
188
+ * daily window) but added ~1500 tokens to every SessionStart for marginal
189
+ * value. Dailies still exist in the DB; agents who want historic context
190
+ * pull on demand via `greprag memory recap` (or
191
+ * `greprag memory search "<query>"` for a specific topic). Pull, don't push.
192
+ *
193
+ * Window: 2 days back to now (50-row pull), filtered to last 24h, sliced
194
+ * to HOURLY_CAP most-recent.
195
+ *
196
+ * Returns "" when there are no recent hourlies — caller treats that as
197
+ * "no recap block". Empty body is not an error; the query itself is the
198
+ * only network call and any failure is swallowed (returns ""). */
199
+ declare function buildRecapBody(apiUrl: string, apiKey: string, anchor: ProjectAnchor, now?: Date): Promise<string>;
200
+ export { HOME, type Part, type TextPart, type ToolPart, type ToolState, type GenericPart, type ProjectAnchor, type AnchorFile, type ToolCallSummary, type Envelope, type TurnProvenance, type ByPeriodRow, formatUuid, computeGitDerivedProjectId, deterministicProjectId, isEphemeralCwd, readAnchorFile, findExistingAnchorFile, readAnchor, extractText, extractToolCalls, extractFilesTouched, classifyUserText, provenanceElisionMarker, buildEnvelope, relayLockPath, tryClaimRelayLock, stripRecapNoise, fmtHoursAgo, buildRecapBody, HOURLY_CAP, };