@powerhousedao/ph-clint-cli 0.1.0-dev.23 → 0.1.0-dev.24

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 (35) hide show
  1. package/dist/cli.js +22 -3
  2. package/dist/codegen/builders/agent-ts.js +6 -6
  3. package/dist/codegen/builders/cli-package-json.js +1 -0
  4. package/dist/codegen/builders/cli-ts.js +25 -9
  5. package/dist/codegen/builders/demo-agent-ts.d.ts +8 -0
  6. package/dist/codegen/builders/demo-agent-ts.js +44 -0
  7. package/dist/codegen/builders/framework-ts.js +11 -6
  8. package/dist/codegen/builders/index.d.ts +2 -1
  9. package/dist/codegen/builders/index.js +8 -3
  10. package/dist/codegen/builders/mastra-index-ts.d.ts +2 -2
  11. package/dist/codegen/builders/mastra-index-ts.js +36 -14
  12. package/dist/gen/agent-profiles/PhClintDevAgent.md +13 -0
  13. package/dist/gen/skills/cli-setup/.cli-docs.md +135 -0
  14. package/dist/gen/skills/cli-setup/SKILL.md +125 -0
  15. package/dist/gen/skills/cli-setup/references/00.assess-cli-needs.md +53 -0
  16. package/dist/gen/skills/cli-setup/references/01.define-config-schema.md +78 -0
  17. package/dist/gen/skills/cli-setup/references/02.wire-cli.md +141 -0
  18. package/dist/gen/skills/cli-setup/references/03.add-entrypoint.md +128 -0
  19. package/dist/gen/skills/cli-setup/references/expected-outcome.md +3 -0
  20. package/dist/gen/skills/command-definition/.cli-docs.md +84 -0
  21. package/dist/gen/skills/command-definition/SKILL.md +99 -0
  22. package/dist/gen/skills/command-definition/references/00.assess-requirements.md +58 -0
  23. package/dist/gen/skills/command-definition/references/01.define-schema.md +72 -0
  24. package/dist/gen/skills/command-definition/references/02.implement-execute.md +118 -0
  25. package/dist/gen/skills/command-definition/references/03.add-prompting.md +54 -0
  26. package/dist/gen/skills/command-definition/references/expected-outcome.md +3 -0
  27. package/dist/gen/skills/service-definition/.cli-docs.md +214 -0
  28. package/dist/gen/skills/service-definition/SKILL.md +148 -0
  29. package/dist/gen/skills/service-definition/references/00.assess-service.md +69 -0
  30. package/dist/gen/skills/service-definition/references/01.define-service.md +166 -0
  31. package/dist/gen/skills/service-definition/references/02.add-preflight.md +132 -0
  32. package/dist/gen/skills/service-definition/references/03.add-project-scanner.md +82 -0
  33. package/dist/gen/skills/service-definition/references/04.wire-events.md +120 -0
  34. package/dist/gen/skills/service-definition/references/expected-outcome.md +3 -0
  35. package/package.json +5 -4
package/dist/cli.js CHANGED
@@ -6,7 +6,9 @@
6
6
  * rewritten by ph-clint-cli's generator when the project spec changes.
7
7
  * Everything outside the markers is user-editable and preserved.
8
8
  */
9
+ import path from 'node:path';
9
10
  import { defineCli, buildDefaultReactor } from '@powerhousedao/ph-clint';
11
+ import { z } from 'zod';
10
12
  import { CLI_NAME, CLI_VERSION, CLI_ROOT } from './config.js';
11
13
  import { configSchema, secretsSchema } from './framework.js';
12
14
  // @clint:begin imports
