proteum 2.5.5 → 2.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/project/AGENTS.md +0 -1
- package/agents/project/root/AGENTS.md +0 -1
- package/cli/commands/configure.ts +63 -4
- package/cli/index.ts +24 -18
- package/cli/presentation/commands.ts +12 -7
- package/cli/runtime/monorepoCommands.ts +625 -0
- package/cli/runtime/worktreeBootstrap.ts +163 -0
- package/cli/utils/agents.ts +211 -43
- package/package.json +1 -1
- package/tests/agents-utils.test.cjs +165 -5
- package/tests/cli-mcp-command.test.cjs +60 -11
- package/tests/worktree-bootstrap.test.cjs +98 -0
|
@@ -2,6 +2,7 @@ import cp from 'child_process';
|
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
4
|
import path from 'path';
|
|
5
|
+
import { findProteumAppRootsUnder } from '../utils/appRoots';
|
|
5
6
|
|
|
6
7
|
/*----------------------------------
|
|
7
8
|
- TYPES
|
|
@@ -124,6 +125,30 @@ export type TRunWorktreeBootstrapCreateOptions = TRunWorktreeBootstrapInitOption
|
|
|
124
125
|
targetRepoRoot: string;
|
|
125
126
|
};
|
|
126
127
|
|
|
128
|
+
export type TMonorepoWorktreeBootstrapAppResult = {
|
|
129
|
+
appRoot: string;
|
|
130
|
+
error?: string;
|
|
131
|
+
markerFilepath?: string;
|
|
132
|
+
ok: boolean;
|
|
133
|
+
refresh?: string;
|
|
134
|
+
relativeAppRoot: string;
|
|
135
|
+
runtimeStatus?: string;
|
|
136
|
+
sourceAppRoot?: string;
|
|
137
|
+
status?: TWorktreeBootstrapStatus;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export type TRunMonorepoWorktreeBootstrapInitOptions = Omit<TRunWorktreeBootstrapInitOptions, 'appRoot' | 'source'> & {
|
|
141
|
+
appRoots?: string[];
|
|
142
|
+
monorepoRoot: string;
|
|
143
|
+
source?: string;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type TRunMonorepoWorktreeBootstrapCreateOptions = TRunMonorepoWorktreeBootstrapInitOptions & {
|
|
147
|
+
base?: string;
|
|
148
|
+
branch: string;
|
|
149
|
+
targetRepoRoot: string;
|
|
150
|
+
};
|
|
151
|
+
|
|
127
152
|
/*----------------------------------
|
|
128
153
|
- CONSTANTS
|
|
129
154
|
----------------------------------*/
|
|
@@ -139,6 +164,16 @@ const codexWorktreeSegment = `${path.sep}.codex${path.sep}worktrees${path.sep}`;
|
|
|
139
164
|
|
|
140
165
|
const normalizePath = (value: string) => path.normalize(path.resolve(value));
|
|
141
166
|
|
|
167
|
+
const normalizeExistingPath = (value: string) => {
|
|
168
|
+
const normalized = normalizePath(value);
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
return path.normalize(fs.realpathSync(normalized));
|
|
172
|
+
} catch {
|
|
173
|
+
return normalized;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
142
177
|
const isTruthyEnv = (value: string | undefined) => value === '1' || value === 'true' || value === 'yes';
|
|
143
178
|
|
|
144
179
|
const nowIso = () => new Date().toISOString();
|
|
@@ -699,3 +734,131 @@ export const runWorktreeBootstrapCreate = async ({
|
|
|
699
734
|
worktreeBootstrap: initResult,
|
|
700
735
|
};
|
|
701
736
|
};
|
|
737
|
+
|
|
738
|
+
const findBootstrapInstallRoot = (appRoot: string) => {
|
|
739
|
+
const packageLockFilepath = findNearestExistingPath(appRoot, 'package-lock.json');
|
|
740
|
+
return packageLockFilepath ? path.dirname(packageLockFilepath) : normalizePath(appRoot);
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
const createSharedDependencyRunner = (runDependencies: (appRoot: string) => Promise<void> = runNpmInstall) => {
|
|
744
|
+
const completedInstallRoots = new Set<string>();
|
|
745
|
+
|
|
746
|
+
return async (appRoot: string) => {
|
|
747
|
+
const installRoot = findBootstrapInstallRoot(appRoot);
|
|
748
|
+
if (completedInstallRoots.has(installRoot)) return;
|
|
749
|
+
|
|
750
|
+
completedInstallRoots.add(installRoot);
|
|
751
|
+
await runDependencies(installRoot);
|
|
752
|
+
};
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const resolveSourceAppRoot = ({
|
|
756
|
+
relativeAppRoot,
|
|
757
|
+
sourceRoot,
|
|
758
|
+
}: {
|
|
759
|
+
relativeAppRoot: string;
|
|
760
|
+
sourceRoot?: string;
|
|
761
|
+
}) => {
|
|
762
|
+
if (!sourceRoot) return undefined;
|
|
763
|
+
|
|
764
|
+
const normalizedSourceRoot = normalizeExistingPath(sourceRoot);
|
|
765
|
+
const sourceAppRoot = path.join(normalizedSourceRoot, relativeAppRoot);
|
|
766
|
+
|
|
767
|
+
if (fs.existsSync(sourceAppRoot)) return sourceAppRoot;
|
|
768
|
+
|
|
769
|
+
return undefined;
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
export const runMonorepoWorktreeBootstrapInit = async ({
|
|
773
|
+
appRoots,
|
|
774
|
+
monorepoRoot,
|
|
775
|
+
runDependencies,
|
|
776
|
+
source,
|
|
777
|
+
...initOptions
|
|
778
|
+
}: TRunMonorepoWorktreeBootstrapInitOptions) => {
|
|
779
|
+
const normalizedMonorepoRoot = normalizeExistingPath(monorepoRoot);
|
|
780
|
+
const targetAppRoots = (appRoots || findProteumAppRootsUnder(normalizedMonorepoRoot))
|
|
781
|
+
.map((appRoot) => normalizeExistingPath(appRoot))
|
|
782
|
+
.sort((left, right) => left.localeCompare(right));
|
|
783
|
+
const sharedDependencyRunner = createSharedDependencyRunner(runDependencies);
|
|
784
|
+
const apps: TMonorepoWorktreeBootstrapAppResult[] = [];
|
|
785
|
+
|
|
786
|
+
if (targetAppRoots.length === 0) throw new Error(`No Proteum app roots were found under ${normalizedMonorepoRoot}.`);
|
|
787
|
+
|
|
788
|
+
for (const appRoot of targetAppRoots) {
|
|
789
|
+
const relativeAppRoot = path.relative(normalizedMonorepoRoot, appRoot) || '.';
|
|
790
|
+
const sourceAppRoot = resolveSourceAppRoot({
|
|
791
|
+
relativeAppRoot,
|
|
792
|
+
sourceRoot: source,
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
try {
|
|
796
|
+
const result = await runWorktreeBootstrapInit({
|
|
797
|
+
...initOptions,
|
|
798
|
+
appRoot,
|
|
799
|
+
runDependencies: sharedDependencyRunner,
|
|
800
|
+
source: sourceAppRoot,
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
apps.push({
|
|
804
|
+
appRoot,
|
|
805
|
+
markerFilepath: result.markerFilepath,
|
|
806
|
+
ok: true,
|
|
807
|
+
refresh: result.refresh,
|
|
808
|
+
relativeAppRoot,
|
|
809
|
+
runtimeStatus: result.runtimeStatus,
|
|
810
|
+
sourceAppRoot,
|
|
811
|
+
status: result.status,
|
|
812
|
+
});
|
|
813
|
+
} catch (error) {
|
|
814
|
+
apps.push({
|
|
815
|
+
appRoot,
|
|
816
|
+
error: error instanceof Error ? error.message : String(error),
|
|
817
|
+
ok: false,
|
|
818
|
+
relativeAppRoot,
|
|
819
|
+
sourceAppRoot,
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
return {
|
|
825
|
+
appRoots: targetAppRoots,
|
|
826
|
+
apps,
|
|
827
|
+
failed: apps.filter((entry) => !entry.ok).length,
|
|
828
|
+
monorepoRoot: normalizedMonorepoRoot,
|
|
829
|
+
ok: apps.every((entry) => entry.ok),
|
|
830
|
+
sourceRoot: source ? normalizeExistingPath(source) : undefined,
|
|
831
|
+
};
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
export const runMonorepoWorktreeBootstrapCreate = async ({
|
|
835
|
+
base = 'HEAD',
|
|
836
|
+
branch,
|
|
837
|
+
source,
|
|
838
|
+
targetRepoRoot,
|
|
839
|
+
...initOptions
|
|
840
|
+
}: TRunMonorepoWorktreeBootstrapCreateOptions) => {
|
|
841
|
+
if (!branch.trim()) throw new Error('worktree create requires --branch <branch>.');
|
|
842
|
+
if (!targetRepoRoot.trim()) throw new Error('worktree create requires <target-repo-root>.');
|
|
843
|
+
|
|
844
|
+
const normalizedSourceRoot = normalizeExistingPath(source || initOptions.monorepoRoot);
|
|
845
|
+
const normalizedTargetRepoRoot = path.resolve(targetRepoRoot);
|
|
846
|
+
const sourceRepoRoot = await findGitRepoRoot(normalizedSourceRoot);
|
|
847
|
+
|
|
848
|
+
await runCapture('git', ['worktree', 'add', '-b', branch, normalizedTargetRepoRoot, base], { cwd: sourceRepoRoot });
|
|
849
|
+
|
|
850
|
+
const initResult = await runMonorepoWorktreeBootstrapInit({
|
|
851
|
+
...initOptions,
|
|
852
|
+
monorepoRoot: normalizedTargetRepoRoot,
|
|
853
|
+
refresh: true,
|
|
854
|
+
source: normalizedSourceRoot,
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
return {
|
|
858
|
+
branch,
|
|
859
|
+
sourceMonorepoRoot: normalizedSourceRoot,
|
|
860
|
+
sourceRepoRoot,
|
|
861
|
+
targetRepoRoot: normalizedTargetRepoRoot,
|
|
862
|
+
worktreeBootstrap: initResult,
|
|
863
|
+
};
|
|
864
|
+
};
|
package/cli/utils/agents.ts
CHANGED
|
@@ -12,14 +12,31 @@ import { createStartDevCommand, findProteumAppRootsUnder, readProteumAppRootSumm
|
|
|
12
12
|
- TYPES
|
|
13
13
|
----------------------------------*/
|
|
14
14
|
|
|
15
|
-
type TProjectInstructionArgs = {
|
|
15
|
+
type TProjectInstructionArgs = {
|
|
16
|
+
appRoot?: string;
|
|
17
|
+
coreRoot: string;
|
|
18
|
+
includeMonorepoRegistry?: boolean;
|
|
19
|
+
instructionRoot: string;
|
|
20
|
+
monorepoRegistryCurrentAppRoot?: string;
|
|
21
|
+
monorepoRoot?: string;
|
|
22
|
+
};
|
|
16
23
|
type TConfigureProjectAgentInstructionsArgs = {
|
|
17
24
|
appRoot: string;
|
|
18
25
|
coreRoot: string;
|
|
19
26
|
dryRun?: boolean;
|
|
27
|
+
includeAppInstructions?: boolean;
|
|
28
|
+
includeRootInstructions?: boolean;
|
|
29
|
+
markCurrentAppInMonorepoRegistry?: boolean;
|
|
20
30
|
monorepoRoot?: string;
|
|
21
31
|
overwriteBlockedPaths?: string[];
|
|
22
32
|
};
|
|
33
|
+
type TConfigureMonorepoProjectAgentInstructionsArgs = {
|
|
34
|
+
appRoots: string[];
|
|
35
|
+
coreRoot: string;
|
|
36
|
+
dryRun?: boolean;
|
|
37
|
+
monorepoRoot: string;
|
|
38
|
+
overwriteBlockedPaths?: string[];
|
|
39
|
+
};
|
|
23
40
|
|
|
24
41
|
type TAgentInstructionDefinition = {
|
|
25
42
|
projectPath: string;
|
|
@@ -49,6 +66,12 @@ export type TConfigureProjectAgentInstructionsResult = {
|
|
|
49
66
|
updatedGitignores: string[];
|
|
50
67
|
};
|
|
51
68
|
|
|
69
|
+
export type TConfigureMonorepoProjectAgentInstructionsResult = Omit<TConfigureProjectAgentInstructionsResult, 'appRoot'> & {
|
|
70
|
+
appRoots: string[];
|
|
71
|
+
monorepoRoot: string;
|
|
72
|
+
mode: 'monorepo';
|
|
73
|
+
};
|
|
74
|
+
|
|
52
75
|
/*----------------------------------
|
|
53
76
|
- CONSTANTS
|
|
54
77
|
----------------------------------*/
|
|
@@ -84,6 +107,14 @@ const sharedTestAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
|
|
|
84
107
|
{ projectPath: path.join('tests', 'e2e', 'REAL_WORLD_JOURNEY_TESTS.md'), ensureParentDir: true, content: 'source' },
|
|
85
108
|
];
|
|
86
109
|
|
|
110
|
+
const projectInstructionSourceMapEntries = [
|
|
111
|
+
{ label: 'Root contract fallback', projectPath: 'AGENTS.md' },
|
|
112
|
+
{ label: 'Documentation fallback', projectPath: 'DOCUMENTATION.md' },
|
|
113
|
+
{ label: 'Diagnostics fallback', projectPath: 'diagnostics.md' },
|
|
114
|
+
{ label: 'Optimization fallback', projectPath: 'optimizations.md' },
|
|
115
|
+
{ label: 'Coding style fallback', projectPath: 'CODING_STYLE.md' },
|
|
116
|
+
];
|
|
117
|
+
|
|
87
118
|
const standaloneAppAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
|
|
88
119
|
{ projectPath: 'AGENTS.md', content: 'router' },
|
|
89
120
|
...sharedRootDocumentInstructionDefinitions,
|
|
@@ -115,6 +146,9 @@ export function configureProjectAgentInstructions({
|
|
|
115
146
|
appRoot,
|
|
116
147
|
coreRoot,
|
|
117
148
|
dryRun = false,
|
|
149
|
+
includeAppInstructions = true,
|
|
150
|
+
includeRootInstructions = true,
|
|
151
|
+
markCurrentAppInMonorepoRegistry = true,
|
|
118
152
|
monorepoRoot,
|
|
119
153
|
overwriteBlockedPaths = [],
|
|
120
154
|
}: TConfigureProjectAgentInstructionsArgs): TConfigureProjectAgentInstructionsResult {
|
|
@@ -139,6 +173,7 @@ export function configureProjectAgentInstructions({
|
|
|
139
173
|
const appEmbeddedInstructions = renderEmbeddedProjectInstructions({
|
|
140
174
|
appRoot: normalizedAppRoot,
|
|
141
175
|
coreRoot,
|
|
176
|
+
instructionRoot: normalizedAppRoot,
|
|
142
177
|
monorepoRoot: normalizedMonorepoRoot,
|
|
143
178
|
});
|
|
144
179
|
const rootEmbeddedInstructions =
|
|
@@ -147,11 +182,13 @@ export function configureProjectAgentInstructions({
|
|
|
147
182
|
appRoot: normalizedAppRoot,
|
|
148
183
|
coreRoot,
|
|
149
184
|
includeMonorepoRegistry: true,
|
|
185
|
+
instructionRoot: normalizedMonorepoRoot,
|
|
186
|
+
monorepoRegistryCurrentAppRoot: markCurrentAppInMonorepoRegistry ? normalizedAppRoot : undefined,
|
|
150
187
|
monorepoRoot: normalizedMonorepoRoot,
|
|
151
188
|
})
|
|
152
189
|
: appEmbeddedInstructions;
|
|
153
190
|
|
|
154
|
-
if (mode === 'monorepo' && normalizedMonorepoRoot) {
|
|
191
|
+
if (includeRootInstructions && mode === 'monorepo' && normalizedMonorepoRoot) {
|
|
155
192
|
result.monorepoRoot = normalizedMonorepoRoot;
|
|
156
193
|
|
|
157
194
|
const rootInstructions = getRootAgentInstructionDefinitions();
|
|
@@ -172,52 +209,127 @@ export function configureProjectAgentInstructions({
|
|
|
172
209
|
result.updatedGitignores.push(path.join(normalizedMonorepoRoot, '.gitignore'));
|
|
173
210
|
}
|
|
174
211
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
appInstructions,
|
|
179
|
-
'[agents]',
|
|
180
|
-
path.join(coreRoot, 'agents', 'project'),
|
|
181
|
-
appEmbeddedInstructions,
|
|
182
|
-
{
|
|
183
|
-
dryRun,
|
|
184
|
-
overwriteBlockedPaths: normalizedOverwriteBlockedPaths,
|
|
185
|
-
},
|
|
186
|
-
);
|
|
187
|
-
mergeInstructionResults(result, appFiles, normalizedAppRoot);
|
|
188
|
-
|
|
189
|
-
if (mode === 'monorepo') {
|
|
190
|
-
const retiredAppRootFiles = removeManagedInstructionFiles(
|
|
212
|
+
if (includeAppInstructions) {
|
|
213
|
+
const appInstructions = getAppAgentInstructionDefinitions({ mode });
|
|
214
|
+
const appFiles = ensureInstructionFiles(
|
|
191
215
|
normalizedAppRoot,
|
|
192
|
-
|
|
216
|
+
appInstructions,
|
|
193
217
|
'[agents]',
|
|
194
218
|
path.join(coreRoot, 'agents', 'project'),
|
|
219
|
+
appEmbeddedInstructions,
|
|
195
220
|
{
|
|
196
221
|
dryRun,
|
|
222
|
+
overwriteBlockedPaths: normalizedOverwriteBlockedPaths,
|
|
197
223
|
},
|
|
198
224
|
);
|
|
199
|
-
mergeInstructionResults(result,
|
|
200
|
-
|
|
225
|
+
mergeInstructionResults(result, appFiles, normalizedAppRoot);
|
|
226
|
+
|
|
227
|
+
if (mode === 'monorepo') {
|
|
228
|
+
const retiredAppRootFiles = removeManagedInstructionFiles(
|
|
229
|
+
normalizedAppRoot,
|
|
230
|
+
[...sharedRootDocumentInstructionDefinitions, ...sharedTestAgentInstructionDefinitions],
|
|
231
|
+
'[agents]',
|
|
232
|
+
path.join(coreRoot, 'agents', 'project'),
|
|
233
|
+
{
|
|
234
|
+
dryRun,
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
mergeInstructionResults(result, retiredAppRootFiles, normalizedAppRoot);
|
|
238
|
+
}
|
|
201
239
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
240
|
+
const appGitignoreCleanupInstructions =
|
|
241
|
+
mode === 'monorepo'
|
|
242
|
+
? [...appInstructions, ...sharedRootDocumentInstructionDefinitions, ...sharedTestAgentInstructionDefinitions]
|
|
243
|
+
: appInstructions;
|
|
244
|
+
|
|
245
|
+
if (
|
|
246
|
+
!dryRun &&
|
|
247
|
+
removeInstructionGitignoreEntries({
|
|
248
|
+
rootDir: normalizedAppRoot,
|
|
249
|
+
instructionDefinitions: appGitignoreCleanupInstructions,
|
|
250
|
+
})
|
|
251
|
+
)
|
|
252
|
+
result.updatedGitignores.push(path.join(normalizedAppRoot, '.gitignore'));
|
|
253
|
+
}
|
|
215
254
|
|
|
216
255
|
return result;
|
|
217
256
|
}
|
|
218
257
|
|
|
219
258
|
export const configureProjectAgentSymlinks = configureProjectAgentInstructions;
|
|
220
259
|
|
|
260
|
+
export function configureMonorepoProjectAgentInstructions({
|
|
261
|
+
appRoots,
|
|
262
|
+
coreRoot,
|
|
263
|
+
dryRun = false,
|
|
264
|
+
monorepoRoot,
|
|
265
|
+
overwriteBlockedPaths = [],
|
|
266
|
+
}: TConfigureMonorepoProjectAgentInstructionsArgs): TConfigureMonorepoProjectAgentInstructionsResult {
|
|
267
|
+
const normalizedMonorepoRoot = path.resolve(monorepoRoot);
|
|
268
|
+
const normalizedAppRoots = [...new Set(appRoots.map((appRoot) => path.resolve(appRoot)))].sort((left, right) =>
|
|
269
|
+
left.localeCompare(right),
|
|
270
|
+
);
|
|
271
|
+
const [firstAppRoot] = normalizedAppRoots;
|
|
272
|
+
|
|
273
|
+
if (!firstAppRoot) throw new Error('No Proteum app roots were found under the monorepo root.');
|
|
274
|
+
|
|
275
|
+
const result: TConfigureMonorepoProjectAgentInstructionsResult = {
|
|
276
|
+
appRoots: normalizedAppRoots,
|
|
277
|
+
blocked: [],
|
|
278
|
+
created: [],
|
|
279
|
+
mode: 'monorepo',
|
|
280
|
+
monorepoRoot: normalizedMonorepoRoot,
|
|
281
|
+
overwritten: [],
|
|
282
|
+
removed: [],
|
|
283
|
+
skipped: [],
|
|
284
|
+
updated: [],
|
|
285
|
+
updatedGitignores: [],
|
|
286
|
+
};
|
|
287
|
+
const mergeProjectResult = (next: TConfigureProjectAgentInstructionsResult) => {
|
|
288
|
+
result.blocked = [...new Set([...result.blocked, ...next.blocked])];
|
|
289
|
+
result.created = [...new Set([...result.created, ...next.created])];
|
|
290
|
+
result.overwritten = [...new Set([...result.overwritten, ...next.overwritten])];
|
|
291
|
+
result.removed = [...new Set([...result.removed, ...next.removed])];
|
|
292
|
+
result.skipped = [...new Set([...result.skipped, ...next.skipped])];
|
|
293
|
+
result.updated = [...new Set([...result.updated, ...next.updated])];
|
|
294
|
+
result.updatedGitignores = [...new Set([...result.updatedGitignores, ...next.updatedGitignores])];
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
mergeProjectResult(
|
|
298
|
+
configureProjectAgentInstructions({
|
|
299
|
+
appRoot: firstAppRoot,
|
|
300
|
+
coreRoot,
|
|
301
|
+
dryRun,
|
|
302
|
+
includeAppInstructions: false,
|
|
303
|
+
markCurrentAppInMonorepoRegistry: false,
|
|
304
|
+
monorepoRoot: normalizedMonorepoRoot,
|
|
305
|
+
overwriteBlockedPaths,
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
for (const appRoot of normalizedAppRoots) {
|
|
310
|
+
mergeProjectResult(
|
|
311
|
+
configureProjectAgentInstructions({
|
|
312
|
+
appRoot,
|
|
313
|
+
coreRoot,
|
|
314
|
+
dryRun,
|
|
315
|
+
includeRootInstructions: false,
|
|
316
|
+
monorepoRoot: normalizedMonorepoRoot,
|
|
317
|
+
overwriteBlockedPaths,
|
|
318
|
+
}),
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
result.blocked = [...new Set(result.blocked)];
|
|
323
|
+
result.created = [...new Set(result.created)];
|
|
324
|
+
result.overwritten = [...new Set(result.overwritten)];
|
|
325
|
+
result.removed = [...new Set(result.removed)];
|
|
326
|
+
result.skipped = [...new Set(result.skipped)];
|
|
327
|
+
result.updated = [...new Set(result.updated)];
|
|
328
|
+
result.updatedGitignores = [...new Set(result.updatedGitignores)];
|
|
329
|
+
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
|
|
221
333
|
export function resolveProjectAgentMonorepoRoot(appRoot: string) {
|
|
222
334
|
const normalizedAppRoot = resolveCanonicalPath(appRoot);
|
|
223
335
|
const likelyRepoRoot = findLikelyRepoRoot(normalizedAppRoot);
|
|
@@ -831,9 +943,11 @@ function renderSingleProjectInstruction({
|
|
|
831
943
|
|
|
832
944
|
function renderMonorepoAppRegistry({
|
|
833
945
|
appRoot,
|
|
946
|
+
currentAppRoot,
|
|
834
947
|
monorepoRoot,
|
|
835
948
|
}: {
|
|
836
949
|
appRoot?: string;
|
|
950
|
+
currentAppRoot?: string;
|
|
837
951
|
monorepoRoot?: string;
|
|
838
952
|
}) {
|
|
839
953
|
if (!monorepoRoot || !appRoot || path.resolve(monorepoRoot) === path.resolve(appRoot)) return [];
|
|
@@ -846,10 +960,13 @@ function renderMonorepoAppRegistry({
|
|
|
846
960
|
return [
|
|
847
961
|
'## Known Proteum Apps',
|
|
848
962
|
'',
|
|
849
|
-
'This is a monorepo root wrapper.
|
|
963
|
+
'This is a monorepo root wrapper. Eligible Proteum commands run across the apps below from this root; use an app root when you need to target exactly one app.',
|
|
850
964
|
'',
|
|
851
965
|
...summaries.map((summary) => {
|
|
852
|
-
const marker =
|
|
966
|
+
const marker =
|
|
967
|
+
currentAppRoot && path.resolve(summary.appRoot) === path.resolve(currentAppRoot)
|
|
968
|
+
? ' (current configured app)'
|
|
969
|
+
: '';
|
|
853
970
|
const port = summary.manifest?.routerPort ? `, default port ${summary.manifest.routerPort}` : '';
|
|
854
971
|
const command = createStartDevCommand({
|
|
855
972
|
appRoot: summary.appRoot,
|
|
@@ -863,7 +980,14 @@ function renderMonorepoAppRegistry({
|
|
|
863
980
|
];
|
|
864
981
|
}
|
|
865
982
|
|
|
866
|
-
function renderEmbeddedProjectInstructions({
|
|
983
|
+
function renderEmbeddedProjectInstructions({
|
|
984
|
+
appRoot,
|
|
985
|
+
coreRoot,
|
|
986
|
+
includeMonorepoRegistry = false,
|
|
987
|
+
instructionRoot,
|
|
988
|
+
monorepoRegistryCurrentAppRoot,
|
|
989
|
+
monorepoRoot,
|
|
990
|
+
}: TProjectInstructionArgs) {
|
|
867
991
|
const agentSourceRoot = path.join(coreRoot, 'agents', 'project');
|
|
868
992
|
if (!fs.existsSync(agentSourceRoot)) throw new Error(`Missing project instruction source root: ${agentSourceRoot}`);
|
|
869
993
|
|
|
@@ -895,7 +1019,13 @@ function renderEmbeddedProjectInstructions({ appRoot, coreRoot, includeMonorepoR
|
|
|
895
1019
|
'',
|
|
896
1020
|
'CLI remains the reproducible surface for `dev`, `build`, `check`, `verify`, migrations, and final command evidence. MCP remains read-only and returns compact `proteum-mcp-v1` JSON.',
|
|
897
1021
|
'',
|
|
898
|
-
...(includeMonorepoRegistry
|
|
1022
|
+
...(includeMonorepoRegistry
|
|
1023
|
+
? renderMonorepoAppRegistry({
|
|
1024
|
+
appRoot,
|
|
1025
|
+
currentAppRoot: monorepoRegistryCurrentAppRoot,
|
|
1026
|
+
monorepoRoot,
|
|
1027
|
+
})
|
|
1028
|
+
: []),
|
|
899
1029
|
'## Always-On Safety',
|
|
900
1030
|
'',
|
|
901
1031
|
'- Never edit generated files under `.proteum`.',
|
|
@@ -945,11 +1075,14 @@ function renderEmbeddedProjectInstructions({ appRoot, coreRoot, includeMonorepoR
|
|
|
945
1075
|
'',
|
|
946
1076
|
'## Canonical Source Map',
|
|
947
1077
|
'',
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1078
|
+
...projectInstructionSourceMapEntries.map(
|
|
1079
|
+
({ label, projectPath }) =>
|
|
1080
|
+
`- ${label}: ${formatProjectInstructionSourceMapPath({
|
|
1081
|
+
coreRoot,
|
|
1082
|
+
instructionRoot,
|
|
1083
|
+
projectPath,
|
|
1084
|
+
})}`,
|
|
1085
|
+
),
|
|
953
1086
|
'',
|
|
954
1087
|
];
|
|
955
1088
|
|
|
@@ -1091,6 +1224,41 @@ function formatResultPath(rootDir: string, relativePath: string) {
|
|
|
1091
1224
|
return normalizeProjectPathForGitignore(path.join(rootDir, relativePath));
|
|
1092
1225
|
}
|
|
1093
1226
|
|
|
1227
|
+
function formatProjectInstructionSourceMapPath({
|
|
1228
|
+
coreRoot,
|
|
1229
|
+
instructionRoot,
|
|
1230
|
+
projectPath,
|
|
1231
|
+
}: {
|
|
1232
|
+
coreRoot: string;
|
|
1233
|
+
instructionRoot: string;
|
|
1234
|
+
projectPath: string;
|
|
1235
|
+
}) {
|
|
1236
|
+
const sourceRoot = resolveProjectInstructionSourceMapRoot({ coreRoot, instructionRoot });
|
|
1237
|
+
const sourcePath = path.resolve(sourceRoot, 'agents', 'project', projectPath);
|
|
1238
|
+
const relativePath = path.relative(path.resolve(instructionRoot), sourcePath);
|
|
1239
|
+
|
|
1240
|
+
return normalizeProjectPathForGitignore(relativePath);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
function resolveProjectInstructionSourceMapRoot({ coreRoot, instructionRoot }: { coreRoot: string; instructionRoot: string }) {
|
|
1244
|
+
const visibleInstallRoot = findVisibleProteumInstructionRoot(instructionRoot);
|
|
1245
|
+
|
|
1246
|
+
return visibleInstallRoot || coreRoot;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function findVisibleProteumInstructionRoot(startRoot: string) {
|
|
1250
|
+
let currentPath = path.resolve(startRoot);
|
|
1251
|
+
|
|
1252
|
+
while (true) {
|
|
1253
|
+
const candidate = path.join(currentPath, 'node_modules', 'proteum');
|
|
1254
|
+
if (fs.existsSync(path.join(candidate, 'agents', 'project'))) return candidate;
|
|
1255
|
+
|
|
1256
|
+
const parentPath = path.dirname(currentPath);
|
|
1257
|
+
if (parentPath === currentPath) return undefined;
|
|
1258
|
+
currentPath = parentPath;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1094
1262
|
export function resolveCanonicalPath(inputPath: string) {
|
|
1095
1263
|
const resolvedPath = path.resolve(inputPath);
|
|
1096
1264
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proteum",
|
|
3
3
|
"description": "LLM-first Opinionated Typescript Framework for web applications.",
|
|
4
|
-
"version": "2.5.
|
|
4
|
+
"version": "2.5.7",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/proteum.git",
|
|
7
7
|
"license": "MIT",
|