@powerhousedao/ph-clint-cli 0.1.0-dev.2 → 0.1.0-dev.21

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 (44) hide show
  1. package/dist/cli.js +8 -10
  2. package/dist/codegen/builders/app-index-ts.d.ts +3 -10
  3. package/dist/codegen/builders/app-index-ts.js +10 -14
  4. package/dist/codegen/builders/cli-package-json.js +47 -8
  5. package/dist/codegen/builders/cli-ts.js +18 -15
  6. package/dist/codegen/builders/config-ts.js +11 -15
  7. package/dist/codegen/builders/framework-gen-ts.d.ts +4 -5
  8. package/dist/codegen/builders/framework-gen-ts.js +22 -19
  9. package/dist/codegen/builders/framework-ts.js +21 -1
  10. package/dist/codegen/builders/index.d.ts +1 -0
  11. package/dist/codegen/builders/index.js +1 -0
  12. package/dist/codegen/builders/publish-config-ts.d.ts +6 -0
  13. package/dist/codegen/builders/publish-config-ts.js +21 -0
  14. package/dist/codegen/builders/readme-md.js +8 -7
  15. package/dist/codegen/builders/root-package-json.d.ts +3 -0
  16. package/dist/codegen/builders/root-package-json.js +24 -11
  17. package/dist/codegen/index.js +24 -11
  18. package/dist/codegen/scaffold.d.ts +8 -0
  19. package/dist/codegen/scaffold.js +51 -2
  20. package/dist/commands/clint-project-init.d.ts +11 -6
  21. package/dist/commands/clint-project-init.js +8 -7
  22. package/dist/commands/clint-project-publish.d.ts +1 -0
  23. package/dist/commands/clint-project-publish.js +5 -3
  24. package/dist/commands/clint-project-regen.js +2 -2
  25. package/dist/commands/clint-skills-sync.d.ts +10 -0
  26. package/dist/commands/clint-skills-sync.js +56 -0
  27. package/dist/config.d.ts +3 -5
  28. package/dist/config.js +5 -7
  29. package/dist/framework.d.ts +5 -160
  30. package/dist/framework.gen.d.ts +1 -32
  31. package/dist/framework.gen.js +1 -1
  32. package/dist/skills/sync.d.ts +40 -0
  33. package/dist/skills/sync.js +119 -0
  34. package/dist/spec/from-document.js +11 -7
  35. package/dist/spec/types.d.ts +55 -6
  36. package/dist/spec/types.js +61 -25
  37. package/dist/triggers/publish-trigger.d.ts +11 -0
  38. package/dist/triggers/publish-trigger.js +172 -0
  39. package/dist/triggers/spec-change.d.ts +1 -32
  40. package/dist/triggers/spec-change.js +59 -0
  41. package/package.json +6 -7
  42. package/prompts/agent-profiles/AgentBase.md +0 -13
  43. package/prompts/skills-ext/.gitkeep +0 -0
  44. package/prompts/skills-tpl/.gitkeep +0 -0
package/dist/cli.js CHANGED
@@ -6,10 +6,8 @@
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';
10
- import { fileURLToPath } from 'node:url';
11
9
  import { defineCli, buildDefaultReactor } from '@powerhousedao/ph-clint';
12
- import { CLI_NAME, CLI_VERSION } from './config.js';
10
+ import { CLI_NAME, CLI_VERSION, CLI_ROOT } from './config.js';
13
11
  import { configSchema, secretsSchema } from './framework.js';
14
12
  // @clint:begin imports
15
13
  import { documentModels } from '@powerhousedao/ph-clint-app';
@@ -19,29 +17,29 @@ import { regen } from './commands/clint-project-regen.js';
19
17
  import { build } from './commands/clint-project-build.js';
20
18
  import { clintProjectPublish } from './commands/clint-project-publish.js';
21
19
  import { clintProject } from './services/clint-project.js';
20
+ import { skillsSync } from './commands/clint-skills-sync.js';
22
21
  import { specChangeTrigger } from './triggers/spec-change.js';
22
+ import { publishTrigger } from './triggers/publish-trigger.js';
23
23
  // @clint:end imports
