@powerhousedao/ph-clint-cli 0.1.0-dev.14 → 0.1.0-dev.16
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 +4 -2
- package/dist/codegen/builders/app-index-ts.d.ts +3 -10
- package/dist/codegen/builders/app-index-ts.js +10 -14
- package/dist/codegen/builders/cli-package-json.js +14 -5
- package/dist/codegen/builders/cli-ts.js +18 -11
- package/dist/codegen/builders/framework-gen-ts.d.ts +4 -5
- package/dist/codegen/builders/framework-gen-ts.js +21 -18
- package/dist/codegen/builders/framework-ts.js +21 -1
- package/dist/codegen/builders/readme-md.js +7 -6
- package/dist/codegen/builders/root-package-json.js +2 -2
- package/dist/codegen/index.js +3 -3
- package/dist/commands/clint-project-init.d.ts +11 -6
- package/dist/commands/clint-project-init.js +6 -5
- package/dist/commands/clint-project-publish.d.ts +1 -0
- package/dist/commands/clint-project-publish.js +2 -1
- package/dist/commands/clint-skills-sync.d.ts +10 -0
- package/dist/commands/clint-skills-sync.js +56 -0
- package/dist/framework.d.ts +5 -160
- package/dist/framework.gen.d.ts +1 -32
- package/dist/skills/sync.d.ts +40 -0
- package/dist/skills/sync.js +119 -0
- package/dist/spec/from-document.js +11 -7
- package/dist/spec/types.d.ts +55 -6
- package/dist/spec/types.js +60 -24
- package/dist/triggers/publish-trigger.d.ts +11 -0
- package/dist/triggers/publish-trigger.js +172 -0
- package/dist/triggers/spec-change.d.ts +1 -32
- package/dist/triggers/spec-change.js +40 -0
- package/package.json +4 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Map document model PublishTag values to CLI --tag values. */
|
|
2
|
+
export declare function tagToCli(tag: string): 'dev' | 'staging' | 'production' | null;
|
|
3
|
+
export declare const publishTrigger: import("@powerhousedao/ph-clint").Trigger<{
|
|
4
|
+
pending: number;
|
|
5
|
+
}, {
|
|
6
|
+
model: string;
|
|
7
|
+
devServicePort: number;
|
|
8
|
+
phVersion: string;
|
|
9
|
+
projectDocumentId?: string | undefined;
|
|
10
|
+
apiKey?: string | undefined;
|
|
11
|
+
}, import("@powerhousedao/ph-clint").InferRegistry<readonly [import("document-model").DocumentModelModule<import("@powerhousedao/ph-clint-app").PhClintProjectPHState>]>>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `publish-trigger` — watches `powerhouse/ph-clint-project` documents for
|
|
3
|
+
* publish records with `Pending` status and invokes the publish pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Unlike `spec-change`, this trigger does NOT convert to a `ClintProjectSpec`.
|
|
6
|
+
* It reads `publishHistory` directly from the document state, since publish
|
|
7
|
+
* records are a doc-model concern (not a codegen input).
|
|
8
|
+
*
|
|
9
|
+
* Flow:
|
|
10
|
+
* 1. On document change, scans `publishHistory` for records with status
|
|
11
|
+
* `"Pending"`.
|
|
12
|
+
* 2. For each pending record, returns a work item that:
|
|
13
|
+
* a. Dispatches SET_PUBLISH_STATUS({ id, status: "InProgress" }).
|
|
14
|
+
* b. Runs `ph-clint clint-project-publish --tag <tag>`.
|
|
15
|
+
* c. Dispatches SET_PUBLISH_STATUS({ id, status: "Succeeded"|"Failed" }).
|
|
16
|
+
*/
|
|
17
|
+
import { spawn } from 'node:child_process';
|
|
18
|
+
import { setPublishStatus } from '@powerhousedao/ph-clint-app';
|
|
19
|
+
import { createDocumentChangeTrigger } from '../framework.js';
|
|
20
|
+
const DOCUMENT_TYPE = 'powerhouse/ph-clint-project';
|
|
21
|
+
const TAG = '[publish]';
|
|
22
|
+
/** Find the first ph-clint-project document on the default drive. */
|
|
23
|
+
async function findProjectDocumentId(client, driveId) {
|
|
24
|
+
try {
|
|
25
|
+
const children = await client.getChildren(driveId);
|
|
26
|
+
const results = (children?.results ?? []);
|
|
27
|
+
for (const node of results) {
|
|
28
|
+
if (node.header?.documentType === DOCUMENT_TYPE && node.header.id) {
|
|
29
|
+
return node.header.id;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// fall through
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
/** Run a shell command and return stdout. Rejects on non-zero exit. */
|
|
39
|
+
function exec(cmd, args, cwd, onLog) {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const proc = spawn(cmd, args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
42
|
+
const out = [];
|
|
43
|
+
const err = [];
|
|
44
|
+
proc.stdout?.on('data', (d) => {
|
|
45
|
+
const s = d.toString();
|
|
46
|
+
out.push(s);
|
|
47
|
+
if (onLog)
|
|
48
|
+
for (const line of s.split('\n'))
|
|
49
|
+
if (line.trim())
|
|
50
|
+
onLog(line.trim());
|
|
51
|
+
});
|
|
52
|
+
proc.stderr?.on('data', (d) => {
|
|
53
|
+
const s = d.toString();
|
|
54
|
+
err.push(s);
|
|
55
|
+
if (onLog)
|
|
56
|
+
for (const line of s.split('\n'))
|
|
57
|
+
if (line.trim())
|
|
58
|
+
onLog(line.trim());
|
|
59
|
+
});
|
|
60
|
+
proc.on('error', reject);
|
|
61
|
+
proc.on('close', (code) => {
|
|
62
|
+
if (code === 0)
|
|
63
|
+
resolve(out.join(''));
|
|
64
|
+
else
|
|
65
|
+
reject(new Error(`${cmd} ${args.join(' ')} exited ${code}\n${err.join('')}`));
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Map document model PublishTag values to CLI --tag values. */
|
|
70
|
+
export function tagToCli(tag) {
|
|
71
|
+
switch (tag) {
|
|
72
|
+
case 'Dev':
|
|
73
|
+
return 'dev';
|
|
74
|
+
case 'Staging':
|
|
75
|
+
return 'staging';
|
|
76
|
+
case 'Production':
|
|
77
|
+
return 'production';
|
|
78
|
+
default:
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export const publishTrigger = createDocumentChangeTrigger({
|
|
83
|
+
id: 'publish-trigger',
|
|
84
|
+
documentType: DOCUMENT_TYPE,
|
|
85
|
+
// Don't fire on startup — only react to new pending records.
|
|
86
|
+
initialReconcile: false,
|
|
87
|
+
// Resolve the target document lazily (same pattern as spec-change trigger).
|
|
88
|
+
documentId: async (ctx) => {
|
|
89
|
+
const configured = ctx.context.config.projectDocumentId &&
|
|
90
|
+
ctx.context.config.projectDocumentId.length > 0
|
|
91
|
+
? ctx.context.config.projectDocumentId
|
|
92
|
+
: undefined;
|
|
93
|
+
if (configured)
|
|
94
|
+
return configured;
|
|
95
|
+
const reactor = await ctx.reactor();
|
|
96
|
+
if (!reactor)
|
|
97
|
+
return undefined;
|
|
98
|
+
return findProjectDocumentId(reactor.client, reactor.driveId);
|
|
99
|
+
},
|
|
100
|
+
async onChange(doc, ctx) {
|
|
101
|
+
const log = ctx.context.log;
|
|
102
|
+
const publishHistory = doc.state.global.publishHistory ?? [];
|
|
103
|
+
const pending = publishHistory.filter((r) => r.status === 'Pending');
|
|
104
|
+
if (pending.length === 0)
|
|
105
|
+
return null;
|
|
106
|
+
// Process the first pending record (one per tick to avoid races).
|
|
107
|
+
const record = pending[0];
|
|
108
|
+
const cliTag = tagToCli(record.tag);
|
|
109
|
+
if (!cliTag) {
|
|
110
|
+
log?.warn(`${TAG} unknown publish tag "${record.tag}" for record ${record.id}`);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
log?.info(`${TAG} found pending ${record.tag} publish (id=${record.id}, version=${record.version})`);
|
|
114
|
+
return {
|
|
115
|
+
type: 'function',
|
|
116
|
+
params: {
|
|
117
|
+
fn: async () => {
|
|
118
|
+
const reactor = await ctx.reactor();
|
|
119
|
+
if (!reactor) {
|
|
120
|
+
log?.error(`${TAG} reactor not available, cannot update publish status`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Mark as InProgress.
|
|
124
|
+
log?.info(`${TAG} marking record ${record.id} as InProgress`);
|
|
125
|
+
await reactor.client.execute(doc.header.id, 'main', [
|
|
126
|
+
setPublishStatus({ id: record.id, status: 'InProgress' }),
|
|
127
|
+
]);
|
|
128
|
+
// Install dependencies in the generated project before publishing.
|
|
129
|
+
const workdir = ctx.context.workdir;
|
|
130
|
+
log?.info(`${TAG} running pnpm install in ${workdir}`);
|
|
131
|
+
try {
|
|
132
|
+
await exec('pnpm', ['install'], workdir, (msg) => log?.debug(`${TAG} [install] ${msg}`));
|
|
133
|
+
}
|
|
134
|
+
catch (installErr) {
|
|
135
|
+
log?.error(`${TAG} pnpm install failed: ${installErr instanceof Error ? installErr.message : String(installErr)}`);
|
|
136
|
+
}
|
|
137
|
+
// Run the publish command.
|
|
138
|
+
let success = false;
|
|
139
|
+
try {
|
|
140
|
+
log?.info(`${TAG} invoking clint-project-publish --tag ${cliTag}`);
|
|
141
|
+
const { clintProjectPublish } = await import('../commands/clint-project-publish.js');
|
|
142
|
+
const result = await clintProjectPublish.execute({ tag: cliTag, dryRun: false, skipBuild: false, skipGitCheck: true, verbose: false },
|
|
143
|
+
// @ts-expect-error — minimal context; publish uses workdir + stdout.
|
|
144
|
+
{
|
|
145
|
+
workdir: ctx.context.workdir,
|
|
146
|
+
stdout: (msg) => log?.info(`${TAG} ${msg}`),
|
|
147
|
+
log,
|
|
148
|
+
});
|
|
149
|
+
success = !result.text.includes('failed');
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
log?.error(`${TAG} publish failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
153
|
+
}
|
|
154
|
+
// Update status.
|
|
155
|
+
const finalStatus = success ? 'Succeeded' : 'Failed';
|
|
156
|
+
log?.info(`${TAG} marking record ${record.id} as ${finalStatus}`);
|
|
157
|
+
await reactor.client.execute(doc.header.id, 'main', [
|
|
158
|
+
setPublishStatus({
|
|
159
|
+
id: record.id,
|
|
160
|
+
status: finalStatus,
|
|
161
|
+
}),
|
|
162
|
+
]);
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
callbacks: {
|
|
166
|
+
onFailure: (err) => {
|
|
167
|
+
log?.error(`${TAG} publish trigger failed: ${err.message}`);
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
});
|
|
@@ -9,35 +9,4 @@ export declare const specChangeTrigger: import("@powerhousedao/ph-clint").Trigge
|
|
|
9
9
|
phVersion: string;
|
|
10
10
|
projectDocumentId?: string | undefined;
|
|
11
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
|
-
enableRoutine: (input: import("@powerhousedao/ph-clint-app").EnableRoutineInput) => import("@powerhousedao/ph-clint-app").EnableRoutineAction;
|
|
17
|
-
disableRoutine: (input: import("@powerhousedao/ph-clint-app").DisableRoutineInput) => import("@powerhousedao/ph-clint-app").DisableRoutineAction;
|
|
18
|
-
enableMastra: (input: import("@powerhousedao/ph-clint-app").EnableMastraInput) => import("@powerhousedao/ph-clint-app").EnableMastraAction;
|
|
19
|
-
disableMastra: (input: import("@powerhousedao/ph-clint-app").DisableMastraInput) => import("@powerhousedao/ph-clint-app").DisableMastraAction;
|
|
20
|
-
enablePowerhouse: (input: import("@powerhousedao/ph-clint-app").EnablePowerhouseInput) => import("@powerhousedao/ph-clint-app").EnablePowerhouseAction;
|
|
21
|
-
setPowerhouseSwitchboard: (input: import("@powerhousedao/ph-clint-app").SetPowerhouseSwitchboardInput) => import("@powerhousedao/ph-clint-app").SetPowerhouseSwitchboardAction;
|
|
22
|
-
setPowerhouseConnect: (input: import("@powerhousedao/ph-clint-app").SetPowerhouseConnectInput) => import("@powerhousedao/ph-clint-app").SetPowerhouseConnectAction;
|
|
23
|
-
setPackageName: (input: import("@powerhousedao/ph-clint-app").SetPackageNameInput) => import("@powerhousedao/ph-clint-app").SetPackageNameAction;
|
|
24
|
-
clearBin: (input: import("@powerhousedao/ph-clint-app").ClearBinInput) => import("@powerhousedao/ph-clint-app").ClearBinAction;
|
|
25
|
-
setBin: (input: import("@powerhousedao/ph-clint-app").SetBinInput) => import("@powerhousedao/ph-clint-app").SetBinAction;
|
|
26
|
-
setDescription: (input: import("@powerhousedao/ph-clint-app").SetDescriptionInput) => import("@powerhousedao/ph-clint-app").SetDescriptionAction;
|
|
27
|
-
setVersion: (input: import("@powerhousedao/ph-clint-app").SetVersionInput) => import("@powerhousedao/ph-clint-app").SetVersionAction;
|
|
28
|
-
clearScope: (input: import("@powerhousedao/ph-clint-app").ClearScopeInput) => import("@powerhousedao/ph-clint-app").ClearScopeAction;
|
|
29
|
-
setScope: (input: import("@powerhousedao/ph-clint-app").SetScopeInput) => import("@powerhousedao/ph-clint-app").SetScopeAction;
|
|
30
|
-
setName: (name: string | {
|
|
31
|
-
name: string;
|
|
32
|
-
}) => import("document-model").SetNameAction;
|
|
33
|
-
undo: (count?: number, scope?: string) => import("document-model").UndoAction;
|
|
34
|
-
redo: (count?: number, scope?: string) => import("document-model").RedoAction;
|
|
35
|
-
prune: (start?: number, end?: number, scope?: string) => import("document-model").SchemaPruneAction;
|
|
36
|
-
loadState: <TState extends import("document-model").PHBaseState = import("document-model").PHBaseState>(state: TState & {
|
|
37
|
-
name: string;
|
|
38
|
-
}, operations: number) => import("document-model").LoadStateAction;
|
|
39
|
-
noop: (scope?: string) => import("document-model").NOOPAction;
|
|
40
|
-
};
|
|
41
|
-
readonly utils: import("document-model").DocumentModelUtils<import("@powerhousedao/ph-clint-app").PhClintProjectPHState>;
|
|
42
|
-
readonly documentModel: import("document-model").DocumentModelPHState;
|
|
43
|
-
}]>>;
|
|
12
|
+
}, import("@powerhousedao/ph-clint").InferRegistry<readonly [import("document-model").DocumentModelModule<import("@powerhousedao/ph-clint-app").PhClintProjectPHState>]>>;
|
|
@@ -20,7 +20,9 @@ import fs from 'node:fs/promises';
|
|
|
20
20
|
import path from 'node:path';
|
|
21
21
|
import { createDocumentChangeTrigger } from '../framework.js';
|
|
22
22
|
import { generateProject } from '../codegen/index.js';
|
|
23
|
+
import { runPhInit } from '../codegen/scaffold.js';
|
|
23
24
|
import { specFromDocumentState } from '../spec/from-document.js';
|
|
25
|
+
import { syncExternalSkills } from '../skills/sync.js';
|
|
24
26
|
const DOCUMENT_TYPE = 'powerhouse/ph-clint-project';
|
|
25
27
|
const HASH_DIR = path.join('.ph', 'ph-clint-cli');
|
|
26
28
|
const HASH_FILE = '.last-spec-hash';
|
|
@@ -147,6 +149,7 @@ export const specChangeTrigger = createDocumentChangeTrigger({
|
|
|
147
149
|
targetDir,
|
|
148
150
|
spec,
|
|
149
151
|
mode: 'update',
|
|
152
|
+
force: true,
|
|
150
153
|
onWarn: (m) => warnings.push(m),
|
|
151
154
|
});
|
|
152
155
|
await writeLastSpecHash(targetDir, nextHash);
|
|
@@ -158,6 +161,43 @@ export const specChangeTrigger = createDocumentChangeTrigger({
|
|
|
158
161
|
for (const w of warnings)
|
|
159
162
|
log.warn(`${TAG} ${w}`);
|
|
160
163
|
}
|
|
164
|
+
// Scaffold the reactor package (app dir) if it hasn't been initialized yet.
|
|
165
|
+
if (result.appDir) {
|
|
166
|
+
const appPkgJson = path.join(result.appDir, 'package.json');
|
|
167
|
+
let appExists = false;
|
|
168
|
+
try {
|
|
169
|
+
await fs.access(appPkgJson);
|
|
170
|
+
appExists = true;
|
|
171
|
+
}
|
|
172
|
+
catch { /* noop */ }
|
|
173
|
+
if (!appExists) {
|
|
174
|
+
log?.info(`${TAG} initializing reactor package at ${result.appDir}`);
|
|
175
|
+
const phResult = await runPhInit({
|
|
176
|
+
targetDir,
|
|
177
|
+
appDir: result.appDir,
|
|
178
|
+
spec,
|
|
179
|
+
log: (msg) => log?.info(`${TAG} ${msg}`),
|
|
180
|
+
stdio: 'ignore',
|
|
181
|
+
});
|
|
182
|
+
if (!phResult.ran) {
|
|
183
|
+
log?.warn(`${TAG} ph init skipped: ${phResult.reason ?? 'unknown'}`);
|
|
184
|
+
}
|
|
185
|
+
else if (phResult.exitCode !== 0) {
|
|
186
|
+
log?.warn(`${TAG} ph init exited with code ${phResult.exitCode}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Sync external skills after codegen.
|
|
191
|
+
if (spec.externalSkills.length > 0) {
|
|
192
|
+
const skillsResult = await syncExternalSkills({
|
|
193
|
+
targetDir,
|
|
194
|
+
desired: spec.externalSkills,
|
|
195
|
+
log: (msg) => log?.info(`${TAG} ${msg}`),
|
|
196
|
+
});
|
|
197
|
+
if (log && (skillsResult.added.length > 0 || skillsResult.removed.length > 0)) {
|
|
198
|
+
log.info(`${TAG} skills sync: +${skillsResult.added.length} -${skillsResult.removed.length}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
161
201
|
},
|
|
162
202
|
},
|
|
163
203
|
callbacks: {
|
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.16",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"mastra:start": "mastra start"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@powerhousedao/ph-clint": "^0.1.0-dev.
|
|
29
|
-
"@powerhousedao/ph-clint-app": "^0.1.0-dev.
|
|
28
|
+
"@powerhousedao/ph-clint": "^0.1.0-dev.15",
|
|
29
|
+
"@powerhousedao/ph-clint-app": "^0.1.0-dev.16",
|
|
30
30
|
"@mastra/core": "^1.22.0",
|
|
31
31
|
"@mastra/libsql": "^1.7.4",
|
|
32
32
|
"@mastra/memory": "^1.13.1",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"document-model": "6.0.0-dev.170",
|
|
38
38
|
"kysely": "^0.27.0",
|
|
39
39
|
"kysely-pglite-dialect": "^1.3.0",
|
|
40
|
-
"@powerhousedao/ph-clint-dev": "^0.1.0-dev.
|
|
40
|
+
"@powerhousedao/ph-clint-dev": "^0.1.0-dev.15",
|
|
41
41
|
"zod": "^4.3.6"
|
|
42
42
|
},
|
|
43
43
|
"pnpm": {
|