@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/claude-code-validator.d.ts +8 -0
- package/dist/cli.js +287 -230
- package/dist/index-7cyxcwf5.js +8486 -0
- package/dist/index.js +127 -124
- package/dist/lib/review-artifact-path.d.ts +20 -0
- package/dist/pi.js +23 -7
- package/package.json +6 -6
- package/skills/ce-review/references/review-summary-schema.json +1 -1
- package/skills/ce-review/references/synthesis-artifact-contract.md +21 -9
- package/dist/index-0stf3ag0.js +0 -17100
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
|
-
|
|
6
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
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
|
|
50
|
+
import fs6 from "fs";
|
|
21
51
|
import os2 from "os";
|
|
22
|
-
import
|
|
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
|
|
1480
|
+
const fileName = parts.at(-1) ?? "";
|
|
1428
1481
|
return {
|
|
1429
1482
|
category: parts[1],
|
|
1430
|
-
agentKey:
|
|
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
|
|
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
|
|
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) =>
|
|
1998
|
-
var DispatchOutcomeSchema =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2016
|
-
var HeadShaSchema =
|
|
2017
|
-
var CompletedAtSchema =
|
|
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 =
|
|
2181
|
+
var RiskCriticalPersonaSchema = _enum(RISK_CRITICAL_PERSONAS);
|
|
2027
2182
|
var FindingTitleSchema = boundedText(256);
|
|
2028
|
-
var SeveritySchema =
|
|
2183
|
+
var SeveritySchema = _enum(["P0", "P1", "P2", "P3", "unknown"]);
|
|
2029
2184
|
var FindingSeveritySchema = SeveritySchema.exclude(["unknown"]);
|
|
2030
|
-
var AutofixClassSchema =
|
|
2185
|
+
var AutofixClassSchema = _enum([
|
|
2031
2186
|
"safe_auto",
|
|
2032
2187
|
"gated_auto",
|
|
2033
2188
|
"manual",
|
|
2034
2189
|
"advisory"
|
|
2035
2190
|
]);
|
|
2036
|
-
var OwnerSchema =
|
|
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 =
|
|
2044
|
-
overflow:
|
|
2198
|
+
var OverflowEvidenceSchema = object({
|
|
2199
|
+
overflow: literal(true),
|
|
2045
2200
|
excerpt: BoundedEvidenceStringSchema
|
|
2046
2201
|
}).strict();
|
|
2047
|
-
var EvidenceSchema =
|
|
2048
|
-
var AdmittedInputFindingSchema =
|
|
2049
|
-
record_type:
|
|
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:
|
|
2207
|
+
confidence: number().min(0).max(1),
|
|
2053
2208
|
disposition: AdmittedDispositionSchema,
|
|
2054
2209
|
reason: ReasonSchema
|
|
2055
2210
|
}).strict();
|
|
2056
|
-
var RejectedInputFindingSchema =
|
|
2057
|
-
record_type:
|
|
2211
|
+
var RejectedInputFindingSchema = object({
|
|
2212
|
+
record_type: literal("rejected_summary"),
|
|
2058
2213
|
reviewer: ReviewerSchema,
|
|
2059
2214
|
dispatch_outcome: DispatchOutcomeSchema,
|
|
2060
|
-
rejected_finding_count:
|
|
2061
|
-
rejected_severities:
|
|
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 =
|
|
2228
|
+
var InputFindingSchema = discriminatedUnion("record_type", [
|
|
2074
2229
|
AdmittedInputFindingSchema,
|
|
2075
2230
|
RejectedInputFindingSchema
|
|
2076
2231
|
]);
|
|
2077
|
-
var ProvenanceSchema =
|
|
2232
|
+
var ProvenanceSchema = object({
|
|
2078
2233
|
fingerprint: boundedText(512),
|
|
2079
|
-
submitters:
|
|
2080
|
-
agreement_credit:
|
|
2234
|
+
submitters: array(ReviewerSchema).max(MAX_PERSONAS),
|
|
2235
|
+
agreement_credit: array(ReviewerSchema).max(MAX_PERSONAS)
|
|
2081
2236
|
}).strict();
|
|
2082
|
-
var SynthesizedFindingFieldsSchema =
|
|
2237
|
+
var SynthesizedFindingFieldsSchema = object({
|
|
2083
2238
|
title: FindingTitleSchema,
|
|
2084
2239
|
severity: FindingSeveritySchema,
|
|
2085
2240
|
file: RepoRelativePathSchema,
|
|
2086
|
-
line:
|
|
2241
|
+
line: number().int().positive(),
|
|
2087
2242
|
why_it_matters: boundedText(2048),
|
|
2088
2243
|
autofix_class: AutofixClassSchema,
|
|
2089
2244
|
owner: OwnerSchema,
|
|
2090
|
-
requires_verification:
|
|
2091
|
-
confidence:
|
|
2245
|
+
requires_verification: boolean(),
|
|
2246
|
+
confidence: number().min(0).max(1),
|
|
2092
2247
|
evidence: EvidenceSchema,
|
|
2093
|
-
pre_existing:
|
|
2094
|
-
suggested_fix:
|
|
2095
|
-
validated:
|
|
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:
|
|
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 =
|
|
2264
|
+
var DispatchSchema = object({
|
|
2110
2265
|
persona: ReviewerSchema,
|
|
2111
2266
|
dispatch_outcome: DispatchOutcomeSchema,
|
|
2112
|
-
input_finding_count:
|
|
2267
|
+
input_finding_count: number().int().nonnegative().max(MAX_FINDINGS),
|
|
2113
2268
|
rejection_reason: ReasonSchema.optional(),
|
|
2114
|
-
selection_surface:
|
|
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 =
|
|
2126
|
-
surviving:
|
|
2127
|
-
merged:
|
|
2128
|
-
suppressed:
|
|
2129
|
-
filtered:
|
|
2130
|
-
rejected:
|
|
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 =
|
|
2133
|
-
reviewers:
|
|
2134
|
-
validators:
|
|
2135
|
-
residual_risks:
|
|
2136
|
-
testing_gaps:
|
|
2137
|
-
failed_reviewers:
|
|
2138
|
-
validator_failures:
|
|
2139
|
-
intent_uncertainty:
|
|
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 =
|
|
2296
|
+
var DeclinedMergeSchema = object({
|
|
2142
2297
|
file: RepoRelativePathSchema,
|
|
2143
|
-
input_finding_ids:
|
|
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 =
|
|
2147
|
-
status:
|
|
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 =
|
|
2325
|
+
var RiskCoverageSchema = object({
|
|
2171
2326
|
persona: RiskCriticalPersonaSchema,
|
|
2172
|
-
satisfied:
|
|
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 =
|
|
2191
|
-
schema_version:
|
|
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:
|
|
2350
|
+
mode: _enum(["interactive", "autofix", "headless"]),
|
|
2196
2351
|
harness: HarnessSchema,
|
|
2197
|
-
run_status:
|
|
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:
|
|
2206
|
-
input_findings:
|
|
2207
|
-
findings:
|
|
2208
|
-
declined_merges:
|
|
2209
|
-
risk_coverage:
|
|
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:
|
|
2212
|
-
residual_actionable_work:
|
|
2213
|
-
advisory_outputs:
|
|
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
|
|
2220
|
-
import
|
|
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:
|
|
2231
|
-
renameSync:
|
|
2232
|
-
unlinkSync:
|
|
2233
|
-
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
|
|
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 =
|
|
2265
|
-
const realCwd =
|
|
2266
|
-
const
|
|
2267
|
-
if (
|
|
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
|
-
|
|
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(
|
|
2445
|
+
const opencodeDirStat = lstatOrNull(path4.join(cwd, ".opencode"));
|
|
2291
2446
|
if (opencodeDirStat?.isSymbolicLink()) {
|
|
2292
|
-
return
|
|
2447
|
+
return path4.join(cwd, OPENCODE_TARGET_CANDIDATES[0]);
|
|
2293
2448
|
}
|
|
2294
2449
|
for (const candidate of OPENCODE_TARGET_CANDIDATES) {
|
|
2295
|
-
const candidatePath =
|
|
2450
|
+
const candidatePath = path4.join(cwd, candidate);
|
|
2296
2451
|
if (lstatOrNull(candidatePath) !== null)
|
|
2297
2452
|
return candidatePath;
|
|
2298
2453
|
}
|
|
2299
|
-
return
|
|
2454
|
+
return path4.join(cwd, "opencode.jsonc");
|
|
2300
2455
|
}
|
|
2301
2456
|
var IS_WINDOWS = process.platform === "win32";
|
|
2302
|
-
var OPEN_FLAGS = IS_WINDOWS ?
|
|
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 =
|
|
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 =
|
|
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:
|
|
2485
|
+
return { bytes: fs5.readFileSync(fd), mode: stat.mode & 511 };
|
|
2331
2486
|
} finally {
|
|
2332
|
-
|
|
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 =
|
|
2348
|
-
const tempPath = makeTempPath(parentDir,
|
|
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,
|
|
2361
|
-
return
|
|
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 (!
|
|
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(
|
|
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
|
|
2417
|
-
atomicWrite(targetPath, applyEdits(rawText,
|
|
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 =
|
|
2457
|
-
assertParentTrusted(
|
|
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
|
|
2479
|
-
atomicWrite(targetPath, applyEdits(rawText,
|
|
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 =
|
|
2518
|
-
if (!
|
|
2672
|
+
const packageJsonPath = path5.join(packageRoot, "package.json");
|
|
2673
|
+
if (!fs6.existsSync(packageJsonPath))
|
|
2519
2674
|
return {};
|
|
2520
|
-
const content =
|
|
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 =
|
|
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 ?
|
|
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:
|
|
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
|
|
2849
|
+
return fs6.realpathSync(root);
|
|
2695
2850
|
} catch {
|
|
2696
|
-
return
|
|
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(
|
|
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 =
|
|
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(
|
|
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 (
|
|
3014
|
+
if (fs6.existsSync(paths.projectConfig)) {
|
|
2958
3015
|
console.log(`
|
|
2959
3016
|
Project configuration:`);
|
|
2960
|
-
console.log(
|
|
3017
|
+
console.log(fs6.readFileSync(paths.projectConfig, "utf-8"));
|
|
2961
3018
|
}
|
|
2962
|
-
if (
|
|
3019
|
+
if (fs6.existsSync(paths.userConfig)) {
|
|
2963
3020
|
console.log(`
|
|
2964
3021
|
User configuration:`);
|
|
2965
|
-
console.log(
|
|
3022
|
+
console.log(fs6.readFileSync(paths.userConfig, "utf-8"));
|
|
2966
3023
|
}
|
|
2967
3024
|
}
|
|
2968
3025
|
function configPath() {
|