@powerhousedao/ph-clint-cli 0.0.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 (65) hide show
  1. package/README.md +84 -0
  2. package/dist/agents/clint-agent.d.ts +12 -0
  3. package/dist/agents/clint-agent.js +42 -0
  4. package/dist/cli.d.ts +1 -0
  5. package/dist/cli.js +122 -0
  6. package/dist/codegen/index.d.ts +11 -0
  7. package/dist/codegen/index.js +11 -0
  8. package/dist/commands/clint-project-build.d.ts +83 -0
  9. package/dist/commands/clint-project-build.js +34 -0
  10. package/dist/commands/clint-project-init.d.ts +177 -0
  11. package/dist/commands/clint-project-init.js +193 -0
  12. package/dist/commands/clint-project-publish.d.ts +92 -0
  13. package/dist/commands/clint-project-publish.js +73 -0
  14. package/dist/commands/clint-project-regen.d.ts +94 -0
  15. package/dist/commands/clint-project-regen.js +92 -0
  16. package/dist/commands/clint-skills-sync.d.ts +86 -0
  17. package/dist/commands/clint-skills-sync.js +56 -0
  18. package/dist/config.d.ts +3 -0
  19. package/dist/config.js +5 -0
  20. package/dist/framework.d.ts +501 -0
  21. package/dist/framework.gen.d.ts +77 -0
  22. package/dist/framework.gen.js +16 -0
  23. package/dist/framework.js +37 -0
  24. package/dist/gen/agent-profiles/PhClintDevAgent.md +13 -0
  25. package/dist/gen/skills/cli-setup/.cli-docs.md +135 -0
  26. package/dist/gen/skills/cli-setup/SKILL.md +125 -0
  27. package/dist/gen/skills/cli-setup/references/00.assess-cli-needs.md +53 -0
  28. package/dist/gen/skills/cli-setup/references/01.define-config-schema.md +78 -0
  29. package/dist/gen/skills/cli-setup/references/02.wire-cli.md +141 -0
  30. package/dist/gen/skills/cli-setup/references/03.add-entrypoint.md +128 -0
  31. package/dist/gen/skills/cli-setup/references/expected-outcome.md +3 -0
  32. package/dist/gen/skills/command-definition/.cli-docs.md +84 -0
  33. package/dist/gen/skills/command-definition/SKILL.md +99 -0
  34. package/dist/gen/skills/command-definition/references/00.assess-requirements.md +58 -0
  35. package/dist/gen/skills/command-definition/references/01.define-schema.md +72 -0
  36. package/dist/gen/skills/command-definition/references/02.implement-execute.md +118 -0
  37. package/dist/gen/skills/command-definition/references/03.add-prompting.md +54 -0
  38. package/dist/gen/skills/command-definition/references/expected-outcome.md +3 -0
  39. package/dist/gen/skills/service-definition/.cli-docs.md +214 -0
  40. package/dist/gen/skills/service-definition/SKILL.md +148 -0
  41. package/dist/gen/skills/service-definition/references/00.assess-service.md +69 -0
  42. package/dist/gen/skills/service-definition/references/01.define-service.md +166 -0
  43. package/dist/gen/skills/service-definition/references/02.add-preflight.md +132 -0
  44. package/dist/gen/skills/service-definition/references/03.add-project-scanner.md +82 -0
  45. package/dist/gen/skills/service-definition/references/04.wire-events.md +120 -0
  46. package/dist/gen/skills/service-definition/references/expected-outcome.md +3 -0
  47. package/dist/main.d.ts +2 -0
  48. package/dist/main.js +3 -0
  49. package/dist/mastra/index.d.ts +10 -0
  50. package/dist/mastra/index.js +11 -0
  51. package/dist/services/clint-project.d.ts +1 -0
  52. package/dist/services/clint-project.js +68 -0
  53. package/dist/spec/ensure-document.d.ts +17 -0
  54. package/dist/spec/ensure-document.js +56 -0
  55. package/dist/spec/file.d.ts +1 -0
  56. package/dist/spec/file.js +1 -0
  57. package/dist/spec/from-document.d.ts +21 -0
  58. package/dist/spec/from-document.js +18 -0
  59. package/dist/spec/types.d.ts +1 -0
  60. package/dist/spec/types.js +1 -0
  61. package/dist/triggers/publish-trigger.d.ts +81 -0
  62. package/dist/triggers/publish-trigger.js +137 -0
  63. package/dist/triggers/spec-change.d.ts +85 -0
  64. package/dist/triggers/spec-change.js +224 -0
  65. package/package.json +54 -0
