@ryuenn3123/agentic-senior-core 3.0.36 → 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.
- package/.agent-context/prompts/bootstrap-design.md +109 -113
- package/.agent-context/rules/frontend-architecture.md +92 -104
- package/.agent-context/state/README.md +26 -0
- package/.cursor/mcp.json +10 -0
- package/.cursor/rules/agentic-senior-core.mdc +48 -0
- package/.cursorrules +22 -88
- package/.gemini/instructions.md +25 -16
- package/.github/copilot-instructions.md +25 -16
- package/.github/instructions/agentic-senior-core.instructions.md +47 -0
- package/.instructions.md +98 -207
- package/.windsurf/rules/agentic-senior-core.md +43 -0
- package/.windsurfrules +22 -88
- package/AGENTS.md +23 -26
- package/CLAUDE.md +43 -0
- package/CONTRIBUTING.md +5 -2
- package/GEMINI.md +43 -0
- package/README.md +24 -7
- package/lib/cli/backup.mjs +4 -4
- package/lib/cli/commands/init/project-context.mjs +101 -0
- package/lib/cli/commands/init/runtime-environment.mjs +59 -0
- package/lib/cli/commands/init/setup-decisions.mjs +83 -0
- package/lib/cli/commands/init.mjs +33 -250
- package/lib/cli/commands/optimize.mjs +1 -1
- package/lib/cli/commands/upgrade.mjs +32 -7
- package/lib/cli/compiler.mjs +59 -17
- package/lib/cli/constants.mjs +5 -0
- package/lib/cli/detector.mjs +4 -0
- package/lib/cli/preflight.mjs +3 -3
- package/lib/cli/project-scaffolder/design-contract/validation.mjs +789 -0
- package/lib/cli/project-scaffolder/design-contract.mjs +137 -861
- package/lib/cli/project-scaffolder/prompt-builders.mjs +73 -81
- package/lib/cli/utils/filesystem.mjs +79 -0
- package/lib/cli/utils/managed-surface.mjs +237 -0
- package/lib/cli/utils/prompting.mjs +44 -0
- package/lib/cli/utils.mjs +33 -335
- package/package.json +21 -2
- package/scripts/clean-local-artifacts.mjs +76 -0
- package/scripts/docs-quality-drift-report.mjs +5 -0
- package/scripts/frontend-usability-audit.mjs +23 -19
- package/scripts/governance-weekly-report.mjs +37 -15
- package/scripts/single-source-lazy-loading-audit.mjs +24 -0
- package/scripts/sync-thin-adapters.mjs +99 -129
- package/scripts/v3-purge-audit.mjs +5 -0
- package/scripts/validate/config.mjs +21 -0
- package/scripts/validate/coverage-checks.mjs +55 -0
- package/.agent-context/marketplace/trust-tiers.json +0 -114
- package/.agent-context/state/benchmark-analysis.json +0 -431
- package/.agent-context/state/benchmark-evidence-bundle.json +0 -1040
- package/.agent-context/state/benchmark-history.json +0 -75
- package/.agent-context/state/benchmark-trend-report.csv +0 -5
- package/.agent-context/state/benchmark-trend-report.json +0 -140
- package/.agent-context/state/benchmark-writer-judge-matrix.json +0 -462
- package/.agent-context/state/memory-continuity-benchmark.json +0 -132
- package/.agent-context/state/onboarding-report.json +0 -102
- package/.agent-context/state/quality-trend-report.json +0 -89
- package/.agent-context/state/token-optimization-benchmark.json +0 -130
- package/.agent-context/state/weekly-governance-report.json +0 -329
- 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,97 +103,88 @@ export function buildDesignBootstrapPrompt({
|
|
|
102
103
|
'2. docs/design-intent.json',
|
|
103
104
|
'',
|
|
104
105
|
'## Required DESIGN.md Sections',
|
|
105
|
-
'1. Design
|
|
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
|
|
109
|
-
'5.
|
|
110
|
-
'6.
|
|
111
|
-
'7.
|
|
112
|
-
'8.
|
|
113
|
-
'9.
|
|
114
|
-
'10.
|
|
115
|
-
'11.
|
|
116
|
-
'12.
|
|
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.
|
|
126
|
-
'6.
|
|
127
|
-
'7.
|
|
124
|
+
'5. visualDirection',
|
|
125
|
+
'6. externalResearchIntake',
|
|
126
|
+
'7. conceptualAnchor',
|
|
128
127
|
'8. derivedTokenLogic',
|
|
129
|
-
'9.
|
|
130
|
-
'10.
|
|
131
|
-
'11.
|
|
132
|
-
'12.
|
|
133
|
-
'13.
|
|
134
|
-
'14.
|
|
135
|
-
'15.
|
|
136
|
-
'16.
|
|
137
|
-
'17.
|
|
138
|
-
'18.
|
|
139
|
-
'19.
|
|
140
|
-
'20.
|
|
141
|
-
'21.
|
|
142
|
-
'22.
|
|
143
|
-
'23.
|
|
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.
|
|
146
|
-
'26.
|
|
147
|
-
'27.
|
|
148
|
-
'
|
|
149
|
-
'',
|
|
150
|
-
'
|
|
151
|
-
'
|
|
152
|
-
'
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'
|
|
158
|
-
'
|
|
159
|
-
'
|
|
160
|
-
'
|
|
161
|
-
'
|
|
162
|
-
'
|
|
163
|
-
'
|
|
164
|
-
'
|
|
165
|
-
'
|
|
166
|
-
'
|
|
167
|
-
'
|
|
168
|
-
'
|
|
169
|
-
'
|
|
170
|
-
'
|
|
171
|
-
'
|
|
172
|
-
'
|
|
173
|
-
'
|
|
174
|
-
'
|
|
175
|
-
'
|
|
176
|
-
'
|
|
177
|
-
'
|
|
178
|
-
'
|
|
179
|
-
'
|
|
180
|
-
'
|
|
181
|
-
'
|
|
182
|
-
'
|
|
183
|
-
'
|
|
184
|
-
'
|
|
185
|
-
'
|
|
186
|
-
'
|
|
187
|
-
'
|
|
188
|
-
'
|
|
189
|
-
'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.',
|
|
190
|
-
'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.',
|
|
191
|
-
'37. The chosen anchor must define a stable conceptualAnchor.anchorReference. derivedTokenLogic.anchorReference must match it exactly so validation does not depend on paraphrased prose.',
|
|
192
|
-
'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.',
|
|
193
|
-
'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.',
|
|
194
|
-
'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.',
|
|
195
|
-
'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.',
|
|
183
|
+
'',
|
|
184
|
+
'## Creative Ambition Floor',
|
|
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.',
|
|
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.',
|
|
196
188
|
'',
|
|
197
189
|
'## Token Derivation Audit',
|
|
198
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
|
+
}
|