@powerhousedao/ph-clint-cli 0.1.0-dev.3 → 0.1.0-dev.31

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 (78) hide show
  1. package/dist/cli.js +31 -12
  2. package/dist/codegen/builders/agent-ts.d.ts +4 -4
  3. package/dist/codegen/builders/agent-ts.js +83 -2
  4. package/dist/codegen/builders/app-index-ts.d.ts +3 -10
  5. package/dist/codegen/builders/app-index-ts.js +10 -14
  6. package/dist/codegen/builders/cli-package-json.js +48 -8
  7. package/dist/codegen/builders/cli-ts.js +63 -22
  8. package/dist/codegen/builders/config-ts.js +11 -15
  9. package/dist/codegen/builders/demo-agent-ts.d.ts +8 -0
  10. package/dist/codegen/builders/demo-agent-ts.js +44 -0
  11. package/dist/codegen/builders/framework-gen-ts.d.ts +4 -5
  12. package/dist/codegen/builders/framework-gen-ts.js +22 -19
  13. package/dist/codegen/builders/framework-ts.js +33 -7
  14. package/dist/codegen/builders/index.d.ts +9 -1
  15. package/dist/codegen/builders/index.js +28 -4
  16. package/dist/codegen/builders/manifest-json.d.ts +6 -0
  17. package/dist/codegen/builders/manifest-json.js +31 -0
  18. package/dist/codegen/builders/mastra-index-ts.d.ts +3 -0
  19. package/dist/codegen/builders/mastra-index-ts.js +47 -5
  20. package/dist/codegen/builders/publish-config-ts.d.ts +6 -0
  21. package/dist/codegen/builders/publish-config-ts.js +21 -0
  22. package/dist/codegen/builders/readme-md.js +8 -7
  23. package/dist/codegen/builders/root-package-json.d.ts +3 -0
  24. package/dist/codegen/builders/root-package-json.js +24 -11
  25. package/dist/codegen/index.js +26 -12
  26. package/dist/codegen/scaffold.d.ts +21 -3
  27. package/dist/codegen/scaffold.js +90 -16
  28. package/dist/commands/clint-project-build.js +10 -27
  29. package/dist/commands/clint-project-init.d.ts +30 -6
  30. package/dist/commands/clint-project-init.js +45 -9
  31. package/dist/commands/clint-project-publish.d.ts +1 -0
  32. package/dist/commands/clint-project-publish.js +5 -3
  33. package/dist/commands/clint-project-regen.js +2 -2
  34. package/dist/commands/clint-skills-sync.d.ts +10 -0
  35. package/dist/commands/clint-skills-sync.js +56 -0
  36. package/dist/config.d.ts +3 -5
  37. package/dist/config.js +5 -7
  38. package/dist/framework.d.ts +5 -169
  39. package/dist/framework.gen.d.ts +1 -32
  40. package/dist/framework.gen.js +1 -1
  41. package/dist/framework.js +0 -4
  42. package/{prompts/agent-profiles/AgentBase.md → dist/gen/agent-profiles/PhClintDevAgent.md} +1 -1
  43. package/dist/gen/skills/cli-setup/.cli-docs.md +135 -0
  44. package/dist/gen/skills/cli-setup/SKILL.md +125 -0
  45. package/dist/gen/skills/cli-setup/references/00.assess-cli-needs.md +53 -0
  46. package/dist/gen/skills/cli-setup/references/01.define-config-schema.md +78 -0
  47. package/dist/gen/skills/cli-setup/references/02.wire-cli.md +141 -0
  48. package/dist/gen/skills/cli-setup/references/03.add-entrypoint.md +128 -0
  49. package/dist/gen/skills/cli-setup/references/expected-outcome.md +3 -0
  50. package/dist/gen/skills/command-definition/.cli-docs.md +84 -0
  51. package/dist/gen/skills/command-definition/SKILL.md +99 -0
  52. package/dist/gen/skills/command-definition/references/00.assess-requirements.md +58 -0
  53. package/dist/gen/skills/command-definition/references/01.define-schema.md +72 -0
  54. package/dist/gen/skills/command-definition/references/02.implement-execute.md +118 -0
  55. package/dist/gen/skills/command-definition/references/03.add-prompting.md +54 -0
  56. package/dist/gen/skills/command-definition/references/expected-outcome.md +3 -0
  57. package/dist/gen/skills/service-definition/.cli-docs.md +214 -0
  58. package/dist/gen/skills/service-definition/SKILL.md +148 -0
  59. package/dist/gen/skills/service-definition/references/00.assess-service.md +69 -0
  60. package/dist/gen/skills/service-definition/references/01.define-service.md +166 -0
  61. package/dist/gen/skills/service-definition/references/02.add-preflight.md +132 -0
  62. package/dist/gen/skills/service-definition/references/03.add-project-scanner.md +82 -0
  63. package/dist/gen/skills/service-definition/references/04.wire-events.md +120 -0
  64. package/dist/gen/skills/service-definition/references/expected-outcome.md +3 -0
  65. package/dist/services/clint-project.js +21 -0
  66. package/dist/skills/sync.d.ts +40 -0
  67. package/dist/skills/sync.js +119 -0
  68. package/dist/spec/from-document.d.ts +4 -1
  69. package/dist/spec/from-document.js +18 -8
  70. package/dist/spec/types.d.ts +85 -6
  71. package/dist/spec/types.js +90 -23
  72. package/dist/triggers/publish-trigger.d.ts +10 -0
  73. package/dist/triggers/publish-trigger.js +169 -0
  74. package/dist/triggers/spec-change.d.ts +5 -34
  75. package/dist/triggers/spec-change.js +175 -99
  76. package/package.json +7 -7
  77. package/prompts/skills-ext/.gitkeep +0 -0
  78. package/prompts/skills-tpl/.gitkeep +0 -0
