@powerhousedao/ph-clint-cli 0.1.0-dev.1 → 0.1.0-dev.3
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 +7 -4
- package/dist/commands/clint-project-build.d.ts +7 -0
- package/dist/commands/clint-project-build.js +51 -0
- package/dist/commands/clint-project-init.d.ts +57 -0
- package/dist/commands/clint-project-init.js +168 -0
- package/dist/commands/clint-project-publish.d.ts +15 -0
- package/dist/commands/clint-project-publish.js +71 -0
- package/dist/commands/clint-project-regen.d.ts +18 -0
- package/dist/commands/clint-project-regen.js +68 -0
- package/dist/services/clint-project.d.ts +1 -0
- package/dist/services/clint-project.js +47 -0
- package/package.json +5 -6
package/dist/cli.js
CHANGED
|
@@ -14,8 +14,11 @@ import { configSchema, secretsSchema } from './framework.js';
|
|
|
14
14
|
// @clint:begin imports
|
|
15
15
|
import { documentModels } from '@powerhousedao/ph-clint-app';
|
|
16
16
|
import { createAgent } from './agents/clint-agent.js';
|
|
17
|
-
import { init } from './commands/init.js';
|
|
18
|
-
import { regen } from './commands/regen.js';
|
|
17
|
+
import { init } from './commands/clint-project-init.js';
|
|
18
|
+
import { regen } from './commands/clint-project-regen.js';
|
|
19
|
+
import { build } from './commands/clint-project-build.js';
|
|
20
|
+
import { clintProjectPublish } from './commands/clint-project-publish.js';
|
|
21
|
+
import { clintProject } from './services/clint-project.js';
|
|
19
22
|
import { specChangeTrigger } from './triggers/spec-change.js';
|
|
20
23
|
// @clint:end imports
|
|
21
24
|
// Connect (ph connect) must run inside the Reactor Package (ph-clint-app).
|
|
@@ -28,10 +31,10 @@ export const cli = defineCli({
|
|
|
28
31
|
configSchema,
|
|
29
32
|
secretsSchema,
|
|
30
33
|
// @clint:begin commands
|
|
31
|
-
commands: [init, regen],
|
|
34
|
+
commands: [init, regen, build, clintProjectPublish],
|
|
32
35
|
// @clint:end commands
|
|
33
36
|
// @clint:begin services
|
|
34
|
-
services: [],
|
|
37
|
+
services: [clintProject],
|
|
35
38
|
// @clint:end services
|
|
36
39
|
// @clint:begin triggers
|
|
37
40
|
triggers: [specChangeTrigger],
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { defineCommand } from '@powerhousedao/ph-clint';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { detectLayout } from '@powerhousedao/ph-clint-dev';
|
|
6
|
+
const inputSchema = z.object({
|
|
7
|
+
dir: z.string().optional().describe('Project root directory (default: cwd)'),
|
|
8
|
+
verbose: z.boolean().default(false),
|
|
9
|
+
});
|
|
10
|
+
function execInDir(dir, cmd, verbose) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const proc = spawn(cmd, {
|
|
13
|
+
cwd: dir,
|
|
14
|
+
shell: true,
|
|
15
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
16
|
+
});
|
|
17
|
+
let stderr = '';
|
|
18
|
+
if (!verbose && proc.stderr) {
|
|
19
|
+
proc.stderr.on('data', (data) => {
|
|
20
|
+
stderr += data.toString();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
proc.on('close', (code) => {
|
|
24
|
+
if (code === 0)
|
|
25
|
+
resolve();
|
|
26
|
+
else
|
|
27
|
+
reject(new Error(`Command "${cmd}" failed in ${dir} (exit ${code})${stderr ? `:\n${stderr}` : ''}`));
|
|
28
|
+
});
|
|
29
|
+
proc.on('error', (err) => reject(err));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export const build = defineCommand({
|
|
33
|
+
id: 'clint-project-build',
|
|
34
|
+
description: 'Build all packages in the project',
|
|
35
|
+
inputSchema,
|
|
36
|
+
execute: async (input, { workdir, stdout }) => {
|
|
37
|
+
const dir = path.resolve(input.dir ?? workdir);
|
|
38
|
+
const layout = detectLayout(dir);
|
|
39
|
+
if (layout?.type === 'split') {
|
|
40
|
+
stdout(`Building app package (${layout.app})...\n`);
|
|
41
|
+
await execInDir(layout.app, 'pnpm build', input.verbose);
|
|
42
|
+
stdout(`Building cli package (${layout.cli})...\n`);
|
|
43
|
+
await execInDir(layout.cli, 'pnpm build', input.verbose);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
stdout(`Building project...\n`);
|
|
47
|
+
await execInDir(layout?.cli ?? dir, 'pnpm build', input.verbose);
|
|
48
|
+
}
|
|
49
|
+
return { text: 'Build complete.' };
|
|
50
|
+
},
|
|
51
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type ClintProjectSpec } from '../spec/types.js';
|
|
3
|
+
declare const inputSchema: z.ZodObject<{
|
|
4
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
5
|
+
name: z.ZodString;
|
|
6
|
+
description: z.ZodOptional<z.ZodString>;
|
|
7
|
+
enablePowerhouse: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
enableMastra: z.ZodDefault<z.ZodBoolean>;
|
|
9
|
+
enableRoutine: z.ZodDefault<z.ZodBoolean>;
|
|
10
|
+
force: z.ZodDefault<z.ZodBoolean>;
|
|
11
|
+
skipPhInit: z.ZodDefault<z.ZodBoolean>;
|
|
12
|
+
skipInstall: z.ZodDefault<z.ZodBoolean>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
type InitInput = z.output<typeof inputSchema>;
|
|
15
|
+
export declare function buildSpec(input: InitInput): ClintProjectSpec;
|
|
16
|
+
export declare const init: import("@powerhousedao/ph-clint").Command<z.ZodObject<{
|
|
17
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
18
|
+
name: z.ZodString;
|
|
19
|
+
description: z.ZodOptional<z.ZodString>;
|
|
20
|
+
enablePowerhouse: z.ZodDefault<z.ZodBoolean>;
|
|
21
|
+
enableMastra: z.ZodDefault<z.ZodBoolean>;
|
|
22
|
+
enableRoutine: z.ZodDefault<z.ZodBoolean>;
|
|
23
|
+
force: z.ZodDefault<z.ZodBoolean>;
|
|
24
|
+
skipPhInit: z.ZodDefault<z.ZodBoolean>;
|
|
25
|
+
skipInstall: z.ZodDefault<z.ZodBoolean>;
|
|
26
|
+
}, z.core.$strip>, {
|
|
27
|
+
text: string;
|
|
28
|
+
data?: undefined;
|
|
29
|
+
} | {
|
|
30
|
+
text: string;
|
|
31
|
+
data: {
|
|
32
|
+
targetDir: string;
|
|
33
|
+
spec: {
|
|
34
|
+
name: string;
|
|
35
|
+
version: string;
|
|
36
|
+
description: string;
|
|
37
|
+
features: {
|
|
38
|
+
powerhouse: {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
switchboard: boolean;
|
|
41
|
+
connect: boolean;
|
|
42
|
+
};
|
|
43
|
+
mastra: {
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
};
|
|
46
|
+
routine: {
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
documentTypes: string[];
|
|
51
|
+
scope?: string | undefined;
|
|
52
|
+
bin?: string | undefined;
|
|
53
|
+
};
|
|
54
|
+
fileCount: number;
|
|
55
|
+
};
|
|
56
|
+
}, Record<string, unknown>>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ph-clint init` — the project bootstrap wizard.
|
|
3
|
+
*
|
|
4
|
+
* Behavior:
|
|
5
|
+
* 1. Resolve target dir (positional `dir`, defaults to cwd).
|
|
6
|
+
* 2. Assert it is empty (or `--force`).
|
|
7
|
+
* 3. Collect project identity + feature toggles (prompt for missing).
|
|
8
|
+
* 4. Assemble a ClintProjectSpec and validate with Zod.
|
|
9
|
+
* 5. Write the spec + emit the generated tree via `generateProject`.
|
|
10
|
+
* 6. (Split layout) run `ph init` in `{name}-app/` unless `--skip-ph-init`.
|
|
11
|
+
* 7. Run `pnpm install` in generated sub-projects unless `--skip-install`.
|
|
12
|
+
* 8. Print next-steps guidance.
|
|
13
|
+
*/
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { defineCommand } from '@powerhousedao/ph-clint';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { clintProjectSpecSchema, } from '../spec/types.js';
|
|
18
|
+
import { generateProject } from '../codegen/index.js';
|
|
19
|
+
import { isDirEmptyEnough } from '../codegen/write.js';
|
|
20
|
+
import { runPhInit, runPnpmInstall } from '../codegen/scaffold.js';
|
|
21
|
+
const inputSchema = z.object({
|
|
22
|
+
dir: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Target directory (defaults to the current directory)'),
|
|
26
|
+
name: z
|
|
27
|
+
.string()
|
|
28
|
+
.describe('Project name — bare, or `@scope/name`. Lowercase letters, digits, hyphens.'),
|
|
29
|
+
description: z.string().optional().describe('Short project description'),
|
|
30
|
+
enablePowerhouse: z
|
|
31
|
+
.boolean()
|
|
32
|
+
.default(false)
|
|
33
|
+
.describe('Enable Powerhouse reactor (requires split layout)'),
|
|
34
|
+
enableMastra: z
|
|
35
|
+
.boolean()
|
|
36
|
+
.default(false)
|
|
37
|
+
.describe('Enable Mastra AI agent stack'),
|
|
38
|
+
enableRoutine: z
|
|
39
|
+
.boolean()
|
|
40
|
+
.default(false)
|
|
41
|
+
.describe('Enable the tick-based routine loop'),
|
|
42
|
+
force: z
|
|
43
|
+
.boolean()
|
|
44
|
+
.default(false)
|
|
45
|
+
.describe('Overwrite files if the target directory is not empty'),
|
|
46
|
+
skipPhInit: z
|
|
47
|
+
.boolean()
|
|
48
|
+
.default(false)
|
|
49
|
+
.describe('Do not run `ph init` in the app folder after scaffolding'),
|
|
50
|
+
skipInstall: z
|
|
51
|
+
.boolean()
|
|
52
|
+
.default(false)
|
|
53
|
+
.describe('Do not run `pnpm install` after scaffolding'),
|
|
54
|
+
});
|
|
55
|
+
/** Parse `@scope/name` into { scope, name }. */
|
|
56
|
+
function splitPackageName(raw) {
|
|
57
|
+
const match = /^@([a-z0-9][a-z0-9-]*)\/([a-z0-9][a-z0-9-]*)$/.exec(raw);
|
|
58
|
+
if (match)
|
|
59
|
+
return { scope: match[1], name: match[2] };
|
|
60
|
+
if (raw.startsWith('@')) {
|
|
61
|
+
throw new Error(`invalid package name "${raw}" — expected "@scope/name" with lowercase letters, digits, hyphens`);
|
|
62
|
+
}
|
|
63
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(raw)) {
|
|
64
|
+
throw new Error(`invalid project name "${raw}" — expected lowercase letters, digits, hyphens`);
|
|
65
|
+
}
|
|
66
|
+
return { name: raw };
|
|
67
|
+
}
|
|
68
|
+
export function buildSpec(input) {
|
|
69
|
+
const { scope, name } = splitPackageName(input.name);
|
|
70
|
+
// Routine is forced on when Mastra is on — the REPL-only path works, but
|
|
71
|
+
// the agent routine loop is what most users actually want.
|
|
72
|
+
const routine = input.enableRoutine || input.enableMastra;
|
|
73
|
+
const candidate = {
|
|
74
|
+
name,
|
|
75
|
+
scope,
|
|
76
|
+
description: input.description ?? '',
|
|
77
|
+
features: {
|
|
78
|
+
powerhouse: { enabled: input.enablePowerhouse },
|
|
79
|
+
mastra: { enabled: input.enableMastra },
|
|
80
|
+
routine: { enabled: routine },
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
return clintProjectSpecSchema.parse(candidate);
|
|
84
|
+
}
|
|
85
|
+
export const init = defineCommand({
|
|
86
|
+
id: 'clint-project-init',
|
|
87
|
+
description: 'Initialize a new ph-clint project in the target directory',
|
|
88
|
+
inputSchema,
|
|
89
|
+
prompt: {
|
|
90
|
+
promptForDefaults: true,
|
|
91
|
+
promptOptional: ['description'],
|
|
92
|
+
},
|
|
93
|
+
execute: async (input, { workdir, stdout }) => {
|
|
94
|
+
const targetDir = path.resolve(workdir, input.dir ?? '.');
|
|
95
|
+
if (!input.force) {
|
|
96
|
+
const empty = await isDirEmptyEnough(targetDir);
|
|
97
|
+
if (!empty) {
|
|
98
|
+
return {
|
|
99
|
+
text: `Error: ${targetDir} is not empty. Re-run with --force to initialize anyway,\n` +
|
|
100
|
+
'or choose a different --dir.',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const spec = buildSpec(input);
|
|
105
|
+
const result = await generateProject({
|
|
106
|
+
targetDir,
|
|
107
|
+
spec,
|
|
108
|
+
mode: 'create',
|
|
109
|
+
allowNonEmpty: input.force,
|
|
110
|
+
});
|
|
111
|
+
stdout(`Generated ${result.files.length} files in ${targetDir}\n`);
|
|
112
|
+
const split = spec.features.powerhouse.enabled;
|
|
113
|
+
// Reactor package scaffolding (split layout only).
|
|
114
|
+
if (split && result.appDir && !input.skipPhInit) {
|
|
115
|
+
const phResult = await runPhInit({
|
|
116
|
+
targetDir,
|
|
117
|
+
appDir: result.appDir,
|
|
118
|
+
spec,
|
|
119
|
+
log: (msg) => stdout(msg + '\n'),
|
|
120
|
+
});
|
|
121
|
+
if (!phResult.ran) {
|
|
122
|
+
stdout(`Note: \`ph init\` did not run — ${phResult.reason ?? 'see above'}.\n`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Dependency installation.
|
|
126
|
+
if (!input.skipInstall) {
|
|
127
|
+
const dirs = [];
|
|
128
|
+
if (split) {
|
|
129
|
+
if (result.appDir)
|
|
130
|
+
dirs.push(result.appDir);
|
|
131
|
+
dirs.push(result.cliDir);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
dirs.push(targetDir);
|
|
135
|
+
}
|
|
136
|
+
await runPnpmInstall({
|
|
137
|
+
dirs,
|
|
138
|
+
log: (msg) => stdout(msg + '\n'),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const next = [];
|
|
142
|
+
next.push('');
|
|
143
|
+
next.push('Next steps:');
|
|
144
|
+
const relDir = path.relative(workdir, targetDir) || '.';
|
|
145
|
+
if (split) {
|
|
146
|
+
next.push(` cd ${relDir}`);
|
|
147
|
+
if (input.skipPhInit) {
|
|
148
|
+
next.push(` cd ${spec.name}-app && ph init . && cd ..`);
|
|
149
|
+
}
|
|
150
|
+
if (input.skipInstall) {
|
|
151
|
+
next.push(' pnpm install');
|
|
152
|
+
}
|
|
153
|
+
next.push(' pnpm dev');
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
next.push(` cd ${relDir}`);
|
|
157
|
+
if (input.skipInstall)
|
|
158
|
+
next.push(' pnpm install');
|
|
159
|
+
next.push(' pnpm dev');
|
|
160
|
+
}
|
|
161
|
+
next.push('');
|
|
162
|
+
next.push('Edit the project spec and run `ph-clint regen` to regenerate.');
|
|
163
|
+
return {
|
|
164
|
+
text: next.join('\n'),
|
|
165
|
+
data: { targetDir, spec, fileCount: result.files.length },
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const clintProjectPublish: import("@powerhousedao/ph-clint").Command<z.ZodObject<{
|
|
3
|
+
tag: z.ZodEnum<{
|
|
4
|
+
dev: "dev";
|
|
5
|
+
staging: "staging";
|
|
6
|
+
production: "production";
|
|
7
|
+
}>;
|
|
8
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
9
|
+
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
10
|
+
skipBuild: z.ZodDefault<z.ZodBoolean>;
|
|
11
|
+
verbose: z.ZodDefault<z.ZodBoolean>;
|
|
12
|
+
}, z.core.$strip>, {
|
|
13
|
+
text: string;
|
|
14
|
+
data: import("@powerhousedao/ph-clint-dev/publish").PublishResult;
|
|
15
|
+
}, Record<string, unknown>>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { defineCommand } from '@powerhousedao/ph-clint';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { resolvePublishPlan, buildPackages, publishPackages, } from '@powerhousedao/ph-clint-dev/publish';
|
|
5
|
+
const inputSchema = z.object({
|
|
6
|
+
tag: z.enum(['dev', 'staging', 'production']).describe('Release channel'),
|
|
7
|
+
dir: z.string().optional().describe('Project root directory'),
|
|
8
|
+
dryRun: z.boolean().default(false).describe('Preview without publishing'),
|
|
9
|
+
skipBuild: z.boolean().default(false),
|
|
10
|
+
verbose: z.boolean().default(false),
|
|
11
|
+
});
|
|
12
|
+
function formatPlanPreview(plan) {
|
|
13
|
+
const lines = [];
|
|
14
|
+
const distTag = plan.tag === 'production' ? 'latest' : plan.tag;
|
|
15
|
+
lines.push(`Publish plan:`);
|
|
16
|
+
lines.push(` Group: ${plan.groupName}`);
|
|
17
|
+
lines.push(` Version: ${plan.version}`);
|
|
18
|
+
lines.push(` Tag: ${distTag}`);
|
|
19
|
+
lines.push(` Registry: ${plan.registry}`);
|
|
20
|
+
lines.push(` Packages:`);
|
|
21
|
+
for (const pkg of plan.packages) {
|
|
22
|
+
lines.push(` - ${pkg.name}`);
|
|
23
|
+
}
|
|
24
|
+
return lines.join('\n');
|
|
25
|
+
}
|
|
26
|
+
export const clintProjectPublish = defineCommand({
|
|
27
|
+
id: 'clint-project-publish',
|
|
28
|
+
description: 'Publish project packages to npm',
|
|
29
|
+
inputSchema,
|
|
30
|
+
execute: async (input, { workdir, stdout }) => {
|
|
31
|
+
const dir = path.resolve(input.dir ?? workdir);
|
|
32
|
+
const logLines = [];
|
|
33
|
+
const log = (msg) => {
|
|
34
|
+
logLines.push(msg);
|
|
35
|
+
stdout(msg + '\n');
|
|
36
|
+
};
|
|
37
|
+
// 1. Resolve plan
|
|
38
|
+
const plan = await resolvePublishPlan({
|
|
39
|
+
tag: input.tag,
|
|
40
|
+
configPath: undefined, // auto-discover from dir
|
|
41
|
+
skipGitCheck: input.dryRun,
|
|
42
|
+
log,
|
|
43
|
+
});
|
|
44
|
+
// 2. Show preview
|
|
45
|
+
stdout(formatPlanPreview(plan) + '\n');
|
|
46
|
+
// 3. Build
|
|
47
|
+
if (!input.skipBuild) {
|
|
48
|
+
await buildPackages(plan, { verbose: input.verbose, log });
|
|
49
|
+
}
|
|
50
|
+
// 4. Publish (or dry-run)
|
|
51
|
+
const result = await publishPackages(plan, {
|
|
52
|
+
dryRun: input.dryRun,
|
|
53
|
+
verbose: input.verbose,
|
|
54
|
+
log,
|
|
55
|
+
});
|
|
56
|
+
if (result.success) {
|
|
57
|
+
return {
|
|
58
|
+
text: result.dryRun
|
|
59
|
+
? `Dry run complete — ${plan.packages.length} packages validated at ${result.version}.`
|
|
60
|
+
: `Published ${result.published.length} packages at ${result.version}.`,
|
|
61
|
+
data: result,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return {
|
|
66
|
+
text: `Publish failed. Published: ${result.published.length}, Failed: ${result.failed.length}.`,
|
|
67
|
+
data: result,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const regen: import("@powerhousedao/ph-clint").Command<z.ZodObject<{
|
|
3
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
4
|
+
force: z.ZodDefault<z.ZodBoolean>;
|
|
5
|
+
}, z.core.$strip>, {
|
|
6
|
+
text: string;
|
|
7
|
+
data?: undefined;
|
|
8
|
+
} | {
|
|
9
|
+
text: string;
|
|
10
|
+
data: {
|
|
11
|
+
mode: "create" | "update";
|
|
12
|
+
files: string[];
|
|
13
|
+
skipped: string[];
|
|
14
|
+
deleted: string[];
|
|
15
|
+
migrated: boolean;
|
|
16
|
+
warnings: string[];
|
|
17
|
+
};
|
|
18
|
+
}, Record<string, unknown>>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ph-clint regen` — reconcile an existing project against its persisted
|
|
3
|
+
* spec. Equivalent to running the generator in update mode.
|
|
4
|
+
*
|
|
5
|
+
* Run inside a project directory (or pass `--dir`). If the spec has flipped
|
|
6
|
+
* `features.powerhouse.enabled` since the last run, the flat → split
|
|
7
|
+
* migration fires first.
|
|
8
|
+
*/
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { defineCommand } from '@powerhousedao/ph-clint';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { readProjectSpec } from '../spec/file.js';
|
|
13
|
+
import { generateProject } from '../codegen/index.js';
|
|
14
|
+
const inputSchema = z.object({
|
|
15
|
+
dir: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Project directory (defaults to the current directory)'),
|
|
19
|
+
force: z
|
|
20
|
+
.boolean()
|
|
21
|
+
.default(false)
|
|
22
|
+
.describe('Overwrite user-edited managed files and bypass the git-dirty guard'),
|
|
23
|
+
});
|
|
24
|
+
export const regen = defineCommand({
|
|
25
|
+
id: 'clint-project-regen',
|
|
26
|
+
description: 'Regenerate project files from the persisted spec',
|
|
27
|
+
inputSchema,
|
|
28
|
+
execute: async (input, { workdir, stdout, log }) => {
|
|
29
|
+
const targetDir = path.resolve(workdir, input.dir ?? '.');
|
|
30
|
+
const spec = await readProjectSpec(targetDir);
|
|
31
|
+
if (!spec) {
|
|
32
|
+
return {
|
|
33
|
+
text: `Error: no spec found at ${targetDir}/.ph/ph-clint-cli/project-spec.json.\n` +
|
|
34
|
+
'Run `ph-clint init` first to scaffold a new project.',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const warnings = [];
|
|
38
|
+
const result = await generateProject({
|
|
39
|
+
targetDir,
|
|
40
|
+
spec,
|
|
41
|
+
mode: 'update',
|
|
42
|
+
force: input.force,
|
|
43
|
+
onWarn: (m) => warnings.push(m),
|
|
44
|
+
});
|
|
45
|
+
const lines = [];
|
|
46
|
+
if (result.migrated)
|
|
47
|
+
lines.push('Migrated layout: flat → split.');
|
|
48
|
+
lines.push(`Wrote ${result.files.length} file(s); skipped ${result.skipped.length}; deleted ${result.deleted.length}.`);
|
|
49
|
+
for (const w of warnings) {
|
|
50
|
+
if (log)
|
|
51
|
+
log.warn(w);
|
|
52
|
+
else
|
|
53
|
+
stdout('warning: ' + w + '\n');
|
|
54
|
+
}
|
|
55
|
+
stdout(lines.join('\n') + '\n');
|
|
56
|
+
return {
|
|
57
|
+
text: lines.join('\n'),
|
|
58
|
+
data: {
|
|
59
|
+
mode: result.mode,
|
|
60
|
+
files: result.files.map((f) => f.relativePath),
|
|
61
|
+
skipped: result.skipped.map((f) => f.relativePath),
|
|
62
|
+
deleted: result.deleted.map((f) => f.relativePath),
|
|
63
|
+
migrated: result.migrated,
|
|
64
|
+
warnings,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const clintProject: import("@powerhousedao/ph-clint").ServiceDefinition<Record<string, unknown>>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { defineService, checkWorkdir } from '@powerhousedao/ph-clint';
|
|
4
|
+
export const clintProject = defineService({
|
|
5
|
+
id: 'clint-project',
|
|
6
|
+
name: 'Clint Project',
|
|
7
|
+
description: 'Development server for a ph-clint implementation project',
|
|
8
|
+
command: () => 'pnpm dev',
|
|
9
|
+
readiness: {
|
|
10
|
+
patterns: [{
|
|
11
|
+
name: 'cli-ready',
|
|
12
|
+
pattern: /Interactive mode started|ready/i,
|
|
13
|
+
captures: {},
|
|
14
|
+
}],
|
|
15
|
+
timeout: 30_000,
|
|
16
|
+
},
|
|
17
|
+
preflight: [
|
|
18
|
+
checkWorkdir((cwd) => {
|
|
19
|
+
const pkg = path.join(cwd, 'package.json');
|
|
20
|
+
if (!fs.existsSync(pkg))
|
|
21
|
+
return false;
|
|
22
|
+
try {
|
|
23
|
+
const json = JSON.parse(fs.readFileSync(pkg, 'utf8'));
|
|
24
|
+
return !!json.dependencies?.['@powerhousedao/ph-clint']
|
|
25
|
+
|| !!json.scripts?.dev;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}, 'Not a ph-clint project directory', 'Use --workdir to point to a ph-clint project root'),
|
|
31
|
+
],
|
|
32
|
+
projectScanner: {
|
|
33
|
+
isProjectFolder: (p) => {
|
|
34
|
+
const pkg = path.join(p, 'package.json');
|
|
35
|
+
if (!fs.existsSync(pkg))
|
|
36
|
+
return false;
|
|
37
|
+
try {
|
|
38
|
+
const json = JSON.parse(fs.readFileSync(pkg, 'utf8'));
|
|
39
|
+
return !!json.dependencies?.['@powerhousedao/ph-clint'];
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
restart: { enabled: false, maxRetries: 0, delay: 0 },
|
|
47
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/ph-clint-cli",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -22,12 +22,11 @@
|
|
|
22
22
|
"lint": "eslint src",
|
|
23
23
|
"mastra:dev": "mastra dev",
|
|
24
24
|
"mastra:build": "mastra build",
|
|
25
|
-
"mastra:start": "mastra start"
|
|
26
|
-
"publish:npm": "pnpm build && pnpm publish --no-git-checks"
|
|
25
|
+
"mastra:start": "mastra start"
|
|
27
26
|
},
|
|
28
27
|
"dependencies": {
|
|
29
|
-
"@powerhousedao/ph-clint": "^0.1.0-dev.
|
|
30
|
-
"@powerhousedao/ph-clint-app": "^0.1.0-dev.
|
|
28
|
+
"@powerhousedao/ph-clint": "^0.1.0-dev.3",
|
|
29
|
+
"@powerhousedao/ph-clint-app": "^0.1.0-dev.3",
|
|
31
30
|
"@mastra/core": "^1.22.0",
|
|
32
31
|
"@mastra/libsql": "^1.7.4",
|
|
33
32
|
"@mastra/memory": "^1.13.1",
|
|
@@ -49,7 +48,7 @@
|
|
|
49
48
|
"@jest/globals": "^30.2.0",
|
|
50
49
|
"@types/node": "^25.5.2",
|
|
51
50
|
"jest": "^30.2.0",
|
|
52
|
-
"@powerhousedao/ph-clint-dev": "^0.1.0-dev.
|
|
51
|
+
"@powerhousedao/ph-clint-dev": "^0.1.0-dev.3",
|
|
53
52
|
"ts-jest": "^29.4.6",
|
|
54
53
|
"tsx": "^4.19.0",
|
|
55
54
|
"typescript": "^6.0.2"
|