proteum 2.2.9 → 2.4.1

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.
Files changed (59) hide show
  1. package/AGENTS.md +10 -4
  2. package/README.md +58 -15
  3. package/agents/project/AGENTS.md +53 -10
  4. package/agents/project/DOCUMENTATION.md +1326 -0
  5. package/agents/project/app-root/AGENTS.md +2 -2
  6. package/agents/project/diagnostics.md +12 -7
  7. package/agents/project/optimizations.md +1 -0
  8. package/agents/project/root/AGENTS.md +24 -9
  9. package/agents/project/tests/AGENTS.md +7 -0
  10. package/agents/project/tests/e2e/AGENTS.md +13 -0
  11. package/agents/project/tests/e2e/REAL_WORLD_JOURNEY_TESTS.md +192 -0
  12. package/cli/commands/connect.ts +40 -4
  13. package/cli/commands/dev.ts +148 -25
  14. package/cli/commands/diagnose.ts +138 -5
  15. package/cli/commands/doctor.ts +24 -4
  16. package/cli/commands/explain.ts +134 -6
  17. package/cli/commands/mcp.ts +133 -0
  18. package/cli/commands/orient.ts +93 -3
  19. package/cli/commands/perf.ts +118 -13
  20. package/cli/commands/runtime.ts +234 -0
  21. package/cli/commands/trace.ts +116 -21
  22. package/cli/mcp/router.ts +1010 -0
  23. package/cli/presentation/commands.ts +93 -26
  24. package/cli/presentation/devSession.ts +2 -0
  25. package/cli/presentation/help.ts +1 -1
  26. package/cli/runtime/commands.ts +215 -24
  27. package/cli/runtime/devSessions.ts +328 -2
  28. package/cli/runtime/mcpDaemon.ts +288 -0
  29. package/cli/runtime/ports.ts +151 -0
  30. package/cli/utils/agentOutput.ts +46 -0
  31. package/cli/utils/agents.ts +194 -51
  32. package/cli/utils/appRoots.ts +232 -0
  33. package/common/dev/diagnostics.ts +1 -1
  34. package/common/dev/inspection.ts +22 -7
  35. package/common/dev/mcpPayloads.ts +1150 -0
  36. package/common/dev/mcpServer.ts +287 -0
  37. package/docs/agent-routing.md +137 -0
  38. package/docs/dev-commands.md +2 -0
  39. package/docs/dev-sessions.md +4 -1
  40. package/docs/diagnostics.md +70 -24
  41. package/docs/mcp.md +206 -0
  42. package/docs/migrate-from-2.1.3.md +14 -6
  43. package/docs/request-tracing.md +12 -6
  44. package/package.json +11 -3
  45. package/server/app/devMcp.ts +204 -0
  46. package/server/services/router/http/cache.ts +116 -0
  47. package/server/services/router/http/index.ts +94 -35
  48. package/server/services/router/index.ts +8 -11
  49. package/server/services/router/request/ip.test.cjs +0 -1
  50. package/tests/agents-utils.test.cjs +92 -14
  51. package/tests/cli-mcp-command.test.cjs +262 -0
  52. package/tests/codex-mcp-usage.test.cjs +307 -0
  53. package/tests/dev-sessions.test.cjs +113 -0
  54. package/tests/dev-transpile-watch.test.cjs +117 -9
  55. package/tests/eslint-rules.test.cjs +0 -1
  56. package/tests/inspection.test.cjs +66 -0
  57. package/tests/mcp.test.cjs +873 -0
  58. package/tests/router-cache-config.test.cjs +73 -0
  59. package/vitest.config.mjs +9 -0
@@ -1,8 +1,72 @@
1
1
  import { Builtins, Cli, Option } from 'clipanion';
2
+ import path from 'path';
2
3
 
3
- import type { TArgsObject } from '../context';
4
+ import cli, { type TArgsObject } from '../context';
4
5
  import { applyLegacyBooleanArgs, assertNoLegacyArgs } from './argv';
5
6
  import { buildUsage, ProteumCommand, runCommandModule } from './command';
