proteum 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,7 +28,7 @@ Proteum combines:
28
28
  - **Explicit request entrypoints.** Controllers are classes. Request access is explicit through `this.request`.
29
29
  - **Local validation.** Validate handler input inside the handler with `this.input(schema)`.
30
30
  - **Deterministic generation.** Proteum owns `.proteum/` and regenerates it from source.
31
- - **Explainability matters.** `proteum explain`, `proteum doctor`, and `proteum trace` expose the framework view of your app and its live requests.
31
+ - **Explainability matters.** `proteum explain`, `proteum doctor`, and `proteum trace` expose the framework view of your app and its live requests, and the profiler renders the same diagnostics surfaces for humans in dev.
32
32
  - **SEO is not an afterthought.** Identity, routes, layouts, and SSR data are part of the app contract.
33
33
 
34
34
  ## What a Proteum App Looks Like
@@ -289,7 +289,8 @@ Proteum ships with a compact CLI focused on the real app lifecycle:
289
289
  | `proteum explain` | Explain routes, controllers, services, layouts, conventions, and env |
290
290
  | `proteum trace` | Inspect live dev-only request traces from the running SSR server |
291
291
  | `proteum command` | Run a dev-only internal command locally or against a running dev server |
292
- | `proteum init` | Experimental project scaffolding when scaffold assets are installed |
292
+ | `proteum init` | Scaffold a new Proteum app with built-in deterministic templates |
293
+ | `proteum create` | Scaffold a page, controller, command, route, or root service inside an app |
293
294
 
294
295
  Recommended daily workflow:
295
296
 
@@ -315,6 +316,18 @@ proteum trace arm --capture deep
315
316
  proteum trace latest
316
317
  ```
317
318
 
319
+ Useful scaffolding commands:
320
+
321
+ ```bash
322
+ proteum init my-app --name "My App"
323
+ proteum init my-app --name "My App" --dry-run --json
324
+ proteum create page marketing/faq --route /faq
325
+ proteum create controller Founder/projects --method list
326
+ proteum create service Conversion/Plans
327
+ ```
328
+
329
+ `proteum explain` and `proteum doctor` share the same manifest-backed diagnostics contract as the profiler `Explain` and `Doctor` tabs. For the full dev diagnostics model, see [docs/diagnostics.md](docs/diagnostics.md).
330
+
318
331
  ## Dev Commands
319
332
 
320
333
  Proteum includes a dev-only command surface for internal testing, debugging, and one-off execution that should not become a normal controller or route.
@@ -326,12 +339,15 @@ Proteum includes a dev-only command surface for internal testing, debugging, and
326
339
  - `proteum command foo/bar` refreshes generated artifacts, builds the dev output, starts a temporary local dev server, runs the command, prints the result, and exits
327
340
  - `proteum command foo/bar --port 3101` runs the same command against an existing `proteum dev` instance
328
341
  - the dev profiler exposes the same command list and run action through the `Commands` tab
342
+ - the same profiler also exposes `Explain` and `Doctor` tabs backed by the same manifest diagnostics contract as the CLI
329
343
 
330
344
  Proteum itself also ships a small built-in diagnostic command at `proteum/diagnostics/ping`, so the command surface is never empty in dev.
331
345
 
332
346
  ## Request Tracing
333
347
 
334
- Proteum includes a dev-only in-memory request trace buffer for routing, controller, context, SSR, and render debugging.
348
+ Proteum includes a dev-only in-memory request trace buffer for auth, routing, controller, context, SSR, and render debugging.
349
+
350
+ This is separate from `proteum explain` and `proteum doctor`: tracing is live request-time data, while explain/doctor are manifest-backed structure and diagnostics.
335
351
 
336
352
  When diagnosing or testing against an app, first read the default port from `PORT` or `./.proteum/manifest.json` and check whether a server is already running there. If it is, inspect the existing traces before reproducing the issue so you can collect past errors and their context.
337
353
 
@@ -363,7 +379,7 @@ export TRACE_PERSIST_ON_ERROR=true
363
379
  Capture modes:
364
380
 
365
381
  - `summary`: request lifecycle plus high-signal events
366
- - `resolve`: adds route resolution and controller/context steps
382
+ - `resolve`: adds auth, route resolution, and controller/context steps
367
383
  - `deep`: adds route skip reasons and deeper payload summaries for one request investigation
368
384
 
369
385
  The trace CLI talks to the running dev server over the dev-only `__proteum/trace` HTTP endpoints. Use `--port` for a different local port or `--url` when the host itself is non-standard. For the full guide, see [docs/request-tracing.md](docs/request-tracing.md).
@@ -387,6 +403,7 @@ Proteum answers those questions with explicit artifacts:
387
403
  - `.proteum/manifest.json` for machine-readable app structure
388
404
  - `proteum explain --json` for structured framework introspection
389
405
  - `proteum doctor --json` for structured diagnostics
406
+ - the profiler `Explain` and `Doctor` tabs for a human-readable view over the same manifest-backed contract
390
407
  - `proteum command ...` plus the profiler `Commands` tab for dev-only internal execution
391
408
 
392
409
  If you are an LLM or automation agent, start here:
@@ -449,15 +466,17 @@ Install in an app:
449
466
  npm install proteum
450
467
  ```
