@codyswann/lisa 2.279.2 → 2.281.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.
Files changed (112) hide show
  1. package/dist/cli/doctor-readiness-audit-allowlist.d.ts +7 -0
  2. package/dist/cli/doctor-readiness-audit-allowlist.d.ts.map +1 -0
  3. package/dist/cli/doctor-readiness-audit-allowlist.js +177 -0
  4. package/dist/cli/doctor-readiness-audit-allowlist.js.map +1 -0
  5. package/dist/cli/doctor-readiness-capabilities.d.ts +33 -0
  6. package/dist/cli/doctor-readiness-capabilities.d.ts.map +1 -0
  7. package/dist/cli/doctor-readiness-capabilities.js +26 -0
  8. package/dist/cli/doctor-readiness-capabilities.js.map +1 -0
  9. package/dist/cli/doctor-readiness-context-claims.d.ts +14 -0
  10. package/dist/cli/doctor-readiness-context-claims.d.ts.map +1 -0
  11. package/dist/cli/doctor-readiness-context-claims.js +117 -0
  12. package/dist/cli/doctor-readiness-context-claims.js.map +1 -0
  13. package/dist/cli/doctor-readiness-context-mechanisms.d.ts +46 -0
  14. package/dist/cli/doctor-readiness-context-mechanisms.d.ts.map +1 -0
  15. package/dist/cli/doctor-readiness-context-mechanisms.js +116 -0
  16. package/dist/cli/doctor-readiness-context-mechanisms.js.map +1 -0
  17. package/dist/cli/doctor-readiness-context.d.ts +16 -0
  18. package/dist/cli/doctor-readiness-context.d.ts.map +1 -0
  19. package/dist/cli/doctor-readiness-context.js +244 -0
  20. package/dist/cli/doctor-readiness-context.js.map +1 -0
  21. package/dist/cli/doctor-readiness-delivery.d.ts.map +1 -1
  22. package/dist/cli/doctor-readiness-delivery.js +1 -10
  23. package/dist/cli/doctor-readiness-delivery.js.map +1 -1
  24. package/dist/cli/doctor-readiness-domain.d.ts +17 -0
  25. package/dist/cli/doctor-readiness-domain.d.ts.map +1 -0
  26. package/dist/cli/doctor-readiness-domain.js +197 -0
  27. package/dist/cli/doctor-readiness-domain.js.map +1 -0
  28. package/dist/cli/doctor-readiness-guardrails.d.ts +15 -0
  29. package/dist/cli/doctor-readiness-guardrails.d.ts.map +1 -0
  30. package/dist/cli/doctor-readiness-guardrails.js +219 -0
  31. package/dist/cli/doctor-readiness-guardrails.js.map +1 -0
  32. package/dist/cli/doctor-readiness-operations.d.ts +103 -0
  33. package/dist/cli/doctor-readiness-operations.d.ts.map +1 -0
  34. package/dist/cli/doctor-readiness-operations.js +207 -0
  35. package/dist/cli/doctor-readiness-operations.js.map +1 -0
  36. package/dist/cli/doctor-readiness-shared.d.ts +47 -0
  37. package/dist/cli/doctor-readiness-shared.d.ts.map +1 -0
  38. package/dist/cli/doctor-readiness-shared.js +88 -0
  39. package/dist/cli/doctor-readiness-shared.js.map +1 -0
  40. package/dist/cli/doctor-readiness-supply-chain-scan.d.ts +67 -0
  41. package/dist/cli/doctor-readiness-supply-chain-scan.d.ts.map +1 -0
  42. package/dist/cli/doctor-readiness-supply-chain-scan.js +316 -0
  43. package/dist/cli/doctor-readiness-supply-chain-scan.js.map +1 -0
  44. package/dist/cli/doctor-readiness-supply-chain.d.ts +14 -0
  45. package/dist/cli/doctor-readiness-supply-chain.d.ts.map +1 -0
  46. package/dist/cli/doctor-readiness-supply-chain.js +231 -0
  47. package/dist/cli/doctor-readiness-supply-chain.js.map +1 -0
  48. package/dist/cli/doctor-readiness-workflows.d.ts +20 -0
  49. package/dist/cli/doctor-readiness-workflows.d.ts.map +1 -1
  50. package/dist/cli/doctor-readiness-workflows.js +29 -7
  51. package/dist/cli/doctor-readiness-workflows.js.map +1 -1
  52. package/dist/cli/doctor-readiness-workspaces.d.ts +22 -0
  53. package/dist/cli/doctor-readiness-workspaces.d.ts.map +1 -0
  54. package/dist/cli/doctor-readiness-workspaces.js +98 -0
  55. package/dist/cli/doctor-readiness-workspaces.js.map +1 -0
  56. package/dist/cli/doctor-readiness.d.ts.map +1 -1
  57. package/dist/cli/doctor-readiness.js +23 -13
  58. package/dist/cli/doctor-readiness.js.map +1 -1
  59. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  60. package/dist/core/upstream-evidence-manifest.js +23 -0
  61. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  62. package/package.json +1 -1
  63. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-agy/plugin.json +1 -1
  66. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  68. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  69. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-expo-agy/plugin.json +1 -1
  76. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  81. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  86. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  91. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  95. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  96. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  100. package/plugins/lisa-rails-agy/plugin.json +1 -1
  101. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  105. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  106. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  110. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  111. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Collect the undocumented audit exceptions across every allowlist file.