7
+ import { createStartDevCommand, quoteShellPath, resolveProteumAppRootContext } from '../utils/appRoots';
8
+ import { printJson } from '../utils/agentOutput';
9
+
10
+ const createRunInAppCommand = ({
11
+ appRoot,
12
+ baseRoot,
13
+ command,
14
+ }: {
15
+ appRoot: string;
16
+ baseRoot: string;
17
+ command: string;
18
+ }) => {
19
+ const relativeAppRoot = path.relative(baseRoot, appRoot) || '.';
20
+ if (relativeAppRoot === '.') return command;
21
+ return `cd ${quoteShellPath(relativeAppRoot)} && ${command}`;
22
+ };
23
+
24
+ const printNonAppRootResponse = ({
25
+ commandName,
26
+ cwd,
27
+ }: {
28
+ commandName: 'dev' | 'runtime';
29
+ cwd: string;
30
+ }) => {
31
+ const context = resolveProteumAppRootContext(cwd);
32
+ const appCandidates = context.appCandidates;
33
+ const commandLabel = commandName === 'dev' ? 'Dev' : 'Runtime Status';
34
+
35
+ printJson({
36
+ ok: false,
37
+ format: 'proteum-agent-v1',
38
+ summary:
39
+ appCandidates.length > 0
40
+ ? `${cwd} is a Proteum workspace wrapper or nested directory, not an app root. Found ${appCandidates.length} app candidate${appCandidates.length === 1 ? '' : 's'}.`
41
+ : `${cwd} is not a Proteum app root.`,
42
+ data: {
43
+ cwd: context.cwd,
44
+ appCandidates,
45
+ },
46
+ nextActions: appCandidates.map((candidate) => ({
47
+ label: `${commandLabel}: ${candidate.relativeAppRoot || candidate.appRoot}`,
48
+ command:
49
+ commandName === 'dev'
50
+ ? createStartDevCommand({
51
+ appRoot: candidate.appRoot,
52
+ baseRoot: context.cwd,
53
+ port: candidate.manifest?.routerPort,
54
+ })
55
+ : createRunInAppCommand({
56
+ appRoot: candidate.appRoot,
57
+ baseRoot: context.cwd,
58
+ command: 'npx proteum runtime status',
59
+ }),
60
+ reason:
61
+ commandName === 'dev'
62
+ ? 'Start Proteum dev from the app root, not the workspace wrapper.'
63
+ : 'Inspect tracked runtime sessions from the app root, not the workspace wrapper.',
64
+ })),
65
+ });
66
+ process.exitCode = 1;
67
+ };
68
+
69
+ const isCurrentWorkdirProteumAppRoot = () => resolveProteumAppRootContext(String(cli.args.workdir || process.cwd())).isAppRoot;
6
70
 
7
71
  class InitCommand extends ProteumCommand {
8
72
  public static paths = [['init']];
@@ -154,6 +218,10 @@ class DevCommand extends ProteumCommand {
154
218
  all: this.all,
155
219
  stale: this.stale,
156
220
  });
221
+ if (!isCurrentWorkdirProteumAppRoot()) {
222
+ printNonAppRootResponse({ commandName: 'dev', cwd: String(cli.args.workdir || process.cwd()) });
223
+ return 1;
224
+ }
157
225
  await runCommandModule(() => import('../commands/dev'));
158
226
  }
159
227
  }
