@powerhousedao/ph-clint-cli 0.1.0-dev.5 → 0.1.0-dev.50

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 (89) hide show
  1. package/dist/cli.js +50 -9
  2. package/dist/codegen/actions.d.ts +46 -0
  3. package/dist/codegen/actions.js +442 -0
  4. package/dist/codegen/builders/agent-ts.d.ts +4 -4
  5. package/dist/codegen/builders/agent-ts.js +92 -2
  6. package/dist/codegen/builders/app-index-ts.d.ts +3 -10
  7. package/dist/codegen/builders/app-index-ts.js +10 -14
  8. package/dist/codegen/builders/cli-package-json.js +46 -14
  9. package/dist/codegen/builders/cli-ts.js +87 -25
  10. package/dist/codegen/builders/config-ts.d.ts +1 -1
  11. package/dist/codegen/builders/config-ts.js +2 -8
  12. package/dist/codegen/builders/demo-agent-ts.d.ts +8 -0
  13. package/dist/codegen/builders/demo-agent-ts.js +50 -0
  14. package/dist/codegen/builders/eslint-config-js.d.ts +1 -1
  15. package/dist/codegen/builders/eslint-config-js.js +7 -4
  16. package/dist/codegen/builders/framework-gen-ts.d.ts +4 -5
  17. package/dist/codegen/builders/framework-gen-ts.js +26 -18
  18. package/dist/codegen/builders/framework-ts.js +33 -7
  19. package/dist/codegen/builders/index.d.ts +10 -1
  20. package/dist/codegen/builders/index.js +30 -5
  21. package/dist/codegen/builders/manifest-json.d.ts +6 -0
  22. package/dist/codegen/builders/manifest-json.js +31 -0
  23. package/dist/codegen/builders/mastra-index-ts.d.ts +3 -0
  24. package/dist/codegen/builders/mastra-index-ts.js +47 -5
  25. package/dist/codegen/builders/publish-config-js.d.ts +8 -0
  26. package/dist/codegen/builders/publish-config-js.js +33 -0
  27. package/dist/codegen/builders/publish-config-ts.d.ts +8 -0
  28. package/dist/codegen/builders/publish-config-ts.js +32 -0
  29. package/dist/codegen/builders/readme-md.js +7 -6
  30. package/dist/codegen/builders/root-package-json.d.ts +3 -0
  31. package/dist/codegen/builders/root-package-json.js +26 -14
  32. package/dist/codegen/builders/smoke-test-ts.d.ts +2 -0
  33. package/dist/codegen/builders/smoke-test-ts.js +15 -0
  34. package/dist/codegen/generated.d.ts +17 -0
  35. package/dist/codegen/generated.js +49 -0
  36. package/dist/codegen/index.d.ts +3 -0
  37. package/dist/codegen/index.js +154 -23
  38. package/dist/codegen/scaffold.d.ts +41 -3
  39. package/dist/codegen/scaffold.js +141 -16
  40. package/dist/commands/clint-project-build.js +10 -27
  41. package/dist/commands/clint-project-init.d.ts +34 -9
  42. package/dist/commands/clint-project-init.js +46 -42
  43. package/dist/commands/clint-project-publish.d.ts +1 -0
  44. package/dist/commands/clint-project-publish.js +5 -3
  45. package/dist/commands/clint-project-regen.js +25 -1
  46. package/dist/commands/clint-skills-sync.d.ts +10 -0
  47. package/dist/commands/clint-skills-sync.js +56 -0
  48. package/dist/framework.d.ts +5 -169
  49. package/dist/framework.gen.d.ts +1 -32
  50. package/dist/framework.js +0 -4
  51. package/{prompts/agent-profiles/AgentBase.md → dist/gen/agent-profiles/PhClintDevAgent.md} +1 -1
  52. package/dist/gen/skills/cli-setup/.cli-docs.md +135 -0
  53. package/dist/gen/skills/cli-setup/SKILL.md +125 -0
  54. package/dist/gen/skills/cli-setup/references/00.assess-cli-needs.md +53 -0
  55. package/dist/gen/skills/cli-setup/references/01.define-config-schema.md +78 -0
  56. package/dist/gen/skills/cli-setup/references/02.wire-cli.md +141 -0
  57. package/dist/gen/skills/cli-setup/references/03.add-entrypoint.md +128 -0
  58. package/dist/gen/skills/cli-setup/references/expected-outcome.md +3 -0
  59. package/dist/gen/skills/command-definition/.cli-docs.md +84 -0
  60. package/dist/gen/skills/command-definition/SKILL.md +99 -0
  61. package/dist/gen/skills/command-definition/references/00.assess-requirements.md +58 -0
  62. package/dist/gen/skills/command-definition/references/01.define-schema.md +72 -0
  63. package/dist/gen/skills/command-definition/references/02.implement-execute.md +118 -0
  64. package/dist/gen/skills/command-definition/references/03.add-prompting.md +54 -0
  65. package/dist/gen/skills/command-definition/references/expected-outcome.md +3 -0
  66. package/dist/gen/skills/service-definition/.cli-docs.md +214 -0
  67. package/dist/gen/skills/service-definition/SKILL.md +148 -0
  68. package/dist/gen/skills/service-definition/references/00.assess-service.md +69 -0
  69. package/dist/gen/skills/service-definition/references/01.define-service.md +166 -0
  70. package/dist/gen/skills/service-definition/references/02.add-preflight.md +132 -0
  71. package/dist/gen/skills/service-definition/references/03.add-project-scanner.md +82 -0
  72. package/dist/gen/skills/service-definition/references/04.wire-events.md +120 -0
  73. package/dist/gen/skills/service-definition/references/expected-outcome.md +3 -0
  74. package/dist/services/clint-project.js +21 -0
  75. package/dist/skills/sync.d.ts +40 -0
  76. package/dist/skills/sync.js +119 -0
  77. package/dist/spec/ensure-document.d.ts +17 -0
  78. package/dist/spec/ensure-document.js +62 -0
  79. package/dist/spec/from-document.d.ts +4 -1
  80. package/dist/spec/from-document.js +4 -16
  81. package/dist/spec/types.d.ts +92 -9
  82. package/dist/spec/types.js +104 -32
  83. package/dist/triggers/publish-trigger.d.ts +10 -0
  84. package/dist/triggers/publish-trigger.js +169 -0
  85. package/dist/triggers/spec-change.d.ts +5 -34
  86. package/dist/triggers/spec-change.js +129 -102
  87. package/package.json +7 -7
  88. package/prompts/skills-ext/.gitkeep +0 -0
  89. package/prompts/skills-tpl/.gitkeep +0 -0