@@ -7,7 +7,26 @@
7
7
  */
8
8
  import fs from 'node:fs/promises';
9
9
  import path from 'node:path';
10
- import { hasCommandOnPath, runCommand } from './exec.js';
10
+ import { execFileSync } from 'node:child_process';
11
+ import { hasCommandOnPath } from './exec.js';
12
+ /**
13
+ * Detect the installed `ph` CLI version by parsing `ph --version` output.
14
+ * Returns the version string (e.g. '6.0.0-dev.194') or undefined if
15
+ * detection fails.
16
+ */
17
+ export function getPhVersion(binName) {
18
+ try {
19
+ const output = execFileSync(binName, ['--version'], {
20
+ encoding: 'utf-8',
21
+ timeout: 5_000,
22
+ });
23
+ const match = output.match(/PH CMD version:\s*(\S+)/);
24
+ return match?.[1];
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
11
30
  export async function runPhInit(options) {
12
31
  const log = options.log ?? (() => { });
13
32
  const binName = options.binName ?? 'ph';
@@ -17,23 +36,65 @@ export async function runPhInit(options) {
17
36
  `Install @powerhousedao/ph-cli and re-run manually inside ${appFolder}/.`);
18
37
  return { ran: false, reason: 'ph-not-on-path' };
19
38
  }
39
+ // Pin ph init to the same version as the installed ph CLI, so the
40
+ // generated app uses matching Powerhouse packages.
41
+ const phVersion = options.phVersion ?? getPhVersion(binName);
42
+ const tags = ['dev', 'staging', 'latest'];
43
+ const versionArgs = phVersion
44
+ ? tags.includes(phVersion)
45
+ ? [`--${phVersion}`]
46
+ : ['--version', phVersion]
47
+ : ['--dev']; // fallback if detection fails
20
48
  // `ph init {name}` creates a fresh directory beside the cwd. Our generator
21
49
  // placed a `.gitkeep` + README.md placeholder there; clear it out so
22
50
  // ph init has a blank slate to work with.
23
51
  await fs.rm(options.appDir, { recursive: true, force: true });
24
- log(`Running \`${binName} init ${appFolder}\` in ${options.targetDir} …`);
25
- const result = await runCommand(binName, [
26
- 'init',
27
- appFolder,
28
- '--pnpm',
29
- '--dev',
30
- ], { cwd: options.targetDir, stdio: options.stdio ?? 'inherit' });
52
+ const args = ['init', appFolder, ...versionArgs, '--pnpm'];
53
+ log(`Running \`${binName} init ${appFolder}\` (version: ${phVersion ?? 'auto'}) in ${options.targetDir} …`);
54
+ const run = options.runProcess;
55
+ if (run) {
56
+ const result = await run(`${binName} ${args.join(' ')}`, {
57
+ cwd: options.targetDir,
58
+ timeout: 300_000, // ph init + pnpm install can be slow
59
+ });
60
+ const exitCode = result.success ? 0 : 1;
61
+ if (!result.success) {
62
+ log(`\`${binName} init\` failed. ` +
63
+ 'You can re-run it manually after resolving the issue.');
64
+ }
65
+ // Patch scoped name
66
+ await patchScopedName(options, appFolder, log);
67
+ return { ran: true, exitCode };
68
+ }
69
+ // Fallback: direct spawn with stdio inherit (for backward compat / tests without context)
70
+ const { runCommand } = await import('./exec.js');
71
+ const result = await runCommand(binName, args, { cwd: options.targetDir, stdio: 'inherit' });
31
72
  if (result.exitCode !== 0) {
32
73
  log(`\`${binName} init\` exited with code ${result.exitCode}. ` +
33
74
  'You can re-run it manually after resolving the issue.');
34
75
  }
76
+ await patchScopedName(options, appFolder, log);
35
77
  return { ran: true, exitCode: result.exitCode };
36
78
  }
79
+ async function patchScopedName(options, appFolder, log) {
80
+ if (!options.spec.scope)
81
+ return;
82
+ const appPkgJsonPath = path.join(options.appDir, 'package.json');
83
+ try {
84
+ const raw = await fs.readFile(appPkgJsonPath, 'utf8');
85
+ const pkg = JSON.parse(raw);
86
+ const scopedName = `@${options.spec.scope}/${appFolder}`;
87
+ if (pkg.name !== scopedName) {
88
+ pkg.name = scopedName;
89
+ pkg.publishConfig = { access: 'public' };
90
+ await fs.writeFile(appPkgJsonPath, JSON.stringify(pkg, null, 2) + '\n');
91
+ log(`Patched app package name to ${scopedName}`);
92
+ }
93
+ }
94
+ catch (err) {
95
+ log(`Warning: could not patch app package name: ${err instanceof Error ? err.message : String(err)}`);
96
+ }
97
+ }
37
98
  export async function runPnpmInstall(options) {
38
99
  const log = options.log ?? (() => { });
39
100
  const binName = options.binName ?? 'pnpm';
@@ -45,14 +106,27 @@ export async function runPnpmInstall(options) {
45
106
  const ran = [];
46
107
  for (const dir of options.dirs) {
47
108
  log(`Running \`${binName} install\` in ${dir} …`);
48
- const result = await runCommand(binName, ['install'], {
49
- cwd: dir,
50
- stdio: options.stdio ?? 'inherit',
51
- });
52
- if (result.exitCode !== 0) {
53
- log(`\`${binName} install\` exited with code ${result.exitCode} in ${dir}. ` +
54
- 'Retry manually once the underlying issue is resolved.');
55
- // Don't abort the whole chain — subsequent dirs may still work.
109
+ const run = options.runProcess;
110
+ if (run) {
111
+ const result = await run(`${binName} install`, {
112
+ cwd: dir,
113
+ timeout: 300_000,
114
+ });
115
+ if (!result.success) {
116
+ log(`\`${binName} install\` failed in ${dir}. ` +
117
+ 'Retry manually once the underlying issue is resolved.');
118
+ }
119
+ }
120
+ else {
121
+ const { runCommand } = await import('./exec.js');
122
+ const result = await runCommand(binName, ['install'], {
123
+ cwd: dir,
124
+ stdio: 'inherit',
125
+ });
126
+ if (result.exitCode !== 0) {
127
+ log(`\`${binName} install\` exited with code ${result.exitCode} in ${dir}. ` +
128
+ 'Retry manually once the underlying issue is resolved.');
129
+ }
56
130
  }
57
131
  ran.push(dir);
58
132
  }
@@ -1,5 +1,4 @@
1
1
  import path from 'node:path';
2
- import { spawn } from 'node:child_process';
3
2
  import { defineCommand } from '@powerhousedao/ph-clint';
4
3
  import { z } from 'zod';
5
4
  import { detectLayout } from '@powerhousedao/ph-clint-dev';
@@ -7,44 +6,28 @@ const inputSchema = z.object({
7
6
  dir: z.string().optional().describe('Project root directory (default: cwd)'),
8
7
  verbose: z.boolean().default(false),
9
8
  });
10
- function execInDir(dir, cmd, verbose) {
11
- return new Promise((resolve, reject) => {
12
- const proc = spawn(cmd, {
13
- cwd: dir,
14
- shell: true,
15
- stdio: verbose ? 'inherit' : 'pipe',
16
- });
17
- let stderr = '';
18
- if (!verbose && proc.stderr) {
19
- proc.stderr.on('data', (data) => {
20
- stderr += data.toString();
21
- });
22
- }
23
- proc.on('close', (code) => {
24
- if (code === 0)
25
- resolve();
26
- else
27
- reject(new Error(`Command "${cmd}" failed in ${dir} (exit ${code})${stderr ? `:\n${stderr}` : ''}`));
28
- });
29
- proc.on('error', (err) => reject(err));
30
- });
31
- }
32
9
  export const build = defineCommand({
33
10
  id: 'clint-project-build',
34
11
  description: 'Build all packages in the project',
35
12
  inputSchema,
36
- execute: async (input, { workdir, stdout }) => {
13
+ execute: async (input, { workdir, stdout, runProcess }) => {
37
14
  const dir = path.resolve(input.dir ?? workdir);
38
15
  const layout = detectLayout(dir);
39
16
  if (layout?.type === 'split') {
40
17
  stdout(`Building app package (${layout.app})...\n`);
41
- await execInDir(layout.app, 'pnpm build', input.verbose);
18
+ const appResult = await runProcess('pnpm build', { cwd: layout.app, timeout: 120_000 });
19
+ if (!appResult.success)
20
+ return { text: `App build failed.` };
42
21
  stdout(`Building cli package (${layout.cli})...\n`);
43
- await execInDir(layout.cli, 'pnpm build', input.verbose);
22
+ const cliResult = await runProcess('pnpm build', { cwd: layout.cli, timeout: 120_000 });
23
+ if (!cliResult.success)
24
+ return { text: `CLI build failed.` };
44
25
  }
45
26
  else {
46
27
  stdout(`Building project...\n`);
47
- await execInDir(layout?.cli ?? dir, 'pnpm build', input.verbose);
28
+ const result = await runProcess('pnpm build', { cwd: layout?.cli ?? dir, timeout: 120_000 });
29
+ if (!result.success)
30
+ return { text: `Build failed.` };
48
31
  }
49
32
  return { text: 'Build complete.' };
50
33
  },
@@ -35,21 +35,45 @@ export declare const init: import("@powerhousedao/ph-clint").Command<z.ZodObject
35
35
  version: string;
36
36
  description: string;
37
37
  features: {
38
- powerhouse: {
39
- enabled: boolean;
40
- switchboard: boolean;
41
- connect: boolean;
42
- };
38
+ powerhouse: "Disabled" | "Reactor" | "Switchboard" | "Connect";
43
39
  mastra: {
44
40
  enabled: boolean;
41
+ agentId: string | null;
42
+ agentName: string | null;
43
+ agentDescription: string | null;
44
+ agentImage: string | null;
45
+ models: {
46
+ id: string;
47
+ isDefault: boolean;
48
+ }[];
49
+ profiles: {
50
+ id: string;
51
+ title: string;
52
+ content: string;
53
+ }[];
45
54
  };
46
55
  routine: {
47
56
  enabled: boolean;
48
57
  };
49
58
  };
50
- documentTypes: string[];
59
+ packages: {
60
+ id: string;
61
+ packageName: string;
62
+ documentTypes: string[];
63
+ }[];
64
+ externalSkills: {
65
+ id: string;
66
+ name: string;
67
+ githubUrl: string;
68
+ }[];
69
+ deployment: {
70
+ serviceAnnouncement: boolean;
71
+ supportedResources: string[];
72
+ };
51
73
  scope?: string | undefined;
52
74
  bin?: string | undefined;
75
+ documentId?: string | undefined;
76
+ documentType?: string | undefined;
53
77
  };
54
78
  fileCount: number;
55
79
  };
@@ -1,8 +1,8 @@
1
1
  /**
2
- * `ph-clint init` — the project bootstrap wizard.
2
+ * `ph-clint clint-project-init` — the project bootstrap wizard.
3
3
  *
4
4
  * Behavior:
5
- * 1. Resolve target dir (positional `dir`, defaults to cwd).
5
+ * 1. Resolve target dir (positional `dir`, defaults to the project name).
6
6
  * 2. Assert it is empty (or `--force`).
7
7
  * 3. Collect project identity + feature toggles (prompt for missing).
8
8
  * 4. Assemble a ClintProjectSpec and validate with Zod.
@@ -13,16 +13,19 @@
13
13
  */
14
14
  import path from 'node:path';
15
15
  import { defineCommand } from '@powerhousedao/ph-clint';
16
+ import { importSpec } from '@powerhousedao/ph-clint-app/document-models/ph-clint-project';
16
17
  import { z } from 'zod';
17
18
  import { clintProjectSpecSchema, } from '../spec/types.js';
18
19
  import { generateProject } from '../codegen/index.js';
19
20
  import { isDirEmptyEnough } from '../codegen/write.js';
20
21
  import { runPhInit, runPnpmInstall } from '../codegen/scaffold.js';
22
+ import { writeProjectSpec } from '../spec/file.js';
23
+ import { specToImportInput } from '../triggers/spec-change.js';
21
24
  const inputSchema = z.object({
22
25
  dir: z
23
26
  .string()
24
27
  .optional()
25
- .describe('Target directory (defaults to the current directory)'),
28
+ .describe('Target directory (defaults to the project name)'),
26
29
  name: z
27
30
  .string()
28
31
  .describe('Project name — bare, or `@scope/name`. Lowercase letters, digits, hyphens.'),
@@ -70,13 +73,22 @@ export function buildSpec(input) {
70
73
  // Routine is forced on when Mastra is on — the REPL-only path works, but
71
74
  // the agent routine loop is what most users actually want.
72
75
  const routine = input.enableRoutine || input.enableMastra;
76
+ const mastra = input.enableMastra
77
+ ? {
78
+ enabled: true,
79
+ agentId: `${name}-agent`,
80
+ agentName: `${name.charAt(0).toUpperCase()}${name.slice(1).replace(/-(\w)/g, (_, c) => ' ' + c.toUpperCase())} Agent`,
81
+ models: [{ id: 'anthropic/claude-sonnet-4-5', isDefault: true }],
82
+ profiles: [{ id: 'base', title: 'Base', content: 'You are a helpful assistant.' }],
83
+ }
84
+ : { enabled: false };
73
85
  const candidate = {
74
86
  name,
75
87
  scope,
76
88
  description: input.description ?? '',
77
89
  features: {
78
- powerhouse: { enabled: input.enablePowerhouse },
79
- mastra: { enabled: input.enableMastra },
90
+ powerhouse: input.enablePowerhouse ? 'Connect' : 'Disabled',
91
+ mastra,
80
92
  routine: { enabled: routine },
81
93
  },
82
94
  };
@@ -90,8 +102,9 @@ export const init = defineCommand({
90
102
  promptForDefaults: true,
91
103
  promptOptional: ['description'],
92
104
  },
93
- execute: async (input, { workdir, stdout }) => {
94
- const targetDir = path.resolve(workdir, input.dir ?? '.');
105
+ execute: async (input, { workdir, stdout, reactor: getReactor, folders, runProcess }) => {
106
+ const { name: bareName } = splitPackageName(input.name);
107
+ const targetDir = path.resolve(workdir, input.dir ?? bareName);
95
108
  if (!input.force) {
96
109
  const empty = await isDirEmptyEnough(targetDir);
97
110
  if (!empty) {
@@ -109,7 +122,28 @@ export const init = defineCommand({
109
122
  allowNonEmpty: input.force,
110
123
  });
111
124
  stdout(`Generated ${result.files.length} files in ${targetDir}\n`);
112
- const split = spec.features.powerhouse.enabled;
125
+ // Create spec document in personal drive (if reactor available)
126
+ if (getReactor && folders) {
127
+ try {
128
+ const reactor = await getReactor();
129
+ if (reactor) {
130
+ const newDoc = await reactor.client.createEmpty('powerhouse/ph-clint-project');
131
+ const docId = newDoc.header.id;
132
+ await folders.addDocument(docId, `specs/${spec.name}`, spec.name);
133
+ const importInput = specToImportInput(spec);
134
+ await reactor.client.execute(docId, 'main', [importSpec(importInput)]);
135
+ spec.documentId = docId;
136
+ spec.documentType = 'powerhouse/ph-clint-project';
137
+ await writeProjectSpec(targetDir, spec);
138
+ stdout(`Created spec document ${docId} in Clint Folders/specs/${spec.name}\n`);
139
+ }
140
+ }
141
+ catch (err) {
142
+ // Non-fatal — trigger will pick it up on next startup
143
+ stdout(`Note: could not create spec document — ${err instanceof Error ? err.message : String(err)}\n`);
144
+ }
145
+ }
146
+ const split = spec.features.powerhouse !== 'Disabled';
113
147
  // Reactor package scaffolding (split layout only).
114
148
  if (split && result.appDir && !input.skipPhInit) {
115
149
  const phResult = await runPhInit({
@@ -117,6 +151,7 @@ export const init = defineCommand({
117
151
  appDir: result.appDir,
118
152
  spec,
119
153
  log: (msg) => stdout(msg + '\n'),
154
+ runProcess,
120
155
  });
121
156
  if (!phResult.ran) {
122
157
  stdout(`Note: \`ph init\` did not run — ${phResult.reason ?? 'see above'}.\n`);
@@ -136,6 +171,7 @@ export const init = defineCommand({
136
171
  await runPnpmInstall({
137
172
  dirs,
138
173
  log: (msg) => stdout(msg + '\n'),
174
+ runProcess,
139
175
  });
140
176
  }
141
177
  const next = [];
@@ -159,7 +195,7 @@ export const init = defineCommand({
159
195
  next.push(' pnpm dev');
160
196
  }
161
197
  next.push('');
162
- next.push('Edit the project spec and run `ph-clint regen` to regenerate.');
198
+ next.push('Edit the project spec and run `ph-clint clint-project-regen` to regenerate.');
163
199
  return {
164
200
  text: next.join('\n'),
165
201
  data: { targetDir, spec, fileCount: result.files.length },
@@ -8,6 +8,7 @@ export declare const clintProjectPublish: import("@powerhousedao/ph-clint").Comm
8
8
  dir: z.ZodOptional<z.ZodString>;
9
9
  dryRun: z.ZodDefault<z.ZodBoolean>;
10
10
  skipBuild: z.ZodDefault<z.ZodBoolean>;
11
+ skipGitCheck: z.ZodDefault<z.ZodBoolean>;
11
12
  verbose: z.ZodDefault<z.ZodBoolean>;
12
13
  }, z.core.$strip>, {
13
14
  text: string;
@@ -7,6 +7,7 @@ const inputSchema = z.object({
7
7
  dir: z.string().optional().describe('Project root directory'),
8
8
  dryRun: z.boolean().default(false).describe('Preview without publishing'),
9
9
  skipBuild: z.boolean().default(false),
10
+ skipGitCheck: z.boolean().default(false).describe('Skip git working tree check'),
10
11
  verbose: z.boolean().default(false),
11
12
  });
12
13
  function formatPlanPreview(plan) {
@@ -34,11 +35,12 @@ export const clintProjectPublish = defineCommand({
34
35
  logLines.push(msg);
35
36
  stdout(msg + '\n');
36
37
  };
37
- // 1. Resolve plan
38
+ // 1. Resolve plan — discover config from the target dir, not process.cwd().
39
+ const configFile = path.join(dir, 'publish.config.ts');
38
40
  const plan = await resolvePublishPlan({
39
41
  tag: input.tag,
40
- configPath: undefined, // auto-discover from dir
41
- skipGitCheck: input.dryRun,
42
+ configPath: configFile,
43
+ skipGitCheck: input.dryRun || input.skipGitCheck,
42
44
  log,
43
45
  });
44
46
  // 2. Show preview
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `ph-clint regen` — reconcile an existing project against its persisted
2
+ * `ph-clint clint-project-regen` — reconcile an existing project against its persisted
3
3
  * spec. Equivalent to running the generator in update mode.
4
4
  *
5
5
  * Run inside a project directory (or pass `--dir`). If the spec has flipped
@@ -31,7 +31,7 @@ export const regen = defineCommand({
31
31
  if (!spec) {
32
32
  return {
33
33
  text: `Error: no spec found at ${targetDir}/.ph/ph-clint-cli/project-spec.json.\n` +
34
- 'Run `ph-clint init` first to scaffold a new project.',
34
+ 'Run `ph-clint clint-project-init` first to scaffold a new project.',
35
35
  };
36
36
  }
37
37
  const warnings = [];
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod';
2
+ export declare const skillsSync: import("@powerhousedao/ph-clint").Command<z.ZodObject<{
3
+ dir: z.ZodOptional<z.ZodString>;
4
+ }, z.core.$strip>, {
5
+ text: string;
6
+ data?: undefined;
7
+ } | {
8
+ text: string;
9
+ data: import("../skills/sync.js").SyncResult;
10
+ }, Record<string, unknown>>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `ph-clint clint-skills-sync` — reconcile external skills on disk to match
3
+ * the project spec. Clones missing skills from GitHub, removes skills no
4
+ * longer referenced, and updates `.skills-manifest.json`.
5
+ */
6
+ import path from 'node:path';
7
+ import { defineCommand } from '@powerhousedao/ph-clint';
8
+ import { z } from 'zod';
9
+ import { readProjectSpec } from '../spec/file.js';
10
+ import { syncExternalSkills } from '../skills/sync.js';
11
+ const inputSchema = z.object({
12
+ dir: z
13
+ .string()
14
+ .optional()
15
+ .describe('Project directory (defaults to the current directory)'),
16
+ });
17
+ export const skillsSync = defineCommand({
18
+ id: 'clint-skills-sync',
19
+ description: 'Synchronise external skills from the project spec',
20
+ inputSchema,
21
+ execute: async (input, { workdir, stdout, log }) => {
22
+ const targetDir = path.resolve(workdir, input.dir ?? '.');
23
+ const spec = await readProjectSpec(targetDir);
24
+ if (!spec) {
25
+ return {
26
+ text: `Error: no spec found at ${targetDir}/.ph/ph-clint-cli/project-spec.json.\n` +
27
+ 'Run `ph-clint clint-project-init` first to scaffold a new project.',
28
+ };
29
+ }
30
+ const result = await syncExternalSkills({
31
+ targetDir,
32
+ desired: spec.externalSkills,
33
+ log: (msg) => {
34
+ if (log)
35
+ log.info(msg);
36
+ else
37
+ stdout(msg + '\n');
38
+ },
39
+ });
40
+ const lines = [];
41
+ if (result.added.length > 0) {
42
+ lines.push(`Added: ${result.added.join(', ')}`);
43
+ }
44
+ if (result.removed.length > 0) {
45
+ lines.push(`Removed: ${result.removed.join(', ')}`);
46
+ }
47
+ if (result.added.length === 0 && result.removed.length === 0) {
48
+ lines.push('External skills are up to date.');
49
+ }
50
+ stdout(lines.join('\n') + '\n');
51
+ return {
52
+ text: lines.join('\n'),
53
+ data: result,
54
+ };
55
+ },
56
+ });
package/dist/config.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- /** CLI name — used for config resolution, env var prefixing, and .ph/ paths. */
2
- export declare const CLI_NAME = "ph-clint";
3
- export declare const CLI_VERSION = "0.0.0";
4
- /** Project root — resolved from this file's location. */
5
- export declare const PROJECT_ROOT: string;
1
+ export declare const CLI_ROOT: string;
2
+ export declare const CLI_NAME: string;
3
+ export declare const CLI_VERSION: string;
package/dist/config.js CHANGED
@@ -1,7 +1,5 @@
1
- import path from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
- /** CLI name — used for config resolution, env var prefixing, and .ph/ paths. */
4
- export const CLI_NAME = 'ph-clint';
5
- export const CLI_VERSION = '0.0.0';
6
- /** Project root — resolved from this file's location. */
7
- export const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
1
+ import { readPackageInfo } from '@powerhousedao/ph-clint';
2
+ const pkg = readPackageInfo(import.meta.url);
3
+ export const CLI_ROOT = pkg.root;
4
+ export const CLI_NAME = pkg.name.replace(/-cli$/, '');
5
+ export const CLI_VERSION = pkg.version;