@@ -324,14 +392,16 @@ class ConnectCommand extends ProteumCommand {
324
392
  public controllers = Option.Boolean('--controllers', false, {
325
393
  description: 'Include imported connected controllers in the output.',
326
394
  });
327
- public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
395
+ public json = Option.Boolean('--json', false, { description: 'Compatibility flag; compact JSON is the default output.' });
396
+ public full = Option.Boolean('--full', false, { description: 'Print the full connect payload.' });
397
+ public human = Option.Boolean('--human', false, { description: 'Print the legacy human-readable report.' });
328
398
  public strict = Option.Boolean('--strict', false, { description: 'Exit with failure if any connect diagnostics exist.' });
329
399
  public legacyArgs = Option.Rest();
330
400
 
331
401
  public async execute() {
332
- const args = { controllers: this.controllers, json: this.json, strict: this.strict } satisfies TArgsObject;
402
+ const args = { controllers: this.controllers, full: this.full, human: this.human, json: this.json, strict: this.strict } satisfies TArgsObject;
333
403
 
334
- applyLegacyBooleanArgs('connect', this.legacyArgs, ['controllers', 'json', 'strict'], args);
404
+ applyLegacyBooleanArgs('connect', this.legacyArgs, ['controllers', 'full', 'human', 'json', 'strict'], args);
335
405
  this.setCliArgs(args);
336
406
  await runCommandModule(() => import('../commands/connect'));
337
407
  }
@@ -345,14 +415,16 @@ class DoctorCommand extends ProteumCommand {
345
415
  public contracts = Option.Boolean('--contracts', false, {
346
416
  description: 'Run contract-focused diagnostics for generated artifacts and manifest-owned source files.',
347
417
  });
348
- public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
418
+ public json = Option.Boolean('--json', false, { description: 'Compatibility flag; compact JSON is the default output.' });
419
+ public full = Option.Boolean('--full', false, { description: 'Print the full doctor payload.' });
420
+ public human = Option.Boolean('--human', false, { description: 'Print the legacy human-readable report.' });
349
421
  public strict = Option.Boolean('--strict', false, { description: 'Exit with failure if any diagnostics exist.' });
350
422
  public legacyArgs = Option.Rest();
351
423
 
352
424
  public async execute() {
353
- const args = { contracts: this.contracts, json: this.json, strict: this.strict } satisfies TArgsObject;
425
+ const args = { contracts: this.contracts, full: this.full, human: this.human, json: this.json, strict: this.strict } satisfies TArgsObject;
354
426
 
355
- applyLegacyBooleanArgs('doctor', this.legacyArgs, ['contracts', 'json', 'strict'], args);
427
+ applyLegacyBooleanArgs('doctor', this.legacyArgs, ['contracts', 'full', 'human', 'json', 'strict'], args);
356
428
  this.setCliArgs(args);
357
429
  await runCommandModule(() => import('../commands/doctor'));
358
430
  }
@@ -363,19 +435,22 @@ class ExplainCommand extends ProteumCommand {
363
435
 
364
436
  public static usage = buildUsage('explain');
365
437
 
366
- public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
367
- public all = Option.Boolean('--all', false, { description: 'Include every explain section.' });
368
- public app = Option.Boolean('--app', false, { description: 'Include the app section.' });
369
- public conventions = Option.Boolean('--conventions', false, { description: 'Include the conventions section.' });
370
- public env = Option.Boolean('--env', false, { description: 'Include the env section.' });
371
- public connected = Option.Boolean('--connected', false, { description: 'Include the connected-projects section.' });
372
- public services = Option.Boolean('--services', false, { description: 'Include the services section.' });
373
- public controllers = Option.Boolean('--controllers', false, { description: 'Include the controllers section.' });
374
- public commands = Option.Boolean('--commands', false, { description: 'Include the commands section.' });
375
- public routes = Option.Boolean('--routes', false, { description: 'Include the routes section.' });
376
- public layouts = Option.Boolean('--layouts', false, { description: 'Include the layouts section.' });
438
+ public json = Option.Boolean('--json', false, { description: 'Compatibility flag; compact JSON is the default output.' });
439
+ public full = Option.Boolean('--full', false, { description: 'Print the full selected machine-readable detail.' });
440
+ public human = Option.Boolean('--human', false, { description: 'Print the legacy human-readable report.' });
441
+ public manifest = Option.Boolean('--manifest', false, { description: 'Print the full generated manifest.' });
442
+ public all = Option.Boolean('--all', false, { description: 'Summarize every explain section; add --full for raw arrays.' });
443
+ public app = Option.Boolean('--app', false, { description: 'Summarize the app section; add --full for raw detail.' });
444
+ public conventions = Option.Boolean('--conventions', false, { description: 'Summarize the conventions section; add --full for raw detail.' });
445
+ public env = Option.Boolean('--env', false, { description: 'Summarize the env section; add --full for raw detail.' });
446
+ public connected = Option.Boolean('--connected', false, { description: 'Summarize the connected-projects section; add --full for raw detail.' });
447
+ public services = Option.Boolean('--services', false, { description: 'Summarize the services section; add --full for raw detail.' });
448
+ public controllers = Option.Boolean('--controllers', false, { description: 'Summarize the controllers section; add --full for raw detail.' });
449
+ public commands = Option.Boolean('--commands', false, { description: 'Summarize the commands section; add --full for raw detail.' });
450
+ public routes = Option.Boolean('--routes', false, { description: 'Summarize the routes section; add --full for raw detail.' });
451
+ public layouts = Option.Boolean('--layouts', false, { description: 'Summarize the layouts section; add --full for raw detail.' });
377
452
  public diagnostics = Option.Boolean('--diagnostics', false, {
378
- description: 'Include the diagnostics section.',
453
+ description: 'Summarize the diagnostics section; add --full for raw detail.',
379
454
  });
380
455
  public args = Option.Rest();
381
456
 
@@ -384,6 +459,9 @@ class ExplainCommand extends ProteumCommand {
384
459
  if (mode === 'owner') {
385
460
  this.setCliArgs({
386
461
  json: this.json,
462
+ full: this.full,
463
+ human: this.human,
464
+ manifest: this.manifest,
387
465
  ownerQuery: restArgs.join(' ').trim(),
388
466
  });
389
467
  await runCommandModule(() => import('../commands/explain'));
@@ -392,6 +470,9 @@ class ExplainCommand extends ProteumCommand {
392
470
 
393
471
  const args = {
394
472
  json: this.json,
473
+ full: this.full,
474
+ human: this.human,
475
+ manifest: this.manifest,
395
476
  all: this.all,
396
477
  app: this.app,
397
478
  conventions: this.conventions,
@@ -408,7 +489,7 @@ class ExplainCommand extends ProteumCommand {
408
489
  applyLegacyBooleanArgs(
409
490
  'explain',
410
491
  this.args,
411
- ['json', 'all', 'app', 'conventions', 'env', 'connected', 'services', 'controllers', 'commands', 'routes', 'layouts', 'diagnostics'],
492
+ ['json', 'full', 'human', 'manifest', 'all', 'app', 'conventions', 'env', 'connected', 'services', 'controllers', 'commands', 'routes', 'layouts', 'diagnostics'],
412
493
  args,
413
494
  );
414
495
  this.setCliArgs(args);
@@ -423,7 +504,9 @@ class OrientCommand extends ProteumCommand {
423
504
 
424
505
  public port = Option.String('--port', { description: 'Target an existing dev server on the given port.' });
425
506
  public url = Option.String('--url', { description: 'Target an existing dev server at the given base URL.' });
426
- public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
507
+ public json = Option.Boolean('--json', false, { description: 'Compatibility flag; compact JSON is the default output.' });
508
+ public full = Option.Boolean('--full', false, { description: 'Print the full orientation payload.' });
509
+ public human = Option.Boolean('--human', false, { description: 'Print the legacy human-readable report.' });
427
510
  public args = Option.Rest();
428
511
 
429
512
  public async execute() {
@@ -431,6 +514,8 @@ class OrientCommand extends ProteumCommand {
431
514
 
432
515
  this.setCliArgs({
433
516
  json: this.json,
517
+ full: this.full,
518
+ human: this.human,
434
519
  port: this.port ?? '',
435
520
  query,
436
521
  url: this.url ?? '',
@@ -447,7 +532,10 @@ class TraceCommand extends ProteumCommand {
447
532
 
448
533
  public port = Option.String('--port', { description: 'Override the router port used to query the running dev server.' });
449
534
  public url = Option.String('--url', { description: 'Override the full base URL used to query the running dev server.' });
450
- public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
535
+ public json = Option.Boolean('--json', false, { description: 'Compatibility flag; compact JSON is the default output.' });
536
+ public full = Option.Boolean('--full', false, { description: 'Print the full trace response.' });
537
+ public events = Option.Boolean('--events', false, { description: 'Include full event, call, SQL, and payload detail.' });
538
+ public human = Option.Boolean('--human', false, { description: 'Print the legacy human-readable trace report.' });
451
539
  public capture = Option.String('--capture', { description: 'Capture mode used by `proteum trace arm`.' });
452
540
  public output = Option.String('--output', { description: 'Output filepath used by `proteum trace export`.' });
453
541
  public args = Option.Rest();
@@ -461,6 +549,9 @@ class TraceCommand extends ProteumCommand {
461
549
  port: this.port ?? '',
462
550
  url: this.url ?? '',
463
551
  json: this.json,
552
+ full: this.full,
553
+ events: this.events,
554
+ human: this.human,
464
555
  capture: this.capture ?? '',
465
556
  output: this.output ?? '',
466
557
  });
@@ -526,7 +617,9 @@ class DiagnoseCommand extends ProteumCommand {
526
617
 
527
618
  public port = Option.String('--port', { description: 'Target an existing dev server on the given port.' });
528
619
  public url = Option.String('--url', { description: 'Target an existing dev server at the given base URL.' });
529
- public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
620
+ public json = Option.Boolean('--json', false, { description: 'Compatibility flag; compact JSON is the default output.' });
621
+ public full = Option.Boolean('--full', false, { description: 'Print the full diagnose payload.' });
622
+ public human = Option.Boolean('--human', false, { description: 'Print the legacy human-readable report.' });
530
623
  public hit = Option.String('--hit', { description: 'Issue one HTTP request before diagnosing. Defaults to the target path when it starts with /.' });
531
624
  public method = Option.String('--method', { description: 'HTTP method used with `--hit`.' });
532
625
  public dataJson = Option.String('--data-json', { description: 'JSON request body used with `--hit`.' });
@@ -547,6 +640,8 @@ class DiagnoseCommand extends ProteumCommand {
547
640
  dataJson: this.dataJson ?? '',
548
641
  hit: this.hit ?? '',
549
642
  json: this.json,
643
+ full: this.full,
644
+ human: this.human,
550
645
  logsLevel: this.logsLevel ?? '',
551
646
  logsLimit: this.logsLimit ?? '',
552
647
  method: this.method ?? '',
@@ -568,7 +663,9 @@ class PerfCommand extends ProteumCommand {
568
663
 
569
664
  public port = Option.String('--port', { description: 'Target an existing dev server on the given port.' });
570
665
  public url = Option.String('--url', { description: 'Target an existing dev server at the given base URL.' });
571
- public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
666
+ public json = Option.Boolean('--json', false, { description: 'Compatibility flag; compact JSON is the default output.' });
667
+ public full = Option.Boolean('--full', false, { description: 'Print the full perf payload.' });
668
+ public human = Option.Boolean('--human', false, { description: 'Print the legacy human-readable report.' });
572
669
  public since = Option.String('--since', { description: 'Window used by `top` and `memory`, for example `today`, `yesterday`, or `1h`.' });
573
670
  public baseline = Option.String('--baseline', { description: 'Baseline window used by `compare`.' });
574
671
  public target = Option.String('--target', { description: 'Target window used by `compare`.' });
@@ -584,6 +681,8 @@ class PerfCommand extends ProteumCommand {
584
681
  baseline: this.baseline ?? '',
585
682
  groupBy: this.groupBy ?? '',
586
683
  json: this.json,
684
+ full: this.full,
685
+ human: this.human,
587
686
  limit: this.limit ?? '',
588
687
  port: this.port ?? '',
589
688
  since: this.since ?? '',
@@ -596,6 +695,94 @@ class PerfCommand extends ProteumCommand {
596
695
  }
597
696
  }
598
697
 
698
+ class RuntimeCommand extends ProteumCommand {
699
+ public static paths = [['runtime']];
700
+
701
+ public static usage = buildUsage('runtime');
702
+
703
+ public full = Option.Boolean('--full', false, { description: 'Print full tracked-session and health detail.' });
704
+ public manifest = Option.Boolean('--manifest', false, {
705
+ description: 'Unsupported compatibility guard. Use `proteum explain --manifest` instead.',
706
+ });
707
+ public sessionFile = Option.String('--session-file', {
708
+ description: 'Inspect one explicit dev session file instead of the app registry.',
709
+ });
710
+ public args = Option.Rest();
711
+
712
+ public async execute() {
713
+ const [action = 'status'] = this.args;
714
+
715
+ this.setCliArgs({
716
+ action,
717
+ full: this.full,
718
+ manifest: this.manifest,
719
+ sessionFile: this.sessionFile ?? '',
720
+ });
721
+
722
+ if (this.manifest) {
723
+ printJson({
724
+ ok: false,
725
+ format: 'proteum-agent-v1',
726
+ summary: '`proteum runtime status --manifest` is not supported. Use `proteum explain --manifest` from the app root.',
727
+ data: {
728
+ command: 'proteum runtime status --manifest',
729
+ },
730
+ nextActions: [
731
+ {
732
+ label: 'Explain Manifest',
733
+ command: 'npx proteum explain --manifest',
734
+ reason: 'The generated manifest belongs to the explain command, not runtime status.',
735
+ },
736
+ ],
737
+ });
738
+ process.exitCode = 1;
739
+ return 1;
740
+ }
741
+
742
+ if (!isCurrentWorkdirProteumAppRoot()) {
743
+ printNonAppRootResponse({ commandName: 'runtime', cwd: String(cli.args.workdir || process.cwd()) });
744
+ return 1;
745
+ }
746
+
747
+ await runCommandModule(() => import('../commands/runtime'));
748
+ }
749
+ }
750
+
751
+ class McpCommand extends ProteumCommand {
752
+ public static paths = [['mcp']];
753
+
754
+ public static usage = buildUsage('mcp');
755
+
756
+ public daemon = Option.Boolean('--daemon', false, {
757
+ description: 'Run the managed machine-scope MCP daemon over local HTTP.',
758
+ });
759
+ public stdio = Option.Boolean('--stdio', false, {
760
+ description: 'Force stdio MCP transport for an MCP client.',
761
+ });
762
+ public port = Option.String('--port', {
763
+ description: 'Port for the managed machine MCP daemon.',
764
+ });
765
+ public json = Option.Boolean('--json', false, {
766
+ description: 'Print machine-readable daemon status output.',
767
+ });
768
+ public args = Option.Rest();
769
+
770
+ public async execute() {
771
+ const [action = 'start', ...restArgs] = this.args;
772
+
773
+ assertNoLegacyArgs('mcp', restArgs);
774
+ this.setCliArgs({
775
+ action,
776
+ daemon: this.daemon,
777
+ stdio: this.stdio,
778
+ port: this.port ?? '',
779
+ json: this.json,
780
+ });
781
+
782
+ await runCommandModule(() => import('../commands/mcp'));
783
+ }
784
+ }
785
+
599
786
  class VerifyCommand extends ProteumCommand {
600
787
  public static paths = [['verify']];
601
788
 
@@ -672,6 +859,8 @@ export const registeredCommands = {
672
859
  orient: OrientCommand,
673
860
  diagnose: DiagnoseCommand,
674
861
  perf: PerfCommand,
862
+ runtime: RuntimeCommand,
863
+ mcp: McpCommand,
675
864
  trace: TraceCommand,
676
865
  command: CommandCommand,
677
866
  session: SessionCommand,
@@ -705,6 +894,8 @@ export const createCli = (version: string) => {
705
894
  clipanion.register(OrientCommand);
706
895
  clipanion.register(DiagnoseCommand);
707
896
  clipanion.register(PerfCommand);
897
+ clipanion.register(RuntimeCommand);
898
+ clipanion.register(McpCommand);
708
899
  clipanion.register(TraceCommand);
709
900
  clipanion.register(CommandCommand);
710
901
  clipanion.register(SessionCommand);