@fro.bot/systematic 3.14.5 → 3.15.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/dist/cli.js CHANGED
@@ -1,25 +1,55 @@
1
1
  #!/usr/bin/env node
2
2
  // @bun
3
3
  import {
4
+ parseFrontmatter,
5
+ parse,
6
+ parseTree,
7
+ modify,
4
8
  applyEdits,
5
- discoverSkills,
6
- exports_external,
9
+ _isoDateTime,
10
+ _isoDate,
11
+ _isoTime,
12
+ _isoDuration,
13
+ string,
14
+ ZodISODateTime,
15
+ ZodISODate,
16
+ ZodISOTime,
17
+ ZodISODuration,
18
+ number,
19
+ boolean,
20
+ array,
21
+ object,
22
+ union,
23
+ discriminatedUnion,
24
+ _enum,
25
+ literal,
26
+ loadConfigWithSources,
27
+ getConfigPaths,
7
28
  extractString,
8
29
  findAgentsInDir,
9
30
  findCommandsInDir,
10
31
  findSkillsInDir,
11
- getConfigPaths,
12
- loadConfigWithSources,
13
- modify,
14
- parse,
15
- parseFrontmatter,
16
- parseTree
17
- } from "./index-0stf3ag0.js";
32
+ discoverSkills
33
+ } from "./index-7cyxcwf5.js";
34
+ var __defProp = Object.defineProperty;
35
+ var __returnValue = (v) => v;
36
+ function __exportSetter(name, newValue) {
37
+ this[name] = __returnValue.bind(null, newValue);
38
+ }
39
+ var __export = (target, all) => {
40
+ for (var name in all)
41
+ __defProp(target, name, {
42
+ get: all[name],
43
+ enumerable: true,
44
+ configurable: true,
45
+ set: __exportSetter.bind(all, name)
46
+ });
47
+ };
18
48
 
19
49
  // src/cli.ts
20
- import fs5 from "fs";
50
+ import fs6 from "fs";
21
51
  import os2 from "os";
22
- import path4 from "path";
52
+ import path5 from "path";
23
53
 
24
54
  // src/lib/agent-resolver.ts
25
55
  import fs from "fs";
@@ -778,7 +808,30 @@ function buildCapabilitySnapshot(options) {
778
808
  options.outputSink?.(serialized);
779
809
  return snapshot;
780
810
  }
781
-
811
+ // node_modules/.bun/zod@4.5.4/node_modules/zod/v4/classic/iso.js
812
+ var exports_iso = {};
813
+ __export(exports_iso, {
814
+ ZodISODate: () => ZodISODate,
815
+ ZodISODateTime: () => ZodISODateTime,
816
+ ZodISODuration: () => ZodISODuration,
817
+ ZodISOTime: () => ZodISOTime,
818
+ date: () => date,
819
+ datetime: () => datetime,
820
+ duration: () => duration,
821
+ time: () => time
822
+ });
823
+ function datetime(params) {
824
+ return _isoDateTime(ZodISODateTime, params);
825
+ }
826
+ function date(params) {
827
+ return _isoDate(ZodISODate, params);
828
+ }
829
+ function time(params) {
830
+ return _isoTime(ZodISOTime, params);
831
+ }
832
+ function duration(params) {
833
+ return _isoDuration(ZodISODuration, params);
834
+ }
782
835
  // src/lib/pi-subagents-export.ts
783
836
  import * as crypto2 from "crypto";
784
837
  import * as fs3 from "fs";
