@ryuenn3123/agentic-senior-core 3.0.7 → 3.0.8

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-04-20T00:30:26.121Z",
2
+ "generatedAt": "2026-04-20T01:05:14.547Z",
3
3
  "reportName": "memory-continuity-benchmark",
4
4
  "schemaVersion": "1.0.0",
5
5
  "passed": true,
package/.cursorrules CHANGED
@@ -1,6 +1,6 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v3.0.7
3
+ Generated by Agentic-Senior-Core CLI v3.0.8
4
4
  Timestamp: 2026-04-18T00:00:00.000Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
package/.windsurfrules CHANGED
@@ -1,6 +1,6 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v3.0.7
3
+ Generated by Agentic-Senior-Core CLI v3.0.8
4
4
  Timestamp: 2026-04-18T00:00:00.000Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
package/README.md CHANGED
@@ -10,12 +10,12 @@
10
10
  **Production-grade Rules Engine (Governance Engine) for AI coding agents.**
11
11
  Works with Cursor, Windsurf, GitHub Copilot, Claude Code, Gemini, and other LLM-powered IDE workflows.
12
12
 
13
- Latest release: 3.0.7 (2026-04-20).
13
+ Latest release: 3.0.8 (2026-04-20).
14
14
 
15
- Highlights in 3.0.7:
16
- - Dynamic UI design contract bootstrap now ships paired markdown and machine-readable intent.
17
- - Upgrade warns when existing UI repositories are missing required design contract files.
18
- - Init discovery stays slimmer by reusing architecture context and removing redundant wizard branches.
15
+ Highlights in 3.0.8:
16
+ - Existing UI repositories now get `docs/design-intent.json` seeded during both `init` and `upgrade`.
17
+ - Upgrade preview explicitly shows when a machine-readable design seed will be created on apply.
18
+ - UI scope detection is now shared between `init` and `upgrade` so design-contract enforcement stays consistent.
19
19
 
20
20
  </div>
21
21
 
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { createInterface } from 'node:readline/promises';
6
6
  import { stdin, stdout } from 'node:process';
7
+ import fs from 'node:fs/promises';
7
8
  import path from 'node:path';
8
9
 