@@ -1,26 +1,29 @@
1
1
  /**
2
- * `spec-change` trigger — watches the `powerhouse/ph-clint-project` document
3
- * on the Reactor drive and, when its state changes, enqueues a work item
4
- * that re-runs the code generator in update mode.
2
+ * `spec-change` trigger — watches ALL `powerhouse/ph-clint-project` documents
3
+ * and, when any change, re-runs the code generator for each affected project.
5
4
  *
6
5
  * Flow:
7
6
  * 1. `createDocumentChangeTrigger` (from ph-clint) owns the subscription,
8
- * event coalescing, doc loading, and type narrowing.
9
- * 2. `documentId` resolves the target doc lazily: it prefers
10
- * `config.projectDocumentId`, falling back to a drive scan for the first
11
- * `powerhouse/ph-clint-project` document (and pinning the id into local
12
- * config on success).
13
- * 3. `onChange` converts the doc state to a `ClintProjectSpec`, hashes it,
7
+ * event coalescing, doc loading, and type narrowing. No `documentId` is
8
+ * set, so it loads ALL matching documents on each poll.
9
+ * 2. `getProjectMapping()` merges an on-disk scan (via the clint-project
10
+ * service's projectScanner) with personal-drive folder entries to
11
+ * correlate each document to an on-disk project directory.
12
+ * 3. For each document, `specFromDocumentState()` builds a spec, hashes it,
14
13
  * and compares against `.ph/ph-clint-cli/.last-spec-hash`. If unchanged,
15
- * emits nothing. If different, returns a `function` work item that calls
16
- * `generateProject(spec, workdir, 'update')` and writes the new hash.
14
+ * skips. Otherwise regenerates at the mapped target directory.
17
15
  */
18
16
  import crypto from 'node:crypto';
19
17
  import fs from 'node:fs/promises';
20
18
  import path from 'node:path';
19
+ import { scanProjects, getProjectMapping } from '@powerhousedao/ph-clint';
21
20
  import { createDocumentChangeTrigger } from '../framework.js';
22
21
  import { generateProject } from '../codegen/index.js';
22
+ import { runPostGenActions } from '../codegen/actions.js';
23
23
  import { specFromDocumentState } from '../spec/from-document.js';
