@ryuenn3123/agentic-senior-core 3.0.5 → 3.0.7
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 +38 -12
- package/.agent-context/prompts/init-project.md +3 -3
- package/.agent-context/prompts/refactor.md +1 -1
- package/.agent-context/prompts/review-code.md +1 -1
- package/.agent-context/review-checklists/pr-checklist.md +1 -1
- package/.agent-context/rules/architecture.md +1 -1
- package/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.cursorrules +2 -2
- package/.gemini/instructions.md +1 -1
- package/.github/copilot-instructions.md +1 -1
- package/.windsurfrules +2 -2
- package/AGENTS.md +1 -1
- package/README.md +5 -5
- package/lib/cli/commands/init.mjs +14 -1
- package/lib/cli/commands/upgrade.mjs +139 -3
- package/lib/cli/compiler.mjs +6 -2
- package/lib/cli/init-architecture-flow.mjs +2 -0
- package/lib/cli/project-scaffolder.mjs +214 -223
- package/lib/cli/utils.mjs +108 -43
- package/package.json +1 -1
- package/scripts/validate.mjs +39 -4
package/lib/cli/utils.mjs
CHANGED
|
@@ -112,6 +112,66 @@ export async function copyDirectory(sourceDirectoryPath, targetDirectoryPath) {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Synchronizes a single file between source and target, returning the operation status.
|
|
117
|
+
*/
|
|
118
|
+
export async function syncFile(sourcePath, targetPath) {
|
|
119
|
+
if (!(await pathExists(sourcePath))) return { status: 'skipped' };
|
|
120
|
+
|
|
121
|
+
if (!(await pathExists(targetPath))) {
|
|
122
|
+
await ensureDirectory(path.dirname(targetPath));
|
|
123
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
124
|
+
return { status: 'created' };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const sourceContent = await fs.readFile(sourcePath);
|
|
128
|
+
const targetContent = await fs.readFile(targetPath);
|
|
129
|
+
|
|
130
|
+
if (sourceContent.equals(targetContent)) {
|
|
131
|
+
return { status: 'unchanged' };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
135
|
+
return { status: 'updated' };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Intelligent MCP configuration synchronization.
|
|
140
|
+
* Merges the agentic-senior-core server into existing config or creates new.
|
|
141
|
+
*/
|
|
142
|
+
async function syncMcpConfig(mcpJsonPath, templateConfig) {
|
|
143
|
+
const topKey = Object.keys(templateConfig)[0]; // e.g. "mcpServers" or "servers"
|
|
144
|
+
const serverName = 'agentic-senior-core';
|
|
145
|
+
const templateServer = templateConfig[topKey][serverName];
|
|
146
|
+
|
|
147
|
+
if (!(await pathExists(mcpJsonPath))) {
|
|
148
|
+
await ensureDirectory(path.dirname(mcpJsonPath));
|
|
149
|
+
await fs.writeFile(mcpJsonPath, JSON.stringify(templateConfig, null, 2) + '\n', 'utf8');
|
|
150
|
+
return { status: 'created' };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const existingContent = await fs.readFile(mcpJsonPath, 'utf8');
|
|
155
|
+
const config = JSON.parse(existingContent);
|
|
156
|
+
|
|
157
|
+
if (!config[topKey]) config[topKey] = {};
|
|
158
|
+
|
|
159
|
+
const existingServer = config[topKey][serverName];
|
|
160
|
+
|
|
161
|
+
if (JSON.stringify(existingServer) === JSON.stringify(templateServer)) {
|
|
162
|
+
return { status: 'unchanged' };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
config[topKey][serverName] = templateServer;
|
|
166
|
+
await fs.writeFile(mcpJsonPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
167
|
+
return { status: 'updated' };
|
|
168
|
+
} catch {
|
|
169
|
+
// If JSON is broken, overwrite it as a last resort to recovery
|
|
170
|
+
await fs.writeFile(mcpJsonPath, JSON.stringify(templateConfig, null, 2) + '\n', 'utf8');
|
|
171
|
+
return { status: 'updated' };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
115
175
|
function toPosixRelativePath(relativePath) {
|
|
116
176
|
return relativePath.split(path.sep).join('/');
|
|
117
177
|
}
|
|
@@ -281,9 +341,7 @@ export async function analyzeManagedGovernanceSurface(
|
|
|
281
341
|
managedTargetFileCount: targetManifest.files.size,
|
|
282
342
|
managedTargetDirectoryCount: targetManifest.directories.size,
|
|
283
343
|
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export async function copyGovernanceAssetsToTarget(
|
|
344
|
+
}export async function copyGovernanceAssetsToTarget(
|
|
287
345
|
resolvedTargetDirectoryPath,
|
|
288
346
|
options = {}
|
|
289
347
|
) {
|
|
@@ -294,6 +352,9 @@ export async function copyGovernanceAssetsToTarget(
|
|
|
294
352
|
: null;
|
|
295
353
|
const deletedManagedFiles = [];
|
|
296
354
|
const deletedManagedDirectories = [];
|
|
355
|
+
const createdFiles = [];
|
|
356
|
+
const updatedFiles = [];
|
|
357
|
+
const unchangedFiles = [];
|
|
297
358
|
|
|
298
359
|
for (const sourceDirectoryName of directoryCopies) {
|
|
299
360
|
const sourceDirectoryPath = path.join(REPO_ROOT, sourceDirectoryName);
|
|
@@ -301,7 +362,16 @@ export async function copyGovernanceAssetsToTarget(
|
|
|
301
362
|
continue;
|
|
302
363
|
}
|
|
303
364
|
|
|
304
|
-
await
|
|
365
|
+
const sourceTree = await collectRelativeTreeEntries(sourceDirectoryPath, sourceDirectoryName);
|
|
366
|
+
for (const relativeFilePath of sourceTree.files) {
|
|
367
|
+
const sourcePath = path.join(REPO_ROOT, ...relativeFilePath.split('/'));
|
|
368
|
+
const targetPath = path.join(resolvedTargetDirectoryPath, ...relativeFilePath.split('/'));
|
|
369
|
+
const syncResult = await syncFile(sourcePath, targetPath);
|
|
370
|
+
|
|
371
|
+
if (syncResult.status === 'created') createdFiles.push(relativeFilePath);
|
|
372
|
+
else if (syncResult.status === 'updated') updatedFiles.push(relativeFilePath);
|
|
373
|
+
else if (syncResult.status === 'unchanged') unchangedFiles.push(relativeFilePath);
|
|
374
|
+
}
|
|
305
375
|
}
|
|
306
376
|
|
|
307
377
|
for (const entryPointFileName of entryPointFiles) {
|
|
@@ -316,8 +386,10 @@ export async function copyGovernanceAssetsToTarget(
|
|
|
316
386
|
continue;
|
|
317
387
|
}
|
|
318
388
|
|
|
319
|
-
await
|
|
320
|
-
|
|
389
|
+
const syncResult = await syncFile(sourceFilePath, targetFilePath);
|
|
390
|
+
if (syncResult.status === 'created') createdFiles.push(entryPointFileName);
|
|
391
|
+
else if (syncResult.status === 'updated') updatedFiles.push(entryPointFileName);
|
|
392
|
+
else if (syncResult.status === 'unchanged') unchangedFiles.push(entryPointFileName);
|
|
321
393
|
}
|
|
322
394
|
|
|
323
395
|
if (shouldPruneManagedSurface && managedSurfacePlan) {
|
|
@@ -346,16 +418,14 @@ export async function copyGovernanceAssetsToTarget(
|
|
|
346
418
|
const projectName = path.basename(resolvedTargetDirectoryPath);
|
|
347
419
|
const mcpArgs = ['./scripts/mcp-server.mjs'];
|
|
348
420
|
|
|
349
|
-
// Ensure the MCP server entrypoint exists in the target project.
|
|
350
|
-
// The workspace MCP configs point at ./scripts/mcp-server.mjs, so we must copy it.
|
|
351
421
|
const sourceMcpServerPath = path.join(REPO_ROOT, 'scripts', 'mcp-server.mjs');
|
|
352
422
|
const targetMcpServerPath = path.join(resolvedTargetDirectoryPath, 'scripts', 'mcp-server.mjs');
|
|
353
|
-
await
|
|
354
|
-
|
|
423
|
+
const mcpServerSync = await syncFile(sourceMcpServerPath, targetMcpServerPath);
|
|
424
|
+
if (mcpServerSync.status === 'created') createdFiles.push('scripts/mcp-server.mjs');
|
|
425
|
+
else if (mcpServerSync.status === 'updated') updatedFiles.push('scripts/mcp-server.mjs');
|
|
355
426
|
|
|
356
427
|
// 1. VS Code (Workspace Local Settings)
|
|
357
|
-
const
|
|
358
|
-
const vscodeMcpJsonPath = path.join(vscodeDirPath, 'mcp.json');
|
|
428
|
+
const vscodeMcpJsonPath = path.join(resolvedTargetDirectoryPath, '.vscode', 'mcp.json');
|
|
359
429
|
const vscodeWorkspaceMcpConfig = {
|
|
360
430
|
servers: {
|
|
361
431
|
'agentic-senior-core': {
|
|
@@ -366,15 +436,12 @@ export async function copyGovernanceAssetsToTarget(
|
|
|
366
436
|
},
|
|
367
437
|
},
|
|
368
438
|
};
|
|
369
|
-
|
|
370
|
-
if (
|
|
371
|
-
|
|
372
|
-
await fs.writeFile(vscodeMcpJsonPath, JSON.stringify(vscodeWorkspaceMcpConfig, null, 2) + '\n', 'utf8');
|
|
373
|
-
}
|
|
439
|
+
const vscodeSync = await syncMcpConfig(vscodeMcpJsonPath, vscodeWorkspaceMcpConfig);
|
|
440
|
+
if (vscodeSync.status === 'created') createdFiles.push('.vscode/mcp.json');
|
|
441
|
+
else if (vscodeSync.status === 'updated') updatedFiles.push('.vscode/mcp.json');
|
|
374
442
|
|
|
375
443
|
// 2. Cursor (Workspace Local Settings)
|
|
376
|
-
const
|
|
377
|
-
const cursorMcpJsonPath = path.join(cursorDirPath, 'mcp.json');
|
|
444
|
+
const cursorMcpJsonPath = path.join(resolvedTargetDirectoryPath, '.cursor', 'mcp.json');
|
|
378
445
|
const cursorWorkspaceMcpConfig = {
|
|
379
446
|
mcpServers: {
|
|
380
447
|
'agentic-senior-core': {
|
|
@@ -384,15 +451,12 @@ export async function copyGovernanceAssetsToTarget(
|
|
|
384
451
|
},
|
|
385
452
|
},
|
|
386
453
|
};
|
|
387
|
-
|
|
388
|
-
if (
|
|
389
|
-
|
|
390
|
-
await fs.writeFile(cursorMcpJsonPath, JSON.stringify(cursorWorkspaceMcpConfig, null, 2) + '\n', 'utf8');
|
|
391
|
-
}
|
|
454
|
+
const cursorSync = await syncMcpConfig(cursorMcpJsonPath, cursorWorkspaceMcpConfig);
|
|
455
|
+
if (cursorSync.status === 'created') createdFiles.push('.cursor/mcp.json');
|
|
456
|
+
else if (cursorSync.status === 'updated') updatedFiles.push('.cursor/mcp.json');
|
|
392
457
|
|
|
393
458
|
// 3. Zed IDE (Workspace Local Settings)
|
|
394
|
-
const
|
|
395
|
-
const zedSettingsPath = path.join(zedDirPath, 'settings.json');
|
|
459
|
+
const zedSettingsPath = path.join(resolvedTargetDirectoryPath, '.zed', 'settings.json');
|
|
396
460
|
const zedMcpConfig = {
|
|
397
461
|
context_servers: {
|
|
398
462
|
'agentic-senior-core': {
|
|
@@ -404,22 +468,22 @@ export async function copyGovernanceAssetsToTarget(
|
|
|
404
468
|
};
|
|
405
469
|
|
|
406
470
|
if (!(await pathExists(zedSettingsPath))) {
|
|
407
|
-
await ensureDirectory(
|
|
471
|
+
await ensureDirectory(path.dirname(zedSettingsPath));
|
|
408
472
|
await fs.writeFile(zedSettingsPath, JSON.stringify(zedMcpConfig, null, 2) + '\n', 'utf8');
|
|
473
|
+
createdFiles.push('.zed/settings.json');
|
|
409
474
|
} else {
|
|
410
475
|
try {
|
|
411
476
|
const existingZedContent = await fs.readFile(zedSettingsPath, 'utf8');
|
|
412
477
|
const parsedZedSettings = JSON.parse(existingZedContent);
|
|
413
|
-
if (!parsedZedSettings.context_servers) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (
|
|
417
|
-
parsedZedSettings.context_servers['agentic-senior-core'] =
|
|
478
|
+
if (!parsedZedSettings.context_servers) parsedZedSettings.context_servers = {};
|
|
479
|
+
|
|
480
|
+
const templateServer = zedMcpConfig.context_servers['agentic-senior-core'];
|
|
481
|
+
if (JSON.stringify(parsedZedSettings.context_servers['agentic-senior-core']) !== JSON.stringify(templateServer)) {
|
|
482
|
+
parsedZedSettings.context_servers['agentic-senior-core'] = templateServer;
|
|
418
483
|
await fs.writeFile(zedSettingsPath, JSON.stringify(parsedZedSettings, null, 2) + '\n', 'utf8');
|
|
484
|
+
updatedFiles.push('.zed/settings.json');
|
|
419
485
|
}
|
|
420
|
-
} catch {
|
|
421
|
-
// Fallback or ignore if user has broken JSON
|
|
422
|
-
}
|
|
486
|
+
} catch { /* Ignore malformed Zed JSON */ }
|
|
423
487
|
}
|
|
424
488
|
|
|
425
489
|
// 4. Antigravity / Gemini (Global Settings)
|
|
@@ -441,28 +505,29 @@ export async function copyGovernanceAssetsToTarget(
|
|
|
441
505
|
|
|
442
506
|
const safeProjectName = projectName.replace(/[^a-zA-Z0-9_-]/g, '-');
|
|
443
507
|
const uniqueServerName = `agentic-senior-core-${safeProjectName}`;
|
|
444
|
-
|
|
445
|
-
// For global configs, we use absolute path for cwd
|
|
446
|
-
geminiConfig.mcpServers[uniqueServerName] = {
|
|
508
|
+
const templateServer = {
|
|
447
509
|
command: 'node',
|
|
448
510
|
args: mcpArgs,
|
|
449
511
|
cwd: resolvedTargetDirectoryPath,
|
|
450
512
|
};
|
|
451
513
|
|
|
452
|
-
|
|
514
|
+
if (JSON.stringify(geminiConfig.mcpServers[uniqueServerName]) !== JSON.stringify(templateServer)) {
|
|
515
|
+
geminiConfig.mcpServers[uniqueServerName] = templateServer;
|
|
516
|
+
await fs.writeFile(globalGeminiMcpPath, JSON.stringify(geminiConfig, null, 2) + '\n', 'utf8');
|
|
517
|
+
}
|
|
453
518
|
}
|
|
454
|
-
} catch {
|
|
455
|
-
// Ignore global injection errors
|
|
456
|
-
}
|
|
519
|
+
} catch { /* Ignore global injection errors */ }
|
|
457
520
|
}
|
|
458
521
|
}
|
|
459
522
|
|
|
460
523
|
return {
|
|
461
524
|
deletedManagedFiles,
|
|
462
525
|
deletedManagedDirectories,
|
|
526
|
+
createdFiles,
|
|
527
|
+
updatedFiles,
|
|
528
|
+
unchangedFiles,
|
|
463
529
|
managedSurfacePlan,
|
|
464
530
|
};
|
|
465
|
-
|
|
466
531
|
}
|
|
467
532
|
|
|
468
533
|
export async function askChoice(promptMessage, options, userInterface) {
|
package/package.json
CHANGED
package/scripts/validate.mjs
CHANGED
|
@@ -211,19 +211,19 @@ const REQUIRED_UNIVERSAL_SOP_SNIPPETS = [
|
|
|
211
211
|
snippets: [
|
|
212
212
|
'### 15. Universal SOP Consolidation',
|
|
213
213
|
'Coding flow is blocked if `docs/architecture-decision-record.md` (or `docs/Architecture-Decision-Record.md`) is missing',
|
|
214
|
-
'UI implementation flow is blocked if `docs/DESIGN.md` is missing',
|
|
214
|
+
'UI implementation flow is blocked if `docs/DESIGN.md` or `docs/design-intent.json` is missing',
|
|
215
215
|
],
|
|
216
216
|
},
|
|
217
217
|
{
|
|
218
218
|
path: '.agent-context/prompts/review-code.md',
|
|
219
219
|
snippets: [
|
|
220
|
-
'Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md`).',
|
|
220
|
+
'Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md` plus `docs/design-intent.json`).',
|
|
221
221
|
],
|
|
222
222
|
},
|
|
223
223
|
{
|
|
224
224
|
path: '.agent-context/prompts/refactor.md',
|
|
225
225
|
snippets: [
|
|
226
|
-
'6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` is missing.',
|
|
226
|
+
'6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` or `docs/design-intent.json` is missing.',
|
|
227
227
|
],
|
|
228
228
|
},
|
|
229
229
|
{
|
|
@@ -231,7 +231,7 @@ const REQUIRED_UNIVERSAL_SOP_SNIPPETS = [
|
|
|
231
231
|
snippets: [
|
|
232
232
|
'Universal SOP hard block policy:',
|
|
233
233
|
'Hard block: do not write application code until docs/project-brief.md and docs/architecture-decision-record.md exist.',
|
|
234
|
-
'For UI scope: if docs/DESIGN.md is missing, execute bootstrap-design prompt before implementing UI surfaces.',
|
|
234
|
+
'For UI scope: if docs/DESIGN.md or docs/design-intent.json is missing, execute bootstrap-design prompt before implementing UI surfaces.',
|
|
235
235
|
],
|
|
236
236
|
},
|
|
237
237
|
];
|
|
@@ -250,10 +250,22 @@ const REQUIRED_TEMPLATE_FREE_BOOTSTRAP_SNIPPETS = [
|
|
|
250
250
|
snippets: [
|
|
251
251
|
'Project docs will be authored dynamically by your IDE assistant from these prompts.',
|
|
252
252
|
'bootstrap-project-context.md',
|
|
253
|
+
'Seed docs:',
|
|
253
254
|
'I prepared dynamic synthesis bootstrap prompts',
|
|
254
255
|
],
|
|
255
256
|
},
|
|
256
257
|
];
|
|
258
|
+
const REQUIRED_UPGRADE_UI_CONTRACT_WARNING_SNIPPETS = [
|
|
259
|
+
{
|
|
260
|
+
path: 'lib/cli/commands/upgrade.mjs',
|
|
261
|
+
snippets: [
|
|
262
|
+
'UI/frontend scope was detected, but the dynamic design contract is incomplete:',
|
|
263
|
+
'docs/design-intent.json',
|
|
264
|
+
'Upgrade synchronizes governance assets, but it does not author project-specific design docs automatically.',
|
|
265
|
+
'collectProjectMarkers',
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
];
|
|
257
269
|
const FORBIDDEN_TEMPLATE_BOOTSTRAP_SNIPPETS = [
|
|
258
270
|
{
|
|
259
271
|
path: 'lib/cli/project-scaffolder.mjs',
|
|
@@ -1021,6 +1033,28 @@ async function validateTemplateFreeBootstrapCoverage() {
|
|
|
1021
1033
|
}
|
|
1022
1034
|
}
|
|
1023
1035
|
|
|
1036
|
+
async function validateUpgradeUiContractWarningCoverage() {
|
|
1037
|
+
console.log('\nChecking upgrade UI contract warning coverage...');
|
|
1038
|
+
|
|
1039
|
+
for (const coverageRule of REQUIRED_UPGRADE_UI_CONTRACT_WARNING_SNIPPETS) {
|
|
1040
|
+
const absoluteCoveragePath = join(ROOT_DIR, coverageRule.path);
|
|
1041
|
+
|
|
1042
|
+
if (!(await fileExists(absoluteCoveragePath))) {
|
|
1043
|
+
fail(`Missing upgrade UI contract warning source: ${coverageRule.path}`);
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const coverageContent = await readTextFile(absoluteCoveragePath);
|
|
1048
|
+
for (const requiredSnippet of coverageRule.snippets) {
|
|
1049
|
+
if (coverageContent.includes(requiredSnippet)) {
|
|
1050
|
+
pass(`${coverageRule.path} includes upgrade UI contract warning snippet: ${requiredSnippet}`);
|
|
1051
|
+
} else {
|
|
1052
|
+
fail(`${coverageRule.path} is missing upgrade UI contract warning snippet: ${requiredSnippet}`);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1024
1058
|
async function validateDeterministicBoundaryEnforcementCoverage() {
|
|
1025
1059
|
console.log('\nChecking deterministic boundary enforcement coverage...');
|
|
1026
1060
|
|
|
@@ -1289,6 +1323,7 @@ async function main() {
|
|
|
1289
1323
|
await validateStackResearchEngineCoverage();
|
|
1290
1324
|
await validateUniversalSopConsolidationCoverage();
|
|
1291
1325
|
await validateTemplateFreeBootstrapCoverage();
|
|
1326
|
+
await validateUpgradeUiContractWarningCoverage();
|
|
1292
1327
|
await validateDeterministicBoundaryEnforcementCoverage();
|
|
1293
1328
|
await validateStackResearchSnapshotState();
|
|
1294
1329
|
await validateMcpConfiguration();
|