greprag 5.83.2 → 5.84.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.
@@ -96,6 +96,22 @@ again. Never rebase a shared default branch. A busy merge lock means retry
96
96
  after its holder finishes — never bypass it, and never take over someone
97
97
  else's in-progress merge.
98
98
 
99
+ ### Another session's branch is rebased by that session
100
+
101
+ Step 2 happens in the checkout of whoever owns the branch, because only that
102
+ session can edit there and so only it can resolve a conflict. A chip rebases
103
+ onto its merge target's tip and retests before it reports, so its parent lands
104
+ it with steps 3 to 5 alone. Never run `git -C <their worktree> rebase`: the
105
+ write guard refuses your edits there, so the first conflict leaves you
106
+ aborting, or leaves a half-finished rebase in someone else's checkout. If the
107
+ fast-forward is refused because the tip moved, free the lock and send the
108
+ branch back to its owner to rebase and retest. A chip that already closed left
109
+ no worktree behind, so rebase its branch in a checkout you can edit.
110
+
111
+ Field trigger (2026-10-06): a parent landing chip branches with
112
+ `git -C <chip-worktree> rebase main` hit the same ADR conflict twice and could
113
+ not resolve it either time.
114
+
99
115
  This lock is a short Git critical section. It is not a delivery lease, and it
100
116
  is not peer-acknowledgement polling.
101
117
 
@@ -40,7 +40,9 @@ Explicitly requested creation, porting, or redesign is authorized work. The prop
40
40
  - Bundled GrepRAG skill: edit `packages/cli/skill/<name>/`, never the installed artifact.
41
41
  - Mirrored/local skill: edit its canonical source, not the generated Codex/Claude/OpenCode adapter.
42
42
  - Unknown source: stop and identify the canonical source before editing.
43
- - Codex/non-interactive harness: do not launch an editor. Use `greprag skill mirror export <skill> --to <dir>`, patch the exported Markdown files with file edits, then `greprag skill mirror apply <dir> --skill <skill> --expected-hash <exported-hash>`. If using `greprag skill edit` as the export helper, pass `--no-open`.
43
+ - Codex/non-interactive harness: do not launch an editor. Use `greprag skill mirror export <skill> --to <dir>`, patch the exported Markdown files with file edits, then `greprag skill mirror apply <dir> --skill <skill> --expected-hash <exported-hash> --reason "<why>"`. If using `greprag skill edit` as the export helper, pass `--no-open`.
44
+ - **ABOUT TO WRITE A MIRRORED SKILL? STOP - PASS `--reason "<one line>"`.** `skill edit`, `skill mirror apply`, `snapshot promote` and `skill revert` refuse without it. GrepRAG keeps every revision's bytes with that reason, so write the learning the edit captures, not "update skill".
45
+ - History: `greprag skill history <skill>` lists revisions (date, author, reason, changes); `greprag skill diff <skill> <revA> [<revB>]` shows a unified diff; `greprag skill revert <skill> --to <rev> --reason "<why>" --expected-hash <current-hash>` makes an old revision current as a new one. Add the adapter's `--skill-id` to each.
44
46
 
45
47
  ## Skill-Refresh Chips
46
48
 
