@powerhousedao/ph-clint-cli 0.1.0-dev.22 → 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.
- package/dist/cli.js +22 -3
- package/dist/codegen/builders/agent-ts.d.ts +4 -4
- package/dist/codegen/builders/agent-ts.js +83 -2
- package/dist/codegen/builders/cli-package-json.js +1 -0
- package/dist/codegen/builders/cli-ts.js +34 -5
- package/dist/codegen/builders/demo-agent-ts.d.ts +8 -0
- package/dist/codegen/builders/demo-agent-ts.js +44 -0
- package/dist/codegen/builders/framework-ts.js +12 -6
- package/dist/codegen/builders/index.d.ts +7 -1
- package/dist/codegen/builders/index.js +25 -4
- package/dist/codegen/builders/mastra-index-ts.d.ts +3 -0
- package/dist/codegen/builders/mastra-index-ts.js +47 -5
- package/dist/codegen/index.js +3 -2
- package/dist/commands/clint-project-init.d.ts +11 -0
- package/dist/commands/clint-project-init.js +10 -1
- package/dist/gen/agent-profiles/PhClintDevAgent.md +13 -0
- package/dist/gen/skills/cli-setup/.cli-docs.md +135 -0
- package/dist/gen/skills/cli-setup/SKILL.md +125 -0
- package/dist/gen/skills/cli-setup/references/00.assess-cli-needs.md +53 -0
- package/dist/gen/skills/cli-setup/references/01.define-config-schema.md +78 -0
- package/dist/gen/skills/cli-setup/references/02.wire-cli.md +141 -0
- package/dist/gen/skills/cli-setup/references/03.add-entrypoint.md +128 -0
- package/dist/gen/skills/cli-setup/references/expected-outcome.md +3 -0
- package/dist/gen/skills/command-definition/.cli-docs.md +84 -0
- package/dist/gen/skills/command-definition/SKILL.md +99 -0
- package/dist/gen/skills/command-definition/references/00.assess-requirements.md +58 -0
- package/dist/gen/skills/command-definition/references/01.define-schema.md +72 -0
- package/dist/gen/skills/command-definition/references/02.implement-execute.md +118 -0
- package/dist/gen/skills/command-definition/references/03.add-prompting.md +54 -0
- package/dist/gen/skills/command-definition/references/expected-outcome.md +3 -0
- package/dist/gen/skills/service-definition/.cli-docs.md +214 -0
- package/dist/gen/skills/service-definition/SKILL.md +148 -0
- package/dist/gen/skills/service-definition/references/00.assess-service.md +69 -0
- package/dist/gen/skills/service-definition/references/01.define-service.md +166 -0
- package/dist/gen/skills/service-definition/references/02.add-preflight.md +132 -0
- package/dist/gen/skills/service-definition/references/03.add-project-scanner.md +82 -0
- package/dist/gen/skills/service-definition/references/04.wire-events.md +120 -0
- package/dist/gen/skills/service-definition/references/expected-outcome.md +3 -0
- package/dist/spec/types.d.ts +22 -0
- package/dist/spec/types.js +20 -1
- package/dist/triggers/spec-change.js +11 -0
- 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
|
-
|
|
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
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Builds `src/agents/agent.ts` — a
|
|
2
|
+
* Builds `src/agents/agent.ts` — a Mastra agent factory.
|
|
3
3
|
*
|
|
4
|
-
* Emitted only when `features.mastra.enabled`.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Emitted only when `features.mastra.enabled`. When the spec includes agent
|
|
5
|
+
* config (agentId, models, profiles), generates a real `@mastra/core` Agent.
|
|
6
|
+
* Otherwise falls back to a deterministic echo-style demo provider.
|
|
7
7
|
*/
|
|
8
8
|
import { type ClintProjectSpec } from '../../spec/types.js';
|
|
9
9
|
export declare function buildAgentTs(spec: ClintProjectSpec): string;
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** Map provider prefix to AI SDK package. */
|
|
2
|
+
function providerImport(provider) {
|
|
3
|
+
switch (provider) {
|
|
4
|
+
case 'anthropic':
|
|
5
|
+
return { pkg: '@ai-sdk/anthropic', fn: 'anthropic' };
|
|
6
|
+
case 'openai':
|
|
7
|
+
return { pkg: '@ai-sdk/openai', fn: 'openai' };
|
|
8
|
+
case 'google':
|
|
9
|
+
return { pkg: '@ai-sdk/google', fn: 'google' };
|
|
10
|
+
default:
|
|
11
|
+
return { pkg: `@ai-sdk/${provider}`, fn: provider };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function buildDemoAgent(spec) {
|
|
15
|
+
const id = spec.features.mastra.agentId ?? spec.name;
|
|
3
16
|
return [
|
|
4
17
|
"import type { AgentProvider, AgentSetupContext, StreamChunk } from '@powerhousedao/ph-clint';",
|
|
5
18
|
"import type { Config } from '../framework.js';",
|
|
@@ -32,3 +45,71 @@ export function buildAgentTs(spec) {
|
|
|
32
45
|
'',
|
|
33
46
|
].join('\n');
|
|
34
47
|
}
|
|
48
|
+
function buildRealAgent(spec) {
|
|
49
|
+
const mastra = spec.features.mastra;
|
|
50
|
+
const agentId = mastra.agentId;
|
|
51
|
+
const agentName = mastra.agentName;
|
|
52
|
+
const defaultModel = mastra.models.find(m => m.isDefault) ?? mastra.models[0];
|
|
53
|
+
const modelId = defaultModel?.id ?? 'anthropic/claude-sonnet-4-5';
|
|
54
|
+
const [provider] = modelId.split(/[:/]/);
|
|
55
|
+
const { pkg, fn } = providerImport(provider);
|
|
56
|
+
const apiKeyField = `${provider}ApiKey`;
|
|
57
|
+
const lines = [];
|
|
58
|
+
lines.push("import { Agent } from '@mastra/core/agent';");
|
|
59
|
+
lines.push("import { MCPClient } from '@mastra/mcp';");
|
|
60
|
+
lines.push("import { createMastraHelpers } from '@powerhousedao/ph-clint/mastra';");
|
|
61
|
+
lines.push("import { createWorkdirStore } from '@powerhousedao/ph-clint';");
|
|
62
|
+
lines.push("import type { AgentSetupContext, AgentProvider } from '@powerhousedao/ph-clint';");
|
|
63
|
+
lines.push("import type { WrapAgentOptions } from '@powerhousedao/ph-clint/mastra';");
|
|
64
|
+
lines.push("import { CLI_NAME } from '../config.js';");
|
|
65
|
+
lines.push("import type { Config } from '../framework.js';");
|
|
66
|
+
lines.push("import { createDemoAgent } from './demo-agent.js';");
|
|
67
|
+
lines.push('');
|
|
68
|
+
lines.push('/**');
|
|
69
|
+
lines.push(' * Agent factory for the CLI.');
|
|
70
|
+
lines.push(' *');
|
|
71
|
+
lines.push(' * Returns a demo agent when no API key is configured, or wraps the full');
|
|
72
|
+
lines.push(' * agent as a ph-clint AgentProvider.');
|
|
73
|
+
lines.push(' */');
|
|
74
|
+
lines.push('export async function createAgent(ctx: AgentSetupContext<Config>): Promise<AgentProvider> {');
|
|
75
|
+
lines.push(` if (!ctx.config.${apiKeyField}) return createDemoAgent();`);
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push(' const m = createMastraHelpers(ctx);');
|
|
78
|
+
lines.push('');
|
|
79
|
+
lines.push(' const agent = new Agent({');
|
|
80
|
+
lines.push(` id: '${agentId}',`);
|
|
81
|
+
lines.push(` name: '${agentName}',`);
|
|
82
|
+
lines.push(` instructions: m.getAgentInstructions('${agentId}'),`);
|
|
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
|
+
lines.push(' tools: async () => {');
|
|
87
|
+
lines.push(" ctx.context.log?.debug('[agent] tools callback invoked');");
|
|
88
|
+
lines.push(' const tools = await m.getTools({ MCPClient });');
|
|
89
|
+
lines.push(" ctx.context.log?.debug(`[agent] tools resolved: ${Object.keys(tools).length} tools`);");
|
|
90
|
+
lines.push(' return tools;');
|
|
91
|
+
lines.push(' },');
|
|
92
|
+
lines.push(' workspace: await m.createWorkspace(),');
|
|
93
|
+
lines.push(' memory: await m.createMemory(),');
|
|
94
|
+
lines.push(' });');
|
|
95
|
+
lines.push('');
|
|
96
|
+
lines.push(' const store = createWorkdirStore(ctx.workdir, CLI_NAME);');
|
|
97
|
+
lines.push(' const wrapOpts: WrapAgentOptions = {');
|
|
98
|
+
lines.push(' maxSteps: 80,');
|
|
99
|
+
lines.push(' enableLogging: ctx.config.agentLogging,');
|
|
100
|
+
lines.push(" logDirectory: store.getStoreFolder('logs'),");
|
|
101
|
+
lines.push(' cacheControl: true,');
|
|
102
|
+
lines.push(' };');
|
|
103
|
+
lines.push(' return m.wrapAgent(agent, wrapOpts);');
|
|
104
|
+
lines.push('}');
|
|
105
|
+
lines.push('');
|
|
106
|
+
return lines.join('\n');
|
|
107
|
+
}
|
|
108
|
+
export function buildAgentTs(spec) {
|
|
109
|
+
const mastra = spec.features.mastra;
|
|
110
|
+
// Generate real agent when we have full config, demo otherwise
|
|
111
|
+
if (mastra.agentId && mastra.models.length > 0) {
|
|
112
|
+
return buildRealAgent(spec);
|
|
113
|
+
}
|
|
114
|
+
return buildDemoAgent(spec);
|
|
115
|
+
}
|
|
@@ -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
|
-
|
|
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,8 +65,34 @@ export function buildCliTs(spec) {
|
|
|
62
65
|
lines.push('');
|
|
63
66
|
lines.push(' // @clint:begin prompts');
|
|
64
67
|
lines.push(' prompts: {');
|
|
65
|
-
|
|
66
|
-
|
|
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) {
|
|
75
|
+
lines.push(' agents: {');
|
|
76
|
+
lines.push(` '${mastra.agentId}': {`);
|
|
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'],");
|
|
88
|
+
}
|
|
89
|
+
lines.push(' skills: [],');
|
|
90
|
+
lines.push(' },');
|
|
91
|
+
lines.push(' },');
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
lines.push(' agents: {},');
|
|
95
|
+
}
|
|
67
96
|
lines.push(' skills: {},');
|
|
68
97
|
lines.push(' },');
|
|
69
98
|
lines.push(' // @clint:end prompts');
|
|
@@ -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,24 +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';
|
|
48
|
+
lines.push(` model: z.string().default('${defaultModel}').describe('LLM model to use'),`, ` agentLogging: z.boolean().default(false).describe('Enable agent conversation logging'),`);
|
|
47
49
|
}
|
|
48
|
-
//
|
|
49
|
-
// Override via switchboard.port / connect.port in configureReactor() if required.
|
|
50
|
-
lines.push(' // Add your own config fields here — this file survives regens.');
|
|
50
|
+
lines.push(' // @clint:end framework-config');
|
|
51
51
|
lines.push('});');
|
|
52
52
|
lines.push('');
|
|
53
53
|
lines.push('export const secretsSchema = z.object({');
|
|
54
|
+
lines.push(' // @clint:begin framework-secrets');
|
|
54
55
|
if (mastra.enabled) {
|
|
55
|
-
|
|
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
|
+
}
|
|
56
61
|
}
|
|
62
|
+
lines.push(' // @clint:end framework-secrets');
|
|
57
63
|
lines.push('});');
|
|
58
64
|
lines.push('');
|
|
59
65
|
lines.push('export type Config = z.infer<typeof configSchema> &');
|
|
60
66
|
lines.push(' z.infer<typeof secretsSchema>;');
|
|
61
67
|
lines.push('');
|
|
62
|
-
lines.push('const fullConfigSchema = configSchema.
|
|
68
|
+
lines.push('const fullConfigSchema = configSchema.extend(secretsSchema.shape);');
|
|
63
69
|
lines.push('');
|
|
64
70
|
if (hasGen) {
|
|
65
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';
|
|
@@ -36,8 +37,13 @@ export interface FileBuilder {
|
|
|
36
37
|
*/
|
|
37
38
|
initOnly?: boolean;
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Generate dynamic file builders for agent profile markdown files.
|
|
42
|
+
* One file per profile in `prompts/agent-profiles/{id}.md`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getProfileFileBuilders(spec: ClintProjectSpec): FileBuilder[];
|
|
39
45
|
export declare const CLI_FILE_BUILDERS: FileBuilder[];
|
|
40
|
-
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, };
|
|
41
47
|
export { buildRootPackageJson } from './root-package-json.js';
|
|
42
48
|
export { buildPublishConfigTs } from './publish-config-ts.js';
|
|
43
49
|
export { buildReadme } from './readme-md.js';
|
|
@@ -17,8 +17,22 @@ 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';
|
|
23
|
+
/**
|
|
24
|
+
* Generate dynamic file builders for agent profile markdown files.
|
|
25
|
+
* One file per profile in `prompts/agent-profiles/{id}.md`.
|
|
26
|
+
*/
|
|
27
|
+
export function getProfileFileBuilders(spec) {
|
|
28
|
+
const { mastra } = spec.features;
|
|
29
|
+
if (!mastra.enabled || mastra.profiles.length === 0)
|
|
30
|
+
return [];
|
|
31
|
+
return mastra.profiles.map((profile) => ({
|
|
32
|
+
relativePath: `prompts/agent-profiles/${profile.id}.md`,
|
|
33
|
+
build: () => profile.content + '\n',
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
22
36
|
export const CLI_FILE_BUILDERS = [
|
|
23
37
|
{ relativePath: 'package.json', build: buildCliPackageJson },
|
|
24
38
|
{ relativePath: 'tsconfig.json', build: () => buildTsconfigJson() },
|
|
@@ -28,17 +42,24 @@ export const CLI_FILE_BUILDERS = [
|
|
|
28
42
|
{ relativePath: 'src/main.ts', build: () => buildMainTs() },
|
|
29
43
|
{ relativePath: 'src/cli.ts', build: buildCliTs },
|
|
30
44
|
{ relativePath: 'src/config.ts', build: buildConfigTs },
|
|
31
|
-
// User-owned
|
|
32
|
-
{ relativePath: 'src/framework.ts', build: buildFrameworkTs
|
|
45
|
+
// User-owned outside markers; generated config/secrets inside markers.
|
|
46
|
+
{ relativePath: 'src/framework.ts', build: buildFrameworkTs },
|
|
33
47
|
// Machine-owned; regenerated on every run. Null when no document types.
|
|
34
48
|
{ relativePath: 'src/framework.gen.ts', build: buildFrameworkGenTs },
|
|
35
49
|
{ relativePath: 'src/mastra/index.ts', build: buildMastraIndexTs },
|
|
36
50
|
{ relativePath: 'scripts/build-skills.ts', build: () => buildBuildSkillsScript() },
|
|
37
|
-
{
|
|
51
|
+
{
|
|
52
|
+
relativePath: 'prompts/agent-profiles/AgentBase.md',
|
|
53
|
+
build: (spec) => (spec.features.mastra.profiles.length > 0 ? null : buildAgentBaseMd(spec)),
|
|
54
|
+
},
|
|
38
55
|
{
|
|
39
56
|
relativePath: 'src/agents/agent.ts',
|
|
40
57
|
build: (spec) => (spec.features.mastra.enabled ? buildAgentTs(spec) : null),
|
|
41
58
|
},
|
|
59
|
+
{
|
|
60
|
+
relativePath: 'src/agents/demo-agent.ts',
|
|
61
|
+
build: buildDemoAgentTs,
|
|
62
|
+
},
|
|
42
63
|
// Gitkeep placeholders so directories are tracked in git when otherwise empty.
|
|
43
64
|
{ relativePath: 'src/commands/.gitkeep', build: () => '' },
|
|
44
65
|
{ relativePath: 'src/services/.gitkeep', build: () => '' },
|
|
@@ -54,7 +75,7 @@ export const CLI_FILE_BUILDERS = [
|
|
|
54
75
|
{ relativePath: 'prompts/skills-ext/.gitkeep', build: () => '' },
|
|
55
76
|
{ relativePath: 'tests/.gitkeep', build: () => '' },
|
|
56
77
|
];
|
|
57
|
-
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, };
|
|
58
79
|
export { buildRootPackageJson } from './root-package-json.js';
|
|
59
80
|
export { buildPublishConfigTs } from './publish-config-ts.js';
|
|
60
81
|
export { buildReadme } from './readme-md.js';
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*
|
|
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
|
+
*
|
|
7
|
+
* When Mastra is on with full agent config, bootstrap the CLI and re-export
|
|
8
|
+
* the configured Mastra Agent via `cli.bootstrap()`.
|
|
6
9
|
*/
|
|
7
10
|
import { type ClintProjectSpec } from '../../spec/types.js';
|
|
8
11
|
export declare function buildMastraIndexTs(spec: ClintProjectSpec): string;
|
|
@@ -1,28 +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
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
// Full agent config available — emit bootstrap-based Mastra instance
|
|
23
|
+
if (mastra.agentId && mastra.models.length > 0) {
|
|
24
|
+
const providers = [...new Set(mastra.models.map(m => m.id.split(/[:/]/)[0]))];
|
|
25
|
+
const lines = [
|
|
26
|
+
'// @clint:begin mastra-index',
|
|
27
|
+
'/**',
|
|
28
|
+
' * Mastra Studio entry point for `mastra dev` / `mastra build` / `mastra start`.',
|
|
29
|
+
' *',
|
|
30
|
+
' * Bootstraps the CLI to get the configured agent, then exports a Mastra',
|
|
31
|
+
' * instance for Studio integration.',
|
|
32
|
+
' */',
|
|
33
|
+
"import { Mastra } from '@mastra/core/mastra';",
|
|
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';",
|
|
38
|
+
'',
|
|
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;',
|
|
44
|
+
'',
|
|
45
|
+
'const rt = await cli.bootstrap({ workdir: projectRoot });',
|
|
46
|
+
'',
|
|
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');
|
|
58
|
+
}
|
|
59
|
+
// Mastra enabled but no full agent config yet — placeholder
|
|
18
60
|
return [
|
|
61
|
+
'// @clint:begin mastra-index',
|
|
19
62
|
'/**',
|
|
20
63
|
' * Mastra Studio entry point for `mastra dev` / `mastra build` / `mastra start`.',
|
|
21
64
|
' *',
|
|
22
65
|
" * Re-export a configured `@mastra/core` Mastra instance from here once you",
|
|
23
66
|
" * replace the demo createAgent shim with a real Mastra Agent.",
|
|
24
67
|
' */',
|
|
25
|
-
'// @clint:begin mastra-index',
|
|
26
68
|
'export {};',
|
|
27
69
|
'// @clint:end mastra-index',
|
|
28
70
|
'',
|
package/dist/codegen/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import { hashContent, hashFile, readHashes, writeHashes, } from './hashes.js';
|
|
|
35
35
|
import { hasMarkers, spliceMarkerRegions } from './markers.js';
|
|
36
36
|
import { writeFileEnsuringDir, isDirEmptyEnough } from './write.js';
|
|
37
37
|
import { migrateFlatToSplit } from './migrate/flat-to-split.js';
|
|
38
|
-
import { CLI_FILE_BUILDERS, buildReadme, buildRootPackageJson, buildPublishConfigTs, buildAppIndexTs, } from './builders/index.js';
|
|
38
|
+
import { CLI_FILE_BUILDERS, getProfileFileBuilders, buildReadme, buildRootPackageJson, buildPublishConfigTs, buildAppIndexTs, } from './builders/index.js';
|
|
39
39
|
/**
|
|
40
40
|
* Generate / reconcile a project at `targetDir` from the given spec.
|
|
41
41
|
*/
|
|
@@ -79,7 +79,8 @@ function planFiles(spec, targetDir) {
|
|
|
79
79
|
const cliDir = split ? path.join(targetDir, getCliFolderName(spec)) : targetDir;
|
|
80
80
|
const appDir = split ? path.join(targetDir, getAppFolderName(spec)) : null;
|
|
81
81
|
const planned = [];
|
|
82
|
-
|
|
82
|
+
const allBuilders = [...CLI_FILE_BUILDERS, ...getProfileFileBuilders(spec)];
|
|
83
|
+
for (const builder of allBuilders) {
|
|
83
84
|
const content = builder.build(spec);
|
|
84
85
|
if (content === null)
|
|
85
86
|
continue;
|
|
@@ -38,6 +38,17 @@ export declare const init: import("@powerhousedao/ph-clint").Command<z.ZodObject
|
|
|
38
38
|
powerhouse: "Disabled" | "Reactor" | "Switchboard" | "Connect";
|
|
39
39
|
mastra: {
|
|
40
40
|
enabled: boolean;
|
|
41
|
+
agentId: string | null;
|
|
42
|
+
agentName: string | null;
|
|
43
|
+
models: {
|
|
44
|
+
id: string;
|
|
45
|
+
isDefault: boolean;
|
|
46
|
+
}[];
|
|
47
|
+
profiles: {
|
|
48
|
+
id: string;
|
|
49
|
+
title: string;
|
|
50
|
+
content: string;
|
|
51
|
+
}[];
|
|
41
52
|
};
|
|
42
53
|
routine: {
|
|
43
54
|
enabled: boolean;
|
|
@@ -73,13 +73,22 @@ export function buildSpec(input) {
|
|
|
73
73
|
// Routine is forced on when Mastra is on — the REPL-only path works, but
|
|
74
74
|
// the agent routine loop is what most users actually want.
|
|
75
75
|
const routine = input.enableRoutine || input.enableMastra;
|
|
76
|
+
const mastra = input.enableMastra
|
|
77
|
+
? {
|
|
78
|
+
enabled: true,
|
|
79
|
+
agentId: `${name}-agent`,
|
|
80
|
+
agentName: `${name.charAt(0).toUpperCase()}${name.slice(1).replace(/-(\w)/g, (_, c) => ' ' + c.toUpperCase())} Agent`,
|
|
81
|
+
models: [{ id: 'anthropic/claude-sonnet-4-5', isDefault: true }],
|
|
82
|
+
profiles: [{ id: 'base', title: 'Base', content: 'You are a helpful assistant.' }],
|
|
83
|
+
}
|
|
84
|
+
: { enabled: false };
|
|
76
85
|
const candidate = {
|
|
77
86
|
name,
|
|
78
87
|
scope,
|
|
79
88
|
description: input.description ?? '',
|
|
80
89
|
features: {
|
|
81
90
|
powerhouse: input.enablePowerhouse ? 'Connect' : 'Disabled',
|
|
82
|
-
mastra
|
|
91
|
+
mastra,
|
|
83
92
|
routine: { enabled: routine },
|
|
84
93
|
},
|
|
85
94
|
};
|
|
@@ -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.
|