proteum 2.2.8 → 2.3.0
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 +5 -3
- package/README.md +50 -12
- package/agents/project/AGENTS.md +47 -10
- package/agents/project/CODING_STYLE.md +5 -1
- package/agents/project/client/AGENTS.md +2 -0
- package/agents/project/diagnostics.md +8 -5
- package/agents/project/optimizations.md +1 -0
- package/agents/project/root/AGENTS.md +18 -10
- package/agents/project/tests/AGENTS.md +6 -1
- package/agents/project/tests/e2e/AGENTS.md +13 -0
- package/agents/project/tests/e2e/REAL_WORLD_JOURNEY_TESTS.md +192 -0
- package/cli/commands/check.ts +21 -3
- package/cli/commands/configure.ts +1 -0
- package/cli/commands/connect.ts +40 -4
- package/cli/commands/diagnose.ts +136 -5
- package/cli/commands/doctor.ts +24 -4
- package/cli/commands/explain.ts +105 -6
- package/cli/commands/mcp.ts +16 -0
- package/cli/commands/orient.ts +66 -3
- package/cli/commands/perf.ts +118 -13
- package/cli/commands/runtime.ts +151 -0
- package/cli/commands/trace.ts +116 -21
- package/cli/mcp/provider.ts +365 -0
- package/cli/mcp/stdio.ts +16 -0
- package/cli/presentation/commands.ts +79 -22
- package/cli/presentation/devSession.ts +2 -0
- package/cli/runtime/commands.ts +95 -12
- package/cli/utils/agentOutput.ts +46 -0
- package/cli/utils/agents.ts +225 -48
- package/common/dev/inspection.ts +30 -9
- package/common/dev/mcpPayloads.ts +736 -0
- package/common/dev/mcpServer.ts +254 -0
- package/docs/agent-routing.md +126 -0
- package/docs/dev-commands.md +2 -0
- package/docs/dev-sessions.md +2 -1
- package/docs/diagnostics.md +68 -23
- package/docs/mcp.md +149 -0
- package/docs/migrate-from-2.1.3.md +15 -5
- package/docs/request-tracing.md +12 -6
- package/eslint.js +220 -0
- package/package.json +2 -1
- package/server/app/devMcp.ts +159 -0
- package/server/services/router/http/cache.ts +116 -0
- package/server/services/router/http/index.ts +94 -35
- package/server/services/router/index.ts +8 -11
- package/tests/agents-utils.test.cjs +89 -11
- package/tests/dev-transpile-watch.test.cjs +117 -8
- package/tests/eslint-rules.test.cjs +110 -0
- package/tests/inspection.test.cjs +67 -0
- package/tests/mcp.test.cjs +127 -0
- package/tests/router-cache-config.test.cjs +74 -0
package/cli/utils/agents.ts
CHANGED
|
@@ -23,12 +23,14 @@ type TConfigureProjectAgentInstructionsArgs = {
|
|
|
23
23
|
type TAgentInstructionDefinition = {
|
|
24
24
|
projectPath: string;
|
|
25
25
|
ensureParentDir?: boolean;
|
|
26
|
+
content?: 'router' | 'source';
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
type TEnsureInstructionFilesResult = {
|
|
29
30
|
blocked: string[];
|
|
30
31
|
created: string[];
|
|
31
32
|
overwritten: string[];
|
|
33
|
+
removed: string[];
|
|
32
34
|
skipped: string[];
|
|
33
35
|
updated: string[];
|
|
34
36
|
};
|
|
@@ -40,6 +42,7 @@ export type TConfigureProjectAgentInstructionsResult = {
|
|
|
40
42
|
monorepoRoot?: string;
|
|
41
43
|
mode: 'monorepo' | 'standalone';
|
|
42
44
|
overwritten: string[];
|
|
45
|
+
removed: string[];
|
|
43
46
|
skipped: string[];
|
|
44
47
|
updated: string[];
|
|
45
48
|
updatedGitignores: string[];
|
|
@@ -57,29 +60,40 @@ const managedInstructionSectionStart = '<!-- proteum-instructions:start -->';
|
|
|
57
60
|
const managedInstructionSectionEnd = '<!-- proteum-instructions:end -->';
|
|
58
61
|
const managedInstructionSectionIntro = 'This section is managed by `proteum configure agents`.';
|
|
59
62
|
|
|
60
|
-
const
|
|
61
|
-
{ projectPath: 'CODING_STYLE.md' },
|
|
62
|
-
{ projectPath: 'diagnostics.md' },
|
|
63
|
-
{ projectPath: 'optimizations.md' },
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{ projectPath: path.join('
|
|
68
|
-
{ projectPath: path.join('
|
|
63
|
+
const sharedRootDocumentInstructionDefinitions: TAgentInstructionDefinition[] = [
|
|
64
|
+
{ projectPath: 'CODING_STYLE.md', content: 'source' },
|
|
65
|
+
{ projectPath: 'diagnostics.md', content: 'source' },
|
|
66
|
+
{ projectPath: 'optimizations.md', content: 'source' },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const sharedAppAreaAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
|
|
70
|
+
{ projectPath: path.join('client', 'AGENTS.md'), content: 'source' },
|
|
71
|
+
{ projectPath: path.join('client', 'pages', 'AGENTS.md'), content: 'source' },
|
|
72
|
+
{ projectPath: path.join('server', 'services', 'AGENTS.md'), content: 'source' },
|
|
73
|
+
{ projectPath: path.join('server', 'routes', 'AGENTS.md'), content: 'source' },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const sharedE2eAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
|
|
77
|
+
{ projectPath: path.join('tests', 'e2e', 'AGENTS.md'), ensureParentDir: true, content: 'source' },
|
|
78
|
+
{ projectPath: path.join('tests', 'e2e', 'REAL_WORLD_JOURNEY_TESTS.md'), ensureParentDir: true, content: 'source' },
|
|
69
79
|
];
|
|
70
80
|
|
|
71
81
|
const standaloneAppAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
|
|
72
|
-
{ projectPath: 'AGENTS.md' },
|
|
73
|
-
...
|
|
82
|
+
{ projectPath: 'AGENTS.md', content: 'router' },
|
|
83
|
+
...sharedRootDocumentInstructionDefinitions,
|
|
84
|
+
...sharedAppAreaAgentInstructionDefinitions,
|
|
85
|
+
...sharedE2eAgentInstructionDefinitions,
|
|
74
86
|
];
|
|
75
87
|
|
|
76
88
|
const monorepoAppAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
|
|
77
|
-
{ projectPath: 'AGENTS.md' },
|
|
78
|
-
...
|
|
89
|
+
{ projectPath: 'AGENTS.md', content: 'router' },
|
|
90
|
+
...sharedAppAreaAgentInstructionDefinitions,
|
|
79
91
|
];
|
|
80
92
|
|
|
81
93
|
const monorepoRootAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
|
|
82
|
-
{ projectPath: 'AGENTS.md' },
|
|
94
|
+
{ projectPath: 'AGENTS.md', content: 'router' },
|
|
95
|
+
...sharedRootDocumentInstructionDefinitions,
|
|
96
|
+
...sharedE2eAgentInstructionDefinitions,
|
|
83
97
|
];
|
|
84
98
|
|
|
85
99
|
const legacyProjectInstructionGitignoreBlockStart = '# Proteum-managed instruction symlinks';
|
|
@@ -111,6 +125,7 @@ export function configureProjectAgentInstructions({
|
|
|
111
125
|
created: [],
|
|
112
126
|
mode,
|
|
113
127
|
overwritten: [],
|
|
128
|
+
removed: [],
|
|
114
129
|
skipped: [],
|
|
115
130
|
updated: [],
|
|
116
131
|
updatedGitignores: [],
|
|
@@ -152,7 +167,31 @@ export function configureProjectAgentInstructions({
|
|
|
152
167
|
);
|
|
153
168
|
mergeInstructionResults(result, appFiles, normalizedAppRoot);
|
|
154
169
|
|
|
155
|
-
if (
|
|
170
|
+
if (mode === 'monorepo') {
|
|
171
|
+
const retiredAppRootFiles = removeManagedInstructionFiles(
|
|
172
|
+
normalizedAppRoot,
|
|
173
|
+
[...sharedRootDocumentInstructionDefinitions, ...sharedE2eAgentInstructionDefinitions],
|
|
174
|
+
'[agents]',
|
|
175
|
+
path.join(coreRoot, 'agents', 'project'),
|
|
176
|
+
{
|
|
177
|
+
dryRun,
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
mergeInstructionResults(result, retiredAppRootFiles, normalizedAppRoot);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const appGitignoreCleanupInstructions =
|
|
184
|
+
mode === 'monorepo'
|
|
185
|
+
? [...appInstructions, ...sharedRootDocumentInstructionDefinitions, ...sharedE2eAgentInstructionDefinitions]
|
|
186
|
+
: appInstructions;
|
|
187
|
+
|
|
188
|
+
if (
|
|
189
|
+
!dryRun &&
|
|
190
|
+
removeInstructionGitignoreEntries({
|
|
191
|
+
rootDir: normalizedAppRoot,
|
|
192
|
+
instructionDefinitions: appGitignoreCleanupInstructions,
|
|
193
|
+
})
|
|
194
|
+
)
|
|
156
195
|
result.updatedGitignores.push(path.join(normalizedAppRoot, '.gitignore'));
|
|
157
196
|
|
|
158
197
|
return result;
|
|
@@ -253,6 +292,7 @@ function ensureInstructionFiles(
|
|
|
253
292
|
blocked: [],
|
|
254
293
|
created: [],
|
|
255
294
|
overwritten: [],
|
|
295
|
+
removed: [],
|
|
256
296
|
skipped: [],
|
|
257
297
|
updated: [],
|
|
258
298
|
};
|
|
@@ -268,13 +308,21 @@ function ensureInstructionFiles(
|
|
|
268
308
|
continue;
|
|
269
309
|
}
|
|
270
310
|
|
|
311
|
+
const instructionContent = renderProjectInstructionContent({
|
|
312
|
+
instructionDefinition,
|
|
313
|
+
managedSourceRoot,
|
|
314
|
+
managedSectionContent,
|
|
315
|
+
});
|
|
271
316
|
const existingState = inspectExistingPath({
|
|
272
317
|
managedSourceRoot,
|
|
273
318
|
projectFilepath,
|
|
274
319
|
});
|
|
275
320
|
|
|
276
321
|
if (existingState.kind === 'file') {
|
|
277
|
-
const nextContent =
|
|
322
|
+
const nextContent =
|
|
323
|
+
instructionDefinition.content === 'source'
|
|
324
|
+
? instructionContent
|
|
325
|
+
: upsertManagedInstructionSection(existingState.content, instructionContent);
|
|
278
326
|
if (nextContent === existingState.content) {
|
|
279
327
|
result.skipped.push(relativeProjectPath);
|
|
280
328
|
continue;
|
|
@@ -289,7 +337,7 @@ function ensureInstructionFiles(
|
|
|
289
337
|
if (existingState.kind === 'managed-different') {
|
|
290
338
|
if (!dryRun) {
|
|
291
339
|
fs.removeSync(projectFilepath);
|
|
292
|
-
fs.writeFileSync(projectFilepath,
|
|
340
|
+
fs.writeFileSync(projectFilepath, instructionContent);
|
|
293
341
|
}
|
|
294
342
|
result.updated.push(relativeProjectPath);
|
|
295
343
|
logVerbose(`${logPrefix} Updated ${relativeProjectPath}`);
|
|
@@ -305,14 +353,14 @@ function ensureInstructionFiles(
|
|
|
305
353
|
if (existingState.kind === 'blocked') {
|
|
306
354
|
if (!dryRun) {
|
|
307
355
|
fs.removeSync(projectFilepath);
|
|
308
|
-
fs.writeFileSync(projectFilepath,
|
|
356
|
+
fs.writeFileSync(projectFilepath, instructionContent);
|
|
309
357
|
}
|
|
310
358
|
result.overwritten.push(relativeProjectPath);
|
|
311
359
|
logVerbose(`${logPrefix} Replaced ${relativeProjectPath}`);
|
|
312
360
|
continue;
|
|
313
361
|
}
|
|
314
362
|
|
|
315
|
-
if (!dryRun) fs.writeFileSync(projectFilepath,
|
|
363
|
+
if (!dryRun) fs.writeFileSync(projectFilepath, instructionContent);
|
|
316
364
|
result.created.push(relativeProjectPath);
|
|
317
365
|
logVerbose(`${logPrefix} Created ${relativeProjectPath}`);
|
|
318
366
|
}
|
|
@@ -320,6 +368,74 @@ function ensureInstructionFiles(
|
|
|
320
368
|
return result;
|
|
321
369
|
}
|
|
322
370
|
|
|
371
|
+
function removeManagedInstructionFiles(
|
|
372
|
+
rootDir: string,
|
|
373
|
+
instructionDefinitions: TAgentInstructionDefinition[],
|
|
374
|
+
logPrefix: string,
|
|
375
|
+
managedSourceRoot: string,
|
|
376
|
+
{
|
|
377
|
+
dryRun,
|
|
378
|
+
}: {
|
|
379
|
+
dryRun: boolean;
|
|
380
|
+
},
|
|
381
|
+
): TEnsureInstructionFilesResult {
|
|
382
|
+
const result: TEnsureInstructionFilesResult = {
|
|
383
|
+
blocked: [],
|
|
384
|
+
created: [],
|
|
385
|
+
overwritten: [],
|
|
386
|
+
removed: [],
|
|
387
|
+
skipped: [],
|
|
388
|
+
updated: [],
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
for (const instructionDefinition of instructionDefinitions) {
|
|
392
|
+
const projectFilepath = path.join(rootDir, instructionDefinition.projectPath);
|
|
393
|
+
const projectParentDir = path.dirname(projectFilepath);
|
|
394
|
+
const relativeProjectPath = path.relative(rootDir, projectFilepath) || '.';
|
|
395
|
+
|
|
396
|
+
if (!fs.existsSync(projectParentDir)) continue;
|
|
397
|
+
|
|
398
|
+
const existingState = inspectExistingPath({
|
|
399
|
+
managedSourceRoot,
|
|
400
|
+
projectFilepath,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
if (existingState.kind === 'missing') continue;
|
|
404
|
+
|
|
405
|
+
if (existingState.kind === 'managed-different') {
|
|
406
|
+
if (!dryRun) fs.removeSync(projectFilepath);
|
|
407
|
+
result.removed.push(relativeProjectPath);
|
|
408
|
+
logVerbose(`${logPrefix} Removed retired app-root ${relativeProjectPath}`);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (existingState.kind === 'file') {
|
|
413
|
+
const retainedContent = removeManagedInstructionContent(existingState.content);
|
|
414
|
+
|
|
415
|
+
if (retainedContent === undefined) {
|
|
416
|
+
result.skipped.push(relativeProjectPath);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (retainedContent.trim() === '') {
|
|
421
|
+
if (!dryRun) fs.removeSync(projectFilepath);
|
|
422
|
+
result.removed.push(relativeProjectPath);
|
|
423
|
+
logVerbose(`${logPrefix} Removed retired app-root ${relativeProjectPath}`);
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (!dryRun) fs.writeFileSync(projectFilepath, retainedContent);
|
|
428
|
+
result.updated.push(relativeProjectPath);
|
|
429
|
+
logVerbose(`${logPrefix} Removed retired managed section from ${relativeProjectPath}`);
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
result.skipped.push(relativeProjectPath);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return result;
|
|
437
|
+
}
|
|
438
|
+
|
|
323
439
|
function inspectExistingPath({
|
|
324
440
|
managedSourceRoot,
|
|
325
441
|
projectFilepath,
|
|
@@ -377,57 +493,108 @@ function mergeInstructionResults(
|
|
|
377
493
|
) {
|
|
378
494
|
result.created.push(...next.created.map((entry) => formatResultPath(rootDir, entry)));
|
|
379
495
|
result.overwritten.push(...next.overwritten.map((entry) => formatResultPath(rootDir, entry)));
|
|
496
|
+
result.removed.push(...next.removed.map((entry) => formatResultPath(rootDir, entry)));
|
|
380
497
|
result.updated.push(...next.updated.map((entry) => formatResultPath(rootDir, entry)));
|
|
381
498
|
result.skipped.push(...next.skipped.map((entry) => formatResultPath(rootDir, entry)));
|
|
382
499
|
result.blocked.push(...next.blocked.map((entry) => formatResultPath(rootDir, entry)));
|
|
383
500
|
}
|
|
384
501
|
|
|
385
|
-
function
|
|
386
|
-
|
|
387
|
-
|
|
502
|
+
function renderProjectInstructionContent({
|
|
503
|
+
instructionDefinition,
|
|
504
|
+
managedSourceRoot,
|
|
505
|
+
managedSectionContent,
|
|
506
|
+
}: {
|
|
507
|
+
instructionDefinition: TAgentInstructionDefinition;
|
|
508
|
+
managedSourceRoot: string;
|
|
509
|
+
managedSectionContent: string;
|
|
510
|
+
}) {
|
|
511
|
+
if (instructionDefinition.content !== 'source') return managedSectionContent;
|
|
512
|
+
|
|
513
|
+
return renderSingleProjectInstruction({
|
|
514
|
+
managedSourceRoot,
|
|
515
|
+
projectPath: instructionDefinition.projectPath,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
388
518
|
|
|
389
|
-
|
|
519
|
+
function renderSingleProjectInstruction({
|
|
520
|
+
managedSourceRoot,
|
|
521
|
+
projectPath,
|
|
522
|
+
}: {
|
|
523
|
+
managedSourceRoot: string;
|
|
524
|
+
projectPath: string;
|
|
525
|
+
}) {
|
|
526
|
+
const sourceFilepath = path.join(managedSourceRoot, projectPath);
|
|
527
|
+
if (!fs.existsSync(sourceFilepath)) throw new Error(`Missing project instruction source file: ${sourceFilepath}`);
|
|
528
|
+
|
|
529
|
+
const content = fs.readFileSync(sourceFilepath, 'utf8');
|
|
530
|
+
const demotedContent = demoteMarkdownHeadings(content).trim();
|
|
390
531
|
const lines = [
|
|
391
532
|
managedInstructionSectionHeader,
|
|
392
533
|
managedInstructionSectionStart,
|
|
393
534
|
'',
|
|
394
535
|
managedInstructionSectionIntro,
|
|
395
536
|
'',
|
|
537
|
+
`## Source: ${normalizeProjectPathForGitignore(projectPath)}`,
|
|
538
|
+
'',
|
|
396
539
|
];
|
|
397
540
|
|
|
398
|
-
|
|
399
|
-
const content = fs.readFileSync(sourceFile.filepath, 'utf8');
|
|
400
|
-
const demotedContent = demoteMarkdownHeadings(content).trim();
|
|
401
|
-
|
|
402
|
-
lines.push(`## Source: ${sourceFile.relativePath}`, '');
|
|
403
|
-
if (demotedContent) lines.push(demotedContent, '');
|
|
404
|
-
}
|
|
405
|
-
|
|
541
|
+
if (demotedContent) lines.push(demotedContent, '');
|
|
406
542
|
lines.push(managedInstructionSectionEnd, '');
|
|
407
543
|
|
|
408
544
|
return lines.join('\n');
|
|
409
545
|
}
|
|
410
546
|
|
|
411
|
-
function
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
|
|
415
|
-
const filepath = path.join(currentDir, entry.name);
|
|
416
|
-
|
|
417
|
-
if (entry.isDirectory()) {
|
|
418
|
-
files.push(...collectMarkdownFiles(rootDir, filepath));
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
547
|
+
function renderEmbeddedProjectInstructions({ coreRoot }: TProjectInstructionArgs) {
|
|
548
|
+
const agentSourceRoot = path.join(coreRoot, 'agents', 'project');
|
|
549
|
+
if (!fs.existsSync(agentSourceRoot)) throw new Error(`Missing project instruction source root: ${agentSourceRoot}`);
|
|
421
550
|
|
|
422
|
-
|
|
551
|
+
const lines = [
|
|
552
|
+
managedInstructionSectionHeader,
|
|
553
|
+
managedInstructionSectionStart,
|
|
554
|
+
'',
|
|
555
|
+
managedInstructionSectionIntro,
|
|
556
|
+
'',
|
|
557
|
+
'## Agent Routing Contract',
|
|
558
|
+
'',
|
|
559
|
+
'Proteum CLI commands are optimized for agents. Do not load the whole instruction corpus up front.',
|
|
560
|
+
'',
|
|
561
|
+
'1. Start ambiguous, generated, connected, route, controller, file, or error work with `npx proteum orient <query>`.',
|
|
562
|
+
'2. Read only the files returned in `mustRead` plus the conditional docs that match the current task.',
|
|
563
|
+
'3. Use `npx proteum runtime status` before starting a dev server, so an existing tracked session can be reused.',
|
|
564
|
+
'4. Use `npx proteum diagnose <target>` for request-time issues before raw trace, perf, browser, or broad source search.',
|
|
565
|
+
'5. Use `--full`, `--manifest`, or `--events` only when a compact CLI response says the omitted detail is needed.',
|
|
566
|
+
'',
|
|
567
|
+
'## Always-On Safety',
|
|
568
|
+
'',
|
|
569
|
+
'- Never edit generated files under `.proteum`.',
|
|
570
|
+
'- Never create or edit Prisma migration files manually.',
|
|
571
|
+
'- Never run schema-mutating SQL such as `ALTER TABLE`, `CREATE TABLE`, `DROP TABLE`, or `CREATE INDEX`.',
|
|
572
|
+
'- If `schema.prisma` changes, ask the user to run `npx prisma migrate dev --config ./prisma.config.ts --name <migration name>` and wait for `continue` before validation.',
|
|
573
|
+
'- Do not run `git restore` or `git reset`.',
|
|
574
|
+
'- Keep `proteum dev` sessions tracked with explicit session files and do not replace another live session.',
|
|
575
|
+
'',
|
|
576
|
+
'## Routing Table',
|
|
577
|
+
'',
|
|
578
|
+
'- Raw errors, failing requests, traces, perf, or reproduction: read `diagnostics.md`.',
|
|
579
|
+
'- Implementation edits: read `CODING_STYLE.md` before editing.',
|
|
580
|
+
'- Client files or pages: read `client/AGENTS.md`; for page route/data/render work also read `client/pages/AGENTS.md`.',
|
|
581
|
+
'- Server services: read `server/services/AGENTS.md`.',
|
|
582
|
+
'- Manual server routes: read `server/routes/AGENTS.md`.',
|
|
583
|
+
'- E2E work: read `tests/e2e/AGENTS.md` and `tests/e2e/REAL_WORLD_JOURNEY_TESTS.md`.',
|
|
584
|
+
'- Package, runtime, build, or client-performance decisions: read `optimizations.md` after implementation or when explicitly optimizing.',
|
|
585
|
+
'',
|
|
586
|
+
'## Canonical Source Map',
|
|
587
|
+
'',
|
|
588
|
+
`- Root contract fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'AGENTS.md'))}`,
|
|
589
|
+
`- Diagnostics fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'diagnostics.md'))}`,
|
|
590
|
+
`- Optimization fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'optimizations.md'))}`,
|
|
591
|
+
`- Coding style fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'CODING_STYLE.md'))}`,
|
|
592
|
+
'',
|
|
593
|
+
];
|
|
423
594
|
|
|
424
|
-
|
|
425
|
-
filepath,
|
|
426
|
-
relativePath: normalizeProjectPathForGitignore(path.relative(rootDir, filepath)),
|
|
427
|
-
});
|
|
428
|
-
}
|
|
595
|
+
lines.push(managedInstructionSectionEnd, '');
|
|
429
596
|
|
|
430
|
-
return
|
|
597
|
+
return lines.join('\n');
|
|
431
598
|
}
|
|
432
599
|
|
|
433
600
|
function demoteMarkdownHeadings(content: string) {
|
|
@@ -472,6 +639,16 @@ function upsertManagedInstructionSection(content: string, managedSectionContent:
|
|
|
472
639
|
return joinMarkdownSections([before, managedSectionContent, after]);
|
|
473
640
|
}
|
|
474
641
|
|
|
642
|
+
function removeManagedInstructionContent(content: string) {
|
|
643
|
+
const managedRange = findManagedInstructionSectionRange(content) || findLegacyManagedInstructionStubRange(content);
|
|
644
|
+
if (!managedRange) return undefined;
|
|
645
|
+
|
|
646
|
+
const before = content.slice(0, managedRange.start);
|
|
647
|
+
const after = content.slice(managedRange.end);
|
|
648
|
+
|
|
649
|
+
return joinMarkdownSections([before, after]);
|
|
650
|
+
}
|
|
651
|
+
|
|
475
652
|
function findManagedInstructionSectionRange(content: string) {
|
|
476
653
|
const markerStartIndex = content.indexOf(managedInstructionSectionStart);
|
|
477
654
|
if (markerStartIndex === -1) return undefined;
|
package/common/dev/inspection.ts
CHANGED
|
@@ -713,12 +713,25 @@ const resolveGuidanceFile = ({
|
|
|
713
713
|
fallbackFilepath: string;
|
|
714
714
|
relativePath: string;
|
|
715
715
|
}) => {
|
|
716
|
-
const
|
|
717
|
-
|
|
716
|
+
const resolvedAppRoot = resolvePath(appRoot);
|
|
717
|
+
const repoRoot = findRepoRoot(resolvedAppRoot);
|
|
718
|
+
let currentRoot = resolvedAppRoot;
|
|
719
|
+
|
|
720
|
+
while (true) {
|
|
721
|
+
const localFilepath = joinPath(currentRoot, relativePath);
|
|
722
|
+
if (fileExists(localFilepath)) return { filepath: localFilepath, warning: undefined as string | undefined };
|
|
723
|
+
|
|
724
|
+
if (currentRoot === repoRoot) break;
|
|
725
|
+
|
|
726
|
+
const parentRoot = dirnamePath(currentRoot);
|
|
727
|
+
if (parentRoot === currentRoot) break;
|
|
728
|
+
|
|
729
|
+
currentRoot = parentRoot;
|
|
730
|
+
}
|
|
718
731
|
|
|
719
732
|
return {
|
|
720
733
|
filepath: fallbackFilepath,
|
|
721
|
-
warning: `Missing ${relativePath} in ${appRoot}; using ${fallbackFilepath}.`,
|
|
734
|
+
warning: `Missing ${relativePath} in ${appRoot} and its repository ancestors; using ${fallbackFilepath}.`,
|
|
722
735
|
};
|
|
723
736
|
};
|
|
724
737
|
|
|
@@ -1143,12 +1156,20 @@ export const explainOwner = (manifest: TProteumManifest, query: string): TExplai
|
|
|
1143
1156
|
const normalizedQuery = normalizeText(query);
|
|
1144
1157
|
if (!normalizedQuery) return { matches: [], normalizedQuery, query };
|
|
1145
1158
|
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1159
|
+
const entries = buildManifestEntries(manifest);
|
|
1160
|
+
const scoredMatches =
|
|
1161
|
+
normalizedQuery === '/'
|
|
1162
|
+
? entries
|
|
1163
|
+
.filter((entry) => (entry.kind === 'route' || entry.kind === 'controller') && normalizeText(entry.label) === '/')
|
|
1164
|
+
.map((entry) => toOwnerMatch(entry, 200, ['/']))
|
|
1165
|
+
: entries
|
|
1166
|
+
.map((entry) => {
|
|
1167
|
+
const { score, matchedOn } = scoreOwnerMatch(query, entry);
|
|
1168
|
+
return score > 0 ? toOwnerMatch(entry, score, matchedOn) : undefined;
|
|
1169
|
+
})
|
|
1170
|
+
.filter((match): match is TExplainOwnerMatch => match !== undefined);
|
|
1171
|
+
|
|
1172
|
+
const matches = scoredMatches
|
|
1152
1173
|
.sort((left, right) => right.score - left.score || left.kind.localeCompare(right.kind) || left.label.localeCompare(right.label))
|
|
1153
1174
|
.slice(0, 12);
|
|
1154
1175
|
|