451
468
 
452
- If the scaffold assets are available in your distribution, you can bootstrap a new app with:
469
+ You can bootstrap a new app with:
453
470
 
454
471
  ```bash
455
- npx proteum init
472
+ npx proteum init my-app --name "My App"
473
+ npx proteum init my-app --name "My App" --dry-run --json
456
474
  ```
457
475
 
458
476
  Then use the normal workflow:
459
477
 
460
478
  ```bash
479
+ npm install
461
480
  npx proteum dev
462
481
  npx proteum check
463
482
  npx proteum build --prod
@@ -66,6 +66,14 @@ Prefer structured CLI surfaces over re-deriving framework facts from source:
66
66
  - `npx proteum doctor --json`
67
67
  - `npx proteum trace ...`
68
68
  - `npx proteum command ...`
69
+ - `npx proteum create ... --dry-run --json`
70
+
71
+ Prefer scaffold commands before hand-writing boilerplate:
72
+
73
+ - Use `npx proteum init <directory> --name <name>` for new apps.
74
+ - Use `npx proteum init ... --dry-run --json` when an agent needs a machine-readable app plan before writing files.
75
+ - Use `npx proteum create page|controller|command|route|service <target>` for new app artifacts before creating the files manually.
76
+ - Use `npx proteum create ... --dry-run --json` when an agent needs a machine-readable artifact plan before writing files.
69
77
 
70
78
  ## File Contracts
71
79
 
@@ -79,6 +87,7 @@ Prefer structured CLI surfaces over re-deriving framework facts from source:
79
87
  - Business logic lives in classes that extend `Service` and use `this.services`, `this.models`, and `this.app`.
80
88
  - Keep auth, input parsing, locale, cookies, and request-derived values in controllers, then pass explicit typed arguments into services.
81
89
  - Split growing features into explicit subservices.
90
+ - `proteum create service ...` scaffolds the service file, its `service.json`, a typed config export under `server/config/*.ts`, and the root registration in `server/index.ts`; review and adapt the generated names before committing.
82
91
 
83
92
  ### Controllers
84
93
 
@@ -87,6 +96,7 @@ Prefer structured CLI surfaces over re-deriving framework facts from source:
87
96
  - Route path comes from the controller file path plus the method name.
88
97
  - `export const controllerPath = 'Custom/path'` can override the base path.
89
98
  - Generated client calls use `POST`.
99
+ - Prefer `proteum create controller ...` for new controller boilerplate, then adapt the generated method to real service calls.
90
100
 
91
101
  ### Commands
92
102
 
@@ -96,6 +106,7 @@ Prefer structured CLI surfaces over re-deriving framework facts from source:
96
106
  - `export const commandPath = 'Custom/path'` can override the base path.
97
107
  - Commands are for dev-only internal execution through `proteum command ...` or the profiler `Commands` tab.
98
108
  - Keep command logic internal; do not turn it into a normal controller unless it is a real app API.
109
+ - Prefer `proteum create command ...` for new command boilerplate.
99
110
 
100
111
  ### Client Pages
101
112
 
@@ -108,6 +119,7 @@ Prefer structured CLI surfaces over re-deriving framework facts from source:
108
119
  - `render` consumes resolved setup data and uses generated controller methods from render args or `@/client/context`.
109
120
  - Use `api.reload(...)` or `api.set(...)` only when intentionally mutating active page setup state.
110
121
  - Error pages use `Router.error(code, options, render)` in `client/pages/_messages/**`.
122
+ - Prefer `proteum create page ...` for new page boilerplate, then review the explicit route path and setup payload.
111
123
 
112
124
  ### Manual Routes
113
125
 
@@ -115,6 +127,7 @@ Prefer structured CLI surfaces over re-deriving framework facts from source:
115
127
  - Good fits include redirects, sitemap or RSS output, OAuth callbacks, webhooks, and public resources with custom semantics.
116
128
  - Import server-side app services from `@app` and use route handler context for `request`, `response`, router plugins, and custom router context.
117
129
  - If the route is a normal app API, prefer a controller.
130
+ - Prefer `proteum create route ...` for new manual-route boilerplate.
118
131
 
119
132
  ### Models And Aliases
120
133
 
@@ -161,4 +174,4 @@ Verify at the correct layer:
161
174
 
162
175
  When an app may already be running, check the default port from `PORT` or `./.proteum/manifest.json` and inspect `proteum trace requests`, `proteum trace latest`, and `proteum trace show <requestId>` before reproducing the issue. If those traces are not enough, arm `npx proteum trace arm --capture deep`, reproduce once, then inspect the new request.
163
176
 
164
- Useful commands: `proteum dev`, `npx proteum refresh`, `npx proteum typecheck`, `npx proteum lint`, `npx proteum check`, `npx proteum build prod`, `npx proteum command <path>`.
177
+ Useful commands: `npx proteum init <dir> --name <name>`, `npx proteum create <kind> <target>`, `proteum dev`, `npx proteum refresh`, `npx proteum typecheck`, `npx proteum lint`, `npx proteum check`, `npx proteum build prod`, `npx proteum command <path>`.
@@ -12,6 +12,8 @@ Coding style source of truth: `./CODING_STYLE.md`.
12
12
  ## Fast Start
13
13
 
14
14
  - Start with `./.proteum/manifest.json`, `npx proteum explain`, and `npx proteum doctor`.
15
+ - For new app or artifact boilerplate, prefer `npx proteum init ...` and `npx proteum create ...` before creating files by hand.
16
+ - Use `--dry-run --json` on scaffold commands when an agent needs a machine-readable plan before writing files.
15
17
  - For request-time issues in dev, inspect traces before adding logs.
16
18
  - If a server is already running on the default port from `PORT` or `./.proteum/manifest.json`, inspect existing traces before reproducing the issue.
17
19
  - If existing traces are insufficient, arm `npx proteum trace arm --capture deep`, reproduce once, then inspect the new request.
@@ -58,6 +60,7 @@ This is a TypeScript, Node.js, Preact, Proteum monolith:
58
60
 
59
61
  - If the user pastes raw errors without asking for a fix, do not implement changes. List likely causes and, for each one, give probability, why, and how to fix it.
60
62
  - For request-time behavior in dev, check whether a server is already running on the default port and prefer `npx proteum trace` before reproducing the issue or adding logs.
63
+ - After running `npx proteum create ...`, adapt the generated code to the real feature instead of leaving placeholder logic in place.
61
64
  - End your work with `Commit message`: one short top-level sentence.
62
65
 
63
66
  ## High-Impact Files
@@ -0,0 +1,5 @@
1
+ import { runCreateScaffold } from '../scaffold';
2
+
3
+ export const run = async (): Promise<void> => {
4
+ await runCreateScaffold();
5
+ };
@@ -22,7 +22,7 @@ import {
22
22
  import Compiler from '../compiler';
23
23
  import { createDevEventServer } from './devEvents';
24
24
  import { ensureProjectAgentSymlinks } from '../utils/agents';
25
- import { renderDevSession, renderServerReadyBanner } from '../presentation/devSession';
25
+ import { renderDevSession, renderServerReadyBanner, renderDevShutdownBanner } from '../presentation/devSession';
26
26
  import { logVerbose } from '../runtime/verbose';
27
27
 
28
28
  // Core
@@ -448,6 +448,7 @@ export const run = async () => {
448
448
  await stopApp(reason);
449
449
  await cleanupPersistedDevTraces(app);
450
450
  await devEventServer.close();
451
+ console.info(await renderDevShutdownBanner());
451
452
  })();
452
453
 
453
454
  return shuttingDownPromise;
@@ -1,97 +1,5 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
1
+ import { runInitScaffold } from '../scaffold';
4
2
 
5
- // Npm
6
- import fs from 'fs-extra';
7
- import path from 'path';
8
- import prompts from 'prompts';
9
- import cmd from 'node-cmd';
10
- import replaceOnce from 'replace-once';
11
- import { UsageError } from 'clipanion';
12
-
13
- // Cor elibs
14
- import cli from '..';
15
- import { ensureProjectAgentSymlinks } from '../utils/agents';
16
-
17
- // Configs
18
- const filesToConfig = ['package.json', 'identity.yaml'];
19
-
20
- /*----------------------------------
21
- - COMMANDE
22
- ----------------------------------*/
23
3
  export const run = async (): Promise<void> => {
24
- const skeletonPath = path.join(cli.paths.core.cli, 'skeleton');
25
-
26
- if (!fs.existsSync(skeletonPath)) {
27
- throw new UsageError(
28
- 'Proteum init is unavailable in this checkout because `cli/skeleton` is missing. Restore the scaffold assets or use an installed Proteum package that includes them.',
29
- );
30
- }
31
-
32
- const config = await prompts([
33
- {
34
- type: 'text',
35
- name: 'name',
36
- message: 'Project name ?',
37
- initial: 'MyProject',
38
- validate: (value) => /[a-z0-9\-\.]/i.test(value) || 'Must only include alphanumeric characters, and - . ',
39
- },
40
- {
41
- type: 'text',
42
- name: 'dirname',
43
- message: 'Folder name ?',
44
- initial: (value) => value.toLowerCase(),
45
- validate: (value) =>
46
- /[a-z0-9\-\.]/.test(value) || 'Must only include lowercase alphanumeric characters, and - . ',
47
- },
48
- {
49
- type: 'text',
50
- name: 'description',
51
- message: 'Briefly describe your project to your mom:',
52
- initial: 'It will revolutionnize the world',
53
- validate: (value) => /[a-z0-9\-\. ]/i.test(value) || 'Must only include alphanumeric characters, and - . ',
54
- },
55
- { type: 'toggle', name: 'microservice', message: 'Separate API from the UI servers ?' },
56
- ]);
57
-
58
- const placeholders = {
59
- PROJECT_NAME: config.name,
60
- PACKAGE_NAME: config.name.toLowerCase(),
61
- PROJECT_DESCRIPTION: config.description,
62
- };
63
-
64
- const paths = {
65
- skeleton: skeletonPath,
66
- project: path.join(process.cwd(), config.dirname),
67
- };
68
-
69
- // Copy skeleton to cwd/<project-name>
70
- console.info('Creating project skeleton ...');
71
- fs.copySync(paths.skeleton, paths.project);
72
-
73
- // Sync framework-owned Codex assets into the new project.
74
- ensureProjectAgentSymlinks({ appRoot: paths.project, coreRoot: cli.paths.core.root });
75
-
76
- // Replace placeholders
77
- console.info('Configuring project ...');
78
- for (const file of filesToConfig) {
79
- console.log('- ' + file);
80
-
81
- const filepath = path.join(paths.project, file);
82
- const content = fs.readFileSync(filepath, 'utf-8');
83
-
84
- const placeholders_keys = Object.keys(placeholders).map((k) => '{{ ' + k + ' }}');
85
- const values = Object.values(placeholders);
86
-
87
- fs.writeFileSync(filepath, replaceOnce(content, placeholders_keys, values));
88
- }
89
-
90
- // Npm install
91
- console.info('Installing packages ...');
92
- cmd.runSync(`cd "${paths.project}" && npm i`);
93
-
94
- // Run demo app
95
- /*console.info("Run demo ...");
96
- await cli.shell('5htp dev');*/
4
+ await runInitScaffold();
97
5
  };
