auxilo-mcp 0.9.2 → 0.9.3

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.
@@ -0,0 +1,160 @@
1
+ /**
2
+ * scripts/sources/cline.js — Cline (VS Code extension) Transcript Source (UC-3)
3
+ *
4
+ * Discovers and reads Cline task conversation histories from VS Code global
5
+ * storage:
6
+ *
7
+ * <vscode-user-dir>/globalStorage/saoudrizwan.claude-dev/tasks/<taskId>/
8
+ * api_conversation_history.json ← read (Anthropic Messages format)
9
+ * ui_messages.json ← ignored (UI event stream)
10
+ *
11
+ * Candidate VS Code roots probed (any-of): Code, Code - Insiders, VSCodium —
12
+ * per-platform user-data locations (darwin: ~/Library/Application Support,
13
+ * linux: ~/.config, win32: %APPDATA%).
14
+ *
15
+ * KNOWN SHAPE (format probe accepts exactly this, refuses everything else):
16
+ * [ { role: "user"|"assistant", content: string | [ {type:"text", text} | ... ] } ]
17
+ * (tool_use / tool_result blocks and non-text parts are skipped)
18
+ *
19
+ * NOTE (2026-07-19): built from Cline's documented/observed storage layout.
20
+ * No Cline installation existed on the build machine to verify against
21
+ * (probed all globalStorage roots — absent). The strict probe + fail-silent
22
+ * contract means a drifted real-world format degrades to "skipped with one
23
+ * log line", never a mis-parse (UC §5 risk rule) — the same posture the
24
+ * gemini-cli adapter shipped with. Publicly labeled best-effort (UC-3).
25
+ *
26
+ * @module sources/cline
27
+ */
28
+
29
+ 'use strict';
30
+
31
+ const fs = require('fs');
32
+ const path = require('path');
33
+ const os = require('os');
34
+ const { TranscriptSource } = require('./source.interface');
35
+ const { normalizeRole, extractText } = require('./generic-jsonl');
36
+
37
+ /** Per-platform VS Code-family user-data roots (any-of detection). */
38
+ function vscodeUserDirs(homeDir, platform, env) {
39
+ const apps = ['Code', 'Code - Insiders', 'VSCodium'];
40
+ if (platform === 'darwin') {
41
+ return apps.map(a => path.join(homeDir, 'Library', 'Application Support', a, 'User'));
42
+ }
43
+ if (platform === 'win32') {
44
+ const appData = (env && env.APPDATA) || path.join(homeDir, 'AppData', 'Roaming');
45
+ return apps.map(a => path.join(appData, a, 'User'));
46
+ }
47
+ return apps.map(a => path.join(homeDir, '.config', a, 'User'));
48
+ }
49
+
50
+ class ClineSource extends TranscriptSource {
51
+ static id = 'cline';
52
+ static displayName = 'Cline (VS Code)';
53
+ static version = '1.0.0';
54
+
55
+ /** Extension publisher dir(s) under globalStorage — subclass override point. */
56
+ static publisherDirs = ['saoudrizwan.claude-dev'];
57
+
58
+ constructor(config = {}) {
59
+ super(config);
60
+ const homeDir = config.homeDir || os.homedir();
61
+ const platform = config.platform || process.platform;
62
+ const env = config.env || process.env;
63
+ /** @type {string[]} candidate tasks roots (first existing wins per discover) */
64
+ this.taskRoots = config.taskRoots || vscodeUserDirs(homeDir, platform, env).flatMap(
65
+ (userDir) => this.constructor.publisherDirs.map(
66
+ (pub) => path.join(userDir, 'globalStorage', pub, 'tasks')
67
+ )
68
+ );
69
+ }
70
+
71
+ /** Detect: any candidate tasks root exists and is a directory. Fail-silent. */
72
+ async detect() {
73
+ return this.taskRoots.some((p) => {
74
+ try { return fs.statSync(p).isDirectory(); } catch { return false; }
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Discover task sessions: each task dir containing
80
+ * api_conversation_history.json modified after `since`.
81
+ */
82
+ async discoverSessions({ since } = {}) {
83
+ const results = [];
84
+ const sinceMs = since ? new Date(since).getTime() : 0;
85
+
86
+ for (const root of this.taskRoots) {
87
+ let taskIds;
88
+ try { taskIds = fs.readdirSync(root); } catch { continue; }
89
+ for (const taskId of taskIds) {
90
+ const historyPath = path.join(root, taskId, 'api_conversation_history.json');
91
+ try {
92
+ const stat = fs.statSync(historyPath);
93
+ if (!stat.isFile()) continue;
94
+ if (stat.mtimeMs <= sinceMs) continue;
95
+ results.push({
96
+ sessionId: `${this.type}-${taskId}`,
97
+ path: historyPath,
98
+ mtime: stat.mtime.toISOString(),
99
+ bytes: stat.size,
100
+ });
101
+ } catch { /* task dir without a history file — skip */ }
102
+ }
103
+ }
104
+ return results;
105
+ }
106
+
107
+ /**
108
+ * Read + normalize one task history. Returns null (single stderr log line)
109
+ * when the format probe refuses — never throws on bad format.
110
+ */
111
+ async readSession(sessionRef) {
112
+ const filePath = sessionRef.path;
113
+ if (!filePath || !fs.existsSync(filePath)) {
114
+ throw new Error(`Transcript file not found: ${filePath}`);
115
+ }
116
+
117
+ let parsed;
118
+ try {
119
+ parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
120
+ } catch {
121
+ return this._refuse(filePath, 'not valid JSON');
122
+ }
123
+
124
+ // Probe: an ARRAY of {role, content} message objects (Anthropic format).
125
+ if (!Array.isArray(parsed)) return this._refuse(filePath, 'not a messages array');
126
+ const messageLike = parsed.filter((m) => m && typeof m === 'object' && m.role !== undefined && m.content !== undefined);
127
+ if (parsed.length === 0 || messageLike.length === 0) {
128
+ return this._refuse(filePath, 'no role/content messages');
129
+ }
130
+
131
+ const turns = [];
132
+ for (const m of messageLike) {
133
+ const role = normalizeRole(m.role);
134
+ if (!role) continue; // system / tool roles — skip
135
+ const text = extractText(m.content);
136
+ if (text && text.trim().length > 0) turns.push(`[${role}]: ${text}`);
137
+ }
138
+
139
+ return {
140
+ transcript: turns.join('\n\n'),
141
+ metadata: {
142
+ sessionId: sessionRef.sessionId,
143
+ source: this.type,
144
+ mtime: sessionRef.mtime,
145
+ bytes: sessionRef.bytes,
146
+ },
147
+ };
148
+ }
149
+
150
+ /** Single log line + null per the UC §5 fail-silent rule. */
151
+ _refuse(filePath, reason) {
152
+ process.stderr.write(`[${this.type}] format probe refused ${filePath} (${reason}) — skipping\n`);
153
+ return null;
154
+ }
155
+
156
+ /** No hook surface — poll-based via the sweeper (UC-3 P-class). */
157
+ async registerSessionEndHook(cb) { return null; }
158
+ }
159
+
160
+ module.exports = { ClineSource, vscodeUserDirs };
@@ -0,0 +1,140 @@
1
+ /**
2
+ * scripts/sources/continue.js — Continue.dev Transcript Source (UC-3)
3
+ *
4
+ * Discovers and reads Continue.dev saved sessions:
5
+ *
6
+ * ~/.continue/sessions/<sessionId>.json ← read (one file per session)
7
+ * ~/.continue/sessions/sessions.json ← index (metadata only; ignored)
8
+ *
9
+ * KNOWN SHAPES (format probe accepts exactly these, refuses everything else):
10
+ *
11
+ * { history: [ ...items ] , title?, sessionId?, workspaceDirectory? }
12
+ * where each item is either
13
+ * { message: { role: "user"|"assistant", content: string|[{...}] }, ... }
14
+ * or (older sessions)
15
+ * { role: "user"|"assistant", content: string|[{...}] }
16
+ *
17
+ * Tool/system roles and non-text content parts are skipped.
18
+ *
19
+ * NOTE (2026-07-19): built from Continue's documented session persistence
20
+ * (~/.continue/sessions). No Continue installation existed on the build
21
+ * machine to verify against (~/.continue absent). Strict probe + fail-silent
22
+ * degradation per UC §5 — a drifted format is skipped with one log line,
23
+ * never mis-parsed. Publicly labeled best-effort (UC-3).
24
+ *
25
+ * @module sources/continue
26
+ */
27
+
28
+ 'use strict';
29
+
30
+ const fs = require('fs');
31
+ const path = require('path');
32
+ const os = require('os');
33
+ const { TranscriptSource } = require('./source.interface');
34
+ const { normalizeRole, extractText } = require('./generic-jsonl');
35
+
36
+ class ContinueSource extends TranscriptSource {
37
+ static id = 'continue';
38
+ static displayName = 'Continue.dev';
39
+ static version = '1.0.0';
40
+
41
+ constructor(config = {}) {
42
+ super(config);
43
+ /** @type {string} Sessions dir */
44
+ this.sessionsDir = config.sessionsDir ||
45
+ path.join(config.homeDir || os.homedir(), '.continue', 'sessions');
46
+ }
47
+
48
+ /** Detect: ~/.continue/sessions exists and is a directory. Fail-silent. */
49
+ async detect() {
50
+ try {
51
+ return fs.statSync(this.sessionsDir).isDirectory();
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+
57
+ /** Discover session JSON files (excluding the sessions.json index). */
58
+ async discoverSessions({ since } = {}) {
59
+ const results = [];
60
+ const sinceMs = since ? new Date(since).getTime() : 0;
61
+
62
+ let files;
63
+ try {
64
+ files = fs.readdirSync(this.sessionsDir)
65
+ .filter(f => f.endsWith('.json') && f !== 'sessions.json');
66
+ } catch {
67
+ return results;
68
+ }
69
+
70
+ for (const file of files) {
71
+ const filePath = path.join(this.sessionsDir, file);
72
+ try {
73
+ const stat = fs.statSync(filePath);
74
+ if (!stat.isFile()) continue;
75
+ if (stat.mtimeMs <= sinceMs) continue;
76
+ results.push({
77
+ sessionId: path.basename(file, '.json'),
78
+ path: filePath,
79
+ mtime: stat.mtime.toISOString(),
80
+ bytes: stat.size,
81
+ });
82
+ } catch { /* skip unreadable files */ }
83
+ }
84
+ return results;
85
+ }
86
+
87
+ /**
88
+ * Read + normalize one session. Returns null (single stderr log line) when
89
+ * the format probe refuses — never throws on bad format.
90
+ */
91
+ async readSession(sessionRef) {
92
+ const filePath = sessionRef.path;
93
+ if (!filePath || !fs.existsSync(filePath)) {
94
+ throw new Error(`Transcript file not found: ${filePath}`);
95
+ }
96
+
97
+ let parsed;
98
+ try {
99
+ parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
100
+ } catch {
101
+ return this._refuse(filePath, 'not valid JSON');
102
+ }
103
+
104
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || !Array.isArray(parsed.history)) {
105
+ return this._refuse(filePath, 'no history array');
106
+ }
107
+
108
+ const turns = [];
109
+ for (const item of parsed.history) {
110
+ if (!item || typeof item !== 'object') continue;
111
+ // New shape nests the message; older items ARE the message.
112
+ const msg = (item.message && typeof item.message === 'object') ? item.message : item;
113
+ const role = normalizeRole(msg.role);
114
+ if (!role) continue;
115
+ const text = extractText(msg.content);
116
+ if (text && text.trim().length > 0) turns.push(`[${role}]: ${text}`);
117
+ }
118
+
119
+ return {
120
+ transcript: turns.join('\n\n'),
121
+ metadata: {
122
+ sessionId: sessionRef.sessionId,
123
+ source: 'continue',
124
+ mtime: sessionRef.mtime,
125
+ bytes: sessionRef.bytes,
126
+ },
127
+ };
128
+ }
129
+
130
+ /** Single log line + null per the UC §5 fail-silent rule. */
131
+ _refuse(filePath, reason) {
132
+ process.stderr.write(`[continue] format probe refused ${filePath} (${reason}) — skipping\n`);
133
+ return null;
134
+ }
135
+
136
+ /** No hook surface — poll-based via the sweeper (UC-3 P-class). */
137
+ async registerSessionEndHook(cb) { return null; }
138
+ }
139
+
140
+ module.exports = { ContinueSource };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * scripts/sources/roo-code.js — Roo Code (VS Code extension) Transcript Source (UC-3)
3
+ *
4
+ * Roo Code is a maintained fork of Cline and preserves its storage layout:
5
+ *
6
+ * <vscode-user-dir>/globalStorage/<publisher>/tasks/<taskId>/
7
+ * api_conversation_history.json
8
+ *
9
+ * Publisher dirs probed (the extension id changed across releases):
10
+ * rooveterinaryinc.roo-cline (original "Roo Cline" id, still used)
11
+ * rooveterinaryinc.roo-code (post-rename id)
12
+ *
13
+ * Everything else — discovery, the Anthropic-messages format probe,
14
+ * normalization, fail-silent refusal — is inherited from ClineSource.
15
+ *
16
+ * NOTE (2026-07-19): like cline.js, built from the documented fork layout;
17
+ * no Roo Code installation existed on the build machine to verify against.
18
+ * Strict probe + fail-silent (UC §5). Publicly labeled best-effort (UC-3).
19
+ *
20
+ * @module sources/roo-code
21
+ */
22
+
23
+ 'use strict';
24
+
25
+ const { ClineSource } = require('./cline');
26
+
27
+ class RooCodeSource extends ClineSource {
28
+ static id = 'roo-code';
29
+ static displayName = 'Roo Code (VS Code)';
30
+ static version = '1.0.0';
31
+
32
+ static publisherDirs = ['rooveterinaryinc.roo-cline', 'rooveterinaryinc.roo-code'];
33
+ }
34
+
35
+ module.exports = { RooCodeSource };