proteum 2.5.4 → 2.5.6
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.md +1 -1
- package/README.md +11 -10
- package/agents/project/AGENTS.md +6 -6
- package/agents/project/CODING_STYLE.md +1 -1
- package/agents/project/diagnostics.md +2 -2
- package/agents/project/root/AGENTS.md +6 -6
- package/agents/project/tests/AGENTS.md +1 -1
- package/cli/commands/configure.ts +63 -4
- package/cli/index.ts +24 -18
- package/cli/mcp/router.ts +61 -15
- package/cli/presentation/commands.ts +13 -8
- package/cli/runtime/freshCopyPreflight.ts +767 -0
- package/cli/runtime/monorepoCommands.ts +625 -0
- package/cli/runtime/worktreeBootstrap.ts +163 -0
- package/cli/utils/agents.ts +156 -38
- package/docs/agent-routing.md +3 -3
- package/docs/diagnostics.md +2 -2
- package/docs/mcp.md +7 -6
- package/docs/request-tracing.md +1 -1
- package/package.json +1 -1
- package/tests/agents-utils.test.cjs +63 -3
- package/tests/cli-mcp-command.test.cjs +60 -11
- package/tests/mcp.test.cjs +98 -1
- 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,30 @@ 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
|
+
monorepoRegistryCurrentAppRoot?: string;
|
|
20
|
+
monorepoRoot?: string;
|
|
21
|
+
};
|
|
16
22
|
type TConfigureProjectAgentInstructionsArgs = {
|
|
17
23
|
appRoot: string;
|
|
18
24
|
coreRoot: string;
|
|
19
25
|
dryRun?: boolean;
|
|
26
|
+
includeAppInstructions?: boolean;
|
|
27
|
+
includeRootInstructions?: boolean;
|
|
28
|
+
markCurrentAppInMonorepoRegistry?: boolean;
|
|
20
29
|
monorepoRoot?: string;
|
|
21
30
|
overwriteBlockedPaths?: string[];
|
|
22
31
|
};
|
|
32
|
+
type TConfigureMonorepoProjectAgentInstructionsArgs = {
|
|
33
|
+
appRoots: string[];
|
|
34
|
+
coreRoot: string;
|
|
35
|
+
dryRun?: boolean;
|
|
36
|
+
monorepoRoot: string;
|
|
37
|
+
overwriteBlockedPaths?: string[];
|
|
38
|
+
};
|
|
23
39
|
|
|
24
40
|
type TAgentInstructionDefinition = {
|
|
25
41
|
projectPath: string;
|
|
@@ -49,6 +65,12 @@ export type TConfigureProjectAgentInstructionsResult = {
|
|
|
49
65
|
updatedGitignores: string[];
|
|
50
66
|
};
|
|
51
67
|
|
|
68
|
+
export type TConfigureMonorepoProjectAgentInstructionsResult = Omit<TConfigureProjectAgentInstructionsResult, 'appRoot'> & {
|
|
69
|
+
appRoots: string[];
|
|
70
|
+
monorepoRoot: string;
|
|
71
|
+
mode: 'monorepo';
|
|
72
|
+
};
|
|
73
|
+
|
|
52
74
|
/*----------------------------------
|
|
53
75
|
- CONSTANTS
|
|
54
76
|
----------------------------------*/
|
|
@@ -115,6 +137,9 @@ export function configureProjectAgentInstructions({
|
|
|
115
137
|
appRoot,
|
|
116
138
|
coreRoot,
|
|
117
139
|
dryRun = false,
|
|
140
|
+
includeAppInstructions = true,
|
|
141
|
+
includeRootInstructions = true,
|
|
142
|
+
markCurrentAppInMonorepoRegistry = true,
|
|
118
143
|
monorepoRoot,
|
|
119
144
|
overwriteBlockedPaths = [],
|
|
120
145
|
}: TConfigureProjectAgentInstructionsArgs): TConfigureProjectAgentInstructionsResult {
|
|
@@ -147,11 +172,12 @@ export function configureProjectAgentInstructions({
|
|
|
147
172
|
appRoot: normalizedAppRoot,
|
|
148
173
|
coreRoot,
|
|
149
174
|
includeMonorepoRegistry: true,
|
|
175
|
+
monorepoRegistryCurrentAppRoot: markCurrentAppInMonorepoRegistry ? normalizedAppRoot : undefined,
|
|
150
176
|
monorepoRoot: normalizedMonorepoRoot,
|
|
151
177
|
})
|
|
152
178
|
: appEmbeddedInstructions;
|
|
153
179
|
|
|
154
|
-
if (mode === 'monorepo' && normalizedMonorepoRoot) {
|
|
180
|
+
if (includeRootInstructions && mode === 'monorepo' && normalizedMonorepoRoot) {
|
|
155
181
|
result.monorepoRoot = normalizedMonorepoRoot;
|
|
156
182
|
|
|
157
183
|
const rootInstructions = getRootAgentInstructionDefinitions();
|
|
@@ -172,52 +198,127 @@ export function configureProjectAgentInstructions({
|
|
|
172
198
|
result.updatedGitignores.push(path.join(normalizedMonorepoRoot, '.gitignore'));
|
|
173
199
|
}
|
|
174
200
|
|
|
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(
|
|
201
|
+
if (includeAppInstructions) {
|
|
202
|
+
const appInstructions = getAppAgentInstructionDefinitions({ mode });
|
|
203
|
+
const appFiles = ensureInstructionFiles(
|
|
191
204
|
normalizedAppRoot,
|
|
192
|
-
|
|
205
|
+
appInstructions,
|
|
193
206
|
'[agents]',
|
|
194
207
|
path.join(coreRoot, 'agents', 'project'),
|
|
208
|
+
appEmbeddedInstructions,
|
|
195
209
|
{
|
|
196
210
|
dryRun,
|
|
211
|
+
overwriteBlockedPaths: normalizedOverwriteBlockedPaths,
|
|
197
212
|
},
|
|
198
213
|
);
|
|
199
|
-
mergeInstructionResults(result,
|
|
200
|
-
|
|
214
|
+
mergeInstructionResults(result, appFiles, normalizedAppRoot);
|
|
215
|
+
|
|
216
|
+
if (mode === 'monorepo') {
|
|
217
|
+
const retiredAppRootFiles = removeManagedInstructionFiles(
|
|
218
|
+
normalizedAppRoot,
|
|
219
|
+
[...sharedRootDocumentInstructionDefinitions, ...sharedTestAgentInstructionDefinitions],
|
|
220
|
+
'[agents]',
|
|
221
|
+
path.join(coreRoot, 'agents', 'project'),
|
|
222
|
+
{
|
|
223
|
+
dryRun,
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
mergeInstructionResults(result, retiredAppRootFiles, normalizedAppRoot);
|
|
227
|
+
}
|
|
201
228
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
229
|
+
const appGitignoreCleanupInstructions =
|
|
230
|
+
mode === 'monorepo'
|
|
231
|
+
? [...appInstructions, ...sharedRootDocumentInstructionDefinitions, ...sharedTestAgentInstructionDefinitions]
|
|
232
|
+
: appInstructions;
|
|
233
|
+
|
|
234
|
+
if (
|
|
235
|
+
!dryRun &&
|
|
236
|
+
removeInstructionGitignoreEntries({
|
|
237
|
+
rootDir: normalizedAppRoot,
|
|
238
|
+
instructionDefinitions: appGitignoreCleanupInstructions,
|
|
239
|
+
})
|
|
240
|
+
)
|
|
241
|
+
result.updatedGitignores.push(path.join(normalizedAppRoot, '.gitignore'));
|
|
242
|
+
}
|
|
215
243
|
|
|
216
244
|
return result;
|
|
217
245
|
}
|
|
218
246
|
|
|
219
247
|
export const configureProjectAgentSymlinks = configureProjectAgentInstructions;
|
|
220
248
|
|
|
249
|
+
export function configureMonorepoProjectAgentInstructions({
|
|
250
|
+
appRoots,
|
|
251
|
+
coreRoot,
|
|
252
|
+
dryRun = false,
|
|
253
|
+
monorepoRoot,
|
|
254
|
+
overwriteBlockedPaths = [],
|
|
255
|
+
}: TConfigureMonorepoProjectAgentInstructionsArgs): TConfigureMonorepoProjectAgentInstructionsResult {
|
|
256
|
+
const normalizedMonorepoRoot = path.resolve(monorepoRoot);
|
|
257
|
+
const normalizedAppRoots = [...new Set(appRoots.map((appRoot) => path.resolve(appRoot)))].sort((left, right) =>
|
|
258
|
+
left.localeCompare(right),
|
|
259
|
+
);
|
|
260
|
+
const [firstAppRoot] = normalizedAppRoots;
|
|
261
|
+
|
|
262
|
+
if (!firstAppRoot) throw new Error('No Proteum app roots were found under the monorepo root.');
|
|
263
|
+
|
|
264
|
+
const result: TConfigureMonorepoProjectAgentInstructionsResult = {
|
|
265
|
+
appRoots: normalizedAppRoots,
|
|
266
|
+
blocked: [],
|
|
267
|
+
created: [],
|
|
268
|
+
mode: 'monorepo',
|
|
269
|
+
monorepoRoot: normalizedMonorepoRoot,
|
|
270
|
+
overwritten: [],
|
|
271
|
+
removed: [],
|
|
272
|
+
skipped: [],
|
|
273
|
+
updated: [],
|
|
274
|
+
updatedGitignores: [],
|
|
275
|
+
};
|
|
276
|
+
const mergeProjectResult = (next: TConfigureProjectAgentInstructionsResult) => {
|
|
277
|
+
result.blocked = [...new Set([...result.blocked, ...next.blocked])];
|
|
278
|
+
result.created = [...new Set([...result.created, ...next.created])];
|
|
279
|
+
result.overwritten = [...new Set([...result.overwritten, ...next.overwritten])];
|
|
280
|
+
result.removed = [...new Set([...result.removed, ...next.removed])];
|
|
281
|
+
result.skipped = [...new Set([...result.skipped, ...next.skipped])];
|
|
282
|
+
result.updated = [...new Set([...result.updated, ...next.updated])];
|
|
283
|
+
result.updatedGitignores = [...new Set([...result.updatedGitignores, ...next.updatedGitignores])];
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
mergeProjectResult(
|
|
287
|
+
configureProjectAgentInstructions({
|
|
288
|
+
appRoot: firstAppRoot,
|
|
289
|
+
coreRoot,
|
|
290
|
+
dryRun,
|
|
291
|
+
includeAppInstructions: false,
|
|
292
|
+
markCurrentAppInMonorepoRegistry: false,
|
|
293
|
+
monorepoRoot: normalizedMonorepoRoot,
|
|
294
|
+
overwriteBlockedPaths,
|
|
295
|
+
}),
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
for (const appRoot of normalizedAppRoots) {
|
|
299
|
+
mergeProjectResult(
|
|
300
|
+
configureProjectAgentInstructions({
|
|
301
|
+
appRoot,
|
|
302
|
+
coreRoot,
|
|
303
|
+
dryRun,
|
|
304
|
+
includeRootInstructions: false,
|
|
305
|
+
monorepoRoot: normalizedMonorepoRoot,
|
|
306
|
+
overwriteBlockedPaths,
|
|
307
|
+
}),
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
result.blocked = [...new Set(result.blocked)];
|
|
312
|
+
result.created = [...new Set(result.created)];
|
|
313
|
+
result.overwritten = [...new Set(result.overwritten)];
|
|
314
|
+
result.removed = [...new Set(result.removed)];
|
|
315
|
+
result.skipped = [...new Set(result.skipped)];
|
|
316
|
+
result.updated = [...new Set(result.updated)];
|
|
317
|
+
result.updatedGitignores = [...new Set(result.updatedGitignores)];
|
|
318
|
+
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
|
|
221
322
|
export function resolveProjectAgentMonorepoRoot(appRoot: string) {
|
|
222
323
|
const normalizedAppRoot = resolveCanonicalPath(appRoot);
|
|
223
324
|
const likelyRepoRoot = findLikelyRepoRoot(normalizedAppRoot);
|
|
@@ -831,9 +932,11 @@ function renderSingleProjectInstruction({
|
|
|
831
932
|
|
|
832
933
|
function renderMonorepoAppRegistry({
|
|
833
934
|
appRoot,
|
|
935
|
+
currentAppRoot,
|
|
834
936
|
monorepoRoot,
|
|
835
937
|
}: {
|
|
836
938
|
appRoot?: string;
|
|
939
|
+
currentAppRoot?: string;
|
|
837
940
|
monorepoRoot?: string;
|
|
838
941
|
}) {
|
|
839
942
|
if (!monorepoRoot || !appRoot || path.resolve(monorepoRoot) === path.resolve(appRoot)) return [];
|
|
@@ -846,10 +949,13 @@ function renderMonorepoAppRegistry({
|
|
|
846
949
|
return [
|
|
847
950
|
'## Known Proteum Apps',
|
|
848
951
|
'',
|
|
849
|
-
'This is a monorepo root wrapper.
|
|
952
|
+
'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
953
|
'',
|
|
851
954
|
...summaries.map((summary) => {
|
|
852
|
-
const marker =
|
|
955
|
+
const marker =
|
|
956
|
+
currentAppRoot && path.resolve(summary.appRoot) === path.resolve(currentAppRoot)
|
|
957
|
+
? ' (current configured app)'
|
|
958
|
+
: '';
|
|
853
959
|
const port = summary.manifest?.routerPort ? `, default port ${summary.manifest.routerPort}` : '';
|
|
854
960
|
const command = createStartDevCommand({
|
|
855
961
|
appRoot: summary.appRoot,
|
|
@@ -863,7 +969,13 @@ function renderMonorepoAppRegistry({
|
|
|
863
969
|
];
|
|
864
970
|
}
|
|
865
971
|
|
|
866
|
-
function renderEmbeddedProjectInstructions({
|
|
972
|
+
function renderEmbeddedProjectInstructions({
|
|
973
|
+
appRoot,
|
|
974
|
+
coreRoot,
|
|
975
|
+
includeMonorepoRegistry = false,
|
|
976
|
+
monorepoRegistryCurrentAppRoot,
|
|
977
|
+
monorepoRoot,
|
|
978
|
+
}: TProjectInstructionArgs) {
|
|
867
979
|
const agentSourceRoot = path.join(coreRoot, 'agents', 'project');
|
|
868
980
|
if (!fs.existsSync(agentSourceRoot)) throw new Error(`Missing project instruction source root: ${agentSourceRoot}`);
|
|
869
981
|
|
|
@@ -895,7 +1007,13 @@ function renderEmbeddedProjectInstructions({ appRoot, coreRoot, includeMonorepoR
|
|
|
895
1007
|
'',
|
|
896
1008
|
'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
1009
|
'',
|
|
898
|
-
...(includeMonorepoRegistry
|
|
1010
|
+
...(includeMonorepoRegistry
|
|
1011
|
+
? renderMonorepoAppRegistry({
|
|
1012
|
+
appRoot,
|
|
1013
|
+
currentAppRoot: monorepoRegistryCurrentAppRoot,
|
|
1014
|
+
monorepoRoot,
|
|
1015
|
+
})
|
|
1016
|
+
: []),
|
|
899
1017
|
'## Always-On Safety',
|
|
900
1018
|
'',
|
|
901
1019
|
'- Never edit generated files under `.proteum`.',
|
package/docs/agent-routing.md
CHANGED
|
@@ -66,9 +66,9 @@ Use MCP for repeated reads when a client is available:
|
|
|
66
66
|
proteum mcp
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
The machine router discovers live `proteum dev` sessions and offline Proteum app roots under a cwd. `proteum dev` ensures one managed machine MCP daemon is running; terminal `proteum mcp` starts or reuses that daemon and prints a compact central MCP banner with the HTTP client URL, while MCP clients can use stdio. Agents should call MCP `workflow_start` with `cwd` or a known `projectId`, use `project_resolve { cwd }` when routing is ambiguous or offline, and pass the returned live `projectId` to every follow-up app-bound MCP tool. Offline candidates include port-inspected next actions, so agents should follow those instead of guessing the manifest default port. The router forwards to the selected dev-hosted `/__proteum/mcp` endpoint and strips routing fields before the app sees the call.
|
|
69
|
+
The machine router discovers live `proteum dev` sessions and offline Proteum app roots under a cwd. `proteum dev` ensures one managed machine MCP daemon is running; terminal `proteum mcp` starts or reuses that daemon and prints a compact central MCP banner with the HTTP client URL, while MCP clients can use stdio. Agents should call MCP `workflow_start` with `cwd` or a known `projectId`, use `project_resolve { cwd }` when routing is ambiguous or offline, resolve any returned `data.readiness.state="blocked"` fresh-copy setup actions, and pass the returned live `projectId` to every follow-up app-bound MCP tool. Offline candidates include readiness and port-inspected next actions, so agents should follow those instead of guessing the manifest default port. The router forwards to the selected dev-hosted `/__proteum/mcp` endpoint and strips routing fields before the app sees the call.
|
|
70
70
|
|
|
71
|
-
If machine MCP routing returns offline candidates, choose the intended app root and follow that candidate's next
|
|
71
|
+
If machine MCP routing returns offline candidates, choose the intended app root and follow that candidate's readiness and next actions from the app root, not from the monorepo wrapper. In `/.codex/worktrees/`, if `workflow_start` returns a worktree bootstrap block, run `npx proteum worktree init --source <source-app-root>` or the returned `--refresh` command before any runtime read. If machine MCP routing fails, run `proteum mcp status` and `proteum runtime status` from the intended app root; if no live session exists, use the exact Start Dev next action from runtime status so occupied router/HMR ports are avoided. If the same app already responds on the configured port without live tracking, use or repair that runtime instead of starting another server. Do not `curl` normal page routes to identify which app owns a port; use runtime status or Proteum dev-only endpoints. If a live session exists but runtime/MCP is unreachable, stop the listed session file first, then start dev again. Do not run diagnose, trace, or perf reads while runtime health is unreachable. Do not start a second dev server in the same worktree, and do not start a second managed MCP daemon. Then retry MCP `workflow_start`.
|
|
72
72
|
|
|
73
73
|
Prefer CLI over MCP when the result must be reproducible as a shell command, part of verification, or copied into CI/debug instructions.
|
|
74
74
|
|
|
@@ -135,6 +135,6 @@ The latest Product `/domains` benchmark used routed instructions plus the compac
|
|
|
135
135
|
The result confirms the intended routing:
|
|
136
136
|
|
|
137
137
|
- use CLI for reproducible verification and final command evidence
|
|
138
|
-
- use `workflow_start` to collapse project resolution, runtime status, instruction previews, owner summary, and first next actions into one read
|
|
138
|
+
- use `workflow_start` to collapse project resolution, fresh-copy readiness, runtime status, instruction previews, owner summary, and first next actions into one read
|
|
139
139
|
- use machine MCP with `projectId` for repeated runtime reads against an already running app
|
|
140
140
|
- use `instructions_resolve` to refresh routing instead of rereading full instruction files
|
package/docs/diagnostics.md
CHANGED
|
@@ -114,7 +114,7 @@ Default compact command output follows this shape:
|
|
|
114
114
|
|
|
115
115
|
Inside `/.codex/worktrees/`, `proteum dev`, `proteum refresh`, `proteum runtime status`, `proteum verify`, and MCP `workflow_start` require a fresh `.proteum/worktree-bootstrap.json`. If the marker is missing, run `npx proteum worktree init --source <source-app-root>`. If hashes, app `.env`, workspace-root `.env` for monorepos with root tooling, `.proteum/manifest.json`, `node_modules`, or the Proteum version are stale, run the returned `npx proteum worktree init --source <source-app-root> --refresh` command. `PROTEUM_ALLOW_UNBOOTSTRAPPED_WORKTREE=1` bypasses the block but remains visible in runtime status, doctor diagnostics, and MCP output.
|
|
116
116
|
|
|
117
|
-
During `proteum dev`, `/__proteum/mcp` exposes compact `workflow_start`, `runtime_status`, `orient`, `instructions_resolve`, `route_candidates`, `explain_summary`, `doctor`, `diagnose`, `trace_*`, `perf_*`, and `logs_tail` tools without spawning CLI commands for each repeated read. `proteum dev` also ensures one managed machine `proteum mcp` daemon is running. Through the machine router, call `workflow_start` with `cwd` or a known `projectId`; if routing is ambiguous or returns offline app candidates, use `project_resolve { cwd }`, follow the selected app root's port-inspected next
|
|
117
|
+
During `proteum dev`, `/__proteum/mcp` exposes compact `workflow_start`, `runtime_status`, `orient`, `instructions_resolve`, `route_candidates`, `explain_summary`, `doctor`, `diagnose`, `trace_*`, `perf_*`, and `logs_tail` tools without spawning CLI commands for each repeated read. `proteum dev` also ensures one managed machine `proteum mcp` daemon is running. Through the machine router, call `workflow_start` with `cwd` or a known `projectId`; if routing is ambiguous or returns offline app candidates, use `project_resolve { cwd }`, follow the selected app root's fresh-copy readiness and port-inspected next actions when needed, then pass the selected live `projectId` to follow-up app-bound tools. `workflow_start` returns `data.readiness` with read-only setup checks for `.env`, dependencies, generated manifest state, connected producers, Prisma/client readiness, redacted database URL shape, and local database TCP reachability.
|
|
118
118
|
|
|
119
119
|
MCP tool/resource output follows compact single-line `proteum-mcp-v1` JSON:
|
|
120
120
|
|
|
@@ -251,7 +251,7 @@ Treat these as framework contract failures first. The fix usually belongs at the
|
|
|
251
251
|
|
|
252
252
|
For AI coding agents or automation:
|
|
253
253
|
|
|
254
|
-
1. When MCP is available, call `workflow_start` with `cwd` or a known `projectId`; if routing is ambiguous or returns offline app candidates, call `project_resolve { cwd }`, select the intended app root, start dev from that app root when needed, then retry with the selected stable live `projectId`.
|
|
254
|
+
1. When MCP is available, call `workflow_start` with `cwd` or a known `projectId`; if routing is ambiguous or returns offline app candidates, call `project_resolve { cwd }`, select the intended app root, resolve any returned `data.readiness.state="blocked"` setup actions, start dev from that app root when needed, then retry with the selected stable live `projectId`.
|
|
255
255
|
2. Use the returned `projectId` for MCP `runtime_status`, `orient`, `instructions_resolve`, `route_candidates`, `explain_summary`, `doctor`, `diagnose`, `trace_show`, `perf_request`, and `logs_tail` read-only runtime, owner, route, instruction, trace, perf, and log reads.
|
|
256
256
|
3. Do not run CLI equivalents after a successful MCP result for the same read, and do not run broad source searches for ownership MCP already returned. Use CLI for fallback, `dev`, `build`, `check`, `verify`, migrations, E2E, and final terminal evidence.
|
|
257
257
|
4. Use selected instruction previews for read-only discovery and diagnostics; read full files only before edits or git writes, when returned `fullRead`/`fullReadPolicy` requires it, or when the preview is insufficient.
|
package/docs/mcp.md
CHANGED
|
@@ -28,7 +28,7 @@ The router is read-only. It does not start or stop dev servers, mutate files, re
|
|
|
28
28
|
Use this flow:
|
|
29
29
|
|
|
30
30
|
1. Call MCP `workflow_start` with `cwd` or a known `projectId`.
|
|
31
|
-
2. If the result is ambiguous or returns offline app candidates, call `project_resolve { cwd }`, pick the intended app root, start exactly one `proteum dev` server from that app root when needed, then retry `workflow_start`.
|
|
31
|
+
2. If the result is ambiguous or returns offline app candidates, call `project_resolve { cwd }`, pick the intended app root, resolve any returned `data.readiness.state="blocked"` setup actions, start exactly one `proteum dev` server from that app root when needed, then retry `workflow_start`.
|
|
32
32
|
3. Pass the returned live `projectId` to every follow-up app-bound MCP call.
|
|
33
33
|
4. After an MCP read succeeds, do not run the equivalent CLI command or broad source search for the same state; keep CLI for fallback, validation, and final terminal evidence.
|
|
34
34
|
|
|
@@ -47,7 +47,7 @@ Example tool calls:
|
|
|
47
47
|
{"tool":"db_query","arguments":{"projectId":"prj_0123abcd4567","sql":"SELECT id, email FROM User LIMIT 5","limit":5}}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
`workflow_start` is the only app-bound bootstrap tool that may resolve from `cwd` when `projectId` is not known. It may return offline app candidates when no matching dev server is running yet. Other app-bound tools require a live `projectId`; if they omit it, the router returns a compact error that tells the agent to call `projects_list` or `project_resolve`. There is no single-project fallback, because wrong-project reads are worse than an explicit routing retry.
|
|
50
|
+
`workflow_start` is the only app-bound bootstrap tool that may resolve from `cwd` when `projectId` is not known. It may return offline app candidates when no matching dev server is running yet. Its machine-router response includes `data.readiness`, a read-only fresh-copy preflight covering app/root `.env` files, dependency install root and package manager, generated Proteum manifest state, local connected producer apps, Prisma schema/client readiness, redacted database URL shape, and local TCP database reachability when the host is local. The preflight adds exact setup commands where safe, such as copying `.env.example`, installing dependencies, running `npx proteum refresh`, generating Prisma Client, checking Prisma migration status, preflighting connected producer apps, and starting `proteum dev` on a checked port. Other app-bound tools require a live `projectId`; if they omit it, the router returns a compact error that tells the agent to call `projects_list` or `project_resolve`. There is no single-project fallback, because wrong-project reads are worse than an explicit routing retry.
|
|
51
51
|
|
|
52
52
|
When the selected app root is inside `/.codex/worktrees/`, `workflow_start` first checks `.proteum/worktree-bootstrap.json`. If the marker is missing or stale, it returns `ok: false` with a single next action such as `npx proteum worktree init --source <source-app-root>` or the same command with `--refresh`. The router does not forward to the app MCP endpoint until bootstrap is complete, unless `PROTEUM_ALLOW_UNBOOTSTRAPPED_WORKTREE=1` is set; bypasses remain visible in MCP, `runtime status`, and `doctor`.
|
|
53
53
|
|
|
@@ -79,9 +79,10 @@ If machine MCP routing fails:
|
|
|
79
79
|
1. Run `proteum mcp status`.
|
|
80
80
|
2. Run `proteum runtime status` from the intended app root. If you are in a monorepo wrapper, use the returned app candidates and exact next action instead of starting dev from the wrapper.
|
|
81
81
|
3. If the app root is inside `/.codex/worktrees/` and runtime status or workflow start reports missing/stale bootstrap, run `proteum worktree init --source <source-app-root>` or the returned `--refresh` command first.
|
|
82
|
-
4. If
|
|
83
|
-
5. If
|
|
84
|
-
6.
|
|
82
|
+
4. If `workflow_start` returns `data.readiness.state="blocked"`, run or resolve the readiness setup actions before starting dev.
|
|
83
|
+
5. If no live app session exists, use the exact Start Dev next action returned by runtime status or `workflow_start`. It checks the configured router/HMR ports and suggests an alternate free port when the manifest default is occupied.
|
|
84
|
+
6. If a live session exists but runtime/MCP is unreachable, stop the listed session file with `proteum dev stop --session-file <path>`, then start dev again.
|
|
85
|
+
7. Retry MCP `workflow_start` and use the returned `projectId`.
|
|
85
86
|
|
|
86
87
|
Offline `project_resolve` and `workflow_start` candidates also inspect configured router/HMR ports before returning `nextAction`. If the configured port already serves the same app but no live machine project is registered, the next action is runtime tracking repair, not starting a second dev server.
|
|
87
88
|
|
|
@@ -122,7 +123,7 @@ App-bound tools require `projectId` when called through `proteum mcp`:
|
|
|
122
123
|
|
|
123
124
|
| Tool | Purpose |
|
|
124
125
|
| --- | --- |
|
|
125
|
-
| `workflow_start` | One-call bootstrap with resolved project, runtime, selected instruction previews, owner summary, doctor summaries, duplicate-avoidance rules, and next actions |
|
|
126
|
+
| `workflow_start` | One-call bootstrap with resolved project, fresh-copy readiness, runtime, selected instruction previews, owner summary, doctor summaries, duplicate-avoidance rules, and next actions |
|
|
126
127
|
| `runtime_status` | Manifest summary, selected runtime, tracked sessions, health, and MCP URL |
|
|
127
128
|
| `orient` | Owner, instruction routing, connected boundaries, and next actions |
|
|
128
129
|
| `instructions_resolve` | Selected instruction files for a query, with short previews and full-read policy |
|
package/docs/request-tracing.md
CHANGED
|
@@ -33,7 +33,7 @@ proteum perf memory --since 1h --group-by controller
|
|
|
33
33
|
|
|
34
34
|
Default trace output is compact `proteum-agent-v1` JSON with counts, failed calls, error events, hot calls, and hot SQL. Use `--events` or `--full` only when raw event details, payload summaries, or SQL text are needed.
|
|
35
35
|
|
|
36
|
-
When an MCP client is available, call `workflow_start` with `cwd` or a known `projectId`, then use the returned live `projectId` with MCP `trace_latest`, `trace_show`, `perf_top`, and `perf_request` for repeated reads against the same running app. If `workflow_start` returns offline app candidates or unreachable runtime health, start or repair exactly one `proteum dev` server from the intended app root before trace or perf reads. Keep the CLI commands for reproducible terminal evidence and final verification logs.
|
|
36
|
+
When an MCP client is available, call `workflow_start` with `cwd` or a known `projectId`, then use the returned live `projectId` with MCP `trace_latest`, `trace_show`, `perf_top`, and `perf_request` for repeated reads against the same running app. If `workflow_start` returns offline app candidates, `data.readiness.state="blocked"`, or unreachable runtime health, resolve the readiness setup actions and start or repair exactly one `proteum dev` server from the intended app root before trace or perf reads. Keep the CLI commands for reproducible terminal evidence and final verification logs.
|
|
37
37
|
|
|
38
38
|
Before reproducing a bug or starting a new test pass:
|
|
39
39
|
|
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.6",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/proteum.git",
|
|
7
7
|
"license": "MIT",
|