@kenkaiiii/gg-boss 4.3.148 → 4.3.150
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/cli.js +221 -39
- package/dist/cli.js.map +1 -1
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -86,8 +86,49 @@ var import_react2 = __toESM(require_react(), 1);
|
|
|
86
86
|
// src/discover.ts
|
|
87
87
|
init_esm_shims();
|
|
88
88
|
import fs2 from "fs/promises";
|
|
89
|
+
import { createReadStream } from "fs";
|
|
90
|
+
import readline from "readline";
|
|
91
|
+
import os from "os";
|
|
89
92
|
import path2 from "path";
|
|
90
93
|
async function discoverProjects() {
|
|
94
|
+
const [gg, cc, cx] = await Promise.all([
|
|
95
|
+
discoverGgcoderProjects(),
|
|
96
|
+
discoverClaudeProjects(),
|
|
97
|
+
discoverCodexProjects()
|
|
98
|
+
]);
|
|
99
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
100
|
+
for (const p of [...gg, ...cc, ...cx]) {
|
|
101
|
+
const existing = byPath.get(p.path);
|
|
102
|
+
if (!existing) {
|
|
103
|
+
byPath.set(p.path, p);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
byPath.set(p.path, {
|
|
107
|
+
name: existing.name,
|
|
108
|
+
path: existing.path,
|
|
109
|
+
lastActiveMs: Math.max(existing.lastActiveMs, p.lastActiveMs),
|
|
110
|
+
lastActiveDisplay: "",
|
|
111
|
+
// recomputed below
|
|
112
|
+
sources: mergeSources(existing.sources, p.sources)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const merged = Array.from(byPath.values()).map((p) => ({
|
|
116
|
+
...p,
|
|
117
|
+
lastActiveDisplay: formatRelativeTime(p.lastActiveMs)
|
|
118
|
+
}));
|
|
119
|
+
merged.sort((a, b) => b.lastActiveMs - a.lastActiveMs);
|
|
120
|
+
return merged;
|
|
121
|
+
}
|
|
122
|
+
var SOURCE_ORDER = {
|
|
123
|
+
ggcoder: 0,
|
|
124
|
+
"claude-code": 1,
|
|
125
|
+
codex: 2
|
|
126
|
+
};
|
|
127
|
+
function mergeSources(a, b) {
|
|
128
|
+
const set = /* @__PURE__ */ new Set([...a, ...b]);
|
|
129
|
+
return Array.from(set).sort((x, y) => SOURCE_ORDER[x] - SOURCE_ORDER[y]);
|
|
130
|
+
}
|
|
131
|
+
async function discoverGgcoderProjects() {
|
|
91
132
|
const sessionsDir = getAppPaths().sessionsDir;
|
|
92
133
|
let entries;
|
|
93
134
|
try {
|
|
@@ -98,46 +139,177 @@ async function discoverProjects() {
|
|
|
98
139
|
const results = [];
|
|
99
140
|
for (const entry of entries) {
|
|
100
141
|
const dir = path2.join(sessionsDir, entry);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
stat = await fs2.stat(dir);
|
|
104
|
-
} catch {
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (!stat.isDirectory()) continue;
|
|
108
|
-
let files;
|
|
109
|
-
try {
|
|
110
|
-
files = await fs2.readdir(dir);
|
|
111
|
-
} catch {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
const sessionFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
115
|
-
if (sessionFiles.length === 0) continue;
|
|
116
|
-
let maxMtime = 0;
|
|
117
|
-
for (const f of sessionFiles) {
|
|
118
|
-
try {
|
|
119
|
-
const s = await fs2.stat(path2.join(dir, f));
|
|
120
|
-
if (s.mtimeMs > maxMtime) maxMtime = s.mtimeMs;
|
|
121
|
-
} catch {
|
|
122
|
-
}
|
|
123
|
-
}
|
|
142
|
+
const mtime = await maxJsonlMtime(dir);
|
|
143
|
+
if (mtime === null) continue;
|
|
124
144
|
const decoded = "/" + entry.replace(/_/g, "/");
|
|
125
|
-
|
|
126
|
-
const pathStat = await fs2.stat(decoded);
|
|
127
|
-
if (!pathStat.isDirectory()) continue;
|
|
128
|
-
} catch {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
145
|
+
if (!await isDirectory(decoded)) continue;
|
|
131
146
|
results.push({
|
|
132
147
|
name: path2.basename(decoded),
|
|
133
148
|
path: decoded,
|
|
134
|
-
lastActiveMs:
|
|
135
|
-
lastActiveDisplay: formatRelativeTime(
|
|
149
|
+
lastActiveMs: mtime,
|
|
150
|
+
lastActiveDisplay: formatRelativeTime(mtime),
|
|
151
|
+
sources: ["ggcoder"]
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return results;
|
|
155
|
+
}
|
|
156
|
+
async function discoverClaudeProjects() {
|
|
157
|
+
const projectsDir = path2.join(os.homedir(), ".claude", "projects");
|
|
158
|
+
let entries;
|
|
159
|
+
try {
|
|
160
|
+
entries = await fs2.readdir(projectsDir);
|
|
161
|
+
} catch {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
const results = await Promise.all(
|
|
165
|
+
entries.map(async (entry) => {
|
|
166
|
+
const dir = path2.join(projectsDir, entry);
|
|
167
|
+
const mtime = await maxJsonlMtime(dir);
|
|
168
|
+
if (mtime === null) return null;
|
|
169
|
+
const cwd = await readFirstFromJsonlDir(dir, claudeCwdExtractor) ?? fallbackDashDecode(entry);
|
|
170
|
+
if (!cwd) return null;
|
|
171
|
+
if (!await isDirectory(cwd)) return null;
|
|
172
|
+
return {
|
|
173
|
+
name: path2.basename(cwd),
|
|
174
|
+
path: cwd,
|
|
175
|
+
lastActiveMs: mtime,
|
|
176
|
+
lastActiveDisplay: formatRelativeTime(mtime),
|
|
177
|
+
sources: ["claude-code"]
|
|
178
|
+
};
|
|
179
|
+
})
|
|
180
|
+
);
|
|
181
|
+
return results.filter((p) => p !== null);
|
|
182
|
+
}
|
|
183
|
+
async function discoverCodexProjects() {
|
|
184
|
+
const sessionsDir = path2.join(os.homedir(), ".codex", "sessions");
|
|
185
|
+
if (!await isDirectory(sessionsDir)) return [];
|
|
186
|
+
const files = await collectJsonlFiles(sessionsDir, 4);
|
|
187
|
+
if (files.length === 0) return [];
|
|
188
|
+
files.sort((a, b) => b.mtime - a.mtime);
|
|
189
|
+
const byCwd = /* @__PURE__ */ new Map();
|
|
190
|
+
for (const f of files) {
|
|
191
|
+
const cwd = await readFirstFromFile(f.path, codexCwdExtractor);
|
|
192
|
+
if (!cwd) continue;
|
|
193
|
+
const prev = byCwd.get(cwd);
|
|
194
|
+
if (prev === void 0 || f.mtime > prev) byCwd.set(cwd, f.mtime);
|
|
195
|
+
}
|
|
196
|
+
const results = [];
|
|
197
|
+
for (const [cwd, mtime] of byCwd) {
|
|
198
|
+
if (!await isDirectory(cwd)) continue;
|
|
199
|
+
results.push({
|
|
200
|
+
name: path2.basename(cwd),
|
|
201
|
+
path: cwd,
|
|
202
|
+
lastActiveMs: mtime,
|
|
203
|
+
lastActiveDisplay: formatRelativeTime(mtime),
|
|
204
|
+
sources: ["codex"]
|
|
136
205
|
});
|
|
137
206
|
}
|
|
138
|
-
results.sort((a, b) => b.lastActiveMs - a.lastActiveMs);
|
|
139
207
|
return results;
|
|
140
208
|
}
|
|
209
|
+
async function isDirectory(p) {
|
|
210
|
+
try {
|
|
211
|
+
const s = await fs2.stat(p);
|
|
212
|
+
return s.isDirectory();
|
|
213
|
+
} catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function maxJsonlMtime(dir) {
|
|
218
|
+
if (!await isDirectory(dir)) return null;
|
|
219
|
+
const files = await collectJsonlFiles(dir, 2);
|
|
220
|
+
if (files.length === 0) return null;
|
|
221
|
+
let max = 0;
|
|
222
|
+
for (const f of files) if (f.mtime > max) max = f.mtime;
|
|
223
|
+
return max > 0 ? max : null;
|
|
224
|
+
}
|
|
225
|
+
async function collectJsonlFiles(dir, maxDepth) {
|
|
226
|
+
const out = [];
|
|
227
|
+
await walk(dir, 0);
|
|
228
|
+
return out;
|
|
229
|
+
async function walk(current, depth) {
|
|
230
|
+
let entries;
|
|
231
|
+
try {
|
|
232
|
+
entries = await fs2.readdir(current, { withFileTypes: true });
|
|
233
|
+
} catch {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
for (const e of entries) {
|
|
237
|
+
const full = path2.join(current, e.name);
|
|
238
|
+
if (e.isFile() && e.name.endsWith(".jsonl")) {
|
|
239
|
+
try {
|
|
240
|
+
const s = await fs2.stat(full);
|
|
241
|
+
out.push({ path: full, mtime: s.mtimeMs });
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
} else if (e.isDirectory() && depth < maxDepth) {
|
|
245
|
+
await walk(full, depth + 1);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
var claudeCwdExtractor = (line) => {
|
|
251
|
+
try {
|
|
252
|
+
const parsed = JSON.parse(line);
|
|
253
|
+
if (typeof parsed.cwd === "string" && parsed.cwd.startsWith("/")) return parsed.cwd;
|
|
254
|
+
} catch {
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
};
|
|
258
|
+
var CODEX_CWD_RE = /<cwd>([^<]+)<\/cwd>/;
|
|
259
|
+
var codexCwdExtractor = (line) => {
|
|
260
|
+
try {
|
|
261
|
+
const parsed = JSON.parse(line);
|
|
262
|
+
const cwd = parsed.payload?.cwd;
|
|
263
|
+
if (typeof cwd === "string" && cwd.startsWith("/")) return cwd;
|
|
264
|
+
} catch {
|
|
265
|
+
}
|
|
266
|
+
const m = CODEX_CWD_RE.exec(line);
|
|
267
|
+
if (m && m[1] && m[1].startsWith("/")) return m[1];
|
|
268
|
+
return null;
|
|
269
|
+
};
|
|
270
|
+
async function readFirstFromJsonlDir(dir, extractor) {
|
|
271
|
+
const files = await collectJsonlFiles(dir, 2);
|
|
272
|
+
if (files.length === 0) return null;
|
|
273
|
+
files.sort((a, b) => b.mtime - a.mtime);
|
|
274
|
+
for (const f of files) {
|
|
275
|
+
const v = await readFirstFromFile(f.path, extractor);
|
|
276
|
+
if (v) return v;
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
async function readFirstFromFile(file, extractor) {
|
|
281
|
+
return new Promise((resolve) => {
|
|
282
|
+
const stream = createReadStream(file, { encoding: "utf-8" });
|
|
283
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
284
|
+
let lines = 0;
|
|
285
|
+
let done = false;
|
|
286
|
+
const MAX_LINES = 200;
|
|
287
|
+
const finish = (value) => {
|
|
288
|
+
if (done) return;
|
|
289
|
+
done = true;
|
|
290
|
+
resolve(value);
|
|
291
|
+
rl.close();
|
|
292
|
+
stream.destroy();
|
|
293
|
+
};
|
|
294
|
+
rl.on("line", (line) => {
|
|
295
|
+
if (done) return;
|
|
296
|
+
lines++;
|
|
297
|
+
if (lines > MAX_LINES) {
|
|
298
|
+
finish(null);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const v = extractor(line);
|
|
302
|
+
if (v) finish(v);
|
|
303
|
+
});
|
|
304
|
+
rl.on("close", () => finish(null));
|
|
305
|
+
rl.on("error", () => finish(null));
|
|
306
|
+
stream.on("error", () => finish(null));
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function fallbackDashDecode(entry) {
|
|
310
|
+
if (!entry.startsWith("-")) return null;
|
|
311
|
+
return "/" + entry.slice(1).replace(/-/g, "/");
|
|
312
|
+
}
|
|
141
313
|
function formatRelativeTime(ms) {
|
|
142
314
|
if (ms === 0) return "\u2014";
|
|
143
315
|
const diff = Date.now() - ms;
|
|
@@ -164,7 +336,7 @@ init_esm_shims();
|
|
|
164
336
|
// package.json
|
|
165
337
|
var package_default = {
|
|
166
338
|
name: "@kenkaiiii/gg-boss",
|
|
167
|
-
version: "4.3.
|
|
339
|
+
version: "4.3.150",
|
|
168
340
|
type: "module",
|
|
169
341
|
description: "Orchestrator agent that drives multiple ggcoder sessions across projects from a single chat",
|
|
170
342
|
license: "MIT",
|
|
@@ -334,6 +506,14 @@ function GradientText({ text }) {
|
|
|
334
506
|
// src/link-command.tsx
|
|
335
507
|
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
336
508
|
var VISIBLE_ROWS = 12;
|
|
509
|
+
function sourceBadge(sources) {
|
|
510
|
+
if (sources.length > 1) return { label: "[mix]", color: COLORS.success };
|
|
511
|
+
const only = sources[0];
|
|
512
|
+
if (only === "ggcoder") return { label: "[gg ]", color: COLORS.accent };
|
|
513
|
+
if (only === "claude-code") return { label: "[cc ]", color: COLORS.warning };
|
|
514
|
+
if (only === "codex") return { label: "[cx ]", color: COLORS.primary };
|
|
515
|
+
return { label: "[?? ]", color: COLORS.textDim };
|
|
516
|
+
}
|
|
337
517
|
function LinkScreen({ projects, initialSelected, onDone }) {
|
|
338
518
|
const [cursor, setCursor] = (0, import_react2.useState)(0);
|
|
339
519
|
const [selected, setSelected] = (0, import_react2.useState)(new Set(initialSelected));
|
|
@@ -406,11 +586,14 @@ function LinkScreen({ projects, initialSelected, onDone }) {
|
|
|
406
586
|
const arrow = isCursor ? "\u276F" : " ";
|
|
407
587
|
const nameColor = isCursor ? COLORS.primary : isSelected ? COLORS.success : COLORS.text;
|
|
408
588
|
const checkboxColor = isSelected ? COLORS.success : COLORS.textDim;
|
|
589
|
+
const badge = sourceBadge(p.sources);
|
|
409
590
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { children: [
|
|
410
591
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.primary, children: arrow }),
|
|
411
592
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
412
593
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: checkboxColor, children: checkbox }),
|
|
413
594
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
595
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: badge.color, children: badge.label }),
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
414
597
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: nameColor, bold: isCursor, children: p.name }),
|
|
415
598
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.textDim, children: " " }),
|
|
416
599
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: COLORS.textDim, children: p.lastActiveDisplay })
|
|
@@ -1219,13 +1402,13 @@ init_esm_shims();
|
|
|
1219
1402
|
import { spawn as spawn2 } from "child_process";
|
|
1220
1403
|
import fs3 from "fs";
|
|
1221
1404
|
import path3 from "path";
|
|
1222
|
-
import
|
|
1405
|
+
import os2 from "os";
|
|
1223
1406
|
var PACKAGE_NAME = "@kenkaiiii/gg-boss";
|
|
1224
1407
|
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
1225
1408
|
var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
1226
1409
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
1227
1410
|
function getStateFilePath() {
|
|
1228
|
-
return path3.join(
|
|
1411
|
+
return path3.join(os2.homedir(), ".gg", "boss", "update-state.json");
|
|
1229
1412
|
}
|
|
1230
1413
|
function readState() {
|
|
1231
1414
|
try {
|
|
@@ -1766,10 +1949,9 @@ function StaticRowView({ row }) {
|
|
|
1766
1949
|
return null;
|
|
1767
1950
|
}
|
|
1768
1951
|
function UpdateNoticeRow({ text }) {
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
text
|
|
1952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { marginTop: 1, flexShrink: 1, borderStyle: "round", borderColor: COLORS.accent, paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { wrap: "wrap", children: [
|
|
1953
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: COLORS.accent, bold: true, children: "\u2728 " }),
|
|
1954
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: COLORS.primary, bold: true, children: text })
|
|
1773
1955
|
] }) });
|
|
1774
1956
|
}
|
|
1775
1957
|
function TaskDispatchRow({
|