24
+ import { readProjectSpec, writeProjectSpec } from '../spec/file.js';
25
+ import { ensureSpecDocument } from '../spec/ensure-document.js';
26
+ import { clintProject } from '../services/clint-project.js';
24
27
  const DOCUMENT_TYPE = 'powerhouse/ph-clint-project';
25
28
  const HASH_DIR = path.join('.ph', 'ph-clint-cli');
26
29
  const HASH_FILE = '.last-spec-hash';
@@ -49,11 +52,13 @@ async function writeLastSpecHash(targetDir, hash) {
49
52
  await fs.mkdir(path.dirname(file), { recursive: true });
50
53
  await fs.writeFile(file, hash + '\n', 'utf8');
51
54
  }
52
- /** Stable hash of a ClintProjectSpec (sha256 of canonical-key JSON). */
55
+ /** Stable hash of a ClintProjectSpec (sha256 of canonical-key JSON).
56
+ * Excludes documentId/documentType — they don't affect codegen output. */
53
57
  export function hashSpec(spec) {
58
+ const { documentId, documentType, ...codegenRelevant } = spec;
54
59
  return crypto
55
60
  .createHash('sha256')
56
- .update(canonicalJson(spec), 'utf8')
61
+ .update(canonicalJson(codegenRelevant), 'utf8')
57
62
  .digest('hex');
58
63
  }
59
64
  function canonicalJson(value) {
@@ -70,102 +75,124 @@ function canonicalJson(value) {
70
75
  .join(',') +
71
76
  '}');
72
77
  }