24
- // Connect (ph connect) must run inside the Reactor Package (ph-clint-app).
25
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
- const appDir = path.resolve(__dirname, '../../ph-clint-app');
27
24
  export const cli = defineCli({
28
25
  name: CLI_NAME,
29
26
  version: CLI_VERSION,
27
+ root: CLI_ROOT,
30
28
  description: `${CLI_NAME} v${CLI_VERSION} — scaffold and maintain ph-clint implementation projects`,
31
29
  configSchema,
32
30
  secretsSchema,
33
31
  // @clint:begin commands
34
- commands: [init, regen, build, clintProjectPublish],
32
+ commands: [init, regen, build, clintProjectPublish, skillsSync],
35
33
  // @clint:end commands
36
34
  // @clint:begin services
37
35
  services: [clintProject],
38
36
  // @clint:end services
39
37
  // @clint:begin triggers
40
- triggers: [specChangeTrigger],
38
+ triggers: [specChangeTrigger, publishTrigger],
41
39
  // @clint:end triggers
42
40
  // @clint:begin prompts
43
41
  prompts: {
44
- sources: [],
42
+ artifacts: [],
45
43
  agents: {},
46
44
  skills: {},
47
45
  },
@@ -74,7 +72,7 @@ cli.configureReactor({
74
72
  subscriptions: { documentTypes: ['powerhouse/ph-clint-project'] },
75
73
  }),
76
74
  switchboard: { enabled: true },
77
- connect: { enabled: true, workdir: appDir },
75
+ connect: { enabled: true },
78
76
  });
79
77
  // @clint:end reactor
80
78
  // @clint:begin mastra
@@ -3,16 +3,9 @@
3
3
  * reactor package. Impl code imports from `'{name}-app'` instead of drilling
4
4
  * into `'{name}-app/document-models/<slug>/v1/gen/...'`.
5
5
  *
6
- * Each entry re-exports a document-model subfolder's `index.ts`, which in
7
- * turn re-exports the module object (`PhClintProject`, ...), action creators,
8
- * document/state types, and type guards. Fully machine-owned: regen on every
9
- * run.
10
- *
11
- * Powerhouse's own codegen might emit its own `index.ts` (as is the case for
12
- * ph-clint-app today, which has manifest/editors/processors re-exports). When
13
- * that exists, leave it alone — users hand-merge. Our codegen only emits when
14
- * the file is absent or was emitted by a previous ph-clint-cli run (hash
15
- * matches the ledger).
6
+ * Only re-exports document types belonging to the app package (the project's
7
+ * own reactor package). External packages' document types are imported from
8
+ * their own npm packages, not from the app barrel.
16
9
  */
17
10
  import { type ClintProjectSpec } from '../../spec/types.js';
18
11
  export declare function buildAppIndexTs(spec: ClintProjectSpec): string | null;
@@ -3,29 +3,25 @@
3
3
  * reactor package. Impl code imports from `'{name}-app'` instead of drilling
4
4
  * into `'{name}-app/document-models/<slug>/v1/gen/...'`.
5
5
  *
6
- * Each entry re-exports a document-model subfolder's `index.ts`, which in
7
- * turn re-exports the module object (`PhClintProject`, ...), action creators,
8
- * document/state types, and type guards. Fully machine-owned: regen on every
9
- * run.
10
- *
11
- * Powerhouse's own codegen might emit its own `index.ts` (as is the case for
12
- * ph-clint-app today, which has manifest/editors/processors re-exports). When
13
- * that exists, leave it alone — users hand-merge. Our codegen only emits when
14
- * the file is absent or was emitted by a previous ph-clint-cli run (hash
15
- * matches the ledger).
6
+ * Only re-exports document types belonging to the app package (the project's
7
+ * own reactor package). External packages' document types are imported from
8
+ * their own npm packages, not from the app barrel.
16
9
  */
