ctxr-cli 0.32.0 → 0.33.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/dist/version.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Kept in sync with package.json's "version" field. */
|
|
2
|
-
export declare const CLI_VERSION = "0.
|
|
2
|
+
export declare const CLI_VERSION = "0.33.0";
|
|
3
3
|
/**
|
|
4
4
|
* The npm package this executable is published as. Bound here, beside the
|
|
5
5
|
* version, because the release check resolves the latest published release by
|
package/dist/version.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Kept in sync with package.json's "version" field. */
|
|
2
|
-
export const CLI_VERSION = '0.
|
|
2
|
+
export const CLI_VERSION = '0.33.0';
|
|
3
3
|
/**
|
|
4
4
|
* The npm package this executable is published as. Bound here, beside the
|
|
5
5
|
* version, because the release check resolves the latest published release by
|
package/package.json
CHANGED
|
@@ -73,7 +73,17 @@ function recentMeetings() {
|
|
|
73
73
|
return meetings.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
/**
|
|
76
|
+
/** Where the runner keeps its review worktree: a fixed path, so a later run finds and clears it. */
|
|
77
|
+
function reviewWorktreePath(root) {
|
|
78
|
+
return path.join(tmpdir(), `ctxr-capture-circleback-${path.basename(root)}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Circleback ids already in the default branch, in each open pull request's diff, and in the store's other
|
|
83
|
+
* local worktrees, tracked or not: an agent's session that captured a meeting but has not pushed yet is work
|
|
84
|
+
* in flight, not a gap. The runner's own review worktree is left out, or what an earlier run rendered there
|
|
85
|
+
* would count as present and a meeting whose pull request was closed unmerged could never be captured again.
|
|
86
|
+
*/
|
|
77
87
|
function presence(root, settings) {
|
|
78
88
|
run('git', ['fetch', '-q', 'origin'], { cwd: root });
|
|
79
89
|
const onMain = new Set(
|
|
@@ -87,7 +97,19 @@ function presence(root, settings) {
|
|
|
87
97
|
const diff = run('gh', ['pr', 'diff', String(pr.number)], { cwd: root, check: false });
|
|
88
98
|
for (const match of diff.matchAll(/circleback\/([A-Za-z0-9_-]+)/g)) if (!inPullRequest.has(match[1])) inPullRequest.set(match[1], pr.number);
|
|
89
99
|
}
|
|
90
|
-
|
|
100
|
+
const inWorktree = new Map();
|
|
101
|
+
const own = path.resolve(reviewWorktreePath(root));
|
|
102
|
+
for (const entry of run('git', ['worktree', 'list', '--porcelain'], { cwd: root, check: false }).split('\n')) {
|
|
103
|
+
if (!entry.startsWith('worktree ')) continue;
|
|
104
|
+
const worktree = entry.slice('worktree '.length);
|
|
105
|
+
if (path.resolve(worktree) === own) continue;
|
|
106
|
+
const found = run('git', ['grep', '--untracked', '-I', '-h', '-o', '-E', 'circleback/[A-Za-z0-9_-]+'], { cwd: worktree, check: false });
|
|
107
|
+
for (const match of found.split('\n').filter(Boolean)) {
|
|
108
|
+
const id = match.slice('circleback/'.length);
|
|
109
|
+
if (!onMain.has(id) && !inWorktree.has(id)) inWorktree.set(id, worktree);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { onMain, inPullRequest, inWorktree };
|
|
91
113
|
}
|
|
92
114
|
|
|
93
115
|
/**
|
|
@@ -96,7 +118,7 @@ function presence(root, settings) {
|
|
|
96
118
|
* first, because an agent running unattended may not be allowed to delete it.
|
|
97
119
|
*/
|
|
98
120
|
function prepareWorktree(root, settings) {
|
|
99
|
-
const worktree =
|
|
121
|
+
const worktree = reviewWorktreePath(root);
|
|
100
122
|
run('git', ['worktree', 'remove', '--force', worktree], { cwd: root, check: false });
|
|
101
123
|
rmSync(worktree, { recursive: true, force: true });
|
|
102
124
|
run('git', ['worktree', 'prune'], { cwd: root });
|
|
@@ -171,7 +193,7 @@ function main() {
|
|
|
171
193
|
const settings = JSON.parse(readFileSync(path.join(HERE, 'settings.json'), 'utf8'));
|
|
172
194
|
const root = storeRoot();
|
|
173
195
|
const otherTags = new Set(settings.other_tags);
|
|
174
|
-
const { onMain, inPullRequest } = presence(root, settings);
|
|
196
|
+
const { onMain, inPullRequest, inWorktree } = presence(root, settings);
|
|
175
197
|
const meetings = recentMeetings();
|
|
176
198
|
const now = Date.now();
|
|
177
199
|
const ageDays = (meeting) => (now - Date.parse(meeting.createdAt)) / 86_400_000;
|
|
@@ -181,13 +203,17 @@ function main() {
|
|
|
181
203
|
const untagged = [];
|
|
182
204
|
const multiStore = [];
|
|
183
205
|
const declined = [];
|
|
206
|
+
const unpushed = [];
|
|
184
207
|
let present = 0;
|
|
185
208
|
for (const meeting of meetings) {
|
|
186
209
|
const tags = tagNames(meeting);
|
|
187
210
|
const isLocked = (meeting.notes ?? '').includes(LOCKED_NOTICE);
|
|
188
|
-
const here = onMain.has(meeting.id) || inPullRequest.has(meeting.id);
|
|
211
|
+
const here = onMain.has(meeting.id) || inPullRequest.has(meeting.id) || inWorktree.has(meeting.id);
|
|
189
212
|
const others = [...tags].filter((tag) => otherTags.has(tag));
|
|
190
213
|
if (tags.has(settings.tag) && here) present += 1;
|
|
214
|
+
if (tags.has(settings.tag) && inWorktree.has(meeting.id) && !onMain.has(meeting.id) && !inPullRequest.has(meeting.id)) {
|
|
215
|
+
unpushed.push(`${line(meeting)} (in ${inWorktree.get(meeting.id)})`);
|
|
216
|
+
}
|
|
191
217
|
if (tags.size === 0 && !here) untagged.push(`${line(meeting)}${isLocked ? ' (locked)' : ageDays(meeting) >= URGENT_DAYS ? ' (URGENT, locks soon)' : ''}`);
|
|
192
218
|
if (tags.has(settings.tag) && others.length > 0) multiStore.push(`${line(meeting)} (also ${others.join(', ')})`);
|
|
193
219
|
if (tags.has(settings.decline_tag) && ageDays(meeting) <= DECLINE_REPORT_DAYS) declined.push(line(meeting));
|
|
@@ -197,7 +223,7 @@ function main() {
|
|
|
197
223
|
}
|
|
198
224
|
|
|
199
225
|
const report = [
|
|
200
|
-
`Store tag \`${settings.tag}\`. Meetings in the last ${LOOKBACK_DAYS} days: ${meetings.length}. Already in this store
|
|
226
|
+
`Store tag \`${settings.tag}\`. Meetings in the last ${LOOKBACK_DAYS} days: ${meetings.length}. Already in this store, an open pull request or a local worktree: ${present}.`,
|
|
201
227
|
...section('TO CAPTURE — date | id | title', owed.map(line)),
|
|
202
228
|
];
|
|
203
229
|
if (owed.length > 0) {
|
|
@@ -214,6 +240,7 @@ function main() {
|
|
|
214
240
|
}
|
|
215
241
|
}
|
|
216
242
|
report.push(
|
|
243
|
+
...section('UNPUSHED — captured in a local worktree but not on the default branch or in an open pull request; land that work', unpushed),
|
|
217
244
|
...section(`LOCKED — tagged \`${settings.tag}\`, never captured, no longer readable in full`, locked),
|
|
218
245
|
...section('UNTAGGED', untagged),
|
|
219
246
|
...section('TAGGED FOR MORE THAN ONE STORE', multiStore),
|
|
@@ -17,12 +17,13 @@ It needs Circleback's command-line tool (`cb`) signed in, plus `git` and `gh`. A
|
|
|
17
17
|
start and puts its report in your prompt; then skip to step 2.
|
|
18
18
|
|
|
19
19
|
The runner reads every meeting from the last 35 days with its tags, and compares them with this store's default
|
|
20
|
-
branch
|
|
21
|
-
|
|
22
|
-
full.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
branch, its open pull requests, and the store's other local worktrees, uncommitted files included. A meeting is owed
|
|
21
|
+
when it carries this store's tag, does not carry the decline tag, carries no other store's tag, is in none of those
|
|
22
|
+
places, and can still be read in full. A meeting found only in a local worktree is someone's capture in flight: it
|
|
23
|
+
is listed under UNPUSHED rather than captured again. For each owed meeting it prepares a review-branch worktree and
|
|
24
|
+
writes the record into `__INBOX_PATH__` there: the meeting's metadata and attendees, Circleback's action items and
|
|
25
|
+
summary (marked as the service's derivation), and the transcript verbatim, with `source_type` and `source_id`
|
|
26
|
+
stamped. It never writes the recording URL, which is a signed, expiring link and so a credential.
|
|
26
27
|
|
|
27
28
|
The report is authoritative. Do not work the list out again yourself, and do not rebuild the files it wrote. If it
|
|
28
29
|
prints `COLLECTION FAILED`, report that line and stop.
|
|
@@ -52,6 +53,8 @@ Give the counts (retrieved, already present), the pull request link, and the run
|
|
|
52
53
|
they are:
|
|
53
54
|
|
|
54
55
|
- **LOCKED**: tagged for this store but no longer readable in full, so it cannot be captured.
|
|
56
|
+
- **UNPUSHED**: captured in a local worktree but not yet on the default branch or in a pull request. That work
|
|
57
|
+
needs landing; nothing here captures it again.
|
|
55
58
|
- **UNTAGGED**, **TAGGED FOR MORE THAN ONE STORE** and **DECLINED IN THE LAST 8 DAYS**, when the runner prints them.
|
|
56
59
|
|
|
57
60
|
If nothing was owed and every section is empty, say "Nothing missing".
|