@powerhousedao/ph-clint-cli 0.1.0-dev.1 → 0.1.0-dev.100

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 (110) hide show
  1. package/dist/cli.js +60 -17
  2. package/dist/codegen/index.d.ts +8 -36
  3. package/dist/codegen/index.js +6 -287
  4. package/dist/commands/clint-project-build.d.ts +83 -0
  5. package/dist/commands/clint-project-build.js +34 -0
  6. package/dist/commands/clint-project-init.d.ts +177 -0
  7. package/dist/commands/{init.js → clint-project-init.js} +75 -50
  8. package/dist/commands/clint-project-publish.d.ts +92 -0
  9. package/dist/commands/clint-project-publish.js +73 -0
  10. package/dist/commands/clint-project-regen.d.ts +94 -0
  11. package/dist/commands/{regen.js → clint-project-regen.js} +29 -5
  12. package/dist/commands/clint-skills-sync.d.ts +86 -0
  13. package/dist/commands/clint-skills-sync.js +56 -0
  14. package/dist/config.d.ts +3 -5
  15. package/dist/config.js +5 -7
  16. package/dist/framework.d.ts +322 -60
  17. package/dist/framework.gen.d.ts +50 -10
  18. package/dist/framework.gen.js +1 -1
  19. package/dist/framework.js +1 -5
  20. package/{prompts/agent-profiles/AgentBase.md → dist/gen/agent-profiles/PhClintDevAgent.md} +1 -1
  21. package/dist/gen/skills/cli-setup/.cli-docs.md +135 -0
  22. package/dist/gen/skills/cli-setup/SKILL.md +125 -0
  23. package/dist/gen/skills/cli-setup/references/00.assess-cli-needs.md +53 -0
  24. package/dist/gen/skills/cli-setup/references/01.define-config-schema.md +78 -0
  25. package/dist/gen/skills/cli-setup/references/02.wire-cli.md +141 -0
  26. package/dist/gen/skills/cli-setup/references/03.add-entrypoint.md +128 -0
  27. package/dist/gen/skills/cli-setup/references/expected-outcome.md +3 -0
  28. package/dist/gen/skills/command-definition/.cli-docs.md +84 -0
  29. package/dist/gen/skills/command-definition/SKILL.md +99 -0
  30. package/dist/gen/skills/command-definition/references/00.assess-requirements.md +58 -0
  31. package/dist/gen/skills/command-definition/references/01.define-schema.md +72 -0
  32. package/dist/gen/skills/command-definition/references/02.implement-execute.md +118 -0
  33. package/dist/gen/skills/command-definition/references/03.add-prompting.md +54 -0
  34. package/dist/gen/skills/command-definition/references/expected-outcome.md +3 -0
  35. package/dist/gen/skills/service-definition/.cli-docs.md +214 -0
  36. package/dist/gen/skills/service-definition/SKILL.md +148 -0
  37. package/dist/gen/skills/service-definition/references/00.assess-service.md +69 -0
  38. package/dist/gen/skills/service-definition/references/01.define-service.md +166 -0
  39. package/dist/gen/skills/service-definition/references/02.add-preflight.md +132 -0
  40. package/dist/gen/skills/service-definition/references/03.add-project-scanner.md +82 -0
  41. package/dist/gen/skills/service-definition/references/04.wire-events.md +120 -0
  42. package/dist/gen/skills/service-definition/references/expected-outcome.md +3 -0
  43. package/dist/main.js +0 -0
  44. package/dist/services/clint-project.d.ts +1 -0
  45. package/dist/services/clint-project.js +68 -0
  46. package/dist/spec/ensure-document.d.ts +17 -0
  47. package/dist/spec/ensure-document.js +56 -0
  48. package/dist/spec/file.d.ts +1 -4
  49. package/dist/spec/file.js +1 -34
  50. package/dist/spec/from-document.d.ts +4 -1
  51. package/dist/spec/from-document.js +4 -16
  52. package/dist/spec/types.d.ts +1 -62
  53. package/dist/spec/types.js +1 -112
  54. package/dist/triggers/publish-trigger.d.ts +81 -0
  55. package/dist/triggers/publish-trigger.js +137 -0
  56. package/dist/triggers/spec-change.d.ts +60 -18
  57. package/dist/triggers/spec-change.js +154 -101
  58. package/package.json +26 -32
  59. package/dist/codegen/builders/agent-base-md.d.ts +0 -5
  60. package/dist/codegen/builders/agent-base-md.js +0 -18
  61. package/dist/codegen/builders/agent-ts.d.ts +0 -9
  62. package/dist/codegen/builders/agent-ts.js +0 -34
  63. package/dist/codegen/builders/app-index-ts.d.ts +0 -18
  64. package/dist/codegen/builders/app-index-ts.js +0 -33
  65. package/dist/codegen/builders/build-skills-ts.d.ts +0 -4
  66. package/dist/codegen/builders/build-skills-ts.js +0 -37
  67. package/dist/codegen/builders/cli-package-json.d.ts +0 -8
  68. package/dist/codegen/builders/cli-package-json.js +0 -69
  69. package/dist/codegen/builders/cli-ts.d.ts +0 -7
  70. package/dist/codegen/builders/cli-ts.js +0 -110
  71. package/dist/codegen/builders/config-ts.d.ts +0 -8
  72. package/dist/codegen/builders/config-ts.js +0 -21
  73. package/dist/codegen/builders/eslint-config-js.d.ts +0 -4
  74. package/dist/codegen/builders/eslint-config-js.js +0 -13
  75. package/dist/codegen/builders/framework-gen-ts.d.ts +0 -20
  76. package/dist/codegen/builders/framework-gen-ts.js +0 -62
  77. package/dist/codegen/builders/framework-ts.d.ts +0 -21
  78. package/dist/codegen/builders/framework-ts.js +0 -74
  79. package/dist/codegen/builders/gitignore.d.ts +0 -4
  80. package/dist/codegen/builders/gitignore.js +0 -18
  81. package/dist/codegen/builders/index.d.ts +0 -43
  82. package/dist/codegen/builders/index.js +0 -60
  83. package/dist/codegen/builders/jest-config-js.d.ts +0 -4
  84. package/dist/codegen/builders/jest-config-js.js +0 -19
  85. package/dist/codegen/builders/main-ts.d.ts +0 -5
  86. package/dist/codegen/builders/main-ts.js +0 -13
  87. package/dist/codegen/builders/mastra-index-ts.d.ts +0 -8
  88. package/dist/codegen/builders/mastra-index-ts.js +0 -30
  89. package/dist/codegen/builders/readme-md.d.ts +0 -5
  90. package/dist/codegen/builders/readme-md.js +0 -44
  91. package/dist/codegen/builders/root-package-json.d.ts +0 -7
  92. package/dist/codegen/builders/root-package-json.js +0 -27
  93. package/dist/codegen/builders/tsconfig-json.d.ts +0 -4
  94. package/dist/codegen/builders/tsconfig-json.js +0 -22
  95. package/dist/codegen/exec.d.ts +0 -20
  96. package/dist/codegen/exec.js +0 -41
  97. package/dist/codegen/hashes.d.ts +0 -6
  98. package/dist/codegen/hashes.js +0 -60
  99. package/dist/codegen/markers.d.ts +0 -36
  100. package/dist/codegen/markers.js +0 -86
  101. package/dist/codegen/migrate/flat-to-split.d.ts +0 -15
  102. package/dist/codegen/migrate/flat-to-split.js +0 -119
  103. package/dist/codegen/scaffold.d.ts +0 -34
  104. package/dist/codegen/scaffold.js +0 -60
  105. package/dist/codegen/write.d.ts +0 -6
  106. package/dist/codegen/write.js +0 -32
  107. package/dist/commands/init.d.ts +0 -57
  108. package/dist/commands/regen.d.ts +0 -18
  109. package/prompts/skills-ext/.gitkeep +0 -0
  110. package/prompts/skills-tpl/.gitkeep +0 -0
