@ryuenn3123/agentic-senior-core 4.3.15 → 5.1.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 (198) hide show
  1. package/.agents/plugins/marketplace.json +21 -0
  2. package/.agents/rules/agentic-senior-core.md +126 -0
  3. package/.claude-plugin/marketplace.json +17 -0
  4. package/.claude-plugin/plugin.json +17 -0
  5. package/.clinerules/agentic-senior-core.md +94 -0
  6. package/.codex-plugin/plugin.json +24 -0
  7. package/.cursor/rules/agentic-senior-core.mdc +99 -0
  8. package/.devin-plugin/plugin.json +13 -0
  9. package/.github/copilot-instructions.md +94 -0
  10. package/.github/plugin/marketplace.json +20 -0
  11. package/.github/plugin/plugin.json +16 -0
  12. package/.kiro/steering/agentic-senior-core.md +94 -0
  13. package/.openclaw/skills/asc/SKILL.md +23 -0
  14. package/.openclaw/skills/asc-audit/SKILL.md +28 -0
  15. package/.openclaw/skills/asc-review/SKILL.md +54 -0
  16. package/.opencode/plugins/agentic-senior-core.mjs +31 -0
  17. package/.windsurf/rules/agentic-senior-core.md +94 -0
  18. package/AGENTS.md +126 -179
  19. package/CONTRIBUTING.md +93 -63
  20. package/README.md +160 -111
  21. package/__init__.py +61 -0
  22. package/bin/agentic-senior-core.js +41 -43
  23. package/commands/asc-audit.md +1 -0
  24. package/commands/asc-audit.toml +2 -0
  25. package/commands/asc-help.md +9 -0
  26. package/commands/asc-help.toml +2 -0
  27. package/commands/asc-refactor.md +1 -0
  28. package/commands/asc-refactor.toml +2 -0
  29. package/commands/asc-review.md +1 -0
  30. package/commands/asc-review.toml +2 -0
  31. package/gemini-extension.json +10 -0
  32. package/hooks/copilot-hooks.json +21 -0
  33. package/hooks/hooks.json +31 -0
  34. package/hooks/session-start.js +36 -0
  35. package/hooks/subagent-start.js +38 -0
  36. package/lib/cli/commands/adapter.mjs +109 -0
  37. package/lib/cli/commands/clean.mjs +63 -0
  38. package/lib/cli/commands/status.mjs +94 -0
  39. package/package.json +43 -67
  40. package/plugin.yaml +16 -0
  41. package/scripts/uninstall.js +38 -0
  42. package/skills/asc/SKILL.md +16 -0
  43. package/skills/asc-audit/SKILL.md +28 -0
  44. package/skills/asc-refactor/SKILL.md +35 -0
  45. package/skills/asc-review/SKILL.md +54 -0
  46. package/.agent-context/policies/llm-judge-threshold.json +0 -29
  47. package/.agent-context/prompts/bootstrap-design.md +0 -63
  48. package/.agent-context/prompts/compact-natural-mode.md +0 -100
  49. package/.agent-context/prompts/init-project.md +0 -60
  50. package/.agent-context/prompts/refactor.md +0 -38
  51. package/.agent-context/prompts/review-code.md +0 -31
  52. package/.agent-context/review-checklists/architecture-review.md +0 -57
  53. package/.agent-context/review-checklists/pr-checklist.md +0 -136
  54. package/.agent-context/rules/api-docs.md +0 -24
  55. package/.agent-context/rules/api-versioning.md +0 -16
  56. package/.agent-context/rules/architecture.md +0 -33
  57. package/.agent-context/rules/background-jobs.md +0 -17
  58. package/.agent-context/rules/config-and-flags.md +0 -16
  59. package/.agent-context/rules/database-design.md +0 -18
  60. package/.agent-context/rules/docker-runtime.md +0 -17
  61. package/.agent-context/rules/error-handling.md +0 -16
  62. package/.agent-context/rules/event-driven.md +0 -16
  63. package/.agent-context/rules/frontend-architecture.md +0 -54
  64. package/.agent-context/rules/microservices.md +0 -16
  65. package/.agent-context/rules/migrations.md +0 -16
  66. package/.agent-context/rules/observability.md +0 -16
  67. package/.agent-context/rules/performance.md +0 -16
  68. package/.agent-context/rules/realtime.md +0 -16
  69. package/.agent-context/rules/resilience.md +0 -18
  70. package/.agent-context/rules/security.md +0 -18
  71. package/.agent-context/rules/testing.md +0 -16
  72. package/.agent-context/state/README.md +0 -25
  73. package/.agent-context/state/architecture-map.md +0 -40
  74. package/.agent-context/state/benchmark-comparison-schema.json +0 -181
  75. package/.agent-context/state/benchmark-reproducibility.json +0 -87
  76. package/.agent-context/state/benchmark-thresholds.json +0 -10
  77. package/.agent-context/state/benchmark-writer-judge-config.json +0 -58
  78. package/.agent-context/state/dependency-map.md +0 -41
  79. package/.agent-context/state/memory-adapter-contract.json +0 -52
  80. package/.agent-context/state/memory-schema-v1.json +0 -88
  81. package/GEMINI.md +0 -1
  82. package/benchmarks/README.md +0 -60
  83. package/benchmarks/compact-natural-mode/fixtures.mjs +0 -359
  84. package/benchmarks/compact-natural-mode/scorer.mjs +0 -331
  85. package/benchmarks/runtime-token-saver/fixtures.mjs +0 -836
  86. package/lib/cli/adaptive-context/catalog.mjs +0 -390
  87. package/lib/cli/adaptive-context/file-signals.mjs +0 -100
  88. package/lib/cli/adaptive-context/implications.mjs +0 -44
  89. package/lib/cli/adaptive-context.mjs +0 -365
  90. package/lib/cli/audits/typography-palette-anti-repeat/color-utils.mjs +0 -156
  91. package/lib/cli/audits/typography-palette-anti-repeat/file-scanner.mjs +0 -103
  92. package/lib/cli/audits/typography-palette-anti-repeat/typography-utils.mjs +0 -70
  93. package/lib/cli/audits/typography-palette-anti-repeat-audit.mjs +0 -239
  94. package/lib/cli/backup.mjs +0 -210
  95. package/lib/cli/commands/audit-design-anti-repeat.mjs +0 -39
  96. package/lib/cli/commands/context.mjs +0 -140
  97. package/lib/cli/commands/init/project-context.mjs +0 -60
  98. package/lib/cli/commands/init/runtime-environment.mjs +0 -59
  99. package/lib/cli/commands/init/setup-decisions.mjs +0 -83
  100. package/lib/cli/commands/init.mjs +0 -654
  101. package/lib/cli/commands/launch.mjs +0 -76
  102. package/lib/cli/commands/optimize.mjs +0 -284
  103. package/lib/cli/commands/rollback.mjs +0 -64
  104. package/lib/cli/commands/upgrade/token-optimization-state.mjs +0 -51
  105. package/lib/cli/commands/upgrade.mjs +0 -451
  106. package/lib/cli/compiler.mjs +0 -570
  107. package/lib/cli/constants.mjs +0 -136
  108. package/lib/cli/detector/constants.mjs +0 -135
  109. package/lib/cli/detector/design-evidence/collector.mjs +0 -256
  110. package/lib/cli/detector/design-evidence/constants.mjs +0 -39
  111. package/lib/cli/detector/design-evidence/file-traversal.mjs +0 -83
  112. package/lib/cli/detector/design-evidence/structured-attribute-evidence.mjs +0 -117
  113. package/lib/cli/detector/design-evidence/summary.mjs +0 -109
  114. package/lib/cli/detector/design-evidence/utility-helpers.mjs +0 -122
  115. package/lib/cli/detector/design-evidence.mjs +0 -25
  116. package/lib/cli/detector/stack-detection.mjs +0 -243
  117. package/lib/cli/detector/ui-signals.mjs +0 -150
  118. package/lib/cli/detector/workspace-scan.mjs +0 -177
  119. package/lib/cli/detector.mjs +0 -23
  120. package/lib/cli/init-detection-flow.mjs +0 -48
  121. package/lib/cli/init-options.mjs +0 -237
  122. package/lib/cli/init-selection.mjs +0 -29
  123. package/lib/cli/memory-continuity.mjs +0 -537
  124. package/lib/cli/preflight.mjs +0 -76
  125. package/lib/cli/project-scaffolder/constants.mjs +0 -69
  126. package/lib/cli/project-scaffolder/discovery.mjs +0 -272
  127. package/lib/cli/project-scaffolder/prompt-builders.mjs +0 -171
  128. package/lib/cli/project-scaffolder/storage.mjs +0 -144
  129. package/lib/cli/project-scaffolder/ui-scope-detection.mjs +0 -36
  130. package/lib/cli/project-scaffolder.mjs +0 -29
  131. package/lib/cli/rollback.mjs +0 -66
  132. package/lib/cli/token-optimization.mjs +0 -486
  133. package/lib/cli/utils/filesystem.mjs +0 -81
  134. package/lib/cli/utils/managed-surface.mjs +0 -280
  135. package/lib/cli/utils/prompting.mjs +0 -44
  136. package/lib/cli/utils.mjs +0 -479
  137. package/mcp.json +0 -134
  138. package/scripts/adaptive-context/fixtures.mjs +0 -183
  139. package/scripts/adaptive-context-benchmark.mjs +0 -9
  140. package/scripts/ascx-runtime-token-saver-benchmark.mjs +0 -9
  141. package/scripts/audit-cache-layer-contract.mjs +0 -263
  142. package/scripts/audit-caching-scope-hygiene.mjs +0 -268
  143. package/scripts/audit-file-size.mjs +0 -219
  144. package/scripts/audit-reflection-citations.mjs +0 -163
  145. package/scripts/audit-release-bundle.mjs +0 -170
  146. package/scripts/audit-rule-id-uniqueness.mjs +0 -313
  147. package/scripts/audit-typography-palette-anti-repeat.mjs +0 -120
  148. package/scripts/benchmark-evidence-bundle.mjs +0 -645
  149. package/scripts/benchmark-gate.mjs +0 -121
  150. package/scripts/benchmark-intelligence.mjs +0 -33
  151. package/scripts/benchmark-writer-judge-matrix.mjs +0 -379
  152. package/scripts/build-release-benchmark-bundle.mjs +0 -202
  153. package/scripts/bump-version.mjs +0 -103
  154. package/scripts/clean-local-artifacts.mjs +0 -78
  155. package/scripts/compact-natural-mode-benchmark.mjs +0 -9
  156. package/scripts/context-triggered-audit.mjs +0 -391
  157. package/scripts/detection-benchmark.mjs +0 -127
  158. package/scripts/docs-quality-drift-report.mjs +0 -358
  159. package/scripts/documentation-boundary-audit.mjs +0 -305
  160. package/scripts/explain-on-demand-audit.mjs +0 -427
  161. package/scripts/forbidden-content-check.mjs +0 -123
  162. package/scripts/frontend-usability-audit.mjs +0 -243
  163. package/scripts/generate-sbom.mjs +0 -61
  164. package/scripts/governance-weekly-report.mjs +0 -466
  165. package/scripts/llm-judge/checklist-loader.mjs +0 -45
  166. package/scripts/llm-judge/constants.mjs +0 -66
  167. package/scripts/llm-judge/diff-collection.mjs +0 -74
  168. package/scripts/llm-judge/prompting.mjs +0 -78
  169. package/scripts/llm-judge/providers.mjs +0 -111
  170. package/scripts/llm-judge/verdict.mjs +0 -134
  171. package/scripts/llm-judge.mjs +0 -200
  172. package/scripts/memory-continuity-benchmark.mjs +0 -322
  173. package/scripts/quality-trend-report.mjs +0 -289
  174. package/scripts/release-gate/audit-checks.mjs +0 -441
  175. package/scripts/release-gate/constants.mjs +0 -53
  176. package/scripts/release-gate/runtime.mjs +0 -63
  177. package/scripts/release-gate/static-checks.mjs +0 -182
  178. package/scripts/release-gate.mjs +0 -42
  179. package/scripts/rules-guardian-audit.mjs +0 -580
  180. package/scripts/single-source-lazy-loading-audit.mjs +0 -449
  181. package/scripts/sync-thin-adapters.mjs +0 -72
  182. package/scripts/token-optimization-benchmark.mjs +0 -252
  183. package/scripts/ui-design-judge/constants.mjs +0 -13
  184. package/scripts/ui-design-judge/design-execution-summary.mjs +0 -81
  185. package/scripts/ui-design-judge/git-input.mjs +0 -134
  186. package/scripts/ui-design-judge/prompting.mjs +0 -52
  187. package/scripts/ui-design-judge/providers.mjs +0 -102
  188. package/scripts/ui-design-judge/reporting.mjs +0 -183
  189. package/scripts/ui-design-judge/rubric-calibration.mjs +0 -214
  190. package/scripts/ui-design-judge/rubric-goldset.json +0 -188
  191. package/scripts/ui-design-judge.mjs +0 -319
  192. package/scripts/ui-rubric-calibration.mjs +0 -35
  193. package/scripts/validate/config.mjs +0 -519
  194. package/scripts/validate/coverage-checks.mjs +0 -446
  195. package/scripts/validate/file-structure.mjs +0 -162
  196. package/scripts/validate/markdown-content.mjs +0 -109
  197. package/scripts/validate/project-metadata.mjs +0 -166
  198. package/scripts/validate.mjs +0 -338
