greprag 5.3.0 → 5.5.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.
@@ -0,0 +1,41 @@
1
+ /** CLI plumbing for `greprag checkpoint`. Pulled out of the subcommand
2
+ * dispatch file (checkpoint.ts) to keep both under the 500-line ceiling.
3
+ *
4
+ * Owns: env / config loading, HTTP wrappers, project resolution, git
5
+ * context capture, --since parsing, generic-title pre-check, age
6
+ * formatting. Pure plumbing — no business logic.
7
+ */
8
+ export declare function loadEnvFile(filePath: string): void;
9
+ export declare function getConfig(): {
10
+ apiUrl: string;
11
+ apiKey: string;
12
+ };
13
+ export declare function abortNotConfigured(): never;
14
+ export declare function apiPost<T>(url: string, apiKey: string, body: Record<string, unknown>): Promise<T>;
15
+ export declare function apiGet<T>(url: string, apiKey: string): Promise<T>;
16
+ export declare function apiPatch<T>(url: string, apiKey: string, body: Record<string, unknown>): Promise<T>;
17
+ export declare function apiDelete<T>(url: string, apiKey: string): Promise<T>;
18
+ export declare function getFlag(args: string[], flag: string): string | undefined;
19
+ export interface ResolvedProject {
20
+ projectId: string;
21
+ projectName: string;
22
+ }
23
+ export declare function resolveProject(args: string[], apiUrl: string, apiKey: string): Promise<ResolvedProject>;
24
+ export declare function readSessionEnv(): string | null;
25
+ export declare function captureGitContext(cwd: string): {
26
+ branch: string | null;
27
+ gitRootSha: string | null;
28
+ workingDir: string;
29
+ };
30
+ /** Convert --since "<duration>" to an ISO windowStart. Accepts:
31
+ * "30m" / "30min" → 30 minutes ago
32
+ * "2h" / "2hr" → 2 hours ago
33
+ * "1d" / "1day" → 1 day ago
34
+ * bare ISO timestamp passes through unchanged
35
+ * Returns null on parse failure. */
36
+ export declare function parseSince(since: string | undefined): string | null;
37
+ export declare function isLikelyGenericTitle(title: string, projectName: string): {
38
+ generic: boolean;
39
+ reason?: string;
40
+ };
41
+ export declare function relativeAge(createdAtIso: string, now: Date): string;
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ /** CLI plumbing for `greprag checkpoint`. Pulled out of the subcommand
3
+ * dispatch file (checkpoint.ts) to keep both under the 500-line ceiling.
4
+ *
5
+ * Owns: env / config loading, HTTP wrappers, project resolution, git
6
+ * context capture, --since parsing, generic-title pre-check, age
7
+ * formatting. Pure plumbing — no business logic.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.loadEnvFile = loadEnvFile;
44
+ exports.getConfig = getConfig;
45
+ exports.abortNotConfigured = abortNotConfigured;
46
+ exports.apiPost = apiPost;
47
+ exports.apiGet = apiGet;
48
+ exports.apiPatch = apiPatch;
49
+ exports.apiDelete = apiDelete;
50
+ exports.getFlag = getFlag;
51
+ exports.resolveProject = resolveProject;
52
+ exports.readSessionEnv = readSessionEnv;
53
+ exports.captureGitContext = captureGitContext;
54
+ exports.parseSince = parseSince;
55
+ exports.isLikelyGenericTitle = isLikelyGenericTitle;
56
+ exports.relativeAge = relativeAge;
57
+ const fs = __importStar(require("fs"));
58
+ const path = __importStar(require("path"));
59
+ const child_process_1 = require("child_process");
60
+ const project_anchor_1 = require("../project-anchor");
61
+ const API_URL_DEFAULT = 'https://api.greprag.com';
62
+ // ---------- Env / config -----------------------------------------------------
63
+ function loadEnvFile(filePath) {
64
+ try {
65
+ const content = fs.readFileSync(filePath, 'utf-8');
66
+ for (const line of content.split('\n')) {
67
+ const trimmed = line.trim();
68
+ if (!trimmed || trimmed.startsWith('#'))
69
+ continue;
70
+ const eqIdx = trimmed.indexOf('=');
71
+ if (eqIdx < 1)
72
+ continue;
73
+ const key = trimmed.slice(0, eqIdx).trim();
74
+ let value = trimmed.slice(eqIdx + 1).trim();
75
+ if ((value.startsWith('"') && value.endsWith('"')) ||
76
+ (value.startsWith("'") && value.endsWith("'"))) {
77
+ value = value.slice(1, -1);
78
+ }
79
+ if (!process.env[key])
80
+ process.env[key] = value;
81
+ }
82
+ }
83
+ catch { /* file missing — fine */ }
84
+ }
85
+ function getConfig() {
86
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
87
+ if (homeDir)
88
+ loadEnvFile(path.join(homeDir, '.greprag', '.env'));
89
+ if (!process.env.GREPRAG_API_KEY)
90
+ loadEnvFile(path.join(process.cwd(), '.env'));
91
+ return {
92
+ apiUrl: process.env.GREPRAG_API_URL || API_URL_DEFAULT,
93
+ apiKey: process.env.GREPRAG_API_KEY || '',
94
+ };
95
+ }
96
+ function abortNotConfigured() {
97
+ console.error('Not configured. Run: greprag init');
98
+ process.exit(1);
99
+ }
100
+ // ---------- HTTP -------------------------------------------------------------
101
+ async function apiPost(url, apiKey, body) {
102
+ const res = await fetch(url, {
103
+ method: 'POST',
104
+ headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
105
+ body: JSON.stringify(body),
106
+ });
107
+ const text = await res.text();
108
+ let json;
109
+ try {
110
+ json = text ? JSON.parse(text) : {};
111
+ }
112
+ catch {
113
+ json = { ok: false, error: 'invalid JSON', detail: text };
114
+ }
115
+ if (!res.ok) {
116
+ const err = new Error(`API ${res.status}: ${text}`);
117
+ err.status = res.status;
118
+ err.body = json;
119
+ throw err;
120
+ }
121
+ return json;
122
+ }
123
+ async function apiGet(url, apiKey) {
124
+ const res = await fetch(url, { headers: { 'Authorization': `Bearer ${apiKey}` } });
125
+ if (!res.ok)
126
+ throw new Error(`API ${res.status}: ${await res.text()}`);
127
+ return res.json();
128
+ }
129
+ async function apiPatch(url, apiKey, body) {
130
+ const res = await fetch(url, {
131
+ method: 'PATCH',
132
+ headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
133
+ body: JSON.stringify(body),
134
+ });
135
+ if (!res.ok)
136
+ throw new Error(`API ${res.status}: ${await res.text()}`);
137
+ return res.json();
138
+ }
139
+ async function apiDelete(url, apiKey) {
140
+ const res = await fetch(url, {
141
+ method: 'DELETE',
142
+ headers: { 'Authorization': `Bearer ${apiKey}` },
143
+ });
144
+ if (!res.ok)
145
+ throw new Error(`API ${res.status}: ${await res.text()}`);
146
+ return res.json();
147
+ }
148
+ // ---------- Flags ------------------------------------------------------------
149
+ function getFlag(args, flag) {
150
+ const idx = args.indexOf(flag);
151
+ if (idx === -1 || idx + 1 >= args.length)
152
+ return undefined;
153
+ return args[idx + 1];
154
+ }
155
+ async function resolveProject(args, apiUrl, apiKey) {
156
+ const projectArg = getFlag(args, '--project');
157
+ if (!projectArg) {
158
+ const anchor = (0, project_anchor_1.readAnchor)(process.cwd());
159
+ return { projectId: anchor.projectId, projectName: anchor.projectName };
160
+ }
161
+ const r = await apiGet(`${apiUrl}/v1/projects`, apiKey);
162
+ const needle = projectArg.trim().toLowerCase();
163
+ const match = (r.projects || []).find(p => p.project_name.toLowerCase() === needle);
164
+ if (!match) {
165
+ throw new Error(`project "${projectArg}" not found under this tenant. ` +
166
+ `Register it via 'greprag init' in that directory, then retry.`);
167
+ }
168
+ return { projectId: match.project_id, projectName: match.project_name };
169
+ }
170
+ function readSessionEnv() {
171
+ return process.env.CLAUDE_SESSION_ID || process.env.GREPRAG_SESSION_ID || null;
172
+ }
173
+ // ---------- Git context capture ---------------------------------------------
174
+ function captureGitContext(cwd) {
175
+ let branch = null;
176
+ let gitRootSha = null;
177
+ try {
178
+ branch = (0, child_process_1.execSync)('git branch --show-current', {
179
+ cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],
180
+ }).trim() || null;
181
+ }
182
+ catch { /* not a git repo */ }
183
+ try {
184
+ gitRootSha = (0, child_process_1.execSync)('git rev-list --max-parents=0 HEAD', {
185
+ cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],
186
+ }).trim().split('\n')[0] || null;
187
+ }
188
+ catch { /* not a git repo */ }
189
+ return { branch, gitRootSha, workingDir: cwd };
190
+ }
191
+ // ---------- --since duration parser -----------------------------------------
192
+ /** Convert --since "<duration>" to an ISO windowStart. Accepts:
193
+ * "30m" / "30min" → 30 minutes ago
194
+ * "2h" / "2hr" → 2 hours ago
195
+ * "1d" / "1day" → 1 day ago
196
+ * bare ISO timestamp passes through unchanged
197
+ * Returns null on parse failure. */
198
+ function parseSince(since) {
199
+ if (!since)
200
+ return null;
201
+ const trimmed = since.trim();
202
+ const asDate = new Date(trimmed);
203
+ if (!Number.isNaN(asDate.getTime()) && /\d{4}-\d{2}-\d{2}/.test(trimmed)) {
204
+ return asDate.toISOString();
205
+ }
206
+ const m = trimmed.match(/^(\d+(?:\.\d+)?)(m|min|h|hr|d|day)s?$/i);
207
+ if (!m)
208
+ return null;
209
+ const n = parseFloat(m[1]);
210
+ const unit = m[2].toLowerCase();
211
+ let ms;
212
+ if (unit === 'm' || unit === 'min')
213
+ ms = n * 60_000;
214
+ else if (unit === 'h' || unit === 'hr')
215
+ ms = n * 3_600_000;
216
+ else
217
+ ms = n * 86_400_000;
218
+ return new Date(Date.now() - ms).toISOString();
219
+ }
220
+ // ---------- Title pre-check (mirrors checkpoint-title.ts on server) ----------
221
+ function isLikelyGenericTitle(title, projectName) {
222
+ const trimmed = title.trim();
223
+ if (!trimmed)
224
+ return { generic: true, reason: 'empty' };
225
+ const wordCount = trimmed.split(/\s+/).filter(Boolean).length;
226
+ if (wordCount < 5)
227
+ return { generic: true, reason: `only ${wordCount} word(s)` };
228
+ if (projectName && trimmed.toLowerCase() === projectName.trim().toLowerCase()) {
229
+ return { generic: true, reason: 'just the project name' };
230
+ }
231
+ if (/^(notes?|stuff|things|misc|pausing|where i was|where i left off|checkpoint|save|wip|bookmark|todo|reminder)\.?\s*$/i.test(trimmed)) {
232
+ return { generic: true, reason: 'generic word' };
233
+ }
234
+ return { generic: false };
235
+ }
236
+ // ---------- Time formatting --------------------------------------------------
237
+ function relativeAge(createdAtIso, now) {
238
+ const diffMs = now.getTime() - new Date(createdAtIso).getTime();
239
+ const diffMin = Math.floor(diffMs / 60_000);
240
+ if (diffMin < 1)
241
+ return 'just now';
242
+ if (diffMin < 60)
243
+ return `${diffMin}m ago`;
244
+ const diffHrs = Math.round(diffMs / 3_600_000);
245
+ if (diffHrs < 24)
246
+ return `${diffHrs}h ago`;
247
+ const diffDays = Math.round(diffMs / 86_400_000);
248
+ return diffDays === 1 ? 'yesterday' : `${diffDays}d ago`;
249
+ }
250
+ //# sourceMappingURL=checkpoint-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint-helpers.js","sourceRoot":"","sources":["../../src/commands/checkpoint-helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** greprag checkpoint — operator-triggered episodic bookmarks.
2
+ *
3
+ * Subcommands:
4
+ * save "<title>" [--note "<text>"] [--since "<duration>"]
5
+ * [--project <name>] [--source-session <id>] [--force]
6
+ * list [--project <name>] [--all | --status open|closed]
7
+ * show <nodeId> [--project <name>]
8
+ * edit-note <nodeId> --append "<text>" | --replace "<text>"
9
+ * close <nodeId> [--project <name>]
10
+ * delete <nodeId> [--project <name>] [--yes]
11
+ *
12
+ * Plumbing (config, HTTP, project resolution, git context, --since
13
+ * parsing, title pre-check, time formatting) lives in
14
+ * `./checkpoint-helpers.ts` so this file stays under the 500-line ceiling.
15
+ */
16
+ export declare function runCheckpoint(args: string[]): Promise<void>;
@@ -0,0 +1,334 @@
1
+ "use strict";
2
+ /** greprag checkpoint — operator-triggered episodic bookmarks.
3
+ *
4
+ * Subcommands:
5
+ * save "<title>" [--note "<text>"] [--since "<duration>"]
6
+ * [--project <name>] [--source-session <id>] [--force]
7
+ * list [--project <name>] [--all | --status open|closed]
8
+ * show <nodeId> [--project <name>]
9
+ * edit-note <nodeId> --append "<text>" | --replace "<text>"
10
+ * close <nodeId> [--project <name>]
11
+ * delete <nodeId> [--project <name>] [--yes]
12
+ *
13
+ * Plumbing (config, HTTP, project resolution, git context, --since
14
+ * parsing, title pre-check, time formatting) lives in
15
+ * `./checkpoint-helpers.ts` so this file stays under the 500-line ceiling.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.runCheckpoint = runCheckpoint;
19
+ const checkpoint_helpers_1 = require("./checkpoint-helpers");
20
+ const NODE_ID_RE = /^[0-9a-f]{8}$/i;
21
+ // ---------- save ------------------------------------------------------------
22
+ async function saveCmd(args) {
23
+ const cfg = (0, checkpoint_helpers_1.getConfig)();
24
+ if (!cfg.apiKey)
25
+ (0, checkpoint_helpers_1.abortNotConfigured)();
26
+ const title = args[0];
27
+ if (!title || title.startsWith('--')) {
28
+ console.error('Usage: greprag checkpoint save "<title>" [--note "<text>"] [--since "<duration>"] [--project <name>] [--source-session <id>] [--force]');
29
+ process.exit(1);
30
+ }
31
+ const note = (0, checkpoint_helpers_1.getFlag)(args, '--note');
32
+ const since = (0, checkpoint_helpers_1.getFlag)(args, '--since');
33
+ const sourceSession = (0, checkpoint_helpers_1.getFlag)(args, '--source-session') || (0, checkpoint_helpers_1.readSessionEnv)();
34
+ const force = args.includes('--force');
35
+ const { projectId, projectName } = await (0, checkpoint_helpers_1.resolveProject)(args, cfg.apiUrl, cfg.apiKey);
36
+ if (!force) {
37
+ const check = (0, checkpoint_helpers_1.isLikelyGenericTitle)(title, projectName);
38
+ if (check.generic) {
39
+ console.error(`Title "${title}" looks generic (${check.reason}). Server will reject this.`);
40
+ console.error('Examples of accepted titles:');
41
+ console.error(' · PayBot backlinks codification — awaiting operator inputs');
42
+ console.error(' · Discord pairing reconciliation — paused mid-debug');
43
+ console.error(' · Episodic V2 prompt validation — 3 dailies left');
44
+ console.error('Try again with a specific title (or pass --force to attempt anyway).');
45
+ process.exit(1);
46
+ }
47
+ }
48
+ const sinceIso = (0, checkpoint_helpers_1.parseSince)(since);
49
+ if (since && !sinceIso) {
50
+ console.error(`--since "${since}" is not a valid duration. Use "30m", "2h", "1d", or ISO.`);
51
+ process.exit(1);
52
+ }
53
+ const gitCtx = (0, checkpoint_helpers_1.captureGitContext)(process.cwd());
54
+ const url = force
55
+ ? `${cfg.apiUrl}/v1/checkpoints/${projectId}?override_title_check=1`
56
+ : `${cfg.apiUrl}/v1/checkpoints/${projectId}`;
57
+ let result;
58
+ try {
59
+ result = await (0, checkpoint_helpers_1.apiPost)(url, cfg.apiKey, {
60
+ title,
61
+ sourceSessionId: sourceSession,
62
+ windowStart: sinceIso || undefined,
63
+ note: note ?? null,
64
+ branch: gitCtx.branch,
65
+ gitRootSha: gitCtx.gitRootSha,
66
+ workingDir: gitCtx.workingDir,
67
+ projectName,
68
+ });
69
+ }
70
+ catch (err) {
71
+ const e = err;
72
+ if (e.status === 400 && e.body) {
73
+ console.error(`Error: ${e.body.detail || e.body.error}`);
74
+ if (e.body.examples) {
75
+ console.error('Examples of accepted titles:');
76
+ for (const ex of e.body.examples)
77
+ console.error(` · ${ex}`);
78
+ }
79
+ process.exit(1);
80
+ }
81
+ throw err;
82
+ }
83
+ if (!result.ok || !result.nodeId) {
84
+ console.error(`Error: ${result.detail || result.error || 'unknown failure'}`);
85
+ process.exit(1);
86
+ }
87
+ const label = result.duplicate ? 'Reusing existing checkpoint' : 'Saved checkpoint';
88
+ console.log(`${label} [${result.nodeId}] in ${projectName}`);
89
+ console.log(`Title: ${result.title}`);
90
+ if (result.placeholder) {
91
+ console.log('');
92
+ console.log('Warning: no turns in window — synthesis was skipped.');
93
+ console.log('Recommend adding a --note via: greprag checkpoint edit-note ' + result.nodeId + ' --append "<text>"');
94
+ }
95
+ else {
96
+ console.log(`Claims: ${result.claimsCount ?? 0} · model: ${result.modelUsed || 'unknown'}`);
97
+ }
98
+ }
99
+ // ---------- list ------------------------------------------------------------
100
+ async function listCmd(args) {
101
+ const cfg = (0, checkpoint_helpers_1.getConfig)();
102
+ if (!cfg.apiKey)
103
+ (0, checkpoint_helpers_1.abortNotConfigured)();
104
+ const all = args.includes('--all');
105
+ const statusArg = (0, checkpoint_helpers_1.getFlag)(args, '--status');
106
+ const status = all ? 'all' : (statusArg === 'closed' ? 'closed' : statusArg === 'all' ? 'all' : 'open');
107
+ const { projectId, projectName } = await (0, checkpoint_helpers_1.resolveProject)(args, cfg.apiUrl, cfg.apiKey);
108
+ const result = await (0, checkpoint_helpers_1.apiGet)(`${cfg.apiUrl}/v1/checkpoints/${projectId}?status=${encodeURIComponent(status)}`, cfg.apiKey);
109
+ if (!result.ok || !result.checkpoints) {
110
+ throw new Error(result.error || 'list failed');
111
+ }
112
+ if (result.checkpoints.length === 0) {
113
+ const noun = status === 'closed' ? 'closed' : status === 'all' ? '' : 'open ';
114
+ console.log(`No ${noun}checkpoints in ${projectName}.`);
115
+ return;
116
+ }
117
+ const now = new Date();
118
+ const sortKey = (r) => (r.status === 'open' ? 0 : 1);
119
+ const rows = result.checkpoints.slice().sort((a, b) => sortKey(a) - sortKey(b) || (a.createdAt < b.createdAt ? 1 : -1));
120
+ console.log(`${rows.length} checkpoint${rows.length === 1 ? '' : 's'} in ${projectName}:`);
121
+ console.log('');
122
+ for (const r of rows) {
123
+ const date = r.createdAt.slice(0, 10);
124
+ const age = (0, checkpoint_helpers_1.relativeAge)(r.createdAt, now);
125
+ console.log(` [${r.nodeId}] ${date} (${age}) ${r.title} [${r.status}]`);
126
+ }
127
+ }
128
+ async function showCmd(args) {
129
+ const cfg = (0, checkpoint_helpers_1.getConfig)();
130
+ if (!cfg.apiKey)
131
+ (0, checkpoint_helpers_1.abortNotConfigured)();
132
+ const nodeId = args[0];
133
+ if (!nodeId || nodeId.startsWith('--')) {
134
+ console.error('Usage: greprag checkpoint show <nodeId> [--project <name>]');
135
+ process.exit(1);
136
+ }
137
+ if (!NODE_ID_RE.test(nodeId)) {
138
+ console.error('nodeId must be 8 hex chars.');
139
+ process.exit(1);
140
+ }
141
+ const { projectId } = await (0, checkpoint_helpers_1.resolveProject)(args, cfg.apiUrl, cfg.apiKey);
142
+ const result = await (0, checkpoint_helpers_1.apiGet)(`${cfg.apiUrl}/v1/checkpoints/${projectId}/${nodeId}`, cfg.apiKey);
143
+ if (!result.ok || !result.checkpoint) {
144
+ console.error(`Error: ${result.error || 'not found'}`);
145
+ process.exit(1);
146
+ }
147
+ const cp = result.checkpoint;
148
+ const savedAt = cp.createdAt.slice(0, 16).replace('T', ' ') + ' UTC';
149
+ const sessionShort = (cp.sourceSessionId || '').slice(0, 8);
150
+ console.log(`${cp.title} [${cp.status}]`);
151
+ const meta = [`Saved ${savedAt}`];
152
+ if (cp.branch)
153
+ meta.push(`branch ${cp.branch}`);
154
+ if (sessionShort)
155
+ meta.push(`session ${sessionShort}`);
156
+ console.log(meta.join(' · '));
157
+ if (cp.workingDir)
158
+ console.log(`Dir: ${cp.workingDir}`);
159
+ console.log('');
160
+ console.log(cp.summary || '(no summary)');
161
+ if (cp.nextLine) {
162
+ console.log('');
163
+ console.log(`NEXT: ${cp.nextLine}`);
164
+ }
165
+ const open = cp.claims.filter(c => c.tag === 'open');
166
+ if (open.length > 0) {
167
+ console.log('');
168
+ console.log('Open items (from claims):');
169
+ for (const c of open)
170
+ console.log(` · ${c.text}`);
171
+ }
172
+ const other = cp.claims.filter(c => c.tag !== 'open');
173
+ if (other.length > 0) {
174
+ console.log('');
175
+ console.log('Decisions / Learned / Investigated:');
176
+ for (const c of other)
177
+ console.log(` [${c.tag}:${c.source}] ${c.text}`);
178
+ }
179
+ if (cp.operatorNote) {
180
+ console.log('');
181
+ console.log('Operator note:');
182
+ console.log(cp.operatorNote);
183
+ }
184
+ if (cp.closedAt) {
185
+ console.log('');
186
+ console.log(`Closed: ${cp.closedAt.slice(0, 16).replace('T', ' ')} UTC`);
187
+ }
188
+ }
189
+ // ---------- edit-note -------------------------------------------------------
190
+ async function editNoteCmd(args) {
191
+ const cfg = (0, checkpoint_helpers_1.getConfig)();
192
+ if (!cfg.apiKey)
193
+ (0, checkpoint_helpers_1.abortNotConfigured)();
194
+ const nodeId = args[0];
195
+ if (!nodeId || nodeId.startsWith('--')) {
196
+ console.error('Usage: greprag checkpoint edit-note <nodeId> --append "<text>" | --replace "<text>"');
197
+ process.exit(1);
198
+ }
199
+ if (!NODE_ID_RE.test(nodeId)) {
200
+ console.error('nodeId must be 8 hex chars.');
201
+ process.exit(1);
202
+ }
203
+ const append = (0, checkpoint_helpers_1.getFlag)(args, '--append');
204
+ const replace = (0, checkpoint_helpers_1.getFlag)(args, '--replace');
205
+ if ((append == null) === (replace == null)) {
206
+ console.error('Pass exactly one of --append "<text>" or --replace "<text>".');
207
+ process.exit(1);
208
+ }
209
+ const action = append != null ? 'append' : 'replace';
210
+ const text = append != null ? append : replace;
211
+ const { projectId } = await (0, checkpoint_helpers_1.resolveProject)(args, cfg.apiUrl, cfg.apiKey);
212
+ const result = await (0, checkpoint_helpers_1.apiPatch)(`${cfg.apiUrl}/v1/checkpoints/${projectId}/${nodeId}`, cfg.apiKey, { action: 'edit-note', note: { action, text } });
213
+ if (!result.ok) {
214
+ console.error(`Error: ${result.error || 'edit-note failed'}`);
215
+ process.exit(1);
216
+ }
217
+ console.log(`Updated note on checkpoint [${nodeId}].`);
218
+ if (result.operatorNote) {
219
+ console.log('');
220
+ console.log('New note:');
221
+ console.log(result.operatorNote);
222
+ }
223
+ }
224
+ // ---------- close -----------------------------------------------------------
225
+ async function closeCmd(args) {
226
+ const cfg = (0, checkpoint_helpers_1.getConfig)();
227
+ if (!cfg.apiKey)
228
+ (0, checkpoint_helpers_1.abortNotConfigured)();
229
+ const nodeId = args[0];
230
+ if (!nodeId || nodeId.startsWith('--')) {
231
+ console.error('Usage: greprag checkpoint close <nodeId> [--project <name>]');
232
+ process.exit(1);
233
+ }
234
+ if (!NODE_ID_RE.test(nodeId)) {
235
+ console.error('nodeId must be 8 hex chars.');
236
+ process.exit(1);
237
+ }
238
+ const { projectId, projectName } = await (0, checkpoint_helpers_1.resolveProject)(args, cfg.apiUrl, cfg.apiKey);
239
+ const result = await (0, checkpoint_helpers_1.apiPatch)(`${cfg.apiUrl}/v1/checkpoints/${projectId}/${nodeId}`, cfg.apiKey, { action: 'close' });
240
+ if (!result.ok) {
241
+ console.error(`Error: ${result.error || 'close failed'}`);
242
+ process.exit(1);
243
+ }
244
+ if (result.alreadyClosed) {
245
+ console.log(`Checkpoint [${nodeId}] in ${projectName} was already closed.`);
246
+ }
247
+ else {
248
+ console.log(`Closed checkpoint [${nodeId}] in ${projectName}.`);
249
+ }
250
+ }
251
+ // ---------- delete ----------------------------------------------------------
252
+ async function deleteCmd(args) {
253
+ const cfg = (0, checkpoint_helpers_1.getConfig)();
254
+ if (!cfg.apiKey)
255
+ (0, checkpoint_helpers_1.abortNotConfigured)();
256
+ const nodeId = args[0];
257
+ if (!nodeId || nodeId.startsWith('--')) {
258
+ console.error('Usage: greprag checkpoint delete <nodeId> [--project <name>] [--yes]');
259
+ process.exit(1);
260
+ }
261
+ if (!NODE_ID_RE.test(nodeId)) {
262
+ console.error('nodeId must be 8 hex chars.');
263
+ process.exit(1);
264
+ }
265
+ if (!args.includes('--yes') && !args.includes('-y')) {
266
+ console.error(`Refusing to delete without confirmation. Pass --yes to delete [${nodeId}].`);
267
+ console.error('Note: "close keeps history; delete is for typos." Consider `close` instead.');
268
+ process.exit(1);
269
+ }
270
+ const { projectId, projectName } = await (0, checkpoint_helpers_1.resolveProject)(args, cfg.apiUrl, cfg.apiKey);
271
+ const result = await (0, checkpoint_helpers_1.apiDelete)(`${cfg.apiUrl}/v1/checkpoints/${projectId}/${nodeId}`, cfg.apiKey);
272
+ if (!result.ok) {
273
+ console.error(`Error: ${result.error || 'delete failed'}`);
274
+ process.exit(1);
275
+ }
276
+ console.log(`Deleted checkpoint [${nodeId}] from ${projectName}.`);
277
+ }
278
+ // ---------- Dispatch --------------------------------------------------------
279
+ const SUB_HELP = `
280
+ greprag checkpoint — operator-triggered episodic bookmarks.
281
+
282
+ A checkpoint is a "where I am + what was about to happen next" snapshot.
283
+ The server synthesizes the session window into a summary + tagged claims
284
+ (gemini-2.5-flash) and stores it as a checkpoint-shape node in the project's
285
+ memory store. Lifecycle ops: list / show / edit-note / close / delete.
286
+
287
+ Subcommands:
288
+ save "<title>" [--note "<text>"] [--since "<duration>"]
289
+ [--project <name>] [--source-session <id>] [--force]
290
+ Save a checkpoint. Title must be 5+ words, specific (server hard-rejects
291
+ "PayBot stuff", "notes", project-name-only, all-caps, etc.). --since
292
+ accepts "30m" / "2h" / "1d" / ISO. --force bypasses heuristic title
293
+ checks (empty title still rejected).
294
+ list [--project <name>] [--status open|closed|all] [--all]
295
+ Default: open only. --all = same as --status all.
296
+ show <nodeId> [--project <name>]
297
+ Print the synthesized summary, NEXT line, claims, operator note.
298
+ edit-note <nodeId> --append "<text>" | --replace "<text>"
299
+ Update the operator-note overlay (appended below the synthesis on show).
300
+ close <nodeId> [--project <name>]
301
+ Flip status to closed. Keeps history. Idempotent.
302
+ delete <nodeId> [--project <name>] [--yes]
303
+ Hard delete (close is usually the right move).
304
+
305
+ Examples:
306
+ greprag checkpoint save "Episodic V2 prompt validation — 3 dailies left" --note "Re-run after weekly cron fires Sunday"
307
+ greprag checkpoint list
308
+ greprag checkpoint show a3f2c1d4
309
+ greprag checkpoint edit-note a3f2c1d4 --append "Operator reverted the prompt before sleeping."
310
+ greprag checkpoint close a3f2c1d4
311
+ `.trim();
312
+ async function runCheckpoint(args) {
313
+ const sub = args[0];
314
+ const rest = args.slice(1);
315
+ switch (sub) {
316
+ case 'save': return saveCmd(rest);
317
+ case 'list': return listCmd(rest);
318
+ case 'show': return showCmd(rest);
319
+ case 'edit-note': return editNoteCmd(rest);
320
+ case 'close': return closeCmd(rest);
321
+ case 'delete': return deleteCmd(rest);
322
+ case undefined:
323
+ case 'help':
324
+ case '--help':
325
+ case '-h':
326
+ console.log(SUB_HELP);
327
+ return;
328
+ default:
329
+ console.error(`Unknown subcommand: ${sub}\n`);
330
+ console.log(SUB_HELP);
331
+ process.exit(1);
332
+ }
333
+ }
334
+ //# sourceMappingURL=checkpoint.js.map