residoo 0.1.0 → 0.2.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/README.md +225 -46
- package/SECURITY.md +29 -22
- package/package.json +1 -1
- package/src/cli.js +82 -16
- package/src/integrity.js +669 -0
- package/src/patterns.js +78 -5
- package/src/report.js +74 -7
- package/src/sources/agent-configs.js +308 -0
- package/src/sources/aider.js +361 -0
- package/src/sources/amazon-q.js +199 -0
- package/src/sources/antigravity-cli.js +155 -0
- package/src/sources/cline.js +208 -0
- package/src/sources/codebuff.js +295 -0
- package/src/sources/codex-cli.js +258 -0
- package/src/sources/cody.js +325 -0
- package/src/sources/continue.js +408 -0
- package/src/sources/copilot-chat.js +272 -0
- package/src/sources/copilot-cli.js +300 -0
- package/src/sources/crush.js +364 -0
- package/src/sources/cursor.js +374 -0
- package/src/sources/devin-cli.js +241 -0
- package/src/sources/factory-droid.js +153 -0
- package/src/sources/fx.js +136 -0
- package/src/sources/gemini-cli.js +242 -0
- package/src/sources/goose.js +366 -0
- package/src/sources/grok-cli.js +267 -0
- package/src/sources/hermes.js +282 -0
- package/src/sources/index.js +172 -8
- package/src/sources/jetbrains-ai-assistant.js +343 -0
- package/src/sources/jetbrains-junie.js +292 -0
- package/src/sources/kilo-code.js +430 -0
- package/src/sources/kimi-code.js +147 -0
- package/src/sources/kiro-cli.js +393 -0
- package/src/sources/kiro-ide.js +230 -0
- package/src/sources/llm.js +328 -0
- package/src/sources/mentat.js +143 -0
- package/src/sources/open-interpreter.js +224 -0
- package/src/sources/openclaw.js +218 -0
- package/src/sources/opencode.js +379 -0
- package/src/sources/openhands.js +181 -0
- package/src/sources/pearai.js +151 -0
- package/src/sources/pi-agent.js +130 -0
- package/src/sources/qodo-gen.js +189 -0
- package/src/sources/qwen-code.js +244 -0
- package/src/sources/roo-code.js +239 -0
- package/src/sources/trae.js +294 -0
- package/src/sources/void.js +273 -0
- package/src/sources/warp.js +395 -0
- package/src/sources/windsurf.js +256 -0
- package/src/sources/zed.js +374 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hermes Agent (NousResearch/hermes-agent, "the agent that grows with you")
|
|
9
|
+
* local state database.
|
|
10
|
+
*
|
|
11
|
+
* VERIFICATION STATUS: NOT checked against a real install — `hermes` is not
|
|
12
|
+
* on PATH and no `~/.hermes` directory exists on the machine this adapter
|
|
13
|
+
* was built on. Ships per CONTRIBUTING.md rule 3 on two independent
|
|
14
|
+
* corroborating sources, WITH an integrity caveat below that is unusually
|
|
15
|
+
* important to read before trusting this file:
|
|
16
|
+
*
|
|
17
|
+
* 1. Hermes' own docs (fetched from its docs site): primary directory is
|
|
18
|
+
* `~/.hermes` on Linux/macOS/WSL2, `%LOCALAPPDATA%\hermes` on native
|
|
19
|
+
* Windows, and explicitly reference `$HERMES_HOME` as the env var that
|
|
20
|
+
* relocates it (e.g. "Session checkout: $HERMES_HOME/hermes-agent").
|
|
21
|
+
* 2. ccusage (github.com/ccusage/ccusage — a real, independent, actively
|
|
22
|
+
* developed usage-tracking CLI; its own GitHub star count was sanity-
|
|
23
|
+
* checked against known repos before being trusted at all, see below)
|
|
24
|
+
* ships a tested Rust adapter for Hermes
|
|
25
|
+
* (rust/adapters/hermes/src/{paths,parser,loader}.rs), fetched and read
|
|
26
|
+
* directly. It confirms: `${HERMES_HOME:-~/.hermes}/state.db`, a real
|
|
27
|
+
* SQLite database, opened read-only, containing (at minimum) a table
|
|
28
|
+
* literally named `sessions` — its own unit test creates that table
|
|
29
|
+
* with `CREATE TABLE sessions (id TEXT PRIMARY KEY, source TEXT NOT
|
|
30
|
+
* NULL, model TEXT, started_at REAL NOT NULL, message_count INTEGER
|
|
31
|
+
* ..., input_tokens INTEGER ..., ... billing_provider TEXT,
|
|
32
|
+
* estimated_cost_usd REAL, actual_cost_usd REAL)` and queries it with
|
|
33
|
+
* `SELECT id, model, billing_provider, started_at, message_count,
|
|
34
|
+
* input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
|
|
35
|
+
* reasoning_tokens, estimated_cost_usd, actual_cost_usd FROM sessions`.
|
|
36
|
+
*
|
|
37
|
+
* THE INTEGRITY CAVEAT: while researching this source, `gh api` search
|
|
38
|
+
* turned up openclaw/openclaw and NousResearch/hermes-agent GitHub star
|
|
39
|
+
* counts (388,546 and 239,598 respectively, for repos 9-13 months old) that
|
|
40
|
+
* are implausible for organic growth — comparable to or exceeding
|
|
41
|
+
* decade-plus flagship repos like facebook/react and torvalds/linux, which
|
|
42
|
+
* were fetched in the same session as a sanity check and came back in the
|
|
43
|
+
* same 240-250k range. Independent search corroborates a real integrity
|
|
44
|
+
* problem, not just a suspicious number: an arXiv paper on large-scale
|
|
45
|
+
* GitHub fake-star campaigns turned up in the same research pass, alongside
|
|
46
|
+
* a Hacker News thread titled "Nous Research edits GitHub issue to remove
|
|
47
|
+
* plagiarism claims about Hermes Agent," and a GitHub topic description
|
|
48
|
+
* referencing "Two zero-human AI companies battle for GitHub stars using
|
|
49
|
+
* Hermes Agent + Paperclip." None of this proves the file format below is
|
|
50
|
+
* wrong — ccusage's corroborating source is independent of Hermes/OpenClaw
|
|
51
|
+
* and its parsing code is real and tested — but it does mean the two "docs"
|
|
52
|
+
* sources for Hermes and OpenClaw may not be independent of EACH OTHER (one
|
|
53
|
+
* plausibly forked/copied the other; Hermes' own docs describe importing
|
|
54
|
+
* OpenClaw's config directory directly), which weakens this source's
|
|
55
|
+
* corroboration below CONTRIBUTING.md's "2+ independent sources" bar in
|
|
56
|
+
* spirit even where it's technically met. Flagged here and in this
|
|
57
|
+
* adapter's PR description/report; a human should weigh this before
|
|
58
|
+
* treating either this file or openclaw.js as more than
|
|
59
|
+
* multi-source-corroborated-but-unverified.
|
|
60
|
+
*
|
|
61
|
+
* WHAT THIS SOURCE ACTUALLY READS: ccusage's own adapter only ever SELECTs
|
|
62
|
+
* the `sessions` table's usage/cost columns — it has no reason to touch
|
|
63
|
+
* anything else, and its code is silent on whether state.db holds the
|
|
64
|
+
* user's actual conversation text anywhere, and if so, in which table.
|
|
65
|
+
* Hermes' own marketing copy ("searches its own past conversations") implies
|
|
66
|
+
* real message content is persisted somewhere in this database, but no
|
|
67
|
+
* source here names the table. Rather than guess a table/column name likely
|
|
68
|
+
* to be wrong, this source applies the same tolerance cursor.js already
|
|
69
|
+
* established for exactly this situation: it enumerates EVERY table via
|
|
70
|
+
* `sqlite_master` at read time and turns every row of every table into one
|
|
71
|
+
* scanned line, keyed by column name, so it works regardless of which table
|
|
72
|
+
* (if any) turns out to hold conversation content, and keeps working if
|
|
73
|
+
* Hermes' schema drifts. See cursor.js's own docstring for the full
|
|
74
|
+
* reasoning against a hardcoded key/table allowlist.
|
|
75
|
+
*/
|
|
76
|
+
const HERMES_HOME_ENV = "HERMES_HOME";
|
|
77
|
+
|
|
78
|
+
function hermesHomeDirs() {
|
|
79
|
+
const envVal = process.env[HERMES_HOME_ENV];
|
|
80
|
+
if (envVal && envVal.trim() !== "") {
|
|
81
|
+
return envVal.split(",").map((s) => s.trim()).filter((s) => s !== "").map((p) => path.resolve(p));
|
|
82
|
+
}
|
|
83
|
+
const home = os.homedir();
|
|
84
|
+
if (process.platform === "win32") {
|
|
85
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
|
|
86
|
+
return [path.join(localAppData, "hermes")];
|
|
87
|
+
}
|
|
88
|
+
return [path.join(home, ".hermes")];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function stateDbPaths() {
|
|
92
|
+
return hermesHomeDirs().map((dir) => path.join(dir, "state.db"));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Lazily require node:sqlite — see cursor.js's getDatabaseSync() docstring
|
|
97
|
+
* for the full reasoning (avoid Node's one-time ExperimentalWarning on every
|
|
98
|
+
* `residoo scan` for users who have never touched a SQLite-backed source).
|
|
99
|
+
* Duplicated rather than shared per this project's one-small-self-contained-
|
|
100
|
+
* file convention.
|
|
101
|
+
*/
|
|
102
|
+
let sqliteRequireAttempted = false;
|
|
103
|
+
let DatabaseSync = null;
|
|
104
|
+
function getDatabaseSync() {
|
|
105
|
+
if (!sqliteRequireAttempted) {
|
|
106
|
+
sqliteRequireAttempted = true;
|
|
107
|
+
try { ({ DatabaseSync } = require("node:sqlite")); }
|
|
108
|
+
catch { DatabaseSync = null; }
|
|
109
|
+
}
|
|
110
|
+
return DatabaseSync;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function id() { return "hermes"; }
|
|
114
|
+
function label() { return "Hermes"; }
|
|
115
|
+
|
|
116
|
+
function homeDirExists() {
|
|
117
|
+
return hermesHomeDirs().some((dir) => {
|
|
118
|
+
try { return fs.statSync(dir).isDirectory(); } catch { return false; }
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function available() {
|
|
123
|
+
// Cheap fs check first — see cursor.js's available() for why short-
|
|
124
|
+
// circuiting matters: the common case is Hermes simply isn't installed,
|
|
125
|
+
// and that answer must not cost requiring node:sqlite.
|
|
126
|
+
return homeDirExists() && Boolean(getDatabaseSync());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function unavailableReason() {
|
|
130
|
+
if (!homeDirExists()) return null;
|
|
131
|
+
if (getDatabaseSync()) return null;
|
|
132
|
+
return "Hermes detected but not scanned — needs Node.js 22.5+ (node:sqlite not present in this runtime)";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Same lstat-first, follow-if-symlink shape as cursor.js's statIfPresent —
|
|
137
|
+
* duplicated for the same reason (no Dirent available for a constructed
|
|
138
|
+
* path). A HERMES_HOME that doesn't have a state.db yet (Hermes installed
|
|
139
|
+
* but never run) is normal and not broken.
|
|
140
|
+
*/
|
|
141
|
+
function* statIfPresent(dbPath) {
|
|
142
|
+
let lst;
|
|
143
|
+
try { lst = fs.lstatSync(dbPath); }
|
|
144
|
+
catch { return; }
|
|
145
|
+
|
|
146
|
+
if (lst.isSymbolicLink()) {
|
|
147
|
+
try {
|
|
148
|
+
const st = fs.statSync(dbPath);
|
|
149
|
+
if (!st.isFile()) { yield { file: dbPath, broken: true }; return; }
|
|
150
|
+
yield { file: dbPath, mtimeMs: st.mtimeMs, sizeBytes: st.size, broken: false };
|
|
151
|
+
} catch {
|
|
152
|
+
yield { file: dbPath, broken: true };
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!lst.isFile()) return;
|
|
158
|
+
yield { file: dbPath, mtimeMs: lst.mtimeMs, sizeBytes: lst.size, broken: false };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function* files() {
|
|
162
|
+
for (const dbPath of stateDbPaths()) yield* statIfPresent(dbPath);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// No real state.db has been inspected to size this against — unlike
|
|
166
|
+
// claude-code.js's MAX_BYTES, this is a generous, untested backstop only,
|
|
167
|
+
// same honesty as cursor.js's own MAX_DB_BYTES about the same gap.
|
|
168
|
+
const MAX_DB_BYTES = 512 * 1024 * 1024;
|
|
169
|
+
const READ_TIMEOUT_MS = 60_000;
|
|
170
|
+
const BUSY_TIMEOUT_MS = 5_000;
|
|
171
|
+
const YIELD_EVERY_N_ROWS = 500;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* A BLOB-affinity column comes back as a Uint8Array from node:sqlite, not a
|
|
175
|
+
* Buffer — see cursor.js's valueToText() docstring, verified there directly
|
|
176
|
+
* against this project's own node:sqlite, not assumed from Node's docs.
|
|
177
|
+
*/
|
|
178
|
+
function valueToText(value) {
|
|
179
|
+
if (typeof value === "string") return value;
|
|
180
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
181
|
+
if (value instanceof Uint8Array) return Buffer.from(value).toString("utf-8");
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Read one state.db as an array of raw text "lines" — one per row, across
|
|
187
|
+
* EVERY table found in sqlite_master (see the module docstring for why this
|
|
188
|
+
* is deliberately schema-agnostic rather than hardcoding just `sessions`).
|
|
189
|
+
* Each row becomes one JSON-stringified object of {column: text}, skipping
|
|
190
|
+
* columns whose value isn't text/number/blob (NULL, mainly).
|
|
191
|
+
*
|
|
192
|
+
* Same synchronous-native-call constraint cursor.js's readLines() docstring
|
|
193
|
+
* explains: node:sqlite's DatabaseSync/StatementSync have no event/AbortSignal
|
|
194
|
+
* to hook a real preemptive timeout onto, so this yields to the event loop
|
|
195
|
+
* and checks a wall-clock deadline every YIELD_EVERY_N_ROWS rows across all
|
|
196
|
+
* tables combined — bounding "too many total rows across too many tables
|
|
197
|
+
* taking too long", not a single pathological row's own decode time.
|
|
198
|
+
*/
|
|
199
|
+
async function readLines(file) {
|
|
200
|
+
const DB = getDatabaseSync();
|
|
201
|
+
if (!DB) return { lines: [], status: "failed", bytesRead: 0 };
|
|
202
|
+
|
|
203
|
+
let stat;
|
|
204
|
+
try { stat = fs.statSync(file); }
|
|
205
|
+
catch { return { lines: [], status: "failed", bytesRead: 0 }; }
|
|
206
|
+
if (stat.size > MAX_DB_BYTES) return { lines: [], status: "too-large", bytesRead: 0 };
|
|
207
|
+
|
|
208
|
+
let db;
|
|
209
|
+
try {
|
|
210
|
+
db = new DB(file, { readOnly: true });
|
|
211
|
+
db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`);
|
|
212
|
+
} catch {
|
|
213
|
+
return { lines: [], status: "failed", bytesRead: 0 };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let tableNames;
|
|
217
|
+
try {
|
|
218
|
+
tableNames = db
|
|
219
|
+
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
|
|
220
|
+
.all()
|
|
221
|
+
.map((row) => row.name)
|
|
222
|
+
.filter((name) => typeof name === "string");
|
|
223
|
+
} catch {
|
|
224
|
+
try { db.close(); } catch { /* best-effort */ }
|
|
225
|
+
return { lines: [], status: "failed", bytesRead: 0 };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const lines = [];
|
|
229
|
+
let bytesRead = 0;
|
|
230
|
+
const deadline = Date.now() + READ_TIMEOUT_MS;
|
|
231
|
+
let timedOut = false;
|
|
232
|
+
let sawError = false;
|
|
233
|
+
let foundAnyTable = false;
|
|
234
|
+
|
|
235
|
+
for (const table of tableNames) {
|
|
236
|
+
let rows;
|
|
237
|
+
try {
|
|
238
|
+
// Table names come from sqlite_master itself, not user input, but are
|
|
239
|
+
// still interpolated into SQL text — quote as a SQLite identifier
|
|
240
|
+
// (doubled internal quotes) rather than trusting they're bare words.
|
|
241
|
+
const quoted = `"${table.replace(/"/g, '""')}"`;
|
|
242
|
+
rows = db.prepare(`SELECT * FROM ${quoted}`).iterate();
|
|
243
|
+
} catch {
|
|
244
|
+
continue; // this table genuinely can't be queried — move on, not fatal to the others
|
|
245
|
+
}
|
|
246
|
+
foundAnyTable = true;
|
|
247
|
+
|
|
248
|
+
let n = 0;
|
|
249
|
+
try {
|
|
250
|
+
for (const row of rows) {
|
|
251
|
+
const record = {};
|
|
252
|
+
let hasText = false;
|
|
253
|
+
for (const [column, value] of Object.entries(row)) {
|
|
254
|
+
const text = valueToText(value);
|
|
255
|
+
if (text !== null && text !== "") { record[column] = text; hasText = true; }
|
|
256
|
+
}
|
|
257
|
+
if (hasText) {
|
|
258
|
+
const line = JSON.stringify(record);
|
|
259
|
+
lines.push(line);
|
|
260
|
+
bytesRead += Buffer.byteLength(line, "utf-8");
|
|
261
|
+
}
|
|
262
|
+
n++;
|
|
263
|
+
if (n % YIELD_EVERY_N_ROWS === 0) {
|
|
264
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
265
|
+
if (Date.now() > deadline) { timedOut = true; break; }
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} catch {
|
|
269
|
+
sawError = true;
|
|
270
|
+
}
|
|
271
|
+
if (timedOut) break;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
try { db.close(); } catch { /* best-effort close */ }
|
|
275
|
+
|
|
276
|
+
if (!foundAnyTable) return { lines: [], status: "failed", bytesRead: 0 };
|
|
277
|
+
if (sawError && lines.length === 0) return { lines: [], status: "failed", bytesRead };
|
|
278
|
+
if (timedOut || sawError) return { lines, status: lines.length > 0 ? "partial" : "failed", bytesRead };
|
|
279
|
+
return { lines, status: "complete", bytesRead };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
module.exports = { id, label, available, unavailableReason, files, readLines };
|
package/src/sources/index.js
CHANGED
|
@@ -1,20 +1,184 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Registry of
|
|
4
|
+
* Registry of scan sources. Each source is a small adapter exposing
|
|
5
5
|
* { id, label, available, files, readLines } — see claude-code.js for the
|
|
6
6
|
* reference implementation and CONTRIBUTING.md for how to add one.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* All but one of these read TRANSCRIPT stores — the session histories agents
|
|
9
|
+
* write as a side effect of working. The exception is agent-configs.js, which
|
|
10
|
+
* reads agent CONFIG files (settings, MCP server configs, memory files)
|
|
11
|
+
* through the identical contract: configs are where users deliberately put
|
|
12
|
+
* env blocks and where approved-command caches accumulate tokens, so they
|
|
13
|
+
* leak by a different mechanism than transcripts but are scanned the same
|
|
14
|
+
* way. The distinction matters for scope reasoning — a transcript source
|
|
15
|
+
* covers what an agent SAW, the config source covers what an agent was
|
|
16
|
+
* CONFIGURED with — and each file's header states which it is.
|
|
17
|
+
*
|
|
18
|
+
* TRUST TIERS — read this before treating every row below the same way.
|
|
19
|
+
* Each source file states its own tier plainly in its header docstring;
|
|
20
|
+
* this is a summary, not a substitute for reading one before relying on it.
|
|
21
|
+
*
|
|
22
|
+
* - REAL-INSTALL-VERIFIED: the adapter was run against an actual,
|
|
23
|
+
* populated installation of the tool and confirmed to find real
|
|
24
|
+
* content. Currently Claude Code, plus agent-configs.js's Claude-family
|
|
25
|
+
* paths (its non-Claude paths sit in the tier below — that file's
|
|
26
|
+
* header tracks verification per path, not per file).
|
|
27
|
+
* - MULTI-SOURCE-CORROBORATED-BUT-UNVERIFIED: the path/schema is backed by
|
|
28
|
+
* 2+ independent, credible sources (official docs, the tool's own
|
|
29
|
+
* shipped source code, a real community tool that reads the same files
|
|
30
|
+
* for a living, or a real user's own reported install) but was NOT
|
|
31
|
+
* checked against a real install of the tool on any machine this project
|
|
32
|
+
* was built on. This is the tier every source below Cursor is in. A
|
|
33
|
+
* scanner that silently checks the wrong path and reports "all clear" is
|
|
34
|
+
* worse than not supporting the tool, so every adapter in this tier is
|
|
35
|
+
* built to fail loudly (`broken: true`, `status: "failed"`) rather than
|
|
36
|
+
* silently — but the PATH ITSELF could still be stale or wrong in a way
|
|
37
|
+
* only a real install can catch. If you have one of these tools
|
|
38
|
+
* installed, running `residoo scan` and reporting back whether
|
|
39
|
+
* `filesScanned` looks right for what you know is on disk is the single
|
|
40
|
+
* most useful thing you can do.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately NOT included here, investigated and skipped rather than
|
|
43
|
+
* guessed at (see each PR/commit description for the full reasoning):
|
|
44
|
+
* - Plandex — confirmed, by reading its actual CLI source, to be
|
|
45
|
+
* client-server with no local transcript content on disk at all (only
|
|
46
|
+
* auth tokens and a project-id pointer live locally); nothing to scan.
|
|
47
|
+
* - CodeGPT — an account/cloud-based product; its own docs describe
|
|
48
|
+
* conversation retention in plan-tier/account terms, and no local
|
|
49
|
+
* chat-history file (official or third-party) was ever found.
|
|
50
|
+
* - Augment Code — only local config/rules files were confirmed; no
|
|
51
|
+
* evidence anywhere (official or third-party) of local chat-transcript
|
|
52
|
+
* storage, consistent with its server-side "Context Engine" design.
|
|
53
|
+
* - Tabby, Tabnine, Zencoder, Tongyi Lingma, Berd — researched, but no
|
|
54
|
+
* source reached this project's 2-independent-source bar for a
|
|
55
|
+
* transcript-content path in the time available. Worth a follow-up PR.
|
|
56
|
+
* - Replit Agent — confirmed cloud-only (server-side storage, nothing
|
|
57
|
+
* local to scan).
|
|
14
58
|
*/
|
|
15
59
|
const claudeCode = require("./claude-code");
|
|
60
|
+
// Not a transcript store — agent config/state files (see module docstring
|
|
61
|
+
// above and that file's own header for the per-path verification trail).
|
|
62
|
+
const agentConfigs = require("./agent-configs");
|
|
63
|
+
const cursor = require("./cursor");
|
|
64
|
+
|
|
65
|
+
// The rest of these are grouped the way they were researched/built, purely
|
|
66
|
+
// to keep this list navigable — the grouping carries no behavioral meaning,
|
|
67
|
+
// every entry goes through the identical { id, label, available, files,
|
|
68
|
+
// readLines } contract. All are MULTI-SOURCE-CORROBORATED-BUT-UNVERIFIED —
|
|
69
|
+
// see each file's own header for exactly what was and wasn't checked, and
|
|
70
|
+
// note two partial exceptions worth naming here rather than only in the
|
|
71
|
+
// file: jetbrains-junie.js and jetbrains-ai-assistant.js had their directory
|
|
72
|
+
// *layout* (not their chat-content schema) confirmed against a real, if
|
|
73
|
+
// long-dormant, JetBrains install found on this project's own build machine
|
|
74
|
+
// — see those two files for what that does and doesn't cover. qodo-gen.js
|
|
75
|
+
// is flagged in its own header as the single weakest-verified source here
|
|
76
|
+
// (one vendor's own docs, restated twice, plus one third-party artifact that
|
|
77
|
+
// likely documents a different Qodo product).
|
|
78
|
+
const codexCli = require("./codex-cli");
|
|
79
|
+
const opencode = require("./opencode");
|
|
80
|
+
|
|
81
|
+
const aider = require("./aider");
|
|
82
|
+
|
|
83
|
+
const cline = require("./cline");
|
|
84
|
+
const rooCode = require("./roo-code");
|
|
85
|
+
const kiloCode = require("./kilo-code");
|
|
86
|
+
|
|
87
|
+
const windsurf = require("./windsurf");
|
|
88
|
+
|
|
89
|
+
const pearai = require("./pearai");
|
|
90
|
+
const trae = require("./trae");
|
|
91
|
+
const voidEditor = require("./void");
|
|
92
|
+
|
|
93
|
+
const geminiCli = require("./gemini-cli");
|
|
94
|
+
const qwenCode = require("./qwen-code");
|
|
95
|
+
|
|
96
|
+
const continueDev = require("./continue");
|
|
97
|
+
|
|
98
|
+
const openInterpreter = require("./open-interpreter");
|
|
99
|
+
const goose = require("./goose");
|
|
100
|
+
|
|
101
|
+
const copilotChat = require("./copilot-chat");
|
|
102
|
+
const copilotCli = require("./copilot-cli");
|
|
103
|
+
|
|
104
|
+
const llm = require("./llm");
|
|
105
|
+
|
|
106
|
+
const codebuff = require("./codebuff");
|
|
107
|
+
const mentat = require("./mentat");
|
|
108
|
+
const hermes = require("./hermes");
|
|
109
|
+
const openclaw = require("./openclaw");
|
|
110
|
+
// Plandex investigated and deliberately not included — see module docstring.
|
|
111
|
+
|
|
112
|
+
const warp = require("./warp");
|
|
113
|
+
const crush = require("./crush");
|
|
114
|
+
const grokCli = require("./grok-cli");
|
|
115
|
+
const kiroCli = require("./kiro-cli");
|
|
116
|
+
const kiroIde = require("./kiro-ide");
|
|
117
|
+
|
|
118
|
+
const zed = require("./zed");
|
|
119
|
+
|
|
120
|
+
const jetbrainsJunie = require("./jetbrains-junie");
|
|
121
|
+
const jetbrainsAiAssistant = require("./jetbrains-ai-assistant");
|
|
122
|
+
|
|
123
|
+
const cody = require("./cody");
|
|
124
|
+
const amazonQ = require("./amazon-q");
|
|
125
|
+
const qodoGen = require("./qodo-gen");
|
|
126
|
+
// Augment Code and CodeGPT investigated and deliberately not included —
|
|
127
|
+
// see module docstring.
|
|
128
|
+
|
|
129
|
+
const openhands = require("./openhands");
|
|
130
|
+
const factoryDroid = require("./factory-droid");
|
|
131
|
+
const devinCli = require("./devin-cli");
|
|
132
|
+
const piAgent = require("./pi-agent");
|
|
133
|
+
const antigravityCli = require("./antigravity-cli");
|
|
134
|
+
const kimiCode = require("./kimi-code");
|
|
135
|
+
const fx = require("./fx");
|
|
16
136
|
|
|
17
|
-
const ALL_SOURCES = [
|
|
137
|
+
const ALL_SOURCES = [
|
|
138
|
+
claudeCode,
|
|
139
|
+
agentConfigs,
|
|
140
|
+
cursor,
|
|
141
|
+
codexCli,
|
|
142
|
+
opencode,
|
|
143
|
+
aider,
|
|
144
|
+
cline,
|
|
145
|
+
rooCode,
|
|
146
|
+
kiloCode,
|
|
147
|
+
windsurf,
|
|
148
|
+
pearai,
|
|
149
|
+
trae,
|
|
150
|
+
voidEditor,
|
|
151
|
+
geminiCli,
|
|
152
|
+
qwenCode,
|
|
153
|
+
continueDev,
|
|
154
|
+
openInterpreter,
|
|
155
|
+
goose,
|
|
156
|
+
copilotChat,
|
|
157
|
+
copilotCli,
|
|
158
|
+
llm,
|
|
159
|
+
codebuff,
|
|
160
|
+
mentat,
|
|
161
|
+
hermes,
|
|
162
|
+
openclaw,
|
|
163
|
+
warp,
|
|
164
|
+
crush,
|
|
165
|
+
grokCli,
|
|
166
|
+
kiroCli,
|
|
167
|
+
kiroIde,
|
|
168
|
+
zed,
|
|
169
|
+
jetbrainsJunie,
|
|
170
|
+
jetbrainsAiAssistant,
|
|
171
|
+
cody,
|
|
172
|
+
amazonQ,
|
|
173
|
+
qodoGen,
|
|
174
|
+
openhands,
|
|
175
|
+
factoryDroid,
|
|
176
|
+
devinCli,
|
|
177
|
+
piAgent,
|
|
178
|
+
antigravityCli,
|
|
179
|
+
kimiCode,
|
|
180
|
+
fx,
|
|
181
|
+
];
|
|
18
182
|
|
|
19
183
|
function availableSources() {
|
|
20
184
|
return ALL_SOURCES.filter((s) => s.available());
|