groundcrew-cli 0.17.0 → 0.18.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/index.js +73 -45
- package/package.json +1 -1
- package/src/index.ts +94 -58
package/dist/index.js
CHANGED
|
@@ -80,14 +80,18 @@ var ACTIVE_SESSIONS_FILE = path.join(GROUNDCREW_HOME, "active-sessions.json");
|
|
|
80
80
|
var HISTORY_FILE = path.join(GROUNDCREW_HOME, "history.json");
|
|
81
81
|
var REPO_NAME = "";
|
|
82
82
|
async function resolveRoot() {
|
|
83
|
-
let
|
|
83
|
+
let repoRoot = null;
|
|
84
84
|
try {
|
|
85
|
-
const { stdout } = await execFileAsync("git", ["rev-parse", "--
|
|
86
|
-
|
|
85
|
+
const { stdout: gitCommonDir } = await execFileAsync("git", ["rev-parse", "--git-common-dir"]);
|
|
86
|
+
const trimmed = gitCommonDir.trim();
|
|
87
|
+
if (trimmed) {
|
|
88
|
+
const absGitDir = path.isAbsolute(trimmed) ? trimmed : path.resolve(process.cwd(), trimmed);
|
|
89
|
+
repoRoot = path.dirname(absGitDir);
|
|
90
|
+
}
|
|
87
91
|
} catch {
|
|
88
92
|
}
|
|
89
|
-
if (!
|
|
90
|
-
REPO_NAME = path.basename(
|
|
93
|
+
if (!repoRoot) repoRoot = process.cwd();
|
|
94
|
+
REPO_NAME = path.basename(repoRoot).replace(/[^a-zA-Z0-9_-]/g, "_") || "unknown";
|
|
91
95
|
await fs.mkdir(SESSIONS_DIR, { recursive: true });
|
|
92
96
|
}
|
|
93
97
|
async function readActiveSessions() {
|
|
@@ -103,45 +107,54 @@ function isRepoSession(sessionId) {
|
|
|
103
107
|
async function resolveSessionDir(explicitSession) {
|
|
104
108
|
if (explicitSession) {
|
|
105
109
|
const dir = path.join(SESSIONS_DIR, explicitSession);
|
|
106
|
-
if (
|
|
107
|
-
throw new Error(`Session "${explicitSession}" not found.`);
|
|
108
|
-
}
|
|
109
|
-
return dir;
|
|
110
|
-
}
|
|
111
|
-
const sessions2 = await readActiveSessions();
|
|
112
|
-
const ids = Object.keys(sessions2).filter(isRepoSession);
|
|
113
|
-
if (ids.length === 0) {
|
|
110
|
+
if (existsSync(dir)) return dir;
|
|
114
111
|
try {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
let latest2 = { dir: dirs[0], mtime: 0 };
|
|
119
|
-
for (const d of dirs) {
|
|
120
|
-
try {
|
|
121
|
-
const stat = await fs.stat(path.join(SESSIONS_DIR, d, "session.json"));
|
|
122
|
-
if (stat.mtimeMs > latest2.mtime) {
|
|
123
|
-
latest2 = { dir: d, mtime: stat.mtimeMs };
|
|
124
|
-
}
|
|
125
|
-
} catch {
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return path.join(SESSIONS_DIR, latest2.dir);
|
|
129
|
-
}
|
|
112
|
+
const allDirs = await fs.readdir(SESSIONS_DIR);
|
|
113
|
+
const match = allDirs.find((d) => d.endsWith("-" + explicitSession));
|
|
114
|
+
if (match) return path.join(SESSIONS_DIR, match);
|
|
130
115
|
} catch {
|
|
131
116
|
}
|
|
132
|
-
throw new Error(`
|
|
117
|
+
throw new Error(`Session "${explicitSession}" not found.`);
|
|
118
|
+
}
|
|
119
|
+
const active = await readActiveSessions();
|
|
120
|
+
const repoIds = Object.keys(active).filter(isRepoSession);
|
|
121
|
+
if (repoIds.length === 1) return path.join(SESSIONS_DIR, repoIds[0]);
|
|
122
|
+
if (repoIds.length > 1) {
|
|
123
|
+
let latest = { id: repoIds[0], time: 0 };
|
|
124
|
+
for (const id of repoIds) {
|
|
125
|
+
const started = new Date(active[id].started).getTime();
|
|
126
|
+
if (started > latest.time) latest = { id, time: started };
|
|
127
|
+
}
|
|
128
|
+
return path.join(SESSIONS_DIR, latest.id);
|
|
133
129
|
}
|
|
134
|
-
|
|
135
|
-
|
|
130
|
+
const allIds = Object.keys(active);
|
|
131
|
+
if (allIds.length === 1) return path.join(SESSIONS_DIR, allIds[0]);
|
|
132
|
+
if (allIds.length > 1) {
|
|
133
|
+
let latest = { id: allIds[0], time: 0 };
|
|
134
|
+
for (const id of allIds) {
|
|
135
|
+
const started = new Date(active[id].started).getTime();
|
|
136
|
+
if (started > latest.time) latest = { id, time: started };
|
|
137
|
+
}
|
|
138
|
+
return path.join(SESSIONS_DIR, latest.id);
|
|
136
139
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
try {
|
|
141
|
+
const dirs = await fs.readdir(SESSIONS_DIR);
|
|
142
|
+
const repoDirs = dirs.filter(isRepoSession);
|
|
143
|
+
const target = repoDirs.length > 0 ? repoDirs : dirs;
|
|
144
|
+
if (target.length >= 1) {
|
|
145
|
+
let latest = { dir: target[0], mtime: 0 };
|
|
146
|
+
for (const d of target) {
|
|
147
|
+
try {
|
|
148
|
+
const stat = await fs.stat(path.join(SESSIONS_DIR, d, "session.json"));
|
|
149
|
+
if (stat.mtimeMs > latest.mtime) latest = { dir: d, mtime: stat.mtimeMs };
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return path.join(SESSIONS_DIR, latest.dir);
|
|
142
154
|
}
|
|
155
|
+
} catch {
|
|
143
156
|
}
|
|
144
|
-
|
|
157
|
+
throw new Error("No active sessions. Start Copilot with groundcrew first.\n Run 'groundcrew sessions' to see available sessions.");
|
|
145
158
|
}
|
|
146
159
|
function sessionQueueFile(sessionDir) {
|
|
147
160
|
return path.join(sessionDir, "queue.json");
|
|
@@ -282,7 +295,7 @@ async function history(_sessionDir) {
|
|
|
282
295
|
console.log();
|
|
283
296
|
}
|
|
284
297
|
}
|
|
285
|
-
async function sessions() {
|
|
298
|
+
async function sessions(filterRepo, filterStatus) {
|
|
286
299
|
const active = await readActiveSessions();
|
|
287
300
|
const ids = Object.keys(active);
|
|
288
301
|
let allDirs = [];
|
|
@@ -298,19 +311,26 @@ async function sessions() {
|
|
|
298
311
|
for (const dir of allDirs) {
|
|
299
312
|
const dashIdx = dir.lastIndexOf("-");
|
|
300
313
|
const repo = dashIdx > 0 ? dir.substring(0, dashIdx) : "unknown";
|
|
314
|
+
if (filterRepo && repo !== filterRepo) continue;
|
|
301
315
|
if (!byRepo.has(repo)) byRepo.set(repo, []);
|
|
302
316
|
byRepo.get(repo).push(dir);
|
|
303
317
|
}
|
|
318
|
+
if (byRepo.size === 0) {
|
|
319
|
+
console.log(dim(filterRepo ? `No sessions found for repo "${filterRepo}".` : "No sessions found."));
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
304
322
|
console.log(bold("Sessions:\n"));
|
|
305
323
|
for (const [repo, dirs] of byRepo) {
|
|
306
324
|
const isCurrent = repo === REPO_NAME;
|
|
307
|
-
|
|
325
|
+
const repoEntries = [];
|
|
308
326
|
for (const dir of dirs) {
|
|
309
327
|
const isActive = ids.includes(dir);
|
|
310
328
|
const sessionDir = path.join(SESSIONS_DIR, dir);
|
|
329
|
+
let sessionStatus = "unknown";
|
|
311
330
|
let info = "";
|
|
312
331
|
try {
|
|
313
332
|
const session = JSON.parse(await fs.readFile(path.join(sessionDir, "session.json"), "utf-8"));
|
|
333
|
+
sessionStatus = session.status || "unknown";
|
|
314
334
|
const startTime = new Date(session.started).getTime();
|
|
315
335
|
const minutes = Math.round((Date.now() - startTime) / 6e4);
|
|
316
336
|
const statusColor = session.status === "active" ? green : session.status === "parked" ? yellow : dim;
|
|
@@ -318,10 +338,15 @@ async function sessions() {
|
|
|
318
338
|
} catch {
|
|
319
339
|
info = dim("no session data");
|
|
320
340
|
}
|
|
341
|
+
if (filterStatus && sessionStatus !== filterStatus) continue;
|
|
321
342
|
const queue = await readQueue(sessionDir);
|
|
322
343
|
const marker = isActive ? green("*") : " ";
|
|
323
344
|
const shortId = dir.substring(repo.length + 1);
|
|
324
|
-
|
|
345
|
+
repoEntries.push(` ${marker} ${cyan(shortId)} ${info} | ${queue.tasks.length} queued`);
|
|
346
|
+
}
|
|
347
|
+
if (repoEntries.length > 0) {
|
|
348
|
+
console.log(` ${isCurrent ? green(repo) : dim(repo)}`);
|
|
349
|
+
for (const entry of repoEntries) console.log(entry);
|
|
325
350
|
}
|
|
326
351
|
}
|
|
327
352
|
if (ids.length > 0) {
|
|
@@ -427,7 +452,6 @@ async function listSessionChoices() {
|
|
|
427
452
|
const active = await readActiveSessions();
|
|
428
453
|
const choices = [];
|
|
429
454
|
for (const [id, entry] of Object.entries(active)) {
|
|
430
|
-
if (!isRepoSession(id)) continue;
|
|
431
455
|
const dir = path.join(SESSIONS_DIR, id);
|
|
432
456
|
let status2 = "active";
|
|
433
457
|
let minutes = 0;
|
|
@@ -452,7 +476,7 @@ async function listSessionChoices() {
|
|
|
452
476
|
async function pickSession(rl) {
|
|
453
477
|
const choices = await listSessionChoices();
|
|
454
478
|
if (choices.length === 0) {
|
|
455
|
-
console.log(red(
|
|
479
|
+
console.log(red("No active sessions. Start Copilot with groundcrew first."));
|
|
456
480
|
return null;
|
|
457
481
|
}
|
|
458
482
|
if (choices.length === 1) {
|
|
@@ -1117,7 +1141,9 @@ ${bold("Usage:")}
|
|
|
1117
1141
|
groundcrew feedback --session <id> <message> Send feedback to a specific session
|
|
1118
1142
|
groundcrew queue List pending tasks
|
|
1119
1143
|
groundcrew status Show session status and last update
|
|
1120
|
-
groundcrew sessions List all sessions
|
|
1144
|
+
groundcrew sessions List all sessions (all repos)
|
|
1145
|
+
groundcrew sessions --repo mekari_credit Filter by repo
|
|
1146
|
+
groundcrew sessions --status active Filter by status (active/parked/ended)
|
|
1121
1147
|
groundcrew history Show completed tasks
|
|
1122
1148
|
groundcrew clear Clear all pending tasks
|
|
1123
1149
|
groundcrew stop Stop all sessions for current repo
|
|
@@ -1151,14 +1177,16 @@ function extractFlag(args, flag) {
|
|
|
1151
1177
|
async function main() {
|
|
1152
1178
|
await resolveRoot();
|
|
1153
1179
|
const rawArgs = process.argv.slice(2);
|
|
1154
|
-
const { value: explicitSession, remaining:
|
|
1180
|
+
const { value: explicitSession, remaining: args1 } = extractFlag(rawArgs, "--session");
|
|
1181
|
+
const { value: filterRepo, remaining: args2 } = extractFlag(args1, "--repo");
|
|
1182
|
+
const { value: filterStatus, remaining: args } = extractFlag(args2, "--status");
|
|
1155
1183
|
const command = args[0];
|
|
1156
1184
|
switch (command) {
|
|
1157
1185
|
case "chat":
|
|
1158
1186
|
await chat(explicitSession);
|
|
1159
1187
|
return;
|
|
1160
1188
|
case "sessions":
|
|
1161
|
-
await sessions();
|
|
1189
|
+
await sessions(filterRepo, filterStatus);
|
|
1162
1190
|
return;
|
|
1163
1191
|
case "history":
|
|
1164
1192
|
await history();
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -78,21 +78,26 @@ const HISTORY_FILE = path.join(GROUNDCREW_HOME, "history.json");
|
|
|
78
78
|
let REPO_NAME = "";
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Derive repo name from CWD
|
|
81
|
+
* Derive repo name from CWD. Uses git --git-common-dir to resolve through
|
|
82
|
+
* worktrees to the main repo root, so all worktrees share one session namespace.
|
|
82
83
|
*/
|
|
83
84
|
async function resolveRoot(): Promise<void> {
|
|
84
|
-
let
|
|
85
|
+
let repoRoot: string | null = null;
|
|
85
86
|
|
|
86
|
-
// 1. Try git
|
|
87
|
+
// 1. Try git --git-common-dir (resolves worktrees to main repo)
|
|
87
88
|
try {
|
|
88
|
-
const { stdout } = await execFileAsync("git", ["rev-parse", "--
|
|
89
|
-
|
|
89
|
+
const { stdout: gitCommonDir } = await execFileAsync("git", ["rev-parse", "--git-common-dir"]);
|
|
90
|
+
const trimmed = gitCommonDir.trim();
|
|
91
|
+
if (trimmed) {
|
|
92
|
+
const absGitDir = path.isAbsolute(trimmed) ? trimmed : path.resolve(process.cwd(), trimmed);
|
|
93
|
+
repoRoot = path.dirname(absGitDir);
|
|
94
|
+
}
|
|
90
95
|
} catch { /* not a git repo or git not installed */ }
|
|
91
96
|
|
|
92
97
|
// 2. Fallback to CWD
|
|
93
|
-
if (!
|
|
98
|
+
if (!repoRoot) repoRoot = process.cwd();
|
|
94
99
|
|
|
95
|
-
REPO_NAME = path.basename(
|
|
100
|
+
REPO_NAME = path.basename(repoRoot).replace(/[^a-zA-Z0-9_-]/g, "_") || "unknown";
|
|
96
101
|
|
|
97
102
|
// Ensure centralized dirs exist
|
|
98
103
|
await fs.mkdir(SESSIONS_DIR, { recursive: true });
|
|
@@ -142,56 +147,68 @@ function isRepoSession(sessionId: string): boolean {
|
|
|
142
147
|
|
|
143
148
|
/**
|
|
144
149
|
* Resolve which session to target.
|
|
145
|
-
* Priority: --session flag >
|
|
150
|
+
* Priority: --session flag > repo match > any active session > error.
|
|
146
151
|
*/
|
|
147
152
|
async function resolveSessionDir(explicitSession?: string): Promise<string> {
|
|
148
153
|
if (explicitSession) {
|
|
154
|
+
// Allow short hex suffix — find the full session ID
|
|
149
155
|
const dir = path.join(SESSIONS_DIR, explicitSession);
|
|
150
|
-
if (
|
|
151
|
-
throw new Error(`Session "${explicitSession}" not found.`);
|
|
152
|
-
}
|
|
153
|
-
return dir;
|
|
154
|
-
}
|
|
156
|
+
if (existsSync(dir)) return dir;
|
|
155
157
|
|
|
156
|
-
|
|
157
|
-
// Filter to sessions for THIS repo
|
|
158
|
-
const ids = Object.keys(sessions).filter(isRepoSession);
|
|
159
|
-
|
|
160
|
-
if (ids.length === 0) {
|
|
161
|
-
// Fallback: check session dirs on disk for this repo
|
|
158
|
+
// Try matching as suffix
|
|
162
159
|
try {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
const stat = await fs.stat(path.join(SESSIONS_DIR, d, "session.json"));
|
|
171
|
-
if (stat.mtimeMs > latest.mtime) {
|
|
172
|
-
latest = { dir: d, mtime: stat.mtimeMs };
|
|
173
|
-
}
|
|
174
|
-
} catch { /* skip */ }
|
|
175
|
-
}
|
|
176
|
-
return path.join(SESSIONS_DIR, latest.dir);
|
|
177
|
-
}
|
|
178
|
-
} catch { /* no sessions dir */ }
|
|
179
|
-
throw new Error(`No active sessions for repo "${REPO_NAME}". Start Copilot with groundcrew first.`);
|
|
160
|
+
const allDirs = await fs.readdir(SESSIONS_DIR);
|
|
161
|
+
const match = allDirs.find((d) => d.endsWith("-" + explicitSession));
|
|
162
|
+
if (match) return path.join(SESSIONS_DIR, match);
|
|
163
|
+
} catch {}
|
|
164
|
+
|
|
165
|
+
throw new Error(`Session "${explicitSession}" not found.`);
|
|
180
166
|
}
|
|
181
167
|
|
|
182
|
-
|
|
183
|
-
|
|
168
|
+
const active = await readActiveSessions();
|
|
169
|
+
|
|
170
|
+
// 1. Try sessions for current repo first
|
|
171
|
+
const repoIds = Object.keys(active).filter(isRepoSession);
|
|
172
|
+
if (repoIds.length === 1) return path.join(SESSIONS_DIR, repoIds[0]);
|
|
173
|
+
if (repoIds.length > 1) {
|
|
174
|
+
let latest = { id: repoIds[0], time: 0 };
|
|
175
|
+
for (const id of repoIds) {
|
|
176
|
+
const started = new Date(active[id].started).getTime();
|
|
177
|
+
if (started > latest.time) latest = { id, time: started };
|
|
178
|
+
}
|
|
179
|
+
return path.join(SESSIONS_DIR, latest.id);
|
|
184
180
|
}
|
|
185
181
|
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
182
|
+
// 2. Fall back to ANY active session
|
|
183
|
+
const allIds = Object.keys(active);
|
|
184
|
+
if (allIds.length === 1) return path.join(SESSIONS_DIR, allIds[0]);
|
|
185
|
+
if (allIds.length > 1) {
|
|
186
|
+
let latest = { id: allIds[0], time: 0 };
|
|
187
|
+
for (const id of allIds) {
|
|
188
|
+
const started = new Date(active[id].started).getTime();
|
|
189
|
+
if (started > latest.time) latest = { id, time: started };
|
|
192
190
|
}
|
|
191
|
+
return path.join(SESSIONS_DIR, latest.id);
|
|
193
192
|
}
|
|
194
|
-
|
|
193
|
+
|
|
194
|
+
// 3. Check dirs on disk
|
|
195
|
+
try {
|
|
196
|
+
const dirs = await fs.readdir(SESSIONS_DIR);
|
|
197
|
+
const repoDirs = dirs.filter(isRepoSession);
|
|
198
|
+
const target = repoDirs.length > 0 ? repoDirs : dirs;
|
|
199
|
+
if (target.length >= 1) {
|
|
200
|
+
let latest = { dir: target[0], mtime: 0 };
|
|
201
|
+
for (const d of target) {
|
|
202
|
+
try {
|
|
203
|
+
const stat = await fs.stat(path.join(SESSIONS_DIR, d, "session.json"));
|
|
204
|
+
if (stat.mtimeMs > latest.mtime) latest = { dir: d, mtime: stat.mtimeMs };
|
|
205
|
+
} catch {}
|
|
206
|
+
}
|
|
207
|
+
return path.join(SESSIONS_DIR, latest.dir);
|
|
208
|
+
}
|
|
209
|
+
} catch {}
|
|
210
|
+
|
|
211
|
+
throw new Error("No active sessions. Start Copilot with groundcrew first.\n Run 'groundcrew sessions' to see available sessions.");
|
|
195
212
|
}
|
|
196
213
|
|
|
197
214
|
function sessionQueueFile(sessionDir: string): string {
|
|
@@ -371,11 +388,11 @@ async function history(_sessionDir?: string): Promise<void> {
|
|
|
371
388
|
}
|
|
372
389
|
}
|
|
373
390
|
|
|
374
|
-
async function sessions(): Promise<void> {
|
|
391
|
+
async function sessions(filterRepo?: string, filterStatus?: string): Promise<void> {
|
|
375
392
|
const active = await readActiveSessions();
|
|
376
393
|
const ids = Object.keys(active);
|
|
377
394
|
|
|
378
|
-
//
|
|
395
|
+
// All session dirs on disk
|
|
379
396
|
let allDirs: string[] = [];
|
|
380
397
|
try {
|
|
381
398
|
allDirs = await fs.readdir(SESSIONS_DIR);
|
|
@@ -391,23 +408,34 @@ async function sessions(): Promise<void> {
|
|
|
391
408
|
for (const dir of allDirs) {
|
|
392
409
|
const dashIdx = dir.lastIndexOf("-");
|
|
393
410
|
const repo = dashIdx > 0 ? dir.substring(0, dashIdx) : "unknown";
|
|
411
|
+
|
|
412
|
+
// Apply --repo filter
|
|
413
|
+
if (filterRepo && repo !== filterRepo) continue;
|
|
414
|
+
|
|
394
415
|
if (!byRepo.has(repo)) byRepo.set(repo, []);
|
|
395
416
|
byRepo.get(repo)!.push(dir);
|
|
396
417
|
}
|
|
397
418
|
|
|
419
|
+
if (byRepo.size === 0) {
|
|
420
|
+
console.log(dim(filterRepo ? `No sessions found for repo "${filterRepo}".` : "No sessions found."));
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
398
424
|
console.log(bold("Sessions:\n"));
|
|
399
425
|
|
|
400
426
|
for (const [repo, dirs] of byRepo) {
|
|
401
427
|
const isCurrent = repo === REPO_NAME;
|
|
402
|
-
|
|
428
|
+
const repoEntries: string[] = [];
|
|
403
429
|
|
|
404
430
|
for (const dir of dirs) {
|
|
405
431
|
const isActive = ids.includes(dir);
|
|
406
432
|
const sessionDir = path.join(SESSIONS_DIR, dir);
|
|
407
433
|
|
|
434
|
+
let sessionStatus = "unknown";
|
|
408
435
|
let info = "";
|
|
409
436
|
try {
|
|
410
437
|
const session = JSON.parse(await fs.readFile(path.join(sessionDir, "session.json"), "utf-8"));
|
|
438
|
+
sessionStatus = session.status || "unknown";
|
|
411
439
|
const startTime = new Date(session.started).getTime();
|
|
412
440
|
const minutes = Math.round((Date.now() - startTime) / 60000);
|
|
413
441
|
const statusColor = session.status === "active" ? green : session.status === "parked" ? yellow : dim;
|
|
@@ -416,12 +444,19 @@ async function sessions(): Promise<void> {
|
|
|
416
444
|
info = dim("no session data");
|
|
417
445
|
}
|
|
418
446
|
|
|
447
|
+
// Apply --status filter
|
|
448
|
+
if (filterStatus && sessionStatus !== filterStatus) continue;
|
|
449
|
+
|
|
419
450
|
const queue = await readQueue(sessionDir);
|
|
420
451
|
const marker = isActive ? green("*") : " ";
|
|
421
|
-
// Show only the hex suffix for cleaner display
|
|
422
452
|
const shortId = dir.substring(repo.length + 1);
|
|
423
453
|
|
|
424
|
-
|
|
454
|
+
repoEntries.push(` ${marker} ${cyan(shortId)} ${info} | ${queue.tasks.length} queued`);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (repoEntries.length > 0) {
|
|
458
|
+
console.log(` ${isCurrent ? green(repo) : dim(repo)}`);
|
|
459
|
+
for (const entry of repoEntries) console.log(entry);
|
|
425
460
|
}
|
|
426
461
|
}
|
|
427
462
|
|
|
@@ -559,9 +594,6 @@ async function listSessionChoices(): Promise<SessionChoice[]> {
|
|
|
559
594
|
const choices: SessionChoice[] = [];
|
|
560
595
|
|
|
561
596
|
for (const [id, entry] of Object.entries(active)) {
|
|
562
|
-
// Only show sessions for the current repo
|
|
563
|
-
if (!isRepoSession(id)) continue;
|
|
564
|
-
|
|
565
597
|
const dir = path.join(SESSIONS_DIR, id);
|
|
566
598
|
let status = "active";
|
|
567
599
|
let minutes = 0;
|
|
@@ -590,7 +622,7 @@ async function pickSession(rl: readline.Interface): Promise<SessionChoice | null
|
|
|
590
622
|
const choices = await listSessionChoices();
|
|
591
623
|
|
|
592
624
|
if (choices.length === 0) {
|
|
593
|
-
console.log(red(
|
|
625
|
+
console.log(red("No active sessions. Start Copilot with groundcrew first."));
|
|
594
626
|
return null;
|
|
595
627
|
}
|
|
596
628
|
|
|
@@ -1253,7 +1285,9 @@ ${bold("Usage:")}
|
|
|
1253
1285
|
groundcrew feedback --session <id> <message> Send feedback to a specific session
|
|
1254
1286
|
groundcrew queue List pending tasks
|
|
1255
1287
|
groundcrew status Show session status and last update
|
|
1256
|
-
groundcrew sessions List all sessions
|
|
1288
|
+
groundcrew sessions List all sessions (all repos)
|
|
1289
|
+
groundcrew sessions --repo mekari_credit Filter by repo
|
|
1290
|
+
groundcrew sessions --status active Filter by status (active/parked/ended)
|
|
1257
1291
|
groundcrew history Show completed tasks
|
|
1258
1292
|
groundcrew clear Clear all pending tasks
|
|
1259
1293
|
groundcrew stop Stop all sessions for current repo
|
|
@@ -1292,8 +1326,10 @@ async function main(): Promise<void> {
|
|
|
1292
1326
|
|
|
1293
1327
|
const rawArgs = process.argv.slice(2);
|
|
1294
1328
|
|
|
1295
|
-
// Extract
|
|
1296
|
-
const { value: explicitSession, remaining:
|
|
1329
|
+
// Extract flags from anywhere in args
|
|
1330
|
+
const { value: explicitSession, remaining: args1 } = extractFlag(rawArgs, "--session");
|
|
1331
|
+
const { value: filterRepo, remaining: args2 } = extractFlag(args1, "--repo");
|
|
1332
|
+
const { value: filterStatus, remaining: args } = extractFlag(args2, "--status");
|
|
1297
1333
|
|
|
1298
1334
|
const command = args[0];
|
|
1299
1335
|
|
|
@@ -1303,7 +1339,7 @@ async function main(): Promise<void> {
|
|
|
1303
1339
|
await chat(explicitSession);
|
|
1304
1340
|
return;
|
|
1305
1341
|
case "sessions":
|
|
1306
|
-
await sessions();
|
|
1342
|
+
await sessions(filterRepo, filterStatus);
|
|
1307
1343
|
return;
|
|
1308
1344
|
case "history":
|
|
1309
1345
|
await history();
|