@ryuenn3123/agentic-senior-core 3.0.37 → 3.0.38

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 (58) hide show
  1. package/.agent-context/prompts/bootstrap-design.md +108 -146
  2. package/.agent-context/rules/frontend-architecture.md +92 -108
  3. package/.agent-context/state/README.md +26 -0
  4. package/.cursor/mcp.json +10 -0
  5. package/.cursor/rules/agentic-senior-core.mdc +48 -0
  6. package/.cursorrules +22 -88
  7. package/.gemini/instructions.md +25 -16
  8. package/.github/copilot-instructions.md +25 -16
  9. package/.github/instructions/agentic-senior-core.instructions.md +47 -0
  10. package/.instructions.md +98 -207
  11. package/.windsurf/rules/agentic-senior-core.md +43 -0
  12. package/.windsurfrules +22 -88
  13. package/AGENTS.md +23 -26
  14. package/CLAUDE.md +43 -0
  15. package/CONTRIBUTING.md +5 -2
  16. package/GEMINI.md +43 -0
  17. package/README.md +24 -7
  18. package/lib/cli/backup.mjs +4 -4
  19. package/lib/cli/commands/init/project-context.mjs +101 -0
  20. package/lib/cli/commands/init/runtime-environment.mjs +59 -0
  21. package/lib/cli/commands/init/setup-decisions.mjs +83 -0
  22. package/lib/cli/commands/init.mjs +33 -250
  23. package/lib/cli/commands/optimize.mjs +1 -1
  24. package/lib/cli/commands/upgrade.mjs +32 -7
  25. package/lib/cli/compiler.mjs +59 -17
  26. package/lib/cli/constants.mjs +5 -0
  27. package/lib/cli/detector.mjs +4 -0
  28. package/lib/cli/preflight.mjs +3 -3
  29. package/lib/cli/project-scaffolder/design-contract/validation.mjs +789 -0
  30. package/lib/cli/project-scaffolder/design-contract.mjs +119 -924
  31. package/lib/cli/project-scaffolder/prompt-builders.mjs +69 -84
  32. package/lib/cli/utils/filesystem.mjs +79 -0
  33. package/lib/cli/utils/managed-surface.mjs +237 -0
  34. package/lib/cli/utils/prompting.mjs +44 -0
  35. package/lib/cli/utils.mjs +33 -335
  36. package/package.json +21 -2
  37. package/scripts/clean-local-artifacts.mjs +76 -0
  38. package/scripts/docs-quality-drift-report.mjs +5 -0
  39. package/scripts/frontend-usability-audit.mjs +23 -19
  40. package/scripts/governance-weekly-report.mjs +37 -15
  41. package/scripts/single-source-lazy-loading-audit.mjs +24 -0
  42. package/scripts/sync-thin-adapters.mjs +99 -129
  43. package/scripts/v3-purge-audit.mjs +5 -0
  44. package/scripts/validate/config.mjs +10 -0
  45. package/scripts/validate/coverage-checks.mjs +55 -0
  46. package/.agent-context/marketplace/trust-tiers.json +0 -114
  47. package/.agent-context/state/benchmark-analysis.json +0 -431
  48. package/.agent-context/state/benchmark-evidence-bundle.json +0 -1040
  49. package/.agent-context/state/benchmark-history.json +0 -75
  50. package/.agent-context/state/benchmark-trend-report.csv +0 -5
  51. package/.agent-context/state/benchmark-trend-report.json +0 -140
  52. package/.agent-context/state/benchmark-writer-judge-matrix.json +0 -462
  53. package/.agent-context/state/memory-continuity-benchmark.json +0 -132
  54. package/.agent-context/state/onboarding-report.json +0 -102
  55. package/.agent-context/state/quality-trend-report.json +0 -89
  56. package/.agent-context/state/token-optimization-benchmark.json +0 -130
  57. package/.agent-context/state/weekly-governance-report.json +0 -329
  58. package/lib/cli/compatibility.mjs +0 -124
