@powerhousedao/ph-clint-cli 0.1.0-dev.53 → 0.1.0-dev.54

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.
@@ -1,150 +1 @@
1
- /**
2
- * Post-generation side-effects: invoke `ph init` to fill in the reactor
3
- * package skeleton, and run `pnpm install` in the relevant sub-projects.
4
- *
5
- * Both steps are opt-out (flags on the init command) and both tolerate
6
- * missing binaries — they never fail the init flow, only emit warnings.
7
- */
8
- import fs from 'node:fs/promises';
9
- import path from 'node:path';
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
- }
30
- export async function runPhInit(options) {
31
- const log = options.log ?? (() => { });
32
- const binName = options.binName ?? 'ph';
33
- const appFolder = path.basename(options.appDir);
34
- // Guard: never re-init an already-initialized app directory.
35
- try {
36
- await fs.access(path.join(options.appDir, 'package.json'));
37
- log(`App directory ${appFolder} already initialized — skipping ph init.`);
38
- return { ran: false, reason: 'already-initialized' };
39
- }
40
- catch {
41
- // No package.json — proceed with init.
42
- }
43
- if (!(await hasCommandOnPath(binName))) {
44
- log(`Skipping \`${binName} init\` — \`${binName}\` is not on PATH. ` +
45
- `Install @powerhousedao/ph-cli and re-run manually inside ${appFolder}/.`);
46
- return { ran: false, reason: 'ph-not-on-path' };
47
- }
48
- // Pin ph init to the same version as the installed ph CLI, so the
49
- // generated app uses matching Powerhouse packages.
50
- const phVersion = options.phVersion ?? getPhVersion(binName);
51
- const tags = ['dev', 'staging', 'latest'];
52
- const versionArgs = phVersion
53
- ? tags.includes(phVersion)
54
- ? [`--${phVersion}`]
55
- : ['--version', phVersion]
56
- : ['--dev']; // fallback if detection fails
57
- // `ph init {name}` creates a fresh directory beside the cwd. Our generator
58
- // placed a `.gitkeep` + README.md placeholder there; clear it out so
59
- // ph init has a blank slate to work with.
60
- await fs.rm(options.appDir, { recursive: true, force: true });
61
- const args = ['init', appFolder, ...versionArgs, '--pnpm'];
62
- log(`Running \`${binName} init ${appFolder}\` (version: ${phVersion ?? 'auto'}) in ${options.targetDir} …`);
63
- const result = await options.runProcess(`${binName} ${args.join(' ')}`, {
64
- cwd: options.targetDir,
65
- timeout: 300_000,
66
- });
67
- const exitCode = result.success ? 0 : 1;
68
- if (!result.success) {
69
- log(`\`${binName} init\` failed. ` +
70
- 'You can re-run it manually after resolving the issue.');
71
- }
72
- await patchScopedName(options, appFolder, log);
73
- return { ran: true, exitCode };
74
- }
75
- /**
76
- * Patch the app `package.json` name to the scoped form (`@scope/name-app`).
77
- * Called after `ph init` and after name/scope renames.
78
- *
79
- * Returns `true` if the file was actually changed.
80
- */
81
- export async function patchAppPackageName(appDir, spec, log) {
82
- const appFolder = path.basename(appDir);
83
- const scopedName = spec.scope
84
- ? `${spec.scope}/${appFolder}`
85
- : appFolder;
86
- const appPkgJsonPath = path.join(appDir, 'package.json');
87
- try {
88
- const raw = await fs.readFile(appPkgJsonPath, 'utf8');
89
- const pkg = JSON.parse(raw);
90
- if (pkg.name !== scopedName) {
91
- pkg.name = scopedName;
92
- if (spec.scope) {
93
- pkg.publishConfig = { access: 'public' };
94
- }
95
- await fs.writeFile(appPkgJsonPath, JSON.stringify(pkg, null, 2) + '\n');
96
- log?.(`Patched app package name to ${scopedName}`);
97
- return true;
98
- }
99
- return false;
100
- }
101
- catch (err) {
102
- log?.(`Warning: could not patch app package name: ${err instanceof Error ? err.message : String(err)}`);
103
- return false;
104
- }
105
- }
106
- async function patchScopedName(options, _appFolder, log) {
107
- await patchAppPackageName(options.appDir, options.spec, log);
108
- }
109
- /**
110
- * Run `ph install <packages> --local --pnpm` in the app directory to register
111
- * external packages in `powerhouse.config.json`.
112
- */
113
- export async function runPhInstallPackages(options) {
114
- const { appDir, packages, log = () => { }, runProcess } = options;
115
- const binName = 'ph';
116
- if (!(await hasCommandOnPath(binName))) {
117
- log(`Skipping — \`${binName}\` is not on PATH.`);
118
- return false;
119
- }
120
- const args = ['install', ...packages, '--local', '--pnpm'];
121
- log(`Running \`${binName} install ${packages.join(' ')} --local\` in ${path.basename(appDir)} …`);
122
- const result = await runProcess(`${binName} ${args.join(' ')}`, {
123
- cwd: appDir,
124
- timeout: 300_000,
125
- });
126
- return result.success;
127
- }
128
- export async function runPnpmInstall(options) {
129
- const log = options.log ?? (() => { });
130
- const binName = options.binName ?? 'pnpm';
131
- if (!(await hasCommandOnPath(binName))) {
132
- log(`Skipping \`${binName} install\` — \`${binName}\` is not on PATH. ` +
133
- 'Install pnpm and re-run manually.');
134
- return { ran: [], skipped: options.dirs, reason: 'pnpm-not-on-path' };
135
- }
136
- const ran = [];
137
- for (const dir of options.dirs) {
138
- log(`Running \`${binName} install\` in ${dir} …`);
139
- const result = await options.runProcess(`${binName} install`, {
140
- cwd: dir,
141
- timeout: 300_000,
142
- });
143
- if (!result.success) {
144
- log(`\`${binName} install\` failed in ${dir}. ` +
145
- 'Retry manually once the underlying issue is resolved.');
146
- }
147
- ran.push(dir);
148
- }
149
- return { ran, skipped: [] };
150
- }
1
+ export { getPhVersion, runPhInit, patchAppPackageName, runPnpmInstall, runPhInstallPackages, } from '@powerhousedao/ph-clint-dev/codegen/scaffold';
@@ -1,6 +1 @@
1
- export declare function writeFileEnsuringDir(filePath: string, content: string): Promise<void>;
2
- /**
3
- * Returns true if the directory doesn't exist or only contains ignorable
4
- * entries (`.git`, `.DS_Store`, `.ph/ph-clint-cli/project-spec.json`).
5
- */
6
- export declare function isDirEmptyEnough(dir: string): Promise<boolean>;
1
+ export { writeFileEnsuringDir, isDirEmptyEnough, } from '@powerhousedao/ph-clint-dev/codegen/write';
@@ -1,32 +1 @@
1
- /**
2
- * Low-level file-writing helpers for the code generator.
3
- */
4
- import fs from 'node:fs/promises';
5
- import path from 'node:path';
6
- export async function writeFileEnsuringDir(filePath, content) {
7
- await fs.mkdir(path.dirname(filePath), { recursive: true });
8
- await fs.writeFile(filePath, content, 'utf8');
9
- }
10
- function isEnoent(err) {
11
- return (!!err &&
12
- typeof err === 'object' &&
13
- 'code' in err &&
14
- err.code === 'ENOENT');
15
- }
16
- /**
17
- * Returns true if the directory doesn't exist or only contains ignorable
18
- * entries (`.git`, `.DS_Store`, `.ph/ph-clint-cli/project-spec.json`).
19
- */
20
- export async function isDirEmptyEnough(dir) {
21
- let entries;
22
- try {
23
- entries = await fs.readdir(dir);
24
- }
25
- catch (err) {
26
- if (isEnoent(err))
27
- return true;
28
- throw err;
29
- }
30
- const ignorable = new Set(['.git', '.DS_Store', '.ph']);
31
- return entries.every((entry) => ignorable.has(entry));
32
- }
1
+ export { writeFileEnsuringDir, isDirEmptyEnough, } from '@powerhousedao/ph-clint-dev/codegen/write';
@@ -16,8 +16,8 @@ import { defineCommand } from '../framework.js';
16
16
  import { z } from 'zod';