3
+ * @param root - Repository root
4
+ * @returns Evidence lines, one per undocumented exception
5
+ */
6
+ export declare function auditExceptionViolations(root: string): Promise<readonly string[]>;
7
+ //# sourceMappingURL=doctor-readiness-audit-allowlist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-audit-allowlist.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-audit-allowlist.ts"],"names":[],"mappings":"AAgMA;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAiB5B"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Audit-allowlist reading for the dependencies/supply-chain readiness producer
3
+ * (B5, PRD #1739, #1896).
4
+ *
5
+ * An audit allowlist is a list of vulnerabilities somebody decided not to fix.
6
+ * B5 asks the only question that keeps such a list honest: is the decision
7
+ * written down next to the exception? Two failure directions matter equally and
8
+ * this module is shaped by both.
9
+ *
10
+ * Reporting too much fabricates findings — audit-ci's own `.nsprc` carries
11
+ * `$schema` and other configuration at the same level as advisory ids, so a
12
+ * naive walk reports the schema pointer as an undocumented exclusion. Reporting
13
+ * too little is worse: an entry with no `id` field, or a bare string in the
14
+ * list, is still a live exception with no written decision, and dropping it
15
+ * turns the one finding this dimension exists for into silence. Identity
16
+ * therefore degrades (advisory id → package name → position) rather than
17
+ * disappearing.
18
+ * @module cli/doctor-readiness-audit-allowlist
19
+ */
20
+ import { isRecord, readFileOrNull } from "./doctor-readiness-shared.js";
21
+ /** Audit allowlists whose entries must each carry a decision record. */
22
+ const ALLOWLIST_FILES = [
23
+ "audit.ignore.config.json",
24
+ "audit.ignore.local.json",
25
+ ".nsprc",
26
+ ];
27
+ /** Keys that count as the written decision behind an audit exception. */
28
+ const DECISION_KEYS = [
29
+ "reason",
30
+ "notes",
31
+ "justification",
32
+ "decision",
33
+ "rationale",
34
+ "comment",
35
+ ];
36
+ /**
37
+ * Keys whose value holds audit exceptions. Everything outside these containers
38
+ * is configuration, not an exception — walking a whole document as if every
39
+ * top-level key were an advisory id turns audit-ci's own `.nsprc` (`$schema`,
40
+ * `exceptions`, …) into fabricated findings.
41
+ */
42
+ const EXCEPTION_CONTAINERS = [
43
+ "exclusions",
44
+ "exceptions",
45
+ "advisories",
46
+ "allowlist",
47
+ ];
48
+ /**
49
+ * Whether a top-level `.nsprc`-style key names an advisory rather than config.
50
+ * @param key - The key to classify
51
+ * @returns True when the key may name an advisory
52
+ */
53
+ function isAdvisoryKey(key) {
54
+ return !key.startsWith("$") && !key.startsWith("//");
55
+ }
56
+ /**
57
+ * Name one array-shaped exception for an operator.
58
+ *
59
+ * An entry with no `id` must never be dropped — a live exception with no written
60
+ * decision is precisely what B5 exists to surface, and a missing field is not a
61
+ * reason to suppress it. So the identity degrades: advisory id, then package
62
+ * name, then the entry's position in the list.
63
+ * @param entry - The entry's fields
64
+ * @param index - Zero-based position in the container
65
+ * @returns The operator-facing identity
66
+ */
67
+ function describeException(entry, index) {
68
+ if (typeof entry.id === "string" && entry.id.trim() !== "") {
69
+ return entry.id;
70
+ }
71
+ return typeof entry.package === "string" && entry.package.trim() !== ""
72
+ ? entry.package
73
+ : `entry #${index + 1}`;
74
+ }
75
+ /**
76
+ * Normalize one array-shaped exception container.
77
+ *
78
+ * A bare string entry IS the advisory id, and it carries no fields at all — so
79
+ * there is nowhere for a decision to live, which makes it undocumented by
80
+ * construction rather than something to skip over.
81
+ * @param container - The array of entries
82
+ * @returns The normalized exceptions
83
+ */
84
+ function arrayEntries(container) {
85
+ return container.flatMap((entry, index) => {
86
+ if (typeof entry === "string" && entry.trim() !== "") {
87
+ return [{ id: entry.trim(), fields: null }];
88
+ }
89
+ return isRecord(entry)
90
+ ? [{ id: describeException(entry, index), fields: entry }]
91
+ : [];
92
+ });
93
+ }
94
+ /**
95
+ * Take the object-valued, non-config entries out of an exception map.
96
+ * @param container - A candidate exception map
97
+ * @returns The entries that are genuinely exceptions
98
+ */
99
+ function mapEntries(container) {
100
+ return Object.entries(container).flatMap(([key, entry]) => isAdvisoryKey(key) && isRecord(entry) ? [{ id: key, fields: entry }] : []);
101
+ }
102
+ /**
103
+ * Normalize one allowlist document into exception entries.
104
+ *
105
+ * Two shapes exist in the wild: a named container holding an array or a map of
106
+ * entries, and audit-ci's `.nsprc`, whose top level maps advisory id to entry.
107
+ * In the map shape only OBJECT-valued entries are exceptions — a string, a
108
+ * boolean, or a `$schema` pointer at the top level is configuration.
109
+ * @param parsed - The parsed allowlist document
110
+ * @returns The exception entries
111
+ */
112
+ function exceptionEntries(parsed) {
113
+ if (!isRecord(parsed)) {
114
+ return [];
115
+ }
116
+ const containers = EXCEPTION_CONTAINERS.flatMap(key => parsed[key] === undefined ? [] : [parsed[key]]);
117
+ if (containers.length > 0) {
118
+ return containers.flatMap(container => Array.isArray(container)
119
+ ? arrayEntries(container)
120
+ : isRecord(container)
121
+ ? mapEntries(container)
122
+ : []);
123
+ }
124
+ // No recognized container: fall back to the `.nsprc` id → entry map. If
125
+ // nothing there is an object-valued advisory key, this document establishes
126
+ // nothing and reports nothing.
127
+ return mapEntries(parsed);
128
+ }
129
+ /**
130
+ * Read one audit allowlist and report the exceptions carrying no decision.
131
+ *
132
+ * An entry with `active: false` is skipped: audit-ci never applies it, so there
133
+ * is no live exception for anybody to justify.
134
+ * @param file - Repo-relative allowlist path
135
+ * @param parsed - The parsed allowlist document
136
+ * @returns Evidence lines, one per undocumented exception
137
+ */
138
+ function undocumentedExceptions(file, parsed) {
139
+ return exceptionEntries(parsed).flatMap(({ id, fields }) => {
140
+ if (fields?.active === false) {
141
+ return [];
142
+ }
143
+ const documented = fields !== null &&
144
+ DECISION_KEYS.some(decisionKey => typeof fields[decisionKey] === "string" &&
145
+ fields[decisionKey].trim() !== "");
146
+ return documented
147
+ ? []
148
+ : [
149
+ `\`${file}\` excludes advisory \`${id}\` from the dependency audit ` +
150
+ "with no written decision (no reason/notes/justification field), so " +
151
+ "nobody can tell whether the exception is still true",
152
+ ];
153
+ });
154
+ }
155
+ /**
156
+ * Collect the undocumented audit exceptions across every allowlist file.
157
+ * @param root - Repository root
158
+ * @returns Evidence lines, one per undocumented exception
159
+ */
160
+ export async function auditExceptionViolations(root) {
161
+ const perFile = await Promise.all(ALLOWLIST_FILES.map(async (file) => {
162
+ const source = await readFileOrNull(root, file);
163
+ if (source === null) {
164
+ return [];
165
+ }
166
+ try {
167
+ return undocumentedExceptions(file, JSON.parse(source));
168
+ }
169
+ catch {
170
+ // An unparseable allowlist establishes nothing about its entries, so it
171
+ // is passed over rather than reported as an undocumented exception.
172
+ return [];
173
+ }
174
+ }));
175
+ return perFile.flat();
176
+ }
177
+ //# sourceMappingURL=doctor-readiness-audit-allowlist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-audit-allowlist.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-audit-allowlist.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The capabilities/tools readiness producer (PRD #1739, #1896).
3
+ *
4
+ * Dimension 2 of the `readiness-rubric` asks whether every tool the work needs
5
+ * is provably *reachable*, not merely installed. The `tool-access-gate` names
6
+ * the anti-pattern explicitly: a binary on `PATH`, a dependency in a manifest,
7
+ * or a configured MCP server proves that something was declared, never that a
8
+ * request through it succeeds. Reachability is established by a live read-only
9
+ * probe against the real system, and nothing an offline file read can see is a
10
+ * substitute for one.
11
+ *
12
+ * So this dimension has no offline evidence to gather and no blocker to stand.
13
+ * What it has is an obligation to say so: a blank dimension reads as "nothing to
14
+ * report" when it actually means "never looked" (#1898), and a presence-based
15
+ * PASS would be worse still — it would report the anti-pattern as proof. The
16
+ * producer therefore renders one reasoned SKIP, every time, naming the live
17
+ * probe it did not run.
18
+ * @module cli/doctor-readiness-capabilities
19
+ */
20
+ import type { ReadinessDimensionRecord } from "./doctor-readiness-types.js";
21
+ /** The capabilities/tools readiness dimension id (readiness-rubric, RRR-1). */
22
+ export declare const CAPABILITIES_TOOLS_DIMENSION_ID = "capabilities-tools";
23
+ /**
24
+ * Assess the capabilities/tools dimension. Always a reasoned SKIP: the question
25
+ * is not offline-answerable, so the honest answer is a stated non-answer. The
26
+ * record names no `blocker`, so it can never stand one up, and the `root`
27
+ * argument is accepted only to satisfy the shared producer signature — reading
28
+ * it would be the presence anti-pattern the rule forbids.
29
+ * @param _root - Project root (deliberately unread)
30
+ * @returns The capabilities/tools dimension record
31
+ */
32
+ export declare function assessCapabilitiesToolsDimension(_root: string): Promise<ReadinessDimensionRecord>;
33
+ //# sourceMappingURL=doctor-readiness-capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-capabilities.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAE5E,+EAA+E;AAC/E,eAAO,MAAM,+BAA+B,uBAAuB,CAAC;AAWpE;;;;;;;;GAQG;AACH,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,CAAC,CAMnC"}
@@ -0,0 +1,26 @@
1
+ /** The capabilities/tools readiness dimension id (readiness-rubric, RRR-1). */
2
+ export const CAPABILITIES_TOOLS_DIMENSION_ID = "capabilities-tools";
3
+ /** The stated reason this dimension is never assessed by an offline read. */
4
+ const CAPABILITIES_SKIP_REASON = "Tool reachability requires a live read-only probe against the real system " +
5
+ "and cannot be established from an offline read. The `tool-access-gate` rule " +
6
+ "names presence — a binary on `PATH`, a dependency in a manifest, a " +
7
+ "configured MCP server — as the anti-pattern: it proves a tool was declared, " +
8
+ "never that a request through it succeeds. This dimension was therefore " +
9
+ "deliberately not assessed rather than answered from presence.";
10
+ /**
11
+ * Assess the capabilities/tools dimension. Always a reasoned SKIP: the question
12
+ * is not offline-answerable, so the honest answer is a stated non-answer. The
13
+ * record names no `blocker`, so it can never stand one up, and the `root`
14
+ * argument is accepted only to satisfy the shared producer signature — reading
15
+ * it would be the presence anti-pattern the rule forbids.
16
+ * @param _root - Project root (deliberately unread)
17
+ * @returns The capabilities/tools dimension record
18
+ */
19
+ export async function assessCapabilitiesToolsDimension(_root) {
20
+ return await Promise.resolve({
21
+ id: CAPABILITIES_TOOLS_DIMENSION_ID,
22
+ status: "SKIP",
23
+ findings: [{ reason: CAPABILITIES_SKIP_REASON, skip: true }],
24
+ });
25
+ }
26
+ //# sourceMappingURL=doctor-readiness-capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-capabilities.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-capabilities.ts"],"names":[],"mappings":"AAqBA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,+BAA+B,GAAG,oBAAoB,CAAC;AAEpE,6EAA6E;AAC7E,MAAM,wBAAwB,GAC5B,4EAA4E;IAC5E,8EAA8E;IAC9E,qEAAqE;IACrE,8EAA8E;IAC9E,yEAAyE;IACzE,+DAA+D,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,KAAa;IAEb,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC;QAC3B,EAAE,EAAE,+BAA+B;QACnC,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,wBAAwB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;KAC7D,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** One enforcement claim read out of an instruction surface. */
2
+ export interface EnforcementClaim {
3
+ readonly file: string;
4
+ readonly line: number;
5
+ readonly text: string;
6
+ readonly mechanisms: readonly string[];
7
+ }
8
+ /**
9
+ * Read every instruction surface a repository declares.
10
+ * @param root - Repository root
11
+ * @returns Every enforcement claim, in stable file order
12
+ */
13
+ export declare function collectClaims(root: string): Promise<readonly EnforcementClaim[]>;
14
+ //# sourceMappingURL=doctor-readiness-context-claims.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-context-claims.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context-claims.ts"],"names":[],"mappings":"AA+CA,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAkED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC,CAWtC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Enforcement-claim extraction for the context-routing readiness producer
3
+ * (B6, PRD #1739, #1896).
4
+ *
5
+ * B6 compares what the repository's instruction surfaces promise against the
6
+ * machinery that would have to deliver it. This module does the reading half —
7
+ * finding the sentences that assert a guarantee and the repository paths they
8
+ * name — while its sibling turns those claims into the rubric-shaped dimension
9
+ * record. Splitting them keeps each file inside the repository's file-size
10
+ * budget and keeps the (deliberately conservative) claim grammar in one place.
11
+ *
12
+ * Every filter here is a false-positive guard. A false B6 tells an operator
13
+ * their documentation lies when it does not, so a sentence is treated as a
14
+ * claim only when it asserts a guarantee, is not hedged, and is not inside a
15
+ * fenced code block — and a token is treated as a mechanism only when it is a
16
+ * checkable repository path.
17
+ * @module cli/doctor-readiness-context-claims
18
+ */
19
+ import { readdir } from "node:fs/promises";
20
+ import * as path from "node:path";
21
+ import { isMechanismPath } from "./doctor-readiness-context-mechanisms.js";
22
+ import { readFileOrNull } from "./doctor-readiness-shared.js";
23
+ /** The instruction surfaces whose enforcement claims are cross-checked. */
24
+ const CLAIM_SOURCE_FILES = ["README.md", "AGENTS.md"];
25
+ /** The rules directory whose every `.md` file is an instruction surface. */
26
+ const RULES_DIR = path.join(".claude", "rules");
27
+ /**
28
+ * Phrases that assert a guarantee something else must enforce. Deliberately
29
+ * narrow: a loose verb list ("uses", "checks") would sweep in ordinary prose and
30
+ * turn B6 into noise the operator learns to ignore.
31
+ */
32
+ const ENFORCEMENT_CLAIM = /\b(is enforced|are enforced|enforced by|enforces|is blocked by|are blocked by|blocked by|always runs|runs on every|required check|is required|hard-fails|automatically runs|automatically loaded)\b/i;
33
+ /**
34
+ * Words that turn a sentence into an illustration, a possibility, or a plan.
35
+ * A hedged sentence claims nothing, so it can overstate nothing.
36
+ */
37
+ const HEDGED_CLAIM = /\b(example|e\.g\.|i\.e\.|for instance|if you|would|could|may|might|should|todo|planned|future|suppose|imagine|placeholder|instead of|rather than|used to)\b/i;
38
+ /** Inline-code spans, the only place a mechanism is read from. */
39
+ const INLINE_CODE = /`([^`\n]+)`/g;
40
+ /**
41
+ * Extract the mechanism paths a claim line names.
42
+ * @param line - The raw documentation line
43
+ * @returns Repo-relative mechanism paths, deduplicated
44
+ */
45
+ function namedMechanisms(line) {
46
+ const tokens = [...line.matchAll(INLINE_CODE)]
47
+ .map(match => (match[1] ?? "").trim())
48
+ .filter(isMechanismPath)
49
+ .map(token => token.replace(/\/$/, ""));
50
+ return [...new Set(tokens)];
51
+ }
52
+ /**
53
+ * Read the enforcement claims out of one instruction surface. Fenced code blocks
54
+ * are skipped wholesale: a sample inside a fence documents a shape, not this
55
+ * repository's guarantees.
56
+ * @param file - Repo-relative file path
57
+ * @param source - File contents
58
+ * @returns The enforcement claims the file makes
59
+ */
60
+ function extractClaims(file, source) {
61
+ return proseLines(source)
62
+ .filter(entry => ENFORCEMENT_CLAIM.test(entry.text) && !HEDGED_CLAIM.test(entry.text))
63
+ .map(entry => ({
64
+ file,
65
+ line: entry.line,
66
+ text: entry.text.trim(),
67
+ mechanisms: namedMechanisms(entry.text),
68
+ }));
69
+ }
70
+ /**
71
+ * Split a document into its prose lines, dropping fenced code blocks entirely.
72
+ * A line is inside a fence when an odd number of fence markers precede it, which
73
+ * is what makes the fence state derivable per line rather than accumulated.
74
+ * @param source - File contents
75
+ * @returns The prose lines, each carrying its 1-based line number
76
+ */
77
+ function proseLines(source) {
78
+ const lines = source.split("\n");
79
+ const fences = lines.flatMap((text, index) => text.trim().startsWith("```") ? [index] : []);
80
+ return lines.flatMap((text, index) => {
81
+ const openFences = fences.filter(fence => fence < index).length;
82
+ return fences.includes(index) || openFences % 2 === 1
83
+ ? []
84
+ : [{ line: index + 1, text }];
85
+ });
86
+ }
87
+ /**
88
+ * Read every instruction surface a repository declares.
89
+ * @param root - Repository root
90
+ * @returns Every enforcement claim, in stable file order
91
+ */
92
+ export async function collectClaims(root) {
93
+ const ruleFiles = await listRuleFiles(root);
94
+ const sources = await Promise.all([...CLAIM_SOURCE_FILES, ...ruleFiles].map(async (file) => ({
95
+ file,
96
+ source: await readFileOrNull(root, file),
97
+ })));
98
+ return sources.flatMap(({ file, source }) => source === null ? [] : extractClaims(file, source));
99
+ }
100
+ /**
101
+ * List the `.claude/rules/*.md` instruction files.
102
+ * @param root - Repository root
103
+ * @returns Repo-relative rule file paths, sorted
104
+ */
105
+ async function listRuleFiles(root) {
106
+ try {
107
+ const entries = await readdir(path.join(root, RULES_DIR));
108
+ return entries
109
+ .filter(entry => entry.endsWith(".md"))
110
+ .sort((a, b) => a.localeCompare(b))
111
+ .map(entry => `${RULES_DIR.split(path.sep).join("/")}/${entry}`);
112
+ }
113
+ catch {
114
+ return [];
115
+ }
116
+ }
117
+ //# sourceMappingURL=doctor-readiness-context-claims.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-context-claims.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context-claims.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /** What a named mechanism turned out to be. */
2
+ export type MechanismVerdict =
3
+ /** The named path exists — the documented guarantee is backed. */
4
+ "present"
5
+ /** The named path provably does not exist — this is what B6 stands on. */
6
+ | "missing"
7
+ /** Not a checkable in-repository path, so it establishes nothing. */
8
+ | "unmappable";
9
+ /** Decides whether a repository path is ignored/generated. */
10
+ export type IgnoreMatcher = (relativePath: string) => boolean;
11
+ /**
12
+ * Whether an inline-code span is even shaped like an in-repository path.
13
+ *
14
+ * Every exclusion is a false-positive guard: a URL is not a repo path, a glob
15
+ * names a set rather than a file, an `@` marks a scoped package or a versioned
16
+ * action reference rather than a path, and a placeholder names nothing.
17
+ * @param token - The inline-code text
18
+ * @returns True when the token is shaped like a repository path
19
+ */
20
+ export declare function isMechanismPath(token: string): boolean;
21
+ /**
22
+ * Build the ignore matcher for a repository from its `.gitignore` plus the
23
+ * always-generated defaults. The matcher is deliberately coarse — it compares
24
+ * path segments and directory prefixes rather than implementing gitignore's full
25
+ * grammar — because its only job is to move a doubtful path OUT of the violation
26
+ * set, where being over-inclusive is the safe direction.
27
+ * @param root - Repository root
28
+ * @returns A matcher that answers "is this path generated or ignored?"
29
+ */
30
+ export declare function loadIgnoreMatcher(root: string): Promise<IgnoreMatcher>;
31
+ /**
32
+ * Classify one named mechanism.
33
+ *
34
+ * A token only becomes checkable when the repository itself vouches for the
35
+ * shape: it carries a known file extension, it lives under a dot-directory the
36
+ * repository conventionally keeps machinery in, or its first segment is a real
37
+ * directory in this repository. `acme/release-gate` in a repository with no
38
+ * `acme/` directory is an `org/repo` slug as far as anything here can tell, so
39
+ * it is unmappable — not missing.
40
+ * @param root - Repository root
41
+ * @param token - The named mechanism path
42
+ * @param isIgnored - The repository's ignore matcher
43
+ * @returns What the mechanism turned out to be
44
+ */
45
+ export declare function classifyMechanism(root: string, token: string, isIgnored: IgnoreMatcher): Promise<MechanismVerdict>;
46
+ //# sourceMappingURL=doctor-readiness-context-mechanisms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-context-mechanisms.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context-mechanisms.ts"],"names":[],"mappings":"AA+CA,+CAA+C;AAC/C,MAAM,MAAM,gBAAgB;AAC1B,kEAAkE;AAChE,SAAS;AACX,0EAA0E;GACxE,SAAS;AACX,qEAAqE;GACnE,YAAY,CAAC;AAEjB,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAmBtD;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmB5E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAa3B"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Mechanism classification for the context-routing readiness producer (B6,
3
+ * PRD #1739, #1896).
4
+ *
5
+ * B6 may only be stood up on a documented guarantee whose named mechanism
6
+ * provably does not exist. Deciding whether a backticked token even IS a
7
+ * checkable repository path is therefore the whole precision budget of the
8
+ * blocker, and a standing blocker flips the entire repository to `NOT_READY` —
9
+ * so every ambiguity here resolves to `unmappable`, never to `missing`.
10
+ *
11
+ * The three false-positive classes this module exists to kill, all of which look
12
+ * exactly like repository paths:
13
+ *
14
+ * - **Package names.** `@codyswann/lisa` is a dependency, not a file.
15
+ * - **Slugs and refs.** `CodySwannGT/lisa` and `origin/main` name a repository
16
+ * and a git ref; neither is a path in the working tree.
17
+ * - **Generated artifacts.** `dist/cli.js` is absent from a clean checkout by
18
+ * design, so faulting it would fault every repository that builds anything.
19
+ * @module cli/doctor-readiness-context-mechanisms
20
+ */
21
+ import { directoryExists, pathExists, readFileOrNull, trimSlashes, } from "./doctor-readiness-shared.js";
22
+ /** File extensions a named mechanism plausibly ends in. */
23
+ const MECHANISM_EXTENSION = /\.(ya?ml|sh|bash|zsh|ts|tsx|js|mjs|cjs|json|jsonc|md|toml|cfg|ini)$/;
24
+ /**
25
+ * Generated paths treated as ignored even when a repository ships no
26
+ * `.gitignore`. Their absence is a property of a clean checkout, never of
27
+ * documentation that lies.
28
+ */
29
+ const ALWAYS_GENERATED = [
30
+ "node_modules",
31
+ "dist",
32
+ "build",
33
+ "coverage",
34
+ "out",
35
+ ".next",
36
+ ".turbo",
37
+ ".cache",
38
+ ];
39
+ /**
40
+ * Whether an inline-code span is even shaped like an in-repository path.
41
+ *
42
+ * Every exclusion is a false-positive guard: a URL is not a repo path, a glob
43
+ * names a set rather than a file, an `@` marks a scoped package or a versioned
44
+ * action reference rather than a path, and a placeholder names nothing.
45
+ * @param token - The inline-code text
46
+ * @returns True when the token is shaped like a repository path
47
+ */
48
+ export function isMechanismPath(token) {
49
+ const trimmed = token.trim();
50
+ if (trimmed === "" || /\s/.test(trimmed)) {
51
+ return false;
52
+ }
53
+ if (trimmed.includes("://") ||
54
+ trimmed.includes("*") ||
55
+ trimmed.includes("<") ||
56
+ trimmed.includes("$") ||
57
+ // A scoped package (`@acme/pkg`) or a pinned action (`actions/checkout@v4`)
58
+ // is a dependency coordinate, not a file this repository owns.
59
+ trimmed.includes("@") ||
60
+ trimmed.startsWith("/") ||
61
+ trimmed.startsWith("-")) {
62
+ return false;
63
+ }
64
+ return trimmed.includes("/") || MECHANISM_EXTENSION.test(trimmed);
65
+ }
66
+ /**
67
+ * Build the ignore matcher for a repository from its `.gitignore` plus the
68
+ * always-generated defaults. The matcher is deliberately coarse — it compares
69
+ * path segments and directory prefixes rather than implementing gitignore's full
70
+ * grammar — because its only job is to move a doubtful path OUT of the violation
71
+ * set, where being over-inclusive is the safe direction.
72
+ * @param root - Repository root
73
+ * @returns A matcher that answers "is this path generated or ignored?"
74
+ */
75
+ export async function loadIgnoreMatcher(root) {
76
+ const source = (await readFileOrNull(root, ".gitignore")) ?? "";
77
+ const declared = source
78
+ .split("\n")
79
+ .map(line => line.trim())
80
+ .filter(line => line !== "" && !line.startsWith("#") && !line.startsWith("!"))
81
+ .map(trimSlashes)
82
+ .filter(line => line !== "" && !line.includes("*"));
83
+ const patterns = new Set([...ALWAYS_GENERATED, ...declared]);
84
+ return relativePath => relativePath
85
+ .split("/")
86
+ .some((segment, index, segments) => patterns.has(segments.slice(0, index + 1).join("/")) ||
87
+ patterns.has(segment));
88
+ }
89
+ /**
90
+ * Classify one named mechanism.
91
+ *
92
+ * A token only becomes checkable when the repository itself vouches for the
93
+ * shape: it carries a known file extension, it lives under a dot-directory the
94
+ * repository conventionally keeps machinery in, or its first segment is a real
95
+ * directory in this repository. `acme/release-gate` in a repository with no
96
+ * `acme/` directory is an `org/repo` slug as far as anything here can tell, so
97
+ * it is unmappable — not missing.
98
+ * @param root - Repository root
99
+ * @param token - The named mechanism path
100
+ * @param isIgnored - The repository's ignore matcher
101
+ * @returns What the mechanism turned out to be
102
+ */
103
+ export async function classifyMechanism(root, token, isIgnored) {
104
+ if (isIgnored(token)) {
105
+ return "unmappable";
106
+ }
107
+ if (await pathExists(root, token)) {
108
+ return "present";
109
+ }
110
+ const [firstSegment = ""] = token.split("/");
111
+ const checkable = MECHANISM_EXTENSION.test(token) ||
112
+ firstSegment.startsWith(".") ||
113
+ (token.includes("/") && (await directoryExists(root, firstSegment)));
114
+ return checkable ? "missing" : "unmappable";
115
+ }
116
+ //# sourceMappingURL=doctor-readiness-context-mechanisms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-context-mechanisms.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context-mechanisms.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { ReadinessDimensionRecord } from "./doctor-readiness-types.js";
2
+ /** The context-routing readiness dimension id (readiness-rubric, RRR-1). */
3
+ export declare const CONTEXT_ROUTING_DIMENSION_ID = "context-routing";
4
+ /**
5
+ * Assess the context-routing dimension: B6, "documentation overstates enforced
6
+ * guarantees". Offline by construction — it reads only the repository's own
7
+ * instruction surfaces and the paths they name — and high-precision by design:
8
+ * it stands B6 only on a named mechanism that survives {@link classifyMechanism}
9
+ * and still demonstrably does not exist. The record's status is `WARN` rather
10
+ * than `FAIL`, but that is a confidence signal to a human reader only: a
11
+ * standing B6 blocker flips the repository to `NOT_READY` either way.
12
+ * @param root - Project root to assess
13
+ * @returns The context-routing dimension record
14
+ */
15
+ export declare function assessContextRoutingDimension(root: string): Promise<ReadinessDimensionRecord>;
16
+ //# sourceMappingURL=doctor-readiness-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-context.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAE5E,4EAA4E;AAC5E,eAAO,MAAM,4BAA4B,oBAAoB,CAAC;AAqN9D;;;;;;;;;;GAUG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,wBAAwB,CAAC,CAoCnC"}