@powerhousedao/ph-clint-cli 0.1.0-dev.6 → 0.1.0-dev.60

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 (101) hide show
  1. package/dist/cli.js +52 -10
  2. package/dist/codegen/actions.d.ts +1 -0
  3. package/dist/codegen/actions.js +1 -0
  4. package/dist/codegen/builders/agent-ts.d.ts +4 -4
  5. package/dist/codegen/builders/agent-ts.js +92 -2
  6. package/dist/codegen/builders/app-index-ts.d.ts +3 -10
  7. package/dist/codegen/builders/app-index-ts.js +30 -20
  8. package/dist/codegen/builders/cli-package-json.d.ts +1 -8
  9. package/dist/codegen/builders/cli-package-json.js +1 -96
  10. package/dist/codegen/builders/cli-ts.js +88 -37
  11. package/dist/codegen/builders/config-ts.d.ts +1 -1
  12. package/dist/codegen/builders/config-ts.js +2 -8
  13. package/dist/codegen/builders/demo-agent-ts.d.ts +8 -0
  14. package/dist/codegen/builders/demo-agent-ts.js +50 -0
  15. package/dist/codegen/builders/eslint-config-js.d.ts +1 -1
  16. package/dist/codegen/builders/eslint-config-js.js +7 -4
  17. package/dist/codegen/builders/framework-gen-ts.d.ts +11 -6
  18. package/dist/codegen/builders/framework-gen-ts.js +73 -21
  19. package/dist/codegen/builders/framework-ts.js +33 -7
  20. package/dist/codegen/builders/index.d.ts +1 -44
  21. package/dist/codegen/builders/index.js +1 -61
  22. package/dist/codegen/builders/manifest-json.d.ts +6 -0
  23. package/dist/codegen/builders/manifest-json.js +31 -0
  24. package/dist/codegen/builders/mastra-index-ts.d.ts +3 -0
  25. package/dist/codegen/builders/mastra-index-ts.js +47 -5
  26. package/dist/codegen/builders/publish-config-js.d.ts +8 -0
  27. package/dist/codegen/builders/publish-config-js.js +33 -0
  28. package/dist/codegen/builders/publish-config-ts.d.ts +4 -2
  29. package/dist/codegen/builders/publish-config-ts.js +25 -11
  30. package/dist/codegen/builders/readme-md.js +7 -6
  31. package/dist/codegen/builders/root-package-json.js +14 -11
  32. package/dist/codegen/builders/smoke-test-ts.d.ts +2 -0
  33. package/dist/codegen/builders/smoke-test-ts.js +15 -0
  34. package/dist/codegen/exec.d.ts +1 -20
  35. package/dist/codegen/exec.js +1 -41
  36. package/dist/codegen/generated.d.ts +1 -0
  37. package/dist/codegen/generated.js +1 -0
  38. package/dist/codegen/hashes.d.ts +1 -6
  39. package/dist/codegen/hashes.js +1 -60
  40. package/dist/codegen/index.d.ts +8 -36
  41. package/dist/codegen/index.js +6 -293
  42. package/dist/codegen/markers.d.ts +1 -36
  43. package/dist/codegen/markers.js +1 -86
  44. package/dist/codegen/scaffold.d.ts +1 -34
  45. package/dist/codegen/scaffold.js +1 -60
  46. package/dist/codegen/write.d.ts +1 -6
  47. package/dist/codegen/write.js +1 -32
  48. package/dist/commands/clint-project-build.d.ts +6 -1
  49. package/dist/commands/clint-project-build.js +11 -28
  50. package/dist/commands/clint-project-init.d.ts +40 -10
  51. package/dist/commands/clint-project-init.js +55 -47
  52. package/dist/commands/clint-project-publish.d.ts +7 -1
  53. package/dist/commands/clint-project-publish.js +6 -4
  54. package/dist/commands/clint-project-regen.d.ts +6 -1
  55. package/dist/commands/clint-project-regen.js +26 -2
  56. package/dist/commands/clint-skills-sync.d.ts +15 -0
  57. package/dist/commands/clint-skills-sync.js +56 -0
  58. package/dist/framework.d.ts +6 -170
  59. package/dist/framework.gen.d.ts +1 -32
  60. package/dist/framework.js +1 -5
  61. package/{prompts/agent-profiles/AgentBase.md → dist/gen/agent-profiles/PhClintDevAgent.md} +1 -1
  62. package/dist/gen/skills/cli-setup/.cli-docs.md +135 -0
  63. package/dist/gen/skills/cli-setup/SKILL.md +125 -0
  64. package/dist/gen/skills/cli-setup/references/00.assess-cli-needs.md +53 -0
  65. package/dist/gen/skills/cli-setup/references/01.define-config-schema.md +78 -0
  66. package/dist/gen/skills/cli-setup/references/02.wire-cli.md +141 -0
  67. package/dist/gen/skills/cli-setup/references/03.add-entrypoint.md +128 -0
  68. package/dist/gen/skills/cli-setup/references/expected-outcome.md +3 -0
  69. package/dist/gen/skills/command-definition/.cli-docs.md +84 -0
  70. package/dist/gen/skills/command-definition/SKILL.md +99 -0
  71. package/dist/gen/skills/command-definition/references/00.assess-requirements.md +58 -0
  72. package/dist/gen/skills/command-definition/references/01.define-schema.md +72 -0
  73. package/dist/gen/skills/command-definition/references/02.implement-execute.md +118 -0
  74. package/dist/gen/skills/command-definition/references/03.add-prompting.md +54 -0
  75. package/dist/gen/skills/command-definition/references/expected-outcome.md +3 -0
  76. package/dist/gen/skills/service-definition/.cli-docs.md +214 -0
  77. package/dist/gen/skills/service-definition/SKILL.md +148 -0
  78. package/dist/gen/skills/service-definition/references/00.assess-service.md +69 -0
  79. package/dist/gen/skills/service-definition/references/01.define-service.md +166 -0
  80. package/dist/gen/skills/service-definition/references/02.add-preflight.md +132 -0
  81. package/dist/gen/skills/service-definition/references/03.add-project-scanner.md +82 -0
  82. package/dist/gen/skills/service-definition/references/04.wire-events.md +120 -0
  83. package/dist/gen/skills/service-definition/references/expected-outcome.md +3 -0
  84. package/dist/services/clint-project.js +21 -0
  85. package/dist/skills/sync.d.ts +1 -0
  86. package/dist/skills/sync.js +1 -0
  87. package/dist/spec/ensure-document.d.ts +17 -0
  88. package/dist/spec/ensure-document.js +56 -0
  89. package/dist/spec/file.d.ts +1 -4
  90. package/dist/spec/file.js +1 -34
  91. package/dist/spec/from-document.d.ts +4 -1
  92. package/dist/spec/from-document.js +4 -16
  93. package/dist/spec/types.d.ts +1 -62
  94. package/dist/spec/types.js +1 -112
  95. package/dist/triggers/publish-trigger.d.ts +10 -0
  96. package/dist/triggers/publish-trigger.js +133 -0
  97. package/dist/triggers/spec-change.d.ts +5 -34
  98. package/dist/triggers/spec-change.js +137 -102
  99. package/package.json +12 -12
  100. package/prompts/skills-ext/.gitkeep +0 -0
  101. package/prompts/skills-tpl/.gitkeep +0 -0
