@ryuenn3123/agentic-senior-core 4.3.2 → 4.3.5
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/.agent-context/prompts/bootstrap-design.md +56 -222
- package/.agent-context/rules/api-docs.md +17 -126
- package/.agent-context/rules/api-versioning.md +9 -86
- package/.agent-context/rules/architecture.md +18 -136
- package/.agent-context/rules/background-jobs.md +9 -85
- package/.agent-context/rules/config-and-flags.md +8 -71
- package/.agent-context/rules/database-design.md +9 -65
- package/.agent-context/rules/docker-runtime.md +9 -62
- package/.agent-context/rules/efficiency-vs-hype.md +7 -37
- package/.agent-context/rules/error-handling.md +8 -33
- package/.agent-context/rules/event-driven.md +8 -34
- package/.agent-context/rules/frontend-architecture.md +22 -140
- package/.agent-context/rules/git-workflow.md +8 -77
- package/.agent-context/rules/microservices.md +8 -36
- package/.agent-context/rules/migrations.md +8 -76
- package/.agent-context/rules/observability.md +7 -60
- package/.agent-context/rules/performance.md +8 -28
- package/.agent-context/rules/realtime.md +7 -22
- package/.agent-context/rules/resilience.md +9 -69
- package/.agent-context/rules/security.md +9 -64
- package/.agent-context/rules/testing.md +8 -34
- package/AGENTS.md +11 -18
- package/README.md +1 -1
- package/lib/cli/adaptive-context/catalog.mjs +1 -6
- package/lib/cli/commands/audit-design-anti-repeat.mjs +26 -185
- package/lib/cli/commands/init/project-context.mjs +0 -41
- package/lib/cli/commands/init.mjs +12 -41
- package/lib/cli/commands/upgrade.mjs +12 -35
- package/lib/cli/compiler.mjs +5 -81
- package/lib/cli/preflight.mjs +0 -21
- package/lib/cli/project-scaffolder/constants.mjs +1 -1
- package/lib/cli/project-scaffolder/design-contract.mjs +3 -45
- package/lib/cli/project-scaffolder/prompt-builders.mjs +18 -161
- package/lib/cli/project-scaffolder/storage.mjs +0 -9
- package/lib/cli/project-scaffolder.mjs +0 -1
- package/package.json +1 -1
- package/scripts/frontend-usability-audit.mjs +4 -45
- package/scripts/release-gate/constants.mjs +1 -0
- package/scripts/release-gate/static-checks.mjs +0 -36
- package/scripts/validate/config.mjs +20 -144
- package/scripts/validate/coverage-checks.mjs +2 -12
- package/scripts/validate/file-structure.mjs +165 -0
- package/scripts/validate/markdown-content.mjs +109 -0
- package/scripts/validate/project-metadata.mjs +166 -0
- package/scripts/validate.mjs +42 -435
- package/.agent-context/prompts/research-design.md +0 -160
- package/lib/cli/commands/upgrade/design-intent-seed.mjs +0 -46
- package/lib/cli/project-scaffolder/design-contract/research-dossier-migration.mjs +0 -190
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { ALLOWED_SEVERITIES } from './config.mjs';
|
|
3
|
+
|
|
4
|
+
export async function validatePackageMetadata(context) {
|
|
5
|
+
const { PACKAGE_JSON_PATH, BUN_LOCK_PATH, readTextFile, fileExists, pass, fail, warn } = context;
|
|
6
|
+
console.log('\nChecking package metadata...');
|
|
7
|
+
|
|
8
|
+
const packageJson = JSON.parse(await readTextFile(PACKAGE_JSON_PATH));
|
|
9
|
+
const versionPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
10
|
+
|
|
11
|
+
if (typeof packageJson.version !== 'string' || !versionPattern.test(packageJson.version)) {
|
|
12
|
+
fail('package.json version must be a semantic version string');
|
|
13
|
+
} else {
|
|
14
|
+
pass(`package.json version ${packageJson.version}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (packageJson.scripts?.validate === 'node ./scripts/validate.mjs') {
|
|
18
|
+
pass('package.json validate script is Node-first');
|
|
19
|
+
} else {
|
|
20
|
+
fail('package.json validate script must use node ./scripts/validate.mjs');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (packageJson.scripts?.test) {
|
|
24
|
+
pass('package.json test script exists');
|
|
25
|
+
} else {
|
|
26
|
+
fail('package.json test script is missing');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (packageJson.devDependencies && Object.keys(packageJson.devDependencies).length > 0) {
|
|
30
|
+
warn('package.json still has devDependencies; review whether they are necessary');
|
|
31
|
+
} else {
|
|
32
|
+
pass('package.json has no unnecessary devDependencies');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (Array.isArray(packageJson.files) && packageJson.files.includes('AGENTS.md')) {
|
|
36
|
+
pass('package.json publishes canonical AGENTS.md');
|
|
37
|
+
} else {
|
|
38
|
+
fail('package.json must publish AGENTS.md so init and upgrade can copy the canonical root instructions file');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (await fileExists(BUN_LOCK_PATH)) {
|
|
42
|
+
fail('bun.lock must not be tracked while npm is the package manager source of truth');
|
|
43
|
+
} else {
|
|
44
|
+
pass('No bun.lock drift file present');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function validatePolicyFile(context) {
|
|
49
|
+
const { POLICY_FILE_PATH, readTextFile, pass, fail } = context;
|
|
50
|
+
console.log('\nChecking LLM Judge policy...');
|
|
51
|
+
|
|
52
|
+
const policyContent = await readTextFile(POLICY_FILE_PATH);
|
|
53
|
+
const parsedPolicy = JSON.parse(policyContent);
|
|
54
|
+
const selectedProfileName = parsedPolicy.selectedProfile;
|
|
55
|
+
const profileThresholds = parsedPolicy.profileThresholds;
|
|
56
|
+
|
|
57
|
+
if (typeof selectedProfileName !== 'string') {
|
|
58
|
+
fail('Policy file must define selectedProfile as a string');
|
|
59
|
+
} else {
|
|
60
|
+
pass(`LLM Judge selected profile: ${selectedProfileName}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!profileThresholds || typeof profileThresholds !== 'object') {
|
|
64
|
+
fail('Policy file must define profileThresholds');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const [profileName, profileSettings] of Object.entries(profileThresholds)) {
|
|
69
|
+
if (!Array.isArray(profileSettings.blockingSeverities)) {
|
|
70
|
+
fail(`Policy profile ${profileName} must define blockingSeverities`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const invalidSeverity = profileSettings.blockingSeverities.find((severity) => !ALLOWED_SEVERITIES.has(severity));
|
|
75
|
+
if (invalidSeverity) {
|
|
76
|
+
fail(`Policy profile ${profileName} uses unsupported severity: ${invalidSeverity}`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pass(`Policy profile ${profileName} blocking severities are valid`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (typeof profileThresholds[selectedProfileName] === 'object') {
|
|
84
|
+
pass('Policy selectedProfile points to a valid profile');
|
|
85
|
+
} else {
|
|
86
|
+
fail('Policy selectedProfile must match one of the configured profileThresholds');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function validateVersionConsistency(context) {
|
|
91
|
+
const { ROOT_DIR, PACKAGE_JSON_PATH, CHANGELOG_PATH, PACKAGE_LOCK_PATH, GENERATED_RULE_FILES, readTextFile, fileExists, pass, fail } = context;
|
|
92
|
+
console.log('\nChecking release version consistency...');
|
|
93
|
+
|
|
94
|
+
const packageJson = JSON.parse(await readTextFile(PACKAGE_JSON_PATH));
|
|
95
|
+
const packageVersion = packageJson.version;
|
|
96
|
+
const changelogContent = await readTextFile(CHANGELOG_PATH);
|
|
97
|
+
|
|
98
|
+
if (changelogContent.includes(`## ${packageVersion}`)) {
|
|
99
|
+
pass(`CHANGELOG.md contains release entry for ${packageVersion}`);
|
|
100
|
+
} else {
|
|
101
|
+
fail(`CHANGELOG.md is missing a ## ${packageVersion} heading`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (await fileExists(PACKAGE_LOCK_PATH)) {
|
|
105
|
+
const packageLock = JSON.parse(await readTextFile(PACKAGE_LOCK_PATH));
|
|
106
|
+
const rootLockVersion = packageLock.packages?.['']?.version;
|
|
107
|
+
if (packageLock.version === packageVersion && rootLockVersion === packageVersion) {
|
|
108
|
+
pass(`package-lock.json matches package version ${packageVersion}`);
|
|
109
|
+
} else {
|
|
110
|
+
fail(`package-lock.json version drift: expected ${packageVersion}, found ${packageLock.version || 'missing'} / ${rootLockVersion || 'missing'}`);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
fail('package-lock.json is required for npm release consistency');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const generatedRuleFileName of GENERATED_RULE_FILES) {
|
|
117
|
+
const generatedRuleContent = await readTextFile(join(ROOT_DIR, generatedRuleFileName));
|
|
118
|
+
|
|
119
|
+
if (generatedRuleContent.includes(`Generated by Agentic-Senior-Core CLI v${packageVersion}`)) {
|
|
120
|
+
pass(`${generatedRuleFileName} matches package version ${packageVersion}`);
|
|
121
|
+
} else {
|
|
122
|
+
fail(`${generatedRuleFileName} does not match package version ${packageVersion}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function validateMcpConfiguration(context) {
|
|
128
|
+
const { ROOT_DIR, readTextFile, pass, fail } = context;
|
|
129
|
+
console.log('\nChecking MCP configuration...');
|
|
130
|
+
|
|
131
|
+
const mcpConfiguration = JSON.parse(await readTextFile(join(ROOT_DIR, 'mcp.json')));
|
|
132
|
+
const workspaceMcpConfiguration = JSON.parse(await readTextFile(join(ROOT_DIR, '.vscode', 'mcp.json')));
|
|
133
|
+
const workspaceServerConfig = workspaceMcpConfiguration.servers?.['agentic-senior-core'];
|
|
134
|
+
|
|
135
|
+
if (mcpConfiguration.knowledgeLayers?.enabled === true) {
|
|
136
|
+
pass('Root MCP config has knowledgeLayers enabled');
|
|
137
|
+
} else {
|
|
138
|
+
fail('Root MCP config must have knowledgeLayers.enabled: true');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (typeof workspaceMcpConfiguration.$schema === 'undefined') {
|
|
142
|
+
pass('Workspace MCP config omits $schema (supported by current VS Code MCP schema inference)');
|
|
143
|
+
} else if (workspaceMcpConfiguration.$schema === 'vscode://schemas/mcp') {
|
|
144
|
+
pass('Workspace MCP config uses trusted VS Code schema');
|
|
145
|
+
} else {
|
|
146
|
+
fail('Workspace MCP config $schema must be omitted or set to vscode://schemas/mcp');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (workspaceServerConfig?.command === 'node') {
|
|
150
|
+
pass('Workspace MCP server command uses Node');
|
|
151
|
+
} else {
|
|
152
|
+
fail('Workspace MCP server command must use Node');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (workspaceServerConfig?.cwd === '${workspaceFolder}') {
|
|
156
|
+
pass('Workspace MCP server cwd uses ${workspaceFolder}');
|
|
157
|
+
} else {
|
|
158
|
+
fail('Workspace MCP server cwd must be ${workspaceFolder}');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (Array.isArray(workspaceServerConfig?.args) && workspaceServerConfig.args.includes('./scripts/mcp-server.mjs')) {
|
|
162
|
+
pass('Workspace MCP server points to scripts/mcp-server.mjs');
|
|
163
|
+
} else {
|
|
164
|
+
fail('Workspace MCP server must include ./scripts/mcp-server.mjs argument');
|
|
165
|
+
}
|
|
166
|
+
}
|
package/scripts/validate.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// Phase 1 governance refactor completed: monolithic script split into modular sub-files.
|
|
4
4
|
/**
|
|
5
5
|
* validate.mjs — Repository Integrity Validator
|
|
6
6
|
*
|
|
@@ -42,6 +42,23 @@ import {
|
|
|
42
42
|
validateUniversalSopConsolidationCoverage,
|
|
43
43
|
validateUpgradeUiContractWarningCoverage,
|
|
44
44
|
} from './validate/coverage-checks.mjs';
|
|
45
|
+
import {
|
|
46
|
+
validateRequiredFiles,
|
|
47
|
+
validateRuleFiles,
|
|
48
|
+
validateChecklistConsolidation,
|
|
49
|
+
} from './validate/file-structure.mjs';
|
|
50
|
+
import {
|
|
51
|
+
validateMarkdownFiles,
|
|
52
|
+
validateCrossReferences,
|
|
53
|
+
validateAgentsManifest,
|
|
54
|
+
validateDocumentationFlow,
|
|
55
|
+
} from './validate/markdown-content.mjs';
|
|
56
|
+
import {
|
|
57
|
+
validatePackageMetadata,
|
|
58
|
+
validatePolicyFile,
|
|
59
|
+
validateVersionConsistency,
|
|
60
|
+
validateMcpConfiguration,
|
|
61
|
+
} from './validate/project-metadata.mjs';
|
|
45
62
|
|
|
46
63
|
const SCRIPT_FILE_PATH = fileURLToPath(import.meta.url);
|
|
47
64
|
const ROOT_DIR = resolve(dirname(SCRIPT_FILE_PATH), '..');
|
|
@@ -128,390 +145,6 @@ function normalizeLineEndings(content) {
|
|
|
128
145
|
return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
129
146
|
}
|
|
130
147
|
|
|
131
|
-
async function validateRequiredFiles() {
|
|
132
|
-
console.log('\nChecking required files...');
|
|
133
|
-
|
|
134
|
-
const requiredFiles = [
|
|
135
|
-
'bin/agentic-senior-core.js',
|
|
136
|
-
'scripts/validate.mjs',
|
|
137
|
-
'scripts/llm-judge.mjs',
|
|
138
|
-
'scripts/detection-benchmark.mjs',
|
|
139
|
-
'scripts/benchmark-evidence-bundle.mjs',
|
|
140
|
-
'scripts/benchmark-writer-judge-matrix.mjs',
|
|
141
|
-
'scripts/benchmark-gate.mjs',
|
|
142
|
-
'scripts/benchmark-intelligence.mjs',
|
|
143
|
-
'scripts/memory-continuity-benchmark.mjs',
|
|
144
|
-
'scripts/docs-quality-drift-report.mjs',
|
|
145
|
-
'scripts/governance-weekly-report.mjs',
|
|
146
|
-
'scripts/mcp-server.mjs',
|
|
147
|
-
'scripts/mcp-server/constants.mjs',
|
|
148
|
-
'scripts/mcp-server/tool-registry.mjs',
|
|
149
|
-
'scripts/mcp-server/tools.mjs',
|
|
150
|
-
'scripts/frontend-usability-audit.mjs',
|
|
151
|
-
'scripts/ui-design-judge.mjs',
|
|
152
|
-
'scripts/documentation-boundary-audit.mjs',
|
|
153
|
-
'scripts/context-triggered-audit.mjs',
|
|
154
|
-
'scripts/rules-guardian-audit.mjs',
|
|
155
|
-
'scripts/explain-on-demand-audit.mjs',
|
|
156
|
-
'scripts/single-source-lazy-loading-audit.mjs',
|
|
157
|
-
'scripts/audit-cache-layer-contract.mjs',
|
|
158
|
-
'scripts/audit-typography-palette-anti-repeat.mjs',
|
|
159
|
-
'lib/cli/audits/typography-palette-anti-repeat-audit.mjs',
|
|
160
|
-
'lib/cli/commands/audit-design-anti-repeat.mjs',
|
|
161
|
-
'scripts/sync-thin-adapters.mjs',
|
|
162
|
-
'scripts/release-gate.mjs',
|
|
163
|
-
'scripts/generate-sbom.mjs',
|
|
164
|
-
'.agent-context/policies/llm-judge-threshold.json',
|
|
165
|
-
'.agent-context/prompts/compact-natural-mode.md',
|
|
166
|
-
'.agent-context/prompts/research-design.md',
|
|
167
|
-
'mcp.json',
|
|
168
|
-
'AGENTS.md',
|
|
169
|
-
'CLAUDE.md',
|
|
170
|
-
'GEMINI.md',
|
|
171
|
-
'README.md',
|
|
172
|
-
'CHANGELOG.md',
|
|
173
|
-
'docs/doc-index.md',
|
|
174
|
-
'docs/project-brief.md',
|
|
175
|
-
'docs/flow-overview.md',
|
|
176
|
-
'docs/api-contract.md',
|
|
177
|
-
'docs/faq.md',
|
|
178
|
-
'docs/deep-dive.md',
|
|
179
|
-
'docs/archive/HISTORY.md',
|
|
180
|
-
'docs/archive/CHANGELOG-archive.md',
|
|
181
|
-
'.agent-context/state/benchmark-reproducibility.json',
|
|
182
|
-
'.agent-context/state/benchmark-writer-judge-config.json',
|
|
183
|
-
'.agent-context/state/memory-schema-v1.json',
|
|
184
|
-
'.agent-context/state/memory-adapter-contract.json',
|
|
185
|
-
'.vscode/mcp.json',
|
|
186
|
-
'.github/workflows/release-gate.yml',
|
|
187
|
-
'.github/workflows/sbom-compliance.yml',
|
|
188
|
-
'.github/workflows/benchmark-intelligence.yml',
|
|
189
|
-
'.github/workflows/docs-quality-drift-report.yml',
|
|
190
|
-
'.github/workflows/governance-weekly-report.yml',
|
|
191
|
-
'tests/cli-smoke.test.mjs',
|
|
192
|
-
'tests/mcp-server.test.mjs',
|
|
193
|
-
'tests/llm-judge.test.mjs',
|
|
194
|
-
'tests/operations.test.mjs',
|
|
195
|
-
'LICENSE',
|
|
196
|
-
'.gitignore',
|
|
197
|
-
];
|
|
198
|
-
|
|
199
|
-
for (const requiredFilePath of requiredFiles) {
|
|
200
|
-
const absoluteRequiredFilePath = join(ROOT_DIR, requiredFilePath);
|
|
201
|
-
|
|
202
|
-
if (await fileExists(absoluteRequiredFilePath)) {
|
|
203
|
-
pass(requiredFilePath);
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
fail(`Missing required file: ${requiredFilePath}`);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async function validateMarkdownFiles() {
|
|
212
|
-
console.log('\nChecking markdown content...');
|
|
213
|
-
|
|
214
|
-
const markdownFilePaths = await collectFiles(ROOT_DIR, (fileName) => fileName.endsWith('.md'));
|
|
215
|
-
|
|
216
|
-
for (const markdownFilePath of markdownFilePaths) {
|
|
217
|
-
const markdownContent = await readTextFile(markdownFilePath);
|
|
218
|
-
const relativeMarkdownPath = relative(ROOT_DIR, markdownFilePath);
|
|
219
|
-
|
|
220
|
-
if (markdownContent.trim().length === 0) {
|
|
221
|
-
fail(`Empty markdown file: ${relativeMarkdownPath}`);
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
pass(`${relativeMarkdownPath} (${markdownContent.length} chars)`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async function validateRuleFiles() {
|
|
230
|
-
console.log('\nChecking rule, checklist, prompt, and state files...');
|
|
231
|
-
|
|
232
|
-
const expectedPaths = [
|
|
233
|
-
'rules/naming-conv.md',
|
|
234
|
-
'rules/architecture.md',
|
|
235
|
-
'rules/security.md',
|
|
236
|
-
'rules/performance.md',
|
|
237
|
-
'rules/error-handling.md',
|
|
238
|
-
'rules/testing.md',
|
|
239
|
-
'rules/git-workflow.md',
|
|
240
|
-
'rules/efficiency-vs-hype.md',
|
|
241
|
-
'rules/api-docs.md',
|
|
242
|
-
'rules/microservices.md',
|
|
243
|
-
'rules/event-driven.md',
|
|
244
|
-
'rules/database-design.md',
|
|
245
|
-
'rules/realtime.md',
|
|
246
|
-
'rules/frontend-architecture.md',
|
|
247
|
-
'rules/docker-runtime.md',
|
|
248
|
-
'rules/observability.md',
|
|
249
|
-
'rules/resilience.md',
|
|
250
|
-
'rules/migrations.md',
|
|
251
|
-
'rules/background-jobs.md',
|
|
252
|
-
'rules/config-and-flags.md',
|
|
253
|
-
'rules/api-versioning.md',
|
|
254
|
-
'review-checklists/pr-checklist.md',
|
|
255
|
-
'review-checklists/architecture-review.md',
|
|
256
|
-
'prompts/init-project.md',
|
|
257
|
-
'prompts/compact-natural-mode.md',
|
|
258
|
-
'prompts/bootstrap-design.md',
|
|
259
|
-
'prompts/refactor.md',
|
|
260
|
-
'prompts/review-code.md',
|
|
261
|
-
'state/architecture-map.md',
|
|
262
|
-
'state/dependency-map.md',
|
|
263
|
-
];
|
|
264
|
-
|
|
265
|
-
for (const expectedPath of expectedPaths) {
|
|
266
|
-
const absoluteExpectedPath = join(AGENT_CONTEXT_DIR, expectedPath);
|
|
267
|
-
|
|
268
|
-
if (!(await fileExists(absoluteExpectedPath))) {
|
|
269
|
-
fail(`Missing agent context file: .agent-context/${expectedPath}`);
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const fileContent = await readTextFile(absoluteExpectedPath);
|
|
274
|
-
if (fileContent.trim().length < 100) {
|
|
275
|
-
fail(`Agent context file is suspiciously short: .agent-context/${expectedPath}`);
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
pass(`.agent-context/${expectedPath}`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
async function validateChecklistConsolidation() {
|
|
284
|
-
console.log('\nChecking review checklist consolidation...');
|
|
285
|
-
|
|
286
|
-
const reviewChecklistDirectoryPath = join(AGENT_CONTEXT_DIR, 'review-checklists');
|
|
287
|
-
const checklistEntries = await readdir(reviewChecklistDirectoryPath, { withFileTypes: true });
|
|
288
|
-
const checklistFileNames = checklistEntries
|
|
289
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
|
|
290
|
-
.map((entry) => entry.name)
|
|
291
|
-
.sort((leftName, rightName) => leftName.localeCompare(rightName));
|
|
292
|
-
|
|
293
|
-
const expectedChecklistFileNames = ['architecture-review.md', 'pr-checklist.md'];
|
|
294
|
-
|
|
295
|
-
if (checklistFileNames.length <= 2) {
|
|
296
|
-
pass(`Checklist count is consolidated (${checklistFileNames.length}/2)`);
|
|
297
|
-
} else {
|
|
298
|
-
fail(`Checklist count exceeds limit (${checklistFileNames.length}/2): ${checklistFileNames.join(', ')}`);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
for (const expectedChecklistFileName of expectedChecklistFileNames) {
|
|
302
|
-
if (checklistFileNames.includes(expectedChecklistFileName)) {
|
|
303
|
-
pass(`Checklist exists: .agent-context/review-checklists/${expectedChecklistFileName}`);
|
|
304
|
-
} else {
|
|
305
|
-
fail(`Missing consolidated checklist: .agent-context/review-checklists/${expectedChecklistFileName}`);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
async function validateCrossReferences() {
|
|
311
|
-
console.log('\nChecking internal links...');
|
|
312
|
-
|
|
313
|
-
const markdownFilePaths = await collectFiles(ROOT_DIR, (fileName) => fileName.endsWith('.md'));
|
|
314
|
-
const linkPattern = /\[([^\]]*)\]\((?!https?:\/\/|#)([^)]+)\)/g;
|
|
315
|
-
let checkedLinkCount = 0;
|
|
316
|
-
|
|
317
|
-
for (const markdownFilePath of markdownFilePaths) {
|
|
318
|
-
const markdownContent = await readTextFile(markdownFilePath);
|
|
319
|
-
const currentFileDirectory = dirname(markdownFilePath);
|
|
320
|
-
const relativeMarkdownPath = relative(ROOT_DIR, markdownFilePath);
|
|
321
|
-
let linkMatch = linkPattern.exec(markdownContent);
|
|
322
|
-
|
|
323
|
-
while (linkMatch) {
|
|
324
|
-
const rawLinkTarget = linkMatch[2].split('#')[0];
|
|
325
|
-
if (rawLinkTarget) {
|
|
326
|
-
checkedLinkCount += 1;
|
|
327
|
-
const resolvedLinkPath = resolve(currentFileDirectory, rawLinkTarget);
|
|
328
|
-
|
|
329
|
-
if (await fileExists(resolvedLinkPath)) {
|
|
330
|
-
pass(`${relativeMarkdownPath} → ${linkMatch[2]}`);
|
|
331
|
-
} else {
|
|
332
|
-
fail(`Broken link in ${relativeMarkdownPath}: ${linkMatch[2]}`);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
linkMatch = linkPattern.exec(markdownContent);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (checkedLinkCount === 0) {
|
|
341
|
-
warn('No internal links were found in markdown files');
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
async function validateAgentsManifest() {
|
|
346
|
-
console.log('\nChecking AGENTS.md manifest links...');
|
|
347
|
-
|
|
348
|
-
const agentsContent = await readTextFile(join(ROOT_DIR, 'AGENTS.md'));
|
|
349
|
-
const fileReferencePattern = /\[`?([^`\]]+)`?\]\(([^)]+)\)/g;
|
|
350
|
-
let manifestLinkCount = 0;
|
|
351
|
-
let fileReferenceMatch = fileReferencePattern.exec(agentsContent);
|
|
352
|
-
|
|
353
|
-
while (fileReferenceMatch) {
|
|
354
|
-
const manifestLinkTarget = fileReferenceMatch[2];
|
|
355
|
-
|
|
356
|
-
if (!manifestLinkTarget.startsWith('http')) {
|
|
357
|
-
manifestLinkCount += 1;
|
|
358
|
-
const resolvedManifestLinkPath = resolve(ROOT_DIR, manifestLinkTarget);
|
|
359
|
-
|
|
360
|
-
if (await fileExists(resolvedManifestLinkPath)) {
|
|
361
|
-
pass(`AGENTS.md → ${manifestLinkTarget}`);
|
|
362
|
-
} else {
|
|
363
|
-
fail(`AGENTS.md references missing file: ${manifestLinkTarget}`);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
fileReferenceMatch = fileReferencePattern.exec(agentsContent);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (manifestLinkCount === 0) {
|
|
371
|
-
warn('AGENTS.md does not contain any local manifest links');
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
async function validatePackageMetadata() {
|
|
376
|
-
console.log('\nChecking package metadata...');
|
|
377
|
-
|
|
378
|
-
const packageJson = JSON.parse(await readTextFile(PACKAGE_JSON_PATH));
|
|
379
|
-
const versionPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
380
|
-
|
|
381
|
-
if (typeof packageJson.version !== 'string' || !versionPattern.test(packageJson.version)) {
|
|
382
|
-
fail('package.json version must be a semantic version string');
|
|
383
|
-
} else {
|
|
384
|
-
pass(`package.json version ${packageJson.version}`);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (packageJson.scripts?.validate === 'node ./scripts/validate.mjs') {
|
|
388
|
-
pass('package.json validate script is Node-first');
|
|
389
|
-
} else {
|
|
390
|
-
fail('package.json validate script must use node ./scripts/validate.mjs');
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (packageJson.scripts?.test) {
|
|
394
|
-
pass('package.json test script exists');
|
|
395
|
-
} else {
|
|
396
|
-
fail('package.json test script is missing');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (packageJson.devDependencies && Object.keys(packageJson.devDependencies).length > 0) {
|
|
400
|
-
warn('package.json still has devDependencies; review whether they are necessary');
|
|
401
|
-
} else {
|
|
402
|
-
pass('package.json has no unnecessary devDependencies');
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (Array.isArray(packageJson.files) && packageJson.files.includes('AGENTS.md')) {
|
|
406
|
-
pass('package.json publishes canonical AGENTS.md');
|
|
407
|
-
} else {
|
|
408
|
-
fail('package.json must publish AGENTS.md so init and upgrade can copy the canonical root instructions file');
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (await fileExists(BUN_LOCK_PATH)) {
|
|
412
|
-
fail('bun.lock must not be tracked while npm is the package manager source of truth');
|
|
413
|
-
} else {
|
|
414
|
-
pass('No bun.lock drift file present');
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
async function validatePolicyFile() {
|
|
419
|
-
console.log('\nChecking LLM Judge policy...');
|
|
420
|
-
|
|
421
|
-
const policyContent = await readTextFile(POLICY_FILE_PATH);
|
|
422
|
-
const parsedPolicy = JSON.parse(policyContent);
|
|
423
|
-
const selectedProfileName = parsedPolicy.selectedProfile;
|
|
424
|
-
const profileThresholds = parsedPolicy.profileThresholds;
|
|
425
|
-
|
|
426
|
-
if (typeof selectedProfileName !== 'string') {
|
|
427
|
-
fail('Policy file must define selectedProfile as a string');
|
|
428
|
-
} else {
|
|
429
|
-
pass(`LLM Judge selected profile: ${selectedProfileName}`);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (!profileThresholds || typeof profileThresholds !== 'object') {
|
|
433
|
-
fail('Policy file must define profileThresholds');
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
for (const [profileName, profileSettings] of Object.entries(profileThresholds)) {
|
|
438
|
-
if (!Array.isArray(profileSettings.blockingSeverities)) {
|
|
439
|
-
fail(`Policy profile ${profileName} must define blockingSeverities`);
|
|
440
|
-
continue;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const invalidSeverity = profileSettings.blockingSeverities.find((severity) => !ALLOWED_SEVERITIES.has(severity));
|
|
444
|
-
if (invalidSeverity) {
|
|
445
|
-
fail(`Policy profile ${profileName} uses unsupported severity: ${invalidSeverity}`);
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
pass(`Policy profile ${profileName} blocking severities are valid`);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (typeof profileThresholds[selectedProfileName] === 'object') {
|
|
453
|
-
pass('Policy selectedProfile points to a valid profile');
|
|
454
|
-
} else {
|
|
455
|
-
fail('Policy selectedProfile must match one of the configured profileThresholds');
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
async function validateVersionConsistency() {
|
|
460
|
-
console.log('\nChecking release version consistency...');
|
|
461
|
-
|
|
462
|
-
const packageJson = JSON.parse(await readTextFile(PACKAGE_JSON_PATH));
|
|
463
|
-
const packageVersion = packageJson.version;
|
|
464
|
-
const changelogContent = await readTextFile(CHANGELOG_PATH);
|
|
465
|
-
|
|
466
|
-
if (changelogContent.includes(`## ${packageVersion}`)) {
|
|
467
|
-
pass(`CHANGELOG.md contains release entry for ${packageVersion}`);
|
|
468
|
-
} else {
|
|
469
|
-
fail(`CHANGELOG.md is missing a ## ${packageVersion} heading`);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (await fileExists(PACKAGE_LOCK_PATH)) {
|
|
473
|
-
const packageLock = JSON.parse(await readTextFile(PACKAGE_LOCK_PATH));
|
|
474
|
-
const rootLockVersion = packageLock.packages?.['']?.version;
|
|
475
|
-
if (packageLock.version === packageVersion && rootLockVersion === packageVersion) {
|
|
476
|
-
pass(`package-lock.json matches package version ${packageVersion}`);
|
|
477
|
-
} else {
|
|
478
|
-
fail(`package-lock.json version drift: expected ${packageVersion}, found ${packageLock.version || 'missing'} / ${rootLockVersion || 'missing'}`);
|
|
479
|
-
}
|
|
480
|
-
} else {
|
|
481
|
-
fail('package-lock.json is required for npm release consistency');
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
for (const generatedRuleFileName of GENERATED_RULE_FILES) {
|
|
485
|
-
const generatedRuleContent = await readTextFile(join(ROOT_DIR, generatedRuleFileName));
|
|
486
|
-
|
|
487
|
-
if (generatedRuleContent.includes(`Generated by Agentic-Senior-Core CLI v${packageVersion}`)) {
|
|
488
|
-
pass(`${generatedRuleFileName} matches package version ${packageVersion}`);
|
|
489
|
-
} else {
|
|
490
|
-
fail(`${generatedRuleFileName} does not match package version ${packageVersion}`);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
async function validateDocumentationFlow() {
|
|
496
|
-
console.log('\nChecking documentation flow...');
|
|
497
|
-
|
|
498
|
-
const readmeContent = await readTextFile(README_PATH);
|
|
499
|
-
const requiredReadmeSnippets = [
|
|
500
|
-
'npx @ryuenn3123/agentic-senior-core init',
|
|
501
|
-
'npm run validate',
|
|
502
|
-
'docs/faq.md',
|
|
503
|
-
'docs/deep-dive.md',
|
|
504
|
-
'docs/archive/HISTORY.md',
|
|
505
|
-
];
|
|
506
|
-
|
|
507
|
-
for (const requiredReadmeSnippet of requiredReadmeSnippets) {
|
|
508
|
-
if (readmeContent.includes(requiredReadmeSnippet)) {
|
|
509
|
-
pass(`README.md mentions ${requiredReadmeSnippet}`);
|
|
510
|
-
} else {
|
|
511
|
-
fail(`README.md must mention ${requiredReadmeSnippet}`);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
148
|
|
|
516
149
|
async function validateFileSizeAudit() {
|
|
517
150
|
console.log('\nChecking file size threshold (audit:file-size)...');
|
|
@@ -619,45 +252,6 @@ async function validateReleaseBundleAudit() {
|
|
|
619
252
|
}
|
|
620
253
|
}
|
|
621
254
|
|
|
622
|
-
async function validateMcpConfiguration() {
|
|
623
|
-
console.log('\nChecking MCP configuration...');
|
|
624
|
-
|
|
625
|
-
const mcpConfiguration = JSON.parse(await readTextFile(join(ROOT_DIR, 'mcp.json')));
|
|
626
|
-
const workspaceMcpConfiguration = JSON.parse(await readTextFile(join(ROOT_DIR, '.vscode', 'mcp.json')));
|
|
627
|
-
const workspaceServerConfig = workspaceMcpConfiguration.servers?.['agentic-senior-core'];
|
|
628
|
-
|
|
629
|
-
if (mcpConfiguration.knowledgeLayers?.enabled === true) {
|
|
630
|
-
pass('Root MCP config has knowledgeLayers enabled');
|
|
631
|
-
} else {
|
|
632
|
-
fail('Root MCP config must have knowledgeLayers.enabled: true');
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
if (typeof workspaceMcpConfiguration.$schema === 'undefined') {
|
|
636
|
-
pass('Workspace MCP config omits $schema (supported by current VS Code MCP schema inference)');
|
|
637
|
-
} else if (workspaceMcpConfiguration.$schema === 'vscode://schemas/mcp') {
|
|
638
|
-
pass('Workspace MCP config uses trusted VS Code schema');
|
|
639
|
-
} else {
|
|
640
|
-
fail('Workspace MCP config $schema must be omitted or set to vscode://schemas/mcp');
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
if (workspaceServerConfig?.command === 'node') {
|
|
644
|
-
pass('Workspace MCP server command uses Node');
|
|
645
|
-
} else {
|
|
646
|
-
fail('Workspace MCP server command must use Node');
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
if (workspaceServerConfig?.cwd === '${workspaceFolder}') {
|
|
650
|
-
pass('Workspace MCP server cwd uses ${workspaceFolder}');
|
|
651
|
-
} else {
|
|
652
|
-
fail('Workspace MCP server cwd must be ${workspaceFolder}');
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
if (Array.isArray(workspaceServerConfig?.args) && workspaceServerConfig.args.includes('./scripts/mcp-server.mjs')) {
|
|
656
|
-
pass('Workspace MCP server points to scripts/mcp-server.mjs');
|
|
657
|
-
} else {
|
|
658
|
-
fail('Workspace MCP server must include ./scripts/mcp-server.mjs argument');
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
255
|
|
|
662
256
|
async function main() {
|
|
663
257
|
console.log('===============================================');
|
|
@@ -675,16 +269,29 @@ async function main() {
|
|
|
675
269
|
readTextFile,
|
|
676
270
|
};
|
|
677
271
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
272
|
+
const generalValidationContext = {
|
|
273
|
+
...coverageValidationContext,
|
|
274
|
+
PACKAGE_JSON_PATH,
|
|
275
|
+
PACKAGE_LOCK_PATH,
|
|
276
|
+
BUN_LOCK_PATH,
|
|
277
|
+
CHANGELOG_PATH,
|
|
278
|
+
README_PATH,
|
|
279
|
+
POLICY_FILE_PATH,
|
|
280
|
+
GENERATED_RULE_FILES,
|
|
281
|
+
collectFiles,
|
|
282
|
+
warn,
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
await validateRequiredFiles(generalValidationContext);
|
|
286
|
+
await validateMarkdownFiles(generalValidationContext);
|
|
287
|
+
await validateRuleFiles(generalValidationContext);
|
|
288
|
+
await validateChecklistConsolidation(generalValidationContext);
|
|
289
|
+
await validateAgentsManifest(generalValidationContext);
|
|
290
|
+
await validateCrossReferences(generalValidationContext);
|
|
291
|
+
await validatePackageMetadata(generalValidationContext);
|
|
292
|
+
await validatePolicyFile(generalValidationContext);
|
|
293
|
+
await validateVersionConsistency(generalValidationContext);
|
|
294
|
+
await validateDocumentationFlow(generalValidationContext);
|
|
688
295
|
await validateTerminologyMapping(coverageValidationContext);
|
|
689
296
|
await validateDetectionTransparencyCoverage(coverageValidationContext);
|
|
690
297
|
await validateStackDecisionBoundaryCoverage(coverageValidationContext);
|
|
@@ -697,7 +304,7 @@ async function main() {
|
|
|
697
304
|
await validateDependencyFreshnessAutomationCoverage(coverageValidationContext);
|
|
698
305
|
await validateDeterministicBoundaryEnforcementCoverage(coverageValidationContext);
|
|
699
306
|
await validateRulesOnlyActiveSurfaceCoverage(coverageValidationContext);
|
|
700
|
-
await validateMcpConfiguration();
|
|
307
|
+
await validateMcpConfiguration(generalValidationContext);
|
|
701
308
|
await validateHumanWritingGovernance(coverageValidationContext);
|
|
702
309
|
await validateInstructionAdapters(coverageValidationContext);
|
|
703
310
|
await validateSkillPurgeSurface(coverageValidationContext);
|