73
- /** Find the first ph-clint-project document on the default drive. */
74
- async function findProjectDocumentId(client, driveId) {
75
- try {
76
- const children = await client.getChildren(driveId);
77
- const results = (children?.results ?? []);
78
- for (const node of results) {
79
- if (node.header?.documentType === DOCUMENT_TYPE && node.header.id) {
80
- return node.header.id;
81
- }
82
- }
83
- }
84
- catch {
85
- // fall through
86
- }
87
- return undefined;
78
+ export function specToImportInput(spec) {
79
+ return {
80
+ name: spec.name,
81
+ scope: spec.scope ?? null,
82
+ version: spec.version,
83
+ description: spec.description,
84
+ powerhouse: spec.features.powerhouse,
85
+ mastraEnabled: spec.features.mastra.enabled,
86
+ routineEnabled: spec.features.routine.enabled,
87
+ agentId: spec.features.mastra.agentId ?? null,
88
+ agentName: spec.features.mastra.agentName ?? null,
89
+ models: spec.features.mastra.models.map(m => ({
90
+ id: m.id,
91
+ isDefault: m.isDefault,
92
+ })),
93
+ profiles: spec.features.mastra.profiles.map(p => ({
94
+ id: p.id,
95
+ title: p.title,
96
+ content: p.content,
97
+ })),
98
+ packages: spec.packages.map(p => ({
99
+ id: p.id,
100
+ packageName: p.packageName,
101
+ documentTypes: p.documentTypes,
102
+ })),
103
+ externalSkills: spec.externalSkills.map(s => ({
104
+ id: s.id,
105
+ name: s.name,
106
+ githubUrl: s.githubUrl,
107
+ })),
108
+ };
88
109
  }
89
110
  export const specChangeTrigger = createDocumentChangeTrigger({
90
111
  id: 'spec-change',
91
112
  documentType: DOCUMENT_TYPE,
92
- // Resolve the target id lazily so we can also do first-time discovery and
93
- // persist the discovered id into local config.
94
- documentId: async (ctx) => {
95
- const configured = ctx.context.config.projectDocumentId &&
96
- ctx.context.config.projectDocumentId.length > 0
97
- ? ctx.context.config.projectDocumentId
98
- : undefined;
99
- if (configured)
100
- return configured;
113
+ // No documentId → watches ALL ph-clint-project docs
114
+ async onChange(docs, ctx) {
115
+ const log = ctx.context.log;
116
+ const folders = ctx.context.folders;
117
+ // Build the mapping: on-disk projects + personal drive documents
118
+ const scanner = clintProject.projectScanner;
119
+ const scanResults = scanner
120
+ ? scanProjects(ctx.context.workdir, scanner)
121
+ : [];
122
+ const mapping = await getProjectMapping(scanResults, folders);
123
+ // Auto-create spec documents for unlinked or orphaned projects
101
124
  const reactor = await ctx.reactor();
102
- if (!reactor)
103
- return undefined;
104
- const discovered = await findProjectDocumentId(reactor.client, reactor.driveId);
105
- if (!discovered) {
106
- ctx.context.log?.debug(`${TAG} no ${DOCUMENT_TYPE} document found on drive ${reactor.driveId}`);
107
- return undefined;
125
+ if (reactor && folders) {
126
+ for (const entry of mapping) {
127
+ if (!entry.path)
128
+ continue;
129
+ const existingSpec = await readProjectSpec(entry.path);
130
+ if (!existingSpec)
131
+ continue;
132
+ try {
133
+ const { docId, created } = await ensureSpecDocument({
134
+ spec: existingSpec,
135
+ targetDir: entry.path,
136
+ reactor,
137
+ folders,
138
+ });
139
+ if (created) {
140
+ log?.info(`${TAG} linked project ${entry.name} to document ${docId}`);
141
+ }
142
+ }
143
+ catch (err) {
144
+ log?.warn(`${TAG} could not ensure spec document for ${entry.name}: ${err instanceof Error ? err.message : String(err)}`);
145
+ }
146
+ }
108
147
  }
109
- // Persist into local config + mutate in-memory so future ticks skip discovery.
110
- try {
111
- const current = await ctx.context.workspace.loadLocalConfig({});
112
- await ctx.context.workspace.storeLocalConfig({
113
- ...current,
114
- projectDocumentId: discovered,
148
+ for (const doc of docs) {
149
+ const spec = specFromDocumentState(doc.state.global, {
150
+ documentId: doc.header.id,
151
+ documentType: doc.header.documentType,
152
+ });
153
+ if (!spec) {
154
+ log?.debug(`${TAG} document ${doc.header.id} state did not validate against ClintProjectSpec, skipping`);
155
+ continue;
156
+ }
157
+ // Find target directory from mapping
158
+ const entry = mapping.find((m) => m.documentId === doc.header.id);
159
+ const targetDir = entry?.path ?? path.join(ctx.context.workdir, spec.name);
160
+ // Per-project hash comparison
161
+ const nextHash = hashSpec(spec);
162
+ const prevHash = await readLastSpecHash(targetDir);
163
+ if (nextHash === prevHash) {
164
+ log?.debug(`${TAG} spec for ${spec.name} unchanged (hash=${nextHash.slice(0, 12)}…), skipping`);
165
+ continue;
166
+ }
167
+ log?.debug(`${TAG} spec for ${spec.name} changed: ${prevHash ? prevHash.slice(0, 12) + '…' : '<none>'} → ${nextHash.slice(0, 12)}…`);
168
+ log?.info(`${TAG} regenerating ${spec.name} at ${targetDir} (doc=${doc.header.id})`);
169
+ const t0 = Date.now();
170
+ const warnings = [];
171
+ const result = await generateProject({
172
+ targetDir,
173
+ spec,
174
+ mode: 'update',
175
+ force: true,
176
+ onWarn: (m) => warnings.push(m),
177
+ });
178
+ // Write spec JSON with documentId
179
+ await writeProjectSpec(targetDir, spec);
180
+ await writeLastSpecHash(targetDir, nextHash);
181
+ const elapsed = Date.now() - t0;
182
+ if (log) {
183
+ if (result.migrated)
184
+ log.info(`${TAG} migrated flat → split layout`);
185
+ log.info(`${TAG} regenerated ${spec.name} in ${elapsed}ms: ${result.files.length} written, ${result.skipped.length} skipped, ${result.deleted.length} deleted`);
186
+ for (const w of warnings)
187
+ log.warn(`${TAG} ${w}`);
188
+ }
189
+ // Run post-generation actions (ph-init, install, build, skills-sync).
190
+ await runPostGenActions(result.pendingActions, {
191
+ log: (msg) => log?.info(`${TAG} ${msg}`),
192
+ runProcess: ctx.context.runProcess,
115
193
  });
116
- ctx.context.config.projectDocumentId = discovered;
117
- ctx.context.log?.info(`${TAG} pinned projectDocumentId=${discovered} in ${ctx.context.workspace.getLocalConfigPath()}`);
118
- }
119
- catch (err) {
120
- ctx.context.log?.warn(`${TAG} failed to persist projectDocumentId: ${err instanceof Error ? err.message : String(err)}`);
121
- }
122
- return discovered;
123
- },
124
- async onChange(doc, ctx) {
125
- const log = ctx.context.log;
126
- const spec = specFromDocumentState(doc.state.global);
127
- if (!spec) {
128
- log?.debug(`${TAG} document ${doc.header.id} state did not validate against ClintProjectSpec, skipping`);
129
- return null;
130
- }
131
- const targetDir = ctx.context.workdir;
132
- const nextHash = hashSpec(spec);
133
- const prevHash = await readLastSpecHash(targetDir);
134
- if (nextHash === prevHash) {
135
- log?.debug(`${TAG} spec unchanged (hash=${nextHash.slice(0, 12)}…), nothing to do`);
136
- return null;
137
194
  }
138
- log?.debug(`${TAG} spec changed: ${prevHash ? prevHash.slice(0, 12) + '…' : '<none>'} → ${nextHash.slice(0, 12)}…`);
139
- return {
140
- type: 'function',
141
- params: {
142
- fn: async () => {
143
- log?.info(`${TAG} regenerating project at ${targetDir} (doc=${doc.header.id})`);
144
- const t0 = Date.now();
145
- const warnings = [];
146
- const result = await generateProject({
147
- targetDir,
148
- spec,
149
- mode: 'update',
150
- onWarn: (m) => warnings.push(m),
151
- });
152
- await writeLastSpecHash(targetDir, nextHash);
153
- const elapsed = Date.now() - t0;
154
- if (log) {
155
- if (result.migrated)
156
- log.info(`${TAG} migrated flat → split layout`);
157
- log.info(`${TAG} regenerated in ${elapsed}ms: ${result.files.length} written, ${result.skipped.length} skipped, ${result.deleted.length} deleted`);
158
- for (const w of warnings)
159
- log.warn(`${TAG} ${w}`);
160
- }
161
- },
162
- },
163
- callbacks: {
164
- onFailure: (err) => {
165
- log?.error(`${TAG} regen failed: ${err.message}`);
166
- log?.debug(`${TAG} stack: ${err.stack ?? '<no stack>'}`);
167
- },
168
- },
169
- };
195
+ // Work done inline, no additional work item needed
196
+ return null;
170
197
  },
171
198
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerhousedao/ph-clint-cli",
3
- "version": "0.1.0-dev.5",
3
+ "version": "0.1.0-dev.50",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -10,8 +10,7 @@
10
10
  "ph-clint": "./dist/main.js"
11
11
  },
12
12
  "files": [
13
- "dist",
14
- "prompts"
13
+ "dist"
15
14
  ],
16
15
  "scripts": {
17
16
  "build:skills": "tsx scripts/build-skills.ts",
@@ -19,21 +18,23 @@
19
18
  "dev": "tsx src/main.ts",
20
19
  "start": "node dist/main.js",
21
20
  "test": "NODE_OPTIONS='--experimental-vm-modules' jest --detectOpenHandles",
21
+ "test:e2e": "NODE_OPTIONS='--experimental-vm-modules' jest --detectOpenHandles --testPathPatterns 'tests/(e2e|codegen-e2e)/' --testPathIgnorePatterns '/node_modules/'",
22
22
  "lint": "eslint src",
23
23
  "mastra:dev": "mastra dev",
24
24
  "mastra:build": "mastra build",
25
25
  "mastra:start": "mastra start"
26
26
  },
27
27
  "dependencies": {
28
- "@powerhousedao/ph-clint": "^0.1.0-dev.5",
29
- "@powerhousedao/ph-clint-app": "^0.1.0-dev.5",
28
+ "@electric-sql/pglite": "0.3.15",
30
29
  "@mastra/core": "^1.22.0",
31
30
  "@mastra/libsql": "^1.7.4",
32
31
  "@mastra/memory": "^1.13.1",
32
+ "@powerhousedao/ph-clint": "^0.1.0-dev.50",
33
+ "@powerhousedao/ph-clint-app": "^0.1.0-dev.50",
34
+ "@powerhousedao/ph-clint-dev": "^0.1.0-dev.50",
33
35
  "@powerhousedao/reactor": "6.0.0-dev.170",
34
36
  "@powerhousedao/reactor-api": "6.0.0-dev.170",
35
37
  "@powerhousedao/shared": "6.0.0-dev.170",
36
- "@electric-sql/pglite": "0.3.15",
37
38
  "document-model": "6.0.0-dev.170",
38
39
  "kysely": "^0.27.0",
39
40
  "kysely-pglite-dialect": "^1.3.0",
@@ -48,7 +49,6 @@
48
49
  "@jest/globals": "^30.2.0",
49
50
  "@types/node": "^25.5.2",
50
51
  "jest": "^30.2.0",
51
- "@powerhousedao/ph-clint-dev": "^0.1.0-dev.5",
52
52
  "ts-jest": "^29.4.6",
53
53
  "tsx": "^4.19.0",
54
54
  "typescript": "^6.0.2"
File without changes
File without changes