@@ -1424,10 +1477,10 @@ function ownedFilenames(manifest) {
1424
1477
  function parseSourceCategoryAndKey(sourceRelPath) {
1425
1478
  const parts = sourceRelPath.split("/");
1426
1479
  if (parts.length >= 3 && parts[0] === "agents") {
1427
- const fileName2 = parts.at(-1) ?? "";
1480
+ const fileName = parts.at(-1) ?? "";
1428
1481
  return {
1429
1482
  category: parts[1],
1430
- agentKey: fileName2.replace(/\.md$/, "")
1483
+ agentKey: fileName.replace(/\.md$/, "")
1431
1484
  };
1432
1485
  }
1433
1486
  const fileName = parts.at(-1) ?? sourceRelPath;
@@ -1949,7 +2002,7 @@ function cleanup(agentsRoot, configOptions) {
1949
2002
  ...targets.map((t) => t.path),
1950
2003
  ...manifestPath ? [manifestPath] : []
1951
2004
  ];
1952
- const tx2 = runWithRollback(pathsToWatch, [
2005
+ const tx = runWithRollback(pathsToWatch, [
1953
2006
  ...targets.map(({ path: p, filename, hash }) => () => {
1954
2007
  if (fs3.existsSync(p)) {
1955
2008
  assertHashMatchesOrThrow(p, hash, filename, "delete");
@@ -1963,7 +2016,7 @@ function cleanup(agentsRoot, configOptions) {
1963
2016
  }
1964
2017
  ] : []
1965
2018
  ]);
1966
- return { kind: "tx", tx: tx2 };
2019
+ return { kind: "tx", tx };
1967
2020
  });
1968
2021
  if (!lockResult.locked) {
1969
2022
  if (lockResult.isContention)
@@ -1978,6 +2031,108 @@ function cleanup(agentsRoot, configOptions) {
1978
2031
  return { status: "ok" };
1979
2032
  }
1980
2033
 
2034
+ // src/lib/review-artifact-path.ts
2035
+ import fs4 from "fs";
2036
+ import path3 from "path";
2037
+ function formatReviewArtifactIssuePath(issuePath) {
2038
+ if (issuePath.length === 0)
2039
+ return "$";
2040
+ return issuePath.map((segment) => typeof segment === "number" ? String(segment) : segment).join(".");
2041
+ }
2042
+ function readReviewArtifact(filePath) {
2043
+ let content;
2044
+ try {
2045
+ content = fs4.readFileSync(filePath, "utf8");
2046
+ } catch {
2047
+ return { message: "Review artifact file could not be read", ok: false };
2048
+ }
2049
+ try {
2050
+ return { ok: true, value: JSON.parse(content) };
2051
+ } catch (error) {
2052
+ return {
2053
+ message: error instanceof SyntaxError ? "Review artifact contains malformed JSON" : "Review artifact file could not be read",
2054
+ ok: false
2055
+ };
2056
+ }
2057
+ }
2058
+ function isLegacyReviewArtifact(value) {
2059
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !Object.hasOwn(value, "schema_version");
2060
+ }
2061
+ function hasParentDirectoryTraversal(input) {
2062
+ return input.split(/[\\/]+/).some((segment) => segment === "..");
2063
+ }
2064
+ function pathContainsSymlink(candidate) {
2065
+ let current = path3.parse(candidate).root;
2066
+ const relative = path3.relative(current, candidate);
2067
+ for (const segment of relative.split(path3.sep)) {
2068
+ if (!segment)
2069
+ continue;
2070
+ current = path3.join(current, segment);
2071
+ try {
2072
+ if (fs4.lstatSync(current).isSymbolicLink())
2073
+ return true;
2074
+ } catch {
2075
+ return false;
2076
+ }
2077
+ }
2078
+ return false;
2079
+ }
2080
+ function isWithinDirectory(candidate, directory) {
2081
+ const relative = path3.relative(directory, candidate);
2082
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path3.sep}`) && !path3.isAbsolute(relative);
2083
+ }
2084
+ function resolveReviewArtifactPath(input, cwd) {
2085
+ if (hasParentDirectoryTraversal(input)) {
2086
+ return {
2087
+ message: "Review artifact path must not contain parent-directory traversal",
2088
+ ok: false
2089
+ };
2090
+ }
2091
+ const artifactRoot = path3.resolve(cwd, ".context", "systematic", "ce-review");
2092
+ let canonicalRoot;
2093
+ try {
2094
+ canonicalRoot = fs4.realpathSync(artifactRoot);
2095
+ if (!fs4.statSync(canonicalRoot).isDirectory()) {
2096
+ return {
2097
+ message: "Review artifact directory is not a directory",
2098
+ ok: false
2099
+ };
2100
+ }
2101
+ } catch {
2102
+ return { message: "Review artifact directory is unavailable", ok: false };
2103
+ }
2104
+ const candidate = path3.resolve(cwd, input);
2105
+ if (pathContainsSymlink(candidate)) {
2106
+ return {
2107
+ message: "Review artifact path must not contain symlinks",
2108
+ ok: false
2109
+ };
2110
+ }
2111
+ let canonicalTarget;
2112
+ try {
2113
+ canonicalTarget = fs4.realpathSync(candidate);
2114
+ } catch {
2115
+ return { message: "Review artifact file was not found", ok: false };
2116
+ }
2117
+ if (!isWithinDirectory(canonicalTarget, canonicalRoot)) {
2118
+ return {
2119
+ message: "Review artifact path must remain inside .context/systematic/ce-review",
2120
+ ok: false
2121
+ };
2122
+ }
2123
+ try {
2124
+ if (!fs4.lstatSync(canonicalTarget).isFile()) {
2125
+ return {
2126
+ message: "Review artifact target is not a regular file",
2127
+ ok: false
2128
+ };
2129
+ }
2130
+ } catch {
2131
+ return { message: "Review artifact file was not found", ok: false };
2132
+ }
2133
+ return { ok: true, path: canonicalTarget };
2134
+ }
2135
+
1981
2136
  // src/lib/review-artifact-schema.ts
1982
2137
  var MAX_REVIEWER_LENGTH = 64;
1983
2138
  var MAX_RUN_ID_LENGTH = 64;
@@ -1994,14 +2149,14 @@ var REVIEW_ARTIFACT_CUSTOM_MESSAGES = [
1994
2149
  "unsatisfied risk coverage must not cite an input finding ID",
1995
2150
  "passed validation must not include a reason; non-passed validation requires a reason"
1996
2151
  ];
1997
- var boundedText = (maxLength) => exports_external.string().min(1).max(maxLength).regex(/\S/);
1998
- var DispatchOutcomeSchema = exports_external.enum([
2152
+ var boundedText = (maxLength) => string().min(1).max(maxLength).regex(/\S/);
2153
+ var DispatchOutcomeSchema = _enum([
1999
2154
  "findings",
2000
2155
  "empty",
2001
2156
  "malformed",
2002
2157
  "never_returned"
2003
2158
  ]);
2004
- var DispositionSchema = exports_external.enum([
2159
+ var DispositionSchema = _enum([
2005
2160
  "surviving",
2006
2161
  "merged",
2007
2162
  "suppressed",
@@ -2009,12 +2164,12 @@ var DispositionSchema = exports_external.enum([
2009
2164
  "rejected"
2010
2165
  ]);
2011
2166
  var AdmittedDispositionSchema = DispositionSchema.exclude(["rejected"]);
2012
- var HarnessSchema = exports_external.enum(["opencode", "pi", "claude-code"]);
2167
+ var HarnessSchema = _enum(["opencode", "pi", "claude-code"]);
2013
2168
  var RepoRelativePathSchema = boundedText(256).regex(/^(?!\/)(?![A-Za-z]:[\\/])(?!\\).+/);
2014
2169
  var ReviewerSchema = boundedText(MAX_REVIEWER_LENGTH);
2015
- var BranchSchema = exports_external.string().max(MAX_BRANCH_LENGTH);
2016
- var HeadShaSchema = exports_external.string().regex(/^[0-9a-f]{40}$/);
2017
- var CompletedAtSchema = exports_external.iso.datetime({ offset: false });
2170
+ var BranchSchema = string().max(MAX_BRANCH_LENGTH);
2171
+ var HeadShaSchema = string().regex(/^[0-9a-f]{40}$/);
2172
+ var CompletedAtSchema = exports_iso.datetime({ offset: false });
2018
2173
  var ReasonSchema = boundedText(MAX_REASON_LENGTH);
2019
2174
  var RISK_CRITICAL_PERSONAS = [
2020
2175
  "security",
@@ -2023,42 +2178,42 @@ var RISK_CRITICAL_PERSONAS = [
2023
2178
  "reliability",
2024
2179
  "performance"
2025
2180
  ];
2026
- var RiskCriticalPersonaSchema = exports_external.enum(RISK_CRITICAL_PERSONAS);
2181
+ var RiskCriticalPersonaSchema = _enum(RISK_CRITICAL_PERSONAS);
2027
2182
  var FindingTitleSchema = boundedText(256);
2028
- var SeveritySchema = exports_external.enum(["P0", "P1", "P2", "P3", "unknown"]);
2183
+ var SeveritySchema = _enum(["P0", "P1", "P2", "P3", "unknown"]);
2029
2184
  var FindingSeveritySchema = SeveritySchema.exclude(["unknown"]);
2030
- var AutofixClassSchema = exports_external.enum([
2185
+ var AutofixClassSchema = _enum([
2031
2186
  "safe_auto",
2032
2187
  "gated_auto",
2033
2188
  "manual",
2034
2189
  "advisory"
2035
2190
  ]);
2036
- var OwnerSchema = exports_external.enum([
2191
+ var OwnerSchema = _enum([
2037
2192
  "review-fixer",
2038
2193
  "downstream-resolver",
2039
2194
  "human",
2040
2195
  "release"
2041
2196
  ]);
2042
2197
  var BoundedEvidenceStringSchema = boundedText(500).regex(/^(?!\/)(?![A-Za-z]:[\\/])(?!\\).+/);
2043
- var OverflowEvidenceSchema = exports_external.object({
2044
- overflow: exports_external.literal(true),
2198
+ var OverflowEvidenceSchema = object({
2199
+ overflow: literal(true),
2045
2200
  excerpt: BoundedEvidenceStringSchema
2046
2201
  }).strict();
2047
- var EvidenceSchema = exports_external.array(exports_external.union([BoundedEvidenceStringSchema, OverflowEvidenceSchema])).min(1).max(5);
2048
- var AdmittedInputFindingSchema = exports_external.object({
2049
- record_type: exports_external.literal("admitted"),
2202
+ var EvidenceSchema = array(union([BoundedEvidenceStringSchema, OverflowEvidenceSchema])).min(1).max(5);
2203
+ var AdmittedInputFindingSchema = object({
2204
+ record_type: literal("admitted"),
2050
2205
  input_id: boundedText(MAX_INPUT_ID_LENGTH),
2051
2206
  reviewer: ReviewerSchema,
2052
- confidence: exports_external.number().min(0).max(1),
2207
+ confidence: number().min(0).max(1),
2053
2208
  disposition: AdmittedDispositionSchema,
2054
2209
  reason: ReasonSchema
2055
2210
  }).strict();
2056
- var RejectedInputFindingSchema = exports_external.object({
2057
- record_type: exports_external.literal("rejected_summary"),
2211
+ var RejectedInputFindingSchema = object({
2212
+ record_type: literal("rejected_summary"),
2058
2213
  reviewer: ReviewerSchema,
2059
2214
  dispatch_outcome: DispatchOutcomeSchema,
2060
- rejected_finding_count: exports_external.number().int().positive().max(MAX_FINDINGS),
2061
- rejected_severities: exports_external.array(SeveritySchema).max(MAX_FINDINGS),
2215
+ rejected_finding_count: number().int().positive().max(MAX_FINDINGS),
2216
+ rejected_severities: array(SeveritySchema).max(MAX_FINDINGS),
2062
2217
  disposition: DispositionSchema.extract(["rejected"]),
2063
2218
  reason: ReasonSchema
2064
2219
  }).strict().superRefine((row, ctx) => {
@@ -2070,31 +2225,31 @@ var RejectedInputFindingSchema = exports_external.object({
2070
2225
  });
2071
2226
  }
2072
2227
  });
2073
- var InputFindingSchema = exports_external.discriminatedUnion("record_type", [
2228
+ var InputFindingSchema = discriminatedUnion("record_type", [
2074
2229
  AdmittedInputFindingSchema,
2075
2230
  RejectedInputFindingSchema
2076
2231
  ]);
2077
- var ProvenanceSchema = exports_external.object({
2232
+ var ProvenanceSchema = object({
2078
2233
  fingerprint: boundedText(512),
2079
- submitters: exports_external.array(ReviewerSchema).max(MAX_PERSONAS),
2080
- agreement_credit: exports_external.array(ReviewerSchema).max(MAX_PERSONAS)
2234
+ submitters: array(ReviewerSchema).max(MAX_PERSONAS),
2235
+ agreement_credit: array(ReviewerSchema).max(MAX_PERSONAS)
2081
2236
  }).strict();
2082
- var SynthesizedFindingFieldsSchema = exports_external.object({
2237
+ var SynthesizedFindingFieldsSchema = object({
2083
2238
  title: FindingTitleSchema,
2084
2239
  severity: FindingSeveritySchema,
2085
2240
  file: RepoRelativePathSchema,
2086
- line: exports_external.number().int().positive(),
2241
+ line: number().int().positive(),
2087
2242
  why_it_matters: boundedText(2048),
2088
2243
  autofix_class: AutofixClassSchema,
2089
2244
  owner: OwnerSchema,
2090
- requires_verification: exports_external.boolean(),
2091
- confidence: exports_external.number().min(0).max(1),
2245
+ requires_verification: boolean(),
2246
+ confidence: number().min(0).max(1),
2092
2247
  evidence: EvidenceSchema,
2093
- pre_existing: exports_external.boolean(),
2094
- suggested_fix: exports_external.string().max(2048).nullable().optional(),
2095
- validated: exports_external.boolean().optional(),
2248
+ pre_existing: boolean(),
2249
+ suggested_fix: string().max(2048).nullable().optional(),
2250
+ validated: boolean().optional(),
2096
2251
  validation_reason: ReasonSchema.optional(),
2097
- input_finding_ids: exports_external.array(boundedText(MAX_INPUT_ID_LENGTH)).min(1).max(MAX_FINDINGS),
2252
+ input_finding_ids: array(boundedText(MAX_INPUT_ID_LENGTH)).min(1).max(MAX_FINDINGS),
2098
2253
  provenance: ProvenanceSchema
2099
2254
  }).strict();
2100
2255
  var SynthesizedFindingSchema = SynthesizedFindingFieldsSchema.superRefine((finding, ctx) => {
@@ -2106,12 +2261,12 @@ var SynthesizedFindingSchema = SynthesizedFindingFieldsSchema.superRefine((findi
2106
2261
  });
2107
2262
  }
2108
2263
  });
2109
- var DispatchSchema = exports_external.object({
2264
+ var DispatchSchema = object({
2110
2265
  persona: ReviewerSchema,
2111
2266
  dispatch_outcome: DispatchOutcomeSchema,
2112
- input_finding_count: exports_external.number().int().nonnegative().max(MAX_FINDINGS),
2267
+ input_finding_count: number().int().nonnegative().max(MAX_FINDINGS),
2113
2268
  rejection_reason: ReasonSchema.optional(),
2114
- selection_surface: exports_external.array(RepoRelativePathSchema).max(MAX_FINDINGS).optional(),
2269
+ selection_surface: array(RepoRelativePathSchema).max(MAX_FINDINGS).optional(),
2115
2270
  selection_reason: ReasonSchema.optional()
2116
2271
  }).strict().superRefine((dispatch, ctx) => {
2117
2272
  if (RISK_CRITICAL_PERSONAS.includes(dispatch.persona) && (!dispatch.selection_surface || dispatch.selection_surface.length === 0)) {
@@ -2122,29 +2277,29 @@ var DispatchSchema = exports_external.object({
2122
2277
  });
2123
2278
  }
2124
2279
  });
2125
- var DispositionCountsSchema = exports_external.object({
2126
- surviving: exports_external.number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
2127
- merged: exports_external.number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
2128
- suppressed: exports_external.number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
2129
- filtered: exports_external.number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
2130
- rejected: exports_external.number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS)
2280
+ var DispositionCountsSchema = object({
2281
+ surviving: number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
2282
+ merged: number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
2283
+ suppressed: number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
2284
+ filtered: number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
2285
+ rejected: number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS)
2131
2286
  }).strict();
2132
- var CoverageSchema = exports_external.object({
2133
- reviewers: exports_external.number().int().nonnegative().max(MAX_PERSONAS).optional(),
2134
- validators: exports_external.number().int().nonnegative().max(MAX_PERSONAS).optional(),
2135
- residual_risks: exports_external.array(ReasonSchema).max(MAX_PERSONAS),
2136
- testing_gaps: exports_external.array(ReasonSchema).max(MAX_PERSONAS),
2137
- failed_reviewers: exports_external.array(ReviewerSchema).max(MAX_PERSONAS),
2138
- validator_failures: exports_external.array(ReasonSchema).max(MAX_PERSONAS),
2139
- intent_uncertainty: exports_external.array(ReasonSchema).max(MAX_PERSONAS)
2287
+ var CoverageSchema = object({
2288
+ reviewers: number().int().nonnegative().max(MAX_PERSONAS).optional(),
2289
+ validators: number().int().nonnegative().max(MAX_PERSONAS).optional(),
2290
+ residual_risks: array(ReasonSchema).max(MAX_PERSONAS),
2291
+ testing_gaps: array(ReasonSchema).max(MAX_PERSONAS),
2292
+ failed_reviewers: array(ReviewerSchema).max(MAX_PERSONAS),
2293
+ validator_failures: array(ReasonSchema).max(MAX_PERSONAS),
2294
+ intent_uncertainty: array(ReasonSchema).max(MAX_PERSONAS)
2140
2295
  }).strict();
2141
- var DeclinedMergeSchema = exports_external.object({
2296
+ var DeclinedMergeSchema = object({
2142
2297
  file: RepoRelativePathSchema,
2143
- input_finding_ids: exports_external.array(boundedText(MAX_INPUT_ID_LENGTH)).min(2).max(MAX_FINDINGS),
2298
+ input_finding_ids: array(boundedText(MAX_INPUT_ID_LENGTH)).min(2).max(MAX_FINDINGS),
2144
2299
  reason: ReasonSchema
2145
2300
  }).strict();
2146
- var ValidationSchema = exports_external.object({
2147
- status: exports_external.enum([
2301
+ var ValidationSchema = object({
2302
+ status: _enum([
2148
2303
  "passed",
2149
2304
  "failed",
2150
2305
  "unavailable",
@@ -2167,9 +2322,9 @@ var ValidationSchema = exports_external.object({
2167
2322
  });
2168
2323
  }
2169
2324
  });
2170
- var RiskCoverageSchema = exports_external.object({
2325
+ var RiskCoverageSchema = object({
2171
2326
  persona: RiskCriticalPersonaSchema,
2172
- satisfied: exports_external.boolean(),
2327
+ satisfied: boolean(),
2173
2328
  input_finding_id: boundedText(MAX_INPUT_ID_LENGTH).optional()
2174
2329
  }).strict().superRefine((coverage, ctx) => {
2175
2330
  if (coverage.satisfied && coverage.input_finding_id === undefined) {
@@ -2187,14 +2342,14 @@ var RiskCoverageSchema = exports_external.object({
2187
2342
  });
2188
2343
  }
2189
2344
  });
2190
- var ReviewArtifactSchema = exports_external.object({
2191
- schema_version: exports_external.literal(1),
2345
+ var ReviewArtifactSchema = object({
2346
+ schema_version: literal(1),
2192
2347
  run_id: boundedText(MAX_RUN_ID_LENGTH),
2193
2348
  branch: BranchSchema,
2194
2349
  head_sha: HeadShaSchema,
2195
- mode: exports_external.enum(["interactive", "autofix", "headless"]),
2350
+ mode: _enum(["interactive", "autofix", "headless"]),
2196
2351
  harness: HarnessSchema,
2197
- run_status: exports_external.enum([
2352
+ run_status: _enum([
2198
2353
  "in_progress",
2199
2354
  "completed",
2200
2355
  "degraded",
@@ -2202,22 +2357,22 @@ var ReviewArtifactSchema = exports_external.object({
2202
2357
  ]),
2203
2358
  verdict: boundedText(256),
2204
2359
  completed_at: CompletedAtSchema,
2205
- dispatches: exports_external.array(DispatchSchema).max(MAX_PERSONAS),
2206
- input_findings: exports_external.array(InputFindingSchema).max(MAX_FINDINGS * MAX_PERSONAS),
2207
- findings: exports_external.array(SynthesizedFindingSchema).max(MAX_FINDINGS),
2208
- declined_merges: exports_external.array(DeclinedMergeSchema).max(MAX_FINDINGS).optional(),
2209
- risk_coverage: exports_external.array(RiskCoverageSchema).max(MAX_PERSONAS).optional(),
2360
+ dispatches: array(DispatchSchema).max(MAX_PERSONAS),
2361
+ input_findings: array(InputFindingSchema).max(MAX_FINDINGS * MAX_PERSONAS),
2362
+ findings: array(SynthesizedFindingSchema).max(MAX_FINDINGS),
2363
+ declined_merges: array(DeclinedMergeSchema).max(MAX_FINDINGS).optional(),
2364
+ risk_coverage: array(RiskCoverageSchema).max(MAX_PERSONAS).optional(),
2210
2365
  disposition_counts: DispositionCountsSchema,
2211
- applied_fixes: exports_external.array(ReasonSchema).max(MAX_FINDINGS),
2212
- residual_actionable_work: exports_external.array(ReasonSchema).max(MAX_FINDINGS),
2213
- advisory_outputs: exports_external.array(ReasonSchema).max(MAX_FINDINGS),
2366
+ applied_fixes: array(ReasonSchema).max(MAX_FINDINGS),
2367
+ residual_actionable_work: array(ReasonSchema).max(MAX_FINDINGS),
2368
+ advisory_outputs: array(ReasonSchema).max(MAX_FINDINGS),
2214
2369
  coverage: CoverageSchema,
2215
2370
  validation: ValidationSchema.optional()
2216
2371
  }).strict();
2217
2372
 
2218
2373
  // src/lib/setup.ts
2219
- import fs4 from "fs";
2220
- import path3 from "path";
2374
+ import fs5 from "fs";
2375
+ import path4 from "path";
2221
2376
  var SYSTEMATIC_PACKAGE_NAME = "@fro.bot/systematic";
2222
2377
  var PI_PACKAGE_IDENTIFIER = `npm:${SYSTEMATIC_PACKAGE_NAME}`;
2223
2378
  var SETUP_ERROR_NAME = "SetupError";
@@ -2227,10 +2382,10 @@ function createSetupError(message) {
2227
2382
  return error;
2228
2383
  }
2229
2384
  var DEFAULT_OPS = {
2230
- writeFileSync: fs4.writeFileSync,
2231
- renameSync: fs4.renameSync,
2232
- unlinkSync: fs4.unlinkSync,
2233
- chmodSync: fs4.chmodSync
2385
+ writeFileSync: fs5.writeFileSync,
2386
+ renameSync: fs5.renameSync,
2387
+ unlinkSync: fs5.unlinkSync,
2388
+ chmodSync: fs5.chmodSync
2234
2389
  };
2235
2390
  function isRecord2(value) {
2236
2391
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2252,7 +2407,7 @@ function isPiSystematicIdentifier(identifier) {
2252
2407
  }
2253
2408
  function lstatOrNull(targetPath) {
2254
2409
  try {
2255
- return fs4.lstatSync(targetPath);
2410
+ return fs5.lstatSync(targetPath);
2256
2411
  } catch (error) {
2257
2412
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2258
2413
  return null;
@@ -2261,10 +2416,10 @@ function lstatOrNull(targetPath) {
2261
2416
  }
2262
2417
  }
2263
2418
  function assertRealpathUnderCwd(dir, cwd) {
2264
- const realDir = fs4.realpathSync(dir);
2265
- const realCwd = fs4.realpathSync(cwd);
2266
- const relative2 = path3.relative(realCwd, realDir);
2267
- if (relative2.startsWith("..") || path3.isAbsolute(relative2)) {
2419
+ const realDir = fs5.realpathSync(dir);
2420
+ const realCwd = fs5.realpathSync(cwd);
2421
+ const relative = path4.relative(realCwd, realDir);
2422
+ if (relative.startsWith("..") || path4.isAbsolute(relative)) {
2268
2423
  throw createSetupError(`Refusing to write under ${dir}: resolved directory escapes the project root`);
2269
2424
  }
2270
2425
  }
@@ -2277,7 +2432,7 @@ function assertParentTrusted(parentDir, cwd) {
2277
2432
  assertRealpathUnderCwd(parentDir, cwd);
2278
2433
  return;
2279
2434
  }
2280
- fs4.mkdirSync(parentDir, { recursive: true });
2435
+ fs5.mkdirSync(parentDir, { recursive: true });
2281
2436
  assertRealpathUnderCwd(parentDir, cwd);
2282
2437
  }
2283
2438
  var OPENCODE_TARGET_CANDIDATES = [
@@ -2287,19 +2442,19 @@ var OPENCODE_TARGET_CANDIDATES = [
2287
2442
  "opencode.json"
2288
2443
  ];
2289
2444
  function resolveOpenCodeTargetPath(cwd) {
2290
- const opencodeDirStat = lstatOrNull(path3.join(cwd, ".opencode"));
2445
+ const opencodeDirStat = lstatOrNull(path4.join(cwd, ".opencode"));
2291
2446
  if (opencodeDirStat?.isSymbolicLink()) {
2292
- return path3.join(cwd, OPENCODE_TARGET_CANDIDATES[0]);
2447
+ return path4.join(cwd, OPENCODE_TARGET_CANDIDATES[0]);
2293
2448
  }
2294
2449
  for (const candidate of OPENCODE_TARGET_CANDIDATES) {
2295
- const candidatePath = path3.join(cwd, candidate);
2450
+ const candidatePath = path4.join(cwd, candidate);
2296
2451
  if (lstatOrNull(candidatePath) !== null)
2297
2452
  return candidatePath;
2298
2453
  }
2299
- return path3.join(cwd, "opencode.jsonc");
2454
+ return path4.join(cwd, "opencode.jsonc");
2300
2455
  }
2301
2456
  var IS_WINDOWS = process.platform === "win32";
2302
- var OPEN_FLAGS = IS_WINDOWS ? fs4.constants.O_RDONLY : fs4.constants.O_RDONLY | fs4.constants.O_NOFOLLOW | fs4.constants.O_NONBLOCK;
2457
+ var OPEN_FLAGS = IS_WINDOWS ? fs5.constants.O_RDONLY : fs5.constants.O_RDONLY | fs5.constants.O_NOFOLLOW | fs5.constants.O_NONBLOCK;
2303
2458
  function assertWindowsPreOpenTrust(targetPath) {
2304
2459
  const preStat = lstatOrNull(targetPath);
2305
2460
  if (preStat && (preStat.isSymbolicLink() || !preStat.isFile())) {
@@ -2311,7 +2466,7 @@ function openTrustedExisting(targetPath) {
2311
2466
  assertWindowsPreOpenTrust(targetPath);
2312
2467
  let fd;
2313
2468
  try {
2314
- fd = fs4.openSync(targetPath, OPEN_FLAGS);
2469
+ fd = fs5.openSync(targetPath, OPEN_FLAGS);
2315
2470
  } catch (error) {
2316
2471
  if (error instanceof Error && "code" in error) {
2317
2472
  if (error.code === "ENOENT")
@@ -2323,13 +2478,13 @@ function openTrustedExisting(targetPath) {
2323
2478
  throw error;
2324
2479
  }
2325
2480
  try {
2326
- const stat = fs4.fstatSync(fd);
2481
+ const stat = fs5.fstatSync(fd);
2327
2482
  if (!stat.isFile()) {
2328
2483
  throw createSetupError(`Refusing to read ${targetPath}: not a regular file`);
2329
2484
  }
2330
- return { bytes: fs4.readFileSync(fd), mode: stat.mode & 511 };
2485
+ return { bytes: fs5.readFileSync(fd), mode: stat.mode & 511 };
2331
2486
  } finally {
2332
- fs4.closeSync(fd);
2487
+ fs5.closeSync(fd);
2333
2488
  }
2334
2489
  }
2335
2490
  function atomicWrite(targetPath, content, originalBytes, mode, ops) {
@@ -2344,8 +2499,8 @@ function atomicWrite(targetPath, content, originalBytes, mode, ops) {
2344
2499
  writeTempAndRename(targetPath, content, mode, preserveMode, ops);
2345
2500
  }
2346
2501
  function writeTempAndRename(destPath, content, mode, preserveExactMode, ops) {
2347
- const parentDir = path3.dirname(destPath);
2348
- const tempPath = makeTempPath(parentDir, path3.basename(destPath));
2502
+ const parentDir = path4.dirname(destPath);
2503
+ const tempPath = makeTempPath(parentDir, path4.basename(destPath));
2349
2504
  try {
2350
2505
  ops.writeFileSync(tempPath, content, { flag: "wx", mode });
2351
2506
  if (preserveExactMode) {
@@ -2357,11 +2512,11 @@ function writeTempAndRename(destPath, content, mode, preserveExactMode, ops) {
2357
2512
  throw error;
2358
2513
  }
2359
2514
  }
2360
- function makeTempPath(parentDir, basename2) {
2361
- return path3.join(parentDir, `.${basename2}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
2515
+ function makeTempPath(parentDir, basename) {
2516
+ return path4.join(parentDir, `.${basename}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
2362
2517
  }
2363
2518
  function cleanupTemp(tempPath, ops) {
2364
- if (!fs4.existsSync(tempPath))
2519
+ if (!fs5.existsSync(tempPath))
2365
2520
  return;
2366
2521
  try {
2367
2522
  ops.unlinkSync(tempPath);
@@ -2395,7 +2550,7 @@ function parseOpenCodeJsonc(rawText, targetPath) {
2395
2550
  }
2396
2551
  function setupOpenCode(cwd, ops) {
2397
2552
  const targetPath = resolveOpenCodeTargetPath(cwd);
2398
- assertParentTrusted(path3.dirname(targetPath), cwd);
2553
+ assertParentTrusted(path4.dirname(targetPath), cwd);
2399
2554
  const existing = openTrustedExisting(targetPath);
2400
2555
  if (existing === null) {
2401
2556
  const content = `${JSON.stringify({ plugin: [SYSTEMATIC_PACKAGE_NAME] }, null, 2)}
@@ -2413,8 +2568,8 @@ function setupOpenCode(cwd, ops) {
2413
2568
  throw createSetupError(`Invalid OpenCode config in ${targetPath}: \`plugins\` (plural) is not supported by OpenCode's schema; use singular \`plugin\``);
2414
2569
  }
2415
2570
  if (!Object.hasOwn(parsed, "plugin")) {
2416
- const edits2 = modify(rawText, ["plugin"], [SYSTEMATIC_PACKAGE_NAME], {});
2417
- atomicWrite(targetPath, applyEdits(rawText, edits2), existing.bytes, existing.mode, ops);
2571
+ const edits = modify(rawText, ["plugin"], [SYSTEMATIC_PACKAGE_NAME], {});
2572
+ atomicWrite(targetPath, applyEdits(rawText, edits), existing.bytes, existing.mode, ops);
2418
2573
  return { status: "configured", targetPath };
2419
2574
  }
2420
2575
  const existingValue = parsed.plugin;
@@ -2453,8 +2608,8 @@ function assertPiTaggedEntryUsable(entry, targetPath) {
2453
2608
  }
2454
2609
  }
2455
2610
  function setupPi(cwd, ops) {
2456
- const targetPath = path3.join(cwd, ".pi", "settings.json");
2457
- assertParentTrusted(path3.dirname(targetPath), cwd);
2611
+ const targetPath = path4.join(cwd, ".pi", "settings.json");
2612
+ assertParentTrusted(path4.dirname(targetPath), cwd);
2458
2613
  const existing = openTrustedExisting(targetPath);
2459
2614
  if (existing === null) {
2460
2615
  const content = `${JSON.stringify({ packages: [PI_PACKAGE_IDENTIFIER] }, null, 2)}
@@ -2475,8 +2630,8 @@ function setupPi(cwd, ops) {
2475
2630
  }
2476
2631
  const existingPackages = parsed.packages;
2477
2632
  if (existingPackages === undefined) {
2478
- const edits2 = modify(rawText, ["packages"], [PI_PACKAGE_IDENTIFIER], {});
2479
- atomicWrite(targetPath, applyEdits(rawText, edits2), existing.bytes, existing.mode, ops);
2633
+ const edits = modify(rawText, ["packages"], [PI_PACKAGE_IDENTIFIER], {});
2634
+ atomicWrite(targetPath, applyEdits(rawText, edits), existing.bytes, existing.mode, ops);
2480
2635
  return { status: "configured", targetPath };
2481
2636
  }
2482
2637
  if (!Array.isArray(existingPackages)) {
@@ -2514,10 +2669,10 @@ function setupHarness(harness, cwd, opsOverride) {
2514
2669
  // src/cli.ts
2515
2670
  function readPackageMetadata(packageRoot) {
2516
2671
  try {
2517
- const packageJsonPath = path4.join(packageRoot, "package.json");
2518
- if (!fs5.existsSync(packageJsonPath))
2672
+ const packageJsonPath = path5.join(packageRoot, "package.json");
2673
+ if (!fs6.existsSync(packageJsonPath))
2519
2674
  return {};
2520
- const content = fs5.readFileSync(packageJsonPath, "utf8");
2675
+ const content = fs6.readFileSync(packageJsonPath, "utf8");
2521
2676
  const parsed = JSON.parse(content);
2522
2677
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2523
2678
  return {};
@@ -2531,7 +2686,7 @@ function readPackageMetadata(packageRoot) {
2531
2686
  return {};
2532
2687
  }
2533
2688
  }
2534
- var PACKAGE_ROOT = path4.resolve(import.meta.dirname, "..");
2689
+ var PACKAGE_ROOT = path5.resolve(import.meta.dirname, "..");
2535
2690
  var VERSION = readPackageMetadata(PACKAGE_ROOT).version ?? "unknown";
2536
2691
  var HELP = `
2537
2692
  systematic - OpenCode plugin for systematic engineering workflows
@@ -2590,9 +2745,9 @@ Scope:
2590
2745
  var VALIDATE_REVIEW_ARTIFACT_USAGE = "Usage: systematic validate-review-artifact <path>";
2591
2746
  var REVIEW_ARTIFACT_SCHEMA_RELATIVE_PATH = "skills/ce-review/references/review-summary-schema.json";
2592
2747
  function defaultCapabilityRoots() {
2593
- const configDir = process.env.XDG_CONFIG_HOME ? path4.join(process.env.XDG_CONFIG_HOME, "opencode") : path4.join(os2.homedir(), ".config/opencode");
2748
+ const configDir = process.env.XDG_CONFIG_HOME ? path5.join(process.env.XDG_CONFIG_HOME, "opencode") : path5.join(os2.homedir(), ".config/opencode");
2594
2749
  return {
2595
- agentsRoot: path4.join(PACKAGE_ROOT, "agents"),
2750
+ agentsRoot: path5.join(PACKAGE_ROOT, "agents"),
2596
2751
  configDir,
2597
2752
  cwd: process.cwd(),
2598
2753
  homeDir: os2.homedir(),
@@ -2691,9 +2846,9 @@ function collectConfigMetadata(roots, injected) {
2691
2846
  }
2692
2847
  function resolveCapabilityRootPath(root) {
2693
2848
  try {
2694
- return fs5.realpathSync(root);
2849
+ return fs6.realpathSync(root);
2695
2850
  } catch {
2696
- return path4.resolve(root);
2851
+ return path5.resolve(root);
2697
2852
  }
2698
2853
  }
2699
2854
  function capabilityRoots(roots) {
@@ -2703,7 +2858,7 @@ function capabilityRoots(roots) {
2703
2858
  { id: "package", path: resolveCapabilityRootPath(roots.packageRoot) },
2704
2859
  {
2705
2860
  id: "skills",
2706
- path: resolveCapabilityRootPath(path4.join(roots.packageRoot, "skills"))
2861
+ path: resolveCapabilityRootPath(path5.join(roots.packageRoot, "skills"))
2707
2862
  },
2708
2863
  { id: "user", path: resolveCapabilityRootPath(roots.homeDir) }
2709
2864
  ];
@@ -2744,85 +2899,6 @@ function runCapabilities(options) {
2744
2899
  function runCapabilitiesCli(options) {
2745
2900
  return runCapabilities(options);
2746
2901
  }
2747
- function hasParentDirectoryTraversal(input) {
2748
- return input.split(/[\\/]+/).some((segment) => segment === "..");
2749
- }
2750
- function pathContainsSymlink(candidate) {
2751
- let current = path4.parse(candidate).root;
2752
- const relative2 = path4.relative(current, candidate);
2753
- for (const segment of relative2.split(path4.sep)) {
2754
- if (!segment)
2755
- continue;
2756
- current = path4.join(current, segment);
2757
- try {
2758
- if (fs5.lstatSync(current).isSymbolicLink())
2759
- return true;
2760
- } catch {
2761
- return false;
2762
- }
2763
- }
2764
- return false;
2765
- }
2766
- function isWithinDirectory(candidate, directory) {
2767
- const relative2 = path4.relative(directory, candidate);
2768
- return relative2 !== "" && relative2 !== ".." && !relative2.startsWith(`..${path4.sep}`) && !path4.isAbsolute(relative2);
2769
- }
2770
- function resolveReviewArtifactPath(input, cwd) {
2771
- if (hasParentDirectoryTraversal(input)) {
2772
- return {
2773
- message: "Review artifact path must not contain parent-directory traversal",
2774
- ok: false
2775
- };
2776
- }
2777
- const artifactRoot = path4.resolve(cwd, ".context", "systematic", "ce-review");
2778
- let canonicalRoot;
2779
- try {
2780
- canonicalRoot = fs5.realpathSync(artifactRoot);
2781
- if (!fs5.statSync(canonicalRoot).isDirectory()) {
2782
- return {
2783
- message: "Review artifact directory is not a directory",
2784
- ok: false
2785
- };
2786
- }
2787
- } catch {
2788
- return { message: "Review artifact directory is unavailable", ok: false };
2789
- }
2790
- const candidate = path4.resolve(cwd, input);
2791
- if (pathContainsSymlink(candidate)) {
2792
- return {
2793
- message: "Review artifact path must not contain symlinks",
2794
- ok: false
2795
- };
2796
- }
2797
- let canonicalTarget;
2798
- try {
2799
- canonicalTarget = fs5.realpathSync(candidate);
2800
- } catch {
2801
- return { message: "Review artifact file was not found", ok: false };
2802
- }
2803
- if (!isWithinDirectory(canonicalTarget, canonicalRoot)) {
2804
- return {
2805
- message: "Review artifact path must remain inside .context/systematic/ce-review",
2806
- ok: false
2807
- };
2808
- }
2809
- try {
2810
- if (!fs5.lstatSync(canonicalTarget).isFile()) {
2811
- return {
2812
- message: "Review artifact target is not a regular file",
2813
- ok: false
2814
- };
2815
- }
2816
- } catch {
2817
- return { message: "Review artifact file was not found", ok: false };
2818
- }
2819
- return { ok: true, path: canonicalTarget };
2820
- }
2821
- function formatReviewArtifactIssuePath(issuePath) {
2822
- if (issuePath.length === 0)
2823
- return "$";
2824
- return issuePath.map((segment) => typeof segment === "number" ? String(segment) : segment).join(".");
2825
- }
2826
2902
  function validateReviewArtifactArgument(argv) {
2827
2903
  const commandIndex = argv[0] === "systematic" ? 1 : 0;
2828
2904
  if (argv[commandIndex] !== "validate-review-artifact")
@@ -2831,25 +2907,6 @@ function validateReviewArtifactArgument(argv) {
2831
2907
  return;
2832
2908
  return argv[commandIndex + 1];
2833
2909
  }
2834
- function readReviewArtifact(filePath) {
2835
- let content;
2836
- try {
2837
- content = fs5.readFileSync(filePath, "utf8");
2838
- } catch {
2839
- return { message: "Review artifact file could not be read", ok: false };
2840
- }
2841
- try {
2842
- return { ok: true, value: JSON.parse(content) };
2843
- } catch (error) {
2844
- return {
2845
- message: error instanceof SyntaxError ? "Review artifact contains malformed JSON" : "Review artifact file could not be read",
2846
- ok: false
2847
- };
2848
- }
2849
- }
2850
- function isLegacyReviewArtifact(value) {
2851
- return value !== null && typeof value === "object" && !Array.isArray(value) && !Object.hasOwn(value, "schema_version");
2852
- }
2853
2910
  function runValidateReviewArtifact(options) {
2854
2911
  const outputSink = options.outputSink ?? ((message) => console.log(message));
2855
2912
  const errorSink = options.errorSink ?? ((message) => console.error(message));
@@ -2916,7 +2973,7 @@ function setupCommand(rest) {
2916
2973
  }
2917
2974
  }
2918
2975
  function listItems(type) {
2919
- const packageRoot = path4.resolve(import.meta.dirname, "..");
2976
+ const packageRoot = path5.resolve(import.meta.dirname, "..");
2920
2977
  const bundledDir = packageRoot;
2921
2978
  let finder;
2922
2979
  let subdir;
@@ -2937,7 +2994,7 @@ function listItems(type) {
2937
2994
  console.error(`Unknown type: ${type}. Use: skills, agents, commands`);
2938
2995
  process.exit(1);
2939
2996
  }
2940
- const items = finder(path4.join(bundledDir, subdir));
2997
+ const items = finder(path5.join(bundledDir, subdir));
2941
2998
  if (items.length === 0) {
2942
2999
  console.log(`No ${type} found.`);
2943
3000
  return;
@@ -2954,15 +3011,15 @@ function configShow() {
2954
3011
  `);
2955
3012
  console.log(` User config: ${paths.userConfig}`);
2956
3013
  console.log(` Project config: ${paths.projectConfig}`);
2957
- if (fs5.existsSync(paths.projectConfig)) {
3014
+ if (fs6.existsSync(paths.projectConfig)) {
2958
3015
  console.log(`
2959
3016
  Project configuration:`);
2960
- console.log(fs5.readFileSync(paths.projectConfig, "utf-8"));
3017
+ console.log(fs6.readFileSync(paths.projectConfig, "utf-8"));
2961
3018
  }
2962
- if (fs5.existsSync(paths.userConfig)) {
3019
+ if (fs6.existsSync(paths.userConfig)) {
2963
3020
  console.log(`
2964
3021
  User configuration:`);
2965
- console.log(fs5.readFileSync(paths.userConfig, "utf-8"));
3022
+ console.log(fs6.readFileSync(paths.userConfig, "utf-8"));
2966
3023
  }
2967
3024
  }
2968
3025
  function configPath() {