@intentset/cli 0.10.0 → 0.13.0
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/agents.d.ts.map +1 -1
- package/dist/agents.js +43 -2
- package/dist/agents.js.map +1 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +20 -4
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/graph.js +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +10 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts +49 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +295 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/review.js +1 -1
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/shared.d.ts +8 -0
- package/dist/commands/shared.d.ts.map +1 -1
- package/dist/commands/shared.js +15 -5
- package/dist/commands/shared.js.map +1 -1
- package/dist/glob.d.ts +4 -13
- package/dist/glob.d.ts.map +1 -1
- package/dist/glob.js +4 -57
- package/dist/glob.js.map +1 -1
- package/dist/main.d.ts +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +78 -5
- package/dist/main.js.map +1 -1
- package/dist/session.d.ts +1 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +6 -4
- package/dist/session.js.map +1 -1
- package/dist/subject.d.ts +2 -0
- package/dist/subject.d.ts.map +1 -1
- package/dist/subject.js +11 -1
- package/dist/subject.js.map +1 -1
- package/package.json +9 -9
- package/src/agents.ts +43 -2
- package/src/commands/context.ts +24 -3
- package/src/commands/graph.ts +1 -1
- package/src/commands/init.ts +10 -8
- package/src/commands/list.ts +346 -0
- package/src/commands/review.ts +1 -1
- package/src/commands/shared.ts +20 -0
- package/src/glob.ts +4 -56
- package/src/main.ts +85 -6
- package/src/session.ts +5 -4
- package/src/subject.ts +13 -1
- package/dist/slices.d.ts +0 -24
- package/dist/slices.d.ts.map +0 -1
- package/dist/slices.js +0 -48
- package/dist/slices.js.map +0 -1
- package/src/slices.ts +0 -54
package/src/commands/shared.ts
CHANGED
|
@@ -3,10 +3,30 @@
|
|
|
3
3
|
* the graph validated, and the CORE003-style refusal for an ID the graph does
|
|
4
4
|
* not hold.
|
|
5
5
|
*/
|
|
6
|
+
import type { TypeScriptMissingError } from "@intentset/architecture";
|
|
6
7
|
import { type Diagnostic, ID_PATTERN } from "@intentset/core";
|
|
7
8
|
import { count } from "../output.ts";
|
|
8
9
|
import type { Session } from "../session.ts";
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* What the CLI says when a check needed TypeScript 7's scanner and the `typescript` the repository gave
|
|
13
|
+
* @intentset/architecture is older or absent: the version found and the command that runs the toolchain with its
|
|
14
|
+
* own TypeScript, for the arguments that were given, with pnpm (which resolves the peer to the repository's own
|
|
15
|
+
* copy) and with npm (which installs the peer beside the CLI, so it rarely gets here).
|
|
16
|
+
*/
|
|
17
|
+
export function typescriptMissing(error: TypeScriptMissingError, argv: readonly string[]): string {
|
|
18
|
+
const args = argv.length === 0 ? "" : ` ${argv.join(" ")}`;
|
|
19
|
+
const found =
|
|
20
|
+
error.found === null
|
|
21
|
+
? "no `typescript` resolves from @intentset/architecture"
|
|
22
|
+
: `the \`typescript\` this repository gives @intentset/architecture is ${error.found}`;
|
|
23
|
+
return (
|
|
24
|
+
`intentset: the architecture check needs TypeScript 7, and ${found}. Run the toolchain without installing it, ` +
|
|
25
|
+
`as \`pnpm dlx --package=@intentset/cli --package=typescript@7 intentset${args}\`, or with npm as ` +
|
|
26
|
+
`\`npx -p @intentset/cli -p typescript@7 intentset${args}\`; or give the repository TypeScript 7.`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
10
30
|
/** The validation state a query ran over, so a reader knows whether the graph was whole. */
|
|
11
31
|
export function validationSummary(session: Session, level: string) {
|
|
12
32
|
return {
|
package/src/glob.ts
CHANGED
|
@@ -1,58 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The pattern language of `.intentset/config.yaml` scope and ignore lists
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* is special: no `?`, no classes, no braces, no negation, so there is no
|
|
6
|
-
* precedence to explain. A pattern and a path match segment by segment, and
|
|
7
|
-
* `*` never crosses a `/`.
|
|
8
|
-
*
|
|
9
|
-
* The architecture package has its own copy for claims. Duplicating thirty
|
|
10
|
-
* lines keeps the CLI's core commands independent of it.
|
|
2
|
+
* The pattern language of `.intentset/config.yaml` scope and ignore lists and of slice claims (VSA §3). It moved to
|
|
3
|
+
* `@intentset/core` in 0.11.0 with the slice helpers that use it (ADR 0016), and the CLI re-exports it, so
|
|
4
|
+
* `matchGlob` and `matchAny` stay on its public surface.
|
|
11
5
|
*/
|
|
12
|
-
|
|
13
|
-
/** VSA §3: true when the repository-relative POSIX `path` matches `pattern`. */
|
|
14
|
-
export function matchGlob(pattern: string, path: string): boolean {
|
|
15
|
-
const p = normalize(pattern).split("/");
|
|
16
|
-
const s = path.split("/");
|
|
17
|
-
// Memo of (pattern index, path index) pairs already known not to match, so
|
|
18
|
-
// several `**` in one pattern stay polynomial rather than exponential.
|
|
19
|
-
const failed = new Set<number>();
|
|
20
|
-
const width = s.length + 1;
|
|
21
|
-
const match = (i: number, j: number): boolean => {
|
|
22
|
-
if (failed.has(i * width + j)) return false;
|
|
23
|
-
let ok: boolean;
|
|
24
|
-
if (i === p.length) ok = j === s.length;
|
|
25
|
-
else if (p[i] === "**") ok = match(i + 1, j) || (j < s.length && match(i, j + 1));
|
|
26
|
-
else ok = j < s.length && matchSegment(p[i], s[j]) && match(i + 1, j + 1);
|
|
27
|
-
if (!ok) failed.add(i * width + j);
|
|
28
|
-
return ok;
|
|
29
|
-
};
|
|
30
|
-
return match(0, 0);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** True when any of `patterns` matches `path`. */
|
|
34
|
-
export function matchAny(patterns: readonly string[], path: string): boolean {
|
|
35
|
-
return patterns.some((pattern) => matchGlob(pattern, path));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** A leading `./` names the same path; repeated `/` separate nothing. */
|
|
39
|
-
function normalize(pattern: string): string {
|
|
40
|
-
return pattern.replace(/^(?:\.\/)+/, "").replace(/\/{2,}/g, "/");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const SEGMENTS = new Map<string, RegExp>();
|
|
44
|
-
|
|
45
|
-
/** One segment: literal text, with each `*` standing for any run of characters other than `/`. */
|
|
46
|
-
function matchSegment(pattern: string, name: string): boolean {
|
|
47
|
-
if (!pattern.includes("*")) return pattern === name;
|
|
48
|
-
let regex = SEGMENTS.get(pattern);
|
|
49
|
-
if (regex === undefined) {
|
|
50
|
-
const body = pattern
|
|
51
|
-
.split("*")
|
|
52
|
-
.map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&"))
|
|
53
|
-
.join("[^/]*");
|
|
54
|
-
regex = new RegExp(`^${body}$`);
|
|
55
|
-
SEGMENTS.set(pattern, regex);
|
|
56
|
-
}
|
|
57
|
-
return regex.test(name);
|
|
58
|
-
}
|
|
6
|
+
export { matchAny, matchGlob } from "@intentset/core";
|
package/src/main.ts
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
* exiting, so tests run it in-process with captured io.
|
|
5
5
|
*
|
|
6
6
|
* Exit codes (Core §11, ADR 0004): 0 no errors, 1 validation errors, 2
|
|
7
|
-
* invocation or tool failure
|
|
8
|
-
*
|
|
7
|
+
* invocation or tool failure (list exits 0 whenever it answered, and 1 only
|
|
8
|
+
* with --check). validate, graph, architecture check, impact, context, list,
|
|
9
|
+
* review, evidence stale, serve, mcp and guide never write; graph --out, architecture
|
|
9
10
|
* check --write-baseline, evidence import --out, publish --out and serve
|
|
10
11
|
* --out write only what they name; init only creates; migrate rewrites the
|
|
11
12
|
* two version values of each record's frontmatter and nothing else.
|
|
12
13
|
*/
|
|
13
14
|
import { parseArgs } from "node:util";
|
|
14
|
-
import type
|
|
15
|
+
import { type Mode, TypeScriptMissingError } from "@intentset/architecture";
|
|
15
16
|
import { EVIDENCE_BINDINGS, type EvidenceBinding, LEVELS, type Level } from "@intentset/core";
|
|
16
17
|
import type { RunScope } from "@intentset/verification";
|
|
17
18
|
import { architectureCommand } from "./commands/architecture.ts";
|
|
@@ -22,6 +23,13 @@ import { graphCommand, graphUsageProblem, parseRelease } from "./commands/graph.
|
|
|
22
23
|
import { guideCommand } from "./commands/guide.ts";
|
|
23
24
|
import { impactCommand } from "./commands/impact.ts";
|
|
24
25
|
import { initCommand } from "./commands/init.ts";
|
|
26
|
+
import {
|
|
27
|
+
listCommand,
|
|
28
|
+
listFromExportCommand,
|
|
29
|
+
type ListOptions,
|
|
30
|
+
listUsageProblem,
|
|
31
|
+
SELECTOR_OPTIONS,
|
|
32
|
+
} from "./commands/list.ts";
|
|
25
33
|
import { migrateCommand, migrateUsageProblem } from "./commands/migrate.ts";
|
|
26
34
|
import { mcpCommand, mcpUsageProblem } from "./commands/mcp.ts";
|
|
27
35
|
import { publishCommand, publishUsageProblem } from "./commands/publish.ts";
|
|
@@ -29,6 +37,7 @@ import { reviewCommand } from "./commands/review.ts";
|
|
|
29
37
|
import { serveCommand } from "./commands/serve.ts";
|
|
30
38
|
import { validateCommand } from "./commands/validate.ts";
|
|
31
39
|
import { type Io, TOOL } from "./output.ts";
|
|
40
|
+
import { typescriptMissing } from "./commands/shared.ts";
|
|
32
41
|
import { type CarrierName, CARRIERS, findRoot } from "./repository.ts";
|
|
33
42
|
import { EVIDENCE_DIR, evidenceFiles, openSession, type SessionOptions } from "./session.ts";
|
|
34
43
|
|
|
@@ -50,7 +59,7 @@ commands
|
|
|
50
59
|
graph [--format json] [--include-bodies] [--release <product>:<label>] [--out <file>]
|
|
51
60
|
[--generated-at <time>] [--report evidence|knowledge|impact|ownership|all]...
|
|
52
61
|
[--include-restricted]
|
|
53
|
-
print the intentset/export/0.
|
|
62
|
+
print the intentset/export/0.5 envelope, or write it to --out;
|
|
54
63
|
each --report adds a section (ownership needs L2, evidence L3; all
|
|
55
64
|
is every one the level reads). Restricted artifacts are withheld
|
|
56
65
|
and counted unless --include-restricted
|
|
@@ -69,6 +78,19 @@ commands
|
|
|
69
78
|
exits 1 when any needs a run
|
|
70
79
|
impact <ID> what depends on an artifact: direct, candidates, review context,
|
|
71
80
|
ancestors
|
|
81
|
+
list [--id <ID|prefix*>] [--type <t>] [--status <s>] [--delivery <d>] [--owner <o>]
|
|
82
|
+
[--audience <a>] [--visibility <v>] [--under <ID>] [--product <ID>] [--release <label>]
|
|
83
|
+
[--not-release <label>] [--role <r>] [--edition <e>] [--flag <f>]
|
|
84
|
+
[--has-extension <key>] [--has [some:|none:|all:]<out|in>:<kind>[=<status>,...]]
|
|
85
|
+
[--describing <slice>] [--claims <path>] [--where <file.json>] [--from <export.json>]
|
|
86
|
+
[--include-restricted] [--count-by <field>] [--check] [--format table|json|ids]
|
|
87
|
+
the records that match: fields combine with AND, the values of one
|
|
88
|
+
field (comma-separated or repeated) with OR, and matching is exact
|
|
89
|
+
(a trailing * on an ID is a prefix). --where gives the selector as
|
|
90
|
+
JSON instead; --from reads an export, not the tree. A record whose
|
|
91
|
+
answer depends on a withheld link is undetermined, listed apart.
|
|
92
|
+
Never writes; exits 0 whenever it answered, and with --check 1 when
|
|
93
|
+
any record matched or is undetermined (spec/list.md)
|
|
72
94
|
context <ID|path> [--include-restricted]
|
|
73
95
|
the bounded engineering context of an artifact, with paths and
|
|
74
96
|
bodies; given a file a slice claims, the context of that slice
|
|
@@ -126,7 +148,8 @@ options
|
|
|
126
148
|
|
|
127
149
|
exit codes: 0 no errors, 1 validation errors (or, with review --fail-on-drift,
|
|
128
150
|
unacknowledged drift; with --fail-on-baseline-growth, a baseline
|
|
129
|
-
entry the base did not hold
|
|
151
|
+
entry the base did not hold; with list --check, a record matched or
|
|
152
|
+
undetermined), 2 invocation or tool failure`;
|
|
130
153
|
|
|
131
154
|
const COMMANDS = [
|
|
132
155
|
"init",
|
|
@@ -137,6 +160,7 @@ const COMMANDS = [
|
|
|
137
160
|
"evidence",
|
|
138
161
|
"impact",
|
|
139
162
|
"context",
|
|
163
|
+
"list",
|
|
140
164
|
"review",
|
|
141
165
|
"publish",
|
|
142
166
|
"serve",
|
|
@@ -182,6 +206,7 @@ const COMMAND_OPTIONS: Record<string, string[]> = {
|
|
|
182
206
|
],
|
|
183
207
|
impact: LEVEL_OPTIONS,
|
|
184
208
|
context: ["include-restricted", ...LEVEL_OPTIONS],
|
|
209
|
+
list: [...SELECTOR_OPTIONS, "where", "from", "count-by", "check", "format", "include-restricted"],
|
|
185
210
|
review: ["base", "fail-on-drift", "fail-on-baseline-growth", ...LEVEL_OPTIONS],
|
|
186
211
|
serve: ["port", "host", "out", ...LEVEL_OPTIONS],
|
|
187
212
|
mcp: [
|
|
@@ -223,6 +248,21 @@ const POSITIONALS: Record<string, { subs?: Record<string, number>; count: number
|
|
|
223
248
|
};
|
|
224
249
|
|
|
225
250
|
export async function main(argv: string[], io: Io): Promise<number> {
|
|
251
|
+
try {
|
|
252
|
+
return await dispatch(argv, io);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
// The one failure a command raises rather than reports: the scanner's TypeScript is not there. It arrives from
|
|
255
|
+
// the first check that needs it (architecture check, validate at L2 and above, review, a subject-bound
|
|
256
|
+
// evidence read), and every other command has already run without it.
|
|
257
|
+
if (error instanceof TypeScriptMissingError) {
|
|
258
|
+
io.stderr(`${typescriptMissing(error, argv)}\n`);
|
|
259
|
+
return 2;
|
|
260
|
+
}
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function dispatch(argv: string[], io: Io): Promise<number> {
|
|
226
266
|
let parsed: ReturnType<typeof parse>;
|
|
227
267
|
try {
|
|
228
268
|
parsed = parse(argv);
|
|
@@ -336,7 +376,7 @@ export async function main(argv: string[], io: Io): Promise<number> {
|
|
|
336
376
|
|
|
337
377
|
// Evidence scope: --product with --release, or graph's --release <product>:<label>.
|
|
338
378
|
let scope: RunScope | null = null;
|
|
339
|
-
if ((command !== "evidence" || sub === "stale") && command !== "publish" && command !== "mcp") {
|
|
379
|
+
if ((command !== "evidence" || sub === "stale") && command !== "publish" && command !== "mcp" && command !== "list") {
|
|
340
380
|
if (command === "graph" || command === "evidence") {
|
|
341
381
|
const release = values.release === undefined ? null : parseRelease(values.release);
|
|
342
382
|
scope = release === null ? null : { product: release.product, release: release.label };
|
|
@@ -425,6 +465,32 @@ export async function main(argv: string[], io: Io): Promise<number> {
|
|
|
425
465
|
const session = openSession(command, sessionOptions(levelGiven ?? "L1"), io);
|
|
426
466
|
return typeof session === "number" ? session : impactCommand(session, args[0], values.json, io);
|
|
427
467
|
}
|
|
468
|
+
case "list": {
|
|
469
|
+
const options: ListOptions = {
|
|
470
|
+
given: tokens.flatMap((token) =>
|
|
471
|
+
token.kind === "option" && SELECTOR_OPTIONS.includes(token.name)
|
|
472
|
+
? [{ name: token.name, value: token.value ?? "" }]
|
|
473
|
+
: [],
|
|
474
|
+
),
|
|
475
|
+
where: values.where,
|
|
476
|
+
from: values.from,
|
|
477
|
+
countBy: values["count-by"],
|
|
478
|
+
format: values.format,
|
|
479
|
+
json: values.json,
|
|
480
|
+
check: values.check,
|
|
481
|
+
includeRestricted: values["include-restricted"],
|
|
482
|
+
};
|
|
483
|
+
const checked = listUsageProblem(options, io);
|
|
484
|
+
if ("problem" in checked) return usage(checked.problem);
|
|
485
|
+
if (options.from !== undefined) {
|
|
486
|
+
const tree = (["root", "carrier", "level"] as const).find((name) => values[name] !== undefined);
|
|
487
|
+
if (tree !== undefined)
|
|
488
|
+
return usage(`--${tree} reads the tree; --from reads the records from the export alone`);
|
|
489
|
+
return listFromExportCommand(checked, { ...options, from: options.from }, io);
|
|
490
|
+
}
|
|
491
|
+
const session = openSession(command, sessionOptions(levelGiven ?? "L1"), io);
|
|
492
|
+
return typeof session === "number" ? session : listCommand(session, checked, options, io);
|
|
493
|
+
}
|
|
428
494
|
case "context": {
|
|
429
495
|
const session = openSession(command, sessionOptions(levelGiven ?? "L1"), io);
|
|
430
496
|
if (typeof session === "number") return session;
|
|
@@ -567,6 +633,19 @@ function parse(argv: string[]) {
|
|
|
567
633
|
host: { type: "string" },
|
|
568
634
|
to: { type: "string" },
|
|
569
635
|
"allow-dirty": { type: "boolean", default: false },
|
|
636
|
+
id: { type: "string", multiple: true },
|
|
637
|
+
type: { type: "string", multiple: true },
|
|
638
|
+
status: { type: "string", multiple: true },
|
|
639
|
+
delivery: { type: "string", multiple: true },
|
|
640
|
+
owner: { type: "string", multiple: true },
|
|
641
|
+
under: { type: "string", multiple: true },
|
|
642
|
+
"not-release": { type: "string", multiple: true },
|
|
643
|
+
has: { type: "string", multiple: true },
|
|
644
|
+
"has-extension": { type: "string", multiple: true },
|
|
645
|
+
describing: { type: "string", multiple: true },
|
|
646
|
+
claims: { type: "string", multiple: true },
|
|
647
|
+
where: { type: "string" },
|
|
648
|
+
"count-by": { type: "string" },
|
|
570
649
|
},
|
|
571
650
|
});
|
|
572
651
|
}
|
package/src/session.ts
CHANGED
|
@@ -48,11 +48,10 @@ import {
|
|
|
48
48
|
import { insideRoot } from "./outputs.ts";
|
|
49
49
|
import { formatDiagnostic, type Io, type Snapshot } from "./output.ts";
|
|
50
50
|
import { type CarrierName, CONFIG_PATH, findRoot, loadRepository, type Repository } from "./repository.ts";
|
|
51
|
-
import { bindingFor, subjectsOf } from "./subject.ts";
|
|
51
|
+
import { bindingFor, EVIDENCE_DIR, subjectsOf } from "./subject.ts";
|
|
52
52
|
import { readTree, type Tree } from "./tree.ts";
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
export const EVIDENCE_DIR = ".intentset/evidence";
|
|
54
|
+
export { EVIDENCE_DIR } from "./subject.ts";
|
|
56
55
|
|
|
57
56
|
export interface SessionOptions {
|
|
58
57
|
root?: string;
|
|
@@ -290,7 +289,9 @@ export function publicationReadiness(graph: Graph): { checked: string[]; diagnos
|
|
|
290
289
|
const review = reviewStatus(graph, artifact);
|
|
291
290
|
if (review.status === "current") continue;
|
|
292
291
|
const reasons: string[] = [];
|
|
293
|
-
if (review.
|
|
292
|
+
if (review.unknownHash !== null) {
|
|
293
|
+
reasons.push(`its review pins hashes of kind ${review.unknownHash}, which this version does not read`);
|
|
294
|
+
} else if (review.changed.length > 0) reasons.push(`${review.changed.join(", ")} changed since review`);
|
|
294
295
|
if (review.missing.length > 0) reasons.push(`no review pin for ${review.missing.join(", ")}`);
|
|
295
296
|
if (review.reviewer === null) reasons.push("it names no reviewer");
|
|
296
297
|
if (review.reviewedAt === null) reasons.push("it gives no review date");
|
package/src/subject.ts
CHANGED
|
@@ -21,6 +21,18 @@ import { changedSince } from "./git.ts";
|
|
|
21
21
|
import type { Repository } from "./repository.ts";
|
|
22
22
|
import type { Tree } from "./tree.ts";
|
|
23
23
|
|
|
24
|
+
/** Where run records are read from when no --evidence is given. Local output: not to be committed. */
|
|
25
|
+
export const EVIDENCE_DIR = ".intentset/evidence";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The run records under EVIDENCE_DIR are the toolchain's own output, never part of a subject, so a change to them
|
|
29
|
+
* says nothing about evidence and is left out of what `changedSince` answers: EVID005 would otherwise list the file
|
|
30
|
+
* `evidence import --out` has just written, until it is ignored or committed.
|
|
31
|
+
*/
|
|
32
|
+
function outsideEvidence(paths: string[] | null): string[] | null {
|
|
33
|
+
return paths === null ? null : paths.filter((path) => path !== EVIDENCE_DIR && !path.startsWith(`${EVIDENCE_DIR}/`));
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
/** The binding in force: the option, else the configuration, else the default (Core §8). */
|
|
25
37
|
export function bindingFor(repo: Repository, graph: Graph, given: EvidenceBinding | undefined): BindingDecision {
|
|
26
38
|
return resolveBinding(given ?? repo.config.evidence.binding, hasSliceClaims(graph));
|
|
@@ -56,7 +68,7 @@ export function subjectsOf(repo: Repository, graph: Graph, tree: Tree): Subjects
|
|
|
56
68
|
subjects: subjectsFor(graph, source),
|
|
57
69
|
source,
|
|
58
70
|
changedSince: (commit) => {
|
|
59
|
-
if (!asked.has(commit)) asked.set(commit, changedSince(repo.root, commit));
|
|
71
|
+
if (!asked.has(commit)) asked.set(commit, outsideEvidence(changedSince(repo.root, commit)));
|
|
60
72
|
return asked.get(commit) ?? null;
|
|
61
73
|
},
|
|
62
74
|
};
|
package/dist/slices.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slices, the code they claim and the records that describe them, for
|
|
3
|
-
* `review`'s list of slices whose code changed while their records did not
|
|
4
|
-
* (Core §10) and for `context <path>`.
|
|
5
|
-
*
|
|
6
|
-
* A slice's code is what its `source`, `backend` and `contract` claims match;
|
|
7
|
-
* its `verification` and `documentation` claims are checks and prose, not
|
|
8
|
-
* implementation. The records that describe a slice are the slice itself, the
|
|
9
|
-
* behaviors it implements, the rules that govern them, the scenarios that
|
|
10
|
-
* illustrate them, the verifications of any of those, the contracts it
|
|
11
|
-
* exposes, and the decisions that inform the slice, its behaviors or its
|
|
12
|
-
* contracts. Knowledge is left out: it explains the model to an audience, and
|
|
13
|
-
* review pins already mark it for review when the model changes.
|
|
14
|
-
*/
|
|
15
|
-
import { type Artifact, type Graph } from "@intentset/core";
|
|
16
|
-
/** Every slice that is not retired and has slice metadata, sorted by ID. */
|
|
17
|
-
export declare function activeSlices(graph: Graph): Artifact[];
|
|
18
|
-
/** The slices with any claim matching `path`, sorted by ID. */
|
|
19
|
-
export declare function claimingSlices(graph: Graph, path: string): Artifact[];
|
|
20
|
-
/** True when one of the slice's source, backend or contract claims matches `path`. */
|
|
21
|
-
export declare function isSliceCode(slice: Artifact, path: string): boolean;
|
|
22
|
-
/** The IDs of the records that describe a slice, the slice included, sorted. Only IDs present in the graph. */
|
|
23
|
-
export declare function describingRecords(graph: Graph, slice: Artifact): string[];
|
|
24
|
-
//# sourceMappingURL=slices.d.ts.map
|
package/dist/slices.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slices.d.ts","sourceRoot":"","sources":["../src/slices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAkB,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAK5E,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,CAIrD;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAErE;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,+GAA+G;AAC/G,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAG,MAAM,EAAE,CAgBzE"}
|
package/dist/slices.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slices, the code they claim and the records that describe them, for
|
|
3
|
-
* `review`'s list of slices whose code changed while their records did not
|
|
4
|
-
* (Core §10) and for `context <path>`.
|
|
5
|
-
*
|
|
6
|
-
* A slice's code is what its `source`, `backend` and `contract` claims match;
|
|
7
|
-
* its `verification` and `documentation` claims are checks and prose, not
|
|
8
|
-
* implementation. The records that describe a slice are the slice itself, the
|
|
9
|
-
* behaviors it implements, the rules that govern them, the scenarios that
|
|
10
|
-
* illustrate them, the verifications of any of those, the contracts it
|
|
11
|
-
* exposes, and the decisions that inform the slice, its behaviors or its
|
|
12
|
-
* contracts. Knowledge is left out: it explains the model to an audience, and
|
|
13
|
-
* review pins already mark it for review when the model changes.
|
|
14
|
-
*/
|
|
15
|
-
import { compareStrings } from "@intentset/core";
|
|
16
|
-
import { matchGlob } from "./glob.js";
|
|
17
|
-
const CODE_CLAIMS = new Set(["source", "backend", "contract"]);
|
|
18
|
-
/** Every slice that is not retired and has slice metadata, sorted by ID. */
|
|
19
|
-
export function activeSlices(graph) {
|
|
20
|
-
return [...graph.artifacts.values()]
|
|
21
|
-
.filter((a) => a.meta.type === "slice" && a.meta.status !== "retired" && a.meta.slice !== undefined)
|
|
22
|
-
.sort((a, b) => compareStrings(a.meta.id, b.meta.id));
|
|
23
|
-
}
|
|
24
|
-
/** The slices with any claim matching `path`, sorted by ID. */
|
|
25
|
-
export function claimingSlices(graph, path) {
|
|
26
|
-
return activeSlices(graph).filter((slice) => slice.meta.slice?.claims.some((claim) => matchGlob(claim.path, path)));
|
|
27
|
-
}
|
|
28
|
-
/** True when one of the slice's source, backend or contract claims matches `path`. */
|
|
29
|
-
export function isSliceCode(slice, path) {
|
|
30
|
-
return slice.meta.slice?.claims.some((claim) => CODE_CLAIMS.has(claim.kind) && matchGlob(claim.path, path)) ?? false;
|
|
31
|
-
}
|
|
32
|
-
/** The IDs of the records that describe a slice, the slice included, sorted. Only IDs present in the graph. */
|
|
33
|
-
export function describingRecords(graph, slice) {
|
|
34
|
-
const out = new Set([slice.meta.id]);
|
|
35
|
-
const targets = (id, kind) => (graph.out.get(id) ?? []).filter((edge) => edge.kind === kind).map((edge) => edge.to);
|
|
36
|
-
const sources = (id, kind) => (graph.in.get(id) ?? []).filter((edge) => edge.kind === kind).map((edge) => edge.from);
|
|
37
|
-
const behaviors = targets(slice.meta.id, "implements");
|
|
38
|
-
const rules = behaviors.flatMap((id) => targets(id, "governedBy"));
|
|
39
|
-
const scenarios = behaviors.flatMap((id) => sources(id, "illustrates"));
|
|
40
|
-
const claims = [...behaviors, ...rules, ...scenarios];
|
|
41
|
-
const verifications = claims.flatMap((id) => sources(id, "verifies"));
|
|
42
|
-
const contracts = targets(slice.meta.id, "exposes");
|
|
43
|
-
const decisions = [slice.meta.id, ...behaviors, ...contracts].flatMap((id) => targets(id, "informedBy"));
|
|
44
|
-
for (const id of [...claims, ...verifications, ...contracts, ...decisions])
|
|
45
|
-
out.add(id);
|
|
46
|
-
return [...out].filter((id) => graph.artifacts.has(id)).sort(compareStrings);
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=slices.js.map
|
package/dist/slices.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slices.js","sourceRoot":"","sources":["../src/slices.ts"],"names":[],"mappings":"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"}
|
package/src/slices.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slices, the code they claim and the records that describe them, for
|
|
3
|
-
* `review`'s list of slices whose code changed while their records did not
|
|
4
|
-
* (Core §10) and for `context <path>`.
|
|
5
|
-
*
|
|
6
|
-
* A slice's code is what its `source`, `backend` and `contract` claims match;
|
|
7
|
-
* its `verification` and `documentation` claims are checks and prose, not
|
|
8
|
-
* implementation. The records that describe a slice are the slice itself, the
|
|
9
|
-
* behaviors it implements, the rules that govern them, the scenarios that
|
|
10
|
-
* illustrate them, the verifications of any of those, the contracts it
|
|
11
|
-
* exposes, and the decisions that inform the slice, its behaviors or its
|
|
12
|
-
* contracts. Knowledge is left out: it explains the model to an audience, and
|
|
13
|
-
* review pins already mark it for review when the model changes.
|
|
14
|
-
*/
|
|
15
|
-
import { type Artifact, compareStrings, type Graph } from "@intentset/core";
|
|
16
|
-
import { matchGlob } from "./glob.ts";
|
|
17
|
-
|
|
18
|
-
const CODE_CLAIMS = new Set(["source", "backend", "contract"]);
|
|
19
|
-
|
|
20
|
-
/** Every slice that is not retired and has slice metadata, sorted by ID. */
|
|
21
|
-
export function activeSlices(graph: Graph): Artifact[] {
|
|
22
|
-
return [...graph.artifacts.values()]
|
|
23
|
-
.filter((a) => a.meta.type === "slice" && a.meta.status !== "retired" && a.meta.slice !== undefined)
|
|
24
|
-
.sort((a, b) => compareStrings(a.meta.id, b.meta.id));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** The slices with any claim matching `path`, sorted by ID. */
|
|
28
|
-
export function claimingSlices(graph: Graph, path: string): Artifact[] {
|
|
29
|
-
return activeSlices(graph).filter((slice) => slice.meta.slice?.claims.some((claim) => matchGlob(claim.path, path)));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** True when one of the slice's source, backend or contract claims matches `path`. */
|
|
33
|
-
export function isSliceCode(slice: Artifact, path: string): boolean {
|
|
34
|
-
return slice.meta.slice?.claims.some((claim) => CODE_CLAIMS.has(claim.kind) && matchGlob(claim.path, path)) ?? false;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** The IDs of the records that describe a slice, the slice included, sorted. Only IDs present in the graph. */
|
|
38
|
-
export function describingRecords(graph: Graph, slice: Artifact): string[] {
|
|
39
|
-
const out = new Set<string>([slice.meta.id]);
|
|
40
|
-
const targets = (id: string, kind: string) =>
|
|
41
|
-
(graph.out.get(id) ?? []).filter((edge) => edge.kind === kind).map((edge) => edge.to);
|
|
42
|
-
const sources = (id: string, kind: string) =>
|
|
43
|
-
(graph.in.get(id) ?? []).filter((edge) => edge.kind === kind).map((edge) => edge.from);
|
|
44
|
-
|
|
45
|
-
const behaviors = targets(slice.meta.id, "implements");
|
|
46
|
-
const rules = behaviors.flatMap((id) => targets(id, "governedBy"));
|
|
47
|
-
const scenarios = behaviors.flatMap((id) => sources(id, "illustrates"));
|
|
48
|
-
const claims = [...behaviors, ...rules, ...scenarios];
|
|
49
|
-
const verifications = claims.flatMap((id) => sources(id, "verifies"));
|
|
50
|
-
const contracts = targets(slice.meta.id, "exposes");
|
|
51
|
-
const decisions = [slice.meta.id, ...behaviors, ...contracts].flatMap((id) => targets(id, "informedBy"));
|
|
52
|
-
for (const id of [...claims, ...verifications, ...contracts, ...decisions]) out.add(id);
|
|
53
|
-
return [...out].filter((id) => graph.artifacts.has(id)).sort(compareStrings);
|
|
54
|
-
}
|