17
- import { getDocumentTypeSlug, } from '../../spec/types.js';
10
+ import { getAppPackageName, getDocumentTypeSlug, phAtLeast, } from '../../spec/types.js';
18
11
  export function buildAppIndexTs(spec) {
19
- if (!spec.features.powerhouse.enabled)
12
+ if (!phAtLeast(spec.features.powerhouse, 'Reactor'))
20
13
  return null;
21
- if (spec.documentTypes.length === 0)
14
+ const appPkg = getAppPackageName(spec);
15
+ const appEntry = spec.packages.find((p) => p.packageName === appPkg);
16
+ const docTypes = appEntry?.documentTypes ?? [];
17
+ if (docTypes.length === 0)
22
18
  return null;
23
19
  const lines = [];
24
20
  lines.push('// THIS FILE IS GENERATED BY ph-clint-cli.');
25
21
  lines.push('// Re-exports every registered document model from a single top-level');
26
22
  lines.push('// entry so impl code can `import { ... } from \'{name}-app\'`.');
27
23
  lines.push('');
28
- for (const docType of spec.documentTypes) {
24
+ for (const docType of docTypes) {
29
25
  const slug = getDocumentTypeSlug(docType);
30
26
  lines.push(`export * from './document-models/${slug}/index.js';`);
31
27
  }
@@ -4,13 +4,44 @@
4
4
  * When `features.powerhouse.enabled`, this file lives at
5
5
  * `{root}/{name}-cli/package.json`. Otherwise at `{root}/package.json`.
6
6
  */
7
- import { getBinName, getPackageName, } from '../../spec/types.js';
8
- /** Pinned version range for peer ph-clint packages emitted into generated projects. */
9
- const PH_CLINT_VERSION = '^0.1.0';
7
+ import { getAppDirName, getAppPackageName, getBinName, getPackageName, phAtLeast, } from '../../spec/types.js';
8
+ import { CLI_VERSION } from '../../config.js';
9
+ /**
10
+ * Derive a semver range from the running ph-clint-cli version that respects
11
+ * the dev / staging / production channel boundary. This is the version range
12
+ * emitted into *scaffolded* projects' package.json for @powerhousedao/ph-clint
13
+ * and @powerhousedao/ph-clint-dev dependencies.
14
+ *
15
+ * CLI_VERSION is the version of ph-clint-cli itself (read from its own
16
+ * package.json at startup). Since all framework packages share a lockstep
17
+ * version, this is also the correct base for the dependency range.
18
+ *
19
+ * Examples:
20
+ * "0.1.0" → "^0.1.0" (production — stable only)
21
+ * "0.1.0-staging.2" → "^0.1.0-staging.0" (staging + stable)
22
+ * "0.1.0-dev.4" → "^0.1.0-dev.0" (dev + staging + stable)
23
+ *
24
+ * This works because semver orders prereleases alphabetically:
25
+ * dev.N < staging.N < release. A caret range starting at dev.0 naturally
26
+ * upgrades through staging to production.
27
+ */
28
+ function phClintRange(version) {
29
+ const match = /^(\d+\.\d+\.\d+)(?:-([a-z]+)\.\d+)?/.exec(version);
30
+ if (!match)
31
+ return `^${version}`;
32
+ const base = match[1];
33
+ const channel = match[2]; // "dev", "staging", or undefined
34
+ // For prerelease channels, pin exact version. Caret ranges like ^0.1.0-dev.0
35
+ // resolve to `latest` dist-tag (0.1.0-dev.0) instead of the highest match,
36
+ // causing "export not found" errors when the scaffolding CLI is newer.
37
+ return channel ? version : `^${base}`;
38
+ }
39
+ /** Version range for @powerhousedao/ph-clint{,-dev} deps in scaffolded projects. */
40
+ const PH_CLINT_VERSION = phClintRange(CLI_VERSION);
10
41
  const POWERHOUSE_VERSION = '6.0.0-dev.170';
11
42
  export function buildCliPackageJson(spec) {
12
43
  const { mastra, powerhouse } = spec.features;
13
- const pkgName = powerhouse.enabled
44
+ const pkgName = phAtLeast(powerhouse, 'Reactor')
14
45
  ? `${getPackageName(spec)}-cli`
15
46
  : getPackageName(spec);
16
47
  const bin = getBinName(spec);
@@ -23,12 +54,20 @@ export function buildCliPackageJson(spec) {
23
54
  dependencies['@mastra/libsql'] = '^1.7.4';
24
55
  dependencies['@mastra/memory'] = '^1.13.1';
25
56
  }
26
- if (powerhouse.enabled) {
27
- dependencies[`${spec.name}-app`] = `file:../${spec.name}-app`;
57
+ if (phAtLeast(powerhouse, 'Reactor')) {
58
+ const appPkg = getAppPackageName(spec);
59
+ const appDir = getAppDirName(spec);
60
+ dependencies[appPkg] = `file:../${appDir}`;
28
61
  dependencies['@powerhousedao/reactor'] = POWERHOUSE_VERSION;
29
62
  dependencies['@powerhousedao/reactor-api'] = POWERHOUSE_VERSION;
30
63
  dependencies['@powerhousedao/shared'] = POWERHOUSE_VERSION;
31
64
  dependencies['document-model'] = POWERHOUSE_VERSION;
65
+ // External reactor packages (non-app) as versioned dependencies.
66
+ for (const pkg of spec.packages) {
67
+ if (pkg.packageName !== appPkg) {
68
+ dependencies[pkg.packageName] = 'latest';
69
+ }
70
+ }
32
71
  }
33
72
  const scripts = {
34
73
  'build:skills': 'tsx scripts/build-skills.ts',
@@ -43,16 +82,16 @@ export function buildCliPackageJson(spec) {
43
82
  scripts['mastra:build'] = 'mastra build';
44
83
  scripts['mastra:start'] = 'mastra start';
45
84
  }
46
- scripts['publish:npm'] = 'pnpm build && pnpm publish --no-git-checks';
47
85
  const pkg = {
48
86
  name: pkgName,
49
87
  version: spec.version,
50
- private: true,
88
+ publishConfig: { access: 'public' },
51
89
  type: 'module',
52
90
  };
53
91
  if (spec.description)
54
92
  pkg.description = spec.description;
55
93
  pkg.bin = { [bin]: './dist/main.js' };
94
+ pkg.files = ['dist'];
56
95
  pkg.scripts = scripts;
57
96
  pkg.dependencies = dependencies;
58
97
  pkg.devDependencies = {
@@ -1,5 +1,12 @@
1
+ /**
2
+ * Builds `src/cli.ts` — the defineCli() call with feature blocks guarded by
3
+ * `@clint:begin`/`@clint:end` markers so the code generator can patch
4
+ * specific regions on subsequent runs (delta mode — future phase).
5
+ */
6
+ import { getAppPackageName, phAtLeast } from '../../spec/types.js';
1
7
  export function buildCliTs(spec) {
2
8
  const { mastra, routine, powerhouse } = spec.features;
9
+ const ph = powerhouse;
3
10
  const lines = [];
4
11
  lines.push('/**');
5
12
  lines.push(' * CLI definition.');
@@ -8,34 +15,30 @@ export function buildCliTs(spec) {
8
15
  lines.push(' * markers are rewritten by ph-clint when the project spec changes.');
9
16
  lines.push(' * Everything outside the markers is user-editable and preserved.');
10
17
  lines.push(' */');
11
- if (powerhouse.enabled) {
12
- lines.push(`import path from 'node:path';`);
13
- lines.push(`import { fileURLToPath } from 'node:url';`);
18
+ if (phAtLeast(ph, 'Reactor')) {
14
19
  lines.push(`import { defineCli, buildDefaultReactor } from '@powerhousedao/ph-clint';`);
15
20
  }
16
21
  else {
17
22
  lines.push(`import { defineCli } from '@powerhousedao/ph-clint';`);
18
23
  }
19
- lines.push(`import { CLI_NAME, CLI_VERSION } from './config.js';`);
24
+ lines.push(`import { CLI_NAME, CLI_VERSION${phAtLeast(ph, 'Reactor') ? ', CLI_ROOT' : ''} } from './config.js';`);
20
25
  lines.push(`import { configSchema, secretsSchema } from './framework.js';`);
21
26
  lines.push('');
22
27
  lines.push('// @clint:begin imports');
23
- if (powerhouse.enabled) {
24
- lines.push(`import { documentModels } from '${spec.name}-app';`);
28
+ if (phAtLeast(ph, 'Reactor')) {
29
+ lines.push(`import { documentModels } from '${getAppPackageName(spec)}';`);
25
30
  }
26
31
  if (mastra.enabled) {
27
32
  lines.push(`import { createAgent } from './agents/agent.js';`);
28
33
  }
29
34
  lines.push('// @clint:end imports');
30
35
  lines.push('');
31
- if (powerhouse.enabled) {
32
- lines.push(`const __dirname = path.dirname(fileURLToPath(import.meta.url));`);
33
- lines.push(`const appDir = path.resolve(__dirname, '../../${spec.name}-app');`);
34
- lines.push('');
35
- }
36
36
  lines.push('export const cli = defineCli({');
37
37
  lines.push(' name: CLI_NAME,');
38
38
  lines.push(' version: CLI_VERSION,');
39
+ if (phAtLeast(ph, 'Reactor')) {
40
+ lines.push(' root: CLI_ROOT,');
41
+ }
39
42
  lines.push(' description: `${CLI_NAME} v${CLI_VERSION}`,');
40
43
  lines.push(' configSchema,');
41
44
  lines.push(' secretsSchema,');
@@ -59,7 +62,7 @@ export function buildCliTs(spec) {
59
62
  lines.push('');
60
63
  lines.push(' // @clint:begin prompts');
61
64
  lines.push(' prompts: {');
62
- lines.push(' sources: [],');
65
+ lines.push(' artifacts: [],');
63
66
  lines.push(' agents: {},');
64
67
  lines.push(' skills: {},');
65
68
  lines.push(' },');
@@ -88,15 +91,15 @@ export function buildCliTs(spec) {
88
91
  lines.push('});');
89
92
  lines.push('');
90
93
  lines.push('// @clint:begin reactor');
91
- if (powerhouse.enabled) {
94
+ if (phAtLeast(ph, 'Reactor')) {
92
95
  lines.push('cli.configureReactor({');
93
96
  lines.push(' create: (ctx) =>');
94
97
  lines.push(' buildDefaultReactor(ctx, {');
95
98
  lines.push(' documentModels,');
96
99
  lines.push(` drive: { name: '${spec.name}' },`);
97
100
  lines.push(' }),');
98
- lines.push(` switchboard: { enabled: ${String(powerhouse.switchboard)} },`);
99
- lines.push(` connect: { enabled: ${String(powerhouse.connect)}, workdir: appDir },`);
101
+ lines.push(` switchboard: { enabled: ${String(phAtLeast(ph, 'Switchboard'))} },`);
102
+ lines.push(` connect: { enabled: ${String(phAtLeast(ph, 'Connect'))} },`);
100
103
  lines.push('});');
101
104
  }
102
105
  lines.push('// @clint:end reactor');
@@ -1,21 +1,17 @@
1
- /**
2
- * Builds `src/config.ts` — CLI identity constants (name, version, project
3
- * root path). The Zod `configSchema` / `secretsSchema` are emitted by
4
- * `buildFrameworkTs` into `src/framework.ts` (user-owned, init-only) so users
5
- * can extend them without fighting the generator.
6
- */
7
- import { getBinName, } from '../../spec/types.js';
8
1
  export function buildConfigTs(spec) {
9
- const cliName = getBinName(spec);
2
+ const hasCustomBin = !!spec.bin;
10
3
  const lines = [];
11
- lines.push(`import path from 'node:path';`);
12
- lines.push(`import { fileURLToPath } from 'node:url';`);
4
+ lines.push(`import { readPackageInfo } from '@powerhousedao/ph-clint';`);
13
5
  lines.push('');
14
- lines.push('/** CLI name — used for config resolution, env var prefixing, and .ph/ paths. */');
15
- lines.push(`export const CLI_NAME = '${cliName}';`);
16
- lines.push(`export const CLI_VERSION = '${spec.version}';`);
6
+ lines.push('const pkg = readPackageInfo(import.meta.url);');
17
7
  lines.push('');
18
- lines.push('/** Project root — resolved from this file location. */');
19
- lines.push('export const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \'..\');');
8
+ lines.push('export const CLI_ROOT = pkg.root;');
9
+ if (hasCustomBin) {
10
+ lines.push(`export const CLI_NAME = '${spec.bin}';`);
11
+ }
12
+ else {
13
+ lines.push(`export const CLI_NAME = pkg.name.replace(/-cli$/, '');`);
14
+ }
15
+ lines.push('export const CLI_VERSION = pkg.version;');
20
16
  return lines.join('\n') + '\n';
21
17
  }
@@ -4,14 +4,13 @@
4
4
  * `defineRegistry([...] as const)` call that describes which document models
5
5
  * are bound into `framework.ts`'s typed factories.
6
6
  *
7
- * Kept deliberately small: this file exports only `registry` and the
8
- * `Registry` type. The `createTypes` call lives in `framework.ts` (the
9
- * user-owned half) so `configSchema` edits stay local and we avoid an import
10
- * cycle between the two files.
7
+ * Iterates all packages' document types. The app package's modules are
8
+ * imported from the local `{name}-app` package; external packages' modules
9
+ * are imported from their npm package name.
11
10
  *
12
11
  * Emitted only when Powerhouse is enabled — flat-layout impls have no
13
12
  * reactor package to import modules from and bind `createTypes` directly in
14
- * `framework.ts`. When Powerhouse is on but no `documentTypes` are declared,
13
+ * `framework.ts`. When Powerhouse is on but no document types are declared,
15
14
  * we still emit this file with an empty `defineRegistry([])` so impl code's
16
15
  * `import { registry } from './framework.gen.js'` keeps resolving across
17
16
  * regens.
@@ -4,47 +4,50 @@
4
4
  * `defineRegistry([...] as const)` call that describes which document models
5
5
  * are bound into `framework.ts`'s typed factories.
6
6
  *
7
- * Kept deliberately small: this file exports only `registry` and the
8
- * `Registry` type. The `createTypes` call lives in `framework.ts` (the
9
- * user-owned half) so `configSchema` edits stay local and we avoid an import
10
- * cycle between the two files.
7
+ * Iterates all packages' document types. The app package's modules are
8
+ * imported from the local `{name}-app` package; external packages' modules
9
+ * are imported from their npm package name.
11
10
  *
12
11
  * Emitted only when Powerhouse is enabled — flat-layout impls have no
13
12
  * reactor package to import modules from and bind `createTypes` directly in
14
- * `framework.ts`. When Powerhouse is on but no `documentTypes` are declared,
13
+ * `framework.ts`. When Powerhouse is on but no document types are declared,
15
14
  * we still emit this file with an empty `defineRegistry([])` so impl code's
16
15
  * `import { registry } from './framework.gen.js'` keeps resolving across
17
16
  * regens.
18
17
  */
19
- import { getDocumentTypeModuleName, } from '../../spec/types.js';
20
- /** Local package name of the reactor package for this project. */
21
- function appPackageName(spec) {
22
- return `${spec.name}-app`;
23
- }
18
+ import { getAppPackageName, getDocumentTypeModuleName, phAtLeast, } from '../../spec/types.js';
24
19
  export function buildFrameworkGenTs(spec) {
25
- if (!spec.features.powerhouse.enabled)
20
+ if (!phAtLeast(spec.features.powerhouse, 'Reactor'))
26
21
  return null;
27
- const pkg = appPackageName(spec);
28
- const entries = spec.documentTypes.map((docType) => ({
29
- docType,
30
- name: getDocumentTypeModuleName(docType),
31
- }));
22
+ const appPkg = getAppPackageName(spec);
23
+ // Collect all (docType, moduleName, importFrom) tuples across packages.
24
+ const entries = [];
25
+ for (const pkg of spec.packages) {
26
+ const importFrom = pkg.packageName === appPkg ? appPkg : pkg.packageName;
27
+ for (const docType of pkg.documentTypes) {
28
+ entries.push({
29
+ docType,
30
+ name: getDocumentTypeModuleName(docType),
31
+ importFrom,
32
+ });
33
+ }
34
+ }
32
35
  const lines = [];
33
36
  lines.push('/**');
34
37
  lines.push(' * THIS FILE IS GENERATED BY ph-clint-cli. DO NOT EDIT.');
35
38
  lines.push(' *');
36
- lines.push(' * Regenerated on every `ph-clint regen` and on every spec-change');
39
+ lines.push(' * Regenerated on every `ph-clint clint-project-regen` and on every spec-change');
37
40
  lines.push(' * tick. Put custom bindings in `framework.ts`, not here — edits to');
38
41
  lines.push(' * this file will be overwritten.');
39
42
  lines.push(' */');
40
43
  lines.push(`import { defineRegistry } from '@powerhousedao/ph-clint';`);
41
44
  for (const e of entries) {
42
- lines.push(`import { ${e.name} } from '${pkg}';`);
45
+ lines.push(`import { ${e.name} } from '${e.importFrom}';`);
43
46
  }
44
47
  lines.push('');
45
48
  lines.push('/**');
46
49
  lines.push(' * Typed document registry derived from the reactor package\'s document models.');
47
- lines.push(' * Regenerated whenever the spec\'s `documentTypes` list changes.');
50
+ lines.push(' * Regenerated whenever the spec\'s packages or document types change.');
48
51
  lines.push(' */');
49
52
  if (entries.length === 0) {
50
53
  lines.push('export const registry = defineRegistry([] as const);');
@@ -1,6 +1,26 @@
1
+ /**
2
+ * Builds `src/framework.ts` — the user-owned half of the typed-binding split.
3
+ *
4
+ * Emitted **only on create**. Once the file exists, codegen never overwrites
5
+ * it: users are expected to extend `configSchema` and `secretsSchema` here,
6
+ * and those edits must survive regens.
7
+ *
8
+ * Behavior differs by layout:
9
+ *
10
+ * - **Powerhouse enabled** — this file declares `configSchema`/`secretsSchema`
11
+ * and re-exports `registry`, `defineCommand`, `defineTrigger`,
12
+ * `defineService`, and `createDocumentChangeTrigger` from the companion
13
+ * `framework.gen.ts`. Adding a document type to the spec regenerates
14
+ * `framework.gen.ts`; this file keeps resolving its re-exports unchanged.
15
+ *
16
+ * - **Powerhouse disabled** — no reactor package exists to register modules
17
+ * against, so this file binds `createTypes({ configSchema })` directly and
18
+ * exports the typed factories from here. No `framework.gen.ts` is emitted.
19
+ */
20
+ import { phAtLeast } from '../../spec/types.js';
1
21
  export function buildFrameworkTs(spec) {
2
22
  const { mastra, powerhouse } = spec.features;
3
- const hasGen = powerhouse.enabled;
23
+ const hasGen = phAtLeast(powerhouse, 'Reactor');
4
24
  const lines = [];
5
25
  lines.push('/**');
6
26
  lines.push(' * Per-CLI typed framework binding.');
@@ -39,5 +39,6 @@ export interface FileBuilder {
39
39
  export declare const CLI_FILE_BUILDERS: FileBuilder[];
40
40
  export { buildCliPackageJson, buildCliTs, buildConfigTs, buildMainTs, buildTsconfigJson, buildJestConfigJs, buildEslintConfigJs, buildGitignore, buildMastraIndexTs, buildBuildSkillsScript, buildAgentBaseMd, buildAgentTs, buildFrameworkGenTs, buildFrameworkTs, };
41
41
  export { buildRootPackageJson } from './root-package-json.js';
42
+ export { buildPublishConfigTs } from './publish-config-ts.js';
42
43
  export { buildReadme } from './readme-md.js';
43
44
  export { buildAppIndexTs } from './app-index-ts.js';
@@ -56,5 +56,6 @@ export const CLI_FILE_BUILDERS = [
56
56
  ];
57
57
  export { buildCliPackageJson, buildCliTs, buildConfigTs, buildMainTs, buildTsconfigJson, buildJestConfigJs, buildEslintConfigJs, buildGitignore, buildMastraIndexTs, buildBuildSkillsScript, buildAgentBaseMd, buildAgentTs, buildFrameworkGenTs, buildFrameworkTs, };
58
58
  export { buildRootPackageJson } from './root-package-json.js';
59
+ export { buildPublishConfigTs } from './publish-config-ts.js';
59
60
  export { buildReadme } from './readme-md.js';
60
61
  export { buildAppIndexTs } from './app-index-ts.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Builds the root `publish.config.ts` for split-layout projects.
3
+ * Defines a single lockstep group containing both the app and cli packages.
4
+ */
5
+ import { type ClintProjectSpec } from '../../spec/types.js';
6
+ export declare function buildPublishConfigTs(spec: ClintProjectSpec): string;
@@ -0,0 +1,21 @@
1
+ export function buildPublishConfigTs(spec) {
2
+ const app = `${spec.name}-app`;
3
+ const cli = `${spec.name}-cli`;
4
+ // The publish pipeline appends the prerelease suffix (e.g. -dev.0), so we
5
+ // only store the base semver (M.m.p) here.
6
+ const baseVersion = spec.version.replace(/-.*$/, '');
7
+ return `// Publish config — loaded by ph-clint's publish pipeline.
8
+ // definePublishConfig is an identity function; a plain export is equivalent.
9
+ export default {
10
+ groups: {
11
+ '${spec.name}': {
12
+ version: '${baseVersion}',
13
+ packages: [
14
+ { path: '${app}', category: 'app' },
15
+ { path: '${cli}', category: 'cli' },
16
+ ],
17
+ },
18
+ },
19
+ };
20
+ `;
21
+ }
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Builds the project README for a freshly-generated ph-clint project.
3
3
  */
4
- import { getPackageName, } from '../../spec/types.js';
4
+ import { getPackageName, phAtLeast, } from '../../spec/types.js';
5
5
  export function buildReadme(spec) {
6
6
  const { mastra, routine, powerhouse } = spec.features;
7
+ const ph = powerhouse;
7
8
  const title = getPackageName(spec);
8
9
  const lines = [];
9
10
  lines.push(`# ${title}`);
@@ -19,10 +20,10 @@ export function buildReadme(spec) {
19
20
  lines.push('');
20
21
  lines.push('## Enabled features');
21
22
  lines.push('');
22
- lines.push(`- **Powerhouse**: ${powerhouse.enabled ? 'on' : 'off'}`);
23
- if (powerhouse.enabled) {
24
- lines.push(` - Switchboard: ${powerhouse.switchboard ? 'on' : 'off'}`);
25
- lines.push(` - Connect: ${powerhouse.connect ? 'on' : 'off'}`);
23
+ lines.push(`- **Powerhouse**: ${ph !== 'Disabled' ? `on (${ph})` : 'off'}`);
24
+ if (phAtLeast(ph, 'Reactor')) {
25
+ lines.push(` - Switchboard: ${phAtLeast(ph, 'Switchboard') ? 'on' : 'off'}`);
26
+ lines.push(` - Connect: ${phAtLeast(ph, 'Connect') ? 'on' : 'off'}`);
26
27
  }
27
28
  lines.push(`- **Mastra agent**: ${mastra.enabled ? 'on' : 'off'}`);
28
29
  lines.push(`- **Routine loop**: ${routine.enabled ? 'on' : 'off'}`);
@@ -30,9 +31,9 @@ export function buildReadme(spec) {
30
31
  lines.push('## Regenerate');
31
32
  lines.push('');
32
33
  lines.push('Toggle features or update metadata in `.ph/ph-clint-cli/project-spec.json`,');
33
- lines.push('then re-run `ph-clint` to regenerate.');
34
+ lines.push('then re-run `ph-clint clint-project-regen` to regenerate.');
34
35
  lines.push('');
35
- if (powerhouse.enabled) {
36
+ if (phAtLeast(ph, 'Reactor')) {
36
37
  lines.push('## Split layout');
37
38
  lines.push('');
38
39
  lines.push(`This project is split into \`${spec.name}-cli/\` (the CLI) and \`${spec.name}-app/\` (the Powerhouse reactor package).`);
@@ -2,6 +2,9 @@
2
2
  * Builds the *root* `package.json` for split-layout projects (Powerhouse
3
3
  * enabled). A plain passthrough — deliberately no pnpm workspaces because
4
4
  * they interfere with `{name}-app`'s own `pnpm build`.
5
+ *
6
+ * Uses `pnpm --prefix <dir>` for all orchestration scripts — no cwd mutation.
7
+ * Build order: app first, then cli (cli depends on app via `file:` dep).
5
8
  */
6
9
  import { type ClintProjectSpec } from '../../spec/types.js';
7
10
  export declare function buildRootPackageJson(spec: ClintProjectSpec): string;
@@ -2,26 +2,39 @@
2
2
  * Builds the *root* `package.json` for split-layout projects (Powerhouse
3
3
  * enabled). A plain passthrough — deliberately no pnpm workspaces because
4
4
  * they interfere with `{name}-app`'s own `pnpm build`.
5
+ *
6
+ * Uses `pnpm --prefix <dir>` for all orchestration scripts — no cwd mutation.
7
+ * Build order: app first, then cli (cli depends on app via `file:` dep).
5
8
  */
6
- import { getPackageName, } from '../../spec/types.js';
9
+ import { getPackageName, phAtLeast, } from '../../spec/types.js';
7
10
  export function buildRootPackageJson(spec) {
8
11
  const cli = `${spec.name}-cli`;
9
12
  const app = `${spec.name}-app`;
13
+ const prefix = (dir, cmd) => `pnpm --prefix ${dir} ${cmd}`;
14
+ const both = (cmd) => `${prefix(app, cmd)} && ${prefix(cli, cmd)}`;
15
+ const connectEnabled = phAtLeast(spec.features.powerhouse, 'Connect');
16
+ const buildScript = connectEnabled
17
+ ? `${prefix(app, 'build')} && ${prefix(app, 'connect:build')} && ${prefix(cli, 'build')}`
18
+ : both('build');
19
+ const scripts = {
20
+ install: both('install'),
21
+ build: buildScript,
22
+ test: both('test'),
23
+ dev: prefix(cli, 'dev'),
24
+ start: prefix(cli, 'start'),
25
+ lint: prefix(cli, 'lint'),
26
+ 'app:dev': prefix(app, 'dev'),
27
+ 'cli:dev': prefix(cli, 'dev'),
28
+ 'publish:dev': `${prefix(cli, 'exec ph-publish')} dev -c ./publish.config.ts`,
29
+ 'publish:staging': `${prefix(cli, 'exec ph-publish')} staging -c ./publish.config.ts`,
30
+ 'publish:production': `${prefix(cli, 'exec ph-publish')} production -c ./publish.config.ts`,
31
+ };
10
32
  const pkg = {
11
33
  name: getPackageName(spec),
12
34
  version: spec.version,
13
35
  private: true,
14
36
  ...(spec.description ? { description: spec.description } : {}),
15
- scripts: {
16
- install: `cd ${app} && pnpm install && cd ../${cli} && pnpm install`,
17
- build: `cd ${app} && pnpm build && cd ../${cli} && pnpm build`,
18
- dev: `cd ${cli} && pnpm dev`,
19
- start: `cd ${cli} && pnpm start`,
20
- test: `cd ${app} && pnpm test && cd ../${cli} && pnpm test`,
21
- lint: `cd ${cli} && pnpm lint`,
22
- 'app:dev': `cd ${app} && pnpm dev`,
23
- 'cli:dev': `cd ${cli} && pnpm dev`,
24
- },
37
+ scripts,
25
38
  };
26
39
  return JSON.stringify(pkg, null, 2) + '\n';
27
40
  }