package/cli/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  process.traceDeprecation = true;
2
2
 
3
- import fs from 'fs';
4
3
  import { Cli } from 'clipanion';
5
4
 
6
5
  import cli from './context';
@@ -9,12 +8,10 @@ import { renderCliOverview, renderCommandHelp, resolveCustomHelpRequest } from '
9
8
  import { normalizeHelpArgv, normalizeLegacyArgv } from './runtime/argv';
10
9
  import { createCli, registeredCommands } from './runtime/commands';
11
10
 
12
- const hasInitScaffold = () => fs.existsSync(`${cli.paths.core.cli}/skeleton`);
13
-
14
11
  export const runCli = async (argv: string[] = process.argv.slice(2)) => {
15
12
  const normalizedArgv = normalizeHelpArgv(normalizeLegacyArgv(argv), proteumCommandNames);
16
13
  const clipanion = createCli(String(cli.packageJson.version || ''));
17
- const initAvailable = hasInitScaffold();
14
+ const initAvailable = true;
18
15
  const helpRequest = resolveCustomHelpRequest(normalizedArgv);
19
16
 
20
17
  if (helpRequest.kind === 'overview') {
@@ -5,6 +5,7 @@ import type { TRow } from './layout';
5
5
 
6
6
  export const proteumCommandNames = [
7
7
  'init',
8
+ 'create',
8
9
  'dev',
9
10
  'refresh',
10
11
  'build',
@@ -48,20 +49,55 @@ export const proteumCommandGroups: Array<{ title: string; names: TProteumCommand
48
49
  { title: 'Daily workflow', names: ['dev', 'refresh', 'build'] },
49
50
  { title: 'Quality gates', names: ['typecheck', 'lint', 'check'] },
50
51
  { title: 'Manifest and contracts', names: ['doctor', 'explain', 'trace', 'command'] },
51
- { title: 'Project scaffolding', names: ['init'] },
52
+ { title: 'Project scaffolding', names: ['init', 'create'] },
52
53
  ];
53
54
 
54
55
  export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> = {
55
56
  init: {
56
57
  name: 'init',
57
58
  category: 'Project scaffolding',
58
- summary: 'Scaffold a new Proteum project.',
59
- usage: 'proteum init',
60
- bestFor: 'Bootstrap a new app when the Proteum scaffold assets are installed in the current package.',
61
- examples: [{ description: 'Create a new app interactively', command: 'proteum init' }],
59
+ summary: 'Scaffold a new Proteum app with deterministic built-in templates.',
60
+ usage: 'proteum init [directory] [--name <name>] [--identifier <identifier>] [--port <port>] [--install] [--dry-run] [--json]',
61
+ bestFor: 'Bootstrapping a new app in a way that is explicit, machine-readable, and safe for LLM coding agents.',
62
+ examples: [
63
+ { description: 'Create a new app in ./my-app', command: 'proteum init my-app --name "My App"' },
64
+ {
65
+ description: 'Scaffold an app and install dependencies immediately',
66
+ command: 'proteum init my-app --name "My App" --install',
67
+ },
68
+ {
69
+ description: 'Emit scaffold details as JSON for an agent',
70
+ command: 'proteum init my-app --name "My App" --json',
71
+ },
72
+ {
73
+ description: 'Preview the full app scaffold without writing files',
74
+ command: 'proteum init my-app --name "My App" --dry-run --json',
75
+ },
76
+ ],
77
+ notes: [
78
+ 'When Proteum is invoked from a local framework checkout, init writes a file: dependency to that checkout by default.',
79
+ 'Use `--dry-run --json` when an agent needs a machine-readable app scaffold plan before writing files.',
80
+ 'Without `--install`, init only writes files and does not touch the network.',
81
+ ],
82
+ status: 'experimental',
83
+ },
84
+ create: {
85
+ name: 'create',
86
+ category: 'Project scaffolding',
87
+ summary: 'Generate a page, controller, command, route, or root service inside a Proteum app.',
88
+ usage: 'proteum create <page|controller|command|route|service> <target> [--route <url>] [--method <name>] [--http-method <verb>] [--dry-run] [--json]',
89
+ bestFor: 'Fast deterministic scaffolding inside an existing Proteum app without inventing file layouts or boilerplate by hand.',
90
+ examples: [
91
+ { description: 'Create a new SSR page', command: 'proteum create page marketing/faq --route /faq' },
92
+ { description: 'Create a new controller', command: 'proteum create controller Founder/projects --method list' },
93
+ { description: 'Create a new command', command: 'proteum create command diagnostics --method ping' },
94
+ { description: 'Preview a new route without writing files', command: 'proteum create route webhooks/stripe --dry-run --json' },
95
+ { description: 'Create and register a new root service', command: 'proteum create service Conversion/Plans' },
96
+ ],
62
97
  notes: [
63
- 'This command is still experimental.',
64
- 'In source checkouts it requires `cli/skeleton` to exist.',
98
+ 'Page scaffolds write `client/pages/**/index.tsx` and default the route path from the logical target path unless `--route` is provided.',
99
+ 'Service scaffolds create `server/services/**/index.ts`, `service.json`, a config export under `server/config/*.ts`, and then try to register the new root service in `server/index.ts`.',
100
+ 'Use `--dry-run --json` when an agent needs a machine-readable plan before writing files.',
65
101
  ],
66
102
  status: 'experimental',
67
103
  },
@@ -248,8 +284,8 @@ export const isLikelyProteumAppRoot = (workdir: string) =>
248
284
 
249
285
  export const getInitAvailabilityNote = (initAvailable: boolean) =>
250
286
  initAvailable
251
- ? 'Scaffold assets are installed in this checkout.'
252
- : 'This checkout does not include `cli/skeleton`, so `proteum init` is unavailable until the scaffold assets are restored.';
287
+ ? 'Init is built into the CLI and does not depend on external scaffold assets.'
288
+ : 'Init scaffolding is currently unavailable in this checkout.';
253
289
 
254
290
  export const createClipanionUsage = (command: TProteumCommandDoc) => ({
255
291
  category: command.category,
@@ -11,6 +11,8 @@ const ProteumWordmark = [
11
11
  String.raw`|_| |_| \_\\___/ |_| |_____|\___/|_| |_|`,
12
12
  ];
13
13
 
14
+ const ProteumTagline = 'Agent-first SSR compiler and server loop.';
15
+
14
16
  export const renderDevSession = async ({
15
17
  appName,
16
18
  appRoot,
@@ -32,9 +34,9 @@ export const renderDevSession = async ({
32
34
  return createElement(
33
35
  Box,
34
36
  { borderStyle: 'round', borderColor: 'cyan', paddingX: 2, paddingY: 0, flexDirection: 'column' },
35
- createElement(Text, { bold: true, color: 'green' }, 'PROTEUM DEV'),
36
- createElement(Text, { dimColor: true }, 'Agent-first SSR compiler and server loop.'),
37
- createElement(Box, { flexDirection: 'column', marginTop: 1 }, ...wordmark),
37
+ createElement(Text, { bold: true, backgroundColor: 'cyan', color: 'black' }, ' WELCOME TO '),
38
+ createElement(Box, { flexDirection: 'column' }, ...wordmark),
39
+ createElement(Text, { dimColor: true }, ProteumTagline),
38
40
  );
39
41
  }),
40
42
  renderRows(
@@ -73,3 +75,15 @@ export const renderServerReadyBanner = async ({
73
75
  createElement(Text, { dimColor: true }, `Trace latest: proteum trace latest --port ${routerPort}`),
74
76
  );
75
77
  });
78
+
79
+ export const renderDevShutdownBanner = async () =>
80
+ renderInk(({ Box, Text }) => {
81
+ const createElement = React.createElement;
82
+
83
+ return createElement(
84
+ Box,
85
+ { borderStyle: 'round', borderColor: 'yellow', paddingX: 2, paddingY: 0, flexDirection: 'column' },
86
+ createElement(Text, { bold: true, backgroundColor: 'yellow', color: 'black' }, ' SHUTTING DOWN '),
87
+ createElement(Text, { bold: true, color: 'yellow' }, 'Thank you for developping with Proteum'),
88
+ );
89
+ });