@skein-code/cli 0.3.20 → 0.3.21
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 +13 -9
- package/dist/cli.js +167 -59
- package/dist/cli.js.map +1 -1
- package/docs/NEXT_STEPS.md +20 -9
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ To build, verify, and install a local package artifact from this checkout:
|
|
|
94
94
|
|
|
95
95
|
```bash
|
|
96
96
|
npm run verify:package -- --output-dir artifacts/package
|
|
97
|
-
npm install -g ./artifacts/package/skein-code-cli-0.3.
|
|
97
|
+
npm install -g ./artifacts/package/skein-code-cli-0.3.21.tgz
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
To install the published package from npm:
|
|
@@ -449,14 +449,18 @@ command instead of allowing a retrieval change to lower the quality bar.
|
|
|
449
449
|
The persisted local index now stores content-addressed TypeScript compiler AST
|
|
450
450
|
facts for definitions, calls, and relative imports. Matching definitions can
|
|
451
451
|
expand a query and import/call neighbors may receive a bounded graph score;
|
|
452
|
-
Python and SQL
|
|
453
|
-
collected through a bounded, isolated
|
|
454
|
-
only a small tie-break score; missing
|
|
455
|
-
non-repository workspaces degrade to
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
452
|
+
Python module imports and SQL definitions/references use explicit syntax-aware
|
|
453
|
+
offline fallbacks. Git recency is collected through a bounded, isolated
|
|
454
|
+
read-only history scan and contributes only a small tie-break score; missing
|
|
455
|
+
Git, timeouts, output limits, and non-repository workspaces degrade to
|
|
456
|
+
lexical/graph retrieval. A failed configured verification may add a bounded
|
|
457
|
+
diagnostic tie-break for paths parsed from non-truncated process output. Those
|
|
458
|
+
hints are current-run only, clear on success or the next run, and cannot create
|
|
459
|
+
a zero-relevance hit. Search and context JSON expose the index generation, file
|
|
460
|
+
hash, matched/expanded terms, and bm25/path/symbol/phrase/graph/recency/
|
|
461
|
+
diagnostic breakdown without persisting the query, diagnostic path, or another
|
|
462
|
+
copy of source. Index schema v2 artifacts rebuild as v3 rather than being
|
|
463
|
+
trusted after the parser contract changes.
|
|
460
464
|
|
|
461
465
|
The included fixture is a deterministic regression gate, not evidence for
|
|
462
466
|
performance on every production repository. `--fresh-index` deletes and
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
5
5
|
import { stdin as input, stdout as output } from "node:process";
|
|
6
6
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
7
|
-
import { basename as basename13, resolve as
|
|
7
|
+
import { basename as basename13, resolve as resolve25 } from "node:path";
|
|
8
8
|
import { Command, Option } from "commander";
|
|
9
9
|
import chalk4 from "chalk";
|
|
10
10
|
|
|
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
|
|
|
220
220
|
// package.json
|
|
221
221
|
var package_default = {
|
|
222
222
|
name: "@skein-code/cli",
|
|
223
|
-
version: "0.3.
|
|
223
|
+
version: "0.3.21",
|
|
224
224
|
description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
|
|
225
225
|
type: "module",
|
|
226
226
|
license: "MIT",
|
|
@@ -236,6 +236,10 @@ var package_default = {
|
|
|
236
236
|
},
|
|
237
237
|
skein: {
|
|
238
238
|
releaseNotes: [
|
|
239
|
+
"Failed configured verification output yields bounded current-run diagnostic ranking without persisting paths",
|
|
240
|
+
"Diagnostic hints cannot create zero-relevance hits and are cleared on success or the next agent run",
|
|
241
|
+
"Python absolute and relative module imports now contribute deterministic offline graph adjacency",
|
|
242
|
+
"Context provenance exposes the diagnostic score alongside graph and isolated Git recency signals",
|
|
239
243
|
"Local index v3 records TypeScript AST definitions, calls, and relative import adjacency for graph-assisted retrieval",
|
|
240
244
|
"Context hits expose generation, content hash, matched terms, and bm25/path/symbol/phrase/graph score provenance",
|
|
241
245
|
"Context benchmark v2 locks multilingual recall, MRR, useful-token, stale-hit, incremental-index, and warm-latency gates",
|
|
@@ -3113,7 +3117,7 @@ function executableNames(command2) {
|
|
|
3113
3117
|
return [command2, ...extensions.map((extension) => `${command2}${extension}`)];
|
|
3114
3118
|
}
|
|
3115
3119
|
function runProcess(command2, args, options) {
|
|
3116
|
-
return new Promise((
|
|
3120
|
+
return new Promise((resolve26, reject) => {
|
|
3117
3121
|
const started = Date.now();
|
|
3118
3122
|
const environment = options.inheritEnv === false ? {} : { ...process.env };
|
|
3119
3123
|
for (const name of options.unsetEnv ?? []) delete environment[name];
|
|
@@ -3205,7 +3209,7 @@ function runProcess(command2, args, options) {
|
|
|
3205
3209
|
reject(callbackError);
|
|
3206
3210
|
return;
|
|
3207
3211
|
}
|
|
3208
|
-
|
|
3212
|
+
resolve26({
|
|
3209
3213
|
command: [command2, ...args].join(" "),
|
|
3210
3214
|
exitCode: code ?? (timedOut ? 124 : 1),
|
|
3211
3215
|
stdout,
|
|
@@ -3474,6 +3478,8 @@ var MAX_QUERY_CACHE_ENTRIES = 64;
|
|
|
3474
3478
|
var CHUNK_LINES = 100;
|
|
3475
3479
|
var CHUNK_OVERLAP = 15;
|
|
3476
3480
|
var MIN_STRUCTURAL_CHUNK_LINES = 12;
|
|
3481
|
+
var MAX_DIAGNOSTIC_SCORE = 0.05;
|
|
3482
|
+
var MAX_DIAGNOSTIC_HINTS_PER_COMMAND = 16;
|
|
3477
3483
|
var LocalContextIndex = class {
|
|
3478
3484
|
constructor(roots) {
|
|
3479
3485
|
this.roots = roots;
|
|
@@ -3487,6 +3493,8 @@ var LocalContextIndex = class {
|
|
|
3487
3493
|
workspace;
|
|
3488
3494
|
queryCache = /* @__PURE__ */ new Map();
|
|
3489
3495
|
gitRecency;
|
|
3496
|
+
diagnosticsByCommand = /* @__PURE__ */ new Map();
|
|
3497
|
+
diagnosticScores = /* @__PURE__ */ new Map();
|
|
3490
3498
|
queryCacheHits = 0;
|
|
3491
3499
|
queryCacheMisses = 0;
|
|
3492
3500
|
fingerprintCache;
|
|
@@ -3605,6 +3613,23 @@ var LocalContextIndex = class {
|
|
|
3605
3613
|
if (paths.length) this.queryCache.clear();
|
|
3606
3614
|
if (this.dirtyPaths.size) this.refreshState = "dirty";
|
|
3607
3615
|
}
|
|
3616
|
+
recordDiagnostics(update) {
|
|
3617
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3618
|
+
for (const candidate of update.paths.slice(0, MAX_DIAGNOSTIC_HINTS_PER_COMMAND)) {
|
|
3619
|
+
const path = this.resolveDiagnosticPath(candidate);
|
|
3620
|
+
if (path) paths.add(path);
|
|
3621
|
+
}
|
|
3622
|
+
if (paths.size) this.diagnosticsByCommand.set(update.commandKey, paths);
|
|
3623
|
+
else this.diagnosticsByCommand.delete(update.commandKey);
|
|
3624
|
+
this.refreshDiagnosticScores();
|
|
3625
|
+
this.queryCache.clear();
|
|
3626
|
+
}
|
|
3627
|
+
resetDiagnostics() {
|
|
3628
|
+
if (!this.diagnosticsByCommand.size) return;
|
|
3629
|
+
this.diagnosticsByCommand.clear();
|
|
3630
|
+
this.diagnosticScores.clear();
|
|
3631
|
+
this.queryCache.clear();
|
|
3632
|
+
}
|
|
3608
3633
|
async flushDirty() {
|
|
3609
3634
|
if (!this.dirtyPaths.size) {
|
|
3610
3635
|
return {
|
|
@@ -3690,6 +3715,7 @@ var LocalContextIndex = class {
|
|
|
3690
3715
|
queryCacheEntries: this.queryCache.size,
|
|
3691
3716
|
queryCacheHits: this.queryCacheHits,
|
|
3692
3717
|
queryCacheMisses: this.queryCacheMisses,
|
|
3718
|
+
diagnosticHints: this.diagnosticScores.size,
|
|
3693
3719
|
refreshState: this.refreshState,
|
|
3694
3720
|
dirtyPaths: this.dirtyPaths.size,
|
|
3695
3721
|
...this.refreshError ? { refreshError: this.refreshError } : {},
|
|
@@ -3939,11 +3965,12 @@ var LocalContextIndex = class {
|
|
|
3939
3965
|
);
|
|
3940
3966
|
const graph = graphBoosts.get(chunk.absolutePath)?.score ?? 0;
|
|
3941
3967
|
const recency = recencyScores.get(chunk.absolutePath) ?? 0;
|
|
3942
|
-
const
|
|
3943
|
-
|
|
3968
|
+
const diagnostic = this.diagnosticScores.get(chunk.absolutePath) ?? 0;
|
|
3969
|
+
const score = lexical.bm25 + lexical.path + lexical.symbol + lexical.phrase + graph + recency + diagnostic;
|
|
3970
|
+
return { chunk, lexical, graph, recency, diagnostic, score };
|
|
3944
3971
|
}).filter(({ lexical, graph }) => lexical.bm25 + lexical.path + lexical.symbol + lexical.phrase + graph > 0).sort((left, right) => right.score - left.score || left.chunk.absolutePath.localeCompare(right.chunk.absolutePath));
|
|
3945
3972
|
const covered = scored.filter(({ lexical, graph }) => graph > 0 || hasSufficientQueryCoverage(lexical.matchedTerms, coverageTerms));
|
|
3946
|
-
return (covered.length ? covered : scored).slice(0, topK).map(({ chunk, lexical, graph, recency, score }) => {
|
|
3973
|
+
return (covered.length ? covered : scored).slice(0, topK).map(({ chunk, lexical, graph, recency, diagnostic, score }) => {
|
|
3947
3974
|
const file = filesByPath.get(chunk.absolutePath);
|
|
3948
3975
|
const breakdown = {
|
|
3949
3976
|
bm25: roundScore(lexical.bm25),
|
|
@@ -3952,6 +3979,7 @@ var LocalContextIndex = class {
|
|
|
3952
3979
|
phrase: roundScore(lexical.phrase),
|
|
3953
3980
|
graph: roundScore(graph),
|
|
3954
3981
|
recency: roundScore(recency),
|
|
3982
|
+
diagnostic: roundScore(diagnostic),
|
|
3955
3983
|
total: roundScore(score)
|
|
3956
3984
|
};
|
|
3957
3985
|
const provenance = {
|
|
@@ -3967,7 +3995,7 @@ var LocalContextIndex = class {
|
|
|
3967
3995
|
endLine: chunk.endLine,
|
|
3968
3996
|
content: chunk.content,
|
|
3969
3997
|
score,
|
|
3970
|
-
source: "local-bm25+path+symbol+graph+recency",
|
|
3998
|
+
source: "local-bm25+path+symbol+graph+recency+diagnostic",
|
|
3971
3999
|
...chunk.symbol ? { symbol: chunk.symbol } : {},
|
|
3972
4000
|
provenance
|
|
3973
4001
|
};
|
|
@@ -4023,6 +4051,22 @@ var LocalContextIndex = class {
|
|
|
4023
4051
|
this.queryCache.delete(oldest);
|
|
4024
4052
|
}
|
|
4025
4053
|
}
|
|
4054
|
+
resolveDiagnosticPath(candidate) {
|
|
4055
|
+
if (!candidate || candidate.includes("\0")) return void 0;
|
|
4056
|
+
const absolute = isAbsolute4(candidate) ? resolve10(candidate) : void 0;
|
|
4057
|
+
if (absolute && this.roots.some((root) => isWithinRoot(root, absolute))) return absolute;
|
|
4058
|
+
for (const root of this.roots) {
|
|
4059
|
+
const path = resolve10(root, candidate);
|
|
4060
|
+
if (isWithinRoot(root, path)) return path;
|
|
4061
|
+
}
|
|
4062
|
+
return void 0;
|
|
4063
|
+
}
|
|
4064
|
+
refreshDiagnosticScores() {
|
|
4065
|
+
this.diagnosticScores.clear();
|
|
4066
|
+
for (const paths of this.diagnosticsByCommand.values()) {
|
|
4067
|
+
for (const path of paths) this.diagnosticScores.set(path, MAX_DIAGNOSTIC_SCORE);
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4026
4070
|
};
|
|
4027
4071
|
function isWithinRoot(root, path) {
|
|
4028
4072
|
const offset = relative5(root, path);
|
|
@@ -4158,7 +4202,7 @@ function packContextHits(hits, roots, maxTokens, engine) {
|
|
|
4158
4202
|
const text = selected.map((hit) => {
|
|
4159
4203
|
const shownPath = workspaceAliasPath(hit.path, roots);
|
|
4160
4204
|
const symbol = hit.symbol ? ` symbol="${escapeAttribute(hit.symbol)}"` : "";
|
|
4161
|
-
const provenance = hit.provenance ? ` source="${escapeAttribute(hit.source)}" hash="${hit.provenance.contentHash.slice(0, 12)}" graph-score="${hit.provenance.score.graph.toFixed(3)}" recency-score="${hit.provenance.score.recency.toFixed(6)}"` : "";
|
|
4205
|
+
const provenance = hit.provenance ? ` source="${escapeAttribute(hit.source)}" hash="${hit.provenance.contentHash.slice(0, 12)}" graph-score="${hit.provenance.score.graph.toFixed(3)}" recency-score="${hit.provenance.score.recency.toFixed(6)}" diagnostic-score="${hit.provenance.score.diagnostic.toFixed(3)}"` : "";
|
|
4162
4206
|
return `<code path="${escapeAttribute(shownPath)}" lines="${hit.startLine}-${hit.endLine}" score="${hit.score.toFixed(3)}"${symbol}${provenance}>
|
|
4163
4207
|
${hit.content}
|
|
4164
4208
|
</code>`;
|
|
@@ -4485,18 +4529,37 @@ function definitionMatchesQuery(definitionTerms, queryTerms) {
|
|
|
4485
4529
|
return shared.length >= required;
|
|
4486
4530
|
}
|
|
4487
4531
|
function resolveImportTargets(importer, specifier, files) {
|
|
4488
|
-
|
|
4489
|
-
const
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
base,
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4532
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
4533
|
+
const extension = extname2(importer.path).toLocaleLowerCase();
|
|
4534
|
+
if (specifier.startsWith(".")) {
|
|
4535
|
+
const base = posix.normalize(posix.join(posix.dirname(importer.path), specifier));
|
|
4536
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
4537
|
+
const importedExtension = posix.extname(base);
|
|
4538
|
+
const extensionless = extensions.includes(importedExtension) ? base.slice(0, -importedExtension.length) : base;
|
|
4539
|
+
candidates.add(base);
|
|
4540
|
+
for (const candidateExtension of extensions) {
|
|
4541
|
+
candidates.add(`${extensionless}${candidateExtension}`);
|
|
4542
|
+
candidates.add(posix.join(extensionless, `index${candidateExtension}`));
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
if (extension === ".py") {
|
|
4546
|
+
const modulePath = resolvePythonModulePath(importer.path, specifier);
|
|
4547
|
+
if (modulePath) {
|
|
4548
|
+
candidates.add(`${modulePath}.py`);
|
|
4549
|
+
candidates.add(posix.join(modulePath, "__init__.py"));
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4498
4552
|
return files.filter((file) => file.root === importer.root && candidates.has(file.path)).map((file) => file.absolutePath);
|
|
4499
4553
|
}
|
|
4554
|
+
function resolvePythonModulePath(importerPath, specifier) {
|
|
4555
|
+
const match = specifier.match(/^(\.+)(.*)$/u);
|
|
4556
|
+
if (!match) return specifier.split(".").filter(Boolean).join("/");
|
|
4557
|
+
const [, dots, remainder] = match;
|
|
4558
|
+
if (!dots || !remainder) return void 0;
|
|
4559
|
+
let base = posix.dirname(importerPath);
|
|
4560
|
+
for (let level = 1; level < dots.length; level += 1) base = posix.dirname(base);
|
|
4561
|
+
return posix.normalize(posix.join(base, remainder.split(".").filter(Boolean).join("/")));
|
|
4562
|
+
}
|
|
4500
4563
|
function scoreChunk(chunk, terms, rawQuery, documentFrequency, documentCount, averageLength) {
|
|
4501
4564
|
const frequencies = /* @__PURE__ */ new Map();
|
|
4502
4565
|
for (const token of chunk.tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
@@ -4587,6 +4650,12 @@ var ContextEngine = class {
|
|
|
4587
4650
|
invalidate(paths) {
|
|
4588
4651
|
this.local.invalidate(paths);
|
|
4589
4652
|
}
|
|
4653
|
+
recordDiagnostics(update) {
|
|
4654
|
+
this.local.recordDiagnostics(update);
|
|
4655
|
+
}
|
|
4656
|
+
resetDiagnostics() {
|
|
4657
|
+
this.local.resetDiagnostics();
|
|
4658
|
+
}
|
|
4590
4659
|
async flushDirty() {
|
|
4591
4660
|
try {
|
|
4592
4661
|
const result = await this.local.flushDirty();
|
|
@@ -4633,7 +4702,7 @@ function formatContextHits(hits, roots) {
|
|
|
4633
4702
|
const path = workspaceAliasPath(hit.path, roots);
|
|
4634
4703
|
const symbol = hit.symbol ? ` ${hit.symbol}` : "";
|
|
4635
4704
|
const score = hit.provenance?.score;
|
|
4636
|
-
const breakdown = score ? ` bm25=${score.bm25.toFixed(2)} path=${score.path.toFixed(2)} symbol=${score.symbol.toFixed(2)} graph=${score.graph.toFixed(2)} recency=${score.recency.toFixed(6)}` : "";
|
|
4705
|
+
const breakdown = score ? ` bm25=${score.bm25.toFixed(2)} path=${score.path.toFixed(2)} symbol=${score.symbol.toFixed(2)} graph=${score.graph.toFixed(2)} recency=${score.recency.toFixed(6)} diagnostic=${score.diagnostic.toFixed(3)}` : "";
|
|
4637
4706
|
const hash4 = hit.provenance?.contentHash.slice(0, 12);
|
|
4638
4707
|
return `[${hit.source} ${hit.score.toFixed(3)}${breakdown}${hash4 ? ` hash=${hash4}` : ""}]${symbol} ${path}:${hit.startLine}-${hit.endLine}`;
|
|
4639
4708
|
}).join("\n");
|
|
@@ -8585,6 +8654,7 @@ function compact(value, limit) {
|
|
|
8585
8654
|
|
|
8586
8655
|
// src/agent/completion-gate.ts
|
|
8587
8656
|
import { createHash as createHash12 } from "node:crypto";
|
|
8657
|
+
import { isAbsolute as isAbsolute6, resolve as resolve17 } from "node:path";
|
|
8588
8658
|
|
|
8589
8659
|
// src/tools/permissions.ts
|
|
8590
8660
|
import { createHash as createHash11, createHmac, randomBytes } from "node:crypto";
|
|
@@ -8788,6 +8858,37 @@ function captureVerification(call, result, changeSequence, configuredCommands) {
|
|
|
8788
8858
|
commandKey: createHash12("sha256").update(normalized).digest("hex")
|
|
8789
8859
|
};
|
|
8790
8860
|
}
|
|
8861
|
+
function verificationDiagnosticPaths(result, workspaceRoots) {
|
|
8862
|
+
if (result.ok || result.metadata?.sourceTruncated === true || typeof result.metadata?.exitCode !== "number" || result.metadata.exitCode === 0) return [];
|
|
8863
|
+
const roots = workspaceRoots.map((root) => resolve17(root));
|
|
8864
|
+
const cwd = typeof result.metadata.cwd === "string" ? resolve17(result.metadata.cwd) : void 0;
|
|
8865
|
+
const bases = cwd ? [cwd, ...roots] : roots;
|
|
8866
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
8867
|
+
const pathPattern = String.raw`((?:[./][^\s():]+|[^\s():]+)\.[A-Za-z0-9]{1,10})`;
|
|
8868
|
+
const patterns = [
|
|
8869
|
+
new RegExp(`${pathPattern}\\(\\d+,\\d+\\):\\s*(?:error|warning|fatal)\\b`, "gimu"),
|
|
8870
|
+
new RegExp(`${pathPattern}:\\d+:\\d+:\\s*(?:error|warning|fatal)\\b`, "gimu"),
|
|
8871
|
+
new RegExp(`^\\s*(?:FAIL|ERROR)\\s+${pathPattern}`, "gimu")
|
|
8872
|
+
];
|
|
8873
|
+
const output2 = result.content.slice(0, 2e5).split(/\r?\n/u).filter((line) => !/^\s*Command:/u.test(line)).join("\n");
|
|
8874
|
+
for (const pattern of patterns) {
|
|
8875
|
+
for (const match of output2.matchAll(pattern)) {
|
|
8876
|
+
const candidate = match[1];
|
|
8877
|
+
if (!candidate) continue;
|
|
8878
|
+
const path = resolveDiagnosticPath(candidate, bases, roots);
|
|
8879
|
+
if (path) candidates.add(path);
|
|
8880
|
+
if (candidates.size >= 16) return [...candidates];
|
|
8881
|
+
}
|
|
8882
|
+
}
|
|
8883
|
+
return [...candidates];
|
|
8884
|
+
}
|
|
8885
|
+
function resolveDiagnosticPath(candidate, bases, roots) {
|
|
8886
|
+
for (const base of bases) {
|
|
8887
|
+
const path = isAbsolute6(candidate) ? resolve17(candidate) : resolve17(base, candidate);
|
|
8888
|
+
if (roots.some((root) => isInside(root, path))) return path;
|
|
8889
|
+
}
|
|
8890
|
+
return void 0;
|
|
8891
|
+
}
|
|
8791
8892
|
function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = [], duplication) {
|
|
8792
8893
|
const files = [...new Set(changedFiles)];
|
|
8793
8894
|
const acceptance = taskContract ? buildAcceptance(taskContract, audit, evidence, currentChangeSequence) : void 0;
|
|
@@ -10500,6 +10601,7 @@ var AgentRunner = class {
|
|
|
10500
10601
|
throw new Error("User input is too large; pass a focused request or attach files with @path.");
|
|
10501
10602
|
}
|
|
10502
10603
|
this.running = true;
|
|
10604
|
+
this.contextEngine.resetDiagnostics?.();
|
|
10503
10605
|
const emit = async (event) => {
|
|
10504
10606
|
await options.onEvent?.(event);
|
|
10505
10607
|
};
|
|
@@ -10526,7 +10628,13 @@ var AgentRunner = class {
|
|
|
10526
10628
|
this.changeSequence,
|
|
10527
10629
|
this.config.agent.verifyCommands
|
|
10528
10630
|
);
|
|
10529
|
-
if (evidence)
|
|
10631
|
+
if (evidence) {
|
|
10632
|
+
verificationEvidence.push(evidence);
|
|
10633
|
+
this.contextEngine.recordDiagnostics?.({
|
|
10634
|
+
commandKey: evidence.commandKey,
|
|
10635
|
+
paths: verificationDiagnosticPaths(result, this.workspace.roots)
|
|
10636
|
+
});
|
|
10637
|
+
}
|
|
10530
10638
|
};
|
|
10531
10639
|
const completionReport = () => {
|
|
10532
10640
|
const completion = buildRunCompletion(
|
|
@@ -11843,7 +11951,7 @@ function numeric(...values) {
|
|
|
11843
11951
|
// src/agent/team-store.ts
|
|
11844
11952
|
import { createHash as createHash16, randomUUID as randomUUID13 } from "node:crypto";
|
|
11845
11953
|
import { lstat as lstat18, readFile as readFile17, readdir as readdir7, rm as rm3 } from "node:fs/promises";
|
|
11846
|
-
import { join as join16, resolve as
|
|
11954
|
+
import { join as join16, resolve as resolve18 } from "node:path";
|
|
11847
11955
|
import { z as z18 } from "zod";
|
|
11848
11956
|
var runIdSchema = z18.string().uuid();
|
|
11849
11957
|
var hashSchema = z18.string().regex(/^[a-f0-9]{64}$/u);
|
|
@@ -11927,9 +12035,9 @@ var TeamRunStore = class {
|
|
|
11927
12035
|
managedDirectory;
|
|
11928
12036
|
writes = Promise.resolve();
|
|
11929
12037
|
constructor(workspace, directory) {
|
|
11930
|
-
this.workspace =
|
|
12038
|
+
this.workspace = resolve18(workspace);
|
|
11931
12039
|
this.managedDirectory = directory === void 0;
|
|
11932
|
-
this.directory = directory ?
|
|
12040
|
+
this.directory = directory ? resolve18(directory) : join16(resolveProjectNamespaceSync(this.workspace).active, "team-runs");
|
|
11933
12041
|
}
|
|
11934
12042
|
async create(input2) {
|
|
11935
12043
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -12016,7 +12124,7 @@ var TeamRunStore = class {
|
|
|
12016
12124
|
const path = join16(this.runDirectory(runId), "manifest.json");
|
|
12017
12125
|
await this.assertRegularFile(path);
|
|
12018
12126
|
const manifest = manifestSchema2.parse(JSON.parse(await readFile17(path, "utf8")));
|
|
12019
|
-
if (manifest.id !== runId ||
|
|
12127
|
+
if (manifest.id !== runId || resolve18(manifest.workspace) !== this.workspace) {
|
|
12020
12128
|
throw new Error("Team run manifest identity does not match its location.");
|
|
12021
12129
|
}
|
|
12022
12130
|
if (verify) {
|
|
@@ -12147,7 +12255,7 @@ var TeamRunStore = class {
|
|
|
12147
12255
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Team run storage is not a regular directory.");
|
|
12148
12256
|
}
|
|
12149
12257
|
async assertRegularFile(path) {
|
|
12150
|
-
await assertNoSymlinkPath(this.workspace,
|
|
12258
|
+
await assertNoSymlinkPath(this.workspace, resolve18(path, ".."));
|
|
12151
12259
|
const info = await lstat18(path);
|
|
12152
12260
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Team run file is not a regular file: ${path}`);
|
|
12153
12261
|
}
|
|
@@ -12195,7 +12303,7 @@ function resolveAgentModelRoute(team, parent, profile) {
|
|
|
12195
12303
|
import { createHash as createHash17 } from "node:crypto";
|
|
12196
12304
|
import { lstat as lstat19, mkdtemp, realpath as realpath7, rm as rm4 } from "node:fs/promises";
|
|
12197
12305
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
12198
|
-
import { isAbsolute as
|
|
12306
|
+
import { isAbsolute as isAbsolute7, join as join17, resolve as resolve19 } from "node:path";
|
|
12199
12307
|
var WriterLaneApplyError = class extends Error {
|
|
12200
12308
|
constructor(message2, attempted, cause) {
|
|
12201
12309
|
super(message2, cause === void 0 ? void 0 : { cause });
|
|
@@ -12208,8 +12316,8 @@ var WriterLane = class {
|
|
|
12208
12316
|
workspace;
|
|
12209
12317
|
constructor(workspace, workspaceRoots) {
|
|
12210
12318
|
this.workspace = new WorkspaceAccess([
|
|
12211
|
-
|
|
12212
|
-
...workspaceRoots.map((root) =>
|
|
12319
|
+
resolve19(workspace),
|
|
12320
|
+
...workspaceRoots.map((root) => resolve19(root))
|
|
12213
12321
|
]);
|
|
12214
12322
|
}
|
|
12215
12323
|
async createDraft(maxPatchBytes, operation, signal) {
|
|
@@ -12414,7 +12522,7 @@ var WriterLane = class {
|
|
|
12414
12522
|
const files = unique2(parseNumstatPaths(numstat.stdout));
|
|
12415
12523
|
if (!files.length) throw new Error("Writer patch contains no file changes.");
|
|
12416
12524
|
for (const file of files) {
|
|
12417
|
-
if (
|
|
12525
|
+
if (isAbsolute7(file) || file === ".git" || file.startsWith(".git/") || file.includes("\0")) {
|
|
12418
12526
|
throw new Error(`Writer patch contains an unsafe path: ${file}`);
|
|
12419
12527
|
}
|
|
12420
12528
|
await this.workspace.resolvePath(join17(repository.root, file), { allowMissing: true });
|
|
@@ -12432,7 +12540,7 @@ var WriterLane = class {
|
|
|
12432
12540
|
if (topLevel.exitCode !== 0 || !topLevel.stdout.trim()) {
|
|
12433
12541
|
throw new Error("The primary workspace must be a Git repository root for writer lanes.");
|
|
12434
12542
|
}
|
|
12435
|
-
const discoveredRoot =
|
|
12543
|
+
const discoveredRoot = resolve19(topLevel.stdout.trim());
|
|
12436
12544
|
if (await realpath7(discoveredRoot) !== await realpath7(root)) {
|
|
12437
12545
|
throw new Error("The primary workspace must be the Git repository root for writer lanes.");
|
|
12438
12546
|
}
|
|
@@ -12441,7 +12549,7 @@ var WriterLane = class {
|
|
|
12441
12549
|
if (common.exitCode !== 0 || !common.stdout.trim()) {
|
|
12442
12550
|
throw new Error(`Unable to resolve the Git common directory: ${processDetail(common)}`);
|
|
12443
12551
|
}
|
|
12444
|
-
const commonDirectory = await realpath7(
|
|
12552
|
+
const commonDirectory = await realpath7(isAbsolute7(common.stdout.trim()) ? common.stdout.trim() : resolve19(repositoryRoot, common.stdout.trim()));
|
|
12445
12553
|
return { root: repositoryRoot, commonDirectory, runtime };
|
|
12446
12554
|
}
|
|
12447
12555
|
async head(repository) {
|
|
@@ -12453,7 +12561,7 @@ var WriterLane = class {
|
|
|
12453
12561
|
return value;
|
|
12454
12562
|
}
|
|
12455
12563
|
async cleanupWorktree(repository, worktree, added) {
|
|
12456
|
-
const physicalWorktree = await realpath7(worktree).catch(() =>
|
|
12564
|
+
const physicalWorktree = await realpath7(worktree).catch(() => resolve19(worktree));
|
|
12457
12565
|
if (added) {
|
|
12458
12566
|
await runIsolatedGit(repository.runtime, [
|
|
12459
12567
|
"worktree",
|
|
@@ -14379,7 +14487,7 @@ function formatResultDetail(result, paint = chalk2, glyphs = resolveCliGlyphs())
|
|
|
14379
14487
|
}
|
|
14380
14488
|
|
|
14381
14489
|
// src/cli/namespace-leases.ts
|
|
14382
|
-
import { resolve as
|
|
14490
|
+
import { resolve as resolve20 } from "node:path";
|
|
14383
14491
|
function cliNamespaceLeaseScopes(actionCommand) {
|
|
14384
14492
|
const names = commandNames(actionCommand);
|
|
14385
14493
|
const topLevel = names[1];
|
|
@@ -14401,7 +14509,7 @@ async function acquireCliNamespaceLeases(actionCommand) {
|
|
|
14401
14509
|
const scopes = cliNamespaceLeaseScopes(actionCommand);
|
|
14402
14510
|
const localOptions = actionCommand.opts();
|
|
14403
14511
|
const globalOptions = actionCommand.optsWithGlobals();
|
|
14404
|
-
const workspace =
|
|
14512
|
+
const workspace = resolve20(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
|
|
14405
14513
|
const targets = scopes.map((scope) => scope === "project" ? projectNamespacePaths(workspace).canonical : homeNamespacePaths().canonical);
|
|
14406
14514
|
const leases = [];
|
|
14407
14515
|
try {
|
|
@@ -14653,7 +14761,7 @@ function graphemes(value) {
|
|
|
14653
14761
|
|
|
14654
14762
|
// src/ui/theme.ts
|
|
14655
14763
|
import { lstat as lstat20, readdir as readdir8, readFile as readFile18 } from "node:fs/promises";
|
|
14656
|
-
import { basename as basename10, join as join19, resolve as
|
|
14764
|
+
import { basename as basename10, join as join19, resolve as resolve21 } from "node:path";
|
|
14657
14765
|
import React, { createContext, useContext } from "react";
|
|
14658
14766
|
function defineTheme(seed) {
|
|
14659
14767
|
return {
|
|
@@ -14775,7 +14883,7 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
|
|
|
14775
14883
|
userThemeNames.clear();
|
|
14776
14884
|
const loaded = [];
|
|
14777
14885
|
const errors = [];
|
|
14778
|
-
const resolvedDirectory =
|
|
14886
|
+
const resolvedDirectory = resolve21(directory);
|
|
14779
14887
|
let entries;
|
|
14780
14888
|
try {
|
|
14781
14889
|
entries = await readdir8(resolvedDirectory, { encoding: "utf8" });
|
|
@@ -17239,7 +17347,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
17239
17347
|
return () => clearInterval(timer);
|
|
17240
17348
|
}, [busy]);
|
|
17241
17349
|
const requestPermission = useCallback((call, category) => {
|
|
17242
|
-
return new Promise((
|
|
17350
|
+
return new Promise((resolve26) => setPermission({ call, category, resolve: resolve26 }));
|
|
17243
17351
|
}, []);
|
|
17244
17352
|
const onEvent = useCallback((event) => {
|
|
17245
17353
|
switch (event.type) {
|
|
@@ -18075,8 +18183,8 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
18075
18183
|
}, [append]);
|
|
18076
18184
|
function settlePermission(grant, stop = false) {
|
|
18077
18185
|
if (!permission) return;
|
|
18078
|
-
const { call, category, resolve:
|
|
18079
|
-
|
|
18186
|
+
const { call, category, resolve: resolve26 } = permission;
|
|
18187
|
+
resolve26(grant);
|
|
18080
18188
|
setPermission(void 0);
|
|
18081
18189
|
if (grant === "session") {
|
|
18082
18190
|
append({
|
|
@@ -19411,7 +19519,7 @@ async function runFirstRunOnboarding(initialConfig, options = {}) {
|
|
|
19411
19519
|
}
|
|
19412
19520
|
|
|
19413
19521
|
// src/runtime/extensions.ts
|
|
19414
|
-
import { resolve as
|
|
19522
|
+
import { resolve as resolve24 } from "node:path";
|
|
19415
19523
|
|
|
19416
19524
|
// src/mcp/manager.ts
|
|
19417
19525
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -19624,7 +19732,7 @@ function isRecord2(value) {
|
|
|
19624
19732
|
|
|
19625
19733
|
// src/mcp/validation.ts
|
|
19626
19734
|
import { stat as stat11, realpath as realpath8 } from "node:fs/promises";
|
|
19627
|
-
import { isAbsolute as
|
|
19735
|
+
import { isAbsolute as isAbsolute8, resolve as resolve22 } from "node:path";
|
|
19628
19736
|
var SERVER_NAME = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
19629
19737
|
var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
19630
19738
|
var HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
@@ -19696,9 +19804,9 @@ async function validateCwd(configured, options) {
|
|
|
19696
19804
|
if (configured !== void 0 && (configured.length > 4e3 || CONTROL_CHARACTERS.test(configured))) {
|
|
19697
19805
|
throw new Error("MCP working directory is invalid or too long");
|
|
19698
19806
|
}
|
|
19699
|
-
const defaultCwd =
|
|
19700
|
-
const candidate = configured ?
|
|
19701
|
-
const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) =>
|
|
19807
|
+
const defaultCwd = resolve22(options.cwd ?? process.cwd());
|
|
19808
|
+
const candidate = configured ? isAbsolute8(configured) ? resolve22(configured) : resolve22(defaultCwd, configured) : defaultCwd;
|
|
19809
|
+
const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) => resolve22(root)) : [defaultCwd];
|
|
19702
19810
|
const resolvedCandidate = await realpath8(candidate).catch(() => {
|
|
19703
19811
|
throw new Error(`MCP working directory does not exist: ${candidate}`);
|
|
19704
19812
|
});
|
|
@@ -20352,7 +20460,7 @@ function scopeKey(scope, context) {
|
|
|
20352
20460
|
// src/skills/catalog.ts
|
|
20353
20461
|
import { lstat as lstat21, readFile as readFile20, readdir as readdir9, realpath as realpath9 } from "node:fs/promises";
|
|
20354
20462
|
import { homedir as homedir4 } from "node:os";
|
|
20355
|
-
import { basename as basename12, join as join21, resolve as
|
|
20463
|
+
import { basename as basename12, join as join21, resolve as resolve23 } from "node:path";
|
|
20356
20464
|
import { parse as parseYaml3 } from "yaml";
|
|
20357
20465
|
var SkillCatalog = class {
|
|
20358
20466
|
constructor(workspace, config) {
|
|
@@ -20421,14 +20529,14 @@ ${skill.content}
|
|
|
20421
20529
|
}
|
|
20422
20530
|
function discoveryLocations(workspace, configured) {
|
|
20423
20531
|
const home = homedir4();
|
|
20424
|
-
const workspaceRoot =
|
|
20532
|
+
const workspaceRoot = resolve23(workspace);
|
|
20425
20533
|
return [
|
|
20426
20534
|
{ path: join21(home, ".agents", "skills"), scope: "user", trusted: true },
|
|
20427
20535
|
{ path: join21(home, ".claude", "skills"), scope: "user", trusted: true },
|
|
20428
20536
|
{ path: join21(home, ".augment", "skills"), scope: "user", trusted: true },
|
|
20429
20537
|
{ path: join21(resolveHomeNamespace(), "skills"), scope: "user", trusted: true },
|
|
20430
20538
|
...configured.map((path) => {
|
|
20431
|
-
const resolved =
|
|
20539
|
+
const resolved = resolve23(workspaceRoot, path);
|
|
20432
20540
|
return {
|
|
20433
20541
|
path: resolved,
|
|
20434
20542
|
scope: "configured",
|
|
@@ -20457,7 +20565,7 @@ async function readMetadata(path) {
|
|
|
20457
20565
|
const { frontmatter } = splitFrontmatter(raw);
|
|
20458
20566
|
if (!frontmatter) return void 0;
|
|
20459
20567
|
const parsed = parseYaml3(frontmatter);
|
|
20460
|
-
const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename12(
|
|
20568
|
+
const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename12(resolve23(path, ".."));
|
|
20461
20569
|
const description = typeof parsed?.description === "string" ? parsed.description.trim() : "";
|
|
20462
20570
|
if (!/^[a-z][a-z0-9_-]{0,63}$/.test(name) || !description || description.length > 1e3) {
|
|
20463
20571
|
return void 0;
|
|
@@ -20478,7 +20586,7 @@ async function safeRead(path, maxBytes) {
|
|
|
20478
20586
|
try {
|
|
20479
20587
|
const info = await lstat21(path);
|
|
20480
20588
|
if (!info.isFile() || info.isSymbolicLink() || info.size > maxBytes) return void 0;
|
|
20481
|
-
const parent =
|
|
20589
|
+
const parent = resolve23(path, "..");
|
|
20482
20590
|
const resolvedParent = await realpath9(parent);
|
|
20483
20591
|
const resolvedPath = await realpath9(path);
|
|
20484
20592
|
if (!resolvedPath.startsWith(`${resolvedParent}/`)) return void 0;
|
|
@@ -20663,7 +20771,7 @@ var ExtensionRuntime = class _ExtensionRuntime {
|
|
|
20663
20771
|
delegation;
|
|
20664
20772
|
initialized = false;
|
|
20665
20773
|
static async create(config, registry, options = {}) {
|
|
20666
|
-
const runtime = new _ExtensionRuntime(config,
|
|
20774
|
+
const runtime = new _ExtensionRuntime(config, resolve24(config.workspaceRoots[0] ?? process.cwd()), options);
|
|
20667
20775
|
try {
|
|
20668
20776
|
await runtime.initialize(registry, options.signal);
|
|
20669
20777
|
return runtime;
|
|
@@ -20868,15 +20976,15 @@ function upgradeCommandOverride(env = process.env) {
|
|
|
20868
20976
|
return trimmed ? trimmed : void 0;
|
|
20869
20977
|
}
|
|
20870
20978
|
function runUpgrade(plan) {
|
|
20871
|
-
return new Promise((
|
|
20979
|
+
return new Promise((resolve26) => {
|
|
20872
20980
|
const child = spawn2(plan.command, plan.args, {
|
|
20873
20981
|
stdio: "inherit",
|
|
20874
20982
|
shell: plan.shell ?? false
|
|
20875
20983
|
});
|
|
20876
|
-
child.on("error", () =>
|
|
20984
|
+
child.on("error", () => resolve26({ ok: false, exitCode: 127, display: plan.display }));
|
|
20877
20985
|
child.on("close", (code) => {
|
|
20878
20986
|
const exitCode = code ?? 1;
|
|
20879
|
-
|
|
20987
|
+
resolve26({ ok: exitCode === 0, exitCode, display: plan.display });
|
|
20880
20988
|
});
|
|
20881
20989
|
});
|
|
20882
20990
|
}
|
|
@@ -21138,7 +21246,7 @@ sessionCommand.command("export <id>").description("Export a session as Markdown"
|
|
|
21138
21246
|
const store = new SessionStore(workspaceOption(options.workspace));
|
|
21139
21247
|
const session = await requireSessionSelector(store, id);
|
|
21140
21248
|
const markdown = sessionMarkdown(session);
|
|
21141
|
-
if (options.output) await writeFile3(
|
|
21249
|
+
if (options.output) await writeFile3(resolve25(options.output), markdown, "utf8");
|
|
21142
21250
|
else process.stdout.write(markdown);
|
|
21143
21251
|
});
|
|
21144
21252
|
var checkpointCommand = program.command("checkpoint").description("Inspect and restore pre-mutation snapshots");
|
|
@@ -21515,7 +21623,7 @@ async function runChat(prompts, options) {
|
|
|
21515
21623
|
const stdinPrompt = !process.stdin.isTTY ? await readStdin() : "";
|
|
21516
21624
|
const firstPrompt = [...prompts, stdinPrompt].filter(Boolean).join("\n\n").trim();
|
|
21517
21625
|
if (shouldPrint && !firstPrompt) throw new Error("Provide a prompt argument or pipe input on stdin.");
|
|
21518
|
-
const workspace =
|
|
21626
|
+
const workspace = resolve25(options.workspace);
|
|
21519
21627
|
let config = await runtimeConfig(workspace, options);
|
|
21520
21628
|
let completedOnboarding = false;
|
|
21521
21629
|
if (!shouldPrint && needsFirstRunOnboarding(config)) {
|
|
@@ -21740,14 +21848,14 @@ async function runAgentSetup(options) {
|
|
|
21740
21848
|
`);
|
|
21741
21849
|
}
|
|
21742
21850
|
async function runtimeConfig(workspaceInput, options) {
|
|
21743
|
-
const workspace =
|
|
21851
|
+
const workspace = resolve25(workspaceInput);
|
|
21744
21852
|
const loaded = await loadConfig(workspace, options.config, {
|
|
21745
21853
|
trustProjectConfig: options.trustProjectConfig === true
|
|
21746
21854
|
});
|
|
21747
21855
|
const roots = [
|
|
21748
21856
|
workspace,
|
|
21749
21857
|
...loaded.workspaceRoots,
|
|
21750
|
-
...(options.addWorkspace ?? []).map((root) =>
|
|
21858
|
+
...(options.addWorkspace ?? []).map((root) => resolve25(workspace, root))
|
|
21751
21859
|
];
|
|
21752
21860
|
const provider = options.provider ? validateProvider(options.provider) : loaded.model.provider;
|
|
21753
21861
|
return {
|
|
@@ -21903,7 +22011,7 @@ function collect(value, previous) {
|
|
|
21903
22011
|
}
|
|
21904
22012
|
function workspaceOption(value) {
|
|
21905
22013
|
const rootOptions = program.opts();
|
|
21906
|
-
return
|
|
22014
|
+
return resolve25(value ?? rootOptions.workspace ?? process.cwd());
|
|
21907
22015
|
}
|
|
21908
22016
|
function runtimeOptions(options) {
|
|
21909
22017
|
const root = program.opts();
|