@@ -92,6 +92,7 @@ export function buildDesignBootstrapPrompt({
92
92
  '',
93
93
  'You are the Lead UI/UX Art Director for this project.',
94
94
  'Create a dynamic design contract, not a fixed stylistic template.',
95
+ 'Use imperative, compact, mechanical rules.',
95
96
  '',
96
97
  '## Mission',
97
98
  `Author docs/DESIGN.md in ${docsLanguage.toUpperCase()} language with strong art direction and engineering-ready guidance.`,
@@ -102,104 +103,88 @@ export function buildDesignBootstrapPrompt({
102
103
  '2. docs/design-intent.json',
103
104
  '',
104
105
  '## Required DESIGN.md Sections',
105
- '1. Design Vision and Product Personality',
106
+ '1. Design Intent and Product Personality',
106
107
  '2. Audience and Use-Context Signals',
107
108
  '3. Visual Direction and Distinctive Moves',
108
- '4. Color Science and Semantic Roles',
109
- '5. Typographic Engineering and Hierarchy',
110
- '6. Spacing, Layout Rhythm, and Density Strategy',
111
- '7. Token Architecture and Alias Strategy',
112
- '8. Responsive Strategy and Cross-Viewport Adaptation Matrix',
113
- '9. Motion and Interaction Principles',
114
- '10. Component Language and Morphology (cards, forms, nav, states)',
115
- '11. Context Hygiene and Source Boundaries',
116
- '12. Accessibility Non-Negotiables',
117
- '13. Anti-Patterns to Avoid',
118
- '14. Implementation Notes for Future UI Tasks',
109
+ '4. Color, Typography, Spacing, and Density Decisions',
110
+ '5. Token Architecture and Alias Strategy',
111
+ '6. Responsive Strategy and Cross-Viewport Adaptation Matrix',
112
+ '7. Motion and Interaction Rules',
113
+ '8. Component Language and Morphology',
114
+ '9. Context Hygiene and Source Boundaries',
115
+ '10. Accessibility Non-Negotiables',
116
+ '11. Anti-Patterns to Avoid',
117
+ '12. Implementation Notes for Future UI Tasks',
119
118
  '',
120
119
  '## Required design-intent.json Fields',
121
120
  '1. mode',
122
121
  '2. status',
123
122
  '3. project',
124
123
  '4. designPhilosophy',
125
- '5. brandAdjectives',
126
- '6. antiAdjectives',
127
- '7. visualDirection',
124
+ '5. visualDirection',
125
+ '6. externalResearchIntake',
126
+ '7. conceptualAnchor',
128
127
  '8. derivedTokenLogic',
129
- '9. aiSafeUiAudit',
130
- '10. libraryResearchStatus',
131
- '11. libraryDecisions',
132
- '12. mathSystems',
133
- '13. tokenSystem',
134
- '14. colorTruth',
135
- '15. crossViewportAdaptation',
136
- '16. motionSystem',
137
- '17. componentMorphology',
138
- '18. accessibilityPolicy',
139
- '19. designExecutionPolicy',
140
- '20. designExecutionHandoff',
141
- '21. reviewRubric',
142
- '22. contextHygiene',
143
- '23. experiencePrinciples',
128
+ '9. motionPaletteDecision',
129
+ '10. aiSafeUiAudit',
130
+ '11. libraryResearchStatus',
131
+ '12. libraryDecisions',
132
+ '13. mathSystems',
133
+ '14. tokenSystem',
134
+ '15. colorTruth',
135
+ '16. crossViewportAdaptation',
136
+ '17. motionSystem',
137
+ '18. componentMorphology',
138
+ '19. accessibilityPolicy',
139
+ '20. designExecutionPolicy',
140
+ '21. designExecutionHandoff',
141
+ '22. reviewRubric',
142
+ '23. contextHygiene',
144
143
  '24. forbiddenPatterns',
145
- '25. validationHints',
146
- '26. requiredDesignSections',
147
- '27. implementation',
148
- '28. repoEvidence when onboarding or detector evidence exists',
149
- '',
150
- '## Hard Rules',
151
- '1. No copy-paste from external style guides.',
152
- '2. Every major decision must include psychological/product rationale.',
153
- '3. Do not choose the final style, framework, component library, animation library, palette, typography, or layout paradigm from this offline scaffold. If unresolved, require an agent recommendation from repo evidence and live official docs before coding.',
154
- '4. Keep tone decisive like an art director, not generic AI boilerplate.',
155
- '5. Do not anchor the final design language to famous products, benchmark visuals, or external reference surfaces.',
156
- '6. Reject interchangeable hero layouts, generic SaaS gradients, and trend-chasing decoration unless the project context explicitly justifies them.',
157
- '7. Encode color intent in perceptual terms first. Hex values may exist only as implementation derivatives.',
158
- '8. Token guidance must define primitive, semantic, and component layers plus aliasing rules. Component tokens must consume semantic aliases instead of raw values.',
159
- '9. Responsive guidance must include layout mutation rules for mobile, tablet, and desktop. Shrinking the desktop layout is not enough.',
160
- '10. Do not let repeated surfaces, component-kit defaults, or safe typography become the final direction without explicit product rationale.',
161
- '11. The LLM must choose color, type, spacing, density, morphology, and motion dynamically from the active project context instead of copying this seed.',
162
- '12. Viewport mutation must be operation-based. For each viewport, define the primary mutation operation, required surface actions, and forbidden responsive fallback patterns.',
163
- '13. Default to a rich motion plan for modern UI work. Motion can be bold, cinematic, spatial, scroll-linked, or highly expressive when it improves memorability, hierarchy, feedback, or product confidence. If a motion library is useful, research current official docs and select the latest stable compatible option instead of using an offline default. Solve clarity, accessibility, and runtime risks without falling back to a static interface by habit.',
164
- '14. Define component morphology across interaction states and viewports so cards, forms, nav, and feedback surfaces adapt coherently instead of only resizing.',
165
- '15. Keep UI-only requests context-isolated. Load frontend design rules first and do not eagerly load backend-only or workflow-only rules unless the task explicitly crosses those boundaries.',
166
- '16. Treat WCAG 2.2 AA as the hard accessibility floor. Use APCA only as advisory perceptual tuning, never as a reason to waive a WCAG failure.',
167
- '17. Accessibility planning must explicitly cover focus visibility, focus appearance, target size, accessible authentication, keyboard access, and dynamic status/state access.',
168
- '18. Structured design execution must stay representation-first. Define a surface plan, component graph, content-priority map, viewport mutation plan, and interaction-state matrix before relying on semantic review.',
169
- '19. design-intent.json must carry an explicit structured handoff, not just policy flags. The handoff must make surface plans, component relationships, task flow, and signature moves executable before coding starts.',
170
- '20. Do not depend on screenshot capture, browser automation, or image diff artifacts as the default path. The contract must be strong enough to guide precise UI from repo evidence, component logic, and user intent alone.',
171
- '21. Semantic review must judge contract fidelity, distinctiveness, hierarchy, state behavior, and viewport mutation directly from the contract and changed UI code.',
172
- '22. Treat prior website memory, unrelated project aesthetics, and remembered screenshots as tainted context unless the user explicitly approves continuity.',
173
- '23. Design continuity is opt-in. If no approved continuity exists, synthesize from the current repo evidence, current brief, and current project docs only.',
174
- '24. Make at least one memorable visual bet so the resulting system is recognizable and not template-neutral.',
175
- '24a. Do not ship AI-safe UI: predictable card stacks, rounded template panels, generic abstract logos, decorative grid wallpaper, beige or slate safety palettes, soft glow backgrounds, or first-output composition with only local copy swapped in are review findings.',
176
- '24b. New screens and broad redesigns must expose at least three at-a-glance product-specific signals and pass the rename test: if the UI can be renamed to another product category without changing composition, palette, iconography, and motion language, revise it before coding.',
177
- '24c. Background lines, grids, scanlines, noise, glows, blobs, logos, and geometric decoration must serve a named product function such as alignment, measurement, navigation, crop guidance, timeline reading, status, or motion continuity.',
178
- '24d. Use visually exploratory, product-derived palettes while preserving WCAG contrast and status clarity; readability must not become an excuse for cream, slate, monochrome, or gradient defaults.',
179
- '24e. Complete the AI color audit before coding: palette choices must explain product evidence, semantic role behavior, and at least one color behavior that would not transfer cleanly to another product category.',
180
- '24f. Meet the creative ambition floor before coding: name one authored visual bet, one product-derived palette move, one signature motion/spatial/interaction behavior, and one morphology or composition choice that breaks away from interchangeable card stacks when the product allows it.',
181
- '24g. Treat motion, 3D, canvas, WebGL, scroll choreography, and modern animation libraries as first-class options. If they are omitted, document the product, content-density, device, performance, or accessibility reason and the replacement interaction quality.',
182
- '25. Define a stable review rubric for distinctiveness, contract fidelity, visual consistency, heuristic UX quality, and motion discipline.',
183
- '26. Genericity findings must name the actual drift signal. Do not label something generic without explaining the anti-pattern or rubric dimension.',
184
- '27. Review rubric must support automatic genericity failure when named drift signals dominate a redesign or new UI surface.',
185
- '28. Separate taste from failure. Bold direction is allowed when it still follows the contract, serves the product, and stays accessible.',
186
- '29. If a future user asks for zero-based redesign ("redesign from zero", "redesain dari 0", "ulang dari 0", or "research ulang"), treat existing UI as content and behavior evidence only, not as visual continuity.',
187
- '30. In zero-based redesign mode, the new contract must name the discarded prior visual DNA and materially reset composition, hierarchy, palette/typography, motion or interaction, and responsive information architecture.',
188
- '31. If modern UI, animation, scroll, 3D, canvas, chart, or icon libraries are useful, choose them from current official docs and record source URL, fetched date, reason, risk, and accessibility fallback.',
189
- '32. If the user supplies research files, library lists, screenshots, articles, or benchmark notes, read them as candidate evidence, summarize the useful signals, filter by project fit, and verify technology claims against current official docs before implementation.',
190
- '33. If no user-supplied research or reference is supplied for UI work, activate the Dynamic Avant-Garde Anchor Engine before coding. User-supplied research means current-task evidence from the user; this scaffold, prior UI, and old design docs do not count as research.',
191
- '34. Before broad compliance review, make a creative commitment and record it in the design contract: one specific real-world anchor reference, one signature motion behavior more specific than smooth transitions, and one typographic decision with meaningful role contrast.',
192
- '35. In Dynamic Avant-Garde mode, perform agent-led research when available, then internally consider at least three high-variance conceptual anchors, discard the two safest or most predictable options, output only the chosen anchor, its specific reference point, and rationale, and forbid final anchors named dashboard, portal, cards, admin panel, SaaS shell, web app shell, or minimalist interface.',
193
- '36. Reject anchors that can only be described with generic quality words such as modern, clean, premium, expressive, minimal, or bold. The anchor must name a material, instrument, artifact class, architecture, editorial genre, cinematic behavior, exhibition system, scientific apparatus, or industrial mechanism.',
194
- '37. The chosen anchor must define a stable conceptualAnchor.anchorReference. derivedTokenLogic.anchorReference must match it exactly so validation does not depend on paraphrased prose.',
195
- '38. Fill derivedTokenLogic with color, spacing, typography, and motion derivation sources before UI implementation. If a token cannot be explained from anchorReference, revise the token.',
196
- '39. If live web research is unavailable or fails, do not hallucinate dependency APIs, package names, versions, or imports. Set libraryResearchStatus to pending-verification, record LIBRARY_TO_VERIFY notes, and use native CSS, browser APIs, or already-present project dependencies until verification is possible.',
197
- '40. Every libraryDecisions entry must either include current official-doc verification metadata or a fallbackIfUnavailable. Do not import a new library without verification or an accepted blocker.',
198
- '41. The chosen anchor must drive typography, spacing, density, color behavior, morphology, motion, and responsive composition. Treat expressive motion, spatial transitions, micro-interactions, and modern animation libraries as first-class options; include performance notes and reduced-motion fallbacks instead of suppressing motion to look safe.',
144
+ '25. requiredDesignSections',
145
+ '26. implementation',
146
+ '27. repoEvidence when onboarding or detector evidence exists',
147
+ '',
148
+ '## Mechanical Gates',
149
+ '1. Do not copy external style guides.',
150
+ '2. Do not anchor the final design language to famous products, benchmark visuals, or external reference surfaces.',
151
+ '3. Do not choose final style, library, palette, typography, motion, or layout from this offline scaffold.',
152
+ '4. Use repo evidence, active brief, current docs, and current official docs for technology choices.',
153
+ '5. Keep UI-only requests context-isolated; load frontend rules first.',
154
+ '6. Preserve Design continuity is opt-in. Use current repo evidence unless the user approves continuity.',
155
+ '7. Treat prior website memory, unrelated project aesthetics, remembered screenshots, and old design docs as tainted unless approved.',
156
+ '8. Use primitive, semantic, and component tokens. Component tokens must consume semantic aliases.',
157
+ '9. Encode colorTruth in perceptual terms. Hex values are implementation derivatives.',
158
+ '10. Define viewport mutation rules for mobile, tablet, and desktop. Shrinking desktop is failure.',
159
+ '11. Keep structured execution representation-first: surface plan, component graph, content-priority map, viewport mutation plan, and interaction-state matrix.',
160
+ '12. Make design-intent.json carry designExecutionPolicy and designExecutionHandoff, not prose-only policy.',
161
+ '13. Keep semantic review focused on contract fidelity, distinctiveness, hierarchy, state behavior, and viewport mutation.',
162
+ '14. Treat WCAG 2.2 AA as the hard floor. Use APCA only as advisory tuning.',
163
+ '15. Cover focus visibility, focus appearance, target size, accessible authentication, keyboard access, and dynamic status/state access.',
164
+ '16. Do not depend on screenshot capture, browser automation, or image diffs as the default quality path.',
165
+ '17. Make one memorable visual bet before code.',
166
+ '18. Do not ship AI-safe UI, decorative grid wallpaper, generic SaaS gradients, or default component-kit styling without product rationale.',
167
+ '19. Use visually exploratory, product-derived palettes while preserving WCAG contrast and status clarity.',
168
+ '20. Complete the AI color audit before code.',
169
+ '21. Treat motion, 3D, canvas, WebGL, scroll choreography, and modern animation libraries as first-class options.',
170
+ '22. If motion or spatial UI is omitted, document the product-fit reason and replacement interaction quality.',
171
+ '23. If the user supplies research files, library lists, screenshots, articles, or benchmark notes, read them as candidate evidence and verify technology claims.',
172
+ '24. If no user-supplied research or reference exists, activate the Dynamic Avant-Garde Anchor Engine before coding; old design docs do not count as research.',
173
+ '25. In Dynamic Avant-Garde mode, consider three high-variance anchors, discard the two safest or most predictable options, and output only the chosen anchor.',
174
+ '26. Reject final anchors named dashboard, portal, cards, admin panel, SaaS shell, web app shell, or minimalist interface.',
175
+ '27. Reject anchors described only as modern, clean, premium, expressive, minimal, or bold.',
176
+ '28. Set conceptualAnchor.anchorReference and make derivedTokenLogic.anchorReference match exactly.',
177
+ '29. Fill derivedTokenLogic before code. If a token cannot trace to anchorReference, revise it.',
178
+ '30. Research current official docs before importing any new UI-related library.',
179
+ '31. If research is unavailable, set libraryResearchStatus to pending-verification and use native CSS, browser APIs, or existing dependencies.',
180
+ '32. Define reviewRubric and require genericity findings to name the actual drift signal.',
181
+ '33. Separate taste from failure. Bold accessible work is valid.',
182
+ '34. For zero-based redesign, create visualResetStrategy and reset composition, hierarchy, palette/typography, motion or interaction, and responsive information architecture.',
199
183
  '',
200
184
  '## Creative Ambition Floor',
201
185
  'Before implementation, the design contract must name one authored visual bet, one product-derived palette move, one signature motion/spatial/interaction behavior, and one morphology or composition choice that would not appear in a generic AI template.',
202
186
  'The ambition floor is not a fixed aesthetic. Quiet, dense, utilitarian, or text-heavy interfaces are allowed when the product requires them, but they still need a project-specific visual decision and a real reason for omitting richer motion, 3D, canvas, WebGL, scroll choreography, or animation libraries.',
187
+ 'Use reduced-motion fallbacks instead of suppressing motion.',
203
188
  '',
204
189
  '## Token Derivation Audit',
205
190
  'Before implementation, docs/design-intent.json must include derivedTokenLogic.anchorReference plus colorDerivationSource, spacingDerivationSource, typographyDerivationSource, motionDerivationSource, and validationRule.',
@@ -0,0 +1,79 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ export async function pathExists(targetPath) {
5
+ try {
6
+ await fs.stat(targetPath);
7
+ return true;
8
+ } catch {
9
+ return false;
10
+ }
11
+ }
12
+
13
+ export async function ensureDirectory(directoryPath) {
14
+ await fs.mkdir(directoryPath, { recursive: true });
15
+ }
16
+
17
+ export async function copyDirectory(sourceDirectoryPath, targetDirectoryPath) {
18
+ if (path.resolve(sourceDirectoryPath) === path.resolve(targetDirectoryPath)) {
19
+ return;
20
+ }
21
+
22
+ await ensureDirectory(targetDirectoryPath);
23
+ const directoryEntries = await fs.readdir(sourceDirectoryPath, { withFileTypes: true });
24
+
25
+ for (const directoryEntry of directoryEntries) {
26
+ const sourceEntryPath = path.join(sourceDirectoryPath, directoryEntry.name);
27
+ const targetEntryPath = path.join(targetDirectoryPath, directoryEntry.name);
28
+
29
+ if (directoryEntry.isDirectory()) {
30
+ await copyDirectory(sourceEntryPath, targetEntryPath);
31
+ continue;
32
+ }
33
+
34
+ if (path.resolve(sourceEntryPath) === path.resolve(targetEntryPath)) {
35
+ continue;
36
+ }
37
+
38
+ await fs.copyFile(sourceEntryPath, targetEntryPath);
39
+ }
40
+ }
41
+
42
+ export function isAgenticManagedContent(content) {
43
+ const normalizedContent = Buffer.isBuffer(content)
44
+ ? content.toString('utf8')
45
+ : String(content || '');
46
+
47
+ return [
48
+ 'Agentic-Senior-Core',
49
+ 'AGENTIC-SENIOR-CORE',
50
+ 'Adapter Mode: thin',
51
+ 'Adapter Mode: legacy-thin',
52
+ 'Canonical Snapshot SHA256',
53
+ 'Canonical baseline: .instructions.md',
54
+ ].some((managedSignal) => normalizedContent.includes(managedSignal));
55
+ }
56
+
57
+ export async function syncFile(sourcePath, targetPath, options = {}) {
58
+ if (!(await pathExists(sourcePath))) return { status: 'skipped' };
59
+
60
+ if (!(await pathExists(targetPath))) {
61
+ await ensureDirectory(path.dirname(targetPath));
62
+ await fs.copyFile(sourcePath, targetPath);
63
+ return { status: 'created' };
64
+ }
65
+
66
+ const sourceContent = await fs.readFile(sourcePath);
67
+ const targetContent = await fs.readFile(targetPath);
68
+
69
+ if (sourceContent.equals(targetContent)) {
70
+ return { status: 'unchanged' };
71
+ }
72
+
73
+ if (options.preserveUserOwned === true && !isAgenticManagedContent(targetContent)) {
74
+ return { status: 'preserved' };
75
+ }
76
+
77
+ await fs.copyFile(sourcePath, targetPath);
78
+ return { status: 'updated' };
79
+ }
@@ -0,0 +1,237 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import {
5
+ REPO_ROOT,
6
+ entryPointFiles,
7
+ directoryCopies,
8
+ } from '../constants.mjs';
9
+ import { pathExists } from './filesystem.mjs';
10
+
11
+ function toPosixRelativePath(relativePath) {
12
+ return relativePath.split(path.sep).join('/');
13
+ }
14
+
15
+ function isPathWithinPrefix(relativePath, prefixPath) {
16
+ const normalizedRelativePath = toPosixRelativePath(relativePath).replace(/\/+$/g, '');
17
+ const normalizedPrefixPath = toPosixRelativePath(prefixPath).replace(/\/+$/g, '');
18
+
19
+ if (!normalizedPrefixPath) {
20
+ return false;
21
+ }
22
+
23
+ return normalizedRelativePath === normalizedPrefixPath
24
+ || normalizedRelativePath.startsWith(`${normalizedPrefixPath}/`);
25
+ }
26
+
27
+ const localOnlyGovernanceFiles = new Set([
28
+ '.agent-context/state/active-memory.json',
29
+ '.agent-context/state/v3-purge-audit.json',
30
+ ]);
31
+
32
+ function isLocalOnlyGovernanceFile(relativePath) {
33
+ return localOnlyGovernanceFiles.has(toPosixRelativePath(relativePath));
34
+ }
35
+
36
+ export async function collectRelativeTreeEntries(baseDirectoryPath, relativeRootPath) {
37
+ const files = [];
38
+ const directories = [];
39
+
40
+ if (!(await pathExists(baseDirectoryPath))) {
41
+ return { files, directories };
42
+ }
43
+
44
+ const normalizedRootPath = toPosixRelativePath(relativeRootPath);
45
+ directories.push(normalizedRootPath);
46
+
47
+ async function walkDirectory(currentDirectoryPath, currentRelativePath) {
48
+ const directoryEntries = await fs.readdir(currentDirectoryPath, { withFileTypes: true });
49
+
50
+ for (const directoryEntry of directoryEntries) {
51
+ const sourceEntryPath = path.join(currentDirectoryPath, directoryEntry.name);
52
+ const relativeEntryPath = toPosixRelativePath(path.join(currentRelativePath, directoryEntry.name));
53
+
54
+ if (directoryEntry.isDirectory()) {
55
+ directories.push(relativeEntryPath);
56
+ await walkDirectory(sourceEntryPath, relativeEntryPath);
57
+ continue;
58
+ }
59
+
60
+ if (isLocalOnlyGovernanceFile(relativeEntryPath)) {
61
+ continue;
62
+ }
63
+
64
+ files.push(relativeEntryPath);
65
+ }
66
+ }
67
+
68
+ await walkDirectory(baseDirectoryPath, normalizedRootPath);
69
+ return { files, directories };
70
+ }
71
+
72
+ async function collectOptionalManagedEntries(baseDirectoryPath, options = {}) {
73
+ const files = new Set();
74
+ const directories = new Set();
75
+
76
+ if (options.includeMcpTemplate === true) {
77
+ const mcpServerEntrypointPath = path.join(baseDirectoryPath, 'scripts', 'mcp-server.mjs');
78
+ if (await pathExists(mcpServerEntrypointPath)) {
79
+ files.add('scripts/mcp-server.mjs');
80
+ }
81
+
82
+ const mcpServerHelpersDirectoryPath = path.join(baseDirectoryPath, 'scripts', 'mcp-server');
83
+ const mcpServerTreeEntries = await collectRelativeTreeEntries(
84
+ mcpServerHelpersDirectoryPath,
85
+ 'scripts/mcp-server'
86
+ );
87
+
88
+ for (const relativeFilePath of mcpServerTreeEntries.files) {
89
+ files.add(relativeFilePath);
90
+ }
91
+
92
+ for (const relativeDirectoryPath of mcpServerTreeEntries.directories) {
93
+ directories.add(relativeDirectoryPath);
94
+ }
95
+ }
96
+
97
+ return { files, directories };
98
+ }
99
+
100
+ async function buildManagedSourceManifest(options = {}) {
101
+ const sourceFiles = new Set();
102
+ const sourceDirectories = new Set();
103
+
104
+ for (const sourceDirectoryName of directoryCopies) {
105
+ const sourceDirectoryPath = path.join(REPO_ROOT, sourceDirectoryName);
106
+ const sourceTreeEntries = await collectRelativeTreeEntries(sourceDirectoryPath, sourceDirectoryName);
107
+
108
+ for (const sourceFilePath of sourceTreeEntries.files) {
109
+ sourceFiles.add(sourceFilePath);
110
+ }
111
+
112
+ for (const sourceDirectoryPathRelative of sourceTreeEntries.directories) {
113
+ sourceDirectories.add(sourceDirectoryPathRelative);
114
+ }
115
+ }
116
+
117
+ for (const entryPointFileName of entryPointFiles) {
118
+ const sourceFilePath = path.join(REPO_ROOT, entryPointFileName);
119
+ if (!(await pathExists(sourceFilePath))) {
120
+ continue;
121
+ }
122
+
123
+ sourceFiles.add(toPosixRelativePath(entryPointFileName));
124
+ }
125
+
126
+ const optionalManagedEntries = await collectOptionalManagedEntries(REPO_ROOT, options);
127
+ for (const sourceFilePath of optionalManagedEntries.files) {
128
+ sourceFiles.add(sourceFilePath);
129
+ }
130
+ for (const sourceDirectoryPath of optionalManagedEntries.directories) {
131
+ sourceDirectories.add(sourceDirectoryPath);
132
+ }
133
+
134
+ return {
135
+ files: sourceFiles,
136
+ directories: sourceDirectories,
137
+ };
138
+ }
139
+
140
+ async function collectManagedTargetManifest(resolvedTargetDirectoryPath, options = {}) {
141
+ const targetFiles = new Set();
142
+ const targetDirectories = new Set();
143
+
144
+ for (const sourceDirectoryName of directoryCopies) {
145
+ const targetDirectoryPath = path.join(resolvedTargetDirectoryPath, sourceDirectoryName);
146
+ const targetTreeEntries = await collectRelativeTreeEntries(targetDirectoryPath, sourceDirectoryName);
147
+
148
+ for (const targetFilePath of targetTreeEntries.files) {
149
+ targetFiles.add(targetFilePath);
150
+ }
151
+
152
+ for (const targetDirectoryPathRelative of targetTreeEntries.directories) {
153
+ targetDirectories.add(targetDirectoryPathRelative);
154
+ }
155
+ }
156
+
157
+ for (const entryPointFileName of entryPointFiles) {
158
+ const targetFilePath = path.join(resolvedTargetDirectoryPath, entryPointFileName);
159
+ if (!(await pathExists(targetFilePath))) {
160
+ continue;
161
+ }
162
+
163
+ targetFiles.add(toPosixRelativePath(entryPointFileName));
164
+ }
165
+
166
+ const optionalManagedEntries = await collectOptionalManagedEntries(resolvedTargetDirectoryPath, options);
167
+ for (const targetFilePath of optionalManagedEntries.files) {
168
+ targetFiles.add(targetFilePath);
169
+ }
170
+ for (const targetDirectoryPath of optionalManagedEntries.directories) {
171
+ targetDirectories.add(targetDirectoryPath);
172
+ }
173
+
174
+ return {
175
+ files: targetFiles,
176
+ directories: targetDirectories,
177
+ };
178
+ }
179
+
180
+ export async function analyzeManagedGovernanceSurface(
181
+ resolvedTargetDirectoryPath,
182
+ options = {}
183
+ ) {
184
+ const preservePathPrefixes = Array.isArray(options.preservePathPrefixes)
185
+ ? options.preservePathPrefixes
186
+ : ['.agent-context/state', '.gemini'];
187
+
188
+ const sourceManifest = await buildManagedSourceManifest(options);
189
+ const targetManifest = await collectManagedTargetManifest(resolvedTargetDirectoryPath, options);
190
+
191
+ const staleFiles = [];
192
+ const staleDirectories = [];
193
+ const preservedFiles = [];
194
+ const preservedDirectories = [];
195
+
196
+ const sortedTargetFiles = [...targetManifest.files].sort((leftPath, rightPath) => leftPath.localeCompare(rightPath));
197
+ const sortedTargetDirectories = [...targetManifest.directories].sort(
198
+ (leftPath, rightPath) => rightPath.length - leftPath.length || leftPath.localeCompare(rightPath)
199
+ );
200
+
201
+ for (const targetFilePath of sortedTargetFiles) {
202
+ if (sourceManifest.files.has(targetFilePath)) {
203
+ continue;
204
+ }
205
+
206
+ if (preservePathPrefixes.some((prefixPath) => isPathWithinPrefix(targetFilePath, prefixPath))) {
207
+ preservedFiles.push(targetFilePath);
208
+ continue;
209
+ }
210
+
211
+ staleFiles.push(targetFilePath);
212
+ }
213
+
214
+ for (const targetDirectoryPathRelative of sortedTargetDirectories) {
215
+ if (sourceManifest.directories.has(targetDirectoryPathRelative)) {
216
+ continue;
217
+ }
218
+
219
+ if (preservePathPrefixes.some((prefixPath) => isPathWithinPrefix(targetDirectoryPathRelative, prefixPath))) {
220
+ preservedDirectories.push(targetDirectoryPathRelative);
221
+ continue;
222
+ }
223
+
224
+ staleDirectories.push(targetDirectoryPathRelative);
225
+ }
226
+
227
+ return {
228
+ staleFiles,
229
+ staleDirectories,
230
+ preservedFiles,
231
+ preservedDirectories,
232
+ managedSourceFileCount: sourceManifest.files.size,
233
+ managedSourceDirectoryCount: sourceManifest.directories.size,
234
+ managedTargetFileCount: targetManifest.files.size,
235
+ managedTargetDirectoryCount: targetManifest.directories.size,
236
+ };
237
+ }
@@ -0,0 +1,44 @@
1
+ export async function askChoice(promptMessage, options, userInterface) {
2
+ console.log(`\n${promptMessage}`);
3
+ options.forEach((choiceLabel, choiceIndex) => {
4
+ console.log(` ${choiceIndex + 1}. ${choiceLabel}`);
5
+ });
6
+
7
+ while (true) {
8
+ const selectedRawInput = await userInterface.question('Choose a number (press Enter for 1): ');
9
+ const normalizedInput = selectedRawInput.trim();
10
+
11
+ if (!normalizedInput) {
12
+ return options[0];
13
+ }
14
+
15
+ const selectedIndex = Number.parseInt(normalizedInput, 10) - 1;
16
+
17
+ if (Number.isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex >= options.length) {
18
+ console.log('Invalid choice. Please select a valid number.');
19
+ continue;
20
+ }
21
+
22
+ return options[selectedIndex];
23
+ }
24
+ }
25
+
26
+ export async function askYesNo(promptMessage, userInterface, defaultValue) {
27
+ const suffix = typeof defaultValue === 'boolean'
28
+ ? defaultValue ? ' (Y/n): ' : ' (y/N): '
29
+ : ' (y/n): ';
30
+
31
+ while (true) {
32
+ const answer = await userInterface.question(`\n${promptMessage}${suffix}`);
33
+ const normalizedAnswer = answer.trim().toLowerCase();
34
+
35
+ if (!normalizedAnswer && typeof defaultValue === 'boolean') {
36
+ return defaultValue;
37
+ }
38
+
39
+ if (normalizedAnswer === 'y' || normalizedAnswer === 'yes') return true;
40
+ if (normalizedAnswer === 'n' || normalizedAnswer === 'no') return false;
41
+
42
+ console.log("Please answer with 'y' or 'n'.");
43
+ }
44
+ }