@@ -0,0 +1,125 @@
1
+ ---
2
+ name: cli-setup
3
+ description: "Set up a new CLI with defineCli, config schemas, entrypoint, and interactive mode"
4
+ metadata:
5
+ author: Powerhouse
6
+ version: "1.0.0"
7
+ ---
8
+
9
+ === BEGIN SKILL BRIEFING ===
10
+
11
+ IMPORTANT: Don't take any action yet. You will be guided through your tasks after the briefing(s). Just process and confirm your understanding.
12
+
13
+ # CLI Setup - Skill Preamble
14
+
15
+ With this skill, you can scaffold or extend a `defineCli()` configuration — the composition root that wires commands, config, services, triggers, and integrations into a single CLI runtime. A single `defineCli()` call produces a CLI that works in command mode (`mycli cmd --arg val`), interactive REPL mode (`/cmd --arg val`), and as an MCP server — all from the same code.
16
+
17
+ ## Core Principles
18
+
19
+ ### defineCli Is Declarative
20
+
21
+ You describe _what_ the CLI has — the framework wires it up. The order of options doesn't matter, but understanding what triggers what does:
22
+
23
+ - `configSchema` present → auto-injects a `config` command for viewing/modifying settings
24
+ - `secretsSchema` present → fields merge into configSchema internally, but are censored in output
25
+ - `services` present → auto-injects per-service management commands (`{id}-start`, `{id}-stop`, etc.)
26
+ - `interactive` present → enables the `-i` flag for REPL mode
27
+ - `triggers` present → enables routine loop
28
+ - `cli-docs` command is always auto-injected (CLI help for the agent)
29
+
30
+ ### Resolvable Values
31
+
32
+ `description` and `interactive.welcome` accept either a static string or a function `({ config, workdir }) => string`. Use functions when the message depends on runtime state — e.g., showing "demo mode" vs "LLM mode" based on whether an API key is configured.
33
+
34
+ ### The Cli Object
35
+
36
+ `defineCli()` returns a `Cli` object — the public API with methods for running, testing, introspection, and metadata. The standard entry point is `cli.run(process.argv)`. For programmatic access (e.g., codegen scripts), use `cli.bootstrap()` which resolves workdir, config, and context without entering the dispatch loop.
37
+
38
+ ### Two Interaction Modes
39
+
40
+ 1. **Command mode** (headless): Execute one subcommand, then exit. Triggered by `mycli cmd --flag val`.
41
+ 2. **Interactive REPL mode**: Persistent session with `/command` syntax and bare text for the default subcommand (typically the agent). Triggered by `mycli -i` or when keep-alive features are active (routine loop, API server).
42
+
43
+ ### 6-Layer Config Resolution
44
+
45
+ Config values are resolved in this priority order (highest wins):
46
+ 1. `--config <file>` flag
47
+ 2. Environment variables: `{CLI_NAME}_{UPPER_SNAKE_FIELD}`
48
+ 3. Local config: `{workdir}/.ph/{cliName}.config.local.json`
49
+ 4. User config: `~/.ph/{cliName}.config.user.json`
50
+ 5. Implementation defaults: `configDefaults` in CliOptions
51
+ 6. Zod schema `.default()` values
52
+
53
+ ### Config vs Secrets
54
+
55
+ - `configSchema` — user-facing settings (model name, ports, feature flags). Shown in `config` command output.
56
+ - `secretsSchema` — sensitive values (API keys, tokens). Merged into configSchema internally but censored as `***` in output unless `--revealSecrets` is used.
57
+
58
+ Put API keys and tokens in `secretsSchema`, not `configSchema`.
59
+
60
+ ### configDefaults vs Schema Defaults
61
+
62
+ - `.default(value)` in the Zod schema → layer 6, the fallback when nothing else is configured
63
+ - `configDefaults` in CliOptions → layer 5, implementation-level overrides that sit above schema defaults but below user/local config
64
+
65
+ Use schema `.default()` for universal sensible defaults. Use `configDefaults` for implementation-specific overrides that differ from the general-purpose default.
66
+
67
+ ### Entry Point Convention
68
+
69
+ The CLI entry point is a TypeScript file with a Node.js shebang:
70
+
71
+ ```typescript
72
+ #!/usr/bin/env node
73
+ import { defineCli } from '@powerhousedao/ph-clint';
74
+ // ... imports ...
75
+ const cli = defineCli({ /* options */ });
76
+ cli.run(process.argv);
77
+ ```
78
+
79
+ The `package.json` needs a `"bin"` field pointing to the compiled output and `"type": "module"` for ESM.
80
+
81
+ ## Common Pitfalls
82
+
83
+ - **Forgetting `version`** — Commander needs it for the `--version` flag; omitting it breaks help output
84
+ - **Putting secrets in `configSchema`** — they show up in `config` command output and metadata, exposing sensitive values
85
+ - **Not setting `interactive.welcome`** — REPL starts with no context, confusing the user
86
+ - **Using `configDefaults` for universal defaults** — use `.default()` in the schema instead; `configDefaults` is for implementation-level overrides only
87
+ - **Calling `configureAgent()` inside `defineCli()`** — it's a method on the returned `Cli` object, called after definition
88
+ - **Forgetting `"type": "module"` in package.json** — ESM imports fail at runtime
89
+ - **Not exporting the `cli` object** — other files (e.g., `mastra/index.ts`) may need it for `cli.bootstrap()`
90
+
91
+ ## When to Use `createTypes()`
92
+
93
+ For projects with a config schema, use the typed factory to get automatic config typing in all commands:
94
+
95
+ ```typescript
96
+ import { createTypes } from '@powerhousedao/ph-clint';
97
+ const { defineCommand, defineTrigger } = createTypes({ configSchema });
98
+ // context.config is now fully typed in every command
99
+ ```
100
+
101
+ ## Codegen-Managed Projects
102
+
103
+ In ph-clint-cli projects, `clint-project-init` generates the initial scaffold including `cli.ts` with `@clint:begin`/`@clint:end` marker regions. The codegen system:
104
+
105
+ - **Generates `cli.ts`** with marker regions for imports, commands, services, triggers, prompts, events, interactive, reactor, and mastra
106
+ - **Generates `framework.gen.ts`** (codegen-owned) with the document registry — regenerated when the spec's `documentTypes` change
107
+ - **Emits `framework.ts`** once (user-owned) with `configSchema`, `secretsSchema`, and `createTypes()` — never overwritten after creation
108
+
109
+ **The agent never hand-edits marker regions in `cli.ts`.** To add or change CLI features:
110
+
111
+ 1. Update `project-spec.json` to declare the new structure
112
+ 2. Run `clint-project-regen` to regenerate the marker regions
113
+ 3. Run `clint-project-build` to verify
114
+
115
+ User-owned code outside markers (`configSchema`, `secretsSchema`, `CLI_NAME`) is preserved across regens. User-owned files (`framework.ts`, `src/commands/*.ts`, `src/services/*.ts`, `src/triggers/*.ts`) are never overwritten.
116
+
117
+ === END OF SKILL BRIEFING ===
118
+
119
+ ## Specific tasks
120
+
121
+ * **Assess Cli Needs** [references/00.assess-cli-needs.md](references/00.assess-cli-needs.md)
122
+ * **Define Config Schema** [references/01.define-config-schema.md](references/01.define-config-schema.md)
123
+ * **Wire Cli** [references/02.wire-cli.md](references/02.wire-cli.md)
124
+ * **Add Entrypoint** [references/03.add-entrypoint.md](references/03.add-entrypoint.md)
125
+ * **Expected Outcome** [references/expected-outcome.md](references/expected-outcome.md)
@@ -0,0 +1,53 @@
1
+ # Phase 0: Assess CLI Needs
2
+
3
+ Before writing any code, determine which features the CLI requires.
4
+
5
+ ## Steps
6
+
7
+ ### 1. Name the CLI
8
+
9
+ Choose a kebab-case name. This name is used for:
10
+ - Config file paths: `{workdir}/.ph/{name}.config.local.json`
11
+ - Environment variable prefix: `{NAME}_*` (UPPER_SNAKE_CASE)
12
+ - Package `"bin"` field and executable name
13
+
14
+ ### 2. Determine version sourcing
15
+
16
+ Choose one:
17
+ - **Hardcoded**: `version: '1.0.0'`
18
+ - **From package.json**: `const { version } = readPackageInfo(import.meta.url)`
19
+
20
+ ### 3. Feature checklist
21
+
22
+ For each feature, determine if the CLI needs it:
23
+
24
+ - [ ] **Custom config?** → needs `configSchema` (Zod object)
25
+ - [ ] **Sensitive config (API keys, tokens)?** → needs `secretsSchema` (Zod object)
26
+ - [ ] **Interactive REPL?** → needs `interactive` with `welcome` message
27
+ - [ ] **Background services?** → needs `services` array of `defineService()` definitions
28
+ - [ ] **Automated triggers?** → needs `triggers` array + `routine` config
29
+ - [ ] **AI agent?** → needs `cli.configureAgent(loader)` after `defineCli()`
30
+ - [ ] **Skill templates?** → needs `prompts` config with `artifacts`, `agents`, `skills`
31
+ - [ ] **Powerhouse documents?** → needs `cli.configureReactor(config)` after `defineCli()`
32
+ - [ ] **Service lifecycle events?** → needs `events` handlers
33
+ - [ ] **Implementation-level workdir override?** → needs `workdir` option (hides `--workdir` flag)
34
+
35
+ ### 4. List commands to register
36
+
37
+ Enumerate all commands that will be passed to the `commands` array. These should already be defined using `defineCommand()` (see the `command-definition` skill). Note which ones need agent-compatible `{ text, data }` returns.
38
+
39
+ ### 5. Detect codegen vs manual project
40
+
41
+ For existing projects, check if `cli.ts` already has `@clint:begin` marker regions:
42
+ - **Codegen-managed**: Markers present — update `project-spec.json` and run `clint-project-regen`. Never hand-edit marker regions.
43
+ - **Manual**: No markers — edit `defineCli()` directly as a regular TypeScript file.
44
+
45
+ For new projects, prefer using `clint-project-init` to generate the scaffold with markers.
46
+
47
+ ### 6. Determine config field names
48
+
49
+ For each config/secret field, verify the auto-generated env var name is sensible:
50
+ - Field `apiKey` on CLI `mycli` → env var `MYCLI_API_KEY`
51
+ - Field `connectPort` on CLI `vetra` → env var `VETRA_CONNECT_PORT`
52
+
53
+ If a name would be confusing, consider renaming the field.
@@ -0,0 +1,78 @@
1
+ # Phase 1: Define Config and Secrets Schemas
2
+
3
+ Design and wire the configuration layer. Skip this phase if the CLI needs no configuration.
4
+
5
+ ## Steps
6
+
7
+ ### 1. Define configSchema
8
+
9
+ Create a Zod object schema for user-facing settings:
10
+
11
+ ```typescript
12
+ import { z } from 'zod';
13
+
14
+ export const configSchema = z.object({
15
+ model: z.string().default('anthropic/claude-sonnet-4-5').describe('LLM model to use'),
16
+ port: z.number().default(4001).describe('Server port'),
17
+ verbose: z.boolean().default(false).describe('Enable verbose output'),
18
+ });
19
+ ```
20
+
21
+ Rules:
22
+ - Every field MUST have `.describe('...')` — it appears in `config` command output, `--help`, and env var listings
23
+ - Use `.default(value)` for fields with sensible defaults
24
+ - Use `.optional()` only for truly optional fields with no default
25
+ - Prefer specific types: `z.enum([...])` for constrained choices, `z.number()` for ports
26
+
27
+ ### 2. Define secretsSchema
28
+
29
+ Create a separate Zod object schema for sensitive values:
30
+
31
+ ```typescript
32
+ export const secretsSchema = z.object({
33
+ apiKey: z.string().optional().describe('LLM API key'),
34
+ dbToken: z.string().optional().describe('Database access token'),
35
+ });
36
+ ```
37
+
38
+ Rules:
39
+ - Secrets are almost always `.optional()` — the CLI should work without them (possibly in a degraded mode)
40
+ - These fields are merged into configSchema internally but censored in output
41
+ - They follow the same env var naming: `{CLI_NAME}_{FIELD}`
42
+
43
+ ### 3. Set configDefaults (if needed)
44
+
45
+ Use `configDefaults` in `defineCli()` for implementation-specific overrides that differ from the schema defaults:
46
+
47
+ ```typescript
48
+ defineCli({
49
+ configSchema,
50
+ configDefaults: {
51
+ port: 8080, // This implementation uses 8080, not the schema default of 4001
52
+ },
53
+ });
54
+ ```
55
+
56
+ This is layer 5 — above schema `.default()` but below user/local config files.
57
+
58
+ ### 4. Use createTypes for typed commands
59
+
60
+ If you have a configSchema, create a typed factory so all commands get typed `context.config`:
61
+
62
+ ```typescript
63
+ // framework.ts
64
+ import { createTypes } from '@powerhousedao/ph-clint';
65
+ import { configSchema, secretsSchema } from './config.js';
66
+
67
+ export const { defineCommand, defineTrigger } = createTypes({ configSchema, secretsSchema });
68
+ export { configSchema, secretsSchema };
69
+ ```
70
+
71
+ Then import `defineCommand` from `./framework.js` instead of `ph-clint` in all command files.
72
+
73
+ ### 5. Verify env var names
74
+
75
+ Run through each field and check the auto-generated env var name:
76
+ - `{CLI_NAME}` is the CLI name uppercased with hyphens replaced by underscores
77
+ - Field keys are converted to UPPER_SNAKE_CASE
78
+ - Example: CLI `my-app`, field `connectPort` → `MY_APP_CONNECT_PORT`
@@ -0,0 +1,141 @@
1
+ # Phase 2: Wire the defineCli Call
2
+
3
+ Compose the `defineCli()` call with all the options determined in Phase 0.
4
+
5
+ ## Steps
6
+
7
+ ### 1. Assemble the core options
8
+
9
+ ```typescript
10
+ import { defineCli } from '@powerhousedao/ph-clint';
11
+ import { configSchema, secretsSchema } from './config.js';
12
+ import { myCommand, otherCommand } from './commands/index.js';
13
+
14
+ export const cli = defineCli({
15
+ name: 'my-app',
16
+ version: '1.0.0',
17
+ description: 'My application CLI',
18
+ commands: [myCommand, otherCommand],
19
+ configSchema,
20
+ secretsSchema,
21
+ });
22
+ ```
23
+
24
+ Always export the `cli` object — other files may need it (e.g., `mastra/index.ts` for `cli.bootstrap()`).
25
+
26
+ ### 2. Configure interactive mode
27
+
28
+ Add an `interactive` block if the CLI supports REPL mode:
29
+
30
+ ```typescript
31
+ interactive: {
32
+ welcome: 'My App CLI — type /help for commands',
33
+ },
34
+ ```
35
+
36
+ For dynamic welcome messages that depend on config:
37
+
38
+ ```typescript
39
+ interactive: {
40
+ welcome: ({ config, workdir }) => {
41
+ const mode = config.apiKey ? config.model : 'demo mode';
42
+ return `My App (${mode})\nWorkdir: ${workdir}`;
43
+ },
44
+ },
45
+ ```
46
+
47
+ ### 3. Add services (if needed)
48
+
49
+ ```typescript
50
+ import { myService } from './services/my-service.js';
51
+
52
+ defineCli({
53
+ // ...
54
+ services: [myService],
55
+ });
56
+ ```
57
+
58
+ This auto-injects `my-service-start`, `my-service-stop`, `my-service-restart`, `my-service-ps`, `my-service-logs`, and `my-service-manage` commands.
59
+
60
+ ### 4. Add event handlers (if needed)
61
+
62
+ Wire lifecycle events for services or custom events:
63
+
64
+ ```typescript
65
+ events: {
66
+ 'service:ready': (event, log) => {
67
+ log.info(`${event.name} is ready`);
68
+ },
69
+ 'service:failed': (event, log) => {
70
+ log.error(`${event.name} failed: ${event.error}`);
71
+ },
72
+ 'service:stopped': (event, log) => {
73
+ log.info(`${event.name} stopped`);
74
+ },
75
+ },
76
+ ```
77
+
78
+ Available service events: `service:pattern-matched`, `service:ready`, `service:failed`, `service:restarting`, `service:stopped`. Each receives `(event, log)` where `log` is a structured Logger.
79
+
80
+ ### 5. Add prompts config (if skills are needed)
81
+
82
+ ```typescript
83
+ import path from 'node:path';
84
+
85
+ prompts: {
86
+ artifacts: [path.join(CLI_ROOT, 'gen', 'skills')],
87
+ agents: {
88
+ 'my-agent': {
89
+ name: 'MyAgent',
90
+ sections: ['AgentBase.md', 'MySpecialization.md'],
91
+ skills: ['skill-one', 'skill-two'],
92
+ },
93
+ },
94
+ skills: {
95
+ 'skill-one': {
96
+ description: 'Description of skill one',
97
+ inputSchema: z.object({
98
+ mode: z.enum(['expert', 'discovery']).default('expert').describe('Interaction mode'),
99
+ }),
100
+ instructionTemplate: 'Use your {{skillId}} skill in {{mode}} mode for: {{prompt}}',
101
+ },
102
+ },
103
+ },
104
+ ```
105
+
106
+ ### 6. Call configureAgent / configureReactor (if needed)
107
+
108
+ These are called AFTER `defineCli()` — they are methods on the returned `Cli` object:
109
+
110
+ ```typescript
111
+ export const cli = defineCli({ /* ... */ });
112
+
113
+ // Agent integration (Mastra)
114
+ cli.configureAgent(createAgent);
115
+
116
+ // Reactor integration (Powerhouse)
117
+ cli.configureReactor({
118
+ registry: [/* document models */],
119
+ // ...
120
+ });
121
+ ```
122
+
123
+ Both are lazy-loaded — the actual agent/reactor is only created when first accessed.
124
+
125
+ ### 7. Codegen projects: use spec + regen
126
+
127
+ In codegen-managed projects, do NOT manually edit `cli.ts` marker regions. Instead:
128
+
129
+ 1. Update `project-spec.json` with the new features (commands, services, triggers, integrations)
130
+ 2. Run `clint-project-regen` to regenerate marker regions
131
+ 3. Edit user-owned files directly: `framework.ts` for config/secrets schemas, `src/commands/*.ts` for commands, etc.
132
+
133
+ Core options outside markers (`name`, `version`, `description`, `configSchema`, `secretsSchema`) are in the user-editable zone — edit those directly in `cli.ts`.
134
+
135
+ ### 8. Verify built-in commands
136
+
137
+ After wiring, confirm these commands will be auto-injected:
138
+ - `cli-docs` — always present
139
+ - `config` — present if `configSchema` was provided
140
+ - Service commands — present for each entry in `services`
141
+ - Skill commands — present for each entry in `prompts.skills`
@@ -0,0 +1,128 @@
1
+ # Phase 3: Create the Entry Point
2
+
3
+ Set up the CLI entry point and package.json configuration.
4
+
5
+ ## Steps
6
+
7
+ ### 1. Create the entry point file
8
+
9
+ Create `src/cli.ts` (or `src/main.ts`) with the Node.js shebang:
10
+
11
+ ```typescript
12
+ #!/usr/bin/env node
13
+ import { defineCli } from '@powerhousedao/ph-clint';
14
+ import { configSchema } from './config.js';
15
+ import { myCommand } from './commands/my-command.js';
16
+
17
+ const cli = defineCli({
18
+ name: 'my-app',
19
+ version: '1.0.0',
20
+ description: 'My application',
21
+ commands: [myCommand],
22
+ configSchema,
23
+ interactive: {
24
+ welcome: 'My App — type /help for commands',
25
+ },
26
+ });
27
+
28
+ cli.run(process.argv);
29
+ ```
30
+
31
+ If the CLI definition is complex, keep it in a separate file (`src/cli-definition.ts`) and import it:
32
+
33
+ ```typescript
34
+ #!/usr/bin/env node
35
+ import { cli } from './cli-definition.js';
36
+ cli.run(process.argv);
37
+ ```
38
+
39
+ ### 2. Configure package.json
40
+
41
+ Add these fields:
42
+
43
+ ```json
44
+ {
45
+ "type": "module",
46
+ "bin": {
47
+ "my-app": "./dist/cli.js"
48
+ },
49
+ "scripts": {
50
+ "build": "tsc",
51
+ "start": "node dist/cli.js",
52
+ "dev": "tsx src/cli.ts"
53
+ }
54
+ }
55
+ ```
56
+
57
+ - `"type": "module"` — required for ESM imports
58
+ - `"bin"` — maps the CLI name to the compiled entry point
59
+ - `"dev"` script uses `tsx` for development without compilation
60
+
61
+ ### 3. Configure tsconfig.json
62
+
63
+ Ensure these compiler options:
64
+
65
+ ```json
66
+ {
67
+ "compilerOptions": {
68
+ "target": "ES2022",
69
+ "module": "Node16",
70
+ "moduleResolution": "Node16",
71
+ "outDir": "./dist",
72
+ "rootDir": "./src",
73
+ "declaration": true,
74
+ "strict": true,
75
+ "esModuleInterop": true
76
+ },
77
+ "include": ["src"]
78
+ }
79
+ ```
80
+
81
+ ### 4. Add ph-clint dependency
82
+
83
+ ```sh
84
+ pnpm add @powerhousedao/ph-clint zod
85
+ ```
86
+
87
+ Or for local development with the library:
88
+
89
+ ```json
90
+ {
91
+ "dependencies": {
92
+ "ph-clint": "file:../../packages/ph-clint"
93
+ }
94
+ }
95
+ ```
96
+
97
+ ### 5. Codegen projects already have entry points
98
+
99
+ In codegen-managed projects (created by `clint-project-init`), the scaffold already includes:
100
+ - `src/main.ts` — shebang entry point with `cli.run(process.argv)`
101
+ - `src/cli.ts` — `defineCli()` with marker regions
102
+ - `src/framework.ts` — user-owned config schemas and `createTypes()`
103
+ - `package.json` — pre-configured with `bin`, `type: "module"`, and scripts
104
+
105
+ Skip steps 1-4 if these already exist. Proceed directly to verification.
106
+
107
+ ### 6. Verify the CLI works
108
+
109
+ Run these checks:
110
+
111
+ ```sh
112
+ # Build
113
+ pnpm build
114
+
115
+ # Help output (should show all commands)
116
+ node dist/cli.js --help
117
+
118
+ # Version
119
+ node dist/cli.js --version
120
+
121
+ # A specific command
122
+ node dist/cli.js my-command --help
123
+
124
+ # Interactive mode (if configured)
125
+ node dist/cli.js -i
126
+ ```
127
+
128
+ Expected: `--help` shows the description, version, all registered commands (including auto-injected ones like `config` and `cli-docs`), and global options (`-i`, `--workdir`, `--config`, `--verbose`).
@@ -0,0 +1,3 @@
1
+ ## Expected Outcome
2
+
3
+ CLI is defined with all required features wired in (config, secrets, services, interactive mode as needed). The entry point is set up with a shebang and `cli.run(process.argv)`. The `package.json` has `"bin"` and `"type": "module"` fields. The CLI responds correctly to `--help`, `--version`, `-i`, and all registered commands. If `configureAgent()` or `configureReactor()` are needed, they are called after `defineCli()`.
@@ -0,0 +1,84 @@
1
+ ## API Reference: Command Definition
2
+
3
+ ### `defineCommand<TInput, TOutput, TConfig>(options)`
4
+
5
+ Identity function that returns the options object with full type inference. Accepts three generic parameters:
6
+
7
+ - `TInput extends z.ZodType` — the input schema type
8
+ - `TOutput` — the return type of `execute`
9
+ - `TConfig` — the config type from `defineCli`
10
+
11
+ ### `Command<TInput, TOutput, TConfig>` Interface
12
+
13
+ ```typescript
14
+ interface Command<TInput, TOutput, TConfig> {
15
+ id: string; // Kebab-case identifier (e.g. 'add-task')
16
+ description: string; // One-line description for help text
17
+ inputSchema: TInput; // Zod object schema — each field = CLI flag
18
+ outputSchema?: z.ZodType; // Optional output validation schema
19
+ prompt?: PromptConfig; // Interactive prompting configuration
20
+ execute: (
21
+ input: z.output<TInput>,
22
+ context: CommandContext<TConfig>
23
+ ) => Promise<TOutput>;
24
+ }
25
+ ```
26
+
27
+ ### `CommandContext<TConfig>` Interface
28
+
29
+ Available properties on the context object:
30
+
31
+ | Property | Type | Description |
32
+ |----------|------|-------------|
33
+ | `workdir` | `string` | Resolved working directory |
34
+ | `workspace` | `WorkdirStore` | Persistent file-based state (`.ph/{cliName}/`) |
35
+ | `config` | `TConfig` | Typed configuration object |
36
+ | `stdout` | `(text: string) => void` | Output function for streaming text |
37
+ | `log` | `Logger?` | Structured logger (debug/info/warn/error) |
38
+ | `routine` | `Routine?` | Routine loop control (if triggers configured) |
39
+ | `processes` | `ProcessManager?` | Run shell commands with timeout and labeling |
40
+ | `services` | `ServiceManager?` | Manage background services (if configured) |
41
+ | `emit` | `EmitFn?` | Event bus emitter |
42
+ | `reactor` | `() => Promise<ReactorContext?>` | Lazy Powerhouse reactor access |
43
+ | `agent` | `() => Promise<AgentProvider?>` | Lazy Mastra agent access |
44
+
45
+ ### `WorkdirStore` Methods
46
+
47
+ ```typescript
48
+ workspace.loadJsonObject<T>(filename, fallback) // Load JSON from .ph/ store
49
+ workspace.storeJsonObject(filename, value) // Save JSON to .ph/ store
50
+ workspace.loadLocalConfig() // Load local config
51
+ workspace.storeLocalConfig(config) // Save local config
52
+ workspace.getWorkdir() // Get workdir path
53
+ workspace.getStoreFolder(subpath?) // Get .ph/ store folder path
54
+ ```
55
+
56
+ ### `PromptConfig` Interface
57
+
58
+ ```typescript
59
+ interface PromptConfig {
60
+ promptForDefaults?: boolean; // Prompt for fields with .default()? (default: true)
61
+ promptOptional?: string[]; // List of optional field IDs to prompt for
62
+ }
63
+ ```
64
+
65
+ ### Return Value Handling
66
+
67
+ The framework processes return values as follows:
68
+
69
+ 1. `undefined` or `null` — nothing is printed
70
+ 2. `string` — displayed directly as output
71
+ 3. Object with `.text` property — the `text` string is displayed; `.data` is available for programmatic/agent consumption
72
+ 4. Any other value — converted via `String(value)`
73
+
74
+ ### Registration in `defineCli`
75
+
76
+ Commands are registered via the `commands` array:
77
+
78
+ ```typescript
79
+ const cli = defineCli({
80
+ name: 'mycli',
81
+ commands: [greet, addTask, listTasks],
82
+ // ...
83
+ });
84
+ ```