residoo 0.1.0 → 0.3.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.
Files changed (52) hide show
  1. package/README.md +334 -46
  2. package/SECURITY.md +29 -22
  3. package/package.json +1 -1
  4. package/src/cli.js +249 -17
  5. package/src/integrity.js +689 -0
  6. package/src/patterns.js +78 -5
  7. package/src/report.js +188 -8
  8. package/src/rotation.js +834 -0
  9. package/src/sources/agent-configs.js +308 -0
  10. package/src/sources/aider.js +361 -0
  11. package/src/sources/amazon-q.js +199 -0
  12. package/src/sources/antigravity-cli.js +155 -0
  13. package/src/sources/cline.js +208 -0
  14. package/src/sources/codebuff.js +295 -0
  15. package/src/sources/codex-cli.js +258 -0
  16. package/src/sources/cody.js +325 -0
  17. package/src/sources/continue.js +408 -0
  18. package/src/sources/copilot-chat.js +272 -0
  19. package/src/sources/copilot-cli.js +300 -0
  20. package/src/sources/crush.js +364 -0
  21. package/src/sources/cursor.js +374 -0
  22. package/src/sources/devin-cli.js +241 -0
  23. package/src/sources/factory-droid.js +153 -0
  24. package/src/sources/fx.js +136 -0
  25. package/src/sources/gemini-cli.js +242 -0
  26. package/src/sources/goose.js +366 -0
  27. package/src/sources/grok-cli.js +267 -0
  28. package/src/sources/hermes.js +282 -0
  29. package/src/sources/index.js +172 -8
  30. package/src/sources/jetbrains-ai-assistant.js +343 -0
  31. package/src/sources/jetbrains-junie.js +292 -0
  32. package/src/sources/kilo-code.js +430 -0
  33. package/src/sources/kimi-code.js +147 -0
  34. package/src/sources/kiro-cli.js +393 -0
  35. package/src/sources/kiro-ide.js +230 -0
  36. package/src/sources/llm.js +328 -0
  37. package/src/sources/mentat.js +143 -0
  38. package/src/sources/open-interpreter.js +224 -0
  39. package/src/sources/openclaw.js +218 -0
  40. package/src/sources/opencode.js +379 -0
  41. package/src/sources/openhands.js +181 -0
  42. package/src/sources/pearai.js +151 -0
  43. package/src/sources/pi-agent.js +130 -0
  44. package/src/sources/project-artifacts.js +355 -0
  45. package/src/sources/qodo-gen.js +189 -0
  46. package/src/sources/qwen-code.js +244 -0
  47. package/src/sources/roo-code.js +239 -0
  48. package/src/sources/trae.js +294 -0
  49. package/src/sources/void.js +273 -0
  50. package/src/sources/warp.js +395 -0
  51. package/src/sources/windsurf.js +256 -0
  52. package/src/sources/zed.js +374 -0
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+
7
+ /**
8
+ * Sourcegraph Cody — the VS Code extension (publisher.name `sourcegraph.cody-ai`).
9
+ *
10
+ * VERIFICATION STATUS (read this before trusting anything below):
11
+ * multi-source-corroborated-but-UNVERIFIED against a real install. Neither VS
12
+ * Code nor the Cody extension is installed on the machine this adapter was
13
+ * built on (checked: no /Applications/*Code*.app, no `code`/`code-insiders`
14
+ * on PATH, no ~/Library/Application Support/Code, no
15
+ * ~/.vscode/extensions/sourcegraph.cody-ai-*, no
16
+ * ~/Library/Application Support/JetBrains/<product>/options/cody_history.xml).
17
+ * What
18
+ * IS unusually strong here, short of a real install: the storage mechanism
19
+ * was read directly out of two projects' own current shipped source — not
20
+ * inferred from a blog post — cross-checked against Sourcegraph's own docs:
21
+ *
22
+ * 1. CODY'S OWN SOURCE, read directly from
23
+ * github.com/sourcegraph/cody-public-snapshot (`main`,
24
+ * vscode/src/services/LocalStorageProvider.ts, fetched verbatim via
25
+ * `gh api repos/sourcegraph/cody-public-snapshot/contents/...` on
26
+ * 2026-09-02): the class keeps
27
+ * `protected readonly KEY_LOCAL_HISTORY = 'cody-local-chatHistory-v2'`
28
+ * and every read/write of chat history
29
+ * (getChatHistory/setChatHistory/deleteChatHistory) goes through
30
+ * `this.storage.get/update(this.KEY_LOCAL_HISTORY, ...)`, where
31
+ * `this.storage` is set, once, at extension activation via
32
+ * `localStorage.setStorage(context.globalState)` (see the same file,
33
+ * `activate()`/`initVSCodeStorage()` in `LocalStorageProvider.ts` and
34
+ * `extension.node.ts`) — i.e. the standard VS Code extension `Memento`
35
+ * API (`context.globalState`), not a bespoke file Cody writes itself.
36
+ * `vscode/package.json` (same repo, same fetch) confirms the extension's
37
+ * identity: `"name": "cody-ai"`, `"publisher": "sourcegraph"` — VS
38
+ * Code's own `getExtensionId(publisher, name)` (see next point) makes
39
+ * that `sourcegraph.cody-ai`, matching the `globalStorage/
40
+ * sourcegraph.cody-ai/` path fragment independently named in
41
+ * Sourcegraph's own troubleshooting docs
42
+ * (sourcegraph.com/docs/cody/troubleshooting, a real user's globalStorage
43
+ * resource path is quoted there verbatim).
44
+ *
45
+ * 2. VS CODE'S OWN SOURCE for what `context.globalState` actually resolves
46
+ * to on disk, read directly from github.com/microsoft/vscode (`main`,
47
+ * fetched the same way, 2026-09-02):
48
+ * - `src/vs/workbench/api/browser/mainThreadStorage.ts` —
49
+ * `$setValue(shared, key, value)` calls
50
+ * `extensionStorageService.setExtensionState(key, value, shared)`,
51
+ * where `key` here is the calling extension's id, not a
52
+ * caller-chosen storage key.
53
+ * - `src/vs/platform/extensionManagement/common/extensionStorage.ts`
54
+ * — `setExtensionState(extension, state, global)` resolves
55
+ * `extensionId = getExtensionId(extension)` and then does
56
+ * `storageService.store(extensionId, JSON.stringify(state),
57
+ * global ? StorageScope.PROFILE : StorageScope.WORKSPACE, ...)`.
58
+ * `getExtensionId(publisher, name)` (`extensionManagementUtil.ts`)
59
+ * is exactly `` `${publisher}.${name}` ``.
60
+ * - Net effect: EVERY globalState key an extension sets (Cody's
61
+ * `cody-local-chatHistory-v2` included) is merged into ONE JSON
62
+ * object, and that whole object is stored as a SINGLE row, keyed by
63
+ * the extension id itself, in the PROFILE-scope storage — which is
64
+ * the same shared, per-profile `state.vscdb` / `ItemTable` that
65
+ * cursor.js already reads for Cursor's own tables in this project
66
+ * (Cursor is a VS Code fork; this is the un-forked, upstream version
67
+ * of that exact mechanism). This is CORE VS Code behavior, not
68
+ * Cody-specific or version-fragile the way Cursor's own bespoke
69
+ * `cursorDiskKV` table naming has reportedly been (see cursor.js).
70
+ *
71
+ * Net path: `<VS Code User dir>/globalStorage/state.vscdb`, table
72
+ * `ItemTable`, one row with `key = 'sourcegraph.cody-ai'` whose `value` is a
73
+ * JSON object containing (among any other keys the extension has ever set)
74
+ * `cody-local-chatHistory-v2` — every chat transcript title, message, and any
75
+ * pasted code/output the user has had Cody see.
76
+ *
77
+ * Deliberately scoped to VS Code only. Cody also ships a JetBrains plugin,
78
+ * but its chat history lives in a fundamentally different place: read
79
+ * directly from the same cody-public-snapshot repo,
80
+ * `jetbrains/src/main/kotlin/com/sourcegraph/cody/history/HistoryService.kt`
81
+ * declares `@State(name = "ChatHistory", storages =
82
+ * [Storage("cody_history.xml")])` at `@Service(Service.Level.PROJECT)` —
83
+ * PROJECT level, meaning one `cody_history.xml` per JetBrains project
84
+ * (typically under that project's own `.idea/` directory), not one file
85
+ * under a single, enumerable user-profile directory the way every other
86
+ * source in this project works. Finding those would mean either scanning the
87
+ * whole filesystem for `.idea/cody_history.xml` or trusting a JetBrains
88
+ * "recent projects" list — neither verified here and both a meaningfully
89
+ * different shape of problem — so it is left out rather than guessed at. See
90
+ * CONTRIBUTING.md.
91
+ *
92
+ * Only the DEFAULT VS Code profile is covered, for the same reason
93
+ * copilot-chat.js in this project already gives for itself: a non-default
94
+ * profile's globalStorage lives under `User/profiles/<profileId>/...`
95
+ * instead of `User/globalStorage` directly, per
96
+ * `IUserDataProfilesService.defaultProfile` in the same VS Code source read
97
+ * above.
98
+ */
99
+ function vscodeUserDirs() {
100
+ const home = os.homedir();
101
+ const variants = ["Code", "Code - Insiders"];
102
+ if (process.platform === "darwin") {
103
+ return variants.map((v) => path.join(home, "Library", "Application Support", v, "User"));
104
+ }
105
+ if (process.platform === "win32") {
106
+ const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
107
+ return variants.map((v) => path.join(appData, v, "User"));
108
+ }
109
+ // Linux and other XDG-following unix platforms.
110
+ const configHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config");
111
+ return variants.map((v) => path.join(configHome, v, "User"));
112
+ }
113
+
114
+ function id() { return "cody"; }
115
+ function label() { return "Sourcegraph Cody"; }
116
+
117
+ /**
118
+ * Same lazy-require pattern as cursor.js, for the same reason: index.js
119
+ * requires every source unconditionally, so an eager top-level
120
+ * `require("node:sqlite")` would print Node's one-per-process
121
+ * ExperimentalWarning for every user on Node 22.5+, even the large majority
122
+ * who have never installed VS Code at all. See cursor.js's own docstring on
123
+ * getDatabaseSync() for the full reasoning — duplicated here rather than
124
+ * imported, matching this project's "small, self-contained file" convention.
125
+ */
126
+ const NODE_SQLITE_REQUIREMENT = "needs Node.js 22.5+ (node:sqlite not present in this runtime)";
127
+ let sqliteRequireAttempted = false;
128
+ let DatabaseSync = null;
129
+
130
+ function getDatabaseSync() {
131
+ if (!sqliteRequireAttempted) {
132
+ sqliteRequireAttempted = true;
133
+ try { ({ DatabaseSync } = require("node:sqlite")); }
134
+ catch { DatabaseSync = null; }
135
+ }
136
+ return DatabaseSync;
137
+ }
138
+
139
+ function anyVscodeUserDirExists() {
140
+ return vscodeUserDirs().some((dir) => {
141
+ try { return fs.statSync(dir).isDirectory(); } catch { return false; }
142
+ });
143
+ }
144
+
145
+ function available() {
146
+ // Cheap fs check first, on purpose — see cursor.js's available() for why
147
+ // short-circuiting here matters (skip requiring node:sqlite, and its
148
+ // possible warning, when there is plainly nothing to read).
149
+ return anyVscodeUserDirExists() && Boolean(getDatabaseSync());
150
+ }
151
+
152
+ /**
153
+ * Same optional, additive contract as cursor.js's unavailableReason() — see
154
+ * that file's docstring. Only fires for the one case worth calling out: a VS
155
+ * Code User dir genuinely exists but this Node runtime is too old for
156
+ * node:sqlite, so silently vanishing from "Sources checked" would misread as
157
+ * "VS Code isn't installed," which would be false.
158
+ */
159
+ function unavailableReason() {
160
+ if (!anyVscodeUserDirExists()) return null;
161
+ if (getDatabaseSync()) return null;
162
+ return `Sourcegraph Cody detected but not scanned — ${NODE_SQLITE_REQUIREMENT}`;
163
+ }
164
+
165
+ /**
166
+ * Same defensive symlink-following stat, duplicated from cursor.js's
167
+ * statIfPresent — see that file's docstring for the full reasoning. A path
168
+ * that doesn't exist yields nothing (normal: e.g. no "Code - Insiders" User
169
+ * dir because Insiders was never installed); a dangling symlink is reported
170
+ * `broken: true` rather than silently skipped.
171
+ */
172
+ function* statIfPresent(dbPath) {
173
+ let lst;
174
+ try { lst = fs.lstatSync(dbPath); }
175
+ catch { return; }
176
+
177
+ if (lst.isSymbolicLink()) {
178
+ try {
179
+ const st = fs.statSync(dbPath); // follow the link
180
+ if (!st.isFile()) { yield { file: dbPath, broken: true }; return; }
181
+ yield { file: dbPath, mtimeMs: st.mtimeMs, sizeBytes: st.size, broken: false };
182
+ } catch {
183
+ yield { file: dbPath, broken: true }; // dangling symlink
184
+ }
185
+ return;
186
+ }
187
+
188
+ if (!lst.isFile()) return; // something unexpected sits at this path — out of scope, not broken
189
+ yield { file: dbPath, mtimeMs: lst.mtimeMs, sizeBytes: lst.size, broken: false };
190
+ }
191
+
192
+ /**
193
+ * Yield { file, mtimeMs, sizeBytes, broken } for the default profile's
194
+ * state.vscdb under every VS Code variant this adapter checks (standard +
195
+ * Insiders). Purely a filesystem walk + stat — never opens the database, so
196
+ * it works even in a Node runtime where node:sqlite isn't available (only
197
+ * readLines() actually needs it, same division of labour as cursor.js).
198
+ */
199
+ function* files() {
200
+ for (const userDir of vscodeUserDirs()) {
201
+ yield* statIfPresent(path.join(userDir, "globalStorage", "state.vscdb"));
202
+ }
203
+ }
204
+
205
+ // Not backed by a real Cody state.vscdb row this tool was tested against (no
206
+ // VS Code install to test with) — see the verification-status note above.
207
+ // Generous backstop against a corrupted/pathological file, same rationale as
208
+ // cursor.js's own MAX_DB_BYTES.
209
+ const MAX_DB_BYTES = 512 * 1024 * 1024;
210
+ const READ_TIMEOUT_MS = 60_000;
211
+ const BUSY_TIMEOUT_MS = 5_000;
212
+
213
+ /**
214
+ * Cody's extension id is `sourcegraph.cody-ai` (verified directly against
215
+ * vscode/package.json — see the module docstring). The LIKE clause is a
216
+ * deliberately narrow safety margin, not a guess-widening: it catches a
217
+ * differently-suffixed Sourcegraph Cody extension id (e.g. a possible future
218
+ * `sourcegraph.cody-ai-nightly`-style variant) without sweeping in any other
219
+ * publisher's or extension's state the way reading the whole ItemTable
220
+ * (cursor.js's approach, appropriate there because Cursor owns that entire
221
+ * file) would for a shared, multi-extension file like this one.
222
+ */
223
+ const CODY_KEY_PATTERN = "sourcegraph.cody%";
224
+
225
+ /**
226
+ * Same storage-class handling as cursor.js's valueToText — see that file's
227
+ * docstring. A PROFILE-scope row here is written via
228
+ * `JSON.stringify(state)` (see module docstring), i.e. always a JS string
229
+ * (TEXT storage class) in every real case this adapter's research found, but
230
+ * the BLOB fallback is kept for parity with cursor.js and cheap insurance
231
+ * against a SQLite storage-class surprise.
232
+ */
233
+ function valueToText(value) {
234
+ if (typeof value === "string") return value;
235
+ if (value instanceof Uint8Array) return Buffer.from(value).toString("utf-8");
236
+ return null;
237
+ }
238
+
239
+ /**
240
+ * Read one state.vscdb's Cody-owned row(s) as an array of raw text "lines" —
241
+ * one per matching ItemTable row's decoded value (in practice, at most one:
242
+ * a single row keyed `sourcegraph.cody-ai` holding a JSON object with every
243
+ * globalState key the extension has ever set, `cody-local-chatHistory-v2`
244
+ * included). Returns { lines, status, bytesRead } with the same status
245
+ * vocabulary as every other source in this project.
246
+ *
247
+ * Synchronous node:sqlite, same "no real preemptive timeout possible, so
248
+ * check a wall-clock deadline between statements" approach as cursor.js —
249
+ * see that file's docstring. With at most a handful of matching rows here
250
+ * (unlike cursor.js's tens-of-thousands-of-rows cursorDiskKV table), the
251
+ * per-row yield-to-event-loop machinery is far less likely to matter in
252
+ * practice, but the deadline check is kept for consistency and as insurance
253
+ * against a single pathologically large row's own decode time — the same
254
+ * named, un-bounded asymmetry cursor.js's own docstring admits.
255
+ */
256
+ async function readLines(file) {
257
+ const DB = getDatabaseSync();
258
+ if (!DB) return { lines: [], status: "failed", bytesRead: 0 };
259
+
260
+ let stat;
261
+ try { stat = fs.statSync(file); }
262
+ catch { return { lines: [], status: "failed", bytesRead: 0 }; }
263
+ if (stat.size > MAX_DB_BYTES) return { lines: [], status: "too-large", bytesRead: 0 };
264
+
265
+ let db;
266
+ try {
267
+ db = new DB(file, { readOnly: true });
268
+ db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`);
269
+ } catch {
270
+ // Deleted between files() and this call, a corrupt/non-SQLite file at
271
+ // this path, or VS Code holding a lock this readonly open can't get past
272
+ // within BUSY_TIMEOUT_MS — genuinely "could not read this," not "read it,
273
+ // found nothing." Status "failed" keeps the scan report honest about
274
+ // that difference (CONTRIBUTING.md rule 5).
275
+ return { lines: [], status: "failed", bytesRead: 0 };
276
+ }
277
+
278
+ let rows;
279
+ try {
280
+ rows = db.prepare("SELECT value FROM ItemTable WHERE key LIKE ?").iterate(CODY_KEY_PATTERN);
281
+ } catch {
282
+ // ItemTable itself doesn't exist — this file opened fine as SQLite but
283
+ // didn't match the schema this adapter understands, a real "could not
284
+ // extract anything," not "extracted zero real rows."
285
+ try { db.close(); } catch { /* best-effort */ }
286
+ return { lines: [], status: "failed", bytesRead: 0 };
287
+ }
288
+
289
+ const lines = [];
290
+ let bytesRead = 0;
291
+ const deadline = Date.now() + READ_TIMEOUT_MS;
292
+ let timedOut = false;
293
+ let sawError = false;
294
+ let n = 0;
295
+
296
+ try {
297
+ for (const row of rows) {
298
+ const text = valueToText(row.value);
299
+ // One matching row becomes one scanned "line," using the value exactly
300
+ // as VS Code wrote it — not re-parsed/re-stringified — same reasoning
301
+ // as cursor.js: keeps every byte the regexes depend on intact, and
302
+ // sidesteps needing to track Cody's own nested key name
303
+ // (`cody-local-chatHistory-v2`) as it evolves across versions.
304
+ if (text) { lines.push(text); bytesRead += Buffer.byteLength(text, "utf-8"); }
305
+ n++;
306
+ if (n % 500 === 0) {
307
+ await new Promise((resolve) => setImmediate(resolve));
308
+ if (Date.now() > deadline) { timedOut = true; break; }
309
+ }
310
+ }
311
+ } catch {
312
+ // Whatever WAS read before a mid-iteration failure (e.g. a corrupted
313
+ // page) is real content and may contain a real secret — kept, not
314
+ // discarded, same as claude-code.js/cursor.js.
315
+ sawError = true;
316
+ }
317
+
318
+ try { db.close(); } catch { /* best-effort close */ }
319
+
320
+ if (sawError && lines.length === 0) return { lines: [], status: "failed", bytesRead };
321
+ if (timedOut || sawError) return { lines, status: lines.length > 0 ? "partial" : "failed", bytesRead };
322
+ return { lines, status: "complete", bytesRead };
323
+ }
324
+
325
+ module.exports = { id, label, available, unavailableReason, files, readLines };