@@ -1,170 +0,0 @@
1
- #!/usr/bin/env node
2
- // @ts-check
3
-
4
- /**
5
- * audit-release-bundle.mjs
6
- *
7
- * Phase 5 Task 5.4 integrity gate. Validates that
8
- * benchmarks/results/release-bundle-4.0.0.json exists, references real source
9
- * artifacts, and that each recorded SHA-256 hash still matches the current
10
- * file content. Wired into npm run validate.
11
- */
12
-
13
- import { createHash } from 'node:crypto';
14
- import { existsSync, readFileSync } from 'node:fs';
15
- import { dirname, join, resolve } from 'node:path';
16
- import { fileURLToPath } from 'node:url';
17
-
18
- const SCRIPT_FILE_PATH = fileURLToPath(import.meta.url);
19
- const REPOSITORY_ROOT = resolve(dirname(SCRIPT_FILE_PATH), '..');
20
- const ARGS = new Set(process.argv.slice(2));
21
- const JSON_ONLY = ARGS.has('--json');
22
-
23
- const DEFAULT_BUNDLE_PATH = join(REPOSITORY_ROOT, 'benchmarks', 'results', 'release-bundle-4.0.0.json');
24
-
25
- function sha256Hex(buffer) {
26
- // Normalize CRLF -> LF before hashing so bundle integrity is reproducible
27
- // across platforms (Windows working tree may be CRLF; CI checkouts are LF).
28
- const normalized = buffer.toString('utf8').replace(/\r\n/g, '\n');
29
- const hash = createHash('sha256');
30
- hash.update(normalized, 'utf8');
31
- return hash.digest('hex');
32
- }
33
-
34
- export function runReleaseBundleAudit(options = {}) {
35
- const rootDir = options.rootDir ? resolve(String(options.rootDir)) : REPOSITORY_ROOT;
36
- const bundlePath = options.bundlePath ? resolve(String(options.bundlePath)) : DEFAULT_BUNDLE_PATH;
37
- const violations = [];
38
-
39
- if (!existsSync(bundlePath)) {
40
- violations.push({
41
- kind: 'bundle.missing',
42
- detail: `Release bundle is missing at ${bundlePath}. Run scripts/build-release-benchmark-bundle.mjs to regenerate.`,
43
- });
44
- return finalizeReport(violations, { bundlePath, artifactCount: 0 });
45
- }
46
-
47
- let parsed;
48
- try {
49
- parsed = JSON.parse(readFileSync(bundlePath, 'utf8'));
50
- } catch (error) {
51
- violations.push({
52
- kind: 'bundle.invalid-json',
53
- detail: `Release bundle is not valid JSON: ${error?.message || error}`,
54
- });
55
- return finalizeReport(violations, { bundlePath, artifactCount: 0 });
56
- }
57
-
58
- if (!Array.isArray(parsed.artifacts) || parsed.artifacts.length === 0) {
59
- violations.push({
60
- kind: 'bundle.empty',
61
- detail: 'Release bundle artifacts array must be non-empty.',
62
- });
63
- return finalizeReport(violations, { bundlePath, artifactCount: 0 });
64
- }
65
-
66
- if (parsed.integrity?.hash_algorithm !== 'SHA-256') {
67
- violations.push({
68
- kind: 'bundle.unexpected-hash-algorithm',
69
- detail: `Expected hash_algorithm "SHA-256", got "${parsed.integrity?.hash_algorithm}".`,
70
- });
71
- }
72
-
73
- if (parsed.release_status !== 'release-candidate-unpublished' && parsed.release_status !== 'released') {
74
- violations.push({
75
- kind: 'bundle.invalid-release-status',
76
- detail: `release_status must be either "release-candidate-unpublished" or "released"; got "${parsed.release_status}".`,
77
- });
78
- }
79
-
80
- for (const artifact of parsed.artifacts) {
81
- if (!artifact.relative_path || typeof artifact.relative_path !== 'string') {
82
- violations.push({
83
- kind: 'artifact.missing-relative-path',
84
- detail: `Artifact "${artifact.artifact_id}" missing relative_path.`,
85
- });
86
- continue;
87
- }
88
-
89
- const absolutePath = join(rootDir, artifact.relative_path);
90
- if (!existsSync(absolutePath)) {
91
- violations.push({
92
- kind: 'artifact.missing-file',
93
- detail: `Artifact "${artifact.artifact_id}" references missing file at ${artifact.relative_path}.`,
94
- });
95
- continue;
96
- }
97
-
98
- if (!artifact.sha256 || typeof artifact.sha256 !== 'string') {
99
- violations.push({
100
- kind: 'artifact.missing-sha256',
101
- detail: `Artifact "${artifact.artifact_id}" missing sha256 hash.`,
102
- });
103
- continue;
104
- }
105
-
106
- const currentHash = sha256Hex(readFileSync(absolutePath));
107
- if (currentHash !== artifact.sha256) {
108
- violations.push({
109
- kind: 'artifact.hash-mismatch',
110
- detail: `Artifact "${artifact.artifact_id}" sha256 drift: bundle records ${artifact.sha256}, current file is ${currentHash}. Regenerate the bundle via scripts/build-release-benchmark-bundle.mjs.`,
111
- });
112
- }
113
- }
114
-
115
- return finalizeReport(violations, {
116
- bundlePath,
117
- artifactCount: parsed.artifacts.length,
118
- releaseTarget: parsed.release_target || null,
119
- releaseStatus: parsed.release_status || null,
120
- });
121
- }
122
-
123
- function finalizeReport(violations, extras) {
124
- return {
125
- auditName: 'audit-release-bundle',
126
- reportVersion: '1.0.0',
127
- generatedAt: new Date().toISOString(),
128
- violationCount: violations.length,
129
- passed: violations.length === 0,
130
- violations,
131
- ...extras,
132
- };
133
- }
134
-
135
- function main() {
136
- const report = runReleaseBundleAudit();
137
-
138
- if (JSON_ONLY) {
139
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
140
- process.exit(report.passed ? 0 : 1);
141
- }
142
-
143
- console.log('===============================================');
144
- console.log(' audit:release-bundle');
145
- console.log('===============================================');
146
- console.log(` Bundle path: ${report.bundlePath}`);
147
- console.log(` Artifact count: ${report.artifactCount}`);
148
- console.log(` Release target: ${report.releaseTarget || '(missing)'}`);
149
- console.log(` Release status: ${report.releaseStatus || '(missing)'}`);
150
- console.log('');
151
-
152
- if (report.passed) {
153
- console.log(' Release bundle is intact. All artifact hashes match.');
154
- process.stderr.write(`AUDIT_RELEASE_BUNDLE_REPORT: ${JSON.stringify({ passed: true, artifactCount: report.artifactCount })}\n`);
155
- process.exit(0);
156
- }
157
-
158
- console.log(' Violations:');
159
- for (const violation of report.violations) {
160
- console.log(` [${violation.kind}] ${violation.detail}`);
161
- }
162
- console.log('');
163
- console.log(` ${report.violationCount} violation(s) found.`);
164
- process.stderr.write(`AUDIT_RELEASE_BUNDLE_REPORT: ${JSON.stringify({ passed: false, violationCount: report.violationCount })}\n`);
165
- process.exit(1);
166
- }
167
-
168
- if (process.argv[1] && (import.meta.url === `file://${process.argv[1].replace(/\\/g, '/')}` || process.argv[1].endsWith('audit-release-bundle.mjs'))) {
169
- main();
170
- }
@@ -1,313 +0,0 @@
1
- #!/usr/bin/env node
2
- // @ts-check
3
-
4
- /**
5
- * audit-rule-id-uniqueness.mjs
6
- *
7
- * Phase 1 GATE B citability gate. Checks every migrated rule file under
8
- * `.agent-context/rules/` for:
9
- *
10
- * 1. YAML frontmatter parses and contains required keys (id_prefix, domain,
11
- * priority, scope, applies_to, keywords).
12
- * 2. Section IDs match the file's locked id_prefix and are unique within
13
- * the file.
14
- * 3. Every `[REF:<PREFIX>-NNN]` mention across the rules pack, prompts/,
15
- * and review-checklists/ resolves to a real section ID.
16
- * 4. Every `related:` entry in any rule's frontmatter resolves to a real
17
- * section ID.
18
- * 5. No ambiguous prose references (`see above`, `as noted earlier`,
19
- * `the next section`, etc.) survive in any rule body.
20
- *
21
- * Files that have not been migrated yet (no YAML frontmatter) are skipped
22
- * with a notice. The audit only enforces shape on migrated files.
23
- *
24
- * Usage:
25
- * node scripts/audit-rule-id-uniqueness.mjs (human + report line)
26
- * node scripts/audit-rule-id-uniqueness.mjs --json (JSON only)
27
- *
28
- * Exit codes:
29
- * 0 — clean
30
- * 1 — at least one violation
31
- */
32
-
33
- import { readdirSync, readFileSync, existsSync } from 'node:fs';
34
- import { dirname, join, resolve } from 'node:path';
35
- import { fileURLToPath } from 'node:url';
36
- import { parse as parseYaml } from 'yaml';
37
-
38
- const SCRIPT_FILE_PATH = fileURLToPath(import.meta.url);
39
- const REPOSITORY_ROOT = resolve(dirname(SCRIPT_FILE_PATH), '..');
40
- const RULES_DIRECTORY = join(REPOSITORY_ROOT, '.agent-context', 'rules');
41
- const PROMPTS_DIRECTORY = join(REPOSITORY_ROOT, '.agent-context', 'prompts');
42
- const REVIEW_CHECKLISTS_DIRECTORY = join(REPOSITORY_ROOT, '.agent-context', 'review-checklists');
43
-
44
- const ARGS = new Set(process.argv.slice(2));
45
- const JSON_ONLY = ARGS.has('--json');
46
-
47
- const RULE_ID_PATTERN = /\b([A-Z]+)-(\d{3,4})(?:-([A-Z]))?\b/;
48
- const REF_PATTERN = /\[REF:([A-Z]+-\d{3,4}(?:-[A-Z])?)\]/g;
49
- const SECTION_HEADING_PATTERN = /^##\s+([A-Z]+-\d{3,4}(?:-[A-Z])?):\s+(.+)$/;
50
- const REQUIRED_FRONTMATTER_KEYS = ['id_prefix', 'domain', 'priority', 'scope', 'applies_to', 'keywords'];
51
- const FRONTMATTER_PATTERN = /^---\n([\s\S]*?)\n---\n/;
52
-
53
- const AMBIGUOUS_PROSE_REFERENCE_PATTERNS = [
54
- /\bsee above\b/i,
55
- /\bsee below\b/i,
56
- /\bas noted earlier\b/i,
57
- /\bas noted above\b/i,
58
- /\bas mentioned earlier\b/i,
59
- /\bas mentioned above\b/i,
60
- /\bin the previous section\b/i,
61
- /\bin the next section\b/i,
62
- /\bthe next section\b/i,
63
- /\bthe previous section\b/i,
64
- /\bsee earlier\b/i,
65
- /\bsee later\b/i,
66
- ];
67
-
68
- function listRuleFiles() {
69
- return readdirSync(RULES_DIRECTORY)
70
- .filter((filename) => filename.endsWith('.md') && !filename.endsWith('.candidate.md'))
71
- .sort()
72
- .map((filename) => ({ filename, absolutePath: join(RULES_DIRECTORY, filename) }));
73
- }
74
-
75
- function parseFrontmatter(sourceText) {
76
- const match = sourceText.match(FRONTMATTER_PATTERN);
77
- if (!match) return null;
78
- try {
79
- return parseYaml(match[1]);
80
- } catch {
81
- return null;
82
- }
83
- }
84
-
85
- function extractSectionIds(sourceText) {
86
- const sectionIds = [];
87
- for (const line of sourceText.split(/\r?\n/)) {
88
- const match = line.match(SECTION_HEADING_PATTERN);
89
- if (match) {
90
- sectionIds.push({ id: match[1], title: match[2].trim() });
91
- }
92
- }
93
- return sectionIds;
94
- }
95
-
96
- function findAmbiguousProseReferences(sourceText) {
97
- const findings = [];
98
- const lines = sourceText.split(/\r?\n/);
99
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
100
- for (const pattern of AMBIGUOUS_PROSE_REFERENCE_PATTERNS) {
101
- if (pattern.test(lines[lineIndex])) {
102
- findings.push({ lineNumber: lineIndex + 1, snippet: lines[lineIndex].trim() });
103
- break;
104
- }
105
- }
106
- }
107
- return findings;
108
- }
109
-
110
- function listMarkdownFilesIn(absoluteDirectoryPath) {
111
- if (!existsSync(absoluteDirectoryPath)) return [];
112
- return readdirSync(absoluteDirectoryPath)
113
- .filter((filename) => filename.endsWith('.md'))
114
- .map((filename) => join(absoluteDirectoryPath, filename));
115
- }
116
-
117
- function collectAllRefMentions() {
118
- const mentions = [];
119
- const candidatePaths = [
120
- ...listRuleFiles().map((entry) => entry.absolutePath),
121
- ...listMarkdownFilesIn(PROMPTS_DIRECTORY),
122
- ...listMarkdownFilesIn(REVIEW_CHECKLISTS_DIRECTORY),
123
- ];
124
- for (const filePath of candidatePaths) {
125
- const sourceText = readFileSync(filePath, 'utf8');
126
- for (const match of sourceText.matchAll(REF_PATTERN)) {
127
- mentions.push({ filePath, refId: match[1] });
128
- }
129
- }
130
- return mentions;
131
- }
132
-
133
- export function runRuleIdUniquenessAudit() {
134
- const violations = [];
135
- const ruleFiles = listRuleFiles();
136
- const allKnownSectionIds = new Set();
137
- const migratedFileCount = { migrated: 0, skipped: 0 };
138
- const perFile = [];
139
-
140
- for (const { filename, absolutePath } of ruleFiles) {
141
- const sourceText = readFileSync(absolutePath, 'utf8');
142
- const frontmatter = parseFrontmatter(sourceText);
143
- if (!frontmatter) {
144
- migratedFileCount.skipped += 1;
145
- perFile.push({ filename, status: 'skipped', reason: 'no-yaml-frontmatter' });
146
- continue;
147
- }
148
-
149
- migratedFileCount.migrated += 1;
150
-
151
- for (const requiredKey of REQUIRED_FRONTMATTER_KEYS) {
152
- if (!(requiredKey in frontmatter)) {
153
- violations.push({
154
- file: filename,
155
- kind: 'frontmatter.missing-key',
156
- detail: `frontmatter is missing required key: ${requiredKey}`,
157
- });
158
- }
159
- }
160
-
161
- const idPrefix = frontmatter.id_prefix;
162
- if (typeof idPrefix !== 'string' || idPrefix.length === 0) {
163
- violations.push({ file: filename, kind: 'frontmatter.invalid-id-prefix', detail: 'id_prefix must be a non-empty string' });
164
- perFile.push({ filename, status: 'failed', reason: 'invalid-id-prefix' });
165
- continue;
166
- }
167
-
168
- const sections = extractSectionIds(sourceText);
169
- const seenIdsInFile = new Set();
170
- for (const section of sections) {
171
- const idMatch = section.id.match(RULE_ID_PATTERN);
172
- if (!idMatch || idMatch[1] !== idPrefix) {
173
- violations.push({
174
- file: filename,
175
- kind: 'id.prefix-mismatch',
176
- detail: `section id '${section.id}' does not match the file's id_prefix '${idPrefix}'`,
177
- });
178
- continue;
179
- }
180
- if (seenIdsInFile.has(section.id)) {
181
- violations.push({
182
- file: filename,
183
- kind: 'id.duplicate',
184
- detail: `section id '${section.id}' is reused within the same file`,
185
- });
186
- }
187
- seenIdsInFile.add(section.id);
188
- allKnownSectionIds.add(section.id);
189
- }
190
-
191
- const ambiguousProse = findAmbiguousProseReferences(sourceText);
192
- for (const finding of ambiguousProse) {
193
- violations.push({
194
- file: filename,
195
- kind: 'prose.ambiguous-reference',
196
- detail: `line ${finding.lineNumber}: ${finding.snippet}`,
197
- });
198
- }
199
-
200
- perFile.push({
201
- filename,
202
- status: 'audited',
203
- idPrefix,
204
- sectionCount: sections.length,
205
- ambiguousProseCount: ambiguousProse.length,
206
- knownSectionIdsInFile: sections.map((section) => section.id),
207
- relatedRefs: (frontmatter.related && typeof frontmatter.related === 'object' && !Array.isArray(frontmatter.related))
208
- ? frontmatter.related
209
- : null,
210
- });
211
- }
212
-
213
- for (const fileEntry of perFile) {
214
- if (fileEntry.status !== 'audited') continue;
215
- if (!fileEntry.relatedRefs) continue;
216
- for (const [parentId, relatedList] of Object.entries(fileEntry.relatedRefs)) {
217
- if (!fileEntry.knownSectionIdsInFile.includes(parentId)) {
218
- violations.push({
219
- file: fileEntry.filename,
220
- kind: 'related.parent-missing',
221
- detail: `related map key '${parentId}' is not a section in this file`,
222
- });
223
- }
224
- if (!Array.isArray(relatedList)) {
225
- violations.push({
226
- file: fileEntry.filename,
227
- kind: 'related.malformed',
228
- detail: `related[${parentId}] must be an array of <PREFIX>-NNN ids`,
229
- });
230
- continue;
231
- }
232
- for (const relatedId of relatedList) {
233
- if (typeof relatedId !== 'string' || !RULE_ID_PATTERN.test(relatedId)) {
234
- violations.push({
235
- file: fileEntry.filename,
236
- kind: 'related.malformed',
237
- detail: `related[${parentId}] entry '${relatedId}' is not a valid <PREFIX>-NNN id`,
238
- });
239
- continue;
240
- }
241
- if (!allKnownSectionIds.has(relatedId)) {
242
- violations.push({
243
- file: fileEntry.filename,
244
- kind: 'related.unresolved',
245
- detail: `related[${parentId}] entry '${relatedId}' does not resolve to any section in the rules pack`,
246
- });
247
- }
248
- }
249
- }
250
- }
251
-
252
- const refMentions = collectAllRefMentions();
253
- for (const mention of refMentions) {
254
- if (!allKnownSectionIds.has(mention.refId)) {
255
- violations.push({
256
- file: mention.filePath.replace(REPOSITORY_ROOT, '').replace(/^[\\/]+/, ''),
257
- kind: 'ref.unresolved',
258
- detail: `[REF:${mention.refId}] does not resolve to any section in the rules pack`,
259
- });
260
- }
261
- }
262
-
263
- return {
264
- auditName: 'audit-rule-id-uniqueness',
265
- reportVersion: '1.0.0',
266
- generatedAt: new Date().toISOString(),
267
- migratedFileCount: migratedFileCount.migrated,
268
- skippedFileCount: migratedFileCount.skipped,
269
- knownSectionIdCount: allKnownSectionIds.size,
270
- refMentionCount: refMentions.length,
271
- perFile,
272
- violationCount: violations.length,
273
- violations,
274
- passed: violations.length === 0,
275
- };
276
- }
277
-
278
- function main() {
279
- const report = runRuleIdUniquenessAudit();
280
-
281
- if (JSON_ONLY) {
282
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
283
- process.exit(report.passed ? 0 : 1);
284
- }
285
-
286
- console.log('===============================================');
287
- console.log(' audit:rule-id-uniqueness');
288
- console.log('===============================================');
289
- console.log(` Migrated rule files: ${report.migratedFileCount} of ${report.migratedFileCount + report.skippedFileCount} scanned`);
290
- console.log(` Pre-migration files: ${report.skippedFileCount} (no YAML frontmatter, skipped)`);
291
- console.log(` Known section IDs: ${report.knownSectionIdCount}`);
292
- console.log(` [REF:...] mentions: ${report.refMentionCount}`);
293
- console.log('');
294
-
295
- if (report.violationCount === 0) {
296
- console.log(' All migrated files clean. No prefix mismatches, duplicates, ambiguous prose references, or unresolved [REF:] / related: links.');
297
- process.stderr.write(`AUDIT_RULE_ID_REPORT: ${JSON.stringify({ passed: true, ...{ migratedFileCount: report.migratedFileCount, knownSectionIdCount: report.knownSectionIdCount, refMentionCount: report.refMentionCount } })}\n`);
298
- process.exit(0);
299
- }
300
-
301
- console.log(' Violations:');
302
- for (const violation of report.violations) {
303
- console.log(` [${violation.kind}] ${violation.file}: ${violation.detail}`);
304
- }
305
- console.log('');
306
- console.log(` ${report.violationCount} violation(s) found. Phase 1 GATE B requires zero.`);
307
- process.stderr.write(`AUDIT_RULE_ID_REPORT: ${JSON.stringify({ passed: false, violationCount: report.violationCount, kinds: [...new Set(report.violations.map((v) => v.kind))] })}\n`);
308
- process.exit(1);
309
- }
310
-
311
- if (import.meta.url === `file://${process.argv[1].replace(/\\/g, '/')}` || process.argv[1].endsWith('audit-rule-id-uniqueness.mjs')) {
312
- main();
313
- }
@@ -1,120 +0,0 @@
1
- #!/usr/bin/env node
2
- // @ts-check
3
-
4
- /**
5
- * audit-typography-palette-anti-repeat.mjs
6
- *
7
- * Thin CLI wrapper around lib/cli/audits/typography-palette-anti-repeat-audit.mjs.
8
- * The wrapper defaults to scanning the current working directory so it works
9
- * for both repo-internal `npm run audit:typography-palette-anti-repeat` (npm
10
- * sets cwd to the repo root) and direct invocation from a user project. The
11
- * user-facing entry point is the `audit:design-anti-repeat` subcommand on
12
- * the published bin (see lib/cli/commands/audit-design-anti-repeat.mjs).
13
- *
14
- * Usage:
15
- * node scripts/audit-typography-palette-anti-repeat.mjs
16
- * node scripts/audit-typography-palette-anti-repeat.mjs --json
17
- * node scripts/audit-typography-palette-anti-repeat.mjs --palette-advisory
18
- * node scripts/audit-typography-palette-anti-repeat.mjs --threshold 0.05
19
- * node scripts/audit-typography-palette-anti-repeat.mjs --root /path/to/project
20
- *
21
- * Default severity:
22
- * - Typography matches always block.
23
- * - Palette matches block by default. Pass --palette-advisory to opt a
24
- * project into advisory-only palette reporting (release stays unblocked
25
- * by palette findings; typography still blocks).
26
- *
27
- * Exit codes:
28
- * 0 = no blocking violations
29
- * 1 = at least one BOUNDARY_TYPOGRAPHY_LEDGER_VIOLATION or, in default
30
- * mode, BOUNDARY_PALETTE_LEDGER_VIOLATION
31
- */
32
-
33
- import { resolve } from 'node:path';
34
-
35
- import { runTypographyPaletteAntiRepeatAudit } from '../lib/cli/audits/typography-palette-anti-repeat-audit.mjs';
36
-
37
- const COMMAND_LINE_ARGS = process.argv.slice(2);
38
- const SHOULD_OUTPUT_JSON_ONLY = COMMAND_LINE_ARGS.includes('--json');
39
- const SHOULD_TREAT_PALETTE_AS_ADVISORY = COMMAND_LINE_ARGS.includes('--palette-advisory');
40
-
41
- function readStringFlag(flagName) {
42
- const flagIndex = COMMAND_LINE_ARGS.indexOf(flagName);
43
- if (flagIndex === -1) {
44
- return null;
45
- }
46
- const flagValue = COMMAND_LINE_ARGS[flagIndex + 1];
47
- if (typeof flagValue !== 'string' || flagValue.length === 0 || flagValue.startsWith('--')) {
48
- return null;
49
- }
50
- return flagValue;
51
- }
52
-
53
- function readNumericFlag(flagName) {
54
- const stringValue = readStringFlag(flagName);
55
- if (stringValue === null) {
56
- return null;
57
- }
58
- const numericValue = Number(stringValue);
59
- return Number.isFinite(numericValue) ? numericValue : null;
60
- }
61
-
62
- function main() {
63
- const oklchDistanceThreshold = readNumericFlag('--threshold');
64
- const explicitRootPath = readStringFlag('--root');
65
- const repositoryRootPath = resolve(explicitRootPath || process.cwd());
66
-
67
- const auditReport = runTypographyPaletteAntiRepeatAudit({
68
- repositoryRootPath,
69
- treatPaletteAsAdvisory: SHOULD_TREAT_PALETTE_AS_ADVISORY,
70
- ...(typeof oklchDistanceThreshold === 'number' ? { oklchDistanceThreshold } : {}),
71
- });
72
-
73
- if (SHOULD_OUTPUT_JSON_ONLY) {
74
- process.stdout.write(`${JSON.stringify({ ...auditReport, repositoryRootPath }, null, 2)}\n`);
75
- process.exit(auditReport.passed ? 0 : 1);
76
- }
77
-
78
- console.log('===============================================');
79
- console.log(' audit:typography-palette-anti-repeat');
80
- console.log('===============================================');
81
- console.log(` Target directory: ${repositoryRootPath}`);
82
- console.log(` Files scanned: ${auditReport.filesScanned}`);
83
- console.log(` Typography violations: ${auditReport.typographyViolationCount} (blocking)`);
84
- console.log(` Palette findings: ${auditReport.paletteFindingCount} (${auditReport.paletteSeverity})`);
85
- console.log(` OKLCH distance threshold: ${auditReport.oklchDistanceThreshold}`);
86
- console.log('');
87
-
88
- if (auditReport.skipped) {
89
- console.log(` Audit skipped: ${auditReport.reason}`);
90
- process.exit(0);
91
- }
92
-
93
- if (auditReport.typographyViolations.length > 0) {
94
- console.log(' Typography violations (blocking):');
95
- for (const violation of auditReport.typographyViolations) {
96
- console.log(` [${violation.kind}] ${violation.file}:${violation.line} ${violation.detail}`);
97
- }
98
- console.log('');
99
- }
100
-
101
- if (auditReport.paletteFindings.length > 0) {
102
- console.log(` Palette findings (${auditReport.paletteSeverity}):`);
103
- for (const finding of auditReport.paletteFindings) {
104
- console.log(` [${finding.kind}] ${finding.file}:${finding.line} ${finding.detail}`);
105
- }
106
- console.log('');
107
- }
108
-
109
- if (auditReport.passed) {
110
- console.log(' No blocking violations against the anti-repeat ledger.');
111
- process.exit(0);
112
- }
113
- const blockingPaletteCount = auditReport.paletteSeverity === 'blocking' ? auditReport.paletteFindingCount : 0;
114
- console.log(` ${auditReport.typographyViolationCount} typography violation(s) and ${blockingPaletteCount} palette violation(s) found; release blocked.`);
115
- process.exit(1);
116
- }
117
-
118
- if (process.argv[1] && (import.meta.url === `file://${process.argv[1].replace(/\\/g, '/')}` || process.argv[1].endsWith('audit-typography-palette-anti-repeat.mjs'))) {
119
- main();
120
- }