@@ -1,215 +0,0 @@
1
- "use strict";
2
- /** greprag sms <pair|unpair|me> — Front Desk SMS rail CLI.
3
- *
4
- * pair POSTs /v1/sms/pair with the current project anchor; prints the
5
- * 6-char code + the greprag number to text it to.
6
- * unpair DELETEs /v1/sms/pair (optionally for one --phone).
7
- * me GETs /v1/sms/me — the tenant's bound phones.
8
- *
9
- * Self-contained HTTP client (does not import @greprag/core), mirroring
10
- * commands/email.ts. See docs/receptionist-sms-mvp.md.
11
- */
12
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- var desc = Object.getOwnPropertyDescriptor(m, k);
15
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
- desc = { enumerable: true, get: function() { return m[k]; } };
17
- }
18
- Object.defineProperty(o, k2, desc);
19
- }) : (function(o, m, k, k2) {
20
- if (k2 === undefined) k2 = k;
21
- o[k2] = m[k];
22
- }));
23
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
- Object.defineProperty(o, "default", { enumerable: true, value: v });
25
- }) : function(o, v) {
26
- o["default"] = v;
27
- });
28
- var __importStar = (this && this.__importStar) || (function () {
29
- var ownKeys = function(o) {
30
- ownKeys = Object.getOwnPropertyNames || function (o) {
31
- var ar = [];
32
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
- return ar;
34
- };
35
- return ownKeys(o);
36
- };
37
- return function (mod) {
38
- if (mod && mod.__esModule) return mod;
39
- var result = {};
40
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
- __setModuleDefault(result, mod);
42
- return result;
43
- };
44
- })();
45
- Object.defineProperty(exports, "__esModule", { value: true });
46
- exports.runSms = runSms;
47
- const fs = __importStar(require("fs"));
48
- const path = __importStar(require("path"));
49
- const project_anchor_1 = require("../project-anchor");
50
- const API_URL_DEFAULT = 'https://api.greprag.com';
51
- function loadEnvFile(filePath) {
52
- try {
53
- const content = fs.readFileSync(filePath, 'utf-8');
54
- for (const line of content.split('\n')) {
55
- const trimmed = line.trim();
56
- if (!trimmed || trimmed.startsWith('#'))
57
- continue;
58
- const eqIdx = trimmed.indexOf('=');
59
- if (eqIdx < 1)
60
- continue;
61
- const key = trimmed.slice(0, eqIdx).trim();
62
- let value = trimmed.slice(eqIdx + 1).trim();
63
- if ((value.startsWith('"') && value.endsWith('"')) ||
64
- (value.startsWith("'") && value.endsWith("'"))) {
65
- value = value.slice(1, -1);
66
- }
67
- if (!process.env[key])
68
- process.env[key] = value;
69
- }
70
- }
71
- catch { /* file missing — fine */ }
72
- }
73
- function getConfig() {
74
- const homeDir = process.env.HOME || process.env.USERPROFILE || '';
75
- if (homeDir)
76
- loadEnvFile(path.join(homeDir, '.greprag', '.env'));
77
- if (!process.env.GREPRAG_API_KEY)
78
- loadEnvFile(path.join(process.cwd(), '.env'));
79
- return {
80
- apiUrl: process.env.GREPRAG_API_URL || API_URL_DEFAULT,
81
- apiKey: process.env.GREPRAG_API_KEY || '',
82
- };
83
- }
84
- function getFlag(args, flag) {
85
- const idx = args.indexOf(flag);
86
- if (idx === -1 || idx + 1 >= args.length)
87
- return undefined;
88
- return args[idx + 1];
89
- }
90
- async function callPair(cfg, phone, projectId) {
91
- const payload = { phone };
92
- if (projectId)
93
- payload.project_id = projectId;
94
- const res = await fetch(`${cfg.apiUrl}/v1/sms/pair`, {
95
- method: 'POST',
96
- headers: { 'Authorization': `Bearer ${cfg.apiKey}`, 'Content-Type': 'application/json' },
97
- body: JSON.stringify(payload),
98
- });
99
- return res.json();
100
- }
101
- async function callUnpair(cfg, phone) {
102
- const res = await fetch(`${cfg.apiUrl}/v1/sms/pair`, {
103
- method: 'DELETE',
104
- headers: { 'Authorization': `Bearer ${cfg.apiKey}`, 'Content-Type': 'application/json' },
105
- body: JSON.stringify(phone ? { phone } : {}),
106
- });
107
- return res.json();
108
- }
109
- async function callMe(cfg) {
110
- const res = await fetch(`${cfg.apiUrl}/v1/sms/me`, {
111
- method: 'GET',
112
- headers: { 'Authorization': `Bearer ${cfg.apiKey}` },
113
- });
114
- return res.json();
115
- }
116
- const SMS_HELP = `
117
- greprag sms — Front Desk SMS rail. Reach a cheap receptionist by text to triage
118
- your inbox and route messages to your sessions from your phone.
119
-
120
- Subcommands:
121
- greprag sms pair --phone <e164> Generate a 15-min code BOUND to that
122
- phone; text it (or "PAIR <code>") FROM
123
- that exact number to the greprag number
124
- to bind it. --phone is required and must
125
- be full E.164 (e.g. +15551234567) — the
126
- code only binds when claimed from it. The
127
- current directory's project anchor becomes
128
- your home project.
129
- greprag sms unpair [--phone <e164>] Unbind one phone (or all phones).
130
- greprag sms me Show bound phones + the greprag number.
131
-
132
- Once paired, text the greprag number:
133
- "what's waiting?" → the receptionist reads your inbox.
134
- "tell session a1b2c3d4 to ship it" → routes a message to that session.
135
- `.trim();
136
- async function runSms(args) {
137
- const sub = args[0];
138
- if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {
139
- console.log(SMS_HELP);
140
- return;
141
- }
142
- const cfg = getConfig();
143
- if (!cfg.apiKey) {
144
- console.error('Not configured. Run: greprag init');
145
- process.exit(1);
146
- }
147
- if (sub === 'pair') {
148
- // --phone is required: the code is bound to it, and only a claim texted from
149
- // that exact number succeeds (the identity guard against brute-force binds).
150
- const phone = getFlag(args, '--phone');
151
- if (!phone) {
152
- console.error('Error: --phone <e164> is required (the number you will text from, e.g. +15551234567).');
153
- process.exit(1);
154
- }
155
- // The home/default project is the current directory's anchor. The API keys
156
- // pairing by project_id; a `--project <name>` would need a server-side name
157
- // lookup, so for the MVP the anchor is the source of truth (run pair from
158
- // the project dir you want as home, or leave a message naming any project).
159
- const anchor = (0, project_anchor_1.readAnchor)(process.cwd());
160
- const projectId = anchor.projectId || null;
161
- const result = await callPair(cfg, phone, projectId);
162
- if (!result.ok) {
163
- console.error(`Error: ${result.error}`);
164
- process.exit(1);
165
- }
166
- console.log(`Pairing code: ${result.code}`);
167
- if (result.bound_phone) {
168
- console.log(`Bound to ${result.bound_phone} — text the code FROM that number only.`);
169
- }
170
- if (result.sms_number) {
171
- console.log(`Text it to the greprag number: ${result.sms_number}`);
172
- console.log(` (send "${result.code}" or "PAIR ${result.code}")`);
173
- }
174
- else {
175
- console.log(`Text "${result.code}" (or "PAIR ${result.code}") to the greprag number.`);
176
- console.log(` (number not configured server-side yet — ask the operator)`);
177
- }
178
- console.log(`Code expires in ${result.expires_in_minutes} minutes.`);
179
- if (result.project_name) {
180
- console.log(`After pairing, your home project is "${result.project_name}".`);
181
- }
182
- else {
183
- console.log(`No default project set — name one when you leave a message.`);
184
- }
185
- return;
186
- }
187
- if (sub === 'unpair') {
188
- const phone = getFlag(args, '--phone');
189
- const result = await callUnpair(cfg, phone);
190
- if (!result.ok) {
191
- console.error(`Error: ${result.error}`);
192
- process.exit(1);
193
- }
194
- console.log(`Unpaired ${result.removed} phone(s).`);
195
- return;
196
- }
197
- if (sub === 'me') {
198
- const result = await callMe(cfg);
199
- if (!result.ok) {
200
- console.error(`Error: ${result.error}`);
201
- process.exit(1);
202
- }
203
- console.log(`greprag number: ${result.sms_number ?? '(not configured)'}`);
204
- if (!result.paired || !result.phones || result.phones.length === 0) {
205
- console.log('No phones paired. Run: greprag sms pair');
206
- return;
207
- }
208
- console.log(`Paired phones (${result.phones.length}):`);
209
- for (const p of result.phones) {
210
- console.log(` ${p.phone} paired ${p.paired_at} last inbound ${p.last_seen_inbound_at ?? '(never)'}`);
211
- }
212
- return;
213
- }
214
- console.log(SMS_HELP);
215
- }