@archpilotlabs/archpilot 0.0.18 → 0.0.19
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/README.md +77 -73
- package/dist/archpilot-cli.js +372 -91
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -8,19 +8,19 @@
|
|
|
8
8
|
Architecture guardrails for AI-native software development
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
|
-
ArchPilot validates your repository against architecture rules
|
|
11
|
+
ArchPilot validates your repository against architecture rules, helping engineering teams move faster with AI-assisted development without sacrificing architectural integrity.
|
|
12
12
|
|
|
13
13
|
**Your AI writes code. ArchPilot protects architecture.**
|
|
14
14
|
|
|
15
15
|
<p align="center">
|
|
16
|
-
Validate
|
|
16
|
+
Validate | Enforce | Measure architecture continuously
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
ArchPilot makes architecture **visible, enforceable, and measurable** as your system evolves.
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Why this exists
|
|
24
24
|
|
|
25
25
|
Most teams rely on documentation or tribal knowledge to maintain architecture.
|
|
26
26
|
|
|
@@ -28,83 +28,85 @@ Over time, boundaries blur, dependencies leak, and architectural decisions drift
|
|
|
28
28
|
|
|
29
29
|
ArchPilot turns architecture into something you can **actually validate, enforce, and track continuously**.
|
|
30
30
|
|
|
31
|
-
AI accelerates code delivery
|
|
31
|
+
AI accelerates code delivery but architectural drift can accelerate too.
|
|
32
32
|
|
|
33
33
|
ArchPilot gives teams deterministic architecture guardrails in local development, CI, and governance workflows.
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## What it does
|
|
38
38
|
|
|
39
39
|
ArchPilot turns architecture into something you can actually run and verify:
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
- Validate module boundaries and dependencies
|
|
42
|
+
- Enforce architecture rules and contracts
|
|
43
|
+
- Detect architectural drift over time
|
|
44
|
+
- Generate architecture scores and reports
|
|
45
|
+
- Surface architecture feedback in PRs and CI
|
|
46
46
|
|
|
47
47
|
No runtime agents. No external services. Fully deterministic and local-first.
|
|
48
48
|
|
|
49
|
+
ArchPilot works alongside GitHub Copilot, Claude Code, Codex, Cursor, and other AI coding tools by continuously validating architectural boundaries before architectural drift becomes technical debt.
|
|
50
|
+
|
|
49
51
|
---
|
|
50
52
|
|
|
51
|
-
##
|
|
53
|
+
## Supported Stacks
|
|
52
54
|
|
|
53
55
|
ArchPilot supports **practical deterministic architecture detection** across modern application stacks.
|
|
54
56
|
|
|
55
57
|
### Backend
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
59
|
+
- Node.js / TypeScript
|
|
60
|
+
- NestJS
|
|
61
|
+
- Express.js
|
|
62
|
+
- Java
|
|
63
|
+
- Spring / Spring Boot
|
|
64
|
+
- Kotlin
|
|
65
|
+
- FastAPI
|
|
66
|
+
- Django
|
|
67
|
+
- Flask
|
|
68
|
+
- PHP
|
|
69
|
+
- Laravel
|
|
70
|
+
- Go
|
|
71
|
+
- .NET
|
|
72
|
+
- Ruby on Rails
|
|
71
73
|
|
|
72
74
|
### Frontend
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
- React
|
|
77
|
+
- Next.js
|
|
78
|
+
- Angular
|
|
79
|
+
- Vue
|
|
78
80
|
|
|
79
81
|
### Infrastructure
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
- Terraform
|
|
84
|
+
- Ansible
|
|
83
85
|
|
|
84
|
-
ArchPilot detects
|
|
86
|
+
ArchPilot detects application stacks from practical repository evidence - framework markers, manifests, project layout, and source structure - then applies conservative architecture discovery defaults locally.
|
|
85
87
|
|
|
86
88
|
**No cloud scanning. No remote indexing. Deterministic local validation.**
|
|
87
89
|
|
|
88
90
|
---
|
|
89
91
|
|
|
90
|
-
##
|
|
92
|
+
## Local-first. Cloud-powered.
|
|
91
93
|
|
|
92
94
|
Start locally in minutes:
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
- Offline validation
|
|
97
|
+
- Local reports and artifacts
|
|
98
|
+
- PR review markdown
|
|
99
|
+
- CI validation
|
|
100
|
+
- Deterministic architecture checks
|
|
99
101
|
|
|
100
102
|
Then connect to **ArchPilot Cloud** to unlock:
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
- Repository history and trends
|
|
105
|
+
- Findings dashboards
|
|
106
|
+
- Team ownership and governance workflows
|
|
107
|
+
- Shared policies and exceptions
|
|
108
|
+
- Organization-wide architecture visibility
|
|
109
|
+
- Continuous governance across repositories
|
|
108
110
|
|
|
109
111
|
```bash
|
|
110
112
|
archpilot cloud status
|
|
@@ -114,14 +116,15 @@ archpilot cloud disconnect
|
|
|
114
116
|
|
|
115
117
|
Cloud setup only asks for your API token.
|
|
116
118
|
|
|
117
|
-
ArchPilot uses the canonical production API automatically
|
|
119
|
+
ArchPilot uses the canonical production API automatically; no server URL setup is required for normal use.
|
|
120
|
+
|
|
118
121
|
Learn more:
|
|
119
122
|
|
|
120
123
|
https://archpilot.org/docs
|
|
121
124
|
|
|
122
125
|
---
|
|
123
126
|
|
|
124
|
-
##
|
|
127
|
+
## Installation
|
|
125
128
|
|
|
126
129
|
Install globally:
|
|
127
130
|
|
|
@@ -137,7 +140,7 @@ npx @archpilotlabs/archpilot --help
|
|
|
137
140
|
|
|
138
141
|
---
|
|
139
142
|
|
|
140
|
-
##
|
|
143
|
+
## Quick Start
|
|
141
144
|
|
|
142
145
|
### 1. Initialize architecture
|
|
143
146
|
|
|
@@ -183,24 +186,24 @@ Applies safe, deterministic fixes for missing architecture artifacts.
|
|
|
183
186
|
archpilot validate --ci
|
|
184
187
|
```
|
|
185
188
|
|
|
186
|
-
|
|
187
|
-
|
|
189
|
+
- Fails only on real architecture violations
|
|
190
|
+
- Keeps setup issues separate from architecture quality
|
|
188
191
|
|
|
189
192
|
---
|
|
190
193
|
|
|
191
|
-
##
|
|
194
|
+
## CLI vs VS Code Extension
|
|
192
195
|
|
|
193
196
|
ArchPilot works in two ways:
|
|
194
197
|
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
- **CLI (this package)** - validation, CI, automation
|
|
199
|
+
- **VS Code Extension** - visual inspector, guided workflows, and architecture exploration
|
|
197
200
|
|
|
198
201
|
Both use the same validation engine and produce identical results.
|
|
199
202
|
The extension enhances developer experience, but is not required.
|
|
200
203
|
|
|
201
204
|
---
|
|
202
205
|
|
|
203
|
-
##
|
|
206
|
+
## Key Concepts
|
|
204
207
|
|
|
205
208
|
### Architecture Health Score
|
|
206
209
|
|
|
@@ -212,13 +215,13 @@ Tracks completeness of configuration and onboarding.
|
|
|
212
215
|
|
|
213
216
|
### Findings
|
|
214
217
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
+
- **violation** - real architecture issue
|
|
219
|
+
- **setup-gap** - missing config / skipped checks
|
|
220
|
+
- **guidance** - non-blocking recommendation
|
|
218
221
|
|
|
219
222
|
---
|
|
220
223
|
|
|
221
|
-
##
|
|
224
|
+
## Core Commands
|
|
222
225
|
|
|
223
226
|
```bash
|
|
224
227
|
archpilot init
|
|
@@ -247,37 +250,38 @@ archpilot governance upload
|
|
|
247
250
|
|
|
248
251
|
---
|
|
249
252
|
|
|
250
|
-
##
|
|
253
|
+
## Core Capabilities
|
|
251
254
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
255
|
+
- Architecture-as-code (`.archpilot/`)
|
|
256
|
+
- Smart Init architecture discovery
|
|
257
|
+
- Dependency validation and module contracts
|
|
258
|
+
- ADR (Architecture Decision Record) support
|
|
259
|
+
- Drift detection and history tracking
|
|
260
|
+
- PR review and CI integration
|
|
261
|
+
- Architecture scoring and reporting
|
|
258
262
|
|
|
259
263
|
---
|
|
260
264
|
|
|
261
|
-
##
|
|
265
|
+
## Philosophy
|
|
262
266
|
|
|
263
267
|
ArchPilot is built on four principles:
|
|
264
268
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
+
- Architecture should be explicit
|
|
270
|
+
- Rules should be executable
|
|
271
|
+
- Validation should be deterministic
|
|
272
|
+
- Feedback should be continuous
|
|
269
273
|
|
|
270
274
|
---
|
|
271
275
|
|
|
272
|
-
##
|
|
276
|
+
## License
|
|
273
277
|
|
|
274
278
|
Proprietary. See LICENSE file for details.
|
|
275
279
|
|
|
276
280
|
---
|
|
277
281
|
|
|
278
|
-
##
|
|
282
|
+
## Learn More
|
|
279
283
|
|
|
280
|
-
|
|
281
|
-
|
|
284
|
+
- Website: https://archpilot.org
|
|
285
|
+
- Documentation: https://archpilot.org/docs
|
|
282
286
|
|
|
283
287
|
---
|
package/dist/archpilot-cli.js
CHANGED
|
@@ -8635,7 +8635,7 @@ ${lanes.join("\n")}
|
|
|
8635
8635
|
watchDirectory,
|
|
8636
8636
|
preferNonRecursiveWatch: !fsSupportsRecursiveFsWatch,
|
|
8637
8637
|
resolvePath: (path71) => _path.resolve(path71),
|
|
8638
|
-
fileExists:
|
|
8638
|
+
fileExists: fileExists6,
|
|
8639
8639
|
directoryExists: directoryExists2,
|
|
8640
8640
|
getAccessibleFileSystemEntries,
|
|
8641
8641
|
createDirectory(directoryName) {
|
|
@@ -8797,7 +8797,7 @@ ${lanes.join("\n")}
|
|
|
8797
8797
|
if (platform === "win32" || platform === "win64") {
|
|
8798
8798
|
return false;
|
|
8799
8799
|
}
|
|
8800
|
-
return !
|
|
8800
|
+
return !fileExists6(swapCase(__filename));
|
|
8801
8801
|
}
|
|
8802
8802
|
function swapCase(s) {
|
|
8803
8803
|
return s.replace(/\w/g, (ch) => {
|
|
@@ -8930,7 +8930,7 @@ ${lanes.join("\n")}
|
|
|
8930
8930
|
return false;
|
|
8931
8931
|
}
|
|
8932
8932
|
}
|
|
8933
|
-
function
|
|
8933
|
+
function fileExists6(path71) {
|
|
8934
8934
|
return fileSystemEntryExists(
|
|
8935
8935
|
path71,
|
|
8936
8936
|
0
|
|
@@ -134756,7 +134756,7 @@ ${lanes.join("\n")}
|
|
|
134756
134756
|
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames2);
|
|
134757
134757
|
return {
|
|
134758
134758
|
useCaseSensitiveFileNames: useCaseSensitiveFileNames2,
|
|
134759
|
-
fileExists:
|
|
134759
|
+
fileExists: fileExists6,
|
|
134760
134760
|
readFile: (path71, encoding) => host.readFile(path71, encoding),
|
|
134761
134761
|
directoryExists: host.directoryExists && directoryExists2,
|
|
134762
134762
|
getDirectories,
|
|
@@ -134842,7 +134842,7 @@ ${lanes.join("\n")}
|
|
|
134842
134842
|
}
|
|
134843
134843
|
return host.writeFile(fileName, data, writeByteOrderMark);
|
|
134844
134844
|
}
|
|
134845
|
-
function
|
|
134845
|
+
function fileExists6(fileName) {
|
|
134846
134846
|
const path71 = toPath3(fileName);
|
|
134847
134847
|
const result = getCachedFileSystemEntriesForBaseDir(path71);
|
|
134848
134848
|
return result && hasEntry(result.sortedAndCanonicalizedFiles, getCanonicalFileName(getBaseNameOfFileName(fileName))) || host.fileExists(fileName);
|
|
@@ -135261,10 +135261,10 @@ ${lanes.join("\n")}
|
|
|
135261
135261
|
function closeFileWatcherOf(objWithWatcher) {
|
|
135262
135262
|
objWithWatcher.watcher.close();
|
|
135263
135263
|
}
|
|
135264
|
-
function findConfigFile(searchPath,
|
|
135264
|
+
function findConfigFile(searchPath, fileExists6, configName = "tsconfig.json") {
|
|
135265
135265
|
return forEachAncestorDirectory(searchPath, (ancestor) => {
|
|
135266
135266
|
const fileName = combinePaths(ancestor, configName);
|
|
135267
|
-
return
|
|
135267
|
+
return fileExists6(fileName) ? fileName : void 0;
|
|
135268
135268
|
});
|
|
135269
135269
|
}
|
|
135270
135270
|
function resolveTripleslashReference(moduleName, containingFile) {
|
|
@@ -135871,14 +135871,14 @@ ${lanes.join("\n")}
|
|
|
135871
135871
|
}
|
|
135872
135872
|
return { file, pos, end, packageId };
|
|
135873
135873
|
}
|
|
135874
|
-
function isProgramUptoDate(program, rootFileNames, newOptions, getSourceVersion,
|
|
135874
|
+
function isProgramUptoDate(program, rootFileNames, newOptions, getSourceVersion, fileExists6, hasInvalidatedResolutions, hasInvalidatedLibResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences) {
|
|
135875
135875
|
if (!program || (hasChangedAutomaticTypeDirectiveNames == null ? void 0 : hasChangedAutomaticTypeDirectiveNames())) return false;
|
|
135876
135876
|
if (!arrayIsEqualTo(program.getRootFileNames(), rootFileNames)) return false;
|
|
135877
135877
|
let seenResolvedRefs;
|
|
135878
135878
|
if (!arrayIsEqualTo(program.getProjectReferences(), projectReferences, projectReferenceUptoDate)) return false;
|
|
135879
135879
|
if (program.getSourceFiles().some(sourceFileNotUptoDate)) return false;
|
|
135880
135880
|
const missingPaths = program.getMissingFilePaths();
|
|
135881
|
-
if (missingPaths && forEachEntry(missingPaths,
|
|
135881
|
+
if (missingPaths && forEachEntry(missingPaths, fileExists6)) return false;
|
|
135882
135882
|
const currentOptions = program.getCompilerOptions();
|
|
135883
135883
|
if (!compareDataObjects(currentOptions, newOptions)) return false;
|
|
135884
135884
|
if (program.resolvedLibReferences && forEachEntry(program.resolvedLibReferences, (_value, libFileName) => hasInvalidatedLibResolutions(libFileName))) return false;
|
|
@@ -136206,7 +136206,7 @@ ${lanes.join("\n")}
|
|
|
136206
136206
|
let mapSourceFileToResolvedRef;
|
|
136207
136207
|
let mapOutputFileToResolvedRef;
|
|
136208
136208
|
const useSourceOfProjectReferenceRedirect = !!((_d = host.useSourceOfProjectReferenceRedirect) == null ? void 0 : _d.call(host)) && !options.disableSourceOfProjectReferenceRedirect;
|
|
136209
|
-
const { onProgramCreateComplete, fileExists:
|
|
136209
|
+
const { onProgramCreateComplete, fileExists: fileExists6, directoryExists: directoryExists2 } = updateHostForUseSourceOfProjectReferenceRedirect({
|
|
136210
136210
|
compilerHost: host,
|
|
136211
136211
|
getSymlinkCache,
|
|
136212
136212
|
useSourceOfProjectReferenceRedirect,
|
|
@@ -136440,7 +136440,7 @@ ${lanes.join("\n")}
|
|
|
136440
136440
|
getImpliedNodeFormatForEmit: getImpliedNodeFormatForEmit2,
|
|
136441
136441
|
shouldTransformImportCall,
|
|
136442
136442
|
emitBuildInfo,
|
|
136443
|
-
fileExists:
|
|
136443
|
+
fileExists: fileExists6,
|
|
136444
136444
|
readFile,
|
|
136445
136445
|
directoryExists: directoryExists2,
|
|
136446
136446
|
getSymlinkCache,
|
|
@@ -138939,8 +138939,8 @@ ${lanes.join("\n")}
|
|
|
138939
138939
|
const originalDirectoryExists = host.compilerHost.directoryExists;
|
|
138940
138940
|
const originalGetDirectories = host.compilerHost.getDirectories;
|
|
138941
138941
|
const originalRealpath = host.compilerHost.realpath;
|
|
138942
|
-
if (!host.useSourceOfProjectReferenceRedirect) return { onProgramCreateComplete: noop, fileExists:
|
|
138943
|
-
host.compilerHost.fileExists =
|
|
138942
|
+
if (!host.useSourceOfProjectReferenceRedirect) return { onProgramCreateComplete: noop, fileExists: fileExists6 };
|
|
138943
|
+
host.compilerHost.fileExists = fileExists6;
|
|
138944
138944
|
let directoryExists2;
|
|
138945
138945
|
if (originalDirectoryExists) {
|
|
138946
138946
|
directoryExists2 = host.compilerHost.directoryExists = (path71) => {
|
|
@@ -138979,13 +138979,13 @@ ${lanes.join("\n")}
|
|
|
138979
138979
|
return ((_a = host.getSymlinkCache().getSymlinkedFiles()) == null ? void 0 : _a.get(host.toPath(s))) || originalRealpath.call(host.compilerHost, s);
|
|
138980
138980
|
};
|
|
138981
138981
|
}
|
|
138982
|
-
return { onProgramCreateComplete, fileExists:
|
|
138982
|
+
return { onProgramCreateComplete, fileExists: fileExists6, directoryExists: directoryExists2 };
|
|
138983
138983
|
function onProgramCreateComplete() {
|
|
138984
138984
|
host.compilerHost.fileExists = originalFileExists;
|
|
138985
138985
|
host.compilerHost.directoryExists = originalDirectoryExists;
|
|
138986
138986
|
host.compilerHost.getDirectories = originalGetDirectories;
|
|
138987
138987
|
}
|
|
138988
|
-
function
|
|
138988
|
+
function fileExists6(file) {
|
|
138989
138989
|
if (originalFileExists.call(host.compilerHost, file)) return true;
|
|
138990
138990
|
if (!host.getResolvedProjectReferences()) return false;
|
|
138991
138991
|
if (!isDeclarationFileName(file)) return false;
|
|
@@ -143337,7 +143337,7 @@ ${lanes.join("\n")}
|
|
|
143337
143337
|
compilerHost.getSourceFile = (fileName, ...args) => getVersionedSourceFileByPath(fileName, toPath3(fileName), ...args);
|
|
143338
143338
|
compilerHost.getSourceFileByPath = getVersionedSourceFileByPath;
|
|
143339
143339
|
compilerHost.getNewLine = () => newLine;
|
|
143340
|
-
compilerHost.fileExists =
|
|
143340
|
+
compilerHost.fileExists = fileExists6;
|
|
143341
143341
|
compilerHost.onReleaseOldSourceFile = onReleaseOldSourceFile;
|
|
143342
143342
|
compilerHost.onReleaseParsedCommandLine = onReleaseParsedCommandLine;
|
|
143343
143343
|
compilerHost.toPath = toPath3;
|
|
@@ -143541,7 +143541,7 @@ ${lanes.join("\n")}
|
|
|
143541
143541
|
function isFilePresenceUnknownOnHost(hostSourceFile) {
|
|
143542
143542
|
return typeof hostSourceFile.version === "boolean";
|
|
143543
143543
|
}
|
|
143544
|
-
function
|
|
143544
|
+
function fileExists6(fileName) {
|
|
143545
143545
|
const path71 = toPath3(fileName);
|
|
143546
143546
|
if (isFileMissingOnHost(sourceFilesCache.get(path71))) {
|
|
143547
143547
|
return false;
|
|
@@ -150510,15 +150510,15 @@ ${lanes.join("\n")}
|
|
|
150510
150510
|
}
|
|
150511
150511
|
return true;
|
|
150512
150512
|
}
|
|
150513
|
-
function getMappedLocation(location, sourceMapper,
|
|
150513
|
+
function getMappedLocation(location, sourceMapper, fileExists6) {
|
|
150514
150514
|
const mapsTo = sourceMapper.tryGetSourcePosition(location);
|
|
150515
|
-
return mapsTo && (!
|
|
150515
|
+
return mapsTo && (!fileExists6 || fileExists6(normalizePath26(mapsTo.fileName)) ? mapsTo : void 0);
|
|
150516
150516
|
}
|
|
150517
|
-
function getMappedDocumentSpan(documentSpan, sourceMapper,
|
|
150517
|
+
function getMappedDocumentSpan(documentSpan, sourceMapper, fileExists6) {
|
|
150518
150518
|
const { fileName, textSpan } = documentSpan;
|
|
150519
|
-
const newPosition = getMappedLocation({ fileName, pos: textSpan.start }, sourceMapper,
|
|
150519
|
+
const newPosition = getMappedLocation({ fileName, pos: textSpan.start }, sourceMapper, fileExists6);
|
|
150520
150520
|
if (!newPosition) return void 0;
|
|
150521
|
-
const newEndPosition = getMappedLocation({ fileName, pos: textSpan.start + textSpan.length }, sourceMapper,
|
|
150521
|
+
const newEndPosition = getMappedLocation({ fileName, pos: textSpan.start + textSpan.length }, sourceMapper, fileExists6);
|
|
150522
150522
|
const newLength = newEndPosition ? newEndPosition.pos - newPosition.pos : textSpan.length;
|
|
150523
150523
|
return {
|
|
150524
150524
|
fileName: newPosition.fileName,
|
|
@@ -150528,20 +150528,20 @@ ${lanes.join("\n")}
|
|
|
150528
150528
|
},
|
|
150529
150529
|
originalFileName: documentSpan.fileName,
|
|
150530
150530
|
originalTextSpan: documentSpan.textSpan,
|
|
150531
|
-
contextSpan: getMappedContextSpan(documentSpan, sourceMapper,
|
|
150531
|
+
contextSpan: getMappedContextSpan(documentSpan, sourceMapper, fileExists6),
|
|
150532
150532
|
originalContextSpan: documentSpan.contextSpan
|
|
150533
150533
|
};
|
|
150534
150534
|
}
|
|
150535
|
-
function getMappedContextSpan(documentSpan, sourceMapper,
|
|
150535
|
+
function getMappedContextSpan(documentSpan, sourceMapper, fileExists6) {
|
|
150536
150536
|
const contextSpanStart = documentSpan.contextSpan && getMappedLocation(
|
|
150537
150537
|
{ fileName: documentSpan.fileName, pos: documentSpan.contextSpan.start },
|
|
150538
150538
|
sourceMapper,
|
|
150539
|
-
|
|
150539
|
+
fileExists6
|
|
150540
150540
|
);
|
|
150541
150541
|
const contextSpanEnd = documentSpan.contextSpan && getMappedLocation(
|
|
150542
150542
|
{ fileName: documentSpan.fileName, pos: documentSpan.contextSpan.start + documentSpan.contextSpan.length },
|
|
150543
150543
|
sourceMapper,
|
|
150544
|
-
|
|
150544
|
+
fileExists6
|
|
150545
150545
|
);
|
|
150546
150546
|
return contextSpanStart && contextSpanEnd ? { start: contextSpanStart.pos, length: contextSpanEnd.pos - contextSpanStart.pos } : void 0;
|
|
150547
150547
|
}
|
|
@@ -199025,9 +199025,9 @@ ${options.prefix}` : "\n" : options.prefix
|
|
|
199025
199025
|
this.markContainingProjectsAsDirty();
|
|
199026
199026
|
}
|
|
199027
199027
|
}
|
|
199028
|
-
close(
|
|
199028
|
+
close(fileExists6 = true) {
|
|
199029
199029
|
this.textStorage.isOpen = false;
|
|
199030
|
-
if (
|
|
199030
|
+
if (fileExists6 && this.textStorage.scheduleReloadIfNeeded()) {
|
|
199031
199031
|
this.markContainingProjectsAsDirty();
|
|
199032
199032
|
}
|
|
199033
199033
|
}
|
|
@@ -200131,11 +200131,11 @@ ${options.prefix}` : "\n" : options.prefix
|
|
|
200131
200131
|
this.rootFilesMap.set(path71, { fileName });
|
|
200132
200132
|
this.markAsDirty();
|
|
200133
200133
|
}
|
|
200134
|
-
removeFile(info,
|
|
200134
|
+
removeFile(info, fileExists6, detachFromProject) {
|
|
200135
200135
|
if (this.isRoot(info)) {
|
|
200136
200136
|
this.removeRoot(info);
|
|
200137
200137
|
}
|
|
200138
|
-
if (
|
|
200138
|
+
if (fileExists6) {
|
|
200139
200139
|
this.resolutionCache.removeResolutionsOfFile(info.path);
|
|
200140
200140
|
} else {
|
|
200141
200141
|
this.resolutionCache.invalidateResolutionOfFile(info.path);
|
|
@@ -203005,8 +203005,8 @@ ${options.prefix}` : "\n" : options.prefix
|
|
|
203005
203005
|
*/
|
|
203006
203006
|
closeOpenFile(info, skipAssignOrphanScriptInfosToInferredProject) {
|
|
203007
203007
|
var _a;
|
|
203008
|
-
const
|
|
203009
|
-
info.close(
|
|
203008
|
+
const fileExists6 = info.isDynamic ? false : this.host.fileExists(info.fileName);
|
|
203009
|
+
info.close(fileExists6);
|
|
203010
203010
|
this.stopWatchingConfigFilesForScriptInfo(info);
|
|
203011
203011
|
const canonicalFileName = this.toCanonicalFileName(info.fileName);
|
|
203012
203012
|
if (this.openFilesWithNonRootedDiskPath.get(canonicalFileName) === info) {
|
|
@@ -203032,7 +203032,7 @@ ${options.prefix}` : "\n" : options.prefix
|
|
|
203032
203032
|
}
|
|
203033
203033
|
p.removeFile(
|
|
203034
203034
|
info,
|
|
203035
|
-
|
|
203035
|
+
fileExists6,
|
|
203036
203036
|
/*detachFromProject*/
|
|
203037
203037
|
true
|
|
203038
203038
|
);
|
|
@@ -203048,7 +203048,7 @@ ${options.prefix}` : "\n" : options.prefix
|
|
|
203048
203048
|
if (!skipAssignOrphanScriptInfosToInferredProject && ensureProjectsForOpenFiles) {
|
|
203049
203049
|
this.assignOrphanScriptInfosToInferredProject();
|
|
203050
203050
|
}
|
|
203051
|
-
if (
|
|
203051
|
+
if (fileExists6) {
|
|
203052
203052
|
this.watchClosedScriptInfo(info);
|
|
203053
203053
|
} else {
|
|
203054
203054
|
this.handleDeletedFile(
|
|
@@ -232861,18 +232861,18 @@ async function previewArch003(context) {
|
|
|
232861
232861
|
const directoryPath = path36.join(context.workspaceRoot, "docs", "architecture");
|
|
232862
232862
|
const filePath = path36.join(directoryPath, "overview.md");
|
|
232863
232863
|
const directoryExists2 = await pathExists13(directoryPath);
|
|
232864
|
-
const
|
|
232864
|
+
const fileExists6 = await pathExists13(filePath);
|
|
232865
232865
|
const filesCreated = [];
|
|
232866
232866
|
if (!directoryExists2) {
|
|
232867
232867
|
filesCreated.push(relative10(directoryPath, context.workspaceRoot));
|
|
232868
232868
|
}
|
|
232869
|
-
if (!
|
|
232869
|
+
if (!fileExists6) {
|
|
232870
232870
|
filesCreated.push(relative10(filePath, context.workspaceRoot));
|
|
232871
232871
|
}
|
|
232872
232872
|
return {
|
|
232873
232873
|
filesCreated,
|
|
232874
232874
|
filesModified: [],
|
|
232875
|
-
diffPreview:
|
|
232875
|
+
diffPreview: fileExists6 ? "(no changes)" : `${directoryExists2 ? "" : `+ create directory ${relative10(directoryPath, context.workspaceRoot)}
|
|
232876
232876
|
`}+ ${architectureOverviewStarterTemplate.trimEnd().replace(/\n/g, "\n+ ")}`,
|
|
232877
232877
|
description: "Create a starter architecture overview document if missing."
|
|
232878
232878
|
};
|
|
@@ -239759,10 +239759,10 @@ function detectProjectKinds(workspaceRoot, options) {
|
|
|
239759
239759
|
addSignal("backend", 3, "Found requirements.txt", true);
|
|
239760
239760
|
}
|
|
239761
239761
|
if (hasFile("pyproject.toml")) {
|
|
239762
|
-
addSignal("
|
|
239762
|
+
addSignal("library", 2, "Found pyproject.toml");
|
|
239763
239763
|
}
|
|
239764
239764
|
if (hasFile("setup.py")) {
|
|
239765
|
-
addSignal("
|
|
239765
|
+
addSignal("library", 3, "Found setup.py", true);
|
|
239766
239766
|
}
|
|
239767
239767
|
if (hasFile("composer.json")) {
|
|
239768
239768
|
addSignal("backend", 3, "Found composer.json", true);
|
|
@@ -239771,7 +239771,10 @@ function detectProjectKinds(workspaceRoot, options) {
|
|
|
239771
239771
|
addSignal("backend", 5, "Found Laravel app-root backend evidence", true);
|
|
239772
239772
|
}
|
|
239773
239773
|
if (hasFile("go.mod")) {
|
|
239774
|
-
addSignal("
|
|
239774
|
+
addSignal("library", 2, "Found go.mod");
|
|
239775
|
+
}
|
|
239776
|
+
if (hasDirectoryPath("cmd") || hasDirectoryNamed("cmd")) {
|
|
239777
|
+
addSignal("backend", 2, "Found Go cmd/ entrypoint directory");
|
|
239775
239778
|
}
|
|
239776
239779
|
if (hasFile("openapi.yaml") || hasFile("openapi.yml")) {
|
|
239777
239780
|
addSignal("backend", 2, "Found openapi.yaml/openapi.yml", true);
|
|
@@ -239782,6 +239785,9 @@ function detectProjectKinds(workspaceRoot, options) {
|
|
|
239782
239785
|
if (hasDirectoryPath("src/main/java")) {
|
|
239783
239786
|
addSignal("backend", 3, "Found src/main/java", true);
|
|
239784
239787
|
}
|
|
239788
|
+
if (scan.filePaths.some((entry) => /(^|\/)(repositories?|repository)\.py$/u.test(entry))) {
|
|
239789
|
+
addSignal("backend", 2, "Found Python repository-layer files");
|
|
239790
|
+
}
|
|
239785
239791
|
for (const dependency of ["express", "fastify", "@nestjs/core", "koa", "hapi"]) {
|
|
239786
239792
|
if (packageHasDependency(dependency)) {
|
|
239787
239793
|
addSignal("backend", 2, `Found package.json with ${dependency} dependency`);
|
|
@@ -239825,6 +239831,12 @@ function detectProjectKinds(workspaceRoot, options) {
|
|
|
239825
239831
|
if (hasFile("src/index.ts") || hasFile("src/index.js")) {
|
|
239826
239832
|
addSignal("library", 2, "Found src/index.ts or src/index.js");
|
|
239827
239833
|
}
|
|
239834
|
+
if (hasDirectoryPath("src/modules") && !hasFile("requirements.txt")) {
|
|
239835
|
+
addSignal("library", 1, "Found reusable module folders under src/modules");
|
|
239836
|
+
}
|
|
239837
|
+
if (scan.filePaths.some((entry) => entry.startsWith("src/") && entry.endsWith("/__init__.py"))) {
|
|
239838
|
+
addSignal("library", 2, "Found Python package __init__.py files");
|
|
239839
|
+
}
|
|
239828
239840
|
if (packageSummaries.some((summary) => summary.hasExports)) {
|
|
239829
239841
|
addSignal("library", 3, "Found package.json exports field", true);
|
|
239830
239842
|
}
|
|
@@ -240306,6 +240318,13 @@ var textDependencyDatabaseHints = [
|
|
|
240306
240318
|
{ database: "snowflake", pattern: /\b(snowflake-connector|snowflake-sqlalchemy|snowflake-jdbc|Snowflake\.Data|snowflake-sdk)\b/iu, label: "Snowflake dependency" },
|
|
240307
240319
|
{ database: "bigquery", pattern: /\b(bigquery|google-cloud-bigquery|Google\.Cloud\.BigQuery)\b/iu, label: "BigQuery dependency" }
|
|
240308
240320
|
];
|
|
240321
|
+
var ormToolingPatterns = [
|
|
240322
|
+
/\b(typeorm|sequelize|drizzle-orm|@mikro-orm\/core|prisma|@prisma\/client)\b/iu,
|
|
240323
|
+
/\b(hibernate-core|org\.hibernate|spring-boot-starter-data-jpa|JpaRepository|CrudRepository|jakarta\.persistence|javax\.persistence)\b/u,
|
|
240324
|
+
/\b(Microsoft\.EntityFrameworkCore|DbContext|DbSet<)\b/u,
|
|
240325
|
+
/\b(SQLAlchemy|sqlalchemy|alembic|Alembic)\b/u,
|
|
240326
|
+
/\b(doctrine\/orm|illuminate\/database)\b/iu
|
|
240327
|
+
];
|
|
240309
240328
|
function normalizeConnectionName(value) {
|
|
240310
240329
|
const normalized = value.trim().toLowerCase().replace(/['"]/gu, "");
|
|
240311
240330
|
for (const entry of databaseAliases) {
|
|
@@ -240529,6 +240548,35 @@ function detectDatabaseHints(workspaceRoot, files, directories, packageJsonSumma
|
|
|
240529
240548
|
}
|
|
240530
240549
|
}
|
|
240531
240550
|
}
|
|
240551
|
+
const hasRelationalOrmEvidence = ["typeorm", "sequelize", "@sequelize/core", "knex", "drizzle-orm", "@mikro-orm/core", "mikro-orm", "prisma", "@prisma/client"].some((dependency) => dependencyExists(packageJsonSummaries, dependency)) || ["doctrine/orm", "illuminate/database"].some((dependency) => composerDependencyExists(composerJsonSummaries, dependency)) || configCandidateFiles.some((relativePath) => {
|
|
240552
|
+
const content = safeReadText(path59.join(workspaceRoot, ...relativePath.split("/")), 5e4);
|
|
240553
|
+
return content ? ormToolingPatterns.some((pattern) => pattern.test(content)) : false;
|
|
240554
|
+
});
|
|
240555
|
+
for (const relativePath of configCandidateFiles.slice(0, 120)) {
|
|
240556
|
+
const content = safeReadText(path59.join(workspaceRoot, ...relativePath.split("/")), 5e4);
|
|
240557
|
+
if (!content) {
|
|
240558
|
+
continue;
|
|
240559
|
+
}
|
|
240560
|
+
if (/\bNpgsql\.EntityFrameworkCore\.PostgreSQL\b/u.test(content)) {
|
|
240561
|
+
addDb("postgresql", 4, `Found Entity Framework PostgreSQL provider in ${relativePath}`);
|
|
240562
|
+
}
|
|
240563
|
+
if (/\bMicrosoft\.EntityFrameworkCore\.SqlServer\b/u.test(content)) {
|
|
240564
|
+
addDb("sqlserver", 4, `Found Entity Framework SQL Server provider in ${relativePath}`);
|
|
240565
|
+
}
|
|
240566
|
+
if (/\b(Pomelo\.EntityFrameworkCore\.MySql|MySql\.EntityFrameworkCore)\b/u.test(content)) {
|
|
240567
|
+
addDb("mysql", 4, `Found Entity Framework MySQL provider in ${relativePath}`);
|
|
240568
|
+
}
|
|
240569
|
+
if (/\bSqlite\b/u.test(content) && /\bMicrosoft\.EntityFrameworkCore\b/u.test(content)) {
|
|
240570
|
+
addDb("sqlite", 3, `Found Entity Framework SQLite provider in ${relativePath}`);
|
|
240571
|
+
}
|
|
240572
|
+
}
|
|
240573
|
+
if (hasRelationalOrmEvidence) {
|
|
240574
|
+
const primaryScores = Object.entries(scores).filter(([database2, score]) => primaryDatabaseKinds.has(database2) && score > 0);
|
|
240575
|
+
const onlyPrimaryScore = primaryScores.length === 1 ? primaryScores[0] : void 0;
|
|
240576
|
+
if (onlyPrimaryScore) {
|
|
240577
|
+
addDb(onlyPrimaryScore[0], 1, `Found ORM/data-mapper evidence supporting ${onlyPrimaryScore[0]} database selection`);
|
|
240578
|
+
}
|
|
240579
|
+
}
|
|
240532
240580
|
if (directoryExists(directories, "db") || directoryExists(directories, "migrations")) {
|
|
240533
240581
|
addEvidence3(evidence, "Found db/ or migrations/ directory");
|
|
240534
240582
|
}
|
|
@@ -240771,16 +240819,67 @@ function parsePackageJsonSummaries3(workspaceRoot, files) {
|
|
|
240771
240819
|
summaries.push({
|
|
240772
240820
|
path: packageJsonPath,
|
|
240773
240821
|
dependencies,
|
|
240774
|
-
scripts
|
|
240822
|
+
scripts,
|
|
240823
|
+
...typeof parsed.packageManager === "string" ? { packageManager: parsed.packageManager } : {}
|
|
240775
240824
|
});
|
|
240776
240825
|
} catch {
|
|
240777
240826
|
}
|
|
240778
240827
|
}
|
|
240779
240828
|
return summaries;
|
|
240780
240829
|
}
|
|
240830
|
+
function parseComposerJsonSummaries2(workspaceRoot, files) {
|
|
240831
|
+
const composerJsonPaths = files.filter((entry) => entry === "composer.json" || entry.endsWith("/composer.json"));
|
|
240832
|
+
const summaries = [];
|
|
240833
|
+
for (const composerJsonPath of composerJsonPaths) {
|
|
240834
|
+
const absolutePath = path60.join(workspaceRoot, ...composerJsonPath.split("/"));
|
|
240835
|
+
const contents = safeReadText2(absolutePath);
|
|
240836
|
+
if (!contents) {
|
|
240837
|
+
continue;
|
|
240838
|
+
}
|
|
240839
|
+
try {
|
|
240840
|
+
const parsed = JSON.parse(contents);
|
|
240841
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
240842
|
+
for (const fieldName of ["require", "require-dev"]) {
|
|
240843
|
+
const value = parsed[fieldName];
|
|
240844
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
240845
|
+
continue;
|
|
240846
|
+
}
|
|
240847
|
+
for (const name of Object.keys(value)) {
|
|
240848
|
+
dependencies.add(name.toLowerCase());
|
|
240849
|
+
}
|
|
240850
|
+
}
|
|
240851
|
+
summaries.push({ path: composerJsonPath, dependencies });
|
|
240852
|
+
} catch {
|
|
240853
|
+
}
|
|
240854
|
+
}
|
|
240855
|
+
return summaries;
|
|
240856
|
+
}
|
|
240857
|
+
function dependencyExists2(packageJsonSummaries, name) {
|
|
240858
|
+
return packageJsonSummaries.some((summary) => summary.dependencies.has(name));
|
|
240859
|
+
}
|
|
240860
|
+
function composerDependencyExists2(composerJsonSummaries, name) {
|
|
240861
|
+
const normalized = name.toLowerCase();
|
|
240862
|
+
return composerJsonSummaries.some((summary) => summary.dependencies.has(normalized));
|
|
240863
|
+
}
|
|
240864
|
+
function fileExists4(files, name) {
|
|
240865
|
+
return files.some((entry) => entry === name || entry.endsWith(`/${name}`));
|
|
240866
|
+
}
|
|
240867
|
+
function anyFileExists(files, names) {
|
|
240868
|
+
return names.some((name) => fileExists4(files, name));
|
|
240869
|
+
}
|
|
240870
|
+
function anyPackageManager(packageJsonSummaries, pattern) {
|
|
240871
|
+
return packageJsonSummaries.some((summary) => summary.packageManager ? pattern.test(summary.packageManager) : false);
|
|
240872
|
+
}
|
|
240873
|
+
function configTextMatches(workspaceRoot, files, filePattern, textPattern) {
|
|
240874
|
+
return files.filter((entry) => filePattern.test(entry)).slice(0, 80).some((entry) => {
|
|
240875
|
+
const content = safeReadText2(path60.join(workspaceRoot, ...entry.split("/")), 6e4);
|
|
240876
|
+
return content ? textPattern.test(content) : false;
|
|
240877
|
+
});
|
|
240878
|
+
}
|
|
240781
240879
|
function detectToolingHints(workspaceRoot, options) {
|
|
240782
240880
|
const scan = scanWorkspace4(workspaceRoot, 7, options?.ignoreMatcher);
|
|
240783
240881
|
const packageJsonSummaries = parsePackageJsonSummaries3(workspaceRoot, scan.files);
|
|
240882
|
+
const composerJsonSummaries = parseComposerJsonSummaries2(workspaceRoot, scan.files);
|
|
240784
240883
|
const toolingHints = [];
|
|
240785
240884
|
const databaseHints = [];
|
|
240786
240885
|
const evidence = [];
|
|
@@ -240801,6 +240900,15 @@ function detectToolingHints(workspaceRoot, options) {
|
|
|
240801
240900
|
if (scan.files.includes("pnpm-workspace.yaml")) {
|
|
240802
240901
|
addToolingHint("pnpm-workspaces", "Found pnpm-workspace.yaml", 2);
|
|
240803
240902
|
}
|
|
240903
|
+
if (anyFileExists(scan.files, ["bun.lock", "bun.lockb"]) || anyPackageManager(packageJsonSummaries, /^bun@/iu)) {
|
|
240904
|
+
addToolingHint("bun", "Found Bun lockfile or packageManager hint", 3);
|
|
240905
|
+
}
|
|
240906
|
+
if (anyFileExists(scan.files, ["deno.json", "deno.jsonc", "deno.lock"])) {
|
|
240907
|
+
addToolingHint("deno", "Found Deno config or lockfile", 3);
|
|
240908
|
+
}
|
|
240909
|
+
if (scan.files.some((entry) => /(^|\/)vite\.config\.(?:js|mjs|cjs|ts|mts|cts)$/iu.test(entry)) || dependencyExists2(packageJsonSummaries, "vite")) {
|
|
240910
|
+
addToolingHint("vite", "Found Vite config or dependency", 2);
|
|
240911
|
+
}
|
|
240804
240912
|
const prismaSchemaPath = scan.files.find((entry) => entry === "prisma/schema.prisma" || entry.endsWith("/prisma/schema.prisma"));
|
|
240805
240913
|
const hasPrismaDependency = packageJsonSummaries.some((summary) => summary.dependencies.has("prisma"));
|
|
240806
240914
|
const hasPrismaClientDependency = packageJsonSummaries.some((summary) => summary.dependencies.has("@prisma/client"));
|
|
@@ -240828,6 +240936,63 @@ function detectToolingHints(workspaceRoot, options) {
|
|
|
240828
240936
|
if (hasPrismaScriptUsage) {
|
|
240829
240937
|
addToolingHint("prisma", "Found package.json scripts using prisma migrate/generate", 1);
|
|
240830
240938
|
}
|
|
240939
|
+
const ormPackageHints = [
|
|
240940
|
+
{ hint: "drizzle-orm", dependencies: ["drizzle-orm"], note: "Found package.json with drizzle-orm dependency" },
|
|
240941
|
+
{ hint: "typeorm", dependencies: ["typeorm"], note: "Found package.json with typeorm dependency" },
|
|
240942
|
+
{ hint: "sequelize", dependencies: ["sequelize", "@sequelize/core"], note: "Found package.json with Sequelize dependency" },
|
|
240943
|
+
{ hint: "mikro-orm", dependencies: ["@mikro-orm/core", "mikro-orm"], note: "Found package.json with MikroORM dependency" }
|
|
240944
|
+
];
|
|
240945
|
+
for (const ormHint of ormPackageHints) {
|
|
240946
|
+
if (ormHint.dependencies.some((dependency) => dependencyExists2(packageJsonSummaries, dependency))) {
|
|
240947
|
+
addToolingHint(ormHint.hint, ormHint.note, 2);
|
|
240948
|
+
}
|
|
240949
|
+
}
|
|
240950
|
+
if (configTextMatches(
|
|
240951
|
+
workspaceRoot,
|
|
240952
|
+
scan.files,
|
|
240953
|
+
/(^|\/)(pom\.xml|build\.gradle(\.kts)?|[^/]+\.java|[^/]+\.kt)$/iu,
|
|
240954
|
+
/\b(org\.hibernate|hibernate-core|jakarta\.persistence|javax\.persistence|@Entity\b|@PersistenceContext\b)/u
|
|
240955
|
+
)) {
|
|
240956
|
+
addToolingHint("hibernate", "Found Hibernate/JPA dependency or entity evidence", 2);
|
|
240957
|
+
}
|
|
240958
|
+
if (configTextMatches(
|
|
240959
|
+
workspaceRoot,
|
|
240960
|
+
scan.files,
|
|
240961
|
+
/(^|\/)(pom\.xml|build\.gradle(\.kts)?|[^/]+\.java|[^/]+\.kt)$/iu,
|
|
240962
|
+
/\b(spring-boot-starter-data-jpa|spring-data-jpa|JpaRepository|CrudRepository|PagingAndSortingRepository)\b/u
|
|
240963
|
+
)) {
|
|
240964
|
+
addToolingHint("spring-data-jpa", "Found Spring Data JPA dependency or repository evidence", 2);
|
|
240965
|
+
}
|
|
240966
|
+
if (configTextMatches(
|
|
240967
|
+
workspaceRoot,
|
|
240968
|
+
scan.files,
|
|
240969
|
+
/(^|\/)([^/]+\.csproj|packages\.config|[^/]+\.cs)$/iu,
|
|
240970
|
+
/\b(Microsoft\.EntityFrameworkCore|DbContext|DbSet<)/u
|
|
240971
|
+
)) {
|
|
240972
|
+
addToolingHint("entity-framework-core", "Found Entity Framework Core dependency or DbContext evidence", 2);
|
|
240973
|
+
}
|
|
240974
|
+
if (configTextMatches(
|
|
240975
|
+
workspaceRoot,
|
|
240976
|
+
scan.files,
|
|
240977
|
+
/(^|\/)(requirements.*\.txt|pyproject\.toml|setup\.py|Pipfile|poetry\.lock|[^/]+\.py)$/iu,
|
|
240978
|
+
/\b(SQLAlchemy|sqlalchemy)\b/u
|
|
240979
|
+
)) {
|
|
240980
|
+
addToolingHint("sqlalchemy", "Found SQLAlchemy dependency or import evidence", 2);
|
|
240981
|
+
}
|
|
240982
|
+
if (configTextMatches(
|
|
240983
|
+
workspaceRoot,
|
|
240984
|
+
scan.files,
|
|
240985
|
+
/(^|\/)(requirements.*\.txt|pyproject\.toml|setup\.py|Pipfile|poetry\.lock|alembic\.ini|[^/]+\.py)$/iu,
|
|
240986
|
+
/\b(alembic|Alembic)\b/u
|
|
240987
|
+
) || fileExists4(scan.files, "alembic.ini")) {
|
|
240988
|
+
addToolingHint("alembic", "Found Alembic dependency or migration config", 2);
|
|
240989
|
+
}
|
|
240990
|
+
if (composerDependencyExists2(composerJsonSummaries, "doctrine/orm")) {
|
|
240991
|
+
addToolingHint("doctrine-orm", "Found composer.json with doctrine/orm dependency", 2);
|
|
240992
|
+
}
|
|
240993
|
+
if (composerDependencyExists2(composerJsonSummaries, "illuminate/database")) {
|
|
240994
|
+
addToolingHint("eloquent", "Found composer.json with illuminate/database dependency", 2);
|
|
240995
|
+
}
|
|
240831
240996
|
const envFilePath = scan.files.find((entry) => entry === ".env" || entry.endsWith("/.env"));
|
|
240832
240997
|
if (hasPrismaEvidence && envFilePath) {
|
|
240833
240998
|
const envText = safeReadText2(path60.join(workspaceRoot, ...envFilePath.split("/")), 2e4);
|
|
@@ -240973,7 +241138,7 @@ function scanRoot(rootAbsolutePath, options) {
|
|
|
240973
241138
|
directories: unique(directories).sort((left, right) => left.localeCompare(right))
|
|
240974
241139
|
};
|
|
240975
241140
|
}
|
|
240976
|
-
function
|
|
241141
|
+
function fileExists5(files, fileName) {
|
|
240977
241142
|
return files.some((entry) => entry === fileName || entry.endsWith(`/${fileName}`));
|
|
240978
241143
|
}
|
|
240979
241144
|
function hasDirectory(directories, directoryName) {
|
|
@@ -241076,11 +241241,11 @@ function inspectRoot(workspaceRoot, root, ignoreMatcher) {
|
|
|
241076
241241
|
const scan = scanRoot(rootAbsolutePath, { rootRelativePath: root, ignoreMatcher });
|
|
241077
241242
|
const packageJson = readPackageJsonSummary(rootAbsolutePath);
|
|
241078
241243
|
const evidence = [];
|
|
241079
|
-
const hasApiBoundary = hasDirectory(scan.directories, "controllers") || hasDirectory(scan.directories, "routes") || hasDirectory(scan.directories, "router") || hasDirectory(scan.directories, "src/app/api") || scan.files.some((entry) => /(^|\/)(.*controller.*|.*route.*|.*router.*)\.(ts|tsx|js|jsx|py|java|kt)$/iu.test(entry)) ||
|
|
241244
|
+
const hasApiBoundary = hasDirectory(scan.directories, "controllers") || hasDirectory(scan.directories, "routes") || hasDirectory(scan.directories, "router") || hasDirectory(scan.directories, "src/app/api") || scan.files.some((entry) => /(^|\/)(.*controller.*|.*route.*|.*router.*)\.(ts|tsx|js|jsx|py|java|kt)$/iu.test(entry)) || fileExists5(scan.files, "openapi.yaml") || fileExists5(scan.files, "openapi.yml") || fileExists5(scan.files, "swagger.yaml") || fileExists5(scan.files, "swagger.yml") || hasAnyDependency2(packageJson, ["express", "fastify", "@nestjs/core", "koa", "hapi", "fastapi", "@apollo/server"]);
|
|
241080
241245
|
if (hasApiBoundary) {
|
|
241081
241246
|
addEvidence5(evidence, "Detected controllers/routes or backend API framework signals");
|
|
241082
241247
|
}
|
|
241083
|
-
const hasDatabaseAccess = hasDirectory(scan.directories, "db") || hasDirectory(scan.directories, "migrations") ||
|
|
241248
|
+
const hasDatabaseAccess = hasDirectory(scan.directories, "db") || hasDirectory(scan.directories, "migrations") || fileExists5(scan.files, "prisma/schema.prisma") || fileExists5(scan.files, "ormconfig.ts") || fileExists5(scan.files, "ormconfig.js") || fileExists5(scan.files, "ormconfig.json") || fileExists5(scan.files, "knexfile.ts") || fileExists5(scan.files, "knexfile.js") || fileExists5(scan.files, "application.yml") || fileExists5(scan.files, "application.yaml") || fileExists5(scan.files, "application.properties") || hasAnyDependency2(packageJson, [
|
|
241084
241249
|
"@prisma/client",
|
|
241085
241250
|
"prisma",
|
|
241086
241251
|
"typeorm",
|
|
@@ -241103,7 +241268,7 @@ function inspectRoot(workspaceRoot, root, ignoreMatcher) {
|
|
|
241103
241268
|
if (hasServiceEnvConfig) {
|
|
241104
241269
|
addEvidence5(evidence, "Detected service-specific environment configuration");
|
|
241105
241270
|
}
|
|
241106
|
-
const hasOpenApiSpec =
|
|
241271
|
+
const hasOpenApiSpec = fileExists5(scan.files, "openapi.yaml") || fileExists5(scan.files, "openapi.yml") || fileExists5(scan.files, "openapi.json") || fileExists5(scan.files, "contracts/openapi.yaml") || fileExists5(scan.files, "contracts/openapi.yml") || fileExists5(scan.files, "swagger.yaml") || fileExists5(scan.files, "swagger.yml") || fileExists5(scan.files, "swagger.json");
|
|
241107
241272
|
if (hasOpenApiSpec) {
|
|
241108
241273
|
addEvidence5(evidence, "Detected service-specific OpenAPI contract");
|
|
241109
241274
|
}
|
|
@@ -241111,8 +241276,8 @@ function inspectRoot(workspaceRoot, root, ignoreMatcher) {
|
|
|
241111
241276
|
if (hasModulesRoot) {
|
|
241112
241277
|
addEvidence5(evidence, "Detected modules root");
|
|
241113
241278
|
}
|
|
241114
|
-
const hasFrontendSignals =
|
|
241115
|
-
const hasBackendSignals = hasApiBoundary || hasDatabaseAccess ||
|
|
241279
|
+
const hasFrontendSignals = fileExists5(scan.files, "next.config.js") || fileExists5(scan.files, "next.config.ts") || fileExists5(scan.files, "angular.json") || fileExists5(scan.files, "vite.config.ts") || fileExists5(scan.files, "vite.config.js") || hasDirectory(scan.directories, "pages") || hasDirectory(scan.directories, "public") || hasAnyDependency2(packageJson, ["next", "react", "vue", "@angular/core"]);
|
|
241280
|
+
const hasBackendSignals = hasApiBoundary || hasDatabaseAccess || fileExists5(scan.files, "pom.xml") || fileExists5(scan.files, "requirements.txt") || fileExists5(scan.files, "go.mod") || hasDirectory(scan.directories, "src/main/java");
|
|
241116
241281
|
let classification = "unknown";
|
|
241117
241282
|
if ((root.startsWith("packages/") || root.startsWith("libs/")) && !hasApiBoundary) {
|
|
241118
241283
|
classification = "shared_package";
|
|
@@ -241562,6 +241727,9 @@ function addEvidence7(evidence, value) {
|
|
|
241562
241727
|
function uniqueSorted5(values) {
|
|
241563
241728
|
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
241564
241729
|
}
|
|
241730
|
+
function uniqueLimited(values, limit = 5) {
|
|
241731
|
+
return uniqueSorted5(values.filter((value) => value.trim().length > 0)).slice(0, limit);
|
|
241732
|
+
}
|
|
241565
241733
|
function uniqueInOrder2(values) {
|
|
241566
241734
|
const seen = /* @__PURE__ */ new Set();
|
|
241567
241735
|
const result = [];
|
|
@@ -241865,6 +242033,51 @@ function hasAttributableBackendRoot(detection) {
|
|
|
241865
242033
|
function isGenericLibraryStack(stack) {
|
|
241866
242034
|
return stack === "node_typescript" || stack === "node_javascript" || stack === "python" || stack === "go" || stack === "kotlin" || stack === "terraform" || stack === "ansible";
|
|
241867
242035
|
}
|
|
242036
|
+
function hasProjectKindEvidence(detection, patterns) {
|
|
242037
|
+
const evidence = detection.projectKinds?.evidence ?? [];
|
|
242038
|
+
return evidence.some((entry) => patterns.some((pattern) => pattern.test(entry)));
|
|
242039
|
+
}
|
|
242040
|
+
function resolveSingleProjectGenericKinds(detection, selectedAdapterId, selectedHints, evidence) {
|
|
242041
|
+
if (detection.topology.topology !== "single_project" || !selectedAdapterId || getStackAdapterById(selectedAdapterId).category !== "generic-language" || !(selectedHints.includes("backend") && selectedHints.includes("library"))) {
|
|
242042
|
+
return void 0;
|
|
242043
|
+
}
|
|
242044
|
+
const hasFrameworkBackendCandidate = (detection.stacks.candidates ?? []).some((candidate) => {
|
|
242045
|
+
const adapter = getStackAdapterById(candidate.adapterId);
|
|
242046
|
+
return adapter.category === "framework" && adapter.id !== selectedAdapterId;
|
|
242047
|
+
});
|
|
242048
|
+
if (hasFrameworkBackendCandidate) {
|
|
242049
|
+
return void 0;
|
|
242050
|
+
}
|
|
242051
|
+
const hasServiceBackendEvidence = hasProjectKindEvidence(detection, [
|
|
242052
|
+
/requirements\.txt/u,
|
|
242053
|
+
/composer\.json/u,
|
|
242054
|
+
/pom\.xml/u,
|
|
242055
|
+
/build\.gradle/u,
|
|
242056
|
+
/openapi/u,
|
|
242057
|
+
/db\/ or migrations/u,
|
|
242058
|
+
/repository-layer/u
|
|
242059
|
+
]);
|
|
242060
|
+
const hasCliEntrypointEvidence = hasProjectKindEvidence(detection, [
|
|
242061
|
+
/cmd\/ entrypoint/u,
|
|
242062
|
+
/CLI-style entrypoint/u
|
|
242063
|
+
]);
|
|
242064
|
+
const hasLibraryEvidence = hasProjectKindEvidence(detection, [
|
|
242065
|
+
/exports field/u,
|
|
242066
|
+
/main\/module\/types fields/u,
|
|
242067
|
+
/src\/index/u,
|
|
242068
|
+
/pyproject\.toml/u,
|
|
242069
|
+
/setup\.py/u,
|
|
242070
|
+
/go\.mod/u,
|
|
242071
|
+
/Python package __init__\.py/u,
|
|
242072
|
+
/reusable module folders/u
|
|
242073
|
+
]);
|
|
242074
|
+
const selected = hasLibraryEvidence && !hasServiceBackendEvidence ? "library" : "backend";
|
|
242075
|
+
addEvidence7(evidence, `Reduced single-project generic ${selectedAdapterId} project kind to ${selected}`);
|
|
242076
|
+
if (selected === "library" && hasCliEntrypointEvidence) {
|
|
242077
|
+
addEvidence7(evidence, "Treated CLI entrypoint as package tooling rather than backend service evidence");
|
|
242078
|
+
}
|
|
242079
|
+
return [selected];
|
|
242080
|
+
}
|
|
241868
242081
|
function normalizeDetectedBackendStack(value) {
|
|
241869
242082
|
if (value === "springboot") {
|
|
241870
242083
|
return "spring";
|
|
@@ -241941,7 +242154,15 @@ function resolveNormalizedProjectKinds(detection, evidence) {
|
|
|
241941
242154
|
evidence: []
|
|
241942
242155
|
}) ?? [] : []);
|
|
241943
242156
|
let normalizedKinds = [];
|
|
241944
|
-
|
|
242157
|
+
const genericSingleProjectKinds = resolveSingleProjectGenericKinds(
|
|
242158
|
+
detection,
|
|
242159
|
+
selectedAdapterId,
|
|
242160
|
+
selectedHints,
|
|
242161
|
+
evidence
|
|
242162
|
+
);
|
|
242163
|
+
if (genericSingleProjectKinds) {
|
|
242164
|
+
normalizedKinds = genericSingleProjectKinds;
|
|
242165
|
+
} else if (detection.topology.topology === "single_project" && selectedHints.length > 0 && selectedAdapter.category === "framework") {
|
|
241945
242166
|
normalizedKinds = uniqueSortedKinds(selectedHints);
|
|
241946
242167
|
addEvidence7(evidence, `Normalized project kinds from selected adapter '${selectedAdapter.id}' hints`);
|
|
241947
242168
|
} else if (detection.topology.topology === "monorepo" && selectedHints.length > 0) {
|
|
@@ -242237,6 +242458,40 @@ function resolveConfidence2(proposal, detection) {
|
|
|
242237
242458
|
}
|
|
242238
242459
|
return "low";
|
|
242239
242460
|
}
|
|
242461
|
+
function resolveProposalReasons(input2) {
|
|
242462
|
+
const reasons = {};
|
|
242463
|
+
const selectedStackIds = uniqueSorted5(
|
|
242464
|
+
Object.values(input2.stackSelections).filter((stack) => Boolean(stack))
|
|
242465
|
+
);
|
|
242466
|
+
const selectedStackReasons = selectedStackIds.flatMap((stackId) => input2.detection.stacks.candidates?.find((candidate) => candidate.adapterId === stackId)?.evidence ?? []);
|
|
242467
|
+
if (selectedStackReasons.length > 0) {
|
|
242468
|
+
reasons.framework = uniqueLimited(selectedStackReasons);
|
|
242469
|
+
}
|
|
242470
|
+
if (input2.apiStyle && input2.apiStyle !== "unknown") {
|
|
242471
|
+
reasons.api = input2.apiStyle === "none" ? uniqueLimited(input2.detection.apiDbHints?.api.evidence ?? ["No API artifacts detected for current project profile"], 4) : uniqueLimited(input2.detection.apiDbHints?.api.evidence ?? [], 4);
|
|
242472
|
+
}
|
|
242473
|
+
if (input2.database && input2.database !== "unknown") {
|
|
242474
|
+
const databaseEvidence = [
|
|
242475
|
+
...input2.detection.apiDbHints?.database.evidence ?? [],
|
|
242476
|
+
...(input2.detection.toolingHints?.evidence ?? []).filter((entry) => /prisma|datasource|database|orm|jpa|entity framework|sqlalchemy|alembic/iu.test(entry))
|
|
242477
|
+
];
|
|
242478
|
+
reasons.database = input2.database === "none" ? uniqueLimited(databaseEvidence.length > 0 ? databaseEvidence : ["No database artifacts detected for current project profile"], 4) : uniqueLimited(databaseEvidence, 4);
|
|
242479
|
+
}
|
|
242480
|
+
if ((input2.detection.toolingHints?.toolingHints.length ?? 0) > 0) {
|
|
242481
|
+
reasons.tooling = uniqueLimited(input2.detection.toolingHints?.evidence ?? [], 5);
|
|
242482
|
+
}
|
|
242483
|
+
if (input2.architectureStyleEvidence.length > 0) {
|
|
242484
|
+
reasons.architectureStyle = uniqueLimited(input2.architectureStyleEvidence, 5);
|
|
242485
|
+
}
|
|
242486
|
+
if (input2.modulesRoot || (input2.moduleRoots?.length ?? 0) > 0) {
|
|
242487
|
+
reasons.modulesRoot = uniqueLimited([
|
|
242488
|
+
...input2.modulesRoot ? [`Detected modules root at ${input2.modulesRoot}`] : [],
|
|
242489
|
+
...(input2.moduleRoots ?? []).length > 0 ? [`Detected module roots: ${(input2.moduleRoots ?? []).join(", ")}`] : [],
|
|
242490
|
+
...input2.detection.modules?.evidence ?? []
|
|
242491
|
+
], 4);
|
|
242492
|
+
}
|
|
242493
|
+
return Object.keys(reasons).length > 0 ? reasons : void 0;
|
|
242494
|
+
}
|
|
242240
242495
|
function buildArchitectureProposal(detection) {
|
|
242241
242496
|
const evidence = [];
|
|
242242
242497
|
addEvidence7(evidence, `Detected ${detection.topology.topology} topology`);
|
|
@@ -242287,6 +242542,15 @@ function buildArchitectureProposal(detection) {
|
|
|
242287
242542
|
if ((detection.toolingHints?.toolingHints.length ?? 0) > 0) {
|
|
242288
242543
|
addEvidence7(evidence, `Detected tooling hints: ${(detection.toolingHints?.toolingHints ?? []).join(", ")}`);
|
|
242289
242544
|
}
|
|
242545
|
+
const proposalReasons = resolveProposalReasons({
|
|
242546
|
+
detection,
|
|
242547
|
+
stackSelections,
|
|
242548
|
+
architectureStyleEvidence: architectureStyleDecision.architectureStyleEvidence,
|
|
242549
|
+
modulesRoot,
|
|
242550
|
+
moduleRoots,
|
|
242551
|
+
apiStyle,
|
|
242552
|
+
database
|
|
242553
|
+
});
|
|
242290
242554
|
const proposalCore = {
|
|
242291
242555
|
...projectKinds.length > 0 ? { projectKinds } : {},
|
|
242292
242556
|
...architectureStyle ? { architectureStyle } : {},
|
|
@@ -242306,7 +242570,8 @@ function buildArchitectureProposal(detection) {
|
|
|
242306
242570
|
...modules.length > 0 ? { modules: uniqueSorted5(modules) } : {},
|
|
242307
242571
|
...moduleCandidates.length > 0 ? { moduleCandidates } : {},
|
|
242308
242572
|
...moduleCandidateGroups ? { moduleCandidateGroups } : {},
|
|
242309
|
-
...(detection.detectedComponents?.length ?? 0) > 0 ? { detectedComponents: detection.detectedComponents } : {}
|
|
242573
|
+
...(detection.detectedComponents?.length ?? 0) > 0 ? { detectedComponents: detection.detectedComponents } : {},
|
|
242574
|
+
...proposalReasons ? { proposalReasons } : {}
|
|
242310
242575
|
};
|
|
242311
242576
|
const confidence = resolveConfidence2(proposalCore, detection);
|
|
242312
242577
|
if (architectureStyle) {
|
|
@@ -245377,8 +245642,6 @@ function normalizeCanonicalStackId(value) {
|
|
|
245377
245642
|
"dotnet",
|
|
245378
245643
|
"ruby_rails",
|
|
245379
245644
|
"kotlin",
|
|
245380
|
-
"kotlin_spring",
|
|
245381
|
-
"rust",
|
|
245382
245645
|
"react",
|
|
245383
245646
|
"angular",
|
|
245384
245647
|
"vue",
|
|
@@ -245510,54 +245773,72 @@ function formatModuleRootsSummary(proposal, inputs) {
|
|
|
245510
245773
|
function renderSmartInitDetectionSummary(detection, inputs) {
|
|
245511
245774
|
const proposal = detection.proposal ?? buildArchitectureProposal(detection);
|
|
245512
245775
|
const formatStack = (stack) => canonicalStackLabels[stack] ?? stack.replace(/_/gu, " ");
|
|
245776
|
+
const formatToken = (value) => value.replace(/_/gu, " ");
|
|
245777
|
+
const formatList = (values, formatter = formatToken) => {
|
|
245778
|
+
const unique2 = [...new Set((values ?? []).filter((value) => typeof value === "string" && value.length > 0))];
|
|
245779
|
+
return unique2.length > 0 ? unique2.map(formatter).join(", ") : "(none)";
|
|
245780
|
+
};
|
|
245781
|
+
const selectedStacks = [
|
|
245782
|
+
inputs.stacks.backendStack,
|
|
245783
|
+
inputs.stacks.frontendStack,
|
|
245784
|
+
inputs.stacks.libraryStack
|
|
245785
|
+
].filter((stack) => typeof stack === "string" && stack.length > 0);
|
|
245786
|
+
const selectedStackIds = new Set(selectedStacks);
|
|
245513
245787
|
const backendStacks = [...new Set(detection.stacks.backendStacks ?? [])];
|
|
245788
|
+
const frontendStacks = [...new Set(detection.stacks.frontendStacks ?? [])];
|
|
245514
245789
|
const libraryStacks = [
|
|
245515
245790
|
...new Set(
|
|
245516
245791
|
(proposal.detectedComponents ?? detection.detectedComponents ?? []).filter((component) => component.kind === "library" || component.kind === "data").map((component) => component.stack).filter((stack) => typeof stack === "string" && stack.length > 0)
|
|
245517
245792
|
)
|
|
245518
245793
|
];
|
|
245794
|
+
const candidateStacks = [
|
|
245795
|
+
...backendStacks,
|
|
245796
|
+
...frontendStacks,
|
|
245797
|
+
...libraryStacks,
|
|
245798
|
+
...detection.stacks.stacks ?? []
|
|
245799
|
+
].filter(
|
|
245800
|
+
(stack, index, values) => typeof stack === "string" && stack.length > 0 && !selectedStackIds.has(stack) && values.indexOf(stack) === index
|
|
245801
|
+
);
|
|
245802
|
+
const toolingHints = proposal.toolingHints ?? detection.toolingHints?.toolingHints ?? [];
|
|
245803
|
+
const databaseHints = proposal.databaseHints ?? detection.apiDbHints?.database?.databaseHints ?? [];
|
|
245804
|
+
const apiHint = proposal.apiStyle ?? inputs.apiStyle;
|
|
245805
|
+
const selectedModules = inputs.modules ?? proposal.modules ?? [];
|
|
245806
|
+
const architectureCandidates = (proposal.moduleCandidates ?? []).filter((candidate) => candidate.classification === "architecture-module").map((candidate) => candidate.name);
|
|
245807
|
+
const ignoredCandidates = (proposal.moduleCandidates ?? []).filter((candidate) => candidate.classification === "technical-layer" || candidate.classification === "shared-infrastructure").map((candidate) => `${candidate.name} (${formatToken(candidate.classification)})`);
|
|
245519
245808
|
const lines = [];
|
|
245520
|
-
lines.push("
|
|
245809
|
+
lines.push("Detected Architecture Signals");
|
|
245521
245810
|
lines.push("");
|
|
245522
|
-
lines.push(`
|
|
245523
|
-
lines.push(`
|
|
245524
|
-
lines.push(`
|
|
245525
|
-
lines.push(
|
|
245526
|
-
|
|
245527
|
-
)
|
|
245528
|
-
|
|
245529
|
-
|
|
245530
|
-
|
|
245531
|
-
|
|
245532
|
-
|
|
245533
|
-
|
|
245534
|
-
if (
|
|
245535
|
-
|
|
245536
|
-
|
|
245537
|
-
|
|
245538
|
-
|
|
245539
|
-
|
|
245540
|
-
|
|
245541
|
-
|
|
245542
|
-
|
|
245543
|
-
|
|
245544
|
-
|
|
245545
|
-
|
|
245546
|
-
|
|
245547
|
-
|
|
245548
|
-
|
|
245549
|
-
if (candidates.length > 0) {
|
|
245550
|
-
lines.push(`${labels[key]}: ${candidates.map((candidate) => candidate.name).join(", ")}`);
|
|
245551
|
-
}
|
|
245552
|
-
}
|
|
245553
|
-
lines.push(`Selected architecture modules: ${inputs.modules?.join(", ") || "(none)"}`);
|
|
245811
|
+
lines.push(`Primary stack: ${formatList(selectedStacks, formatStack)}`);
|
|
245812
|
+
lines.push(`Additional stack signals: ${formatList(candidateStacks, formatStack)}`);
|
|
245813
|
+
lines.push(`Project kind: ${formatList(proposal.projectKinds ?? detection.projectKinds?.kinds, formatToken)}`);
|
|
245814
|
+
lines.push(`Repository topology: ${formatToken(detection.topology.topology)}`);
|
|
245815
|
+
lines.push(`Modules root: ${formatModuleRootsSummary(proposal, inputs)}`);
|
|
245816
|
+
if (selectedModules.length > 0) {
|
|
245817
|
+
lines.push(`Selected architecture modules: ${formatList(selectedModules)}`);
|
|
245818
|
+
} else if (architectureCandidates.length > 0) {
|
|
245819
|
+
lines.push(`Architecture module candidates: ${formatList(architectureCandidates)}`);
|
|
245820
|
+
} else {
|
|
245821
|
+
lines.push("Architecture modules: none confidently detected");
|
|
245822
|
+
}
|
|
245823
|
+
if (ignoredCandidates.length > 0) {
|
|
245824
|
+
lines.push(`Ignored technical/shared folders: ${formatList(ignoredCandidates)}`);
|
|
245825
|
+
}
|
|
245826
|
+
lines.push(`API hints: ${apiHint && apiHint !== "unknown" ? formatToken(apiHint) : "(none)"}`);
|
|
245827
|
+
lines.push(`Database hints: ${formatList(databaseHints, formatToken)}`);
|
|
245828
|
+
lines.push(`Tooling/data hints: ${formatList(toolingHints, formatToken)}`);
|
|
245829
|
+
lines.push(`Detection confidence: ${proposal.confidence}`);
|
|
245830
|
+
const proposalReasons = proposal.proposalReasons ?? {};
|
|
245831
|
+
const reasonEntries = [
|
|
245832
|
+
...proposalReasons.framework?.length ? [`Framework: ${formatList(proposalReasons.framework.slice(0, 2))}`] : [],
|
|
245833
|
+
...proposalReasons.api?.length ? [`API: ${formatList(proposalReasons.api.slice(0, 2))}`] : [],
|
|
245834
|
+
...proposalReasons.database?.length ? [`Database: ${formatList(proposalReasons.database.slice(0, 2))}`] : []
|
|
245835
|
+
];
|
|
245836
|
+
if (reasonEntries.length > 0) {
|
|
245837
|
+
lines.push(`Reasons: ${reasonEntries.join(" | ")}`);
|
|
245554
245838
|
}
|
|
245555
|
-
lines.push(`
|
|
245556
|
-
lines.push(`Database: ${proposal.database ?? inputs.database}`);
|
|
245557
|
-
lines.push(`Components: ${Array.isArray(proposal.components) && proposal.components.length > 0 ? proposal.components.map((component) => `${component.id ?? "(unnamed)"}:${component.path ?? "(root)"}`).join(", ") : "(none)"}`);
|
|
245558
|
-
lines.push(`Resources: ${Array.isArray(proposal.resources) && proposal.resources.length > 0 ? proposal.resources.map((resource) => `${resource.id ?? "(unnamed)"}:${resource.type ?? "resource"}:${resource.provider ?? "unknown"}`).join(", ") : "(none)"}`);
|
|
245839
|
+
lines.push(`Evidence: ${formatList((proposal.evidence ?? []).slice(0, 5))}`);
|
|
245559
245840
|
lines.push("");
|
|
245560
|
-
lines.push("Detected values are preselected. You can edit
|
|
245841
|
+
lines.push("Detected values are preselected. Tooling/data hints are supporting signals, not primary stack adapters. You can edit everything before anything is written.");
|
|
245561
245842
|
return lines.join("\n");
|
|
245562
245843
|
}
|
|
245563
245844
|
function renderArchitectureInputsSummary(inputs) {
|
|
@@ -245693,7 +245974,7 @@ async function editModulesAndApi(rl, inputs) {
|
|
|
245693
245974
|
return authStyle;
|
|
245694
245975
|
}
|
|
245695
245976
|
inputs.authStyle = authStyle;
|
|
245696
|
-
const database = await askInitValue(rl, "
|
|
245977
|
+
const database = await askInitValue(rl, "Primary database (none, postgresql, mysql)", inputs.database, isValidDatabaseChoice);
|
|
245697
245978
|
if (database === "back" || database === "cancel") {
|
|
245698
245979
|
return database;
|
|
245699
245980
|
}
|
|
@@ -249444,7 +249725,7 @@ async function runArchpilotCli() {
|
|
|
249444
249725
|
if (workspaceRoot !== invocationDirectory) {
|
|
249445
249726
|
process.chdir(workspaceRoot);
|
|
249446
249727
|
}
|
|
249447
|
-
} else {
|
|
249728
|
+
} else if (command !== "init") {
|
|
249448
249729
|
const discoveredWorkspaceRoot = findArchpilotWorkspaceRoot(invocationDirectory);
|
|
249449
249730
|
if (discoveredWorkspaceRoot && discoveredWorkspaceRoot !== invocationDirectory) {
|
|
249450
249731
|
process.chdir(discoveredWorkspaceRoot);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archpilotlabs/archpilot",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Executable architecture governance CLI",
|
|
3
|
+
"version": "0.0.19",
|
|
4
|
+
"description": "Executable architecture governance CLI for Smart Init, validation, drift detection, and CI.",
|
|
5
5
|
"homepage": "https://archpilot.org",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://archpilot.org/contact"
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"dependency-validation",
|
|
23
23
|
"adr",
|
|
24
24
|
"ci",
|
|
25
|
-
"developer-tools"
|
|
25
|
+
"developer-tools",
|
|
26
|
+
"smart-init"
|
|
26
27
|
],
|
|
27
28
|
"scripts": {
|
|
28
29
|
"build": "npm run compile",
|