@fro.bot/systematic 3.9.1 → 3.10.1
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/HARNESSES.md +1 -1
- package/agents/review/architecture-strategist.md +1 -1
- package/agents/review/code-simplicity-reviewer.md +1 -1
- package/agents/review/pattern-recognition-specialist.md +1 -1
- package/dist/index.js +119 -496
- package/dist/lib/agent-overlays.d.ts +0 -1
- package/dist/lib/config-handler.d.ts +0 -3
- package/package.json +1 -1
- package/skills/ce-review/SKILL.md +55 -42
- package/skills/ce-review/references/findings-schema.json +321 -117
- package/skills/ce-review/references/persona-catalog.md +8 -0
- package/skills/ce-review/references/review-output-template.md +65 -3
- package/skills/ce-review/references/subagent-template.md +9 -20
- package/dist/lib/model-availability.d.ts +0 -83
- package/dist/lib/source-model-defaults.d.ts +0 -117
package/dist/index.js
CHANGED
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
} from "./index-y8nc60tz.js";
|
|
18
18
|
|
|
19
19
|
// src/index.ts
|
|
20
|
-
import { createHash as
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
import { fileURLToPath as
|
|
20
|
+
import { createHash as createHash4 } from "crypto";
|
|
21
|
+
import fs10 from "fs";
|
|
22
|
+
import path8 from "path";
|
|
23
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "url";
|
|
24
24
|
|
|
25
25
|
// src/lib/bootstrap.ts
|
|
26
26
|
import fs2 from "fs";
|
|
@@ -136,217 +136,27 @@ ${skillUsage}${profileSection}
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// src/lib/config-handler.ts
|
|
139
|
-
import
|
|
140
|
-
import
|
|
141
|
-
import
|
|
139
|
+
import fs5 from "fs";
|
|
140
|
+
import os2 from "os";
|
|
141
|
+
import path4 from "path";
|
|
142
142
|
|
|
143
143
|
// src/lib/agent-overlays.ts
|
|
144
144
|
import fs3 from "fs";
|
|
145
|
-
import
|
|
146
|
-
|
|
147
|
-
// src/lib/source-model-defaults.ts
|
|
148
|
-
import * as path2 from "path";
|
|
149
|
-
import { fileURLToPath } from "url";
|
|
150
|
-
var __filename2 = fileURLToPath(import.meta.url);
|
|
151
|
-
var __dirname2 = path2.dirname(__filename2);
|
|
152
|
-
var packageRoot = path2.resolve(__dirname2, "..", "..");
|
|
153
|
-
var bundledAgentsDir = path2.join(packageRoot, "agents");
|
|
154
|
-
var ProviderID = exports_external.union([
|
|
155
|
-
exports_external.literal("vercel"),
|
|
156
|
-
exports_external.literal("opencode"),
|
|
157
|
-
exports_external.literal("github-copilot"),
|
|
158
|
-
exports_external.literal("opencode-go"),
|
|
159
|
-
exports_external.literal("openai"),
|
|
160
|
-
exports_external.literal("anthropic"),
|
|
161
|
-
exports_external.literal("google")
|
|
162
|
-
]);
|
|
163
|
-
var variantSchema = exports_external.string().min(1, "variant must be a non-empty string").max(128, "variant must be at most 128 characters").regex(/^\S+$/, "variant must not contain whitespace");
|
|
164
|
-
var tableCellSchema = exports_external.string().min(1, "must be a non-empty string").regex(/^[^|\r\n]+$/, "must not contain pipe (|) or newline characters \u2014 these break Markdown table generation");
|
|
165
|
-
var ModelEntrySchema = exports_external.object({
|
|
166
|
-
model: exports_external.string().min(1, "model must be a non-empty string"),
|
|
167
|
-
variant: variantSchema.optional()
|
|
168
|
-
}).strict();
|
|
169
|
-
var ProviderEntrySchema = exports_external.object({
|
|
170
|
-
provider: ProviderID,
|
|
171
|
-
models: exports_external.array(ModelEntrySchema).min(1, "models must be non-empty \u2014 every provider entry must list at least one model")
|
|
172
|
-
}).strict().refine((entry) => {
|
|
173
|
-
const seen = new Set;
|
|
174
|
-
for (const m of entry.models) {
|
|
175
|
-
const key = `${m.model}::${m.variant ?? ""}`;
|
|
176
|
-
if (seen.has(key))
|
|
177
|
-
return false;
|
|
178
|
-
seen.add(key);
|
|
179
|
-
}
|
|
180
|
-
return true;
|
|
181
|
-
}, {
|
|
182
|
-
message: "duplicate (model, variant) pair within a provider entry \u2014 each model+variant combination must be unique"
|
|
183
|
-
});
|
|
184
|
-
var CategoryDefaultSchema = exports_external.object({
|
|
185
|
-
rationale: tableCellSchema,
|
|
186
|
-
whenToOverride: tableCellSchema.optional(),
|
|
187
|
-
providers: exports_external.array(ProviderEntrySchema).min(1, "providers must be non-empty \u2014 every category must list at least one provider")
|
|
188
|
-
}).strict().refine((cat) => {
|
|
189
|
-
const seen = new Set;
|
|
190
|
-
for (const p of cat.providers) {
|
|
191
|
-
if (seen.has(p.provider))
|
|
192
|
-
return false;
|
|
193
|
-
seen.add(p.provider);
|
|
194
|
-
}
|
|
195
|
-
return true;
|
|
196
|
-
}, {
|
|
197
|
-
message: "duplicate provider ID within a category \u2014 each provider must appear at most once per category"
|
|
198
|
-
});
|
|
199
|
-
var SourceCategoryDefaultsSchema = exports_external.record(exports_external.string(), CategoryDefaultSchema);
|
|
200
|
-
var SOURCE_CATEGORY_MODEL_DEFAULTS = {
|
|
201
|
-
design: {
|
|
202
|
-
rationale: "High-judgment UX, product, and design work benefits from a strong general reasoning model with broad creative capability.",
|
|
203
|
-
whenToOverride: "Override to a faster/cheaper model when design tasks are primarily templating or low-stakes layout work.",
|
|
204
|
-
providers: [
|
|
205
|
-
{
|
|
206
|
-
provider: "github-copilot",
|
|
207
|
-
models: [{ model: "gemini-3.1-pro-preview" }]
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
provider: "openai",
|
|
211
|
-
models: [{ model: "gpt-5.5", variant: "high" }]
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
provider: "anthropic",
|
|
215
|
-
models: [{ model: "claude-opus-4-7", variant: "max" }]
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
provider: "vercel",
|
|
219
|
-
models: [{ model: "v0-1.5-md" }]
|
|
220
|
-
}
|
|
221
|
-
]
|
|
222
|
-
},
|
|
223
|
-
"document-review": {
|
|
224
|
-
rationale: "Requirements and plan critique benefit from the strongest nuanced reasoning to surface non-obvious gaps and contradictions.",
|
|
225
|
-
providers: [
|
|
226
|
-
{
|
|
227
|
-
provider: "anthropic",
|
|
228
|
-
models: [{ model: "claude-opus-4-7", variant: "max" }]
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
provider: "openai",
|
|
232
|
-
models: [{ model: "gpt-5.5", variant: "high" }]
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
provider: "github-copilot",
|
|
236
|
-
models: [{ model: "gemini-3.1-pro-preview" }]
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
provider: "opencode",
|
|
240
|
-
models: [{ model: "claude-sonnet-4-6" }]
|
|
241
|
-
}
|
|
242
|
-
]
|
|
243
|
-
},
|
|
244
|
-
research: {
|
|
245
|
-
rationale: "Tool-heavy synthesis and source evaluation benefit from a strong general reasoning model with broad knowledge.",
|
|
246
|
-
providers: [
|
|
247
|
-
{
|
|
248
|
-
provider: "openai",
|
|
249
|
-
models: [{ model: "gpt-5.4-mini" }]
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
provider: "anthropic",
|
|
253
|
-
models: [{ model: "claude-sonnet-4-6" }]
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
provider: "github-copilot",
|
|
257
|
-
models: [{ model: "gemini-3.1-pro-preview" }]
|
|
258
|
-
},
|
|
259
|
-
{
|
|
260
|
-
provider: "opencode",
|
|
261
|
-
models: [{ model: "claude-sonnet-4-6" }]
|
|
262
|
-
}
|
|
263
|
-
]
|
|
264
|
-
},
|
|
265
|
-
review: {
|
|
266
|
-
rationale: "Code, security, and adversarial review benefits from the strongest reasoning to catch subtle bugs and security issues.",
|
|
267
|
-
whenToOverride: "Override to a faster model when review tasks are primarily style or formatting checks rather than correctness or security analysis.",
|
|
268
|
-
providers: [
|
|
269
|
-
{
|
|
270
|
-
provider: "anthropic",
|
|
271
|
-
models: [{ model: "claude-sonnet-4-6" }]
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
provider: "openai",
|
|
275
|
-
models: [{ model: "gpt-5.3-codex" }]
|
|
276
|
-
},
|
|
277
|
-
{
|
|
278
|
-
provider: "github-copilot",
|
|
279
|
-
models: [{ model: "gemini-3.1-pro-preview" }]
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
provider: "opencode",
|
|
283
|
-
models: [{ model: "claude-sonnet-4-6" }]
|
|
284
|
-
}
|
|
285
|
-
]
|
|
286
|
-
},
|
|
287
|
-
workflow: {
|
|
288
|
-
rationale: "Orchestration and bounded implementation tasks should default cheaper and faster; strong reasoning is rarely needed for routing decisions.",
|
|
289
|
-
providers: [
|
|
290
|
-
{
|
|
291
|
-
provider: "openai",
|
|
292
|
-
models: [{ model: "gpt-5.4-mini" }]
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
provider: "anthropic",
|
|
296
|
-
models: [{ model: "claude-sonnet-4-6" }]
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
provider: "opencode",
|
|
300
|
-
models: [{ model: "claude-haiku-4-5" }]
|
|
301
|
-
},
|
|
302
|
-
{
|
|
303
|
-
provider: "opencode-go",
|
|
304
|
-
models: [{ model: "claude-haiku-4-5" }]
|
|
305
|
-
}
|
|
306
|
-
]
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
function resolveSourceModel(category, availabilitySet) {
|
|
310
|
-
const categoryDefault = SOURCE_CATEGORY_MODEL_DEFAULTS[category];
|
|
311
|
-
if (!categoryDefault) {
|
|
312
|
-
throw new Error(`resolveSourceModel: unknown category "${category}". Valid categories: ${Object.keys(SOURCE_CATEGORY_MODEL_DEFAULTS).join(", ")}`);
|
|
313
|
-
}
|
|
314
|
-
for (const providerEntry of categoryDefault.providers) {
|
|
315
|
-
for (const modelEntry of providerEntry.models) {
|
|
316
|
-
const key = `${providerEntry.provider}/${modelEntry.model}`;
|
|
317
|
-
if (availabilitySet.has(key)) {
|
|
318
|
-
return {
|
|
319
|
-
provider: providerEntry.provider,
|
|
320
|
-
model: modelEntry.model,
|
|
321
|
-
variant: modelEntry.variant
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
const firstProvider = categoryDefault.providers[0];
|
|
327
|
-
const firstModel = firstProvider.models[0];
|
|
328
|
-
return {
|
|
329
|
-
provider: firstProvider.provider,
|
|
330
|
-
model: firstModel.model,
|
|
331
|
-
variant: firstModel.variant
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// src/lib/agent-overlays.ts
|
|
145
|
+
import path2 from "path";
|
|
336
146
|
function buildBundledAgentInventory(agentsDir, disabledAgents) {
|
|
337
147
|
const categories = readCategoryDirs(agentsDir);
|
|
338
148
|
const agentsByQualifiedId = {};
|
|
339
149
|
const stemCategories = new Map;
|
|
340
150
|
const disabledSet = new Set(disabledAgents);
|
|
341
151
|
for (const category of categories) {
|
|
342
|
-
for (const fileName of readMarkdownFiles(
|
|
152
|
+
for (const fileName of readMarkdownFiles(path2.join(agentsDir, category))) {
|
|
343
153
|
const key = fileName.replace(/\.md$/, "");
|
|
344
154
|
const id = `${category}/${key}`;
|
|
345
155
|
agentsByQualifiedId[id] = {
|
|
346
156
|
id,
|
|
347
157
|
key,
|
|
348
158
|
category,
|
|
349
|
-
file:
|
|
159
|
+
file: path2.join(agentsDir, category, fileName),
|
|
350
160
|
disabled: disabledSet.has(key) || disabledSet.has(id)
|
|
351
161
|
};
|
|
352
162
|
const existing = stemCategories.get(key) ?? [];
|
|
@@ -383,12 +193,6 @@ function resolveAgentOverlaySet(overlays) {
|
|
|
383
193
|
categoriesByKey: new Map(overlays.categories.map((overlay) => [overlay.key, overlay]))
|
|
384
194
|
};
|
|
385
195
|
}
|
|
386
|
-
function assertSourceCategoryModelCoverage(categories) {
|
|
387
|
-
const missingCategories = categories.filter((category) => !Object.hasOwn(SOURCE_CATEGORY_MODEL_DEFAULTS, category));
|
|
388
|
-
if (missingCategories.length > 0) {
|
|
389
|
-
throw new Error(`Source category model defaults missing intentional coverage for: ${missingCategories.join(", ")}`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
196
|
function validateExactAgentOverlays(inventory, overlays, nativeAgents, enabledSkills) {
|
|
393
197
|
const result = [];
|
|
394
198
|
const seenTargets = new Map;
|
|
@@ -490,174 +294,9 @@ function throwConfigError(sourcePath, keyPath, message) {
|
|
|
490
294
|
throw new Error(`Invalid Systematic config in ${sourcePath}: ${keyPath} ${message}`);
|
|
491
295
|
}
|
|
492
296
|
|
|
493
|
-
// src/lib/model-availability.ts
|
|
494
|
-
import { createHash } from "crypto";
|
|
495
|
-
import fs4 from "fs";
|
|
496
|
-
import os2 from "os";
|
|
497
|
-
import path4 from "path";
|
|
498
|
-
function emptyAvailability() {
|
|
499
|
-
return { status: "unknown", models: new Set };
|
|
500
|
-
}
|
|
501
|
-
var MAX_CACHE_FILE_BYTES = 16 * 1024 * 1024;
|
|
502
|
-
var DEFAULT_API_TIMEOUT_MS = 1500;
|
|
503
|
-
var MODELS_JSON_FILENAME = "models.json";
|
|
504
|
-
var availabilityCache = new WeakMap;
|
|
505
|
-
function resolveCacheDir() {
|
|
506
|
-
const xdgCacheHome = process.env.XDG_CACHE_HOME?.trim();
|
|
507
|
-
const cacheBase = xdgCacheHome && path4.isAbsolute(xdgCacheHome) ? xdgCacheHome : path4.join(os2.homedir(), ".cache");
|
|
508
|
-
return path4.join(cacheBase, "opencode");
|
|
509
|
-
}
|
|
510
|
-
function fastHash(input) {
|
|
511
|
-
return createHash("sha1").update(input).digest("hex");
|
|
512
|
-
}
|
|
513
|
-
function isProviderRecord(value) {
|
|
514
|
-
if (!isRecord(value))
|
|
515
|
-
return false;
|
|
516
|
-
for (const entry of Object.values(value)) {
|
|
517
|
-
if (!isRecord(entry))
|
|
518
|
-
return false;
|
|
519
|
-
if (!isRecord(entry.models))
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
522
|
-
return true;
|
|
523
|
-
}
|
|
524
|
-
function readModelsFromCache(filePath) {
|
|
525
|
-
let fd;
|
|
526
|
-
try {
|
|
527
|
-
fd = fs4.openSync(filePath, "r");
|
|
528
|
-
} catch {
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
let raw;
|
|
532
|
-
try {
|
|
533
|
-
let stat;
|
|
534
|
-
try {
|
|
535
|
-
stat = fs4.fstatSync(fd);
|
|
536
|
-
} catch {
|
|
537
|
-
return null;
|
|
538
|
-
}
|
|
539
|
-
if (!stat.isFile())
|
|
540
|
-
return null;
|
|
541
|
-
if (stat.size === 0)
|
|
542
|
-
return null;
|
|
543
|
-
if (stat.size > MAX_CACHE_FILE_BYTES) {
|
|
544
|
-
console.warn(`[systematic] models.json at ${filePath} is ${stat.size} bytes (>${MAX_CACHE_FILE_BYTES}); treating as cache miss.`);
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
const buffer = Buffer.alloc(stat.size);
|
|
548
|
-
let bytesRead;
|
|
549
|
-
try {
|
|
550
|
-
bytesRead = fs4.readSync(fd, buffer, 0, stat.size, 0);
|
|
551
|
-
} catch {
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
if (bytesRead !== stat.size)
|
|
555
|
-
return null;
|
|
556
|
-
raw = buffer.toString("utf8");
|
|
557
|
-
} finally {
|
|
558
|
-
try {
|
|
559
|
-
fs4.closeSync(fd);
|
|
560
|
-
} catch {}
|
|
561
|
-
}
|
|
562
|
-
if (raw.trim().length === 0)
|
|
563
|
-
return null;
|
|
564
|
-
let parsed;
|
|
565
|
-
try {
|
|
566
|
-
parsed = JSON.parse(raw);
|
|
567
|
-
} catch {
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
if (!isProviderRecord(parsed)) {
|
|
571
|
-
console.warn(`[systematic] models.json schema mismatch at ${filePath}; treating as cache miss. Upstream shape may have drifted.`);
|
|
572
|
-
return null;
|
|
573
|
-
}
|
|
574
|
-
const result = new Set;
|
|
575
|
-
for (const [providerId, providerData] of Object.entries(parsed)) {
|
|
576
|
-
for (const modelId of Object.keys(providerData.models)) {
|
|
577
|
-
result.add(`${providerId}/${modelId}`);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
return result;
|
|
581
|
-
}
|
|
582
|
-
function readFallbackCache() {
|
|
583
|
-
const cacheDir = resolveCacheDir();
|
|
584
|
-
const openCodeModelsUrl = process.env.OPENCODE_MODELS_URL?.trim();
|
|
585
|
-
if (openCodeModelsUrl) {
|
|
586
|
-
const urlDerivedPath = path4.join(cacheDir, `models-${fastHash(openCodeModelsUrl)}.json`);
|
|
587
|
-
const urlResult = readModelsFromCache(urlDerivedPath);
|
|
588
|
-
if (urlResult !== null && urlResult.size > 0) {
|
|
589
|
-
return { status: "cache", models: urlResult };
|
|
590
|
-
}
|
|
591
|
-
return emptyAvailability();
|
|
592
|
-
}
|
|
593
|
-
const defaultPath = path4.join(cacheDir, MODELS_JSON_FILENAME);
|
|
594
|
-
const defaultResult = readModelsFromCache(defaultPath);
|
|
595
|
-
if (defaultResult !== null && defaultResult.size > 0) {
|
|
596
|
-
return { status: "cache", models: defaultResult };
|
|
597
|
-
}
|
|
598
|
-
return emptyAvailability();
|
|
599
|
-
}
|
|
600
|
-
function buildSetFromProviders(providers) {
|
|
601
|
-
const result = new Set;
|
|
602
|
-
for (const provider of providers) {
|
|
603
|
-
for (const modelId of Object.keys(provider.models)) {
|
|
604
|
-
result.add(`${provider.id}/${modelId}`);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return result;
|
|
608
|
-
}
|
|
609
|
-
async function getAvailableModels(client, options = {}) {
|
|
610
|
-
const cached = availabilityCache.get(client);
|
|
611
|
-
if (cached !== undefined) {
|
|
612
|
-
return cached;
|
|
613
|
-
}
|
|
614
|
-
const cacheAndReturn = (envelope) => {
|
|
615
|
-
if (envelope.status !== "unknown") {
|
|
616
|
-
availabilityCache.set(client, envelope);
|
|
617
|
-
}
|
|
618
|
-
return envelope;
|
|
619
|
-
};
|
|
620
|
-
const timeoutMs = options.apiTimeoutMs === undefined ? DEFAULT_API_TIMEOUT_MS : options.apiTimeoutMs;
|
|
621
|
-
if (typeof client.config?.providers !== "function") {
|
|
622
|
-
return cacheAndReturn(readFallbackCache());
|
|
623
|
-
}
|
|
624
|
-
const apiCall = client.config.providers();
|
|
625
|
-
let response;
|
|
626
|
-
try {
|
|
627
|
-
if (timeoutMs === null) {
|
|
628
|
-
response = await apiCall;
|
|
629
|
-
} else {
|
|
630
|
-
const TIMEOUT_SENTINEL = Symbol("timeout");
|
|
631
|
-
const timeoutPromise = new Promise((resolve2) => {
|
|
632
|
-
const timer = setTimeout(() => resolve2(TIMEOUT_SENTINEL), timeoutMs);
|
|
633
|
-
timer.unref?.();
|
|
634
|
-
});
|
|
635
|
-
const raced = await Promise.race([apiCall, timeoutPromise]);
|
|
636
|
-
if (raced === TIMEOUT_SENTINEL) {
|
|
637
|
-
console.warn(`[systematic] client.config.providers() exceeded ${timeoutMs}ms; falling back to models.json cache.`);
|
|
638
|
-
return cacheAndReturn(readFallbackCache());
|
|
639
|
-
}
|
|
640
|
-
response = raced;
|
|
641
|
-
}
|
|
642
|
-
} catch {
|
|
643
|
-
return cacheAndReturn(readFallbackCache());
|
|
644
|
-
}
|
|
645
|
-
if (response.error !== undefined || response.data === undefined) {
|
|
646
|
-
return cacheAndReturn(readFallbackCache());
|
|
647
|
-
}
|
|
648
|
-
const models = buildSetFromProviders(response.data.providers);
|
|
649
|
-
if (models.size === 0) {
|
|
650
|
-
return cacheAndReturn(emptyAvailability());
|
|
651
|
-
}
|
|
652
|
-
return cacheAndReturn({
|
|
653
|
-
status: "api",
|
|
654
|
-
models
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
|
|
658
297
|
// src/lib/skill-loader.ts
|
|
659
|
-
import
|
|
660
|
-
import
|
|
298
|
+
import fs4 from "fs";
|
|
299
|
+
import path3 from "path";
|
|
661
300
|
var SKILL_PREFIX = "systematic:";
|
|
662
301
|
var SKILL_DESCRIPTION_PREFIX = "(Systematic) ";
|
|
663
302
|
function formatSkillCommandName(name2) {
|
|
@@ -674,7 +313,7 @@ function formatSkillDescription(description, fallbackName) {
|
|
|
674
313
|
return `${SKILL_DESCRIPTION_PREFIX}${desc}`;
|
|
675
314
|
}
|
|
676
315
|
function wrapSkillTemplate(skillPath, body2) {
|
|
677
|
-
const skillDir =
|
|
316
|
+
const skillDir = path3.dirname(skillPath);
|
|
678
317
|
return `<skill-instruction>
|
|
679
318
|
Base directory for this skill: ${skillDir}/
|
|
680
319
|
File references (@path) in this skill are relative to this directory.
|
|
@@ -692,7 +331,7 @@ function extractSkillBody(wrappedTemplate) {
|
|
|
692
331
|
}
|
|
693
332
|
function loadSkill(skillInfo) {
|
|
694
333
|
try {
|
|
695
|
-
const content =
|
|
334
|
+
const content = fs4.readFileSync(skillInfo.skillFile, "utf8");
|
|
696
335
|
const { body: body2 } = parseFrontmatter(content);
|
|
697
336
|
const wrappedTemplate = wrapSkillTemplate(skillInfo.skillFile, body2);
|
|
698
337
|
return {
|
|
@@ -750,7 +389,7 @@ function formatAgentDescription(name2, description) {
|
|
|
750
389
|
}
|
|
751
390
|
function loadAgentAsConfig(agentInfo) {
|
|
752
391
|
try {
|
|
753
|
-
const content =
|
|
392
|
+
const content = fs5.readFileSync(agentInfo.file, "utf8");
|
|
754
393
|
const {
|
|
755
394
|
description,
|
|
756
395
|
prompt,
|
|
@@ -799,7 +438,7 @@ function loadAgentAsConfig(agentInfo) {
|
|
|
799
438
|
}
|
|
800
439
|
function loadCommandAsConfig(commandInfo) {
|
|
801
440
|
try {
|
|
802
|
-
const content =
|
|
441
|
+
const content = fs5.readFileSync(commandInfo.file, "utf8");
|
|
803
442
|
const { name: name2, description, agent, model, subtask } = extractCommandFrontmatter(content);
|
|
804
443
|
const { body: body2 } = parseFrontmatter(content);
|
|
805
444
|
const cleanName = commandInfo.name.replace(/^\//, "");
|
|
@@ -839,7 +478,7 @@ function loadSkillAsCommand(loaded) {
|
|
|
839
478
|
config.subtask = loaded.subtask;
|
|
840
479
|
return config;
|
|
841
480
|
}
|
|
842
|
-
function collectAgents(dir, disabledAgents, nativeAgents, overlays
|
|
481
|
+
function collectAgents(dir, disabledAgents, nativeAgents, overlays) {
|
|
843
482
|
const agents = {};
|
|
844
483
|
const agentList = findAgentsInDir(dir);
|
|
845
484
|
const disabledSet = new Set(disabledAgents);
|
|
@@ -854,12 +493,12 @@ function collectAgents(dir, disabledAgents, nativeAgents, overlays, availability
|
|
|
854
493
|
continue;
|
|
855
494
|
const config = loadAgentAsConfig(agentInfo);
|
|
856
495
|
if (config) {
|
|
857
|
-
agents[agentInfo.name] = applyAgentOverlays(config, agentInfo, overlays
|
|
496
|
+
agents[agentInfo.name] = applyAgentOverlays(config, agentInfo, overlays);
|
|
858
497
|
}
|
|
859
498
|
}
|
|
860
499
|
return agents;
|
|
861
500
|
}
|
|
862
|
-
function applyAgentOverlays(config, agentInfo, overlays
|
|
501
|
+
function applyAgentOverlays(config, agentInfo, overlays) {
|
|
863
502
|
const id = agentInfo.category ? `${agentInfo.category}/${agentInfo.name}` : agentInfo.name;
|
|
864
503
|
const categoryOverlay = agentInfo.category ? overlays.categoriesByKey.get(agentInfo.category) : undefined;
|
|
865
504
|
const exactOverlay = overlays.agentsByTargetId.get(id);
|
|
@@ -869,23 +508,11 @@ function applyAgentOverlays(config, agentInfo, overlays, availabilitySet) {
|
|
|
869
508
|
if (hasPermissionOverlay && isRecord(config.permission)) {
|
|
870
509
|
addPermissionRules(permissionRules, config.permission);
|
|
871
510
|
}
|
|
872
|
-
applySourceModelDefault(result, agentInfo, availabilitySet);
|
|
873
511
|
applyAgentOverlay(result, categoryOverlay?.value, permissionRules);
|
|
874
512
|
applyAgentOverlay(result, exactOverlay?.value, permissionRules);
|
|
875
513
|
applyPermissionOverlay(result, permissionRules, hasPermissionOverlay);
|
|
876
514
|
return result;
|
|
877
515
|
}
|
|
878
|
-
function applySourceModelDefault(target, agentInfo, availabilitySet) {
|
|
879
|
-
if (!agentInfo.category || availabilitySet === undefined)
|
|
880
|
-
return;
|
|
881
|
-
const resolved = resolveSourceModel(agentInfo.category, availabilitySet);
|
|
882
|
-
target.model = `${resolved.provider}/${resolved.model}`;
|
|
883
|
-
if (resolved.variant !== undefined) {
|
|
884
|
-
target.variant = resolved.variant;
|
|
885
|
-
} else {
|
|
886
|
-
delete target.variant;
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
516
|
function applyAgentOverlay(target, overlay, permissionRules) {
|
|
890
517
|
if (overlay === undefined)
|
|
891
518
|
return;
|
|
@@ -1058,9 +685,9 @@ function collectEnabledSkillNames(dir, disabledSkills) {
|
|
|
1058
685
|
return findSkillsInDir(dir).filter((skillInfo) => !disabledSet.has(skillInfo.name)).map((skillInfo) => skillInfo.name);
|
|
1059
686
|
}
|
|
1060
687
|
function createConfigHandler(deps) {
|
|
1061
|
-
const { directory, bundledSkillsDir, bundledAgentsDir
|
|
1062
|
-
const homeDir = deps.homeDir ??
|
|
1063
|
-
const opencodeConfigDir = deps.opencodeConfigDir ??
|
|
688
|
+
const { directory, bundledSkillsDir, bundledAgentsDir, bundledCommandsDir } = deps;
|
|
689
|
+
const homeDir = deps.homeDir ?? os2.homedir();
|
|
690
|
+
const opencodeConfigDir = deps.opencodeConfigDir ?? path4.join(homeDir, ".config/opencode");
|
|
1064
691
|
const opencodeConfigDirOverride = process.env.OPENCODE_CONFIG_DIR?.trim() ? process.env.OPENCODE_CONFIG_DIR : undefined;
|
|
1065
692
|
return async (config) => {
|
|
1066
693
|
const { config: systematicConfig, overlays } = loadConfigWithSources(directory);
|
|
@@ -1069,10 +696,7 @@ function createConfigHandler(deps) {
|
|
|
1069
696
|
const nativeAgents = Object.fromEntries(Object.entries(existingAgents).filter(([, agent]) => !isSystematicAgentConfig(agent)));
|
|
1070
697
|
const bundledSkills = collectSkillsAsCommands(bundledSkillsDir, systematicConfig.disabled_skills);
|
|
1071
698
|
const enabledSkillNames = collectEnabledSkillNames(bundledSkillsDir, systematicConfig.disabled_skills);
|
|
1072
|
-
const inventory = buildBundledAgentInventory(
|
|
1073
|
-
const availability = deps.client ? await getAvailableModels(deps.client) : undefined;
|
|
1074
|
-
const availabilitySet = availability && availability.status !== "unknown" ? availability.models : undefined;
|
|
1075
|
-
assertSourceCategoryModelCoverage(inventory.categories);
|
|
699
|
+
const inventory = buildBundledAgentInventory(bundledAgentsDir, systematicConfig.disabled_agents);
|
|
1076
700
|
const validatedOverlays = validateAgentOverlays({
|
|
1077
701
|
inventory,
|
|
1078
702
|
overlays,
|
|
@@ -1080,7 +704,7 @@ function createConfigHandler(deps) {
|
|
|
1080
704
|
enabledSkills: enabledSkillNames
|
|
1081
705
|
});
|
|
1082
706
|
const resolvedOverlays = resolveAgentOverlaySet(validatedOverlays);
|
|
1083
|
-
const bundledAgents = collectAgents(
|
|
707
|
+
const bundledAgents = collectAgents(bundledAgentsDir, systematicConfig.disabled_agents, nativeAgents, resolvedOverlays);
|
|
1084
708
|
const bundledCommands = collectCommands(bundledCommandsDir, systematicConfig.disabled_commands);
|
|
1085
709
|
const discoveredSkillCommands = systematicConfig.skills_as_commands !== false ? collectDiscoveredSkillsAsCommands(directory, homeDir, opencodeConfigDir, opencodeConfigDirOverride, systematicConfig.disabled_commands) : {};
|
|
1086
710
|
const bundledAgentKeys = new Set(Object.keys(bundledAgents));
|
|
@@ -1106,21 +730,21 @@ function registerSkillsPaths(config, skillsDir) {
|
|
|
1106
730
|
};
|
|
1107
731
|
}
|
|
1108
732
|
function removeSystematicSkillPaths(paths) {
|
|
1109
|
-
return paths.filter((
|
|
733
|
+
return paths.filter((path5) => !isSystematicSkillPath(path5));
|
|
1110
734
|
}
|
|
1111
|
-
function isSystematicSkillPath(
|
|
1112
|
-
const normalizedPath = normalizePath(
|
|
735
|
+
function isSystematicSkillPath(path5) {
|
|
736
|
+
const normalizedPath = normalizePath(path5);
|
|
1113
737
|
return normalizedPath.endsWith("/.config/opencode/systematic/skills") || normalizedPath.endsWith("/.cache/opencode/systematic/skills") || normalizedPath.endsWith("/.local/share/opencode/systematic/skills") || normalizedPath.endsWith("/.opencode/systematic/skills") || /(?:^|\/)\.cache\/opencode\/packages\/@fro\.bot\/systematic@[^/]+\/node_modules\/@fro\.bot\/systematic\/skills(?:$|\/)/u.test(normalizedPath);
|
|
1114
738
|
}
|
|
1115
|
-
function normalizePath(
|
|
1116
|
-
return
|
|
739
|
+
function normalizePath(path5) {
|
|
740
|
+
return path5.replaceAll("\\", "/").replace(/\/+$/u, "");
|
|
1117
741
|
}
|
|
1118
742
|
|
|
1119
743
|
// src/lib/opencode-operation-observer.ts
|
|
1120
744
|
import { spawnSync } from "child_process";
|
|
1121
|
-
import { createHash
|
|
1122
|
-
import
|
|
1123
|
-
import
|
|
745
|
+
import { createHash } from "crypto";
|
|
746
|
+
import fs6 from "fs";
|
|
747
|
+
import path5 from "path";
|
|
1124
748
|
var DEFAULT_LIMITS = {
|
|
1125
749
|
maxCommandOutputBytes: 1048576,
|
|
1126
750
|
maxCommandTimeoutMs: 5000,
|
|
@@ -1141,7 +765,7 @@ function unavailable(result) {
|
|
|
1141
765
|
return { status: "unavailable", reasonCode: result.reasonCode };
|
|
1142
766
|
}
|
|
1143
767
|
function digest(domain, facts) {
|
|
1144
|
-
const hash =
|
|
768
|
+
const hash = createHash("sha256");
|
|
1145
769
|
hash.update(`systematic:opencode-observer:${domain}:v1\x00`);
|
|
1146
770
|
for (const fact of facts) {
|
|
1147
771
|
hash.update(fact);
|
|
@@ -1232,11 +856,11 @@ function parseStageEntries(output, maxPathBytes) {
|
|
|
1232
856
|
return entries.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
1233
857
|
}
|
|
1234
858
|
function safePath(root, relativePath) {
|
|
1235
|
-
if (
|
|
859
|
+
if (path5.isAbsolute(relativePath))
|
|
1236
860
|
return;
|
|
1237
|
-
const absolutePath =
|
|
1238
|
-
const relative =
|
|
1239
|
-
if (relative === "" || relative === ".." || relative.startsWith(`..${
|
|
861
|
+
const absolutePath = path5.resolve(root, relativePath);
|
|
862
|
+
const relative = path5.relative(root, absolutePath);
|
|
863
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path5.sep}`)) {
|
|
1240
864
|
return;
|
|
1241
865
|
}
|
|
1242
866
|
return absolutePath;
|
|
@@ -1259,25 +883,25 @@ function runCommand(runner, args2, cwd, limits) {
|
|
|
1259
883
|
}
|
|
1260
884
|
return { status: "ok", output: { stdout: result.stdout } };
|
|
1261
885
|
}
|
|
1262
|
-
function canonicalPath(filePath, realPath =
|
|
886
|
+
function canonicalPath(filePath, realPath = fs6.realpathSync) {
|
|
1263
887
|
try {
|
|
1264
888
|
return realPath(filePath);
|
|
1265
889
|
} catch {
|
|
1266
890
|
return;
|
|
1267
891
|
}
|
|
1268
892
|
}
|
|
1269
|
-
function requiredGitPath(runner, args2, cwd, limits, realPath =
|
|
893
|
+
function requiredGitPath(runner, args2, cwd, limits, realPath = fs6.realpathSync) {
|
|
1270
894
|
const result = runCommand(runner, args2, cwd, limits);
|
|
1271
895
|
if (result.status === "error")
|
|
1272
896
|
return result;
|
|
1273
897
|
const rawValue = result.output.stdout.trim();
|
|
1274
|
-
if (!
|
|
898
|
+
if (!path5.isAbsolute(rawValue)) {
|
|
1275
899
|
return { status: "error", reasonCode: "target-unavailable" };
|
|
1276
900
|
}
|
|
1277
901
|
const value = canonicalPath(rawValue, realPath);
|
|
1278
902
|
return value === undefined ? { status: "error", reasonCode: "target-unavailable" } : { status: "ok", value };
|
|
1279
903
|
}
|
|
1280
|
-
function captureParentIdentity(targetDirectory, runner, limits, realPath =
|
|
904
|
+
function captureParentIdentity(targetDirectory, runner, limits, realPath = fs6.realpathSync) {
|
|
1281
905
|
const targetRoot = requiredGitPath(runner, ["rev-parse", "--show-toplevel"], targetDirectory, limits, realPath);
|
|
1282
906
|
if (targetRoot.status === "error")
|
|
1283
907
|
return targetRoot;
|
|
@@ -1305,53 +929,53 @@ function captureParentIdentity(targetDirectory, runner, limits, realPath = fs7.r
|
|
|
1305
929
|
}
|
|
1306
930
|
} : { status: "error", reasonCode: "target-unavailable" };
|
|
1307
931
|
}
|
|
1308
|
-
function readGitfileTarget(gitfilePath, realPath =
|
|
932
|
+
function readGitfileTarget(gitfilePath, realPath = fs6.realpathSync) {
|
|
1309
933
|
let contents;
|
|
1310
934
|
try {
|
|
1311
|
-
contents =
|
|
935
|
+
contents = fs6.readFileSync(gitfilePath, "utf8");
|
|
1312
936
|
} catch {
|
|
1313
937
|
return;
|
|
1314
938
|
}
|
|
1315
939
|
const match = /^gitdir:\s*(.+?)\s*$/im.exec(contents);
|
|
1316
940
|
if (!match)
|
|
1317
941
|
return;
|
|
1318
|
-
const target =
|
|
942
|
+
const target = path5.isAbsolute(match[1]) ? match[1] : path5.resolve(path5.dirname(gitfilePath), match[1]);
|
|
1319
943
|
return canonicalPath(target, realPath);
|
|
1320
944
|
}
|
|
1321
945
|
function readGitdirBacklink(backlinkPath, realPath) {
|
|
1322
946
|
let contents;
|
|
1323
947
|
try {
|
|
1324
|
-
contents =
|
|
948
|
+
contents = fs6.readFileSync(backlinkPath, "utf8").trim();
|
|
1325
949
|
} catch {
|
|
1326
950
|
return;
|
|
1327
951
|
}
|
|
1328
952
|
if (contents.length === 0)
|
|
1329
953
|
return;
|
|
1330
|
-
const target =
|
|
954
|
+
const target = path5.isAbsolute(contents) ? contents : path5.resolve(path5.dirname(backlinkPath), contents);
|
|
1331
955
|
return canonicalPath(target, realPath);
|
|
1332
956
|
}
|
|
1333
957
|
function isPathWithin(root, candidate) {
|
|
1334
|
-
const relative =
|
|
1335
|
-
return relative !== "" && relative !== ".." && !relative.startsWith(`..${
|
|
958
|
+
const relative = path5.relative(root, candidate);
|
|
959
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path5.sep}`) && !path5.isAbsolute(relative);
|
|
1336
960
|
}
|
|
1337
961
|
function validateDotGitLinkage(targetRoot, gitDir, commonDir, realPath) {
|
|
1338
|
-
const dotGit =
|
|
962
|
+
const dotGit = path5.join(targetRoot, ".git");
|
|
1339
963
|
let dotGitStat;
|
|
1340
964
|
try {
|
|
1341
|
-
dotGitStat =
|
|
965
|
+
dotGitStat = fs6.lstatSync(dotGit);
|
|
1342
966
|
} catch {
|
|
1343
967
|
return false;
|
|
1344
968
|
}
|
|
1345
969
|
if (dotGitStat.isDirectory()) {
|
|
1346
970
|
return gitDir === commonDir && canonicalPath(dotGit, realPath) === commonDir;
|
|
1347
971
|
}
|
|
1348
|
-
if (!dotGitStat.isFile() || dotGitStat.isSymbolicLink() || !isPathWithin(
|
|
972
|
+
if (!dotGitStat.isFile() || dotGitStat.isSymbolicLink() || !isPathWithin(path5.join(commonDir, "worktrees"), gitDir) || readGitfileTarget(dotGit, realPath) !== gitDir) {
|
|
1349
973
|
return false;
|
|
1350
974
|
}
|
|
1351
|
-
const backlinkPath =
|
|
975
|
+
const backlinkPath = path5.join(gitDir, "gitdir");
|
|
1352
976
|
let backlinkStat;
|
|
1353
977
|
try {
|
|
1354
|
-
backlinkStat =
|
|
978
|
+
backlinkStat = fs6.lstatSync(backlinkPath);
|
|
1355
979
|
} catch {
|
|
1356
980
|
return false;
|
|
1357
981
|
}
|
|
@@ -1360,7 +984,7 @@ function validateDotGitLinkage(targetRoot, gitDir, commonDir, realPath) {
|
|
|
1360
984
|
function validateRegisteredWorktree(candidateDirectory, parentIdentity, options = {}) {
|
|
1361
985
|
const runner = options.commandRunner ?? defaultCommandRunner;
|
|
1362
986
|
const limits = mergeLimits(options.limits);
|
|
1363
|
-
const realPath = options.realPath ??
|
|
987
|
+
const realPath = options.realPath ?? fs6.realpathSync;
|
|
1364
988
|
const candidateRoot = canonicalPath(candidateDirectory, realPath);
|
|
1365
989
|
if (candidateRoot === undefined) {
|
|
1366
990
|
return { status: "error", reasonCode: "target-unavailable" };
|
|
@@ -1527,7 +1151,7 @@ function readWorktreeRevisionV2(runner, fileReader, symlinkReader, statReader, c
|
|
|
1527
1151
|
if (paths.length > limits.maxFiles) {
|
|
1528
1152
|
return { status: "error", reasonCode: "file-limit" };
|
|
1529
1153
|
}
|
|
1530
|
-
const hash =
|
|
1154
|
+
const hash = createHash("sha256");
|
|
1531
1155
|
hash.update("systematic:opencode-observer:worktree:v2\x00");
|
|
1532
1156
|
let totalBytes = 0;
|
|
1533
1157
|
for (const relativePath of paths) {
|
|
@@ -1745,19 +1369,19 @@ function createOpencodeOperationObserver(options) {
|
|
|
1745
1369
|
const limits = mergeLimits(options.limits);
|
|
1746
1370
|
let targetDirectory;
|
|
1747
1371
|
try {
|
|
1748
|
-
targetDirectory = (options.realPath ??
|
|
1372
|
+
targetDirectory = (options.realPath ?? fs6.realpathSync)(options.targetDirectory);
|
|
1749
1373
|
} catch {
|
|
1750
|
-
targetDirectory =
|
|
1374
|
+
targetDirectory = path5.resolve(options.targetDirectory);
|
|
1751
1375
|
}
|
|
1752
1376
|
const runner = options.commandRunner ?? defaultCommandRunner;
|
|
1753
1377
|
const remoteRunner = options.remoteCommandRunner ?? defaultRemoteCommandRunner;
|
|
1754
|
-
const realPath = options.realPath ??
|
|
1378
|
+
const realPath = options.realPath ?? fs6.realpathSync;
|
|
1755
1379
|
const parentIdentityResult = captureParentIdentity(targetDirectory, runner, limits, realPath);
|
|
1756
1380
|
const targetDigest = digest("target", [targetDirectory]);
|
|
1757
|
-
const fileReader = options.fileReader ??
|
|
1758
|
-
const symlinkReader = options.symlinkReader ??
|
|
1381
|
+
const fileReader = options.fileReader ?? fs6.readFileSync;
|
|
1382
|
+
const symlinkReader = options.symlinkReader ?? fs6.readlinkSync;
|
|
1759
1383
|
const statReader = options.statReader ?? ((filePath) => {
|
|
1760
|
-
const stat =
|
|
1384
|
+
const stat = fs6.lstatSync(filePath);
|
|
1761
1385
|
return {
|
|
1762
1386
|
isFile: stat.isFile(),
|
|
1763
1387
|
isSymbolicLink: stat.isSymbolicLink(),
|
|
@@ -1820,11 +1444,11 @@ function createOpencodeOperationObserver(options) {
|
|
|
1820
1444
|
|
|
1821
1445
|
// src/lib/opencode-workflow-guard.ts
|
|
1822
1446
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
1823
|
-
import
|
|
1824
|
-
import
|
|
1447
|
+
import fs8 from "fs";
|
|
1448
|
+
import path6 from "path";
|
|
1825
1449
|
|
|
1826
1450
|
// src/lib/question-attestation.ts
|
|
1827
|
-
import { createHash as
|
|
1451
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
1828
1452
|
var CANONICAL_QUESTION_WORDING = "Confirm the requested guarded transition.";
|
|
1829
1453
|
var MAX_ID_LENGTH = 128;
|
|
1830
1454
|
var MAX_RESOURCE_LENGTH = 512;
|
|
@@ -1855,7 +1479,7 @@ function isQuestionPurpose(value) {
|
|
|
1855
1479
|
return value === "transition" || value === "session-disablement";
|
|
1856
1480
|
}
|
|
1857
1481
|
function digestResource(resource) {
|
|
1858
|
-
return `sha256:${
|
|
1482
|
+
return `sha256:${createHash2("sha256").update(`systematic-question-attestation:v1:resource:${resource}`, "utf8").digest("hex")}`;
|
|
1859
1483
|
}
|
|
1860
1484
|
function bindingKey(input, resourceDigest) {
|
|
1861
1485
|
return [
|
|
@@ -2341,7 +1965,7 @@ function createQuestionAttestation(options = {}) {
|
|
|
2341
1965
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
2342
1966
|
|
|
2343
1967
|
// src/lib/receipt-readback.ts
|
|
2344
|
-
import { createHash as
|
|
1968
|
+
import { createHash as createHash3 } from "crypto";
|
|
2345
1969
|
var RECEIPT_READBACK_SCHEMA_VERSION = 2;
|
|
2346
1970
|
var RECEIPT_READBACK_PROTOCOL_VERSION = 2;
|
|
2347
1971
|
var LEGACY_RECEIPT_READBACK_SCHEMA_VERSION = 1;
|
|
@@ -2512,7 +2136,7 @@ function sessionSaltHex(value) {
|
|
|
2512
2136
|
}
|
|
2513
2137
|
function digestReceiptIdentity(domain, identity, sessionSalt) {
|
|
2514
2138
|
const salt = sessionSaltHex(sessionSalt) ?? "invalid-salt";
|
|
2515
|
-
return
|
|
2139
|
+
return createHash3("sha256").update(`systematic/receipt-ledger/${domain}/v1/${salt}/${identity}`).digest("hex");
|
|
2516
2140
|
}
|
|
2517
2141
|
function decodeSessionSalt(value) {
|
|
2518
2142
|
return Uint8Array.from(Buffer.from(value, "hex"));
|
|
@@ -2675,7 +2299,7 @@ function serializeEnvelope(envelope) {
|
|
|
2675
2299
|
});
|
|
2676
2300
|
}
|
|
2677
2301
|
function hashIntegrity(scope, value) {
|
|
2678
|
-
return
|
|
2302
|
+
return createHash3("sha256").update(`systematic/receipt-readback/${scope}/v1/${value}`).digest("hex");
|
|
2679
2303
|
}
|
|
2680
2304
|
function mintIntegrity(envelope, sessionSalt) {
|
|
2681
2305
|
return hashIntegrity("mint", `${sessionSalt}/${serializeEnvelope(envelope)}`);
|
|
@@ -4361,8 +3985,8 @@ function normalizeCapabilities(input) {
|
|
|
4361
3985
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
4362
3986
|
|
|
4363
3987
|
// src/lib/receipt-classifier.ts
|
|
4364
|
-
import
|
|
4365
|
-
import { fileURLToPath
|
|
3988
|
+
import fs7 from "fs";
|
|
3989
|
+
import { fileURLToPath } from "url";
|
|
4366
3990
|
|
|
4367
3991
|
// node_modules/.bun/web-tree-sitter@0.25.10/node_modules/web-tree-sitter/tree-sitter.js
|
|
4368
3992
|
var __defProp = Object.defineProperty;
|
|
@@ -5798,8 +5422,8 @@ var Module2 = (() => {
|
|
|
5798
5422
|
var moduleRtn;
|
|
5799
5423
|
var Module = moduleArg;
|
|
5800
5424
|
var readyPromiseResolve, readyPromiseReject;
|
|
5801
|
-
var readyPromise = new Promise((
|
|
5802
|
-
readyPromiseResolve =
|
|
5425
|
+
var readyPromise = new Promise((resolve, reject) => {
|
|
5426
|
+
readyPromiseResolve = resolve;
|
|
5803
5427
|
readyPromiseReject = reject;
|
|
5804
5428
|
});
|
|
5805
5429
|
var ENVIRONMENT_IS_WEB = typeof window == "object";
|
|
@@ -5821,11 +5445,11 @@ var Module2 = (() => {
|
|
|
5821
5445
|
throw toThrow;
|
|
5822
5446
|
}, "quit_");
|
|
5823
5447
|
var scriptDirectory = "";
|
|
5824
|
-
function locateFile(
|
|
5448
|
+
function locateFile(path6) {
|
|
5825
5449
|
if (Module["locateFile"]) {
|
|
5826
|
-
return Module["locateFile"](
|
|
5450
|
+
return Module["locateFile"](path6, scriptDirectory);
|
|
5827
5451
|
}
|
|
5828
|
-
return scriptDirectory +
|
|
5452
|
+
return scriptDirectory + path6;
|
|
5829
5453
|
}
|
|
5830
5454
|
__name(locateFile, "locateFile");
|
|
5831
5455
|
var readAsync, readBinary;
|
|
@@ -5879,13 +5503,13 @@ var Module2 = (() => {
|
|
|
5879
5503
|
}
|
|
5880
5504
|
readAsync = /* @__PURE__ */ __name(async (url) => {
|
|
5881
5505
|
if (isFileURI(url)) {
|
|
5882
|
-
return new Promise((
|
|
5506
|
+
return new Promise((resolve, reject) => {
|
|
5883
5507
|
var xhr = new XMLHttpRequest;
|
|
5884
5508
|
xhr.open("GET", url, true);
|
|
5885
5509
|
xhr.responseType = "arraybuffer";
|
|
5886
5510
|
xhr.onload = () => {
|
|
5887
5511
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
|
5888
|
-
|
|
5512
|
+
resolve(xhr.response);
|
|
5889
5513
|
return;
|
|
5890
5514
|
}
|
|
5891
5515
|
reject(xhr.status);
|
|
@@ -6105,10 +5729,10 @@ var Module2 = (() => {
|
|
|
6105
5729
|
__name(receiveInstantiationResult, "receiveInstantiationResult");
|
|
6106
5730
|
var info2 = getWasmImports();
|
|
6107
5731
|
if (Module["instantiateWasm"]) {
|
|
6108
|
-
return new Promise((
|
|
5732
|
+
return new Promise((resolve, reject) => {
|
|
6109
5733
|
Module["instantiateWasm"](info2, (mod, inst) => {
|
|
6110
5734
|
receiveInstance(mod, inst);
|
|
6111
|
-
|
|
5735
|
+
resolve(mod.exports);
|
|
6112
5736
|
});
|
|
6113
5737
|
});
|
|
6114
5738
|
}
|
|
@@ -7899,7 +7523,7 @@ function matchesToolIdentity(tool, executable) {
|
|
|
7899
7523
|
async function initializeParserAssets(options) {
|
|
7900
7524
|
const treeSitterWasmPath = options.treeSitterWasmPath ?? resolveAsset("web-tree-sitter/tree-sitter.wasm");
|
|
7901
7525
|
const bashWasmPath = options.bashWasmPath ?? resolveAsset("tree-sitter-bash/tree-sitter-bash.wasm");
|
|
7902
|
-
if (!
|
|
7526
|
+
if (!fs7.existsSync(treeSitterWasmPath) || !fs7.existsSync(bashWasmPath)) {
|
|
7903
7527
|
throw new Error("parser-asset-unavailable");
|
|
7904
7528
|
}
|
|
7905
7529
|
try {
|
|
@@ -7920,7 +7544,7 @@ async function initializeParserAssets(options) {
|
|
|
7920
7544
|
}
|
|
7921
7545
|
function resolveAsset(specifier) {
|
|
7922
7546
|
const resolved = import.meta.resolve(specifier);
|
|
7923
|
-
return resolved.startsWith("file://") ?
|
|
7547
|
+
return resolved.startsWith("file://") ? fileURLToPath(resolved) : resolved;
|
|
7924
7548
|
}
|
|
7925
7549
|
function classifyParserFailure(error) {
|
|
7926
7550
|
if (error instanceof Error && error.message === "grammar-incompatible") {
|
|
@@ -10777,16 +10401,16 @@ function canonicalFileTarget(filePath, realPath) {
|
|
|
10777
10401
|
const existing = canonicalExistingPath(filePath, realPath);
|
|
10778
10402
|
if (existing)
|
|
10779
10403
|
return existing;
|
|
10780
|
-
const parent = canonicalExistingPath(
|
|
10781
|
-
const basename =
|
|
10782
|
-
return parent && basename && basename !== "." && basename !== ".." ?
|
|
10404
|
+
const parent = canonicalExistingPath(path6.dirname(filePath), realPath);
|
|
10405
|
+
const basename = path6.basename(filePath);
|
|
10406
|
+
return parent && basename && basename !== "." && basename !== ".." ? path6.join(parent, basename) : undefined;
|
|
10783
10407
|
}
|
|
10784
10408
|
function pathWithinOrEqual(root, candidate) {
|
|
10785
|
-
const relative =
|
|
10786
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
10409
|
+
const relative = path6.relative(root, candidate);
|
|
10410
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path6.sep}`) && !path6.isAbsolute(relative);
|
|
10787
10411
|
}
|
|
10788
10412
|
function targetIsGitAdminStorage(targetPath, validation) {
|
|
10789
|
-
return pathWithinOrEqual(
|
|
10413
|
+
return pathWithinOrEqual(path6.join(validation.targetRoot, ".git"), targetPath) || pathWithinOrEqual(validation.gitDir, targetPath) || pathWithinOrEqual(validation.commonDir, targetPath);
|
|
10790
10414
|
}
|
|
10791
10415
|
function targetResultFromValidation(candidatePath, validation) {
|
|
10792
10416
|
if (validation.status === "error")
|
|
@@ -10802,7 +10426,7 @@ function targetResultFromValidation(candidatePath, validation) {
|
|
|
10802
10426
|
function trustedParentTarget(parentTargetRoot, candidatePath) {
|
|
10803
10427
|
if (!pathWithinOrEqual(parentTargetRoot, candidatePath))
|
|
10804
10428
|
return;
|
|
10805
|
-
if (pathWithinOrEqual(
|
|
10429
|
+
if (pathWithinOrEqual(path6.join(parentTargetRoot, ".git"), candidatePath)) {
|
|
10806
10430
|
return unavailableOperationTarget();
|
|
10807
10431
|
}
|
|
10808
10432
|
return { status: "available", targetRoot: parentTargetRoot };
|
|
@@ -10815,14 +10439,14 @@ function validationForCandidate(candidatePath, options, targetPath = candidatePa
|
|
|
10815
10439
|
}
|
|
10816
10440
|
}
|
|
10817
10441
|
function deriveFileTarget(rawPath, baseDirectory, parentTargetRoot, options, realPath, allowParentFastPath = true) {
|
|
10818
|
-
const resolvedPath =
|
|
10442
|
+
const resolvedPath = path6.resolve(baseDirectory, rawPath);
|
|
10819
10443
|
const canonicalPath2 = canonicalFileTarget(resolvedPath, realPath);
|
|
10820
10444
|
if (!canonicalPath2)
|
|
10821
10445
|
return unavailableOperationTarget();
|
|
10822
|
-
const parentResult = allowParentFastPath && !
|
|
10446
|
+
const parentResult = allowParentFastPath && !path6.isAbsolute(rawPath) ? trustedParentTarget(parentTargetRoot, canonicalPath2) : undefined;
|
|
10823
10447
|
if (parentResult)
|
|
10824
10448
|
return parentResult;
|
|
10825
|
-
return validationForCandidate(
|
|
10449
|
+
return validationForCandidate(path6.dirname(canonicalPath2), options, canonicalPath2);
|
|
10826
10450
|
}
|
|
10827
10451
|
function sharedTargetRoot(targets, extraRoots = []) {
|
|
10828
10452
|
const roots = [...extraRoots];
|
|
@@ -10835,10 +10459,10 @@ function sharedTargetRoot(targets, extraRoots = []) {
|
|
|
10835
10459
|
return targetRoot && roots.every((root) => root === targetRoot) ? targetRoot : undefined;
|
|
10836
10460
|
}
|
|
10837
10461
|
function deriveDirectoryTarget(rawPath, baseDirectory, parentTargetRoot, options, realPath) {
|
|
10838
|
-
const resolvedPath = canonicalExistingPath(
|
|
10462
|
+
const resolvedPath = canonicalExistingPath(path6.resolve(baseDirectory, rawPath), realPath);
|
|
10839
10463
|
if (!resolvedPath)
|
|
10840
10464
|
return { status: "unavailable" };
|
|
10841
|
-
const parentResult =
|
|
10465
|
+
const parentResult = path6.isAbsolute(rawPath) ? undefined : trustedParentTarget(parentTargetRoot, resolvedPath);
|
|
10842
10466
|
if (parentResult?.status === "unavailable") {
|
|
10843
10467
|
return { status: "unavailable" };
|
|
10844
10468
|
}
|
|
@@ -10946,14 +10570,14 @@ function deriveBashTarget(args2, sessionLocation, parentTargetRoot, options, rea
|
|
|
10946
10570
|
if (typeof args2.workdir !== "string" || args2.workdir.length === 0) {
|
|
10947
10571
|
return unavailableOperationTarget();
|
|
10948
10572
|
}
|
|
10949
|
-
const candidatePath = canonicalExistingPath(
|
|
10573
|
+
const candidatePath = canonicalExistingPath(path6.resolve(sessionLocation, args2.workdir), realPath);
|
|
10950
10574
|
if (!candidatePath)
|
|
10951
10575
|
return unavailableOperationTarget();
|
|
10952
|
-
const parentResult =
|
|
10576
|
+
const parentResult = path6.isAbsolute(args2.workdir) ? undefined : trustedParentTarget(parentTargetRoot, candidatePath);
|
|
10953
10577
|
return parentResult ?? validationForCandidate(candidatePath, options);
|
|
10954
10578
|
}
|
|
10955
10579
|
function deriveOpencodeOperationTarget(tool, args2, options) {
|
|
10956
|
-
const realPath = options.realPath ??
|
|
10580
|
+
const realPath = options.realPath ?? fs8.realpathSync;
|
|
10957
10581
|
const parentTargetRoot = canonicalExistingPath(options.parentTargetRoot, realPath);
|
|
10958
10582
|
if (!parentTargetRoot)
|
|
10959
10583
|
return unavailableOperationTarget();
|
|
@@ -11030,7 +10654,7 @@ function canonicalPatchText(patchText, sessionLocation) {
|
|
|
11030
10654
|
const filePath = line.slice(prefix.length).trim();
|
|
11031
10655
|
if (!filePath)
|
|
11032
10656
|
return;
|
|
11033
|
-
segments[index] = `${prefix}${
|
|
10657
|
+
segments[index] = `${prefix}${path6.resolve(sessionLocation, filePath)}`;
|
|
11034
10658
|
}
|
|
11035
10659
|
return segments.join("");
|
|
11036
10660
|
}
|
|
@@ -11044,13 +10668,13 @@ function canonicalHunks(value, sessionLocation) {
|
|
|
11044
10668
|
}
|
|
11045
10669
|
const canonical = {
|
|
11046
10670
|
...hunk,
|
|
11047
|
-
path:
|
|
10671
|
+
path: path6.resolve(sessionLocation, hunk.path)
|
|
11048
10672
|
};
|
|
11049
10673
|
if (hunk.move_path !== undefined) {
|
|
11050
10674
|
if (typeof hunk.move_path !== "string" || !hunk.move_path) {
|
|
11051
10675
|
return;
|
|
11052
10676
|
}
|
|
11053
|
-
canonical.move_path =
|
|
10677
|
+
canonical.move_path = path6.resolve(sessionLocation, hunk.move_path);
|
|
11054
10678
|
}
|
|
11055
10679
|
hunks.push(canonical);
|
|
11056
10680
|
}
|
|
@@ -11339,7 +10963,7 @@ function createSessionRuntime(options, operationObservers, recoveredOperationCon
|
|
|
11339
10963
|
function parentObserverRegistration() {
|
|
11340
10964
|
if (!options.observer)
|
|
11341
10965
|
return;
|
|
11342
|
-
const targetRoot = canonicalExistingPath(options.targetDirectory ?? process.cwd(),
|
|
10966
|
+
const targetRoot = canonicalExistingPath(options.targetDirectory ?? process.cwd(), fs8.realpathSync);
|
|
11343
10967
|
if (!targetRoot)
|
|
11344
10968
|
return;
|
|
11345
10969
|
let validation;
|
|
@@ -12288,7 +11912,7 @@ function createSessionRuntime(options, operationObservers, recoveredOperationCon
|
|
|
12288
11912
|
markUnavailable();
|
|
12289
11913
|
return;
|
|
12290
11914
|
}
|
|
12291
|
-
const canonicalParentTargetRoot = canonicalExistingPath(parentTargetRoot,
|
|
11915
|
+
const canonicalParentTargetRoot = canonicalExistingPath(parentTargetRoot, fs8.realpathSync);
|
|
12292
11916
|
if (!canonicalParentTargetRoot) {
|
|
12293
11917
|
markUnavailable();
|
|
12294
11918
|
return;
|
|
@@ -13324,8 +12948,8 @@ function createOpencodeWorkflowGuard(options) {
|
|
|
13324
12948
|
}
|
|
13325
12949
|
|
|
13326
12950
|
// src/lib/skill-resolver.ts
|
|
13327
|
-
import
|
|
13328
|
-
import
|
|
12951
|
+
import fs9 from "fs";
|
|
12952
|
+
import path7 from "path";
|
|
13329
12953
|
import { pathToFileURL } from "url";
|
|
13330
12954
|
|
|
13331
12955
|
// src/lib/skill-catalog.ts
|
|
@@ -13387,7 +13011,7 @@ function buildSkillToolParameterHint(options) {
|
|
|
13387
13011
|
}
|
|
13388
13012
|
function buildSkillContentOutput(matchedSkill) {
|
|
13389
13013
|
const body2 = extractSkillBody(matchedSkill.wrappedTemplate);
|
|
13390
|
-
const dir =
|
|
13014
|
+
const dir = path7.dirname(matchedSkill.skillFile);
|
|
13391
13015
|
const base = pathToFileURL(dir).href;
|
|
13392
13016
|
const files = discoverSkillFiles(dir);
|
|
13393
13017
|
const lines = [
|
|
@@ -13418,17 +13042,17 @@ function discoverSkillFiles(dir, limit = 10) {
|
|
|
13418
13042
|
function handleEntry(entry, currentDir) {
|
|
13419
13043
|
if (entry.isDirectory()) {
|
|
13420
13044
|
if (!shouldSkipDirectory(entry.name)) {
|
|
13421
|
-
recurse(
|
|
13045
|
+
recurse(path7.resolve(currentDir, entry.name));
|
|
13422
13046
|
}
|
|
13423
13047
|
} else if (shouldIncludeFile(entry.name)) {
|
|
13424
|
-
files.push(
|
|
13048
|
+
files.push(path7.resolve(currentDir, entry.name));
|
|
13425
13049
|
}
|
|
13426
13050
|
}
|
|
13427
13051
|
function recurse(currentDir) {
|
|
13428
13052
|
if (files.length >= limit)
|
|
13429
13053
|
return;
|
|
13430
13054
|
try {
|
|
13431
|
-
const entries =
|
|
13055
|
+
const entries = fs9.readdirSync(currentDir, { withFileTypes: true });
|
|
13432
13056
|
for (const entry of entries) {
|
|
13433
13057
|
if (files.length >= limit)
|
|
13434
13058
|
break;
|
|
@@ -13486,19 +13110,19 @@ function createSkillTool(options) {
|
|
|
13486
13110
|
}
|
|
13487
13111
|
|
|
13488
13112
|
// src/index.ts
|
|
13489
|
-
var
|
|
13490
|
-
var
|
|
13491
|
-
var bundledSkillsDir =
|
|
13492
|
-
var
|
|
13493
|
-
var bundledCommandsDir =
|
|
13494
|
-
var packageJsonPath =
|
|
13495
|
-
var canonicalPackageSource = pathToFileURL2(
|
|
13496
|
-
var registrationSourceIdentity =
|
|
13113
|
+
var __dirname2 = path8.dirname(fileURLToPath2(import.meta.url));
|
|
13114
|
+
var packageRoot = path8.resolve(__dirname2, "..");
|
|
13115
|
+
var bundledSkillsDir = path8.join(packageRoot, "skills");
|
|
13116
|
+
var bundledAgentsDir = path8.join(packageRoot, "agents");
|
|
13117
|
+
var bundledCommandsDir = path8.join(packageRoot, "commands");
|
|
13118
|
+
var packageJsonPath = path8.join(packageRoot, "package.json");
|
|
13119
|
+
var canonicalPackageSource = pathToFileURL2(fs10.realpathSync(packageRoot)).href;
|
|
13120
|
+
var registrationSourceIdentity = createHash4("sha256").update(`systematic/opencode-registration-source/v1/${canonicalPackageSource}`).digest("hex");
|
|
13497
13121
|
var getPackageVersion = () => {
|
|
13498
13122
|
try {
|
|
13499
|
-
if (!
|
|
13123
|
+
if (!fs10.existsSync(packageJsonPath))
|
|
13500
13124
|
return "unknown";
|
|
13501
|
-
const content =
|
|
13125
|
+
const content = fs10.readFileSync(packageJsonPath, "utf8");
|
|
13502
13126
|
const parsed = JSON.parse(content);
|
|
13503
13127
|
return parsed.version ?? "unknown";
|
|
13504
13128
|
} catch {
|
|
@@ -13519,9 +13143,8 @@ var initializePlugin = async ({
|
|
|
13519
13143
|
const configHandler = createConfigHandler({
|
|
13520
13144
|
directory,
|
|
13521
13145
|
bundledSkillsDir,
|
|
13522
|
-
bundledAgentsDir
|
|
13523
|
-
bundledCommandsDir
|
|
13524
|
-
client
|
|
13146
|
+
bundledAgentsDir,
|
|
13147
|
+
bundledCommandsDir
|
|
13525
13148
|
});
|
|
13526
13149
|
const observer = createOpencodeOperationObserver({
|
|
13527
13150
|
targetDirectory: typeof worktree === "string" ? worktree : directory
|