package/dist/cli.js CHANGED
@@ -7,40 +7,81 @@
7
7
  * Everything outside the markers is user-editable and preserved.
8
8
  */
9
9
  import path from 'node:path';
10
- import { fileURLToPath } from 'node:url';
11
- import { defineCli, buildDefaultReactor } from '@powerhousedao/ph-clint';
12
- import { CLI_NAME, CLI_VERSION } from './config.js';
10
+ import { defineCli, buildDefaultReactor, deterministicId } from '@powerhousedao/ph-clint';
11
+ import { z } from 'zod';
12
+ import { CLI_NAME, CLI_VERSION, CLI_ROOT } from './config.js';
13
13
  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';
22
+ import { skillsSync } from './commands/clint-skills-sync.js';
19
23
  import { specChangeTrigger } from './triggers/spec-change.js';
24
+ import { publishTrigger } from './triggers/publish-trigger.js';
20
25
  // @clint:end imports
21
- // Connect (ph connect) must run inside the Reactor Package (ph-clint-app).
22
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
23
- const appDir = path.resolve(__dirname, '../../ph-clint-app');
24
26
  export const cli = defineCli({
25
27
  name: CLI_NAME,
26
28
  version: CLI_VERSION,
29
+ root: CLI_ROOT,
27
30
  description: `${CLI_NAME} v${CLI_VERSION} — scaffold and maintain ph-clint implementation projects`,
28
31
  configSchema,
29
32
  secretsSchema,
30
33
  // @clint:begin commands
31
- commands: [init, regen],
34
+ commands: [init, regen, build, clintProjectPublish, skillsSync],
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
- triggers: [specChangeTrigger],
40
+ triggers: [specChangeTrigger, publishTrigger],
41
+ routine: { id: 'triggers', name: 'Triggers' },
38
42
  // @clint:end triggers
39
43
  // @clint:begin prompts
40
44
  prompts: {
41
- sources: [],
42
- agents: {},
43
- skills: {},
45
+ artifacts: [path.join(CLI_ROOT, 'gen', 'skills'), path.join(CLI_ROOT, 'dist', 'gen', 'skills')],
46
+ agents: {
47
+ 'ph-clint-dev-agent': {
48
+ name: 'PhClintDevAgent',
49
+ sections: ['AgentBase.md'],
50
+ skills: ['cli-setup', 'command-definition', 'service-definition'],
51
+ },
52
+ },
53
+ skills: {
54
+ 'cli-setup': {
55
+ description: 'Set up a new CLI with defineCli, config schemas, entrypoint, and interactive mode',
56
+ inputSchema: z.object({
57
+ mode: z
58
+ .enum(['expert', 'discovery', 'one-shot'])
59
+ .default('expert')
60
+ .describe('Expert: align design decisions with the developer. Discovery: explain concepts step by step. One-shot: make all decisions autonomously'),
61
+ }),
62
+ instructionTemplate: 'Use your {{skillId}} skill in {{mode}} mode for: {{prompt}}',
63
+ },
64
+ 'command-definition': {
65
+ description: 'Define commands with Zod schemas, execute functions, return values, and parameter prompting',
66
+ inputSchema: z.object({
67
+ mode: z
68
+ .enum(['expert', 'discovery', 'one-shot'])
69
+ .default('expert')
70
+ .describe('Expert: align design decisions with the developer. Discovery: explain concepts step by step. One-shot: make all decisions autonomously'),
71
+ }),
72
+ instructionTemplate: 'Use your {{skillId}} skill in {{mode}} mode for: {{prompt}}',
73
+ },
74
+ 'service-definition': {
75
+ description: 'Define background services with readiness detection, preflight checks, and project scanning',
76
+ inputSchema: z.object({
77
+ mode: z
78
+ .enum(['expert', 'discovery', 'one-shot'])
79
+ .default('expert')
80
+ .describe('Expert: align design decisions with the developer. Discovery: explain concepts step by step. One-shot: make all decisions autonomously'),
81
+ }),
82
+ instructionTemplate: 'Use your {{skillId}} skill in {{mode}} mode for: {{prompt}}',
83
+ },
84
+ },
44
85
  },
45
86
  // @clint:end prompts
46
87
  // @clint:begin events
@@ -66,12 +107,14 @@ export const cli = defineCli({
66
107
  // @clint:begin reactor
67
108
  cli.configureReactor({
68
109
  create: (ctx) => buildDefaultReactor(ctx, {
69
- documentModels,
70
- drive: { name: 'Clint' },
110
+ documentModels: [...documentModels],
111
+ drives: [
112
+ { name: 'Clint Folders', role: 'personal', id: deterministicId(CLI_NAME, 'personal-drive') },
113
+ ],
71
114
  subscriptions: { documentTypes: ['powerhouse/ph-clint-project'] },
72
115
  }),
73
116
  switchboard: { enabled: true },
74
- connect: { enabled: true, workdir: appDir },
117
+ connect: { enabled: true },
75
118
  });
76
119
  // @clint:end reactor
77
120
  // @clint:begin mastra
@@ -1,39 +1,11 @@
1
- import { type ClintProjectSpec } from '../spec/types.js';
2
- export type GenerateMode = 'create' | 'update' | 'auto';
3
- export interface GenerateProjectOptions {
4
- /** Absolute path to the project root. */
5
- targetDir: string;
6
- spec: ClintProjectSpec;
7
- /** Defaults to `'auto'` — create if no persisted spec, else update. */
8
- mode?: GenerateMode;
9
- /** Create mode: allow writing into a non-empty directory. */
10
- allowNonEmpty?: boolean;
11
- /**
12
- * Update mode: overwrite user-edited files (hash-mismatch) anyway.
13
- * Also relaxes the git-dirty guard on flat → split migration.
14
- */
15
- force?: boolean;
16
- /** Optional warning sink (diagnostics, not errors). */
17
- onWarn?: (msg: string) => void;
18
- }
19
- export interface GeneratedFile {
20
- absolutePath: string;
21
- relativePath: string;
22
- }
23
- export interface GenerateProjectResult {
24
- mode: 'create' | 'update';
25
- /** Files newly written or rewritten. */
26
- files: GeneratedFile[];
27
- /** Files skipped because the user had edited them. */
28
- skipped: GeneratedFile[];
29
- /** Files removed because the new spec no longer emits them. */
30
- deleted: GeneratedFile[];
31
- /** True if a flat → split migration ran. */
32
- migrated: boolean;
33
- cliDir: string;
34
- appDir: string | null;
35
- }
36
1
  /**
37
- * Generate / reconcile a project at `targetDir` from the given spec.
2
+ * Re-exports from @powerhousedao/ph-clint-dev/codegen with CLI_VERSION default.
38
3
  */
4
+ import { type GenerateProjectOptions as DevGenerateProjectOptions, type GenerateProjectResult, type GenerateMode, type GeneratedFile } from '@powerhousedao/ph-clint-dev/codegen';
5
+ import type { CodegenContext } from '@powerhousedao/ph-clint-dev/codegen/types';
6
+ export type { GenerateProjectResult, GenerateMode, GeneratedFile, CodegenContext };
7
+ export interface GenerateProjectOptions extends Omit<DevGenerateProjectOptions, 'context'> {
8
+ /** Codegen context; defaults to CLI_VERSION when omitted. */
9
+ context?: CodegenContext;
10
+ }
39
11
  export declare function generateProject(options: GenerateProjectOptions): Promise<GenerateProjectResult>;
@@ -1,292 +1,11 @@
1
1
  /**
2
- * Code generator entry point.
3
- *
4
- * Takes a `ClintProjectSpec` + target directory and reconciles the on-disk
5
- * project tree with it.
6
- *
7
- * Two modes:
8
- *
9
- * - **create** — the target dir is empty (or only contains ignorable
10
- * artefacts). Every templated file is written fresh. Refuses to
11
- * overwrite existing non-empty state unless `allowNonEmpty`.
12
- *
13
- * - **update** — a previous run's spec exists at
14
- * `.ph/ph-clint-cli/project-spec.json`. Re-runs the builders, then for
15
- * each emitted file:
16
- * - files containing `@clint:begin/@clint:end` markers (today: just
17
- * `src/cli.ts`) have their marker regions spliced in-place, leaving
18
- * user code outside markers untouched;
19
- * - files without markers are overwritten iff their on-disk hash still
20
- * matches the hash stored from the previous write (user-edited files
21
- * are skipped and surfaced via `onWarn`);
22
- * - files the new spec no longer emits are removed if their hash still
23
- * matches.
24
- *
25
- * A spec flip of `features.powerhouse.enabled` from `false` to `true`
26
- * triggers the flat → split migration before the update flow runs.
27
- *
28
- * Mode is auto-detected by the presence of the persisted spec.
29
- */
30
- import fs from 'node:fs/promises';
31
- import path from 'node:path';
32
- import { getAppFolderName, getCliFolderName, } from '../spec/types.js';
33
- import { getSpecPath, readProjectSpec, writeProjectSpec, } from '../spec/file.js';
34
- import { hashContent, hashFile, readHashes, writeHashes, } from './hashes.js';
35
- import { hasMarkers, spliceMarkerRegions } from './markers.js';
36
- import { writeFileEnsuringDir, isDirEmptyEnough } from './write.js';
37
- import { migrateFlatToSplit } from './migrate/flat-to-split.js';
38
- import { CLI_FILE_BUILDERS, buildReadme, buildRootPackageJson, buildAppIndexTs, } from './builders/index.js';
39
- /**
40
- * Generate / reconcile a project at `targetDir` from the given spec.
2
+ * Re-exports from @powerhousedao/ph-clint-dev/codegen with CLI_VERSION default.
41
3
  */
4
+ import { generateProject as _generateProject, } from '@powerhousedao/ph-clint-dev/codegen';
5
+ import { CLI_VERSION } from '../config.js';
42
6
  export async function generateProject(options) {
43
- const { targetDir, spec } = options;
44
- const warn = options.onWarn ?? (() => { });
45
- const existingSpec = await readProjectSpec(targetDir);
46
- const resolvedMode = resolveMode(options.mode ?? 'auto', existingSpec !== null);
47
- // Flat → split migration, if needed. Runs BEFORE we compute planned files
48
- // so the hash record and on-disk state are already in split-layout shape
49
- // by the time the normal reconciliation runs.
50
- let migrated = false;
51
- if (resolvedMode === 'update' &&
52
- existingSpec &&
53
- !existingSpec.features.powerhouse.enabled &&
54
- spec.features.powerhouse.enabled) {
55
- await migrateFlatToSplit({
56
- targetDir,
57
- spec,
58
- force: options.force,
59
- onWarn: warn,
60
- });
61
- migrated = true;
62
- }
63
- // Dispatch to the mode-specific flow.
64
- if (resolvedMode === 'create') {
65
- return runCreate(options, warn);
66
- }
67
- return runUpdate(options, warn, migrated);
68
- }
69
- function resolveMode(requested, specExists) {
70
- if (requested === 'create')
71
- return 'create';
72
- if (requested === 'update')
73
- return 'update';
74
- return specExists ? 'update' : 'create';
75
- }
76
- /** Build the list of files the new spec would emit, with their content. */
77
- function planFiles(spec, targetDir) {
78
- const split = spec.features.powerhouse.enabled;
79
- const cliDir = split ? path.join(targetDir, getCliFolderName(spec)) : targetDir;
80
- const appDir = split ? path.join(targetDir, getAppFolderName(spec)) : null;
81
- const planned = [];
82
- for (const builder of CLI_FILE_BUILDERS) {
83
- const content = builder.build(spec);
84
- if (content === null)
85
- continue;
86
- const abs = path.join(cliDir, builder.relativePath);
87
- planned.push({
88
- relativePath: path.relative(targetDir, abs),
89
- absolutePath: abs,
90
- content,
91
- initOnly: builder.initOnly,
92
- });
93
- }
94
- // Project-root README — always at the top level.
95
- const readmeAbs = path.join(targetDir, 'README.md');
96
- planned.push({
97
- relativePath: 'README.md',
98
- absolutePath: readmeAbs,
99
- content: buildReadme(spec),
7
+ return _generateProject({
8
+ ...options,
9
+ context: options.context ?? { toolVersion: CLI_VERSION },
100
10
  });
101
- // Split-layout only: root package.json + app placeholder files.
102
- if (split && appDir) {
103
- const rootPkg = path.join(targetDir, 'package.json');
104
- planned.push({
105
- relativePath: 'package.json',
106
- absolutePath: rootPkg,
107
- content: buildRootPackageJson(spec),
108
- });
109
- const appGitkeep = path.join(appDir, '.gitkeep');
110
- planned.push({
111
- relativePath: path.relative(targetDir, appGitkeep),
112
- absolutePath: appGitkeep,
113
- content: '',
114
- });
115
- const appReadme = path.join(appDir, 'README.md');
116
- planned.push({
117
- relativePath: path.relative(targetDir, appReadme),
118
- absolutePath: appReadme,
119
- content: buildAppReadmeContent(spec),
120
- });
121
- // Top-level barrel for the reactor package — lets impl code import from
122
- // `{name}-app` instead of drilling into `document-models/<slug>/v1/gen`.
123
- const appIndex = buildAppIndexTs(spec);
124
- if (appIndex !== null) {
125
- const appIndexAbs = path.join(appDir, 'index.ts');
126
- planned.push({
127
- relativePath: path.relative(targetDir, appIndexAbs),
128
- absolutePath: appIndexAbs,
129
- content: appIndex,
130
- });
131
- }
132
- }
133
- return { planned, cliDir, appDir };
134
- }
135
- function buildAppReadmeContent(spec) {
136
- return [
137
- `# ${spec.name}-app`,
138
- '',
139
- 'Powerhouse reactor-package for this project. Scaffold it with:',
140
- '',
141
- '```sh',
142
- `cd ${spec.name}-app`,
143
- 'ph init',
144
- '```',
145
- '',
146
- 'Then run `pnpm install` at the project root.',
147
- '',
148
- ].join('\n');
149
- }
150
- /** Create mode — empty target dir, write everything fresh. */
151
- async function runCreate(options, _warn) {
152
- const { targetDir, spec } = options;
153
- if (!options.allowNonEmpty) {
154
- const empty = await isDirEmptyEnough(targetDir);
155
- if (!empty) {
156
- throw new Error(`target directory ${targetDir} is not empty (pass allowNonEmpty or --force to override)`);
157
- }
158
- }
159
- const { planned, cliDir, appDir } = planFiles(spec, targetDir);
160
- const hashes = {};
161
- const files = [];
162
- for (const p of planned) {
163
- await writeFileEnsuringDir(p.absolutePath, p.content);
164
- hashes[p.relativePath] = hashContent(p.content);
165
- files.push({ absolutePath: p.absolutePath, relativePath: p.relativePath });
166
- }
167
- await writeProjectSpec(targetDir, spec);
168
- files.push({
169
- absolutePath: getSpecPath(targetDir),
170
- relativePath: path.relative(targetDir, getSpecPath(targetDir)),
171
- });
172
- await writeHashes(targetDir, hashes);
173
- return {
174
- mode: 'create',
175
- files,
176
- skipped: [],
177
- deleted: [],
178
- migrated: false,
179
- cliDir,
180
- appDir,
181
- };
182
- }
183
- /** Update mode — reconcile against an existing project. */
184
- async function runUpdate(options, warn, migrated) {
185
- const { targetDir, spec } = options;
186
- const force = !!options.force;
187
- const { planned, cliDir, appDir } = planFiles(spec, targetDir);
188
- const plannedByKey = new Map(planned.map((p) => [p.relativePath, p]));
189
- const previous = await readHashes(targetDir);
190
- const next = {};
191
- const files = [];
192
- const skipped = [];
193
- const deleted = [];
194
- // Step 1: write / patch / skip each planned file.
195
- for (const p of planned) {
196
- if (p.initOnly) {
197
- // User-owned files (e.g. `src/framework.ts` with its `configSchema`)
198
- // are emitted only on create. In update mode we never overwrite them
199
- // and never re-create them if missing — the user may have deliberately
200
- // deleted or inlined them. Keep whatever hash the previous run stored
201
- // so the deletion pass (step 2) still considers them "ours".
202
- const stored = previous[p.relativePath];
203
- if (stored !== undefined)
204
- next[p.relativePath] = stored;
205
- continue;
206
- }
207
- const existing = await hashFile(p.absolutePath);
208
- if (existing === null) {
209
- // Not yet on disk — fresh write.
210
- await writeFileEnsuringDir(p.absolutePath, p.content);
211
- next[p.relativePath] = hashContent(p.content);
212
- files.push({
213
- absolutePath: p.absolutePath,
214
- relativePath: p.relativePath,
215
- });
216
- continue;
217
- }
218
- // File exists. Decide how to update it.
219
- if (hasMarkers(p.content)) {
220
- const existingContent = await fs.readFile(p.absolutePath, 'utf8');
221
- const patched = hasMarkers(existingContent)
222
- ? spliceMarkerRegions(existingContent, p.content)
223
- : p.content;
224
- if (patched === existingContent) {
225
- // No-op: regions already match.
226
- next[p.relativePath] = hashContent(patched);
227
- continue;
228
- }
229
- await writeFileEnsuringDir(p.absolutePath, patched);
230
- next[p.relativePath] = hashContent(patched);
231
- files.push({
232
- absolutePath: p.absolutePath,
233
- relativePath: p.relativePath,
234
- });
235
- continue;
236
- }
237
- // No markers — hash-protected overwrite.
238
- const stored = previous[p.relativePath];
239
- const pristine = stored !== undefined && stored === existing;
240
- if (!pristine && !force) {
241
- // User edited a managed file. Leave it alone.
242
- warn(`skipped ${p.relativePath} — modified since last generation. Use --force to overwrite.`);
243
- skipped.push({
244
- absolutePath: p.absolutePath,
245
- relativePath: p.relativePath,
246
- });
247
- // Preserve the previous hash so the file stays on our ledger.
248
- if (stored !== undefined)
249
- next[p.relativePath] = stored;
250
- continue;
251
- }
252
- if (existing === hashContent(p.content)) {
253
- // Already up to date.
254
- next[p.relativePath] = existing;
255
- continue;
256
- }
257
- await writeFileEnsuringDir(p.absolutePath, p.content);
258
- next[p.relativePath] = hashContent(p.content);
259
- files.push({
260
- absolutePath: p.absolutePath,
261
- relativePath: p.relativePath,
262
- });
263
- }
264
- // Step 2: delete files that the new spec no longer emits, IF we still
265
- // recognise their content (hash match). Otherwise warn and keep.
266
- for (const [relPath, storedHash] of Object.entries(previous)) {
267
- if (plannedByKey.has(relPath))
268
- continue;
269
- const abs = path.join(targetDir, relPath);
270
- const onDisk = await hashFile(abs);
271
- if (onDisk === null)
272
- continue; // already gone
273
- if (onDisk !== storedHash && !force) {
274
- warn(`kept ${relPath} — modified since last generation; new spec no longer emits it.`);
275
- next[relPath] = storedHash;
276
- continue;
277
- }
278
- await fs.rm(abs, { force: true });
279
- deleted.push({ absolutePath: abs, relativePath: relPath });
280
- }
281
- await writeProjectSpec(targetDir, spec);
282
- await writeHashes(targetDir, next);
283
- return {
284
- mode: 'update',
285
- files,
286
- skipped,
287
- deleted,
288
- migrated,
289
- cliDir,
290
- appDir,
291
- };
292
11
  }
@@ -0,0 +1,83 @@
1
+ import { z } from 'zod';
2
+ export declare const build: import("@powerhousedao/ph-clint").Command<z.ZodObject<{
3
+ dir: z.ZodOptional<z.ZodString>;
4
+ verbose: z.ZodDefault<z.ZodBoolean>;
5
+ }, z.core.$strip>, {
6
+ text: string;
7
+ }, {
8
+ model: string;
9
+ devServicePort: number;
10
+ phVersion: string;
11
+ apiKey?: string | undefined;
12
+ }, import("@powerhousedao/ph-clint").InferRegistry<readonly [{
13
+ readonly version: 1;
14
+ readonly reducer: import("document-model").Reducer<import("@powerhousedao/ph-clint-app").PhClintProjectPHState>;
15
+ readonly actions: {
16
+ setAgentModel: (input: import("@powerhousedao/ph-clint-app").SetAgentModelInput) => import("@powerhousedao/ph-clint-app").SetAgentModelAction;
17
+ addAgentProfileRef: (input: import("@powerhousedao/ph-clint-app").AddAgentProfileRefInput) => import("@powerhousedao/ph-clint-app").AddAgentProfileRefAction;
18
+ removeAgentProfileRef: (input: import("@powerhousedao/ph-clint-app").RemoveAgentProfileRefInput) => import("@powerhousedao/ph-clint-app").RemoveAgentProfileRefAction;
19
+ reorderAgentProfileRefs: (input: import("@powerhousedao/ph-clint-app").ReorderAgentProfileRefsInput) => import("@powerhousedao/ph-clint-app").ReorderAgentProfileRefsAction;
20
+ addAgentSkill: (input: import("@powerhousedao/ph-clint-app").AddAgentSkillInput) => import("@powerhousedao/ph-clint-app").AddAgentSkillAction;
21
+ removeAgentSkill: (input: import("@powerhousedao/ph-clint-app").RemoveAgentSkillInput) => import("@powerhousedao/ph-clint-app").RemoveAgentSkillAction;
22
+ addAgentToolPattern: (input: import("@powerhousedao/ph-clint-app").AddAgentToolPatternInput) => import("@powerhousedao/ph-clint-app").AddAgentToolPatternAction;
23
+ removeAgentToolPattern: (input: import("@powerhousedao/ph-clint-app").RemoveAgentToolPatternInput) => import("@powerhousedao/ph-clint-app").RemoveAgentToolPatternAction;
24
+ addSubAgent: (input: import("@powerhousedao/ph-clint-app").AddSubAgentInput) => import("@powerhousedao/ph-clint-app").AddSubAgentAction;
25
+ removeSubAgent: (input: import("@powerhousedao/ph-clint-app").RemoveSubAgentInput) => import("@powerhousedao/ph-clint-app").RemoveSubAgentAction;
26
+ setSubAgentName: (input: import("@powerhousedao/ph-clint-app").SetSubAgentNameInput) => import("@powerhousedao/ph-clint-app").SetSubAgentNameAction;
27
+ setSubAgentDescription: (input: import("@powerhousedao/ph-clint-app").SetSubAgentDescriptionInput) => import("@powerhousedao/ph-clint-app").SetSubAgentDescriptionAction;
28
+ setMainAgentName: (input: import("@powerhousedao/ph-clint-app").SetMainAgentNameInput) => import("@powerhousedao/ph-clint-app").SetMainAgentNameAction;
29
+ setMainAgentDescription: (input: import("@powerhousedao/ph-clint-app").SetMainAgentDescriptionInput) => import("@powerhousedao/ph-clint-app").SetMainAgentDescriptionAction;
30
+ clearMainAgentDescription: (input: import("@powerhousedao/ph-clint-app").ClearMainAgentDescriptionInput) => import("@powerhousedao/ph-clint-app").ClearMainAgentDescriptionAction;
31
+ setMainAgentImage: (input: import("@powerhousedao/ph-clint-app").SetMainAgentImageInput) => import("@powerhousedao/ph-clint-app").SetMainAgentImageAction;
32
+ clearMainAgentImage: (input: import("@powerhousedao/ph-clint-app").ClearMainAgentImageInput) => import("@powerhousedao/ph-clint-app").ClearMainAgentImageAction;
33
+ addProfile: (input: import("@powerhousedao/ph-clint-app").AddProfileInput) => import("@powerhousedao/ph-clint-app").AddProfileAction;
34
+ updateProfile: (input: import("@powerhousedao/ph-clint-app").UpdateProfileInput) => import("@powerhousedao/ph-clint-app").UpdateProfileAction;
35
+ removeProfile: (input: import("@powerhousedao/ph-clint-app").RemoveProfileInput) => import("@powerhousedao/ph-clint-app").RemoveProfileAction;
36
+ reorderProfiles: (input: import("@powerhousedao/ph-clint-app").ReorderProfilesInput) => import("@powerhousedao/ph-clint-app").ReorderProfilesAction;
37
+ addModel: (input: import("@powerhousedao/ph-clint-app").AddModelInput) => import("@powerhousedao/ph-clint-app").AddModelAction;
38
+ removeModel: (input: import("@powerhousedao/ph-clint-app").RemoveModelInput) => import("@powerhousedao/ph-clint-app").RemoveModelAction;
39
+ enableMastra: (input: import("@powerhousedao/ph-clint-app").EnableMastraInput) => import("@powerhousedao/ph-clint-app").EnableMastraAction;
40
+ disableMastra: (input: import("@powerhousedao/ph-clint-app").DisableMastraInput) => import("@powerhousedao/ph-clint-app").DisableMastraAction;
41
+ setEnableChat: (input: import("@powerhousedao/ph-clint-app").SetEnableChatInput) => import("@powerhousedao/ph-clint-app").SetEnableChatAction;
42
+ addSupportedResource: (input: import("@powerhousedao/ph-clint-app").AddSupportedResourceInput) => import("@powerhousedao/ph-clint-app").AddSupportedResourceAction;
43
+ removeSupportedResource: (input: import("@powerhousedao/ph-clint-app").RemoveSupportedResourceInput) => import("@powerhousedao/ph-clint-app").RemoveSupportedResourceAction;
44
+ setProxyEnabled: (input: import("@powerhousedao/ph-clint-app").SetProxyEnabledInput) => import("@powerhousedao/ph-clint-app").SetProxyEnabledAction;
45
+ setObservabilityEnabled: (input: import("@powerhousedao/ph-clint-app").SetObservabilityEnabledInput) => import("@powerhousedao/ph-clint-app").SetObservabilityEnabledAction;
46
+ importSpec: (input: import("@powerhousedao/ph-clint-app").ImportSpecInput) => import("@powerhousedao/ph-clint-app").ImportSpecAction;
47
+ bumpVersion: (input: import("@powerhousedao/ph-clint-app").BumpVersionInput) => import("@powerhousedao/ph-clint-app").BumpVersionAction;
48
+ publishDev: (input: import("@powerhousedao/ph-clint-app").PublishDevInput) => import("@powerhousedao/ph-clint-app").PublishDevAction;
49
+ publishStaging: (input: import("@powerhousedao/ph-clint-app").PublishStagingInput) => import("@powerhousedao/ph-clint-app").PublishStagingAction;
50
+ publishProduction: (input: import("@powerhousedao/ph-clint-app").PublishProductionInput) => import("@powerhousedao/ph-clint-app").PublishProductionAction;
51
+ setPublishStatus: (input: import("@powerhousedao/ph-clint-app").SetPublishStatusInput) => import("@powerhousedao/ph-clint-app").SetPublishStatusAction;
52
+ addExternalSkill: (input: import("@powerhousedao/ph-clint-app").AddExternalSkillInput) => import("@powerhousedao/ph-clint-app").AddExternalSkillAction;
53
+ removeExternalSkill: (input: import("@powerhousedao/ph-clint-app").RemoveExternalSkillInput) => import("@powerhousedao/ph-clint-app").RemoveExternalSkillAction;
54
+ setExternalSkillName: (input: import("@powerhousedao/ph-clint-app").SetExternalSkillNameInput) => import("@powerhousedao/ph-clint-app").SetExternalSkillNameAction;
55
+ setExternalSkillGithubUrl: (input: import("@powerhousedao/ph-clint-app").SetExternalSkillGithubUrlInput) => import("@powerhousedao/ph-clint-app").SetExternalSkillGithubUrlAction;
56
+ addPowerhousePackage: (input: import("@powerhousedao/ph-clint-app").AddPowerhousePackageInput) => import("@powerhousedao/ph-clint-app").AddPowerhousePackageAction;
57
+ removePowerhousePackage: (input: import("@powerhousedao/ph-clint-app").RemovePowerhousePackageInput) => import("@powerhousedao/ph-clint-app").RemovePowerhousePackageAction;
58
+ addPackageDocumentType: (input: import("@powerhousedao/ph-clint-app").AddPackageDocumentTypeInput) => import("@powerhousedao/ph-clint-app").AddPackageDocumentTypeAction;
59
+ removePackageDocumentType: (input: import("@powerhousedao/ph-clint-app").RemovePackageDocumentTypeInput) => import("@powerhousedao/ph-clint-app").RemovePackageDocumentTypeAction;
60
+ setPackageVersion: (input: import("@powerhousedao/ph-clint-app").SetPackageVersionInput) => import("@powerhousedao/ph-clint-app").SetPackageVersionAction;
61
+ enableRoutine: (input: import("@powerhousedao/ph-clint-app").EnableRoutineInput) => import("@powerhousedao/ph-clint-app").EnableRoutineAction;
62
+ disableRoutine: (input: import("@powerhousedao/ph-clint-app").DisableRoutineInput) => import("@powerhousedao/ph-clint-app").DisableRoutineAction;
63
+ setPowerhouseLevel: (input: import("@powerhousedao/ph-clint-app").SetPowerhouseLevelInput) => import("@powerhousedao/ph-clint-app").SetPowerhouseLevelAction;
64
+ setDescription: (input: import("@powerhousedao/ph-clint-app").SetDescriptionInput) => import("@powerhousedao/ph-clint-app").SetDescriptionAction;
65
+ setVersion: (input: import("@powerhousedao/ph-clint-app").SetVersionInput) => import("@powerhousedao/ph-clint-app").SetVersionAction;
66
+ setPackageIdentifier: (input: import("@powerhousedao/ph-clint-app").SetPackageIdentifierInput) => import("@powerhousedao/ph-clint-app").SetPackageIdentifierAction;
67
+ setName: (name: string | {
68
+ name: string;
69
+ }) => import("document-model").SetNameAction;
70
+ setPreferredEditor: (input: string | null | {
71
+ preferredEditor: string | null;
72
+ }) => import("document-model").SetPreferredEditorAction;
73
+ undo: (count?: number, scope?: string) => import("document-model").UndoAction;
74
+ redo: (count?: number, scope?: string) => import("document-model").RedoAction;
75
+ prune: (start?: number, end?: number, scope?: string) => import("document-model").SchemaPruneAction;
76
+ loadState: <TState extends import("document-model").PHBaseState = import("document-model").PHBaseState>(state: TState & {
77
+ name: string;
78
+ }, operations: number) => import("document-model").LoadStateAction;
79
+ noop: (scope?: string) => import("document-model").NOOPAction;
80
+ };
81
+ readonly utils: import("document-model").DocumentModelUtils<import("@powerhousedao/ph-clint-app").PhClintProjectPHState>;
82
+ readonly documentModel: import("document-model").DocumentModelPHState;
83
+ }]>>;
@@ -0,0 +1,34 @@
1
+ import path from 'node:path';
2
+ import { defineCommand } from '../framework.js';
3
+ import { z } from 'zod';
4
+ import { detectLayout } from '@powerhousedao/ph-clint-dev';
5
+ const inputSchema = z.object({
6
+ dir: z.string().optional().describe('Project root directory (default: cwd)'),
7
+ verbose: z.boolean().default(false),
8
+ });
9
+ export const build = defineCommand({
10
+ id: 'clint-project-build',
11
+ description: 'Build all packages in the project',
12
+ inputSchema,
13
+ execute: async (input, { workdir, stdout, runProcess }) => {
14
+ const dir = path.resolve(input.dir ?? workdir);
15
+ const layout = detectLayout(dir);
16
+ if (layout?.type === 'split') {
17
+ stdout(`Building app package (${layout.app})...\n`);
18
+ const appResult = await runProcess('pnpm build', { cwd: layout.app, timeout: 120_000 });
19
+ if (!appResult.success)
20
+ return { text: `App build failed.` };
21
+ stdout(`Building cli package (${layout.cli})...\n`);
22
+ const cliResult = await runProcess('pnpm build', { cwd: layout.cli, timeout: 120_000 });
23
+ if (!cliResult.success)
24
+ return { text: `CLI build failed.` };
25
+ }
26
+ else {
27
+ stdout(`Building project...\n`);
28
+ const result = await runProcess('pnpm build', { cwd: layout?.cli ?? dir, timeout: 120_000 });
29
+ if (!result.success)
30
+ return { text: `Build failed.` };
31
+ }
32
+ return { text: 'Build complete.' };
33
+ },
34
+ });