opencode-agent-skill 15.1.1 → 15.1.2
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 +5 -5
- package/lib/affected-tests.mjs +7 -3
- package/lib/code-intelligence/index.mjs +47 -9
- package/lib/code-intelligence/lsp-pool.mjs +191 -54
- package/lib/code-intelligence/lsp-provider.mjs +44 -7
- package/lib/context-manifest.mjs +55 -20
- package/lib/execution-contract.mjs +2 -2
- package/lib/memory-engine.mjs +14 -9
- package/lib/pi-rpc-pool.mjs +55 -24
- package/lib/repo-graph.mjs +2 -2
- package/lib/repo-inspect.mjs +3 -2
- package/lib/semantic-index.mjs +2 -1
- package/lib/task-engine.mjs +28 -9
- package/lib/workspace-fingerprint.mjs +17 -9
- package/lib/workspace-hygiene.mjs +2 -2
- package/lib/worktree-sandbox.mjs +14 -8
- package/package.json +1 -1
- package/pi/extensions/ues-child-runtime.ts +38 -2
- package/pi/extensions/ues.ts +95 -10
- package/scripts/check-source-integrity.mjs +60 -7
package/lib/context-manifest.mjs
CHANGED
|
@@ -7,7 +7,8 @@ import { taskFiles } from "./task-graph.mjs"
|
|
|
7
7
|
import { buildSemanticIndexCached, querySemanticIndex } from "./semantic-index.mjs"
|
|
8
8
|
import { queryContextHierarchy } from "./hierarchical-context.mjs"
|
|
9
9
|
import { rankContextGraph } from "./context-graph-rank.mjs"
|
|
10
|
-
import { runtimeWorkspaceSnapshot } from "./workspace-fingerprint.mjs"
|
|
10
|
+
import { runtimeWorkspaceSnapshot, workspaceStatusEntries } from "./workspace-fingerprint.mjs"
|
|
11
|
+
import { sourceFacingPaths, sourceGitPathspecs } from "./runtime-artifacts.mjs"
|
|
11
12
|
|
|
12
13
|
const ROOT_INSTRUCTIONS = [
|
|
13
14
|
"AGENTS.md",
|
|
@@ -29,6 +30,14 @@ function unique(values) {
|
|
|
29
30
|
return [...new Set(values.filter(Boolean))]
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function boundedFailure(component, error, impact = "evidence") {
|
|
34
|
+
return {
|
|
35
|
+
component,
|
|
36
|
+
impact,
|
|
37
|
+
message: String(error instanceof Error ? error.message : error || "unknown failure").slice(0, 500),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
function safeFull(root, relative) {
|
|
33
42
|
const base = path.resolve(root)
|
|
34
43
|
const full = path.resolve(base, relative)
|
|
@@ -149,18 +158,22 @@ function symbolHits(source, terms, limit = 12) {
|
|
|
149
158
|
function gitChangedFiles(root) {
|
|
150
159
|
const result = spawnSync(
|
|
151
160
|
"git",
|
|
152
|
-
[
|
|
161
|
+
[
|
|
162
|
+
"status",
|
|
163
|
+
"--porcelain=v1",
|
|
164
|
+
"-z",
|
|
165
|
+
"--untracked-files=all",
|
|
166
|
+
"--",
|
|
167
|
+
...sourceGitPathspecs(),
|
|
168
|
+
],
|
|
153
169
|
{ cwd: root, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 },
|
|
154
170
|
)
|
|
155
171
|
if (result.status !== 0) return []
|
|
156
|
-
return unique(
|
|
157
|
-
result.stdout
|
|
158
|
-
.
|
|
159
|
-
.
|
|
160
|
-
|
|
161
|
-
.map((value) => value.includes(" -> ") ? value.split(" -> ").at(-1) : value)
|
|
162
|
-
.map((value) => value.replaceAll("\\", "/")),
|
|
163
|
-
)
|
|
172
|
+
return unique(sourceFacingPaths(
|
|
173
|
+
workspaceStatusEntries(result.stdout)
|
|
174
|
+
.filter((entry) => entry.renameSource !== true)
|
|
175
|
+
.map((entry) => entry.file),
|
|
176
|
+
))
|
|
164
177
|
}
|
|
165
178
|
|
|
166
179
|
async function nearbyInstructions(root, declared) {
|
|
@@ -272,11 +285,15 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
272
285
|
...(task?.verification || []),
|
|
273
286
|
].filter(Boolean).join(" ")
|
|
274
287
|
|
|
288
|
+
const contextDiagnostics = []
|
|
275
289
|
const semanticSnapshot = await buildSemanticIndexCached(root, {
|
|
276
290
|
maxFiles: options.semanticMaxFiles ?? 6000,
|
|
277
291
|
maxDepth: options.semanticMaxDepth ?? 14,
|
|
278
292
|
workspaceFingerprint: reusableWorkspaceFingerprint,
|
|
279
|
-
}).catch(() =>
|
|
293
|
+
}).catch((error) => {
|
|
294
|
+
contextDiagnostics.push(boundedFailure("semantic-snapshot", error, "performance"))
|
|
295
|
+
return null
|
|
296
|
+
})
|
|
280
297
|
|
|
281
298
|
const useGraph = strategy.includes("graph")
|
|
282
299
|
const [hierarchy, semantic, graph, instructions] = await Promise.all([
|
|
@@ -285,19 +302,31 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
285
302
|
maxScopes: options.hierarchyMaxScopes ?? 6,
|
|
286
303
|
maxFiles: options.semanticMaxFiles ?? 6000,
|
|
287
304
|
builtIndex: semanticSnapshot || undefined,
|
|
288
|
-
}).catch(() =>
|
|
305
|
+
}).catch((error) => {
|
|
306
|
+
contextDiagnostics.push(boundedFailure("hierarchy", error))
|
|
307
|
+
return null
|
|
308
|
+
}),
|
|
289
309
|
querySemanticIndex(root, queryText, {
|
|
290
310
|
limit: 32,
|
|
291
311
|
maxFiles: options.semanticMaxFiles ?? 6000,
|
|
292
312
|
builtIndex: semanticSnapshot || undefined,
|
|
293
|
-
}).catch(() =>
|
|
313
|
+
}).catch((error) => {
|
|
314
|
+
contextDiagnostics.push(boundedFailure("semantic-query", error))
|
|
315
|
+
return null
|
|
316
|
+
}),
|
|
294
317
|
useGraph
|
|
295
318
|
? buildRepoGraphCached(root, {
|
|
296
319
|
maxFiles: options.maxFiles ?? 2500,
|
|
297
320
|
workspaceFingerprint: reusableWorkspaceFingerprint,
|
|
298
|
-
}).catch(() =>
|
|
321
|
+
}).catch((error) => {
|
|
322
|
+
contextDiagnostics.push(boundedFailure("repo-graph", error))
|
|
323
|
+
return { nodes: [], edges: [], hotspots: [], scannedFiles: 0, truncated: false }
|
|
324
|
+
})
|
|
299
325
|
: Promise.resolve({ nodes: [], edges: [], hotspots: [], scannedFiles: 0, truncated: false }),
|
|
300
|
-
nearbyInstructions(root, declared).catch(() =>
|
|
326
|
+
nearbyInstructions(root, declared).catch((error) => {
|
|
327
|
+
contextDiagnostics.push(boundedFailure("instructions", error))
|
|
328
|
+
return []
|
|
329
|
+
}),
|
|
301
330
|
])
|
|
302
331
|
const hierarchyScopes = hierarchy?.scopes?.map((item) => item.path) || []
|
|
303
332
|
|
|
@@ -402,11 +431,8 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
402
431
|
...instructions,
|
|
403
432
|
])
|
|
404
433
|
|
|
405
|
-
const files =
|
|
406
|
-
|
|
407
|
-
const info = await fileInfo(root, file)
|
|
408
|
-
if (info) files.push(info)
|
|
409
|
-
}
|
|
434
|
+
const files = (await Promise.all(priority.map((file) => fileInfo(root, file))))
|
|
435
|
+
.filter(Boolean)
|
|
410
436
|
|
|
411
437
|
let remaining = budget
|
|
412
438
|
const categoryRemaining = {
|
|
@@ -436,9 +462,18 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
436
462
|
})
|
|
437
463
|
}
|
|
438
464
|
|
|
465
|
+
contextDiagnostics.sort((a, b) =>
|
|
466
|
+
a.component.localeCompare(b.component) ||
|
|
467
|
+
a.impact.localeCompare(b.impact) ||
|
|
468
|
+
a.message.localeCompare(b.message)
|
|
469
|
+
)
|
|
470
|
+
const evidenceDegraded = contextDiagnostics.some((item) => item.impact === "evidence")
|
|
471
|
+
|
|
439
472
|
return {
|
|
440
473
|
schemaVersion: 4,
|
|
441
474
|
task: task?.id || null,
|
|
475
|
+
contextQuality: evidenceDegraded ? "degraded" : "full",
|
|
476
|
+
degradedComponents: contextDiagnostics,
|
|
442
477
|
strategy,
|
|
443
478
|
queryTerms: terms,
|
|
444
479
|
declared,
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto"
|
|
|
2
2
|
import { spawnSync } from "node:child_process"
|
|
3
3
|
import { lstatSync, readFileSync, readlinkSync } from "node:fs"
|
|
4
4
|
import path from "node:path"
|
|
5
|
-
import { sourceFacingPaths } from "./runtime-artifacts.mjs"
|
|
5
|
+
import { sourceFacingPaths, sourceGitPathspecs } from "./runtime-artifacts.mjs"
|
|
6
6
|
|
|
7
7
|
const PHASE_HEADING = /^\s*(?:=+\s*)?PHASE\s+(\d+)\s*(?:[—–:-]+)\s*(.+?)\s*$/i
|
|
8
8
|
const LOCAL_ENV_BASENAME = /^\.env(?:\.[^/\\]+)?$/i
|
|
@@ -187,7 +187,7 @@ export function captureInheritedDirtyState(root, options = {}) {
|
|
|
187
187
|
) {
|
|
188
188
|
statusOutput = workspaceState.statusOutput
|
|
189
189
|
} else {
|
|
190
|
-
const probe = spawnSync("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
|
|
190
|
+
const probe = spawnSync("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ...sourceGitPathspecs()], {
|
|
191
191
|
cwd: resolved,
|
|
192
192
|
encoding: "utf8",
|
|
193
193
|
maxBuffer: 16 * 1024 * 1024,
|
package/lib/memory-engine.mjs
CHANGED
|
@@ -5,6 +5,8 @@ import { spawnSync } from "node:child_process"
|
|
|
5
5
|
import path from "node:path"
|
|
6
6
|
import { evidenceExists, putEvidence } from "./evidence-store.mjs"
|
|
7
7
|
import { buildRepoGraphCached } from "./repo-graph.mjs"
|
|
8
|
+
import { sourceFacingPaths, sourceGitPathspecs } from "./runtime-artifacts.mjs"
|
|
9
|
+
import { workspaceStatusEntries } from "./workspace-fingerprint.mjs"
|
|
8
10
|
|
|
9
11
|
const MEMORY_DIR = ".ues-memory"
|
|
10
12
|
const MEMORY_FILE = "MEMORY.json"
|
|
@@ -671,21 +673,24 @@ export async function memoryStatus(root = process.cwd()) {
|
|
|
671
673
|
}
|
|
672
674
|
|
|
673
675
|
function changedFiles(root) {
|
|
674
|
-
const result = spawnSync("git", [
|
|
676
|
+
const result = spawnSync("git", [
|
|
677
|
+
"status",
|
|
678
|
+
"--porcelain=v1",
|
|
679
|
+
"-z",
|
|
680
|
+
"--untracked-files=all",
|
|
681
|
+
"--",
|
|
682
|
+
...sourceGitPathspecs(),
|
|
683
|
+
], {
|
|
675
684
|
cwd: path.resolve(root),
|
|
676
685
|
encoding: "utf8",
|
|
677
686
|
windowsHide: true,
|
|
678
687
|
maxBuffer: 4 * 1024 * 1024,
|
|
679
688
|
})
|
|
680
689
|
if (result.status !== 0) return []
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
.map((line) => line.slice(3).trim())
|
|
686
|
-
.map((value) => value.includes(" -> ") ? value.split(" -> ").at(-1) : value)
|
|
687
|
-
.map(normalizeFile),
|
|
688
|
-
).slice(0, 80)
|
|
690
|
+
const changed = workspaceStatusEntries(String(result.stdout || ""))
|
|
691
|
+
.filter((entry) => entry.renameSource !== true)
|
|
692
|
+
.map((entry) => normalizeFile(entry.file))
|
|
693
|
+
return uniq(sourceFacingPaths(changed)).slice(0, 80)
|
|
689
694
|
}
|
|
690
695
|
|
|
691
696
|
function compactVerification(value, limit = 1200) {
|
package/lib/pi-rpc-pool.mjs
CHANGED
|
@@ -469,6 +469,17 @@ export class PiRpcWorkerPool {
|
|
|
469
469
|
constructor(options = {}) {
|
|
470
470
|
this.maxWorkers = Math.max(1, Math.min(16, Number(options.maxWorkers || 8)))
|
|
471
471
|
this.workers = new Map()
|
|
472
|
+
this.reservations = new Map()
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
reserve(key) {
|
|
476
|
+
this.reservations.set(key, (this.reservations.get(key) || 0) + 1)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
release(key) {
|
|
480
|
+
const next = Math.max(0, (this.reservations.get(key) || 0) - 1)
|
|
481
|
+
if (next > 0) this.reservations.set(key, next)
|
|
482
|
+
else this.reservations.delete(key)
|
|
472
483
|
}
|
|
473
484
|
|
|
474
485
|
async pruneIdleWorkers(protectedKey = null) {
|
|
@@ -476,7 +487,7 @@ export class PiRpcWorkerPool {
|
|
|
476
487
|
let candidateKey = null
|
|
477
488
|
let candidate = null
|
|
478
489
|
for (const [key, worker] of this.workers.entries()) {
|
|
479
|
-
if (key === protectedKey) continue
|
|
490
|
+
if (key === protectedKey || this.reservations.has(key)) continue
|
|
480
491
|
if (!worker || worker.dead) {
|
|
481
492
|
candidateKey = key
|
|
482
493
|
candidate = worker
|
|
@@ -494,37 +505,56 @@ export class PiRpcWorkerPool {
|
|
|
494
505
|
}
|
|
495
506
|
|
|
496
507
|
async run(key, spec, message, options = {}) {
|
|
497
|
-
|
|
498
|
-
let
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
if (
|
|
506
|
-
|
|
508
|
+
this.reserve(key)
|
|
509
|
+
let worker = null
|
|
510
|
+
let workerReused = false
|
|
511
|
+
let staleStop = null
|
|
512
|
+
|
|
513
|
+
try {
|
|
514
|
+
worker = this.workers.get(key)
|
|
515
|
+
workerReused = Boolean(worker && !worker.dead && worker.runs > 0)
|
|
516
|
+
if (options.reuseSession === true && !workerReused) {
|
|
517
|
+
const error = new Error("Pi RPC same-session resume unavailable")
|
|
518
|
+
error.code = "UES_RPC_SESSION_UNAVAILABLE"
|
|
519
|
+
throw error
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (!worker || worker.dead) {
|
|
523
|
+
const staleWorker = worker
|
|
524
|
+
if (staleWorker) this.workers.delete(key)
|
|
525
|
+
|
|
526
|
+
// Publish the replacement synchronously before any await. Concurrent
|
|
527
|
+
// callers for the same key must see this worker rather than allocate a
|
|
528
|
+
// second untracked process while the stale worker is being reaped.
|
|
529
|
+
worker = new RpcWorker(spec)
|
|
530
|
+
workerReused = false
|
|
531
|
+
this.workers.set(key, worker)
|
|
532
|
+
if (staleWorker) staleStop = staleWorker.stop().catch(() => {})
|
|
533
|
+
} else {
|
|
534
|
+
// Refresh insertion order so the bounded worker map behaves as an LRU.
|
|
507
535
|
this.workers.delete(key)
|
|
536
|
+
this.workers.set(key, worker)
|
|
508
537
|
}
|
|
509
|
-
worker = new RpcWorker(spec)
|
|
510
|
-
workerReused = false
|
|
511
|
-
this.workers.set(key, worker)
|
|
512
|
-
await this.pruneIdleWorkers(key)
|
|
513
|
-
} else {
|
|
514
|
-
// Refresh insertion order so the bounded worker map behaves as an LRU.
|
|
515
|
-
this.workers.delete(key)
|
|
516
|
-
this.workers.set(key, worker)
|
|
517
|
-
}
|
|
518
538
|
|
|
519
|
-
|
|
520
|
-
|
|
539
|
+
// Queue the run before pruning. The reservation prevents another
|
|
540
|
+
// concurrent key from treating this not-yet-active worker as idle during
|
|
541
|
+
// the small window before RpcWorker.execute sets active=true.
|
|
542
|
+
const runPromise = worker.run(message, options)
|
|
521
543
|
await this.pruneIdleWorkers(key)
|
|
544
|
+
if (staleStop) await staleStop
|
|
545
|
+
|
|
546
|
+
const result = await runPromise
|
|
522
547
|
return { ...result, workerReused }
|
|
523
548
|
} catch (error) {
|
|
524
|
-
this.workers.
|
|
525
|
-
|
|
549
|
+
if (worker && this.workers.get(key) === worker) {
|
|
550
|
+
this.workers.delete(key)
|
|
551
|
+
await worker.stop().catch(() => {})
|
|
552
|
+
}
|
|
526
553
|
await this.pruneIdleWorkers().catch(() => {})
|
|
527
554
|
throw error
|
|
555
|
+
} finally {
|
|
556
|
+
this.release(key)
|
|
557
|
+
await this.pruneIdleWorkers(key).catch(() => {})
|
|
528
558
|
}
|
|
529
559
|
}
|
|
530
560
|
|
|
@@ -643,6 +673,7 @@ export class PiRpcWorkerPool {
|
|
|
643
673
|
return {
|
|
644
674
|
workers: this.workers.size,
|
|
645
675
|
activeWorkers: active.length,
|
|
676
|
+
reservedWorkers: this.reservations.size,
|
|
646
677
|
keys: [...this.workers.keys()],
|
|
647
678
|
active,
|
|
648
679
|
}
|
package/lib/repo-graph.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { readFile, readdir, stat } from "node:fs/promises"
|
|
2
2
|
import path from "node:path"
|
|
3
3
|
import { runtimeWorkspaceFingerprint } from "./workspace-fingerprint.mjs"
|
|
4
|
+
import { UES_RUNTIME_DIRS } from "./runtime-artifacts.mjs"
|
|
4
5
|
|
|
5
6
|
const SKIP = new Set([
|
|
6
7
|
".git", "node_modules", ".next", "dist", "build", "coverage", ".venv", "venv",
|
|
7
8
|
"Pods", "DerivedData", ".gradle", ".idea", ".cache", ".turbo", "target", "obj",
|
|
8
|
-
|
|
9
|
-
".ues-sandboxes", ".ues-memory", ".ues-evals", ".ues-services",
|
|
9
|
+
...UES_RUNTIME_DIRS,
|
|
10
10
|
])
|
|
11
11
|
|
|
12
12
|
const SOURCE_EXTENSIONS = new Set([
|
package/lib/repo-inspect.mjs
CHANGED
|
@@ -2,11 +2,12 @@ import { existsSync } from "node:fs"
|
|
|
2
2
|
import { readFile, readdir, stat } from "node:fs/promises"
|
|
3
3
|
import path from "node:path"
|
|
4
4
|
import { spawnSync } from "node:child_process"
|
|
5
|
+
import { UES_RUNTIME_DIRS, sourceGitPathspecs } from "./runtime-artifacts.mjs"
|
|
5
6
|
|
|
6
7
|
const SKIP_DIRS = new Set([
|
|
7
8
|
".git", "node_modules", ".next", "dist", "build", "coverage", ".venv", "venv",
|
|
8
9
|
"Pods", "DerivedData", ".gradle", ".idea", ".cache", ".turbo", "target", "bin", "obj",
|
|
9
|
-
|
|
10
|
+
...UES_RUNTIME_DIRS,
|
|
10
11
|
])
|
|
11
12
|
|
|
12
13
|
const TEXT_EXTENSIONS = new Set([
|
|
@@ -231,7 +232,7 @@ export async function impactMap(root = process.cwd(), query = "") {
|
|
|
231
232
|
|
|
232
233
|
export async function checkWorkingTree(root = process.cwd()) {
|
|
233
234
|
root = path.resolve(root)
|
|
234
|
-
const status = runGit(root, ["status", "--porcelain=v1", "--branch"])
|
|
235
|
+
const status = runGit(root, ["status", "--porcelain=v1", "--branch", "--", ...sourceGitPathspecs()])
|
|
235
236
|
if (status.status !== 0) {
|
|
236
237
|
return { root, git: false, error: (status.stderr || status.stdout || "").trim() }
|
|
237
238
|
}
|
package/lib/semantic-index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs"
|
|
2
2
|
import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises"
|
|
3
3
|
import path from "node:path"
|
|
4
|
+
import { UES_RUNTIME_DIRS } from "./runtime-artifacts.mjs"
|
|
4
5
|
|
|
5
6
|
const SCHEMA_VERSION = 1
|
|
6
7
|
const CACHE_DIR = ".ues-cache"
|
|
@@ -12,7 +13,7 @@ const SOURCE_EXTENSIONS = new Set([
|
|
|
12
13
|
const SKIP_DIRS = new Set([
|
|
13
14
|
".git", "node_modules", ".next", "dist", "build", "coverage", ".venv", "venv",
|
|
14
15
|
"Pods", "DerivedData", ".gradle", ".idea", ".cache", ".turbo", "target", "obj",
|
|
15
|
-
|
|
16
|
+
...UES_RUNTIME_DIRS,
|
|
16
17
|
])
|
|
17
18
|
const STOP_IDENTIFIERS = new Set([
|
|
18
19
|
"const","let","var","function","class","interface","type","export","import","from","return",
|
package/lib/task-engine.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import { capabilityFabricStatus } from "./capability-fabric.mjs"
|
|
|
20
20
|
import { assertActivePlanScope, persistPlanScope } from "./work-plan-scope.mjs"
|
|
21
21
|
import { classifyDecisionPolicy } from "./decision-policy.mjs"
|
|
22
22
|
import { readTextAuto } from "./text-encoding.mjs"
|
|
23
|
+
import { UES_RUNTIME_DIRS, sourceGitPathspecs } from "./runtime-artifacts.mjs"
|
|
23
24
|
|
|
24
25
|
const WORK_DIR = ".ues-work"
|
|
25
26
|
const STATE_SCHEMA = 4
|
|
@@ -176,7 +177,7 @@ function gitCapture(root, args) {
|
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
const FINGERPRINT_SKIP_DIRS = new Set([
|
|
179
|
-
".git",
|
|
180
|
+
".git", ...UES_RUNTIME_DIRS,
|
|
180
181
|
"node_modules", ".next", "dist", "build", "coverage", ".venv", "venv",
|
|
181
182
|
"Pods", "DerivedData", ".gradle", ".cache", ".turbo", "target", "bin", "obj",
|
|
182
183
|
])
|
|
@@ -250,17 +251,35 @@ function nonGitWorkspaceFingerprint(root, options = {}) {
|
|
|
250
251
|
|
|
251
252
|
export function workspaceFingerprint(root) {
|
|
252
253
|
root = path.resolve(root)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
|
|
255
|
+
// Fast common path: resolve worktree identity and HEAD in one Git process.
|
|
256
|
+
// Preserve the unborn-HEAD behavior by falling back to separate probes only
|
|
257
|
+
// when the combined command cannot resolve HEAD.
|
|
258
|
+
const identity = gitCapture(root, ["rev-parse", "--is-inside-work-tree", "HEAD"])
|
|
259
|
+
const identityLines = String(identity.stdout || "").trim().split(/\r?\n/).filter(Boolean)
|
|
260
|
+
let insideWorktree = identity.status === 0 && identityLines[0] === "true"
|
|
261
|
+
let headOutput = identity.status === 0 && insideWorktree
|
|
262
|
+
? (identityLines[1] || "")
|
|
263
|
+
: null
|
|
264
|
+
|
|
265
|
+
if (!insideWorktree) {
|
|
266
|
+
const inside = gitCapture(root, ["rev-parse", "--is-inside-work-tree"])
|
|
267
|
+
if (inside.status !== 0 || String(inside.stdout || "").trim() !== "true") {
|
|
268
|
+
return nonGitWorkspaceFingerprint(root)
|
|
269
|
+
}
|
|
270
|
+
insideWorktree = true
|
|
271
|
+
const head = gitCapture(root, ["rev-parse", "HEAD"])
|
|
272
|
+
headOutput = head.status === 0
|
|
273
|
+
? head.stdout
|
|
274
|
+
: "ERROR:" + (head.stderr || head.stdout || "")
|
|
256
275
|
}
|
|
257
276
|
|
|
258
|
-
const parts = []
|
|
277
|
+
const parts = [String(headOutput || "")]
|
|
278
|
+
const sourcePathspecs = sourceGitPathspecs()
|
|
259
279
|
const commands = [
|
|
260
|
-
["
|
|
261
|
-
["
|
|
262
|
-
["diff", "--binary", "--no-ext-diff", "--",
|
|
263
|
-
["diff", "--cached", "--binary", "--no-ext-diff", "--", ".", ":(exclude).ues-work", ":(exclude).ues-learning", ":(exclude).ues-dashboard", ":(exclude).ues-sandboxes", ":(exclude).ues-cache", ":(exclude).ues-traces", ":(exclude).ues-memory"],
|
|
280
|
+
["status", "--porcelain=v1", "--untracked-files=all", "--", ...sourcePathspecs],
|
|
281
|
+
["diff", "--binary", "--no-ext-diff", "--", ...sourcePathspecs],
|
|
282
|
+
["diff", "--cached", "--binary", "--no-ext-diff", "--", ...sourcePathspecs],
|
|
264
283
|
]
|
|
265
284
|
for (const args of commands) {
|
|
266
285
|
const result = gitCapture(root, args)
|
|
@@ -2,12 +2,9 @@ import { createHash } from "node:crypto"
|
|
|
2
2
|
import { lstatSync, readFileSync, readlinkSync } from "node:fs"
|
|
3
3
|
import { spawnSync } from "node:child_process"
|
|
4
4
|
import path from "node:path"
|
|
5
|
-
import {
|
|
5
|
+
import { sourceGitPathspecs } from "./runtime-artifacts.mjs"
|
|
6
6
|
|
|
7
|
-
const RUNTIME_PATHSPECS =
|
|
8
|
-
".",
|
|
9
|
-
...UES_RUNTIME_DIRS.map((dir) => `:(exclude)${dir}/**`),
|
|
10
|
-
]
|
|
7
|
+
const RUNTIME_PATHSPECS = sourceGitPathspecs()
|
|
11
8
|
|
|
12
9
|
let nonGitNonce = 0
|
|
13
10
|
|
|
@@ -132,12 +129,23 @@ function nonGitSnapshot(root) {
|
|
|
132
129
|
|
|
133
130
|
export function captureWorkspaceStateV2(root = process.cwd()) {
|
|
134
131
|
root = path.resolve(root)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
|
|
133
|
+
// Fast common path: resolve worktree identity and HEAD in one Git process.
|
|
134
|
+
// A fallback probe is only needed for an unborn/missing HEAD.
|
|
135
|
+
const identity = git(root, ["rev-parse", "--is-inside-work-tree", "HEAD"])
|
|
136
|
+
const identityLines = String(identity.stdout || "").trim().split(/\r?\n/).filter(Boolean)
|
|
137
|
+
let insideWorktree = identityLines[0] === "true"
|
|
138
|
+
let head = {
|
|
139
|
+
...identity,
|
|
140
|
+
stdout: identity.status === 0 && insideWorktree ? (identityLines[1] || "") : "",
|
|
141
|
+
}
|
|
142
|
+
if (identity.status !== 0) {
|
|
143
|
+
const inside = git(root, ["rev-parse", "--is-inside-work-tree"])
|
|
144
|
+
insideWorktree = inside.status === 0 && String(inside.stdout || "").trim() === "true"
|
|
145
|
+
head = { ...identity, stdout: "" }
|
|
138
146
|
}
|
|
147
|
+
if (!insideWorktree) return nonGitSnapshot(root)
|
|
139
148
|
|
|
140
|
-
const head = git(root, ["rev-parse", "HEAD"])
|
|
141
149
|
const status = git(root, [
|
|
142
150
|
"status",
|
|
143
151
|
"--porcelain=v1",
|
|
@@ -4,7 +4,7 @@ import { rm } from "node:fs/promises"
|
|
|
4
4
|
import { spawnSync } from "node:child_process"
|
|
5
5
|
import path from "node:path"
|
|
6
6
|
import { decodeTextBuffer } from "./text-encoding.mjs"
|
|
7
|
-
import { isUesRuntimeArtifactPath, sourceFacingPaths } from "./runtime-artifacts.mjs"
|
|
7
|
+
import { isUesRuntimeArtifactPath, sourceFacingPaths, sourceGitPathspecs } from "./runtime-artifacts.mjs"
|
|
8
8
|
|
|
9
9
|
const CODE_EXTENSIONS = new Set([
|
|
10
10
|
".js", ".cjs", ".mjs", ".jsx", ".ts", ".cts", ".mts", ".tsx",
|
|
@@ -113,7 +113,7 @@ function gitStatusEntries(root, workspaceState = null) {
|
|
|
113
113
|
) {
|
|
114
114
|
return parseGitStatusEntries(workspaceState.statusOutput)
|
|
115
115
|
}
|
|
116
|
-
const result = spawnSync("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
|
|
116
|
+
const result = spawnSync("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ...sourceGitPathspecs()], {
|
|
117
117
|
cwd: resolved,
|
|
118
118
|
encoding: "utf8",
|
|
119
119
|
windowsHide: true,
|
package/lib/worktree-sandbox.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { copyFile, mkdir, readFile, readdir, rm, unlink, writeFile } from "node:
|
|
|
4
4
|
import { spawnSync } from "node:child_process"
|
|
5
5
|
import path from "node:path"
|
|
6
6
|
import { isUesRuntimeArtifactPath, sourceFacingPaths, sourceGitPathspecs } from "./runtime-artifacts.mjs"
|
|
7
|
+
import { workspaceStatusEntries } from "./workspace-fingerprint.mjs"
|
|
7
8
|
|
|
8
9
|
function git(root, args, options = {}) {
|
|
9
10
|
return spawnSync("git", args, {
|
|
@@ -15,15 +16,20 @@ function git(root, args, options = {}) {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
function statusFiles(root) {
|
|
18
|
-
const result = git(root, [
|
|
19
|
+
const result = git(root, [
|
|
20
|
+
"status",
|
|
21
|
+
"--porcelain=v1",
|
|
22
|
+
"-z",
|
|
23
|
+
"--untracked-files=all",
|
|
24
|
+
"--",
|
|
25
|
+
...sourceGitPathspecs(),
|
|
26
|
+
])
|
|
19
27
|
if (result.status !== 0) throw new Error((result.stderr || result.stdout || "git status failed").trim())
|
|
20
|
-
return
|
|
21
|
-
.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.map((value) => value.replaceAll("\\", "/"))
|
|
26
|
-
.filter((value) => !isUesRuntimeArtifactPath(value))
|
|
28
|
+
return sourceFacingPaths(
|
|
29
|
+
workspaceStatusEntries(result.stdout)
|
|
30
|
+
.filter((entry) => entry.renameSource !== true)
|
|
31
|
+
.map((entry) => entry.file),
|
|
32
|
+
)
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
function sourceIntentBatches(values, options = {}) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-agent-skill",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.2",
|
|
4
4
|
"description": "Pi-native UES coding-agent runtime with adaptive stability, Turbo Fast Path, durable verification, managed services, and weak-model orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -450,9 +450,45 @@ export default function (pi: ExtensionAPI) {
|
|
|
450
450
|
throw new Error("unsupported ues_code action");
|
|
451
451
|
}
|
|
452
452
|
const encoded = JSON.stringify(result, null, 2);
|
|
453
|
+
const bounded = encoded.length > 32000;
|
|
454
|
+
let contextRef: string | null = null;
|
|
455
|
+
let visible = encoded;
|
|
456
|
+
if (bounded) {
|
|
457
|
+
const preserved = await compactContext(ctx.cwd, encoded, {
|
|
458
|
+
kind: "ues-code-result",
|
|
459
|
+
source: `ues_code:${params.action}`,
|
|
460
|
+
summary: `Full child code-intelligence result for ${params.action}; preserve exact JSON before model-visible bounding`,
|
|
461
|
+
}).catch(() => null);
|
|
462
|
+
contextRef = preserved?.ref || null;
|
|
463
|
+
const metadataFirst = {
|
|
464
|
+
schemaVersion: result?.schemaVersion || 1,
|
|
465
|
+
action: params.action,
|
|
466
|
+
file: result?.file || null,
|
|
467
|
+
available: result?.available ?? null,
|
|
468
|
+
provider: result?.provider || null,
|
|
469
|
+
operation: result?.operation || null,
|
|
470
|
+
reason: result?.reason || null,
|
|
471
|
+
persistent: result?.persistent ?? null,
|
|
472
|
+
pool: result?.pool || result?.lsp?.persistentPool || null,
|
|
473
|
+
bounded: true,
|
|
474
|
+
originalChars: encoded.length,
|
|
475
|
+
contextRef,
|
|
476
|
+
preview: encoded.slice(0, 22000),
|
|
477
|
+
};
|
|
478
|
+
visible = JSON.stringify(metadataFirst, null, 2) +
|
|
479
|
+
"\n...[full result preserved; use ues_code context-expand with contextRef when more evidence is needed]";
|
|
480
|
+
}
|
|
453
481
|
return {
|
|
454
|
-
content: [{ type: "text", text:
|
|
455
|
-
details: {
|
|
482
|
+
content: [{ type: "text", text: visible }],
|
|
483
|
+
details: {
|
|
484
|
+
action: params.action,
|
|
485
|
+
bounded,
|
|
486
|
+
originalChars: encoded.length,
|
|
487
|
+
contextRef,
|
|
488
|
+
provider: result?.provider || null,
|
|
489
|
+
persistent: result?.persistent ?? null,
|
|
490
|
+
pool: result?.pool || result?.lsp?.persistentPool || null,
|
|
491
|
+
},
|
|
456
492
|
};
|
|
457
493
|
} catch (error) {
|
|
458
494
|
return {
|