17
17
  import { clintProjectSpecSchema, } from '../spec/types.js';
18
18
  import { generateProject } from '../codegen/index.js';
19
- import { isDirEmptyEnough } from '../codegen/write.js';
20
- import { runPostGenActions } from '../codegen/actions.js';
19
+ import { isDirEmptyEnough } from '@powerhousedao/ph-clint-dev/codegen/write';
20
+ import { runPostGenActions } from '@powerhousedao/ph-clint-dev/codegen/actions';
21
21
  import { ensureSpecDocument } from '../spec/ensure-document.js';
22
22
  const inputSchema = z.object({
23
23
  dir: z
@@ -11,7 +11,7 @@ import { defineCommand } from '../framework.js';
11
11
  import { z } from 'zod';
12
12
  import { readProjectSpec } from '../spec/file.js';
13
13
  import { generateProject } from '../codegen/index.js';
14
- import { runPostGenActions } from '../codegen/actions.js';
14
+ import { runPostGenActions } from '@powerhousedao/ph-clint-dev/codegen/actions';
15
15
  import { ensureSpecDocument } from '../spec/ensure-document.js';
16
16
  const inputSchema = z.object({
17
17
  dir: z
@@ -6,7 +6,7 @@ export declare const skillsSync: import("@powerhousedao/ph-clint").Command<z.Zod
6
6
  data?: undefined;
7
7
  } | {
8
8
  text: string;
9
- data: import("../skills/sync.js").SyncResult;
9
+ data: import("@powerhousedao/ph-clint-dev/skills/sync").SyncResult;
10
10
  }, {
11
11
  model: string;
12
12
  devServicePort: number;
@@ -7,7 +7,7 @@ import path from 'node:path';
7
7
  import { defineCommand } from '../framework.js';
8
8
  import { z } from 'zod';
9
9
  import { readProjectSpec } from '../spec/file.js';
10
- import { syncExternalSkills } from '../skills/sync.js';
10
+ import { syncExternalSkills } from '@powerhousedao/ph-clint-dev/skills/sync';
11
11
  const inputSchema = z.object({
12
12
  dir: z
13
13
  .string()
package/dist/framework.js CHANGED
@@ -23,7 +23,7 @@ export const configSchema = z.object({
23
23
  .describe('Port reserved for the impl project (Service B) `pnpm dev`'),
24
24
  phVersion: z
25
25
  .string()
26
- .default('6.0.0-dev.170')
26
+ .default('latest')
27
27
  .describe('Pinned Powerhouse version used when emitting a new impl project'),
28
28
  });
29
29
  export const secretsSchema = z.object({
@@ -1,40 +1 @@
1
- import type { ExternalSkill } from '../spec/types.js';
2
- /** On-disk manifest entry (what is currently installed). */
3
- export interface ManifestEntry {
4
- name: string;
5
- githubUrl: string;
6
- }
7
- /** Persisted manifest — a simple JSON array. */
8
- export type SkillsManifest = ManifestEntry[];
9
- export interface SyncResult {
10
- added: string[];
11
- removed: string[];
12
- unchanged: string[];
13
- }
14
- export interface SyncOptions {
15
- /** Project root (CLI sub-tree when split layout). */
16
- targetDir: string;
17
- /** Desired state from the spec. */
18
- desired: ExternalSkill[];
19
- /** Optional log callback. */
20
- log?: (msg: string) => void;
21
- }
22
- export declare function readManifest(targetDir: string): Promise<SkillsManifest>;
23
- /**
24
- * Compute the diff between desired (spec) and current (manifest) skills.
25
- *
26
- * Pure function — no I/O.
27
- */
28
- export declare function diffSkills(desired: ExternalSkill[], current: SkillsManifest): {
29
- toAdd: ExternalSkill[];
30
- toRemove: ManifestEntry[];
31
- unchanged: string[];
32
- };
33
- /**
34
- * Synchronise external skills on disk to match the spec.
35
- *
36
- * - Clones missing skills via `git clone --depth 1`.
37
- * - Removes skills no longer in the spec.
38
- * - Updates the `.skills-manifest.json`.
39
- */
40
- export declare function syncExternalSkills(opts: SyncOptions): Promise<SyncResult>;
1
+ export { type ManifestEntry, type SkillsManifest, type SyncResult, type SyncOptions, readManifest, diffSkills, syncExternalSkills, } from '@powerhousedao/ph-clint-dev/skills/sync';
@@ -1,119 +1 @@
1
- /**
2
- * External skills synchronisation.
3
- *
4
- * Reads the desired external skills from a `ClintProjectSpec`, diffs them
5
- * against a `.skills-manifest.json` on disk, and runs `git clone` / `rm -rf`
6
- * to reconcile. Each external skill ends up as a subdirectory of
7
- * `prompts/skills-ext/<name>/` so the build-skills pipeline picks it up.
8
- */
9
- import { spawn } from 'node:child_process';
10
- import fs from 'node:fs/promises';
11
- import path from 'node:path';
12
- const MANIFEST_FILE = '.skills-manifest.json';
13
- const SKILLS_DIR = path.join('prompts', 'skills-ext');
14
- function manifestPath(targetDir) {
15
- return path.join(targetDir, SKILLS_DIR, MANIFEST_FILE);
16
- }
17
- export async function readManifest(targetDir) {
18
- try {
19
- const raw = await fs.readFile(manifestPath(targetDir), 'utf8');
20
- return JSON.parse(raw);
21
- }
22
- catch {
23
- return [];
24
- }
25
- }
26
- async function writeManifest(targetDir, manifest) {
27
- const file = manifestPath(targetDir);
28
- await fs.mkdir(path.dirname(file), { recursive: true });
29
- await fs.writeFile(file, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
30
- }
31
- function execGit(args, cwd) {
32
- return new Promise((resolve, reject) => {
33
- const proc = spawn('git', args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
34
- let stderr = '';
35
- if (proc.stderr) {
36
- proc.stderr.on('data', (d) => {
37
- stderr += d.toString();
38
- });
39
- }
40
- proc.on('close', (code) => resolve({ code: code ?? 1, stderr }));
41
- proc.on('error', (err) => reject(err));
42
- });
43
- }
44
- /**
45
- * Compute the diff between desired (spec) and current (manifest) skills.
46
- *
47
- * Pure function — no I/O.
48
- */
49
- export function diffSkills(desired, current) {
50
- const desiredMap = new Map(desired.map((s) => [s.name, s]));
51
- const currentMap = new Map(current.map((s) => [s.name, s]));
52
- const toAdd = [];
53
- const toRemove = [];
54
- const unchanged = [];
55
- for (const [name, skill] of desiredMap) {
56
- const existing = currentMap.get(name);
57
- if (!existing) {
58
- toAdd.push(skill);
59
- }
60
- else if (existing.githubUrl !== skill.githubUrl) {
61
- // URL changed — remove old, add new.
62
- toRemove.push(existing);
63
- toAdd.push(skill);
64
- }
65
- else {
66
- unchanged.push(name);
67
- }
68
- }
69
- for (const [name, entry] of currentMap) {
70
- if (!desiredMap.has(name)) {
71
- toRemove.push(entry);
72
- }
73
- }
74
- return { toAdd, toRemove, unchanged };
75
- }
76
- /**
77
- * Synchronise external skills on disk to match the spec.
78
- *
79
- * - Clones missing skills via `git clone --depth 1`.
80
- * - Removes skills no longer in the spec.
81
- * - Updates the `.skills-manifest.json`.
82
- */
83
- export async function syncExternalSkills(opts) {
84
- const { targetDir, desired, log } = opts;
85
- const skillsDir = path.join(targetDir, SKILLS_DIR);
86
- await fs.mkdir(skillsDir, { recursive: true });
87
- const current = await readManifest(targetDir);
88
- const { toAdd, toRemove, unchanged } = diffSkills(desired, current);
89
- const result = { added: [], removed: [], unchanged };
90
- // Remove skills.
91
- for (const entry of toRemove) {
92
- const dir = path.join(skillsDir, entry.name);
93
- log?.(`Removing skill "${entry.name}"…`);
94
- await fs.rm(dir, { recursive: true, force: true });
95
- result.removed.push(entry.name);
96
- }
97
- // Add skills.
98
- for (const skill of toAdd) {
99
- const dir = path.join(skillsDir, skill.name);
100
- log?.(`Cloning skill "${skill.name}" from ${skill.githubUrl}…`);
101
- // Ensure the target doesn't exist (re-add after URL change).
102
- await fs.rm(dir, { recursive: true, force: true });
103
- const { code, stderr } = await execGit(['clone', '--depth', '1', skill.githubUrl, skill.name], skillsDir);
104
- if (code !== 0) {
105
- log?.(`Warning: git clone failed for "${skill.name}": ${stderr.trim()}`);
106
- continue;
107
- }
108
- // Remove .git dir — we don't want a nested repo.
109
- await fs.rm(path.join(dir, '.git'), { recursive: true, force: true });
110
- result.added.push(skill.name);
111
- }
112
- // Write updated manifest.
113
- const newManifest = desired.map((s) => ({
114
- name: s.name,
115
- githubUrl: s.githubUrl,
116
- }));
117
- await writeManifest(targetDir, newManifest);
118
- return result;
119
- }
1
+ export { readManifest, diffSkills, syncExternalSkills, } from '@powerhousedao/ph-clint-dev/skills/sync';
@@ -28,30 +28,24 @@ export async function ensureSpecDocument(opts) {
28
28
  if (inDrive)
29
29
  return { docId: spec.documentId, created: false };
30
30
  }
31
- // Reuse existing document if it's still in the reactor, otherwise create new
32
31
  let docId;
33
32
  if (spec.documentId) {
34
33
  try {
35
- const existing = await reactor.client.get(spec.documentId);
36
- if (existing) {
37
- docId = spec.documentId;
38
- }
39
- else {
40
- const newDoc = await reactor.client.createEmpty(DOCUMENT_TYPE);
41
- docId = newDoc.header.id;
42
- }
34
+ await reactor.client.get(spec.documentId);
35
+ docId = spec.documentId;
36
+ // Orphan recovery: doc exists in reactor but not in drive. Re-attach via
37
+ // addDocument; we can't retrofit CREATE/UPGRADE into the drive's stream.
38
+ await folders.addDocument(docId, `specs/${spec.name}`, spec.name);
43
39
  }
44
40
  catch {
45
- const newDoc = await reactor.client.createEmpty(DOCUMENT_TYPE);
46
- docId = newDoc.header.id;
41
+ const created = await folders.createDocument(DOCUMENT_TYPE, `specs/${spec.name}`, spec.name);
42
+ docId = created.documentId;
47
43
  }
48
44
  }
49
45
  else {
50
- const newDoc = await reactor.client.createEmpty(DOCUMENT_TYPE);
51
- docId = newDoc.header.id;
46
+ const created = await folders.createDocument(DOCUMENT_TYPE, `specs/${spec.name}`, spec.name);
47
+ docId = created.documentId;
52
48
  }
53
- // Add to the drive's folder tree
54
- await folders.addDocument(docId, `specs/${spec.name}`, spec.name);
55
49
  const importInput = specToImportInput(spec);
56
50
  await reactor.client.execute(docId, 'main', [importSpec(importInput)]);
57
51
  // Persist the link back to disk
@@ -1,4 +1 @@
1
- import { type ClintProjectSpec } from './types.js';
2
- export declare function getSpecPath(projectDir: string): string;
3
- export declare function readProjectSpec(projectDir: string): Promise<ClintProjectSpec | null>;
4
- export declare function writeProjectSpec(projectDir: string, spec: ClintProjectSpec): Promise<void>;
1
+ export { getSpecPath, readProjectSpec, writeProjectSpec, } from '@powerhousedao/ph-clint-dev/spec/file';
package/dist/spec/file.js CHANGED
@@ -1,34 +1 @@
1
- /**
2
- * Persistence for `ClintProjectSpec`.
3
- * Stored at `{impl-project}/.ph/ph-clint-cli/project-spec.json`.
4
- */
5
- import fs from 'node:fs/promises';
6
- import path from 'node:path';
7
- import { clintProjectSpecSchema, } from './types.js';
8
- const SPEC_DIR = path.join('.ph', 'ph-clint-cli');
9
- const SPEC_FILE = 'project-spec.json';
10
- export function getSpecPath(projectDir) {
11
- return path.join(projectDir, SPEC_DIR, SPEC_FILE);
12
- }
13
- function isEnoent(err) {
14
- return (!!err &&
15
- typeof err === 'object' &&
16
- 'code' in err &&
17
- err.code === 'ENOENT');
18
- }
19
- export async function readProjectSpec(projectDir) {
20
- try {
21
- const raw = await fs.readFile(getSpecPath(projectDir), 'utf8');
22
- return clintProjectSpecSchema.parse(JSON.parse(raw));
23
- }
24
- catch (err) {
25
- if (isEnoent(err))
26
- return null;
27
- throw err;
28
- }
29
- }
30
- export async function writeProjectSpec(projectDir, spec) {
31
- const file = getSpecPath(projectDir);
32
- await fs.mkdir(path.dirname(file), { recursive: true });
33
- await fs.writeFile(file, JSON.stringify(spec, null, 2) + '\n', 'utf8');
34
- }
1
+ export { getSpecPath, readProjectSpec, writeProjectSpec, } from '@powerhousedao/ph-clint-dev/spec/file';
@@ -1,164 +1 @@
1
- /**
2
- * ClintProjectSpec — the in-memory shape that describes a ph-clint
3
- * implementation project. Persisted at
4
- * `{impl-project}/.ph/ph-clint-cli/project-spec.json` and consumed by
5
- * the code generator (src/codegen/).
6
- *
7
- * Intentionally minimal. Richer structure (services, skills, agents,
8
- * triggers, document-models) gets added in later phases as each
9
- * generator step actually needs it.
10
- */
11
- import { z } from 'zod';
12
- /**
13
- * Ordered Powerhouse integration level. Higher levels imply all lower ones:
14
- * Disabled → Reactor → Switchboard → Connect
15
- */
16
- export declare const POWERHOUSE_LEVELS: readonly ["Disabled", "Reactor", "Switchboard", "Connect"];
17
- export declare const powerhouseLevelSchema: z.ZodDefault<z.ZodEnum<{
18
- Disabled: "Disabled";
19
- Reactor: "Reactor";
20
- Switchboard: "Switchboard";
21
- Connect: "Connect";
22
- }>>;
23
- export type PowerhouseLevel = z.infer<typeof powerhouseLevelSchema>;
24
- /** Check whether `level` is at least `threshold` in the ordered enum. */
25
- export declare function phAtLeast(level: PowerhouseLevel, threshold: PowerhouseLevel): boolean;
26
- export declare const agentModelSchema: z.ZodObject<{
27
- id: z.ZodString;
28
- isDefault: z.ZodBoolean;
29
- }, z.core.$strip>;
30
- export type AgentModel = z.infer<typeof agentModelSchema>;
31
- export declare const agentProfileSchema: z.ZodObject<{
32
- id: z.ZodString;
33
- title: z.ZodString;
34
- content: z.ZodString;
35
- }, z.core.$strip>;
36
- export type AgentProfile = z.infer<typeof agentProfileSchema>;
37
- export declare const powerhousePackageSchema: z.ZodObject<{
38
- id: z.ZodString;
39
- packageName: z.ZodString;
40
- documentTypes: z.ZodDefault<z.ZodArray<z.ZodString>>;
41
- version: z.ZodDefault<z.ZodNullable<z.ZodString>>;
42
- }, z.core.$strip>;
43
- export type PowerhousePackage = z.infer<typeof powerhousePackageSchema>;
44
- export declare const externalSkillSchema: z.ZodObject<{
45
- id: z.ZodString;
46
- name: z.ZodString;
47
- githubUrl: z.ZodString;
48
- }, z.core.$strip>;
49
- export type ExternalSkill = z.infer<typeof externalSkillSchema>;
50
- export declare const clintProjectSpecSchema: z.ZodObject<{
51
- name: z.ZodString;
52
- scope: z.ZodOptional<z.ZodString>;
53
- version: z.ZodDefault<z.ZodString>;
54
- description: z.ZodDefault<z.ZodString>;
55
- features: z.ZodDefault<z.ZodObject<{
56
- powerhouse: z.ZodDefault<z.ZodEnum<{
57
- Disabled: "Disabled";
58
- Reactor: "Reactor";
59
- Switchboard: "Switchboard";
60
- Connect: "Connect";
61
- }>>;
62
- mastra: z.ZodDefault<z.ZodObject<{
63
- enabled: z.ZodDefault<z.ZodBoolean>;
64
- agentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
65
- agentName: z.ZodDefault<z.ZodNullable<z.ZodString>>;
66
- agentDescription: z.ZodDefault<z.ZodNullable<z.ZodString>>;
67
- agentImage: z.ZodDefault<z.ZodNullable<z.ZodString>>;
68
- models: z.ZodDefault<z.ZodArray<z.ZodObject<{
69
- id: z.ZodString;
70
- isDefault: z.ZodBoolean;
71
- }, z.core.$strip>>>;
72
- profiles: z.ZodDefault<z.ZodArray<z.ZodObject<{
73
- id: z.ZodString;
74
- title: z.ZodString;
75
- content: z.ZodString;
76
- }, z.core.$strip>>>;
77
- common: z.ZodDefault<z.ZodObject<{
78
- enableChat: z.ZodDefault<z.ZodBoolean>;
79
- }, z.core.$strip>>;
80
- }, z.core.$strip>>;
81
- routine: z.ZodDefault<z.ZodObject<{
82
- enabled: z.ZodDefault<z.ZodBoolean>;
83
- }, z.core.$strip>>;
84
- }, z.core.$strip>>;
85
- packages: z.ZodDefault<z.ZodArray<z.ZodObject<{
86
- id: z.ZodString;
87
- packageName: z.ZodString;
88
- documentTypes: z.ZodDefault<z.ZodArray<z.ZodString>>;
89
- version: z.ZodDefault<z.ZodNullable<z.ZodString>>;
90
- }, z.core.$strip>>>;
91
- externalSkills: z.ZodDefault<z.ZodArray<z.ZodObject<{
92
- id: z.ZodString;
93
- name: z.ZodString;
94
- githubUrl: z.ZodString;
95
- }, z.core.$strip>>>;
96
- deployment: z.ZodDefault<z.ZodObject<{
97
- proxyEnabled: z.ZodDefault<z.ZodBoolean>;
98
- supportedResources: z.ZodDefault<z.ZodArray<z.ZodString>>;
99
- }, z.core.$strip>>;
100
- documentId: z.ZodOptional<z.ZodString>;
101
- documentType: z.ZodOptional<z.ZodString>;
102
- }, z.core.$strip>;
103
- export type ClintProjectSpec = z.infer<typeof clintProjectSpecSchema>;
104
- export type ClintProjectSpecInput = z.input<typeof clintProjectSpecSchema>;
105
- /** npm package name (e.g. `@scope/my-tool-cli` or `my-tool-cli`). */
106
- export declare function getPackageName(spec: ClintProjectSpec): string;
107
- /** bin name (strips -cli suffix, e.g. `my-tool-cli` → `my-tool`). */
108
- export declare function getBinName(spec: ClintProjectSpec): string;
109
- /**
110
- * Name of the CLI sub-folder when the project is split (Powerhouse enabled).
111
- * For flat projects this is irrelevant — callers use the project root directly.
112
- */
113
- export declare function getCliFolderName(spec: ClintProjectSpec): string;
114
- /**
115
- * Name of the reactor-package sub-folder when the project is split.
116
- */
117
- export declare function getAppFolderName(spec: ClintProjectSpec): string;
118
- /**
119
- * Flat list of all document types across all packages. Convenience for
120
- * codegen that needs to iterate every registered document type regardless
121
- * of which package provides it.
122
- */
123
- export declare function getAllDocumentTypes(spec: ClintProjectSpec): string[];
124
- /**
125
- * The app npm package name (`@scope/my-tool-app` or `my-tool-app`).
126
- * Matches what the document model reducer stores in `packages[].packageName`.
127
- */
128
- export declare function getAppPackageName(spec: ClintProjectSpec): string;
129
- /** Directory name for the app package (e.g. `my-tool-app`, no scope prefix). */
130
- export declare function getAppDirName(spec: ClintProjectSpec): string;
131
- /**
132
- * Folder slug for a documentType ID. The slug is the portion after the last
133
- * `/` (e.g. `powerhouse/ph-clint-project` → `ph-clint-project`). Matches the
134
- * convention used by Powerhouse's reactor-package codegen — each document
135
- * model lives at `document-models/<slug>/`.
136
- */
137
- export declare function getDocumentTypeSlug(documentType: string): string;
138
- /**
139
- * PascalCase export name for a documentType ID. Powerhouse's module
140
- * generator exports each module as `PascalCase(<slug>)` (e.g.
141
- * `powerhouse/ph-clint-project` → `PhClintProject`). The top-level reactor
142
- * package re-exports these names, so this helper gives codegen the string
143
- * it needs both for the import and the `defineRegistry` entry.
144
- */
145
- export declare function getDocumentTypeModuleName(documentType: string): string;
146
- /**
147
- * Returns true when a documentType entry contains glob wildcards (`*`).
148
- * Glob entries like `*​/*`, `powerhouse/*`, or `powerhouse/doc-*` are spec
149
- * shorthand meaning "all matching document types from this package" and
150
- * must be resolved at runtime rather than emitted as static imports.
151
- */
152
- export declare function isDocTypeGlob(documentType: string): boolean;
153
- /**
154
- * Convert a documentType glob pattern to a regex source string suitable
155
- * for embedding in a `/pattern/` literal. Each `*` matches any non-`/`
156
- * characters. Forward slashes are escaped so the output is safe inside
157
- * `/.../`.
158
- *
159
- * Examples:
160
- * `*​/*` → `^[^\/]*\/[^\/]*$` (any org, any name)
161
- * `powerhouse/*` → `^powerhouse\/[^\/]*$`
162
- * `powerhouse/doc-*` → `^powerhouse\/doc-[^\/]*$`
163
- */
164
- export declare function docTypeGlobToRegex(pattern: string): string;
1
+ export { POWERHOUSE_LEVELS, powerhouseLevelSchema, type PowerhouseLevel, phAtLeast, agentModelSchema, type AgentModel, agentProfileSchema, type AgentProfile, powerhousePackageSchema, type PowerhousePackage, externalSkillSchema, type ExternalSkill, clintProjectSpecSchema, type ClintProjectSpec, type ClintProjectSpecInput, getPackageName, getBinName, getCliFolderName, getAppFolderName, getAllDocumentTypes, getAppPackageName, getAppDirName, getDocumentTypeSlug, getDocumentTypeModuleName, isDocTypeGlob, docTypeGlobToRegex, } from '@powerhousedao/ph-clint-dev/spec';