thoth-agents 0.3.8 → 0.3.10
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +27 -0
- package/dist/agents/prompt-dialects.d.ts +3 -0
- package/dist/{chunk-3WNTCPVK.js → chunk-JBG7XMIG.js} +1291 -290
- package/dist/{chunk-F2K4CS5J.js → chunk-M2WF46BP.js} +186 -128
- package/dist/{chunk-6GQYMWWT.js → chunk-TKIDBRM3.js} +1 -1
- package/dist/cli/commands.d.ts +1 -0
- package/dist/cli/config-io.d.ts +2 -1
- package/dist/cli/index.js +126 -428
- package/dist/cli/install-completion.d.ts +34 -0
- package/dist/cli/install-ledger.d.ts +44 -0
- package/dist/cli/install.d.ts +9 -1
- package/dist/cli/operations/claude-code.d.ts +12 -1
- package/dist/cli/operations/codex.d.ts +16 -0
- package/dist/cli/operations/opencode.d.ts +20 -3
- package/dist/cli/operations/types.d.ts +13 -0
- package/dist/cli/package-version.d.ts +19 -0
- package/dist/cli/tui/index.js +63 -24
- package/dist/harness/core/sdd.d.ts +1 -1
- package/dist/hooks/auto-update-checker/checker.d.ts +0 -5
- package/dist/hooks/auto-update-checker/index.d.ts +1 -2
- package/dist/hooks/auto-update-checker/types.d.ts +0 -1
- package/dist/index.js +98 -251
- package/package.json +1 -1
- package/plugin/.claude-plugin/.thoth-agents-plugin-assets.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/agents/orchestrator.md +17 -17
- package/plugin/skills/thoth-archive/SKILL.md +7 -0
- package/plugin/skills/thoth-archive/scripts/archive.mjs +28 -74
- package/plugin/skills/thoth-sdd/references/phases/specify.md +12 -1
- package/plugin/skills/thoth-sdd/scripts/durable-deltas.mjs +158 -0
- package/plugin/skills/thoth-sdd/scripts/validate.mjs +67 -28
- package/plugin/skills/thoth-sdd/templates/spec.md +9 -1
- package/skills/thoth-archive/SKILL.md +7 -0
- package/skills/thoth-archive/scripts/archive.mjs +28 -74
- package/skills/thoth-sdd/references/phases/specify.md +12 -1
- package/skills/thoth-sdd/scripts/durable-deltas.mjs +158 -0
- package/skills/thoth-sdd/scripts/validate.mjs +67 -28
- package/skills/thoth-sdd/templates/spec.md +9 -1
- package/dist/hooks/auto-update-checker/cache.d.ts +0 -7
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
renderOpenCodeAgentConfigs,
|
|
4
4
|
spawn,
|
|
5
5
|
spawnSync
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TKIDBRM3.js";
|
|
7
7
|
import {
|
|
8
8
|
POLL_INTERVAL_BACKGROUND_MS,
|
|
9
9
|
context7,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
grep_app,
|
|
13
13
|
loadPluginConfig,
|
|
14
14
|
stripJsonComments
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-M2WF46BP.js";
|
|
16
16
|
|
|
17
17
|
// src/index.ts
|
|
18
18
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -61,24 +61,25 @@ import { createRequire } from "module";
|
|
|
61
61
|
var require2 = createRequire(import.meta.url);
|
|
62
62
|
function getLogFile() {
|
|
63
63
|
const os2 = require2("node:os");
|
|
64
|
-
const
|
|
64
|
+
const path3 = require2("node:path");
|
|
65
65
|
const configuredPath = process.env.THOTH_AGENTS_LOG_FILE?.trim();
|
|
66
|
-
return configuredPath ||
|
|
66
|
+
return configuredPath || path3.join(os2.tmpdir(), "thoth-agents.log");
|
|
67
67
|
}
|
|
68
68
|
function log(message, data) {
|
|
69
69
|
try {
|
|
70
|
-
const
|
|
70
|
+
const fs2 = require2("node:fs");
|
|
71
71
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
72
72
|
const logEntry = `[${timestamp}] ${message} ${data ? JSON.stringify(data) : ""}
|
|
73
73
|
`;
|
|
74
|
-
|
|
74
|
+
fs2.appendFileSync(getLogFile(), logEntry);
|
|
75
75
|
} catch {
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// src/hooks/auto-update-checker/
|
|
79
|
+
// src/hooks/auto-update-checker/checker.ts
|
|
80
80
|
import * as fs from "fs";
|
|
81
81
|
import * as path2 from "path";
|
|
82
|
+
import { fileURLToPath } from "url";
|
|
82
83
|
|
|
83
84
|
// src/hooks/auto-update-checker/constants.ts
|
|
84
85
|
import * as os from "os";
|
|
@@ -103,54 +104,7 @@ var configPaths = getOpenCodeConfigPaths();
|
|
|
103
104
|
var USER_OPENCODE_CONFIG = configPaths[0];
|
|
104
105
|
var USER_OPENCODE_CONFIG_JSONC = configPaths[1];
|
|
105
106
|
|
|
106
|
-
// src/hooks/auto-update-checker/cache.ts
|
|
107
|
-
function invalidatePnpmPackageCache(packageName = PACKAGE_NAME) {
|
|
108
|
-
try {
|
|
109
|
-
const pkgDir = path2.join(CACHE_DIR, "node_modules", packageName);
|
|
110
|
-
const pkgJsonPath = path2.join(CACHE_DIR, "package.json");
|
|
111
|
-
let packageRemoved = false;
|
|
112
|
-
let dependencyRemoved = false;
|
|
113
|
-
if (fs.existsSync(pkgDir)) {
|
|
114
|
-
fs.rmSync(pkgDir, { recursive: true, force: true });
|
|
115
|
-
log(`[auto-update-checker] Package removed: ${pkgDir}`);
|
|
116
|
-
packageRemoved = true;
|
|
117
|
-
}
|
|
118
|
-
if (fs.existsSync(pkgJsonPath)) {
|
|
119
|
-
try {
|
|
120
|
-
const content = fs.readFileSync(pkgJsonPath, "utf-8");
|
|
121
|
-
const pkgJson = JSON.parse(stripJsonComments(content));
|
|
122
|
-
if (pkgJson.dependencies?.[packageName]) {
|
|
123
|
-
delete pkgJson.dependencies[packageName];
|
|
124
|
-
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
125
|
-
log(
|
|
126
|
-
`[auto-update-checker] Dependency removed from package.json: ${packageName}`
|
|
127
|
-
);
|
|
128
|
-
dependencyRemoved = true;
|
|
129
|
-
}
|
|
130
|
-
} catch (err) {
|
|
131
|
-
log(
|
|
132
|
-
`[auto-update-checker] Failed to update package.json for invalidation:`,
|
|
133
|
-
err
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (!packageRemoved && !dependencyRemoved) {
|
|
138
|
-
log(
|
|
139
|
-
`[auto-update-checker] Package not found, nothing to invalidate: ${packageName}`
|
|
140
|
-
);
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
return true;
|
|
144
|
-
} catch (err) {
|
|
145
|
-
log("[auto-update-checker] Failed to invalidate package:", err);
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
107
|
// src/hooks/auto-update-checker/checker.ts
|
|
151
|
-
import * as fs2 from "fs";
|
|
152
|
-
import * as path3 from "path";
|
|
153
|
-
import { fileURLToPath } from "url";
|
|
154
108
|
function isPrereleaseVersion(version) {
|
|
155
109
|
return version.includes("-");
|
|
156
110
|
}
|
|
@@ -171,8 +125,8 @@ function extractChannel(version) {
|
|
|
171
125
|
}
|
|
172
126
|
function getConfigPaths(directory) {
|
|
173
127
|
return [
|
|
174
|
-
|
|
175
|
-
|
|
128
|
+
path2.join(directory, ".opencode", "opencode.json"),
|
|
129
|
+
path2.join(directory, ".opencode", "opencode.jsonc"),
|
|
176
130
|
USER_OPENCODE_CONFIG,
|
|
177
131
|
USER_OPENCODE_CONFIG_JSONC
|
|
178
132
|
];
|
|
@@ -180,8 +134,8 @@ function getConfigPaths(directory) {
|
|
|
180
134
|
function getLocalDevPath(directory) {
|
|
181
135
|
for (const configPath of getConfigPaths(directory)) {
|
|
182
136
|
try {
|
|
183
|
-
if (!
|
|
184
|
-
const content =
|
|
137
|
+
if (!fs.existsSync(configPath)) continue;
|
|
138
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
185
139
|
const config = JSON.parse(stripJsonComments(content));
|
|
186
140
|
const plugins = config.plugin ?? [];
|
|
187
141
|
for (const entry of plugins) {
|
|
@@ -200,19 +154,19 @@ function getLocalDevPath(directory) {
|
|
|
200
154
|
}
|
|
201
155
|
function findPackageJsonUp(startPath) {
|
|
202
156
|
try {
|
|
203
|
-
const stat =
|
|
204
|
-
let dir = stat.isDirectory() ? startPath :
|
|
157
|
+
const stat = fs.statSync(startPath);
|
|
158
|
+
let dir = stat.isDirectory() ? startPath : path2.dirname(startPath);
|
|
205
159
|
for (let i = 0; i < 10; i++) {
|
|
206
|
-
const pkgPath =
|
|
207
|
-
if (
|
|
160
|
+
const pkgPath = path2.join(dir, "package.json");
|
|
161
|
+
if (fs.existsSync(pkgPath)) {
|
|
208
162
|
try {
|
|
209
|
-
const content =
|
|
163
|
+
const content = fs.readFileSync(pkgPath, "utf-8");
|
|
210
164
|
const pkg = JSON.parse(content);
|
|
211
165
|
if (pkg.name === PACKAGE_NAME) return pkgPath;
|
|
212
166
|
} catch {
|
|
213
167
|
}
|
|
214
168
|
}
|
|
215
|
-
const parent =
|
|
169
|
+
const parent = path2.dirname(dir);
|
|
216
170
|
if (parent === dir) break;
|
|
217
171
|
dir = parent;
|
|
218
172
|
}
|
|
@@ -226,7 +180,7 @@ function getLocalDevVersion(directory) {
|
|
|
226
180
|
try {
|
|
227
181
|
const pkgPath = findPackageJsonUp(localPath);
|
|
228
182
|
if (!pkgPath) return null;
|
|
229
|
-
const content =
|
|
183
|
+
const content = fs.readFileSync(pkgPath, "utf-8");
|
|
230
184
|
const pkg = JSON.parse(content);
|
|
231
185
|
return pkg.version ?? null;
|
|
232
186
|
} catch {
|
|
@@ -236,8 +190,8 @@ function getLocalDevVersion(directory) {
|
|
|
236
190
|
function findPluginEntry(directory) {
|
|
237
191
|
for (const configPath of getConfigPaths(directory)) {
|
|
238
192
|
try {
|
|
239
|
-
if (!
|
|
240
|
-
const content =
|
|
193
|
+
if (!fs.existsSync(configPath)) continue;
|
|
194
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
241
195
|
const config = JSON.parse(stripJsonComments(content));
|
|
242
196
|
const plugins = config.plugin ?? [];
|
|
243
197
|
for (const entry of plugins) {
|
|
@@ -264,8 +218,8 @@ var cachedPackageVersion = null;
|
|
|
264
218
|
function getCachedVersion() {
|
|
265
219
|
if (cachedPackageVersion) return cachedPackageVersion;
|
|
266
220
|
try {
|
|
267
|
-
if (
|
|
268
|
-
const content =
|
|
221
|
+
if (fs.existsSync(INSTALLED_PACKAGE_JSON)) {
|
|
222
|
+
const content = fs.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
|
|
269
223
|
const pkg = JSON.parse(content);
|
|
270
224
|
if (pkg.version) {
|
|
271
225
|
cachedPackageVersion = pkg.version;
|
|
@@ -275,10 +229,10 @@ function getCachedVersion() {
|
|
|
275
229
|
} catch {
|
|
276
230
|
}
|
|
277
231
|
try {
|
|
278
|
-
const currentDir =
|
|
232
|
+
const currentDir = path2.dirname(fileURLToPath(import.meta.url));
|
|
279
233
|
const pkgPath = findPackageJsonUp(currentDir);
|
|
280
234
|
if (pkgPath) {
|
|
281
|
-
const content =
|
|
235
|
+
const content = fs.readFileSync(pkgPath, "utf-8");
|
|
282
236
|
const pkg = JSON.parse(content);
|
|
283
237
|
if (pkg.version) {
|
|
284
238
|
cachedPackageVersion = pkg.version;
|
|
@@ -293,36 +247,6 @@ function getCachedVersion() {
|
|
|
293
247
|
}
|
|
294
248
|
return null;
|
|
295
249
|
}
|
|
296
|
-
function updatePinnedVersion(configPath, oldEntry, newVersion) {
|
|
297
|
-
try {
|
|
298
|
-
if (!fs2.existsSync(configPath)) return false;
|
|
299
|
-
const content = fs2.readFileSync(configPath, "utf-8");
|
|
300
|
-
const newEntry = `${PACKAGE_NAME}@${newVersion}`;
|
|
301
|
-
const escapedOldEntry = oldEntry.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
302
|
-
const entryRegex = new RegExp(`(["'])${escapedOldEntry}\\1`, "g");
|
|
303
|
-
if (!entryRegex.test(content)) {
|
|
304
|
-
log(
|
|
305
|
-
`[auto-update-checker] Entry "${oldEntry}" not found in ${configPath}`
|
|
306
|
-
);
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
const updatedContent = content.replace(entryRegex, `$1${newEntry}$1`);
|
|
310
|
-
if (updatedContent === content) {
|
|
311
|
-
return false;
|
|
312
|
-
}
|
|
313
|
-
fs2.writeFileSync(configPath, updatedContent, "utf-8");
|
|
314
|
-
log(
|
|
315
|
-
`[auto-update-checker] Updated ${configPath}: ${oldEntry} \u2192 ${newEntry}`
|
|
316
|
-
);
|
|
317
|
-
return true;
|
|
318
|
-
} catch (err) {
|
|
319
|
-
log(
|
|
320
|
-
`[auto-update-checker] Failed to update config file ${configPath}:`,
|
|
321
|
-
err
|
|
322
|
-
);
|
|
323
|
-
return false;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
250
|
async function getLatestVersion(channel = "latest") {
|
|
327
251
|
const controller = new AbortController();
|
|
328
252
|
const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
|
|
@@ -342,8 +266,8 @@ async function getLatestVersion(channel = "latest") {
|
|
|
342
266
|
}
|
|
343
267
|
|
|
344
268
|
// src/hooks/auto-update-checker/index.ts
|
|
345
|
-
function createAutoUpdateCheckerHook(ctx, options = {}
|
|
346
|
-
const { showStartupToast = true
|
|
269
|
+
function createAutoUpdateCheckerHook(ctx, options = {}) {
|
|
270
|
+
const { showStartupToast = true } = options;
|
|
347
271
|
let hasChecked = false;
|
|
348
272
|
return {
|
|
349
273
|
event: ({ event }) => {
|
|
@@ -376,14 +300,14 @@ function createAutoUpdateCheckerHook(ctx, options = {}, shell) {
|
|
|
376
300
|
"info"
|
|
377
301
|
);
|
|
378
302
|
}
|
|
379
|
-
runBackgroundUpdateCheck(ctx
|
|
303
|
+
runBackgroundUpdateCheck(ctx).catch((err) => {
|
|
380
304
|
log("[auto-update-checker] Background update check failed:", err);
|
|
381
305
|
});
|
|
382
306
|
}, 0);
|
|
383
307
|
}
|
|
384
308
|
};
|
|
385
309
|
}
|
|
386
|
-
async function runBackgroundUpdateCheck(ctx
|
|
310
|
+
async function runBackgroundUpdateCheck(ctx) {
|
|
387
311
|
const pluginInfo = findPluginEntry(ctx.directory);
|
|
388
312
|
if (!pluginInfo) {
|
|
389
313
|
log("[auto-update-checker] Plugin not found in config");
|
|
@@ -414,86 +338,14 @@ async function runBackgroundUpdateCheck(ctx, shell, autoUpdate) {
|
|
|
414
338
|
log(
|
|
415
339
|
`[auto-update-checker] Update available (${channel}): ${currentVersion} \u2192 ${latestVersion}`
|
|
416
340
|
);
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
log("[auto-update-checker] Auto-update disabled, notification only");
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
if (pluginInfo.isPinned) {
|
|
429
|
-
const updated = updatePinnedVersion(
|
|
430
|
-
pluginInfo.configPath,
|
|
431
|
-
pluginInfo.entry,
|
|
432
|
-
latestVersion
|
|
433
|
-
);
|
|
434
|
-
if (!updated) {
|
|
435
|
-
showToast(
|
|
436
|
-
ctx,
|
|
437
|
-
`thoth-agents ${latestVersion}`,
|
|
438
|
-
`v${latestVersion} available. Restart to apply.`,
|
|
439
|
-
"info",
|
|
440
|
-
8e3
|
|
441
|
-
);
|
|
442
|
-
log("[auto-update-checker] Failed to update pinned version in config");
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
log(
|
|
446
|
-
`[auto-update-checker] Config updated: ${pluginInfo.entry} \u2192 ${PACKAGE_NAME}@${latestVersion}`
|
|
447
|
-
);
|
|
448
|
-
}
|
|
449
|
-
invalidatePnpmPackageCache(PACKAGE_NAME);
|
|
450
|
-
const installSuccess = await runPnpmInstallSafe(ctx, shell);
|
|
451
|
-
if (installSuccess) {
|
|
452
|
-
showToast(
|
|
453
|
-
ctx,
|
|
454
|
-
"thoth-agents Updated!",
|
|
455
|
-
`v${currentVersion} \u2192 v${latestVersion}
|
|
456
|
-
Restart OpenCode to apply.`,
|
|
457
|
-
"success",
|
|
458
|
-
8e3
|
|
459
|
-
);
|
|
460
|
-
log(
|
|
461
|
-
`[auto-update-checker] Update installed: ${currentVersion} \u2192 ${latestVersion}`
|
|
462
|
-
);
|
|
463
|
-
} else {
|
|
464
|
-
showToast(
|
|
465
|
-
ctx,
|
|
466
|
-
`thoth-agents ${latestVersion}`,
|
|
467
|
-
`v${latestVersion} available. Restart to apply.`,
|
|
468
|
-
"info",
|
|
469
|
-
8e3
|
|
470
|
-
);
|
|
471
|
-
log("[auto-update-checker] pnpm install failed; update not installed");
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
async function runPnpmInstallSafe(ctx, shell) {
|
|
475
|
-
try {
|
|
476
|
-
const timeoutPromise = new Promise(
|
|
477
|
-
(resolve4) => setTimeout(() => resolve4("timeout"), 6e4)
|
|
478
|
-
);
|
|
479
|
-
const installPromise = (async () => {
|
|
480
|
-
try {
|
|
481
|
-
await shell`cd ${ctx.directory} && pnpm install`;
|
|
482
|
-
return "completed";
|
|
483
|
-
} catch {
|
|
484
|
-
return "failed";
|
|
485
|
-
}
|
|
486
|
-
})();
|
|
487
|
-
const result = await Promise.race([installPromise, timeoutPromise]);
|
|
488
|
-
if (result === "timeout") {
|
|
489
|
-
log("[auto-update-checker] pnpm install timed out after 60 seconds");
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
return result === "completed";
|
|
493
|
-
} catch (err) {
|
|
494
|
-
log("[auto-update-checker] pnpm install error:", err);
|
|
495
|
-
return false;
|
|
496
|
-
}
|
|
341
|
+
showToast(
|
|
342
|
+
ctx,
|
|
343
|
+
`thoth-agents ${latestVersion} available`,
|
|
344
|
+
`v${currentVersion} \u2192 v${latestVersion}. Run npx thoth-agents@latest install --agent=opencode or use interactive CLI Update.`,
|
|
345
|
+
"info",
|
|
346
|
+
1e4
|
|
347
|
+
);
|
|
348
|
+
log("[auto-update-checker] Notification only; explicit CLI update required");
|
|
497
349
|
}
|
|
498
350
|
function showToast(ctx, title, message, variant = "info", duration = 3e3) {
|
|
499
351
|
ctx.client.tui.showToast({
|
|
@@ -925,19 +777,19 @@ function createBuiltinMcps(disabledMcps = []) {
|
|
|
925
777
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
926
778
|
|
|
927
779
|
// src/tools/ast-grep/cli.ts
|
|
928
|
-
import { existsSync as
|
|
780
|
+
import { existsSync as existsSync5 } from "fs";
|
|
929
781
|
|
|
930
782
|
// src/tools/ast-grep/constants.ts
|
|
931
783
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
932
|
-
import { existsSync as
|
|
784
|
+
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
933
785
|
import { createRequire as createRequire3 } from "module";
|
|
934
|
-
import { dirname as dirname2, join as
|
|
786
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
935
787
|
|
|
936
788
|
// src/tools/ast-grep/downloader.ts
|
|
937
|
-
import { chmodSync, existsSync as
|
|
789
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync, unlinkSync } from "fs";
|
|
938
790
|
import { createRequire as createRequire2 } from "module";
|
|
939
791
|
import { homedir as homedir2 } from "os";
|
|
940
|
-
import { join as
|
|
792
|
+
import { join as join4 } from "path";
|
|
941
793
|
|
|
942
794
|
// src/utils/tmux.ts
|
|
943
795
|
var tmuxPath = null;
|
|
@@ -991,22 +843,22 @@ async function findTmuxPath() {
|
|
|
991
843
|
return null;
|
|
992
844
|
}
|
|
993
845
|
const stdout = await new Response(proc.stdout).text();
|
|
994
|
-
const
|
|
995
|
-
if (!
|
|
846
|
+
const path3 = stdout.trim().split("\n")[0];
|
|
847
|
+
if (!path3) {
|
|
996
848
|
log("[tmux] findTmuxPath: no path in output");
|
|
997
849
|
return null;
|
|
998
850
|
}
|
|
999
|
-
const verifyProc = spawn([
|
|
851
|
+
const verifyProc = spawn([path3, "-V"], {
|
|
1000
852
|
stdout: "pipe",
|
|
1001
853
|
stderr: "pipe"
|
|
1002
854
|
});
|
|
1003
855
|
const verifyExit = await verifyProc.exited;
|
|
1004
856
|
if (verifyExit !== 0) {
|
|
1005
|
-
log("[tmux] findTmuxPath: tmux -V failed", { path:
|
|
857
|
+
log("[tmux] findTmuxPath: tmux -V failed", { path: path3, verifyExit });
|
|
1006
858
|
return null;
|
|
1007
859
|
}
|
|
1008
|
-
log("[tmux] findTmuxPath: found tmux", { path:
|
|
1009
|
-
return
|
|
860
|
+
log("[tmux] findTmuxPath: found tmux", { path: path3 });
|
|
861
|
+
return path3;
|
|
1010
862
|
} catch (err) {
|
|
1011
863
|
log("[tmux] findTmuxPath: exception", { error: String(err) });
|
|
1012
864
|
return null;
|
|
@@ -1206,8 +1058,8 @@ function isPwshAvailable() {
|
|
|
1206
1058
|
});
|
|
1207
1059
|
return result.exitCode === 0;
|
|
1208
1060
|
}
|
|
1209
|
-
function escapePowerShellPath(
|
|
1210
|
-
return
|
|
1061
|
+
function escapePowerShellPath(path3) {
|
|
1062
|
+
return path3.replace(/'/g, "''");
|
|
1211
1063
|
}
|
|
1212
1064
|
function getWindowsZipExtractor() {
|
|
1213
1065
|
const buildNumber = getWindowsBuildNumber();
|
|
@@ -1303,19 +1155,19 @@ var PLATFORM_MAP = {
|
|
|
1303
1155
|
function getCacheDir2() {
|
|
1304
1156
|
if (process.platform === "win32") {
|
|
1305
1157
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
1306
|
-
const base2 = localAppData ||
|
|
1307
|
-
return
|
|
1158
|
+
const base2 = localAppData || join4(homedir2(), "AppData", "Local");
|
|
1159
|
+
return join4(base2, "thoth-agents", "bin");
|
|
1308
1160
|
}
|
|
1309
1161
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
1310
|
-
const base = xdgCache ||
|
|
1311
|
-
return
|
|
1162
|
+
const base = xdgCache || join4(homedir2(), ".cache");
|
|
1163
|
+
return join4(base, "thoth-agents", "bin");
|
|
1312
1164
|
}
|
|
1313
1165
|
function getBinaryName() {
|
|
1314
1166
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
1315
1167
|
}
|
|
1316
1168
|
function getCachedBinaryPath() {
|
|
1317
|
-
const binaryPath =
|
|
1318
|
-
return
|
|
1169
|
+
const binaryPath = join4(getCacheDir2(), getBinaryName());
|
|
1170
|
+
return existsSync3(binaryPath) ? binaryPath : null;
|
|
1319
1171
|
}
|
|
1320
1172
|
async function downloadAstGrep(version = DEFAULT_VERSION) {
|
|
1321
1173
|
const platformKey = `${process.platform}-${process.arch}`;
|
|
@@ -1328,8 +1180,8 @@ async function downloadAstGrep(version = DEFAULT_VERSION) {
|
|
|
1328
1180
|
}
|
|
1329
1181
|
const cacheDir = getCacheDir2();
|
|
1330
1182
|
const binaryName = getBinaryName();
|
|
1331
|
-
const binaryPath =
|
|
1332
|
-
if (
|
|
1183
|
+
const binaryPath = join4(cacheDir, binaryName);
|
|
1184
|
+
if (existsSync3(binaryPath)) {
|
|
1333
1185
|
return binaryPath;
|
|
1334
1186
|
}
|
|
1335
1187
|
const { arch, os: os2 } = platformInfo;
|
|
@@ -1337,21 +1189,21 @@ async function downloadAstGrep(version = DEFAULT_VERSION) {
|
|
|
1337
1189
|
const downloadUrl = `https://github.com/${REPO}/releases/download/${version}/${assetName}`;
|
|
1338
1190
|
console.log(`[thoth-agents] Downloading ast-grep binary...`);
|
|
1339
1191
|
try {
|
|
1340
|
-
if (!
|
|
1192
|
+
if (!existsSync3(cacheDir)) {
|
|
1341
1193
|
mkdirSync(cacheDir, { recursive: true });
|
|
1342
1194
|
}
|
|
1343
1195
|
const response = await fetch(downloadUrl, { redirect: "follow" });
|
|
1344
1196
|
if (!response.ok) {
|
|
1345
1197
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1346
1198
|
}
|
|
1347
|
-
const archivePath =
|
|
1199
|
+
const archivePath = join4(cacheDir, assetName);
|
|
1348
1200
|
const arrayBuffer = await response.arrayBuffer();
|
|
1349
1201
|
await writeTextFile(archivePath, arrayBuffer);
|
|
1350
1202
|
await extractZip(archivePath, cacheDir);
|
|
1351
|
-
if (
|
|
1203
|
+
if (existsSync3(archivePath)) {
|
|
1352
1204
|
unlinkSync(archivePath);
|
|
1353
1205
|
}
|
|
1354
|
-
if (process.platform !== "win32" &&
|
|
1206
|
+
if (process.platform !== "win32" && existsSync3(binaryPath)) {
|
|
1355
1207
|
chmodSync(binaryPath, 493);
|
|
1356
1208
|
}
|
|
1357
1209
|
console.log(`[thoth-agents] ast-grep binary ready.`);
|
|
@@ -1435,8 +1287,8 @@ function findSgCliPathSync() {
|
|
|
1435
1287
|
const require3 = createRequire3(import.meta.url);
|
|
1436
1288
|
const cliPkgPath = require3.resolve("@ast-grep/cli/package.json");
|
|
1437
1289
|
const cliDir = dirname2(cliPkgPath);
|
|
1438
|
-
const sgPath =
|
|
1439
|
-
if (
|
|
1290
|
+
const sgPath = join5(cliDir, binaryName);
|
|
1291
|
+
if (existsSync4(sgPath) && isValidBinary(sgPath)) {
|
|
1440
1292
|
return sgPath;
|
|
1441
1293
|
}
|
|
1442
1294
|
} catch {
|
|
@@ -1448,8 +1300,8 @@ function findSgCliPathSync() {
|
|
|
1448
1300
|
const pkgPath = require3.resolve(`${platformPkg}/package.json`);
|
|
1449
1301
|
const pkgDir = dirname2(pkgPath);
|
|
1450
1302
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
1451
|
-
const binaryPath =
|
|
1452
|
-
if (
|
|
1303
|
+
const binaryPath = join5(pkgDir, astGrepName);
|
|
1304
|
+
if (existsSync4(binaryPath) && isValidBinary(binaryPath)) {
|
|
1453
1305
|
return binaryPath;
|
|
1454
1306
|
}
|
|
1455
1307
|
} catch {
|
|
@@ -1457,9 +1309,9 @@ function findSgCliPathSync() {
|
|
|
1457
1309
|
}
|
|
1458
1310
|
if (process.platform === "darwin") {
|
|
1459
1311
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
1460
|
-
for (const
|
|
1461
|
-
if (
|
|
1462
|
-
return
|
|
1312
|
+
for (const path3 of homebrewPaths) {
|
|
1313
|
+
if (existsSync4(path3) && isValidBinary(path3)) {
|
|
1314
|
+
return path3;
|
|
1463
1315
|
}
|
|
1464
1316
|
}
|
|
1465
1317
|
}
|
|
@@ -1476,8 +1328,8 @@ function getSgCliPath() {
|
|
|
1476
1328
|
}
|
|
1477
1329
|
return "sg";
|
|
1478
1330
|
}
|
|
1479
|
-
function setSgCliPath(
|
|
1480
|
-
resolvedCliPath =
|
|
1331
|
+
function setSgCliPath(path3) {
|
|
1332
|
+
resolvedCliPath = path3;
|
|
1481
1333
|
}
|
|
1482
1334
|
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
1483
1335
|
var DEFAULT_MAX_OUTPUT_BYTES = 1 * 1024 * 1024;
|
|
@@ -1487,7 +1339,7 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
1487
1339
|
var initPromise = null;
|
|
1488
1340
|
async function getAstGrepPath() {
|
|
1489
1341
|
const currentPath = getSgCliPath();
|
|
1490
|
-
if (currentPath !== "sg" &&
|
|
1342
|
+
if (currentPath !== "sg" && existsSync5(currentPath)) {
|
|
1491
1343
|
return currentPath;
|
|
1492
1344
|
}
|
|
1493
1345
|
if (initPromise) {
|
|
@@ -1495,7 +1347,7 @@ async function getAstGrepPath() {
|
|
|
1495
1347
|
}
|
|
1496
1348
|
initPromise = (async () => {
|
|
1497
1349
|
const syncPath = findSgCliPathSync();
|
|
1498
|
-
if (syncPath &&
|
|
1350
|
+
if (syncPath && existsSync5(syncPath)) {
|
|
1499
1351
|
setSgCliPath(syncPath);
|
|
1500
1352
|
return syncPath;
|
|
1501
1353
|
}
|
|
@@ -1534,7 +1386,7 @@ async function runSg(options) {
|
|
|
1534
1386
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
1535
1387
|
args.push(...paths);
|
|
1536
1388
|
let cliPath = getSgCliPath();
|
|
1537
|
-
if (!
|
|
1389
|
+
if (!existsSync5(cliPath) && cliPath !== "sg") {
|
|
1538
1390
|
const downloadedPath = await getAstGrepPath();
|
|
1539
1391
|
if (downloadedPath) {
|
|
1540
1392
|
cliPath = downloadedPath;
|
|
@@ -1827,7 +1679,7 @@ var ast_grep_replace = tool({
|
|
|
1827
1679
|
});
|
|
1828
1680
|
|
|
1829
1681
|
// src/tools/lsp/client.ts
|
|
1830
|
-
import { readFileSync as
|
|
1682
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1831
1683
|
import { extname, resolve as resolve2 } from "path";
|
|
1832
1684
|
import { Readable, Writable } from "stream";
|
|
1833
1685
|
import { pathToFileURL } from "url";
|
|
@@ -1838,9 +1690,9 @@ import {
|
|
|
1838
1690
|
} from "vscode-jsonrpc/node.js";
|
|
1839
1691
|
|
|
1840
1692
|
// src/tools/lsp/config.ts
|
|
1841
|
-
import { existsSync as
|
|
1693
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1842
1694
|
import { homedir as homedir3 } from "os";
|
|
1843
|
-
import { join as
|
|
1695
|
+
import { join as join6 } from "path";
|
|
1844
1696
|
import whichSync from "which";
|
|
1845
1697
|
|
|
1846
1698
|
// src/tools/lsp/config-store.ts
|
|
@@ -1871,7 +1723,7 @@ function hasUserLspConfig() {
|
|
|
1871
1723
|
}
|
|
1872
1724
|
|
|
1873
1725
|
// src/tools/lsp/constants.ts
|
|
1874
|
-
import { existsSync as
|
|
1726
|
+
import { existsSync as existsSync6, readdirSync, statSync as statSync3 } from "fs";
|
|
1875
1727
|
import { dirname as dirname3, resolve } from "path";
|
|
1876
1728
|
var SEVERITY_MAP = {
|
|
1877
1729
|
1: "error",
|
|
@@ -1911,7 +1763,7 @@ function NearestRoot(includePatterns, excludePatterns) {
|
|
|
1911
1763
|
if (excludePatterns) {
|
|
1912
1764
|
for (const dir of walkUpDirectories(file, cwd)) {
|
|
1913
1765
|
for (const pattern of excludePatterns) {
|
|
1914
|
-
if (
|
|
1766
|
+
if (existsSync6(`${dir}/${pattern}`)) {
|
|
1915
1767
|
return void 0;
|
|
1916
1768
|
}
|
|
1917
1769
|
}
|
|
@@ -1930,7 +1782,7 @@ function NearestRoot(includePatterns, excludePatterns) {
|
|
|
1930
1782
|
}
|
|
1931
1783
|
} catch {
|
|
1932
1784
|
}
|
|
1933
|
-
} else if (
|
|
1785
|
+
} else if (existsSync6(`${dir}/${pattern}`)) {
|
|
1934
1786
|
return dir;
|
|
1935
1787
|
}
|
|
1936
1788
|
}
|
|
@@ -2552,11 +2404,11 @@ function isServerInstalled(command) {
|
|
|
2552
2404
|
if (command.length === 0) return false;
|
|
2553
2405
|
const cmd = command[0];
|
|
2554
2406
|
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
2555
|
-
return
|
|
2407
|
+
return existsSync7(cmd);
|
|
2556
2408
|
}
|
|
2557
2409
|
const isWindows = process.platform === "win32";
|
|
2558
2410
|
const ext = isWindows ? ".exe" : "";
|
|
2559
|
-
const opencodeBin =
|
|
2411
|
+
const opencodeBin = join6(homedir3(), ".config", "opencode", "bin");
|
|
2560
2412
|
const searchPath = (process.env.PATH ?? "") + (isWindows ? ";" : ":") + opencodeBin;
|
|
2561
2413
|
const result = whichSync.sync(cmd, {
|
|
2562
2414
|
path: searchPath,
|
|
@@ -2567,8 +2419,8 @@ function isServerInstalled(command) {
|
|
|
2567
2419
|
return true;
|
|
2568
2420
|
}
|
|
2569
2421
|
const cwd = process.cwd();
|
|
2570
|
-
const localBin =
|
|
2571
|
-
if (
|
|
2422
|
+
const localBin = join6(cwd, "node_modules", ".bin", cmd);
|
|
2423
|
+
if (existsSync7(localBin) || existsSync7(localBin + ext)) {
|
|
2572
2424
|
return true;
|
|
2573
2425
|
}
|
|
2574
2426
|
return false;
|
|
@@ -2908,7 +2760,7 @@ stderr: ${stderr}` : "")
|
|
|
2908
2760
|
log("[lsp] openFile: already open, skipping", { filePath: absPath });
|
|
2909
2761
|
return;
|
|
2910
2762
|
}
|
|
2911
|
-
const text =
|
|
2763
|
+
const text = readFileSync3(absPath, "utf-8");
|
|
2912
2764
|
const ext = extname(absPath);
|
|
2913
2765
|
const languageId = getLanguageId(ext);
|
|
2914
2766
|
log("[lsp] openFile: opening document", {
|
|
@@ -2999,13 +2851,13 @@ import { tool as tool2 } from "@opencode-ai/plugin/tool";
|
|
|
2999
2851
|
|
|
3000
2852
|
// src/tools/lsp/utils.ts
|
|
3001
2853
|
import {
|
|
3002
|
-
existsSync as
|
|
3003
|
-
readFileSync as
|
|
2854
|
+
existsSync as existsSync8,
|
|
2855
|
+
readFileSync as readFileSync4,
|
|
3004
2856
|
statSync as statSync4,
|
|
3005
2857
|
unlinkSync as unlinkSync2,
|
|
3006
|
-
writeFileSync
|
|
2858
|
+
writeFileSync
|
|
3007
2859
|
} from "fs";
|
|
3008
|
-
import { dirname as dirname4, extname as extname2, join as
|
|
2860
|
+
import { dirname as dirname4, extname as extname2, join as join7, resolve as resolve3 } from "path";
|
|
3009
2861
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3010
2862
|
function findServerProjectRoot(filePath, server) {
|
|
3011
2863
|
if (server.root) {
|
|
@@ -3111,7 +2963,7 @@ function filterDiagnosticsBySeverity(diagnostics, severityFilter) {
|
|
|
3111
2963
|
}
|
|
3112
2964
|
function applyTextEditsToFile(filePath, edits) {
|
|
3113
2965
|
try {
|
|
3114
|
-
const content =
|
|
2966
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
3115
2967
|
const lines = content.split("\n");
|
|
3116
2968
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
3117
2969
|
if (b.range.start.line !== a.range.start.line) {
|
|
@@ -3138,7 +2990,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
3138
2990
|
);
|
|
3139
2991
|
}
|
|
3140
2992
|
}
|
|
3141
|
-
|
|
2993
|
+
writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
3142
2994
|
return { success: true, editCount: edits.length };
|
|
3143
2995
|
} catch (err) {
|
|
3144
2996
|
return {
|
|
@@ -3185,7 +3037,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
3185
3037
|
if (change.kind === "create") {
|
|
3186
3038
|
try {
|
|
3187
3039
|
const filePath = uriToPath(change.uri);
|
|
3188
|
-
|
|
3040
|
+
writeFileSync(filePath, "", "utf-8");
|
|
3189
3041
|
result.filesModified.push(filePath);
|
|
3190
3042
|
} catch (err) {
|
|
3191
3043
|
result.success = false;
|
|
@@ -3195,8 +3047,8 @@ function applyWorkspaceEdit(edit) {
|
|
|
3195
3047
|
try {
|
|
3196
3048
|
const oldPath = uriToPath(change.oldUri);
|
|
3197
3049
|
const newPath = uriToPath(change.newUri);
|
|
3198
|
-
const content =
|
|
3199
|
-
|
|
3050
|
+
const content = readFileSync4(oldPath, "utf-8");
|
|
3051
|
+
writeFileSync(newPath, content, "utf-8");
|
|
3200
3052
|
unlinkSync2(oldPath);
|
|
3201
3053
|
result.filesModified.push(newPath);
|
|
3202
3054
|
} catch (err) {
|
|
@@ -3578,7 +3430,7 @@ var TmuxSessionManager = class {
|
|
|
3578
3430
|
|
|
3579
3431
|
// src/index.ts
|
|
3580
3432
|
var ThothAgents = async (ctx, _options) => {
|
|
3581
|
-
const { client, directory
|
|
3433
|
+
const { client, directory } = ctx;
|
|
3582
3434
|
const config = loadPluginConfig(directory);
|
|
3583
3435
|
const agentDefs = createAgents(config);
|
|
3584
3436
|
const agents = renderOpenCodeAgentConfigs(config);
|
|
@@ -3629,14 +3481,9 @@ var ThothAgents = async (ctx, _options) => {
|
|
|
3629
3481
|
}
|
|
3630
3482
|
const mcps = createBuiltinMcps(config.disabled_mcps);
|
|
3631
3483
|
const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
|
|
3632
|
-
const autoUpdateChecker = createAutoUpdateCheckerHook(
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
showStartupToast: true,
|
|
3636
|
-
autoUpdate: true
|
|
3637
|
-
},
|
|
3638
|
-
shell
|
|
3639
|
-
);
|
|
3484
|
+
const autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
|
|
3485
|
+
showStartupToast: true
|
|
3486
|
+
});
|
|
3640
3487
|
const delegateTaskRetryHook = createDelegateTaskRetryHook(ctx);
|
|
3641
3488
|
const jsonErrorRecoveryHook = createJsonErrorRecoveryHook(ctx);
|
|
3642
3489
|
const foregroundFallback = new ForegroundFallbackManager(
|