9
10
  import {
@@ -29,10 +30,16 @@ import {
29
30
  formatBlockingSeverities,
30
31
  formatDuration,
31
32
  copyGovernanceAssetsToTarget,
33
+ pathExists,
32
34
  } from '../utils.mjs';
33
35
 
34
36
  import { collectProfilePacks, findProfilePackByInput } from '../profile-packs.mjs';
35
- import { detectProjectContext, buildDetectionSummary, formatDetectionCandidates } from '../detector.mjs';
37
+ import {
38
+ detectProjectContext,
39
+ buildDetectionSummary,
40
+ formatDetectionCandidates,
41
+ detectUiScopeSignals,
42
+ } from '../detector.mjs';
36
43
  import { compileDynamicContext, writeSelectedPolicy, writeOnboardingReport } from '../compiler.mjs';
37
44
  import {
38
45
  filterBlueprintFileNamesByCandidates,
@@ -55,6 +62,7 @@ import {
55
62
  hasExistingProjectDocs,
56
63
  loadProjectConfig,
57
64
  normalizeDocsLanguage,
65
+ buildDesignIntentSeedFromSignals,
58
66
  } from '../project-scaffolder.mjs';
59
67
  import { performRollback } from '../rollback.mjs';
60
68
  import {
@@ -162,6 +170,41 @@ async function askBlueprintSelection(promptMessage, selectableBlueprintFileNames
162
170
  return selectableBlueprintFileNames[selectedIndex] || selectableBlueprintFileNames[0] || null;
163
171
  }
164
172
 
173
+ function buildInitExistingProjectDesignIntentSeed({
174
+ targetDirectoryPath,
175
+ packageManifest,
176
+ selectedStackFileName,
177
+ selectedBlueprintFileName,
178
+ uiScopeSignals,
179
+ architectureProjectDescription,
180
+ }) {
181
+ const projectName = String(packageManifest?.name || path.basename(targetDirectoryPath)).trim() || 'existing-ui-project';
182
+ const isMobileUiProject = String(selectedStackFileName || '').toLowerCase().includes('react-native')
183
+ || String(selectedStackFileName || '').toLowerCase().includes('flutter')
184
+ || uiScopeSignals.signalReasons.some((signalReason) => signalReason.includes('android') || signalReason.includes('ios'));
185
+ const resolvedDomain = isMobileUiProject ? 'Mobile app' : 'Web application';
186
+ const projectDescription = String(packageManifest?.description || architectureProjectDescription || '').trim()
187
+ || `Existing ${resolvedDomain.toLowerCase()} detected during init. Create a project-specific dynamic design contract before shipping new UI work.`;
188
+
189
+ return buildDesignIntentSeedFromSignals({
190
+ projectName,
191
+ projectDescription,
192
+ primaryDomain: resolvedDomain,
193
+ features: [],
194
+ initContext: {
195
+ stackFileName: selectedStackFileName,
196
+ blueprintFileName: selectedBlueprintFileName,
197
+ },
198
+ status: 'seed-generated-during-init',
199
+ supplementalFields: {
200
+ initSignals: {
201
+ detectedFrom: uiScopeSignals.signalReasons,
202
+ generatedBy: 'init-existing-project-seed',
203
+ },
204
+ },
205
+ });
206
+ }
207
+
165
208
  export async function runInitCommand(targetDirectoryArgument, initOptions = {}) {
166
209
  const resolvedTargetDirectoryPath = path.resolve(targetDirectoryArgument || '.');
167
210
  const isTokenOptimizationEnabled = typeof initOptions.tokenOptimize === 'boolean'
@@ -580,6 +623,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
580
623
 
581
624
  // --- Project Documentation Scaffolding ---
582
625
  let scaffoldingResult = null;
626
+ const supplementalMaterializedDocFileNames = [];
583
627
  const isFreshProjectTarget = wasDirectoryEffectivelyEmpty && !hadExistingProjectDocsBeforeInit;
584
628
  const shouldOfferScaffolding = initOptions.scaffoldDocs === true
585
629
  || Boolean(initOptions.projectConfig)
@@ -661,6 +705,36 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
661
705
  }
662
706
  }
663
707
 
708
+ const existingProjectUiScopeSignals = projectDetection.hasExistingProjectFiles
709
+ ? await detectUiScopeSignals({
710
+ targetDirectoryPath: resolvedTargetDirectoryPath,
711
+ selectedStackFileName: selectedResolvedStackFileName,
712
+ selectedBlueprintFileName: selectedResolvedBlueprintFileName,
713
+ })
714
+ : null;
715
+ const designIntentTargetPath = path.join(resolvedTargetDirectoryPath, 'docs', 'design-intent.json');
716
+ const shouldSeedExistingUiDesignIntent = projectDetection.hasExistingProjectFiles
717
+ && existingProjectUiScopeSignals?.isUiScopeLikely === true
718
+ && !(await pathExists(designIntentTargetPath));
719
+
720
+ if (shouldSeedExistingUiDesignIntent) {
721
+ const docsDirectoryPath = path.join(resolvedTargetDirectoryPath, 'docs');
722
+ const designIntentSeedContent = buildInitExistingProjectDesignIntentSeed({
723
+ targetDirectoryPath: resolvedTargetDirectoryPath,
724
+ packageManifest: existingProjectUiScopeSignals.packageManifest,
725
+ selectedStackFileName: selectedResolvedStackFileName,
726
+ selectedBlueprintFileName: selectedResolvedBlueprintFileName,
727
+ uiScopeSignals: existingProjectUiScopeSignals,
728
+ architectureProjectDescription,
729
+ });
730
+
731
+ await ensureDirectory(docsDirectoryPath);
732
+ await fs.writeFile(designIntentTargetPath, designIntentSeedContent, 'utf8');
733
+ supplementalMaterializedDocFileNames.push('design-intent.json');
734
+
735
+ console.log('\nExisting UI/frontend scope detected. Seeded docs/design-intent.json so the machine-readable design contract exists before UI implementation work continues.');
736
+ }
737
+
664
738
  await compileDynamicContext({
665
739
  targetDirectoryPath: resolvedTargetDirectoryPath,
666
740
  selectedProfileName,
@@ -765,6 +839,9 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
765
839
  console.log(`- Project docs: ${scaffoldingResult.generatedFileNames.length} files generated in docs/`);
766
840
  console.log(`- Project docs language: ${scaffoldingResult.docsLanguage}`);
767
841
  }
842
+ if (supplementalMaterializedDocFileNames.length > 0) {
843
+ console.log(`- Design seed docs: ${supplementalMaterializedDocFileNames.length} files generated in docs/`);
844
+ }
768
845
  console.log(`- Repository workflows copied: no (workflows remain source-repo assets)`);
769
846
  console.log(`- MCP configuration: ${shouldIncludeMcpTemplate ? 'auto-configured for your IDEs (VS Code, Cursor, Zed, Gemini)' : 'disabled (--no-mcp-template)'}`);
770
847
  if (isMemoryContinuityEnabled) {
@@ -804,6 +881,11 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
804
881
  console.log(`- Build an MVP for ${promptProjectName}. Follow Layer 9 docs and keep the current stack, database, and auth constraints.`);
805
882
  console.log('- Add [new feature] and update docs/project-brief.md plus docs/flow-overview.md in the same change.');
806
883
  console.log('- If this change needs architecture migration, propose a migration plan first, then implement after approval.');
884
+ } else if (supplementalMaterializedDocFileNames.includes('design-intent.json')) {
885
+ console.log('I also seeded docs/design-intent.json for this existing UI repository so future UI work starts from a machine-readable design contract without forcing a canned visual concept.');
886
+ console.log('\nPrompt starter examples (copy and adapt in your IDE):');
887
+ console.log('- If docs/DESIGN.md is missing, execute .agent-context/prompts/bootstrap-design.md now and refine docs/design-intent.json into a complete design contract before building UI components.');
888
+ console.log('- Keep docs/design-intent.json and docs/DESIGN.md synchronized whenever the UI direction changes.');
807
889
  }
808
890
  console.log('Your AI tools will now receive one compiled rulebook plus the original source rules, and your review threshold is stored in .agent-context/policies/llm-judge-threshold.json.');
809
891
  console.log('MCP server registration is manual inside your IDE settings, even when mcp.json exists.');
@@ -29,7 +29,7 @@ import {
29
29
  detectProjectContext,
30
30
  buildDetectionSummary,
31
31
  formatDetectionCandidates,
32
- collectProjectMarkers,
32
+ detectUiScopeSignals,
33
33
  } from '../detector.mjs';
34
34
  import {
35
35
  buildCompiledRulesContent,
@@ -41,7 +41,10 @@ import {
41
41
  import { runPreflightChecks } from '../preflight.mjs';
42
42
  import { createBackup } from '../backup.mjs';
43
43
  import { performRollback } from '../rollback.mjs';
44
- import { detectProjectDocTemplateStaleness } from '../project-scaffolder.mjs';
44
+ import {
45
+ detectProjectDocTemplateStaleness,
46
+ buildDesignIntentSeedFromSignals,
47
+ } from '../project-scaffolder.mjs';
45
48
 
46
49
  export function parseUpgradeArguments(commandArguments) {
47
50
  const parsedUpgradeOptions = {
@@ -104,100 +107,38 @@ function buildExistingProjectMajorConstraints() {
104
107
  ];
105
108
  }
106
109
 
107
- async function readPackageJsonIfExists(targetDirectoryPath) {
108
- const packageJsonPath = path.join(targetDirectoryPath, 'package.json');
109
- if (!(await pathExists(packageJsonPath))) {
110
- return null;
111
- }
112
-
113
- try {
114
- return JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
115
- } catch {
116
- return null;
117
- }
118
- }
119
-
120
- async function detectUiScopeSignals({
110
+ function buildUpgradeDesignIntentSeed({
121
111
  targetDirectoryPath,
122
- existingOnboardingReport,
112
+ packageManifest,
123
113
  selectedStackFileName,
124
114
  selectedBlueprintFileName,
115
+ uiScopeSignals,
125
116
  }) {
126
- const signalReasons = [];
127
- const markerNames = await collectProjectMarkers(targetDirectoryPath);
128
- const packageManifest = await readPackageJsonIfExists(targetDirectoryPath);
129
-
130
- const persistedProjectScopeKey = String(existingOnboardingReport?.projectScope?.key || '').trim().toLowerCase();
131
- if (persistedProjectScopeKey === 'frontend-only' || persistedProjectScopeKey === 'both') {
132
- signalReasons.push(`onboarding project scope: ${persistedProjectScopeKey}`);
133
- }
134
-
135
- const selectedStackKey = String(selectedStackFileName || '').trim().toLowerCase();
136
- if (selectedStackKey === 'react-native.md' || selectedStackKey === 'flutter.md') {
137
- signalReasons.push(`selected stack implies UI runtime: ${selectedStackKey}`);
138
- }
139
-
140
- const selectedBlueprintKey = String(selectedBlueprintFileName || '').trim().toLowerCase();
141
- if (selectedBlueprintKey.includes('frontend') || selectedBlueprintKey.includes('landing') || selectedBlueprintKey.includes('mobile-app')) {
142
- signalReasons.push(`selected blueprint implies UI scope: ${selectedBlueprintKey}`);
143
- }
144
-
145
- const directUiMarkerNames = [
146
- 'next.config.js',
147
- 'next.config.mjs',
148
- 'next.config.ts',
149
- 'tailwind.config.js',
150
- 'tailwind.config.mjs',
151
- 'tailwind.config.ts',
152
- 'vite.config.js',
153
- 'vite.config.mjs',
154
- 'vite.config.ts',
155
- 'react-native.config.js',
156
- 'app',
157
- 'pages',
158
- 'components',
159
- 'public',
160
- 'styles',
161
- 'android',
162
- 'ios',
163
- 'index.html',
164
- ];
165
-
166
- const detectedUiMarkers = directUiMarkerNames.filter((markerName) => markerNames.has(markerName));
167
- if (detectedUiMarkers.length > 0) {
168
- signalReasons.push(`ui markers: ${detectedUiMarkers.join(', ')}`);
169
- }
170
-
171
- const dependencyMap = {
172
- next: 'next',
173
- react: 'react',
174
- reactDom: 'react-dom',
175
- reactNative: 'react-native',
176
- expo: 'expo',
177
- tailwindcss: 'tailwindcss',
178
- };
179
- const dependencySource = {
180
- ...(packageManifest?.dependencies || {}),
181
- ...(packageManifest?.devDependencies || {}),
182
- };
183
- const detectedUiDependencies = Object.values(dependencyMap).filter((dependencyName) => dependencySource[dependencyName]);
184
- if (detectedUiDependencies.length > 0) {
185
- signalReasons.push(`ui dependencies: ${detectedUiDependencies.join(', ')}`);
186
- }
187
-
188
- const hasStrongUiMarker = detectedUiMarkers.some((markerName) => (
189
- markerName.startsWith('next.config')
190
- || markerName === 'react-native.config.js'
191
- || markerName === 'android'
192
- || markerName === 'ios'
193
- ));
194
- const hasUiDependencies = detectedUiDependencies.length > 0;
195
- const hasStructuralUiMarkers = detectedUiMarkers.length >= 2;
196
-
197
- return {
198
- isUiScopeLikely: signalReasons.length > 0 && (hasStrongUiMarker || hasUiDependencies || hasStructuralUiMarkers || persistedProjectScopeKey.length > 0),
199
- signalReasons,
200
- };
117
+ const projectName = String(packageManifest?.name || path.basename(targetDirectoryPath)).trim() || 'existing-ui-project';
118
+ const isMobileUiProject = String(selectedStackFileName || '').toLowerCase().includes('react-native')
119
+ || String(selectedStackFileName || '').toLowerCase().includes('flutter')
120
+ || uiScopeSignals.signalReasons.some((signalReason) => signalReason.includes('android') || signalReason.includes('ios'));
121
+ const resolvedDomain = isMobileUiProject ? 'Mobile app' : 'Web application';
122
+ const projectDescription = String(packageManifest?.description || '').trim()
123
+ || `Existing ${resolvedDomain.toLowerCase()} detected during upgrade. Create a project-specific dynamic design contract before shipping new UI work.`;
124
+
125
+ return buildDesignIntentSeedFromSignals({
126
+ projectName,
127
+ projectDescription,
128
+ primaryDomain: resolvedDomain,
129
+ features: [],
130
+ initContext: {
131
+ stackFileName: selectedStackFileName,
132
+ blueprintFileName: selectedBlueprintFileName,
133
+ },
134
+ status: 'seed-generated-during-upgrade',
135
+ supplementalFields: {
136
+ upgradeSignals: {
137
+ detectedFrom: uiScopeSignals.signalReasons,
138
+ generatedBy: 'upgrade-seed',
139
+ },
140
+ },
141
+ });
201
142
  }
202
143
 
203
144
  export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions = {}) {
@@ -232,7 +173,6 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
232
173
  const blueprintFileNames = await collectFileNames(path.join(AGENT_CONTEXT_DIR, 'blueprints'));
233
174
  const existingOnboardingReport = await loadOnboardingReportIfExists(resolvedTargetDirectoryPath);
234
175
  const projectDetection = await detectProjectContext(resolvedTargetDirectoryPath);
235
-
236
176
  const selectedProfileName = PROFILE_PRESETS[existingOnboardingReport?.selectedProfile]
237
177
  ? existingOnboardingReport.selectedProfile
238
178
  : 'balanced';
@@ -266,10 +206,12 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
266
206
  : true;
267
207
  const uiScopeSignals = await detectUiScopeSignals({
268
208
  targetDirectoryPath: resolvedTargetDirectoryPath,
269
- existingOnboardingReport,
270
209
  selectedStackFileName,
271
210
  selectedBlueprintFileName,
211
+ projectScopeKey: existingOnboardingReport?.projectScope?.key || null,
212
+ projectScopeSourceLabel: 'onboarding project scope',
272
213
  });
214
+ const packageManifest = uiScopeSignals.packageManifest;
273
215
  const designContractPaths = ['docs/DESIGN.md', 'docs/design-intent.json'];
274
216
  const missingDesignContractPaths = [];
275
217
 
@@ -279,6 +221,17 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
279
221
  missingDesignContractPaths.push(designContractPath);
280
222
  }
281
223
  }
224
+ const shouldSeedDesignIntentOnApply = uiScopeSignals.isUiScopeLikely
225
+ && missingDesignContractPaths.includes('docs/design-intent.json');
226
+ const designIntentSeedContent = shouldSeedDesignIntentOnApply
227
+ ? buildUpgradeDesignIntentSeed({
228
+ targetDirectoryPath: resolvedTargetDirectoryPath,
229
+ packageManifest,
230
+ selectedStackFileName,
231
+ selectedBlueprintFileName,
232
+ uiScopeSignals,
233
+ })
234
+ : null;
282
235
 
283
236
  const detectionMajorConstraints = buildExistingProjectMajorConstraints();
284
237
  const detectionTransparency = {
@@ -408,8 +361,11 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
408
361
  if (uiScopeSignals.signalReasons.length > 0) {
409
362
  console.log(`- Detection signals: ${uiScopeSignals.signalReasons.join('; ')}`);
410
363
  }
364
+ if (shouldSeedDesignIntentOnApply) {
365
+ console.log('- Planned seed on apply: docs/design-intent.json');
366
+ }
411
367
  console.log('Recommendation: create or refresh docs/DESIGN.md and docs/design-intent.json before allowing UI implementation work.');
412
- console.log('Upgrade synchronizes governance assets, but it does not author project-specific design docs automatically.');
368
+ console.log('Upgrade synchronizes governance assets and can seed docs/design-intent.json, but it does not author project-specific docs/DESIGN.md automatically.');
413
369
  }
414
370
 
415
371
  if (upgradeOptions.dryRun) {
@@ -434,6 +390,15 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
434
390
  pruneManagedSurface: upgradeOptions.pruneManagedSurface === true,
435
391
  managedSurfacePlan,
436
392
  });
393
+ const supplementalCreatedFileNames = [];
394
+
395
+ if (shouldSeedDesignIntentOnApply && designIntentSeedContent) {
396
+ const docsDirectoryPath = path.join(resolvedTargetDirectoryPath, 'docs');
397
+ const designIntentTargetPath = path.join(docsDirectoryPath, 'design-intent.json');
398
+ await ensureDirectory(docsDirectoryPath);
399
+ await fs.writeFile(designIntentTargetPath, designIntentSeedContent, 'utf8');
400
+ supplementalCreatedFileNames.push('docs/design-intent.json');
401
+ }
437
402
 
438
403
  await fs.writeFile(currentRulesPath, plannedRulesContent, 'utf8');
439
404
  await fs.writeFile(path.join(resolvedTargetDirectoryPath, '.windsurfrules'), plannedRulesContent, 'utf8');
@@ -471,6 +436,12 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
471
436
  governanceSyncResult.updatedFiles.forEach((fileName) => console.log(` [UPDATED] ${fileName}`));
472
437
  governanceSyncResult.deletedManagedFiles.forEach((fileName) => console.log(` [DELETED] ${fileName}`));
473
438
  }
439
+ if (supplementalCreatedFileNames.length > 0) {
440
+ if (!(governanceSyncResult.updatedFiles.length > 0 || governanceSyncResult.createdFiles.length > 0 || governanceSyncResult.deletedManagedFiles.length > 0)) {
441
+ console.log('\nDetailed changes:');
442
+ }
443
+ supplementalCreatedFileNames.forEach((fileName) => console.log(` [NEW] ${fileName} (seed)`));
444
+ }
474
445
 
475
446
  console.log('\nRefreshed files: .cursorrules, .windsurfrules, .agent-context/state/onboarding-report.json');
476
447
  } catch (error) {
@@ -3,6 +3,7 @@
3
3
  * Depends on: constants.mjs, utils.mjs
4
4
  */
5
5
  import fs from 'node:fs/promises';
6
+ import path from 'node:path';
6
7
 
7
8
  import { BLUEPRINT_RECOMMENDATIONS } from './constants.mjs';
8
9
  import { toTitleCase } from './utils.mjs';
@@ -22,6 +23,106 @@ export async function collectProjectMarkers(targetDirectoryPath) {
22
23
  return markerNames;
23
24
  }
24
25
 
26
+ async function readPackageJsonIfExists(targetDirectoryPath) {
27
+ const packageJsonPath = path.join(targetDirectoryPath, 'package.json');
28
+
29
+ try {
30
+ const packageJsonContent = await fs.readFile(packageJsonPath, 'utf8');
31
+ return JSON.parse(packageJsonContent);
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ export async function detectUiScopeSignals({
38
+ targetDirectoryPath,
39
+ selectedStackFileName,
40
+ selectedBlueprintFileName,
41
+ packageManifest = null,
42
+ projectScopeKey = null,
43
+ projectScopeSourceLabel = 'project scope',
44
+ }) {
45
+ const signalReasons = [];
46
+ const markerNames = await collectProjectMarkers(targetDirectoryPath);
47
+ const resolvedPackageManifest = packageManifest || await readPackageJsonIfExists(targetDirectoryPath);
48
+
49
+ const normalizedProjectScopeKey = String(projectScopeKey || '').trim().toLowerCase();
50
+ if (normalizedProjectScopeKey === 'frontend-only' || normalizedProjectScopeKey === 'both') {
51
+ signalReasons.push(`${projectScopeSourceLabel}: ${normalizedProjectScopeKey}`);
52
+ }
53
+
54
+ const selectedStackKey = String(selectedStackFileName || '').trim().toLowerCase();
55
+ if (selectedStackKey === 'react-native.md' || selectedStackKey === 'flutter.md') {
56
+ signalReasons.push(`selected stack implies UI runtime: ${selectedStackKey}`);
57
+ }
58
+
59
+ const selectedBlueprintKey = String(selectedBlueprintFileName || '').trim().toLowerCase();
60
+ if (selectedBlueprintKey.includes('frontend') || selectedBlueprintKey.includes('landing') || selectedBlueprintKey.includes('mobile-app')) {
61
+ signalReasons.push(`selected blueprint implies UI scope: ${selectedBlueprintKey}`);
62
+ }
63
+
64
+ const directUiMarkerNames = [
65
+ 'next.config.js',
66
+ 'next.config.mjs',
67
+ 'next.config.ts',
68
+ 'tailwind.config.js',
69
+ 'tailwind.config.mjs',
70
+ 'tailwind.config.ts',
71
+ 'vite.config.js',
72
+ 'vite.config.mjs',
73
+ 'vite.config.ts',
74
+ 'react-native.config.js',
75
+ 'app',
76
+ 'pages',
77
+ 'components',
78
+ 'public',
79
+ 'styles',
80
+ 'android',
81
+ 'ios',
82
+ 'index.html',
83
+ ];
84
+
85
+ const detectedUiMarkers = directUiMarkerNames.filter((markerName) => markerNames.has(markerName));
86
+ if (detectedUiMarkers.length > 0) {
87
+ signalReasons.push(`ui markers: ${detectedUiMarkers.join(', ')}`);
88
+ }
89
+
90
+ const dependencySource = {
91
+ ...(resolvedPackageManifest?.dependencies || {}),
92
+ ...(resolvedPackageManifest?.devDependencies || {}),
93
+ };
94
+ const detectableUiDependencies = [
95
+ 'next',
96
+ 'react',
97
+ 'react-dom',
98
+ 'react-native',
99
+ 'expo',
100
+ 'tailwindcss',
101
+ ];
102
+ const detectedUiDependencies = detectableUiDependencies.filter((dependencyName) => dependencySource[dependencyName]);
103
+ if (detectedUiDependencies.length > 0) {
104
+ signalReasons.push(`ui dependencies: ${detectedUiDependencies.join(', ')}`);
105
+ }
106
+
107
+ const hasStrongUiMarker = detectedUiMarkers.some((markerName) => (
108
+ markerName.startsWith('next.config')
109
+ || markerName === 'react-native.config.js'
110
+ || markerName === 'android'
111
+ || markerName === 'ios'
112
+ ));
113
+ const hasUiDependencies = detectedUiDependencies.length > 0;
114
+ const hasStructuralUiMarkers = detectedUiMarkers.length >= 2;
115
+
116
+ return {
117
+ isUiScopeLikely: signalReasons.length > 0
118
+ && (hasStrongUiMarker || hasUiDependencies || hasStructuralUiMarkers || normalizedProjectScopeKey.length > 0),
119
+ signalReasons,
120
+ detectedUiMarkers,
121
+ detectedUiDependencies,
122
+ packageManifest: resolvedPackageManifest,
123
+ };
124
+ }
125
+
25
126
  export async function detectProjectContext(targetDirectoryPath) {
26
127
  const markerNames = await collectProjectMarkers(targetDirectoryPath);
27
128
  const detectionCandidates = [];
@@ -373,21 +373,30 @@ function inferDesignKeywords(discoveryAnswers) {
373
373
  };
374
374
  }
375
375
 
376
- function buildDesignIntentSeed({
377
- discoveryAnswers,
376
+ export function buildDesignIntentSeedFromSignals({
377
+ projectName,
378
+ projectDescription,
379
+ primaryDomain,
380
+ features = [],
378
381
  initContext,
379
- architectureRecommendation,
382
+ architectureRecommendation = null,
383
+ status = 'seed-needs-design-synthesis',
384
+ supplementalFields = {},
380
385
  }) {
381
- const inferredKeywords = inferDesignKeywords(discoveryAnswers);
386
+ const inferredKeywords = inferDesignKeywords({
387
+ projectDescription,
388
+ primaryDomain,
389
+ features,
390
+ });
382
391
  const designSignals = architectureRecommendation?.designGuidance?.normalizedSignals || null;
383
392
 
384
393
  return `${JSON.stringify({
385
394
  mode: 'dynamic',
386
- status: 'seed-needs-design-synthesis',
395
+ status,
387
396
  project: {
388
- name: discoveryAnswers.projectName,
389
- context: discoveryAnswers.projectDescription,
390
- domain: discoveryAnswers.primaryDomain,
397
+ name: projectName,
398
+ context: projectDescription,
399
+ domain: primaryDomain,
391
400
  stack: toTitleCase(initContext.stackFileName),
392
401
  blueprint: toTitleCase(initContext.blueprintFileName),
393
402
  },
@@ -431,9 +440,26 @@ function buildDesignIntentSeed({
431
440
  bootstrapPrompt: '.agent-context/prompts/bootstrap-design.md',
432
441
  },
433
442
  architectSignals: designSignals,
443
+ ...supplementalFields,
434
444
  }, null, 2)}\n`;
435
445
  }
436
446
 
447
+ function buildDesignIntentSeed({
448
+ discoveryAnswers,
449
+ initContext,
450
+ architectureRecommendation,
451
+ }) {
452
+ return buildDesignIntentSeedFromSignals({
453
+ projectName: discoveryAnswers.projectName,
454
+ projectDescription: discoveryAnswers.projectDescription,
455
+ primaryDomain: discoveryAnswers.primaryDomain,
456
+ features: discoveryAnswers.features,
457
+ initContext,
458
+ architectureRecommendation,
459
+ status: 'seed-needs-design-synthesis',
460
+ });
461
+ }
462
+
437
463
  function buildProjectContextBootstrapPrompt({
438
464
  discoveryAnswers,
439
465
  initContext,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuenn3123/agentic-senior-core",
3
- "version": "3.0.7",
3
+ "version": "3.0.8",
4
4
  "type": "module",
5
5
  "description": "Force your AI Agent to code like a Staff Engineer, not a Junior.",
6
6
  "bin": {
@@ -261,8 +261,10 @@ const REQUIRED_UPGRADE_UI_CONTRACT_WARNING_SNIPPETS = [
261
261
  snippets: [
262
262
  'UI/frontend scope was detected, but the dynamic design contract is incomplete:',
263
263
  'docs/design-intent.json',
264
- 'Upgrade synchronizes governance assets, but it does not author project-specific design docs automatically.',
265
- 'collectProjectMarkers',
264
+ 'Planned seed on apply: docs/design-intent.json',
265
+ 'Upgrade synchronizes governance assets and can seed docs/design-intent.json, but it does not author project-specific docs/DESIGN.md automatically.',
266
+ 'detectUiScopeSignals',
267
+ 'seed-generated-during-upgrade',
266
268
  ],
267
269
  },
268
270
  ];