@@ -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
+ ```
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: command-definition
3
+ description: "Define commands with Zod schemas, execute functions, return values, and parameter prompting"
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
+ # Command Definition - Skill Preamble
14
+
15
+ With this skill, you can define ph-clint commands — the atomic building blocks of every CLI. A single command definition produces a CLI subcommand (`mycli greet --name World`), a REPL command (`/greet --name World`), an MCP tool, and an agent tool — all from the same code.
16
+
17
+ ## Core Principles
18
+
19
+ ### One Command = One User Intent
20
+
21
+ Each command does one thing. Don't create CRUD bundles — instead define `add-task`, `list-tasks`, `remove-task` as separate commands. This keeps schemas focused and help text clear.
22
+
23
+ ### inputSchema Is the Single Source of Truth
24
+
25
+ The Zod `inputSchema` drives everything: CLI flags, help text, validation, agent tool descriptions, and interactive prompting. Design the schema carefully — it's the command's public API.
26
+
27
+ ### `.describe()` on Every Field
28
+
29
+ Every Zod field MUST have `.describe('...')`. This text appears in:
30
+ - CLI `--help` output
31
+ - REPL auto-completion hints
32
+ - Agent tool descriptions
33
+ - Parameter prompting labels
34
+
35
+ Without `.describe()`, the user sees empty descriptions everywhere.
36
+
37
+ ### Return `{ text, data }` for Agent-Compatible Commands
38
+
39
+ - Return a **string** for simple display-only commands
40
+ - Return `{ text: 'human-readable', data: structuredResult }` when the command will also be used as an agent tool — `text` is shown to users, `data` is consumed by the agent
41
+
42
+ ### Execute Receives Validated Input and Injected Context
43
+
44
+ The `execute(input, context)` function receives:
45
+ 1. `input` — already validated and parsed by Zod. Destructure directly: `({ name, loud })`
46
+ 2. `context` — injected by the framework. Destructure only what you need: `{ workspace, config }`
47
+
48
+ Never construct context yourself. Never call `process.exit()` or `console.log()` — use the injected `context.stdout()` for streaming output, and throw errors for failures.
49
+
50
+ ## Common Pitfalls
51
+
52
+ - **Forgetting `.describe()`** — help text and tool descriptions show empty fields
53
+ - **Using `.optional()` when you mean `.default('value')`** — optional means the flag can be omitted entirely with no fallback; default provides a sensible fallback value
54
+ - **Returning void** — the CLI has nothing to display; always return a string or `{ text, data }`
55
+ - **Accessing `context.services` without null check** — it's `undefined` when no services are configured
56
+ - **Using `console.log`** — use `context.stdout(text)` instead so output is capturable in tests
57
+ - **Calling `process.exit()`** — throw an error instead; the framework handles exit codes
58
+
59
+ ## When to Use Typed Generics
60
+
61
+ For simple commands, `defineCommand({ ... })` with inline schemas infers types automatically. Use explicit generics when you need to:
62
+ - Share the input schema across commands (extract to a const)
63
+ - Enforce a specific output type
64
+ - Bind to a project-specific config type via `createTypes()`
65
+
66
+ ```typescript
67
+ // Project-level typed factory (from framework.ts)
68
+ const { defineCommand } = createTypes({ configSchema });
69
+
70
+ // Now context.config is typed automatically
71
+ const myCmd = defineCommand({
72
+ id: 'my-cmd',
73
+ inputSchema: z.object({ name: z.string().describe('Name') }),
74
+ execute: async ({ name }, { config }) => {
75
+ // config is fully typed here
76
+ },
77
+ });
78
+ ```
79
+
80
+ ## Working in a Codegen-Managed Project
81
+
82
+ ph-clint-cli projects are spec-driven. When defining commands:
83
+
84
+ - **Import `defineCommand` from `../framework.js`**, not from `@powerhousedao/ph-clint`. The typed factory from `framework.ts` gives automatic config typing via `createTypes()`.
85
+ - **Never hand-edit `cli.ts` marker regions.** The `@clint:begin`/`@clint:end` regions are codegen-owned. Instead:
86
+ 1. Write the command file in `src/commands/`
87
+ 2. Update `project-spec.json` to declare the new command
88
+ 3. Run `clint-project-regen` to regenerate `cli.ts` with the correct imports and registration
89
+ - **Build** after regen with `clint-project-build` to verify TypeScript compiles
90
+
91
+ === END OF SKILL BRIEFING ===
92
+
93
+ ## Specific tasks
94
+
95
+ * **Assess Requirements** [references/00.assess-requirements.md](references/00.assess-requirements.md)
96
+ * **Define Schema** [references/01.define-schema.md](references/01.define-schema.md)
97
+ * **Implement Execute** [references/02.implement-execute.md](references/02.implement-execute.md)
98
+ * **Add Prompting** [references/03.add-prompting.md](references/03.add-prompting.md)
99
+ * **Expected Outcome** [references/expected-outcome.md](references/expected-outcome.md)
@@ -0,0 +1,58 @@
1
+ # Phase 0: Assess Requirements
2
+
3
+ Before writing any code, clarify what the command should do.
4
+
5
+ ## Steps
6
+
7
+ ### 1. Name the command
8
+
9
+ Choose a kebab-case, verb-noun name that describes the action:
10
+ - `add-task`, `list-images`, `run-build`, `show-status`
11
+ - The `id` becomes the CLI subcommand and REPL `/command` name
12
+
13
+ ### 2. Identify all inputs
14
+
15
+ For each parameter, determine:
16
+ - **Type**: `z.string()`, `z.number()`, `z.boolean()`, `z.enum([...])`, `z.array(z.string())`
17
+ - **Required vs optional**: Is this mandatory or can it be omitted?
18
+ - **Default value**: What's a sensible default? Use `.default(value)` instead of `.optional()` when there is one
19
+ - **Validation**: Regex patterns, min/max ranges, URL format, etc.
20
+ - **Description**: One-line user-facing text for `--help`
21
+
22
+ ### 3. Determine context needs
23
+
24
+ Which `CommandContext` properties does the command need?
25
+
26
+ - [ ] `workspace` — persistent state across invocations
27
+ - [ ] `config` — typed CLI configuration
28
+ - [ ] `processes` — spawn shell commands
29
+ - [ ] `services` — manage background services
30
+ - [ ] `stdout` — streaming output (for long-running operations)
31
+ - [ ] `log` — debug/info/warn/error logging
32
+ - [ ] `reactor()` — Powerhouse document operations
33
+ - [ ] `agent()` — AI agent access
34
+
35
+ ### 4. Determine return shape
36
+
37
+ - **Display-only**: return a string
38
+ - **Agent-compatible**: return `{ text: 'human message', data: structuredResult }`
39
+ - **Silent**: return `undefined` (rare — most commands should confirm what they did)
40
+
41
+ ### 5. Determine prompting needs
42
+
43
+ If the command will be used in interactive/REPL mode:
44
+ - Which optional fields should be prompted for?
45
+ - Should fields with defaults be re-prompted? (Usually no — set `promptForDefaults: false`)
46
+ - Enum fields get selection prompts automatically
47
+
48
+ ### 6. Detect codegen project
49
+
50
+ Check if `cli.ts` contains `@clint:begin` markers. If it does, this is a codegen-managed project:
51
+ - Import `defineCommand` from `../framework.js`, not from `@powerhousedao/ph-clint`
52
+ - Do NOT manually edit marker regions in `cli.ts` — update `project-spec.json` instead and run `clint-project-regen` to regenerate
53
+
54
+ ### 7. Check for existing commands
55
+
56
+ - Are there existing commands that overlap with this one?
57
+ - Does this command depend on another command's output?
58
+ - Could this be an extension of an existing command instead?
@@ -0,0 +1,72 @@
1
+ # Phase 1: Define the Input Schema
2
+
3
+ Design and write the Zod `inputSchema` that defines the command's parameters.
4
+
5
+ ## Steps
6
+
7
+ ### 1. Choose field types
8
+
9
+ Select the appropriate Zod type for each parameter:
10
+
11
+ ```typescript
12
+ z.string() // Free text
13
+ z.string().url() // URL validation
14
+ z.string().regex(/^[a-z-]+$/) // Pattern validation
15
+ z.number() // Numeric input
16
+ z.number().min(1).max(100) // Bounded number
17
+ z.boolean() // Flag (--verbose, --dry-run)
18
+ z.enum(['low', 'medium', 'high']) // Fixed choices (auto-completion in REPL)
19
+ z.array(z.string()) // Multiple values (--tag a --tag b)
20
+ ```
21
+
22
+ ### 2. Add `.describe()` to every field
23
+
24
+ This is mandatory. The description text appears in help output, agent tool descriptions, and prompting labels:
25
+
26
+ ```typescript
27
+ z.string().describe('Name of the project to initialize')
28
+ z.boolean().default(false).describe('Run without making changes')
29
+ ```
30
+
31
+ ### 3. Add `.default()` for sensible defaults
32
+
33
+ Fields with defaults become optional CLI flags. Without a default, the flag is mandatory:
34
+
35
+ ```typescript
36
+ z.number().default(3000).describe('Port number') // --port is optional, defaults to 3000
37
+ z.enum(['json', 'table']).default('table').describe('Output format')
38
+ ```
39
+
40
+ ### 4. Mark truly optional fields
41
+
42
+ Use `.optional()` only when there is genuinely no default value and the field can be absent:
43
+
44
+ ```typescript
45
+ z.string().optional().describe('Due date (YYYY-MM-DD)') // --due can be omitted entirely
46
+ ```
47
+
48
+ ### 5. Add validation constraints
49
+
50
+ ```typescript
51
+ z.string().regex(/^[a-zA-Z0-9-]+$/).describe('Project name (alphanumeric + hyphens)')
52
+ z.number().min(1).max(65535).describe('Port number')
53
+ z.string().url().describe('API endpoint URL')
54
+ ```
55
+
56
+ ### 6. Assemble the schema
57
+
58
+ ```typescript
59
+ const inputSchema = z.object({
60
+ name: z.string().describe('Project name'),
61
+ port: z.number().default(3000).describe('Dev server port'),
62
+ template: z.enum(['minimal', 'full']).default('minimal').describe('Project template'),
63
+ dryRun: z.boolean().default(false).describe('Preview changes without writing files'),
64
+ });
65
+ ```
66
+
67
+ ### 7. Verify
68
+
69
+ - Every required field without a default will be a mandatory `--flag`
70
+ - Every field has `.describe()`
71
+ - Field names are camelCase (they become `--camel-case` CLI flags automatically)
72
+ - Enum types list all valid options