@@ -39,9 +41,26 @@ export const cli = defineCli({
39
41
  // @clint:end triggers
40
42
  // @clint:begin prompts
41
43
  prompts: {
42
- artifacts: [],
43
- agents: {},
44
- skills: {},
44
+ artifacts: [path.join(CLI_ROOT, 'gen', 'skills'), path.join(CLI_ROOT, 'dist', 'gen', 'skills')],
45
+ agents: {
46
+ 'ph-clint-dev-agent': {
47
+ name: 'PhClintDevAgent',
48
+ sections: ['AgentBase.md'],
49
+ skills: ['command-definition'],
50
+ },
51
+ },
52
+ skills: {
53
+ 'command-definition': {
54
+ description: 'Define commands with Zod schemas, execute functions, return values, and parameter prompting',
55
+ inputSchema: z.object({
56
+ mode: z
57
+ .enum(['expert', 'discovery', 'one-shot'])
58
+ .default('expert')
59
+ .describe('Expert: align design decisions with the developer. Discovery: explain concepts step by step. One-shot: make all decisions autonomously'),
60
+ }),
61
+ instructionTemplate: 'Use your {{skillId}} skill in {{mode}} mode for: {{prompt}}',
62
+ },
63
+ },
45
64
  },
46
65
  // @clint:end prompts
47
66
  // @clint:begin events
@@ -51,9 +51,9 @@ function buildRealAgent(spec) {
51
51
  const agentName = mastra.agentName;
52
52
  const defaultModel = mastra.models.find(m => m.isDefault) ?? mastra.models[0];
53
53
  const modelId = defaultModel?.id ?? 'anthropic/claude-sonnet-4-5';
54
- const [provider] = modelId.split('/');
54
+ const [provider] = modelId.split(/[:/]/);
55
55
  const { pkg, fn } = providerImport(provider);
56
- const envKey = `${provider.toUpperCase()}_API_KEY`;
56
+ const apiKeyField = `${provider}ApiKey`;
57
57
  const lines = [];
58
58
  lines.push("import { Agent } from '@mastra/core/agent';");
59
59
  lines.push("import { MCPClient } from '@mastra/mcp';");
@@ -72,7 +72,7 @@ function buildRealAgent(spec) {
72
72
  lines.push(' * agent as a ph-clint AgentProvider.');
73
73
  lines.push(' */');
74
74
  lines.push('export async function createAgent(ctx: AgentSetupContext<Config>): Promise<AgentProvider> {');
75
- lines.push(` if (!ctx.config.apiKey) return createDemoAgent();`);
75
+ lines.push(` if (!ctx.config.${apiKeyField}) return createDemoAgent();`);
76
76
  lines.push('');
77
77
  lines.push(' const m = createMastraHelpers(ctx);');
78
78
  lines.push('');
@@ -80,9 +80,9 @@ function buildRealAgent(spec) {
80
80
  lines.push(` id: '${agentId}',`);
81
81
  lines.push(` name: '${agentName}',`);
82
82
  lines.push(` instructions: m.getAgentInstructions('${agentId}'),`);
83
- lines.push(' model: ctx.config.apiKey');
84
- lines.push(" ? { id: ctx.config.model as `${string}/${string}`, apiKey: ctx.config.apiKey }");
85
- lines.push(" : (ctx.config.model as `${string}/${string}`),");
83
+ lines.push(` model: ctx.config.${apiKeyField}`);
84
+ lines.push(` ? { id: ctx.config.model as \`\${string}/\${string}\`, apiKey: ctx.config.${apiKeyField} }`);
85
+ lines.push(` : (ctx.config.model as \`\${string}/\${string}\`),`);
86
86
  lines.push(' tools: async () => {');
87
87
  lines.push(" ctx.context.log?.debug('[agent] tools callback invoked');");
88
88
  lines.push(' const tools = await m.getTools({ MCPClient });');
@@ -52,6 +52,7 @@ export function buildCliPackageJson(spec) {
52
52
  if (mastra.enabled) {
53
53
  dependencies['@mastra/core'] = '^1.22.0';
54
54
  dependencies['@mastra/libsql'] = '^1.7.4';
55
+ dependencies['@mastra/mcp'] = '^1.4.1';
55
56
  dependencies['@mastra/memory'] = '^1.13.1';
56
57
  }
57
58
  if (phAtLeast(powerhouse, 'Reactor')) {
@@ -15,16 +15,19 @@ export function buildCliTs(spec) {
15
15
  lines.push(' * markers are rewritten by ph-clint when the project spec changes.');
16
16
  lines.push(' * Everything outside the markers is user-editable and preserved.');
17
17
  lines.push(' */');
18
+ const needsRoot = phAtLeast(ph, 'Reactor') || mastra.enabled;
19
+ lines.push('// @clint:begin imports');
18
20
  if (phAtLeast(ph, 'Reactor')) {
19
21
  lines.push(`import { defineCli, buildDefaultReactor } from '@powerhousedao/ph-clint';`);
20
22
  }
21
23
  else {
22
24
  lines.push(`import { defineCli } from '@powerhousedao/ph-clint';`);
23
25
  }
24
- lines.push(`import { CLI_NAME, CLI_VERSION${phAtLeast(ph, 'Reactor') ? ', CLI_ROOT' : ''} } from './config.js';`);
26
+ if (needsRoot) {
27
+ lines.push("import path from 'node:path';");
28
+ }
29
+ lines.push(`import { CLI_NAME, CLI_VERSION${needsRoot ? ', CLI_ROOT' : ''} } from './config.js';`);
25
30
  lines.push(`import { configSchema, secretsSchema } from './framework.js';`);
26
- lines.push('');
27
- lines.push('// @clint:begin imports');
28
31
  if (phAtLeast(ph, 'Reactor')) {
29
32
  lines.push(`import { documentModels } from '${getAppPackageName(spec)}';`);
30
33
  }
@@ -62,15 +65,28 @@ export function buildCliTs(spec) {
62
65
  lines.push('');
63
66
  lines.push(' // @clint:begin prompts');
64
67
  lines.push(' prompts: {');
65
- lines.push(' artifacts: [],');
66
- if (mastra.enabled && mastra.agentId && mastra.profiles.length > 0) {
68
+ if (mastra.enabled) {
69
+ lines.push(" artifacts: [path.join(CLI_ROOT, 'gen', 'skills'), path.join(CLI_ROOT, 'dist', 'gen', 'skills')],");
70
+ }
71
+ else {
72
+ lines.push(' artifacts: [],');
73
+ }
74
+ if (mastra.enabled && mastra.agentId) {
67
75
  lines.push(' agents: {');
68
76
  lines.push(` '${mastra.agentId}': {`);
69
- lines.push(' sections: [');
70
- for (const p of mastra.profiles) {
71
- lines.push(` { id: '${p.id}', title: '${p.title}', file: '${p.id}.md' },`);
77
+ if (mastra.profiles.length > 0) {
78
+ lines.push(` name: '${mastra.profiles[0].id}',`);
79
+ lines.push(' sections: [');
80
+ for (const p of mastra.profiles) {
81
+ lines.push(` '${p.id}.md',`);
82
+ }
83
+ lines.push(' ],');
84
+ }
85
+ else {
86
+ lines.push(" name: 'AgentBase',");
87
+ lines.push(" sections: ['AgentBase.md'],");
72
88
  }
73
- lines.push(' ],');
89
+ lines.push(' skills: [],');
74
90
  lines.push(' },');
75
91
  lines.push(' },');
76
92
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Builds `src/agents/demo-agent.ts` — a deterministic echo agent fallback.
3
+ *
4
+ * Generated when `features.mastra.enabled` with full agent config so the real
5
+ * agent factory can fall back to demo mode when no API key is configured.
6
+ */
7
+ import { type ClintProjectSpec } from '../../spec/types.js';
8
+ export declare function buildDemoAgentTs(spec: ClintProjectSpec): string | null;
@@ -0,0 +1,44 @@
1
+ export function buildDemoAgentTs(spec) {
2
+ const { mastra } = spec.features;
3
+ // Only needed alongside the real agent (which imports it)
4
+ if (!mastra.enabled || !mastra.agentId || mastra.models.length === 0)
5
+ return null;
6
+ const agentId = mastra.agentId;
7
+ return [
8
+ "import type { AgentProvider, StreamChunk } from '@powerhousedao/ph-clint';",
9
+ '',
10
+ '/**',
11
+ ' * Demo agent — deterministic echo responses without an API key.',
12
+ ' *',
13
+ ' * Echoes user prompts with conversation tracking per thread.',
14
+ ' */',
15
+ 'export function createDemoAgent(): AgentProvider {',
16
+ ' const conversations = new Map<string, string[]>();',
17
+ '',
18
+ ' return {',
19
+ ` id: '${agentId}',`,
20
+ ' async *stream(prompt, opts) {',
21
+ " const threadId = opts?.threadId ?? 'default';",
22
+ ' if (!conversations.has(threadId)) {',
23
+ ' conversations.set(threadId, []);',
24
+ ' }',
25
+ ' const history = conversations.get(threadId)!;',
26
+ ' history.push(prompt);',
27
+ '',
28
+ ' const turnCount = history.length;',
29
+ ' if (turnCount > 1) {',
30
+ ' yield {',
31
+ " type: 'text-delta',",
32
+ ' text: `I understand you\'re continuing our conversation (turn ${turnCount}). `,',
33
+ ' } satisfies StreamChunk;',
34
+ ' }',
35
+ ' yield {',
36
+ " type: 'text-delta',",
37
+ ' text: `You said: "${prompt}". I\'m the demo agent — set an API key for real LLM responses.`,',
38
+ ' } satisfies StreamChunk;',
39
+ ' },',
40
+ ' };',
41
+ '}',
42
+ '',
43
+ ].join('\n');
44
+ }
@@ -42,25 +42,30 @@ export function buildFrameworkTs(spec) {
42
42
  }
43
43
  lines.push('');
44
44
  lines.push('export const configSchema = z.object({');
45
+ lines.push(' // @clint:begin framework-config');
45
46
  if (mastra.enabled) {
46
47
  const defaultModel = mastra.models.find(m => m.isDefault)?.id ?? 'anthropic/claude-sonnet-4-5';
47
- lines.push(` model: z.string().default('${defaultModel}').describe('LLM model to use'),`);
48
+ lines.push(` model: z.string().default('${defaultModel}').describe('LLM model to use'),`, ` agentLogging: z.boolean().default(false).describe('Enable agent conversation logging'),`);
48
49
  }
49
- // Ports are derived from CLI name by resolveReactorDefaults() — no config fields needed.
50
- // Override via switchboard.port / connect.port in configureReactor() if required.
51
- lines.push(' // Add your own config fields here — this file survives regens.');
50
+ lines.push(' // @clint:end framework-config');
52
51
  lines.push('});');
53
52
  lines.push('');
54
53
  lines.push('export const secretsSchema = z.object({');
54
+ lines.push(' // @clint:begin framework-secrets');
55
55
  if (mastra.enabled) {
56
- lines.push(" apiKey: z.string().optional().describe('LLM API key'),");
56
+ const providers = [...new Set(mastra.models.map(m => m.id.split(/[:/]/)[0]))];
57
+ for (const provider of providers) {
58
+ const field = `${provider}ApiKey`;
59
+ lines.push(` ${field}: z.string().optional().describe('${provider} API key'),`);
60
+ }
57
61
  }
62
+ lines.push(' // @clint:end framework-secrets');
58
63
  lines.push('});');
59
64
  lines.push('');
60
65
  lines.push('export type Config = z.infer<typeof configSchema> &');
61
66
  lines.push(' z.infer<typeof secretsSchema>;');
62
67
  lines.push('');
63
- lines.push('const fullConfigSchema = configSchema.merge(secretsSchema);');
68
+ lines.push('const fullConfigSchema = configSchema.extend(secretsSchema.shape);');
64
69
  lines.push('');
65
70
  if (hasGen) {
66
71
  lines.push('export const {');
@@ -17,6 +17,7 @@ import { buildMastraIndexTs } from './mastra-index-ts.js';
17
17
  import { buildBuildSkillsScript } from './build-skills-ts.js';
18
18
  import { buildAgentBaseMd } from './agent-base-md.js';
19
19
  import { buildAgentTs } from './agent-ts.js';
20
+ import { buildDemoAgentTs } from './demo-agent-ts.js';
20
21
  import { buildFrameworkGenTs } from './framework-gen-ts.js';
21
22
  import { buildFrameworkTs } from './framework-ts.js';
22
23
  import { type ClintProjectSpec } from '../../spec/types.js';
@@ -42,7 +43,7 @@ export interface FileBuilder {
42
43
  */
43
44
  export declare function getProfileFileBuilders(spec: ClintProjectSpec): FileBuilder[];
44
45
  export declare const CLI_FILE_BUILDERS: FileBuilder[];
45
- export { buildCliPackageJson, buildCliTs, buildConfigTs, buildMainTs, buildTsconfigJson, buildJestConfigJs, buildEslintConfigJs, buildGitignore, buildMastraIndexTs, buildBuildSkillsScript, buildAgentBaseMd, buildAgentTs, buildFrameworkGenTs, buildFrameworkTs, };
46
+ export { buildCliPackageJson, buildCliTs, buildConfigTs, buildMainTs, buildTsconfigJson, buildJestConfigJs, buildEslintConfigJs, buildGitignore, buildMastraIndexTs, buildBuildSkillsScript, buildAgentBaseMd, buildAgentTs, buildDemoAgentTs, buildFrameworkGenTs, buildFrameworkTs, };
46
47
  export { buildRootPackageJson } from './root-package-json.js';
47
48
  export { buildPublishConfigTs } from './publish-config-ts.js';
48
49
  export { buildReadme } from './readme-md.js';
@@ -17,6 +17,7 @@ import { buildMastraIndexTs } from './mastra-index-ts.js';
17
17
  import { buildBuildSkillsScript } from './build-skills-ts.js';
18
18
  import { buildAgentBaseMd } from './agent-base-md.js';
19
19
  import { buildAgentTs } from './agent-ts.js';
20
+ import { buildDemoAgentTs } from './demo-agent-ts.js';
20
21
  import { buildFrameworkGenTs } from './framework-gen-ts.js';
21
22
  import { buildFrameworkTs } from './framework-ts.js';
22
23
  /**
@@ -41,8 +42,8 @@ export const CLI_FILE_BUILDERS = [
41
42
  { relativePath: 'src/main.ts', build: () => buildMainTs() },
42
43
  { relativePath: 'src/cli.ts', build: buildCliTs },
43
44
  { relativePath: 'src/config.ts', build: buildConfigTs },
44
- // User-owned; init-only. Holds `configSchema` + `secretsSchema` edits.
45
- { relativePath: 'src/framework.ts', build: buildFrameworkTs, initOnly: true },
45
+ // User-owned outside markers; generated config/secrets inside markers.
46
+ { relativePath: 'src/framework.ts', build: buildFrameworkTs },
46
47
  // Machine-owned; regenerated on every run. Null when no document types.
47
48
  { relativePath: 'src/framework.gen.ts', build: buildFrameworkGenTs },
48
49
  { relativePath: 'src/mastra/index.ts', build: buildMastraIndexTs },
@@ -55,6 +56,10 @@ export const CLI_FILE_BUILDERS = [
55
56
  relativePath: 'src/agents/agent.ts',
56
57
  build: (spec) => (spec.features.mastra.enabled ? buildAgentTs(spec) : null),
57
58
  },
59
+ {
60
+ relativePath: 'src/agents/demo-agent.ts',
61
+ build: buildDemoAgentTs,
62
+ },
58
63
  // Gitkeep placeholders so directories are tracked in git when otherwise empty.
59
64
  { relativePath: 'src/commands/.gitkeep', build: () => '' },
60
65
  { relativePath: 'src/services/.gitkeep', build: () => '' },
@@ -70,7 +75,7 @@ export const CLI_FILE_BUILDERS = [
70
75
  { relativePath: 'prompts/skills-ext/.gitkeep', build: () => '' },
71
76
  { relativePath: 'tests/.gitkeep', build: () => '' },
72
77
  ];
73
- export { buildCliPackageJson, buildCliTs, buildConfigTs, buildMainTs, buildTsconfigJson, buildJestConfigJs, buildEslintConfigJs, buildGitignore, buildMastraIndexTs, buildBuildSkillsScript, buildAgentBaseMd, buildAgentTs, buildFrameworkGenTs, buildFrameworkTs, };
78
+ export { buildCliPackageJson, buildCliTs, buildConfigTs, buildMainTs, buildTsconfigJson, buildJestConfigJs, buildEslintConfigJs, buildGitignore, buildMastraIndexTs, buildBuildSkillsScript, buildAgentBaseMd, buildAgentTs, buildDemoAgentTs, buildFrameworkGenTs, buildFrameworkTs, };
74
79
  export { buildRootPackageJson } from './root-package-json.js';
75
80
  export { buildPublishConfigTs } from './publish-config-ts.js';
76
81
  export { buildReadme } from './readme-md.js';
@@ -4,8 +4,8 @@
4
4
  * When Mastra is off, emit a no-op placeholder so the directory still exists
5
5
  * and `mastra dev` fails predictably rather than importing partial code.
6
6
  *
7
- * When Mastra is on with full agent config, emit a real Mastra instance
8
- * that registers the agent.
7
+ * When Mastra is on with full agent config, bootstrap the CLI and re-export
8
+ * the configured Mastra Agent via `cli.bootstrap()`.
9
9
  */
10
10
  import { type ClintProjectSpec } from '../../spec/types.js';
11
11
  export declare function buildMastraIndexTs(spec: ClintProjectSpec): string;
@@ -1,48 +1,70 @@
1
+ /**
2
+ * Derive the `{PROVIDER}_API_KEY` env var name from a provider prefix
3
+ * (e.g. `anthropic` → `ANTHROPIC_API_KEY`).
4
+ */
5
+ function getApiKeyEnvVar(provider) {
6
+ return `${provider.toUpperCase()}_API_KEY`;
7
+ }
1
8
  export function buildMastraIndexTs(spec) {
2
9
  const { mastra } = spec.features;
3
10
  if (!mastra.enabled) {
4
11
  return [
12
+ '// @clint:begin mastra-index',
5
13
  '/**',
6
14
  ' * Mastra Studio entry point — placeholder. Enable the `mastra` feature to',
7
15
  ' * populate this file. Until then `mastra:*` scripts are effectively no-ops.',
8
16
  ' */',
9
- '// @clint:begin mastra-index',
10
17
  'export {};',
11
18
  '// @clint:end mastra-index',
12
19
  '',
13
20
  ].join('\n');
14
21
  }
15
- // Full agent config available — emit real Mastra instance
22
+ // Full agent config available — emit bootstrap-based Mastra instance
16
23
  if (mastra.agentId && mastra.models.length > 0) {
17
- const agentId = mastra.agentId;
18
- return [
24
+ const providers = [...new Set(mastra.models.map(m => m.id.split(/[:/]/)[0]))];
25
+ const lines = [
26
+ '// @clint:begin mastra-index',
19
27
  '/**',
20
28
  ' * Mastra Studio entry point for `mastra dev` / `mastra build` / `mastra start`.',
21
29
  ' *',
22
- ' * Exports a configured Mastra instance with the project agent registered.',
30
+ ' * Bootstraps the CLI to get the configured agent, then exports a Mastra',
31
+ ' * instance for Studio integration.',
23
32
  ' */',
24
33
  "import { Mastra } from '@mastra/core/mastra';",
25
- "import { Agent } from '@mastra/core/agent';",
34
+ "import type { Agent } from '@mastra/core/agent';",
35
+ "import path from 'node:path';",
36
+ "import { cli } from '../cli.js';",
37
+ "import { CLI_ROOT } from '../config.js';",
26
38
  '',
27
- '// @clint:begin mastra-index',
28
- `const agent = new Agent({ id: '${agentId}', model: '${(mastra.models.find(m => m.isDefault) ?? mastra.models[0]).id}' });`,
39
+ "// Under `mastra dev`, CLI_ROOT points to .mastra/ (bundler output).",
40
+ '// The actual project root is its parent.',
41
+ "const projectRoot = path.basename(CLI_ROOT) === '.mastra'",
42
+ ' ? path.dirname(CLI_ROOT)',
43
+ ' : CLI_ROOT;',
29
44
  '',
30
- 'export const mastra = new Mastra({',
31
- ` agents: { '${agentId}': agent },`,
32
- '});',
33
- '// @clint:end mastra-index',
45
+ 'const rt = await cli.bootstrap({ workdir: projectRoot });',
34
46
  '',
35
- ].join('\n');
47
+ ];
48
+ // API key env bridge for Studio
49
+ lines.push(`// Workaround: Mastra Studio checks process.env for API keys directly.`, `// Bridge the ph-clint resolved keys so Studio's provider detection works.`);
50
+ for (const provider of providers) {
51
+ const field = `${provider}ApiKey`;
52
+ const envVar = getApiKeyEnvVar(provider);
53
+ lines.push(`{`, ` const key = rt.config.${field} as string | undefined;`, ` if (key && !process.env.${envVar}) process.env.${envVar} = key;`, `}`);
54
+ }
55
+ lines.push('');
56
+ lines.push('const mastraAgent = await rt.mastraAgent as Agent | undefined;', '', 'export const mastra = new Mastra({', ' agents: mastraAgent ? { [mastraAgent.id]: mastraAgent } : {},', '});', '// @clint:end mastra-index', '');
57
+ return lines.join('\n');
36
58
  }
37
59
  // Mastra enabled but no full agent config yet — placeholder
38
60
  return [
61
+ '// @clint:begin mastra-index',
39
62
  '/**',
40
63
  ' * Mastra Studio entry point for `mastra dev` / `mastra build` / `mastra start`.',
41
64
  ' *',
42
65
  " * Re-export a configured `@mastra/core` Mastra instance from here once you",
43
66
  " * replace the demo createAgent shim with a real Mastra Agent.",
44
67
  ' */',
45
- '// @clint:begin mastra-index',
46
68
  'export {};',
47
69
  '// @clint:end mastra-index',
48
70
  '',
@@ -0,0 +1,13 @@
1
+ # Agent Base System Prompt
2
+
3
+ You are PhClintDevAgent, an agent available through the CLI.
4
+
5
+ ## Capabilities
6
+
7
+ This base profile is a minimal placeholder. Extend it with project-specific
8
+ context, workflows, and domain knowledge. Additional sections listed in the
9
+ agent's `sections` array are concatenated after this file at build time.
10
+
11
+ See `examples/05-ph-rupert/prompts/agent-profiles/AgentBase.md` for a more
12
+ complete example covering Powerhouse document system fundamentals,
13
+ collaboration documents (inbox, WBS), and task management guidelines.
@@ -0,0 +1,135 @@
1
+ ## API Reference: CLI Setup
2
+
3
+ ### `defineCli<TSchema, TSecrets>(options): Cli`
4
+
5
+ Factory function that creates a CLI from a declarative options object. Two generic type parameters:
6
+
7
+ - `TSchema extends z.ZodType` — config schema type (defaults to `Record<string, unknown>`)
8
+ - `TSecrets extends z.ZodType` — secrets schema type (defaults to `Record<string, unknown>`)
9
+
10
+ ### `CliOptions<TSchema, TSecrets>` Interface
11
+
12
+ ```typescript
13
+ interface CliOptions<TSchema, TSecrets> {
14
+ name: string; // CLI name (kebab-case, used for config files and env var prefix)
15
+ version: string; // Semantic version (shown in --version)
16
+ root?: string; // Absolute path to package root (resolves sibling packages)
17
+ description: Resolvable<string, Config>; // Static string or function of ({ config, workdir })
18
+ commands: Command<any, any, any>[]; // Array of command definitions
19
+ configSchema?: TSchema; // Zod schema for user-facing config
20
+ secretsSchema?: TSecrets; // Zod schema for sensitive config (API keys, tokens)
21
+ interactive?: InteractiveConfig<Config>; // REPL configuration
22
+ triggers?: Trigger<any, any, any>[]; // Routine loop triggers
23
+ routine?: RoutineConfig; // Routine loop timing & project scanner
24
+ services?: ServiceDefinition<Config>[]; // Background service definitions
25
+ events?: Record<string, (data: any, log: Logger) => void>; // Event handlers
26
+ workdir?: string; // Implementation-level workdir override (hides --workdir flag)
27
+ configDefaults?: Record<string, unknown>; // Layer 5 config defaults
28
+ prompts?: PromptsConfig; // Agent skills & profiles
29
+ }
30
+ ```
31
+
32
+ ### `InteractiveConfig<TConfig>` Interface
33
+
34
+ ```typescript
35
+ interface InteractiveConfig<TConfig> {
36
+ welcome: Resolvable<string, TConfig>; // Welcome message (static or dynamic)
37
+ outputWindow?: number; // Trailing output lines in REPL (default: 6)
38
+ }
39
+ ```
40
+
41
+ ### `Resolvable<T, TConfig>` Type
42
+
43
+ ```typescript
44
+ type Resolvable<T, TConfig> = T | ((ctx: { workdir: string; config: TConfig }) => T);
45
+ ```
46
+
47
+ Allows static values OR dynamic computation based on resolved workdir and config.
48
+
49
+ ### `Cli` Object Interface
50
+
51
+ Methods available on the object returned by `defineCli()`:
52
+
53
+ | Method | Signature | Description |
54
+ |--------|-----------|-------------|
55
+ | `run` | `(argv: string[], options?: RunOptions) => Promise<void>` | Main entry point — parse args, dispatch command or start REPL |
56
+ | `bootstrap` | `(opts?: BootstrapOptions) => Promise<BootstrapResult>` | Initialize runtime without dispatch — for programmatic access |
57
+ | `execute` | `(commandId, args, context?) => Promise<unknown>` | Execute a command directly (programmatic use) |
58
+ | `parseArgs` | `(commandId, argv) => Record<string, unknown>` | Parse CLI argv for a specific command |
59
+ | `getCommand` | `(id: string) => Command \| undefined` | Look up a registered command |
60
+ | `listCommands` | `() => Command[]` | List all registered commands |
61
+ | `generateHelp` | `() => string` | Generate CLI help text |
62
+ | `generateCommandHelp` | `(commandId: string) => string` | Generate help for a specific command |
63
+ | `generateCompletion` | `(shell: string) => string` | Generate shell completion script (bash/zsh/fish) |
64
+ | `configEnvVars` | `() => ConfigEnvVar[]` | List config env var mappings |
65
+ | `getMetadata` | `() => CliMetadata` | JSON-serializable CLI metadata |
66
+ | `configureAgent` | `(loader: AgentLoader<any>) => void` | Set the agent factory (lazy-loaded) |
67
+ | `configureReactor` | `(config: ReactorConfiguration) => void` | Set the reactor configuration (lazy-loaded) |
68
+
69
+ Properties:
70
+
71
+ | Property | Type | Description |
72
+ |----------|------|-------------|
73
+ | `name` | `string` | CLI name |
74
+ | `version` | `string` | CLI version |
75
+ | `description` | `string` | CLI description |
76
+ | `hasAgent` | `boolean` | True when `configureAgent()` has been called |
77
+ | `hasReactor` | `boolean` | True when `configureReactor()` has been called |
78
+
79
+ ### `RunOptions` Interface
80
+
81
+ ```typescript
82
+ interface RunOptions {
83
+ exit?: (code: number) => void; // Override process.exit (for testing)
84
+ stdout?: (message: string) => void; // Capture stdout
85
+ stderr?: (message: string) => void; // Capture stderr
86
+ interactiveInput?: AsyncIterable<string>; // Headless REPL input (for testing)
87
+ resume?: string; // Resume agent conversation by thread ID
88
+ workdir?: string; // Override workdir (for testing)
89
+ configFile?: string; // --config flag value
90
+ logLevel?: LogLevel; // Override log verbosity (default: 'info')
91
+ }
92
+ ```
93
+
94
+ ### `BootstrapOptions` & `BootstrapResult`
95
+
96
+ ```typescript
97
+ interface BootstrapOptions {
98
+ workdir?: string; // Explicit workdir (skips flag/cwd logic)
99
+ configFile?: string; // Config file override
100
+ logLevel?: LogLevel; // Log level (default: 'info')
101
+ stdout?: (text: string) => void;
102
+ stderr?: (text: string) => void;
103
+ }
104
+
105
+ interface BootstrapResult {
106
+ workdir: string;
107
+ config: Record<string, unknown>;
108
+ context: CommandContext;
109
+ getAgent(): Promise<AgentProvider | undefined>;
110
+ get mastraAgent(): Promise<unknown | undefined>;
111
+ }
112
+ ```
113
+
114
+ ### Built-in Commands (Auto-Injected)
115
+
116
+ | Command ID | Condition | Purpose |
117
+ |-----------|-----------|---------|
118
+ | `cli-docs` | Always | Show CLI help (also usable as agent tool) |
119
+ | `config` | When `configSchema` provided | View/modify configuration settings |
120
+ | `{service-id}-start` | Per service definition | Start a background service |
121
+ | `{service-id}-stop` | Per service definition | Stop a running service |
122
+ | `{service-id}-restart` | Per service definition | Restart a service |
123
+ | `{service-id}-ps` | Per service definition | Show service status |
124
+ | `{service-id}-logs` | Per service definition | Tail service logs |
125
+ | `{service-id}-manage` | Per service definition | Interactive management (REPL only) |
126
+ | `{service-id}-ls` | Per service + projectScanner | List discovered projects |
127
+
128
+ ### `readPackageInfo(importMetaUrl)`
129
+
130
+ Utility to read name and version from the nearest `package.json`:
131
+
132
+ ```typescript
133
+ import { readPackageInfo } from '@powerhousedao/ph-clint';
134
+ const { name, version } = readPackageInfo(import.meta.url);
135
+ ```