@funkai/cli 0.2.0 → 0.3.1
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/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +39 -0
- package/README.md +11 -11
- package/dist/index.mjs +640 -267
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -4
- package/src/commands/generate.ts +23 -1
- package/src/commands/prompts/create.ts +33 -2
- package/src/commands/prompts/generate.ts +97 -12
- package/src/commands/prompts/lint.ts +69 -7
- package/src/commands/prompts/setup.ts +103 -95
- package/src/commands/setup.ts +151 -4
- package/src/commands/validate.ts +20 -2
- package/src/config.ts +28 -0
- package/src/index.ts +4 -0
- package/src/lib/prompts/__tests__/lint.test.ts +36 -24
- package/src/lib/prompts/codegen.ts +112 -43
- package/src/lib/prompts/flatten.ts +10 -5
- package/src/lib/prompts/frontmatter.ts +24 -8
- package/src/lib/prompts/lint.ts +31 -10
- package/src/lib/prompts/paths.ts +71 -18
- package/src/lib/prompts/pipeline.ts +112 -14
- package/tsconfig.json +11 -3
package/dist/index.mjs
CHANGED
|
@@ -10,21 +10,23 @@ import * as k$2 from "node:readline";
|
|
|
10
10
|
import ot from "node:readline";
|
|
11
11
|
import { ReadStream } from "node:tty";
|
|
12
12
|
import fs, { existsSync, lstatSync, mkdirSync, promises, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "node:fs";
|
|
13
|
-
import path, { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
14
|
-
import { attempt, attemptAsync } from "es-toolkit";
|
|
13
|
+
import path, { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
14
|
+
import { attempt, attemptAsync, isNil } from "es-toolkit";
|
|
15
15
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
16
16
|
import { URL as URL$1, fileURLToPath, pathToFileURL } from "node:url";
|
|
17
17
|
import { homedir } from "node:os";
|
|
18
18
|
import assert from "node:assert";
|
|
19
19
|
import v8 from "node:v8";
|
|
20
20
|
import { readFileSync as readFileSync$1, readdirSync as readdirSync$1, statSync as statSync$1, writeFile as writeFile$1 } from "fs";
|
|
21
|
-
import { basename as basename$1, dirname as dirname$1, extname as extname$1, join as join$1, normalize, relative, resolve as resolve$1 } from "path";
|
|
21
|
+
import { basename as basename$1, dirname as dirname$1, extname as extname$1, join as join$1, normalize, relative as relative$1, resolve as resolve$1 } from "path";
|
|
22
22
|
import { createHash } from "node:crypto";
|
|
23
23
|
import { parse, stringify } from "yaml";
|
|
24
24
|
import { notStrictEqual, strictEqual } from "assert";
|
|
25
25
|
import { format as format$1, inspect as inspect$1 } from "util";
|
|
26
26
|
import { fileURLToPath as fileURLToPath$1 } from "url";
|
|
27
|
+
import { configSchema } from "@funkai/config";
|
|
27
28
|
import { PARTIALS_DIR, clean } from "@funkai/prompts/cli";
|
|
29
|
+
import picomatch from "picomatch";
|
|
28
30
|
import { Liquid } from "liquidjs";
|
|
29
31
|
|
|
30
32
|
//#region \0rolldown/runtime.js
|
|
@@ -140,7 +142,7 @@ var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
140
142
|
}));
|
|
141
143
|
|
|
142
144
|
//#endregion
|
|
143
|
-
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+
|
|
145
|
+
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+ap_ac2wgu34upx3xtuhxg7cvmcnvm/node_modules/@kidd-cli/core/dist/tally-KfEitTrZ.js
|
|
144
146
|
/**
|
|
145
147
|
* Format a duration in milliseconds to a human-readable string.
|
|
146
148
|
*
|
|
@@ -2152,7 +2154,7 @@ ${r ? styleText("cyan", x$2) : ""}
|
|
|
2152
2154
|
}));
|
|
2153
2155
|
|
|
2154
2156
|
//#endregion
|
|
2155
|
-
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+
|
|
2157
|
+
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+ap_ac2wgu34upx3xtuhxg7cvmcnvm/node_modules/@kidd-cli/core/dist/lib/logger.js
|
|
2156
2158
|
/**
|
|
2157
2159
|
* Create a new {@link CliLogger} instance.
|
|
2158
2160
|
*
|
|
@@ -2308,7 +2310,7 @@ var init_json$1 = __esmMin((() => {
|
|
|
2308
2310
|
}));
|
|
2309
2311
|
|
|
2310
2312
|
//#endregion
|
|
2311
|
-
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+
|
|
2313
|
+
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+ap_ac2wgu34upx3xtuhxg7cvmcnvm/node_modules/@kidd-cli/core/dist/create-context-vWwSL8R5.js
|
|
2312
2314
|
/**
|
|
2313
2315
|
* Create a ContextError with an exit code and optional error code.
|
|
2314
2316
|
*
|
|
@@ -12621,7 +12623,7 @@ var init_dist$1 = __esmMin((() => {
|
|
|
12621
12623
|
}));
|
|
12622
12624
|
|
|
12623
12625
|
//#endregion
|
|
12624
|
-
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+
|
|
12626
|
+
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+ap_ac2wgu34upx3xtuhxg7cvmcnvm/node_modules/@kidd-cli/core/dist/config-BSyREvk7.js
|
|
12625
12627
|
/**
|
|
12626
12628
|
* Determine the config format from a file path's extension.
|
|
12627
12629
|
*
|
|
@@ -15337,7 +15339,7 @@ var init_esm = __esmMin((() => {
|
|
|
15337
15339
|
basename: basename$1,
|
|
15338
15340
|
dirname: dirname$1,
|
|
15339
15341
|
extname: extname$1,
|
|
15340
|
-
relative,
|
|
15342
|
+
relative: relative$1,
|
|
15341
15343
|
resolve: resolve$1,
|
|
15342
15344
|
join: join$1
|
|
15343
15345
|
},
|
|
@@ -18295,6 +18297,28 @@ var init_yargs = __esmMin((() => {
|
|
|
18295
18297
|
Yargs = YargsFactory(esm_default);
|
|
18296
18298
|
}));
|
|
18297
18299
|
|
|
18300
|
+
//#endregion
|
|
18301
|
+
//#region src/config.ts
|
|
18302
|
+
/**
|
|
18303
|
+
* Extract the typed funkai config from a command context.
|
|
18304
|
+
*
|
|
18305
|
+
* kidd-cli's `Merge<CliConfig, TConfig>` erases augmented keys when
|
|
18306
|
+
* `TConfig` defaults to `Record<string, unknown>`, so we cast here.
|
|
18307
|
+
*
|
|
18308
|
+
* @param ctx - The CLI context.
|
|
18309
|
+
* @returns The typed funkai configuration.
|
|
18310
|
+
*
|
|
18311
|
+
* @example
|
|
18312
|
+
* ```ts
|
|
18313
|
+
* const config = getConfig(ctx);
|
|
18314
|
+
* const promptsConfig = config.prompts;
|
|
18315
|
+
* ```
|
|
18316
|
+
*/
|
|
18317
|
+
function getConfig(ctx) {
|
|
18318
|
+
return ctx.config;
|
|
18319
|
+
}
|
|
18320
|
+
var init_config = __esmMin((() => {}));
|
|
18321
|
+
|
|
18298
18322
|
//#endregion
|
|
18299
18323
|
//#region src/lib/prompts/codegen.ts
|
|
18300
18324
|
/**
|
|
@@ -18336,6 +18360,11 @@ function formatGroupValue(group) {
|
|
|
18336
18360
|
return "undefined";
|
|
18337
18361
|
}
|
|
18338
18362
|
/** @private */
|
|
18363
|
+
function formatGroupJsdoc(group) {
|
|
18364
|
+
if (group) return [" *", ` * @group ${group}`];
|
|
18365
|
+
return [];
|
|
18366
|
+
}
|
|
18367
|
+
/** @private */
|
|
18339
18368
|
function parseGroupSegments(group) {
|
|
18340
18369
|
if (group) return group.split("/").map(toCamelCase);
|
|
18341
18370
|
return [];
|
|
@@ -18353,22 +18382,68 @@ function generateSchemaExpression(vars) {
|
|
|
18353
18382
|
return ` ${v.name}: ${expr},`;
|
|
18354
18383
|
}).join("\n")}\n})`;
|
|
18355
18384
|
}
|
|
18385
|
+
/** @private */
|
|
18386
|
+
function formatHeader(sourcePath) {
|
|
18387
|
+
return [
|
|
18388
|
+
"// ─── AUTO-GENERATED ────────────────────────────────────────",
|
|
18389
|
+
`${match(sourcePath).with(void 0, () => "").otherwise((p) => `// Source: ${p}\n`)}// Regenerate: funkai prompts generate`,
|
|
18390
|
+
"// ───────────────────────────────────────────────────────────"
|
|
18391
|
+
].join("\n");
|
|
18392
|
+
}
|
|
18393
|
+
/**
|
|
18394
|
+
* Derive a unique file slug from group + name.
|
|
18395
|
+
*
|
|
18396
|
+
* Ungrouped prompts use the name alone. Grouped prompts
|
|
18397
|
+
* join group segments and name with hyphens.
|
|
18398
|
+
*
|
|
18399
|
+
* @param name - The prompt name (kebab-case).
|
|
18400
|
+
* @param group - Optional group path (e.g., 'core/agent').
|
|
18401
|
+
* @returns The file slug string.
|
|
18402
|
+
*
|
|
18403
|
+
* @example
|
|
18404
|
+
* ```ts
|
|
18405
|
+
* toFileSlug('system', 'core/agent') // => 'core-agent-system'
|
|
18406
|
+
* toFileSlug('greeting', undefined) // => 'greeting'
|
|
18407
|
+
* ```
|
|
18408
|
+
*/
|
|
18409
|
+
function toFileSlug(name, group) {
|
|
18410
|
+
if (group) return `${group.replaceAll("/", "-")}-${name}`;
|
|
18411
|
+
return name;
|
|
18412
|
+
}
|
|
18413
|
+
/**
|
|
18414
|
+
* Derive a unique import name (camelCase) from group + name.
|
|
18415
|
+
*
|
|
18416
|
+
* @param name - The prompt name (kebab-case).
|
|
18417
|
+
* @param group - Optional group path (e.g., 'core/agent').
|
|
18418
|
+
* @returns The camelCase import identifier.
|
|
18419
|
+
*
|
|
18420
|
+
* @example
|
|
18421
|
+
* ```ts
|
|
18422
|
+
* toImportName('system', 'core/agent') // => 'coreAgentSystem'
|
|
18423
|
+
* toImportName('greeting', undefined) // => 'greeting'
|
|
18424
|
+
* ```
|
|
18425
|
+
*/
|
|
18426
|
+
function toImportName(name, group) {
|
|
18427
|
+
return toCamelCase(toFileSlug(name, group));
|
|
18428
|
+
}
|
|
18356
18429
|
/**
|
|
18357
18430
|
* Generate a per-prompt TypeScript module with a default export.
|
|
18358
18431
|
*
|
|
18359
|
-
* The module
|
|
18360
|
-
*
|
|
18432
|
+
* The module uses `createPrompt` from `@funkai/prompts` to
|
|
18433
|
+
* encapsulate the Zod schema, inlined template, and render logic.
|
|
18434
|
+
*
|
|
18435
|
+
* @param prompt - The parsed prompt configuration.
|
|
18436
|
+
* @returns The generated TypeScript module source code.
|
|
18361
18437
|
*/
|
|
18362
18438
|
function generatePromptModule(prompt) {
|
|
18363
18439
|
const escaped = escapeTemplateLiteral(prompt.template);
|
|
18364
18440
|
const schemaExpr = generateSchemaExpression(prompt.schema);
|
|
18365
18441
|
const groupValue = formatGroupValue(prompt.group);
|
|
18366
18442
|
return [
|
|
18367
|
-
|
|
18368
|
-
`// Source: ${prompt.sourcePath}`,
|
|
18443
|
+
formatHeader(prompt.sourcePath),
|
|
18369
18444
|
"",
|
|
18370
18445
|
"import { z } from 'zod'",
|
|
18371
|
-
"import {
|
|
18446
|
+
"import { createPrompt } from '@funkai/prompts'",
|
|
18372
18447
|
"",
|
|
18373
18448
|
`const schema = ${schemaExpr}`,
|
|
18374
18449
|
"",
|
|
@@ -18376,32 +18451,25 @@ function generatePromptModule(prompt) {
|
|
|
18376
18451
|
"",
|
|
18377
18452
|
`const template = \`${escaped}\``,
|
|
18378
18453
|
"",
|
|
18379
|
-
"
|
|
18380
|
-
`
|
|
18454
|
+
"/**",
|
|
18455
|
+
` * **${prompt.name}** prompt module.`,
|
|
18456
|
+
...formatGroupJsdoc(prompt.group),
|
|
18457
|
+
" */",
|
|
18458
|
+
"export default createPrompt<Variables>({",
|
|
18459
|
+
` name: '${prompt.name}',`,
|
|
18381
18460
|
` group: ${groupValue},`,
|
|
18461
|
+
" template,",
|
|
18382
18462
|
" schema,",
|
|
18383
|
-
|
|
18384
|
-
" render(variables?: undefined): string {",
|
|
18385
|
-
" return liquidEngine.parseAndRenderSync(template, {})",
|
|
18386
|
-
" },",
|
|
18387
|
-
" validate(variables?: undefined): Variables {",
|
|
18388
|
-
" return schema.parse(variables ?? {})",
|
|
18389
|
-
" },"
|
|
18390
|
-
]).otherwise(() => [
|
|
18391
|
-
" render(variables: Variables): string {",
|
|
18392
|
-
" return liquidEngine.parseAndRenderSync(template, schema.parse(variables))",
|
|
18393
|
-
" },",
|
|
18394
|
-
" validate(variables: unknown): Variables {",
|
|
18395
|
-
" return schema.parse(variables)",
|
|
18396
|
-
" },"
|
|
18397
|
-
]),
|
|
18398
|
-
"}",
|
|
18463
|
+
"})",
|
|
18399
18464
|
""
|
|
18400
18465
|
].join("\n");
|
|
18401
18466
|
}
|
|
18402
18467
|
/**
|
|
18403
18468
|
* Build a nested tree from sorted prompts, grouped by their `group` field.
|
|
18404
18469
|
*
|
|
18470
|
+
* Leaf values are the unique import name derived from group+name,
|
|
18471
|
+
* so prompts with the same name in different groups do not collide.
|
|
18472
|
+
*
|
|
18405
18473
|
* @param prompts - Sorted parsed prompts.
|
|
18406
18474
|
* @returns A tree where leaves are import names and branches are group namespaces.
|
|
18407
18475
|
* @throws If a prompt name collides with a group namespace at the same level.
|
|
@@ -18410,15 +18478,16 @@ function generatePromptModule(prompt) {
|
|
|
18410
18478
|
*/
|
|
18411
18479
|
function buildTree(prompts) {
|
|
18412
18480
|
return prompts.reduce((root, prompt) => {
|
|
18413
|
-
const
|
|
18481
|
+
const leafKey = toCamelCase(prompt.name);
|
|
18482
|
+
const importName = toImportName(prompt.name, prompt.group);
|
|
18414
18483
|
const target = parseGroupSegments(prompt.group).reduce((current, segment) => {
|
|
18415
18484
|
const existing = current[segment];
|
|
18416
18485
|
if (typeof existing === "string") throw new TypeError(`Collision: prompt "${existing}" and group namespace "${segment}" share the same key at the same level.`);
|
|
18417
18486
|
if (existing === null || existing === void 0) current[segment] = {};
|
|
18418
18487
|
return current[segment];
|
|
18419
18488
|
}, root);
|
|
18420
|
-
if (typeof target[
|
|
18421
|
-
target[
|
|
18489
|
+
if (typeof target[leafKey] === "object" && target[leafKey] !== null) throw new Error(`Collision: prompt "${leafKey}" conflicts with existing group namespace "${leafKey}" at the same level.`);
|
|
18490
|
+
target[leafKey] = importName;
|
|
18422
18491
|
return root;
|
|
18423
18492
|
}, {});
|
|
18424
18493
|
}
|
|
@@ -18433,7 +18502,10 @@ function buildTree(prompts) {
|
|
|
18433
18502
|
*/
|
|
18434
18503
|
function serializeTree(node, indent) {
|
|
18435
18504
|
const pad = " ".repeat(indent);
|
|
18436
|
-
return Object.entries(node).flatMap(([key, value]) => match(typeof value).with("string", () =>
|
|
18505
|
+
return Object.entries(node).flatMap(([key, value]) => match(typeof value).with("string", () => {
|
|
18506
|
+
if (key === value) return [`${pad}${key},`];
|
|
18507
|
+
return [`${pad}${key}: ${value},`];
|
|
18508
|
+
}).otherwise(() => [
|
|
18437
18509
|
`${pad}${key}: {`,
|
|
18438
18510
|
...serializeTree(value, indent + 1),
|
|
18439
18511
|
`${pad}},`
|
|
@@ -18445,34 +18517,41 @@ function serializeTree(node, indent) {
|
|
|
18445
18517
|
*
|
|
18446
18518
|
* Prompts are organized into a nested object structure based on their
|
|
18447
18519
|
* `group` field, with each `/`-separated segment becoming a nesting level.
|
|
18520
|
+
*
|
|
18521
|
+
* @param prompts - Sorted parsed prompts to include in the registry.
|
|
18522
|
+
* @returns The generated TypeScript source for the registry index module.
|
|
18523
|
+
*
|
|
18524
|
+
* @example
|
|
18525
|
+
* ```ts
|
|
18526
|
+
* const source = generateRegistry([
|
|
18527
|
+
* { name: 'system', group: 'core/agent', schema: [], template: '...', sourcePath: 'prompts/system.prompt' },
|
|
18528
|
+
* ])
|
|
18529
|
+
* writeFileSync('index.ts', source)
|
|
18530
|
+
* ```
|
|
18448
18531
|
*/
|
|
18449
18532
|
function generateRegistry(prompts) {
|
|
18450
|
-
const sorted = [...prompts].toSorted((a, b) =>
|
|
18533
|
+
const sorted = [...prompts].toSorted((a, b) => {
|
|
18534
|
+
const slugA = toFileSlug(a.name, a.group);
|
|
18535
|
+
const slugB = toFileSlug(b.name, b.group);
|
|
18536
|
+
return slugA.localeCompare(slugB);
|
|
18537
|
+
});
|
|
18538
|
+
const imports = sorted.map((p) => {
|
|
18539
|
+
return `import ${toImportName(p.name, p.group)} from './${toFileSlug(p.name, p.group)}.js'`;
|
|
18540
|
+
}).join("\n");
|
|
18541
|
+
const treeLines = serializeTree(buildTree(sorted), 1);
|
|
18451
18542
|
return [
|
|
18452
|
-
|
|
18543
|
+
formatHeader(),
|
|
18453
18544
|
"",
|
|
18454
18545
|
"import { createPromptRegistry } from '@funkai/prompts'",
|
|
18455
|
-
|
|
18546
|
+
imports,
|
|
18456
18547
|
"",
|
|
18457
18548
|
"export const prompts = createPromptRegistry({",
|
|
18458
|
-
...
|
|
18549
|
+
...treeLines,
|
|
18459
18550
|
"})",
|
|
18460
18551
|
""
|
|
18461
18552
|
].join("\n");
|
|
18462
18553
|
}
|
|
18463
|
-
var
|
|
18464
|
-
var init_codegen = __esmMin((() => {
|
|
18465
|
-
HEADER = [
|
|
18466
|
-
"/*",
|
|
18467
|
-
"|==========================================================================",
|
|
18468
|
-
"| AUTO-GENERATED — DO NOT EDIT",
|
|
18469
|
-
"|==========================================================================",
|
|
18470
|
-
"|",
|
|
18471
|
-
"| Run `funkai prompts generate` to regenerate.",
|
|
18472
|
-
"|",
|
|
18473
|
-
"*/"
|
|
18474
|
-
].join("\n");
|
|
18475
|
-
}));
|
|
18554
|
+
var init_codegen = __esmMin((() => {}));
|
|
18476
18555
|
|
|
18477
18556
|
//#endregion
|
|
18478
18557
|
//#region src/lib/prompts/lint.ts
|
|
@@ -18483,13 +18562,20 @@ var init_codegen = __esmMin((() => {
|
|
|
18483
18562
|
* - **Error**: template uses a variable NOT declared in the schema (undefined var).
|
|
18484
18563
|
* - **Warn**: schema declares a variable NOT used in the template (unused var).
|
|
18485
18564
|
*
|
|
18486
|
-
* @param
|
|
18487
|
-
* @param filePath - Source file path (for error messages).
|
|
18488
|
-
* @param schemaVars - Variables declared in frontmatter schema.
|
|
18489
|
-
* @param templateVars - Variables extracted from the template body.
|
|
18565
|
+
* @param params - Lint prompt parameters.
|
|
18490
18566
|
* @returns Lint result with diagnostics.
|
|
18567
|
+
*
|
|
18568
|
+
* @example
|
|
18569
|
+
* ```ts
|
|
18570
|
+
* const result = lintPrompt({
|
|
18571
|
+
* name: 'greeting',
|
|
18572
|
+
* filePath: 'prompts/greeting.prompt',
|
|
18573
|
+
* schemaVars: [{ name: 'name', type: 'string', required: true }],
|
|
18574
|
+
* templateVars: ['name'],
|
|
18575
|
+
* });
|
|
18576
|
+
* ```
|
|
18491
18577
|
*/
|
|
18492
|
-
function lintPrompt(name, filePath, schemaVars, templateVars) {
|
|
18578
|
+
function lintPrompt({ name, filePath, schemaVars, templateVars }) {
|
|
18493
18579
|
const declared = new Set(schemaVars.map((v) => v.name));
|
|
18494
18580
|
const used = new Set(templateVars);
|
|
18495
18581
|
const undeclaredErrors = [...used].filter((varName) => !declared.has(varName)).map((varName) => ({
|
|
@@ -18573,11 +18659,12 @@ function parseParamsOrEmpty(raw, partialName) {
|
|
|
18573
18659
|
*/
|
|
18574
18660
|
function parseParams(raw, partialName) {
|
|
18575
18661
|
const literalMatches = [...raw.matchAll(LITERAL_PARAM_RE)];
|
|
18576
|
-
const allParamNames = [...raw.matchAll(/(\w+)\s*:/g)].map((
|
|
18662
|
+
const allParamNames = [...raw.matchAll(/(\w+)\s*:/g)].map(([, m1]) => m1);
|
|
18577
18663
|
return Object.fromEntries(allParamNames.map((name) => {
|
|
18578
|
-
const literal = literalMatches.find((
|
|
18664
|
+
const literal = literalMatches.find(([, m1]) => m1 === name);
|
|
18579
18665
|
if (!literal) throw new Error(`Cannot flatten {% render '${partialName}' %}: parameter "${name}" uses a variable reference. Only literal string values are supported at codegen time.`);
|
|
18580
|
-
|
|
18666
|
+
const { 2: literalValue } = literal;
|
|
18667
|
+
return [name, literalValue];
|
|
18581
18668
|
}));
|
|
18582
18669
|
}
|
|
18583
18670
|
/** @private */
|
|
@@ -18605,10 +18692,12 @@ function renderPartial(engine, tag) {
|
|
|
18605
18692
|
*/
|
|
18606
18693
|
function parseRenderTags(template) {
|
|
18607
18694
|
return [...template.matchAll(RENDER_TAG_RE)].map((m) => {
|
|
18608
|
-
const
|
|
18695
|
+
const [, partialName] = m;
|
|
18696
|
+
if (partialName === void 0) throw new Error("Malformed render tag: missing partial name");
|
|
18697
|
+
const params = parseParamsOrEmpty((m[2] ?? "").trim(), partialName);
|
|
18609
18698
|
return {
|
|
18610
18699
|
fullMatch: m[0],
|
|
18611
|
-
partialName
|
|
18700
|
+
partialName,
|
|
18612
18701
|
params
|
|
18613
18702
|
};
|
|
18614
18703
|
});
|
|
@@ -18687,17 +18776,22 @@ function parseYamlContent(yaml, filePath) {
|
|
|
18687
18776
|
function parseFrontmatter({ content, filePath }) {
|
|
18688
18777
|
const fmMatch = content.match(FRONTMATTER_RE);
|
|
18689
18778
|
if (!fmMatch) throw new Error(`No frontmatter found in ${filePath}`);
|
|
18690
|
-
const
|
|
18779
|
+
const [, fmContent] = fmMatch;
|
|
18780
|
+
if (fmContent === void 0) throw new Error(`No frontmatter content found in ${filePath}`);
|
|
18781
|
+
const parsed = parseYamlContent(fmContent, filePath);
|
|
18691
18782
|
if (!parsed || typeof parsed !== "object") throw new Error(`Frontmatter is not a valid object in ${filePath}`);
|
|
18692
18783
|
const { name } = parsed;
|
|
18693
18784
|
if (typeof name !== "string" || name.length === 0) throw new Error(`Missing or empty "name" in frontmatter: ${filePath}`);
|
|
18694
18785
|
if (!NAME_RE.test(name)) throw new Error(`Invalid prompt name "${name}" in ${filePath}. Names must be lowercase alphanumeric with hyphens only.`);
|
|
18695
|
-
|
|
18786
|
+
const group = parseGroup(parsed["group"], filePath);
|
|
18787
|
+
const version = parseVersion(parsed["version"]);
|
|
18788
|
+
const result = {
|
|
18696
18789
|
name,
|
|
18697
|
-
|
|
18698
|
-
version: parseVersion(parsed.version),
|
|
18699
|
-
schema: parseSchemaBlock(parsed.schema, filePath)
|
|
18790
|
+
schema: parseSchemaBlock(parsed["schema"], filePath)
|
|
18700
18791
|
};
|
|
18792
|
+
if (group !== void 0) result.group = group;
|
|
18793
|
+
if (version !== void 0) result.version = version;
|
|
18794
|
+
return result;
|
|
18701
18795
|
}
|
|
18702
18796
|
/** @private */
|
|
18703
18797
|
function stringOrDefault(value, fallback) {
|
|
@@ -18743,9 +18837,9 @@ function parseSchemaBlock(raw, filePath) {
|
|
|
18743
18837
|
})).with(P.when((v) => typeof v === "object" && v !== null && !Array.isArray(v)), (def) => {
|
|
18744
18838
|
return {
|
|
18745
18839
|
name: varName,
|
|
18746
|
-
type: stringOrDefault(def
|
|
18747
|
-
required: def
|
|
18748
|
-
description: stringOrUndefined(def
|
|
18840
|
+
type: stringOrDefault(def["type"], "string"),
|
|
18841
|
+
required: def["required"] !== false,
|
|
18842
|
+
description: stringOrUndefined(def["description"])
|
|
18749
18843
|
};
|
|
18750
18844
|
}).otherwise(() => {
|
|
18751
18845
|
throw new Error(`Invalid schema definition for "${varName}" in ${filePath}. Expected a type string or an object with { type, required?, description? }.`);
|
|
@@ -18770,8 +18864,10 @@ function extractName(content) {
|
|
|
18770
18864
|
const fmMatch = content.match(FRONTMATTER_RE);
|
|
18771
18865
|
if (!fmMatch) return;
|
|
18772
18866
|
try {
|
|
18773
|
-
const
|
|
18774
|
-
if (
|
|
18867
|
+
const [, fmContent] = fmMatch;
|
|
18868
|
+
if (fmContent === void 0) return;
|
|
18869
|
+
const parsed = parse(fmContent);
|
|
18870
|
+
if (parsed !== null && parsed !== void 0 && typeof parsed["name"] === "string") return parsed["name"];
|
|
18775
18871
|
return;
|
|
18776
18872
|
} catch {
|
|
18777
18873
|
return;
|
|
@@ -18791,6 +18887,28 @@ function deriveNameFromPath(filePath) {
|
|
|
18791
18887
|
return stem;
|
|
18792
18888
|
}
|
|
18793
18889
|
/**
|
|
18890
|
+
* Extract the static base directory from a glob pattern.
|
|
18891
|
+
*
|
|
18892
|
+
* Returns the longest directory prefix before any glob characters
|
|
18893
|
+
* (`*`, `?`, `{`, `[`). Falls back to `'.'` if the pattern starts
|
|
18894
|
+
* with a glob character.
|
|
18895
|
+
*
|
|
18896
|
+
* @private
|
|
18897
|
+
*/
|
|
18898
|
+
function extractBaseDir(pattern) {
|
|
18899
|
+
const globChars = new Set([
|
|
18900
|
+
"*",
|
|
18901
|
+
"?",
|
|
18902
|
+
"{",
|
|
18903
|
+
"["
|
|
18904
|
+
]);
|
|
18905
|
+
const parts = pattern.split("/");
|
|
18906
|
+
const firstGlobIndex = parts.findIndex((part) => [...part].some((ch) => globChars.has(ch)));
|
|
18907
|
+
const staticParts = match(firstGlobIndex).with(-1, () => parts).otherwise(() => parts.slice(0, firstGlobIndex));
|
|
18908
|
+
if (staticParts.length === 0) return ".";
|
|
18909
|
+
return staticParts.join("/");
|
|
18910
|
+
}
|
|
18911
|
+
/**
|
|
18794
18912
|
* Recursively scan a directory for `.prompt` files.
|
|
18795
18913
|
*
|
|
18796
18914
|
* @private
|
|
@@ -18815,21 +18933,29 @@ function scanDirectory(dir, depth) {
|
|
|
18815
18933
|
});
|
|
18816
18934
|
}
|
|
18817
18935
|
/**
|
|
18818
|
-
* Discover all `.prompt` files
|
|
18936
|
+
* Discover all `.prompt` files matching the given include/exclude patterns.
|
|
18937
|
+
*
|
|
18938
|
+
* Extracts base directories from the include patterns, scans them
|
|
18939
|
+
* recursively, then filters results through picomatch.
|
|
18819
18940
|
*
|
|
18820
|
-
*
|
|
18821
|
-
*
|
|
18822
|
-
*
|
|
18941
|
+
* Name uniqueness is **not** enforced here — prompts with the same name
|
|
18942
|
+
* are allowed as long as they belong to different groups. Uniqueness
|
|
18943
|
+
* is validated downstream in the pipeline after frontmatter parsing,
|
|
18944
|
+
* where group information is available.
|
|
18945
|
+
*
|
|
18946
|
+
* @param options - Include and exclude glob patterns.
|
|
18947
|
+
* @returns Sorted list of discovered prompts.
|
|
18823
18948
|
*/
|
|
18824
|
-
function discoverPrompts(
|
|
18825
|
-
const
|
|
18826
|
-
const
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
|
|
18830
|
-
|
|
18831
|
-
|
|
18832
|
-
|
|
18949
|
+
function discoverPrompts(options) {
|
|
18950
|
+
const { includes, excludes = [] } = options;
|
|
18951
|
+
const all = [...new Set(includes.map((pattern) => resolve(extractBaseDir(pattern))))].flatMap((dir) => scanDirectory(dir, 0));
|
|
18952
|
+
const isIncluded = picomatch(includes);
|
|
18953
|
+
const isExcluded = picomatch(excludes);
|
|
18954
|
+
const filtered = all.filter((prompt) => {
|
|
18955
|
+
const matchPath = relative(process.cwd(), prompt.filePath).replaceAll("\\", "/");
|
|
18956
|
+
return isIncluded(matchPath) && !isExcluded(matchPath);
|
|
18957
|
+
});
|
|
18958
|
+
return [...new Map(filtered.map((prompt) => [prompt.filePath, prompt])).values()].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
18833
18959
|
}
|
|
18834
18960
|
var MAX_DEPTH, PROMPT_EXT;
|
|
18835
18961
|
var init_paths = __esmMin((() => {
|
|
@@ -18841,6 +18967,44 @@ var init_paths = __esmMin((() => {
|
|
|
18841
18967
|
//#endregion
|
|
18842
18968
|
//#region src/lib/prompts/pipeline.ts
|
|
18843
18969
|
/**
|
|
18970
|
+
* Validate that no two prompts share the same group+name combination.
|
|
18971
|
+
*
|
|
18972
|
+
* @param prompts - Parsed prompts with group and name fields.
|
|
18973
|
+
* @throws If duplicate group+name combinations are found.
|
|
18974
|
+
*
|
|
18975
|
+
* @private
|
|
18976
|
+
*/
|
|
18977
|
+
function validateUniqueness(prompts) {
|
|
18978
|
+
const duplicate = [...Map.groupBy(prompts, (p) => toFileSlug(p.name, p.group)).entries()].find(([, entries]) => entries.length > 1);
|
|
18979
|
+
if (duplicate) {
|
|
18980
|
+
const [slug, entries] = duplicate;
|
|
18981
|
+
const paths = entries.map((p) => p.sourcePath).join("\n ");
|
|
18982
|
+
throw new Error(`Duplicate prompt "${slug}" (group+name) found in:\n ${paths}`);
|
|
18983
|
+
}
|
|
18984
|
+
}
|
|
18985
|
+
/**
|
|
18986
|
+
* Resolve a prompt's group from config-defined group patterns.
|
|
18987
|
+
*
|
|
18988
|
+
* Matches the prompt's file path against each group's `includes`/`excludes`
|
|
18989
|
+
* patterns. First matching group wins.
|
|
18990
|
+
*
|
|
18991
|
+
* @param filePath - Absolute path to the prompt file.
|
|
18992
|
+
* @param groups - Config-defined group definitions.
|
|
18993
|
+
* @returns The matching group name, or undefined if no match.
|
|
18994
|
+
*
|
|
18995
|
+
* @private
|
|
18996
|
+
*/
|
|
18997
|
+
function resolveGroupFromConfig(filePath, groups) {
|
|
18998
|
+
const matchPath = relative(process.cwd(), filePath).replaceAll("\\", "/");
|
|
18999
|
+
const matched = groups.find((group) => {
|
|
19000
|
+
const isIncluded = picomatch(group.includes);
|
|
19001
|
+
const isExcluded = picomatch(group.excludes ?? []);
|
|
19002
|
+
return isIncluded(matchPath) && !isExcluded(matchPath);
|
|
19003
|
+
});
|
|
19004
|
+
if (isNil(matched)) return;
|
|
19005
|
+
return matched.name;
|
|
19006
|
+
}
|
|
19007
|
+
/**
|
|
18844
19008
|
* Resolve the list of partial directories to search.
|
|
18845
19009
|
*
|
|
18846
19010
|
* @private
|
|
@@ -18858,7 +19022,9 @@ function resolvePartialsDirs(customDir) {
|
|
|
18858
19022
|
* @returns Lint results for all discovered prompts.
|
|
18859
19023
|
*/
|
|
18860
19024
|
function runLintPipeline(options) {
|
|
18861
|
-
const
|
|
19025
|
+
const discoverLintOptions = { includes: [...options.includes] };
|
|
19026
|
+
if (options.excludes !== void 0) discoverLintOptions.excludes = [...options.excludes];
|
|
19027
|
+
const discovered = discoverPrompts(discoverLintOptions);
|
|
18862
19028
|
const partialsDirs = resolvePartialsDirs(resolve(options.partials ?? ".prompts/partials"));
|
|
18863
19029
|
const results = discovered.map((d) => {
|
|
18864
19030
|
const raw = readFileSync(d.filePath, "utf8");
|
|
@@ -18870,7 +19036,12 @@ function runLintPipeline(options) {
|
|
|
18870
19036
|
template: clean(raw),
|
|
18871
19037
|
partialsDirs
|
|
18872
19038
|
}));
|
|
18873
|
-
return lintPrompt(
|
|
19039
|
+
return lintPrompt({
|
|
19040
|
+
name: frontmatter.name,
|
|
19041
|
+
filePath: d.filePath,
|
|
19042
|
+
schemaVars: frontmatter.schema,
|
|
19043
|
+
templateVars
|
|
19044
|
+
});
|
|
18874
19045
|
});
|
|
18875
19046
|
return {
|
|
18876
19047
|
discovered: discovered.length,
|
|
@@ -18886,8 +19057,11 @@ function runLintPipeline(options) {
|
|
|
18886
19057
|
* @returns Parsed prompts ready for code generation, along with lint results.
|
|
18887
19058
|
*/
|
|
18888
19059
|
function runGeneratePipeline(options) {
|
|
18889
|
-
const
|
|
19060
|
+
const discoverGenerateOptions = { includes: [...options.includes] };
|
|
19061
|
+
if (options.excludes !== void 0) discoverGenerateOptions.excludes = [...options.excludes];
|
|
19062
|
+
const discovered = discoverPrompts(discoverGenerateOptions);
|
|
18890
19063
|
const partialsDirs = resolvePartialsDirs(resolve(options.partials ?? resolve(options.out, "../partials")));
|
|
19064
|
+
const configGroups = options.groups ?? [];
|
|
18891
19065
|
const processed = discovered.map((d) => {
|
|
18892
19066
|
const raw = readFileSync(d.filePath, "utf8");
|
|
18893
19067
|
const frontmatter = parseFrontmatter({
|
|
@@ -18899,24 +19073,34 @@ function runGeneratePipeline(options) {
|
|
|
18899
19073
|
partialsDirs
|
|
18900
19074
|
});
|
|
18901
19075
|
const templateVars = extractVariables(template);
|
|
19076
|
+
const group = frontmatter.group ?? resolveGroupFromConfig(d.filePath, configGroups);
|
|
19077
|
+
const promptObj = {
|
|
19078
|
+
name: frontmatter.name,
|
|
19079
|
+
schema: frontmatter.schema,
|
|
19080
|
+
template,
|
|
19081
|
+
sourcePath: d.filePath
|
|
19082
|
+
};
|
|
19083
|
+
if (group !== void 0) promptObj.group = group;
|
|
18902
19084
|
return {
|
|
18903
|
-
lintResult: lintPrompt(
|
|
18904
|
-
prompt: {
|
|
19085
|
+
lintResult: lintPrompt({
|
|
18905
19086
|
name: frontmatter.name,
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
19087
|
+
filePath: d.filePath,
|
|
19088
|
+
schemaVars: frontmatter.schema,
|
|
19089
|
+
templateVars
|
|
19090
|
+
}),
|
|
19091
|
+
prompt: promptObj
|
|
18911
19092
|
};
|
|
18912
19093
|
});
|
|
19094
|
+
const prompts = processed.map((p) => p.prompt);
|
|
19095
|
+
validateUniqueness(prompts);
|
|
18913
19096
|
return {
|
|
18914
19097
|
discovered: discovered.length,
|
|
18915
19098
|
lintResults: processed.map((p) => p.lintResult),
|
|
18916
|
-
prompts
|
|
19099
|
+
prompts
|
|
18917
19100
|
};
|
|
18918
19101
|
}
|
|
18919
19102
|
var init_pipeline = __esmMin((() => {
|
|
19103
|
+
init_codegen();
|
|
18920
19104
|
init_extract_variables();
|
|
18921
19105
|
init_flatten();
|
|
18922
19106
|
init_frontmatter();
|
|
@@ -18927,17 +19111,44 @@ var init_pipeline = __esmMin((() => {
|
|
|
18927
19111
|
//#endregion
|
|
18928
19112
|
//#region src/commands/prompts/generate.ts
|
|
18929
19113
|
/**
|
|
18930
|
-
*
|
|
19114
|
+
* Resolve generate args by merging CLI flags with config defaults.
|
|
18931
19115
|
*
|
|
18932
|
-
* @param
|
|
19116
|
+
* @param args - CLI arguments (take precedence).
|
|
19117
|
+
* @param config - Prompts config from funkai.config.ts (fallback).
|
|
19118
|
+
* @param fail - Error handler for missing required values.
|
|
19119
|
+
* @returns Resolved args with required fields guaranteed.
|
|
18933
19120
|
*/
|
|
18934
|
-
function
|
|
18935
|
-
const
|
|
18936
|
-
const
|
|
18937
|
-
|
|
19121
|
+
function resolveGenerateArgs(args, config, fail) {
|
|
19122
|
+
const out = args.out ?? (config && config.out);
|
|
19123
|
+
const includes = args.includes ?? (config && config.includes) ?? ["./**"];
|
|
19124
|
+
const excludes = (config && config.excludes) ?? [];
|
|
19125
|
+
const partials = args.partials ?? (config && config.partials);
|
|
19126
|
+
if (!out) fail("Missing --out flag. Provide it via CLI or set prompts.out in funkai.config.ts.");
|
|
19127
|
+
const resolved = {
|
|
18938
19128
|
out,
|
|
18939
|
-
|
|
18940
|
-
|
|
19129
|
+
includes,
|
|
19130
|
+
excludes,
|
|
19131
|
+
silent: args.silent
|
|
19132
|
+
};
|
|
19133
|
+
if (partials !== void 0) resolved.partials = partials;
|
|
19134
|
+
return resolved;
|
|
19135
|
+
}
|
|
19136
|
+
/**
|
|
19137
|
+
* Shared handler for prompts code generation.
|
|
19138
|
+
*
|
|
19139
|
+
* @param params - Handler context with args, config, logger, and fail callback.
|
|
19140
|
+
*/
|
|
19141
|
+
function handleGenerate({ args, config, logger, fail }) {
|
|
19142
|
+
const { out, includes, excludes, partials, silent } = resolveGenerateArgs(args, config, fail);
|
|
19143
|
+
const configGroups = config && config.groups;
|
|
19144
|
+
const pipelineOptions = {
|
|
19145
|
+
includes,
|
|
19146
|
+
excludes,
|
|
19147
|
+
out
|
|
19148
|
+
};
|
|
19149
|
+
if (partials !== void 0) pipelineOptions.partials = partials;
|
|
19150
|
+
if (configGroups !== void 0) pipelineOptions.groups = configGroups;
|
|
19151
|
+
const { discovered, lintResults, prompts } = runGeneratePipeline(pipelineOptions);
|
|
18941
19152
|
if (!silent) logger.info(`Found ${discovered} prompt(s)`);
|
|
18942
19153
|
if (!silent) for (const prompt of prompts) {
|
|
18943
19154
|
const varList = formatVarList(prompt.schema);
|
|
@@ -18951,7 +19162,7 @@ function handleGenerate({ args, logger, fail }) {
|
|
|
18951
19162
|
mkdirSync(outDir, { recursive: true });
|
|
18952
19163
|
for (const prompt of prompts) {
|
|
18953
19164
|
const content = generatePromptModule(prompt);
|
|
18954
|
-
writeFileSync(resolve(outDir, `${prompt.name}.ts`), content, "utf8");
|
|
19165
|
+
writeFileSync(resolve(outDir, `${toFileSlug(prompt.name, prompt.group)}.ts`), content, "utf8");
|
|
18955
19166
|
}
|
|
18956
19167
|
const registryContent = generateRegistry(prompts);
|
|
18957
19168
|
writeFileSync(resolve(outDir, "index.ts"), registryContent, "utf8");
|
|
@@ -18965,12 +19176,13 @@ function formatVarList(schema) {
|
|
|
18965
19176
|
var generateArgs, generate_default$1;
|
|
18966
19177
|
var init_generate$1 = __esmMin((() => {
|
|
18967
19178
|
init_dist();
|
|
19179
|
+
init_config();
|
|
18968
19180
|
init_codegen();
|
|
18969
19181
|
init_lint$1();
|
|
18970
19182
|
init_pipeline();
|
|
18971
19183
|
generateArgs = z.object({
|
|
18972
|
-
out: z.string().describe("Output directory for generated files"),
|
|
18973
|
-
|
|
19184
|
+
out: z.string().optional().describe("Output directory for generated files"),
|
|
19185
|
+
includes: z.array(z.string()).optional().describe("Glob patterns to scan for .prompt files"),
|
|
18974
19186
|
partials: z.string().optional().describe("Custom partials directory"),
|
|
18975
19187
|
silent: z.boolean().default(false).describe("Suppress output except errors")
|
|
18976
19188
|
});
|
|
@@ -18978,8 +19190,14 @@ var init_generate$1 = __esmMin((() => {
|
|
|
18978
19190
|
description: "Generate TypeScript modules from .prompt files",
|
|
18979
19191
|
options: generateArgs,
|
|
18980
19192
|
handler(ctx) {
|
|
19193
|
+
const config = getConfig(ctx);
|
|
19194
|
+
const generateArgs2 = { silent: ctx.args.silent };
|
|
19195
|
+
if (ctx.args.out !== void 0) generateArgs2.out = ctx.args.out;
|
|
19196
|
+
if (ctx.args.includes !== void 0) generateArgs2.includes = ctx.args.includes;
|
|
19197
|
+
if (ctx.args.partials !== void 0) generateArgs2.partials = ctx.args.partials;
|
|
18981
19198
|
handleGenerate({
|
|
18982
|
-
args:
|
|
19199
|
+
args: generateArgs2,
|
|
19200
|
+
config: config.prompts,
|
|
18983
19201
|
logger: ctx.logger,
|
|
18984
19202
|
fail: ctx.fail
|
|
18985
19203
|
});
|
|
@@ -18993,14 +19211,21 @@ var generate_default;
|
|
|
18993
19211
|
var init_generate = __esmMin((() => {
|
|
18994
19212
|
init_dist();
|
|
18995
19213
|
init_generate$1();
|
|
19214
|
+
init_config();
|
|
18996
19215
|
generate_default = command({
|
|
18997
19216
|
description: "Run all code generation across the funkai SDK",
|
|
18998
19217
|
options: generateArgs,
|
|
18999
19218
|
handler(ctx) {
|
|
19000
19219
|
const { silent } = ctx.args;
|
|
19220
|
+
const config = getConfig(ctx);
|
|
19001
19221
|
if (!silent) ctx.logger.info("Running prompts code generation...");
|
|
19222
|
+
const generateHandleArgs = { silent: ctx.args.silent };
|
|
19223
|
+
if (ctx.args.out !== void 0) generateHandleArgs.out = ctx.args.out;
|
|
19224
|
+
if (ctx.args.includes !== void 0) generateHandleArgs.includes = ctx.args.includes;
|
|
19225
|
+
if (ctx.args.partials !== void 0) generateHandleArgs.partials = ctx.args.partials;
|
|
19002
19226
|
handleGenerate({
|
|
19003
|
-
args:
|
|
19227
|
+
args: generateHandleArgs,
|
|
19228
|
+
config: config.prompts,
|
|
19004
19229
|
logger: ctx.logger,
|
|
19005
19230
|
fail: ctx.fail
|
|
19006
19231
|
});
|
|
@@ -19009,18 +19234,245 @@ var init_generate = __esmMin((() => {
|
|
|
19009
19234
|
}));
|
|
19010
19235
|
|
|
19011
19236
|
//#endregion
|
|
19012
|
-
//#region src/commands/setup.ts
|
|
19013
|
-
|
|
19237
|
+
//#region src/commands/prompts/setup.ts
|
|
19238
|
+
/**
|
|
19239
|
+
* Shared prompts setup logic used by both `funkai prompts setup` and `funkai setup`.
|
|
19240
|
+
*
|
|
19241
|
+
* @param ctx - The CLI context with prompts and logger.
|
|
19242
|
+
*/
|
|
19243
|
+
async function setupPrompts(ctx) {
|
|
19244
|
+
if (await ctx.prompts.confirm({
|
|
19245
|
+
message: "Configure VSCode to treat .prompt files as Markdown with Liquid syntax?",
|
|
19246
|
+
initialValue: true
|
|
19247
|
+
})) {
|
|
19248
|
+
const vscodeDir = resolve(VSCODE_DIR);
|
|
19249
|
+
mkdirSync(vscodeDir, { recursive: true });
|
|
19250
|
+
const settingsPath = resolve(vscodeDir, SETTINGS_FILE);
|
|
19251
|
+
const settings = readJsonFile(settingsPath);
|
|
19252
|
+
const updatedSettings = {
|
|
19253
|
+
...settings,
|
|
19254
|
+
"files.associations": {
|
|
19255
|
+
...settings["files.associations"] ?? {},
|
|
19256
|
+
"*.prompt": "markdown"
|
|
19257
|
+
},
|
|
19258
|
+
"liquid.engine": "standard"
|
|
19259
|
+
};
|
|
19260
|
+
writeFileSync(settingsPath, `${JSON.stringify(updatedSettings, null, 2)}\n`, "utf8");
|
|
19261
|
+
ctx.logger.success(`Updated ${settingsPath}`);
|
|
19262
|
+
}
|
|
19263
|
+
if (await ctx.prompts.confirm({
|
|
19264
|
+
message: "Add Shopify Liquid extension to VSCode recommendations?",
|
|
19265
|
+
initialValue: true
|
|
19266
|
+
})) {
|
|
19267
|
+
const vscodeDir = resolve(VSCODE_DIR);
|
|
19268
|
+
mkdirSync(vscodeDir, { recursive: true });
|
|
19269
|
+
const extensionsPath = resolve(vscodeDir, EXTENSIONS_FILE);
|
|
19270
|
+
const extensions = readJsonFile(extensionsPath);
|
|
19271
|
+
const recommendations = ensureRecommendation(extensions["recommendations"] ?? [], "sissel.shopify-liquid");
|
|
19272
|
+
const updatedExtensions = {
|
|
19273
|
+
...extensions,
|
|
19274
|
+
recommendations
|
|
19275
|
+
};
|
|
19276
|
+
writeFileSync(extensionsPath, `${JSON.stringify(updatedExtensions, null, 2)}\n`, "utf8");
|
|
19277
|
+
ctx.logger.success(`Updated ${extensionsPath}`);
|
|
19278
|
+
}
|
|
19279
|
+
if (await ctx.prompts.confirm({
|
|
19280
|
+
message: "Add .prompts/client/ to .gitignore? (generated client should not be committed)",
|
|
19281
|
+
initialValue: true
|
|
19282
|
+
})) {
|
|
19283
|
+
const gitignorePath = resolve(GITIGNORE_FILE);
|
|
19284
|
+
const existing = readFileOrEmpty(gitignorePath);
|
|
19285
|
+
if (existing.includes(GITIGNORE_ENTRY)) ctx.logger.info(`${GITIGNORE_ENTRY} already in ${gitignorePath}`);
|
|
19286
|
+
else {
|
|
19287
|
+
writeFileSync(gitignorePath, `${existing}${`${trailingSeparator(existing)}\n# Generated prompt client (created by \`funkai prompts generate\`)\n${GITIGNORE_ENTRY}\n`}`, "utf8");
|
|
19288
|
+
ctx.logger.success(`Added ${GITIGNORE_ENTRY} to ${gitignorePath}`);
|
|
19289
|
+
}
|
|
19290
|
+
}
|
|
19291
|
+
if (await ctx.prompts.confirm({
|
|
19292
|
+
message: "Add ~prompts path alias to tsconfig.json?",
|
|
19293
|
+
initialValue: true
|
|
19294
|
+
})) {
|
|
19295
|
+
const tsconfigPath = resolve(TSCONFIG_FILE);
|
|
19296
|
+
const tsconfig = readJsonFile(tsconfigPath);
|
|
19297
|
+
const compilerOptions = tsconfig["compilerOptions"] ?? {};
|
|
19298
|
+
const existingPaths = compilerOptions["paths"] ?? {};
|
|
19299
|
+
if (existingPaths[PROMPTS_ALIAS]) ctx.logger.info(`${PROMPTS_ALIAS} alias already in ${tsconfigPath}`);
|
|
19300
|
+
else {
|
|
19301
|
+
const updatedTsconfig = {
|
|
19302
|
+
...tsconfig,
|
|
19303
|
+
compilerOptions: {
|
|
19304
|
+
...compilerOptions,
|
|
19305
|
+
paths: {
|
|
19306
|
+
...existingPaths,
|
|
19307
|
+
[PROMPTS_ALIAS]: [PROMPTS_ALIAS_PATH]
|
|
19308
|
+
}
|
|
19309
|
+
}
|
|
19310
|
+
};
|
|
19311
|
+
writeFileSync(tsconfigPath, `${JSON.stringify(updatedTsconfig, null, 2)}\n`, "utf8");
|
|
19312
|
+
ctx.logger.success(`Added ${PROMPTS_ALIAS} alias to ${tsconfigPath}`);
|
|
19313
|
+
}
|
|
19314
|
+
}
|
|
19315
|
+
}
|
|
19316
|
+
/** @private */
|
|
19317
|
+
function errorMessage(error) {
|
|
19318
|
+
if (error instanceof Error) return error.message;
|
|
19319
|
+
return String(error);
|
|
19320
|
+
}
|
|
19321
|
+
/** @private */
|
|
19322
|
+
function ensureRecommendation(current, id) {
|
|
19323
|
+
if (current.includes(id)) return [...current];
|
|
19324
|
+
return [...current, id];
|
|
19325
|
+
}
|
|
19326
|
+
/** @private */
|
|
19327
|
+
function readFileOrEmpty(filePath) {
|
|
19328
|
+
if (existsSync(filePath)) return readFileSync(filePath, "utf8");
|
|
19329
|
+
return "";
|
|
19330
|
+
}
|
|
19331
|
+
/** @private */
|
|
19332
|
+
function trailingSeparator(content) {
|
|
19333
|
+
if (content.length > 0 && !content.endsWith("\n")) return "\n";
|
|
19334
|
+
return "";
|
|
19335
|
+
}
|
|
19336
|
+
/**
|
|
19337
|
+
* Read a JSON file, returning an empty object if it doesn't exist.
|
|
19338
|
+
* Throws if the file exists but contains invalid JSON, preventing
|
|
19339
|
+
* silent data loss from overwriting malformed config files.
|
|
19340
|
+
*
|
|
19341
|
+
* @private
|
|
19342
|
+
*/
|
|
19343
|
+
function readJsonFile(filePath) {
|
|
19344
|
+
if (!existsSync(filePath)) return {};
|
|
19345
|
+
const content = readFileSync(filePath, "utf8");
|
|
19346
|
+
try {
|
|
19347
|
+
return JSON.parse(content);
|
|
19348
|
+
} catch (error) {
|
|
19349
|
+
throw new Error(`Failed to parse ${filePath}: ${errorMessage(error)}. Fix the JSON syntax or remove the file before running setup.`, { cause: error });
|
|
19350
|
+
}
|
|
19351
|
+
}
|
|
19352
|
+
var VSCODE_DIR, SETTINGS_FILE, EXTENSIONS_FILE, GITIGNORE_FILE, TSCONFIG_FILE, GITIGNORE_ENTRY, PROMPTS_ALIAS, PROMPTS_ALIAS_PATH, setup_default$1;
|
|
19014
19353
|
var init_setup$1 = __esmMin((() => {
|
|
19015
19354
|
init_dist();
|
|
19355
|
+
VSCODE_DIR = ".vscode";
|
|
19356
|
+
SETTINGS_FILE = "settings.json";
|
|
19357
|
+
EXTENSIONS_FILE = "extensions.json";
|
|
19358
|
+
GITIGNORE_FILE = ".gitignore";
|
|
19359
|
+
TSCONFIG_FILE = "tsconfig.json";
|
|
19360
|
+
GITIGNORE_ENTRY = ".prompts/client/";
|
|
19361
|
+
PROMPTS_ALIAS = "~prompts";
|
|
19362
|
+
PROMPTS_ALIAS_PATH = "./.prompts/client/index.ts";
|
|
19016
19363
|
setup_default$1 = command({
|
|
19364
|
+
description: "Configure VSCode IDE settings for .prompt files",
|
|
19365
|
+
async handler(ctx) {
|
|
19366
|
+
ctx.logger.intro("Prompt SDK — Project Setup");
|
|
19367
|
+
await setupPrompts(ctx);
|
|
19368
|
+
ctx.logger.outro("Prompts setup complete.");
|
|
19369
|
+
}
|
|
19370
|
+
});
|
|
19371
|
+
}));
|
|
19372
|
+
|
|
19373
|
+
//#endregion
|
|
19374
|
+
//#region src/commands/setup.ts
|
|
19375
|
+
/**
|
|
19376
|
+
* Gather prompt include patterns and output directory from the user.
|
|
19377
|
+
*
|
|
19378
|
+
* @private
|
|
19379
|
+
* @param ctx - CLI context for prompts.
|
|
19380
|
+
* @param hasPrompts - Whether the prompts domain is selected.
|
|
19381
|
+
* @returns The resolved prompt settings.
|
|
19382
|
+
*/
|
|
19383
|
+
async function resolvePromptSettings(ctx, hasPrompts) {
|
|
19384
|
+
if (!hasPrompts) return {
|
|
19385
|
+
includes: ["src/prompts/**"],
|
|
19386
|
+
out: ".prompts/client"
|
|
19387
|
+
};
|
|
19388
|
+
const includesInput = await ctx.prompts.text({
|
|
19389
|
+
message: "Prompt include patterns (comma-separated)",
|
|
19390
|
+
defaultValue: "src/prompts/**",
|
|
19391
|
+
placeholder: "src/prompts/**"
|
|
19392
|
+
});
|
|
19393
|
+
const out = await ctx.prompts.text({
|
|
19394
|
+
message: "Output directory for generated prompt modules",
|
|
19395
|
+
defaultValue: ".prompts/client",
|
|
19396
|
+
placeholder: ".prompts/client"
|
|
19397
|
+
});
|
|
19398
|
+
return {
|
|
19399
|
+
includes: includesInput.split(",").map((r) => r.trim()),
|
|
19400
|
+
out
|
|
19401
|
+
};
|
|
19402
|
+
}
|
|
19403
|
+
/** @private */
|
|
19404
|
+
function buildConfigTemplate({ hasPrompts, hasAgents, includes, out }) {
|
|
19405
|
+
if (hasPrompts && hasAgents) return buildCustomTemplate(includes, out, true);
|
|
19406
|
+
if (hasPrompts) return buildCustomTemplate(includes, out, false);
|
|
19407
|
+
return CONFIG_TEMPLATE_AGENTS_ONLY;
|
|
19408
|
+
}
|
|
19409
|
+
/** @private */
|
|
19410
|
+
function buildCustomTemplate(includes, out, includeAgents) {
|
|
19411
|
+
return `import { defineConfig } from "@funkai/config";
|
|
19412
|
+
|
|
19413
|
+
export default defineConfig({
|
|
19414
|
+
prompts: {
|
|
19415
|
+
includes: [${includes.map((r) => `"${r}"`).join(", ")}],
|
|
19416
|
+
out: "${out}",
|
|
19417
|
+
},${match(includeAgents).with(true, () => "\n agents: {},\n").with(false, () => "\n").exhaustive()}});
|
|
19418
|
+
`;
|
|
19419
|
+
}
|
|
19420
|
+
var CONFIG_TEMPLATE_AGENTS_ONLY, setup_default;
|
|
19421
|
+
var init_setup = __esmMin((() => {
|
|
19422
|
+
init_dist();
|
|
19423
|
+
init_setup$1();
|
|
19424
|
+
CONFIG_TEMPLATE_AGENTS_ONLY = `import { defineConfig } from "@funkai/config";
|
|
19425
|
+
|
|
19426
|
+
export default defineConfig({
|
|
19427
|
+
agents: {},
|
|
19428
|
+
});
|
|
19429
|
+
`;
|
|
19430
|
+
setup_default = command({
|
|
19017
19431
|
description: "Set up your project for the funkai SDK",
|
|
19018
19432
|
async handler(ctx) {
|
|
19019
19433
|
ctx.logger.intro("funkai — Project Setup");
|
|
19020
|
-
ctx.
|
|
19021
|
-
|
|
19022
|
-
|
|
19023
|
-
|
|
19434
|
+
const domains = await ctx.prompts.multiselect({
|
|
19435
|
+
message: "Which domains do you want to set up?",
|
|
19436
|
+
options: [{
|
|
19437
|
+
value: "prompts",
|
|
19438
|
+
label: "Prompts",
|
|
19439
|
+
hint: "LiquidJS templating, codegen, IDE integration"
|
|
19440
|
+
}, {
|
|
19441
|
+
value: "agents",
|
|
19442
|
+
label: "Agents",
|
|
19443
|
+
hint: "Agent scaffolding and configuration"
|
|
19444
|
+
}],
|
|
19445
|
+
initialValues: ["prompts"],
|
|
19446
|
+
required: true
|
|
19447
|
+
});
|
|
19448
|
+
const hasPrompts = domains.includes("prompts");
|
|
19449
|
+
const hasAgents = domains.includes("agents");
|
|
19450
|
+
if (await ctx.prompts.confirm({
|
|
19451
|
+
message: "Create funkai.config.ts?",
|
|
19452
|
+
initialValue: true
|
|
19453
|
+
})) {
|
|
19454
|
+
const { includes, out } = await resolvePromptSettings(ctx, hasPrompts);
|
|
19455
|
+
const template = buildConfigTemplate({
|
|
19456
|
+
hasPrompts,
|
|
19457
|
+
hasAgents,
|
|
19458
|
+
includes,
|
|
19459
|
+
out
|
|
19460
|
+
});
|
|
19461
|
+
const configPath = resolve("funkai.config.ts");
|
|
19462
|
+
writeFileSync(configPath, template, "utf8");
|
|
19463
|
+
ctx.logger.success(`Created ${configPath}`);
|
|
19464
|
+
}
|
|
19465
|
+
if (hasPrompts) {
|
|
19466
|
+
ctx.logger.info("");
|
|
19467
|
+
ctx.logger.info("Configuring Prompts...");
|
|
19468
|
+
await setupPrompts(ctx);
|
|
19469
|
+
}
|
|
19470
|
+
if (hasAgents) {
|
|
19471
|
+
ctx.logger.info("");
|
|
19472
|
+
ctx.logger.info("Agents configuration is not yet available.");
|
|
19473
|
+
ctx.logger.info("The agents section has been added to your config for future use.");
|
|
19474
|
+
}
|
|
19475
|
+
ctx.logger.outro("Project setup complete.");
|
|
19024
19476
|
}
|
|
19025
19477
|
});
|
|
19026
19478
|
}));
|
|
@@ -19028,16 +19480,38 @@ var init_setup$1 = __esmMin((() => {
|
|
|
19028
19480
|
//#endregion
|
|
19029
19481
|
//#region src/commands/prompts/lint.ts
|
|
19030
19482
|
/**
|
|
19483
|
+
* Resolve lint args by merging CLI flags with config defaults.
|
|
19484
|
+
*
|
|
19485
|
+
* @param args - CLI arguments (take precedence).
|
|
19486
|
+
* @param config - Prompts config from funkai.config.ts (fallback).
|
|
19487
|
+
* @param fail - Error handler for missing required values.
|
|
19488
|
+
* @returns Resolved args with required fields guaranteed.
|
|
19489
|
+
*/
|
|
19490
|
+
function resolveLintArgs(args, config, _fail) {
|
|
19491
|
+
const includes = args.includes ?? (config && config.includes) ?? ["./**"];
|
|
19492
|
+
const excludes = (config && config.excludes) ?? [];
|
|
19493
|
+
const partials = args.partials ?? (config && config.partials);
|
|
19494
|
+
const resolved = {
|
|
19495
|
+
includes,
|
|
19496
|
+
excludes,
|
|
19497
|
+
silent: args.silent
|
|
19498
|
+
};
|
|
19499
|
+
if (partials !== void 0) resolved.partials = partials;
|
|
19500
|
+
return resolved;
|
|
19501
|
+
}
|
|
19502
|
+
/**
|
|
19031
19503
|
* Shared handler for prompts lint/validation.
|
|
19032
19504
|
*
|
|
19033
|
-
* @param params - Handler context with args, logger, and fail callback.
|
|
19505
|
+
* @param params - Handler context with args, config, logger, and fail callback.
|
|
19034
19506
|
*/
|
|
19035
|
-
function handleLint({ args, logger, fail }) {
|
|
19036
|
-
const {
|
|
19037
|
-
const
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
}
|
|
19507
|
+
function handleLint({ args, config, logger, fail }) {
|
|
19508
|
+
const { includes, excludes, partials, silent } = resolveLintArgs(args, config, fail);
|
|
19509
|
+
const lintPipelineOptions = {
|
|
19510
|
+
includes,
|
|
19511
|
+
excludes
|
|
19512
|
+
};
|
|
19513
|
+
if (partials !== void 0) lintPipelineOptions.partials = partials;
|
|
19514
|
+
const { discovered, results } = runLintPipeline(lintPipelineOptions);
|
|
19041
19515
|
if (!silent) logger.info(`Linting ${discovered} prompt(s)...`);
|
|
19042
19516
|
const diagnostics = results.flatMap((result) => result.diagnostics);
|
|
19043
19517
|
for (const diag of diagnostics) match(diag.level).with("error", () => logger.error(diag.message)).with("warn", () => logger.warn(diag.message)).exhaustive();
|
|
@@ -19055,10 +19529,11 @@ function handleLint({ args, logger, fail }) {
|
|
|
19055
19529
|
var lintArgs, lint_default;
|
|
19056
19530
|
var init_lint = __esmMin((() => {
|
|
19057
19531
|
init_dist();
|
|
19532
|
+
init_config();
|
|
19058
19533
|
init_lint$1();
|
|
19059
19534
|
init_pipeline();
|
|
19060
19535
|
lintArgs = z.object({
|
|
19061
|
-
|
|
19536
|
+
includes: z.array(z.string()).optional().describe("Glob patterns to scan for .prompt files"),
|
|
19062
19537
|
partials: z.string().optional().describe("Custom partials directory"),
|
|
19063
19538
|
silent: z.boolean().default(false).describe("Suppress output except errors")
|
|
19064
19539
|
});
|
|
@@ -19066,8 +19541,13 @@ var init_lint = __esmMin((() => {
|
|
|
19066
19541
|
description: "Validate .prompt files for schema/template mismatches",
|
|
19067
19542
|
options: lintArgs,
|
|
19068
19543
|
handler(ctx) {
|
|
19544
|
+
const config = getConfig(ctx);
|
|
19545
|
+
const lintHandleArgs = { silent: ctx.args.silent };
|
|
19546
|
+
if (ctx.args.includes !== void 0) lintHandleArgs.includes = ctx.args.includes;
|
|
19547
|
+
if (ctx.args.partials !== void 0) lintHandleArgs.partials = ctx.args.partials;
|
|
19069
19548
|
handleLint({
|
|
19070
|
-
args:
|
|
19549
|
+
args: lintHandleArgs,
|
|
19550
|
+
config: config.prompts,
|
|
19071
19551
|
logger: ctx.logger,
|
|
19072
19552
|
fail: ctx.fail
|
|
19073
19553
|
});
|
|
@@ -19081,18 +19561,24 @@ var validate_default$1;
|
|
|
19081
19561
|
var init_validate$1 = __esmMin((() => {
|
|
19082
19562
|
init_dist();
|
|
19083
19563
|
init_lint();
|
|
19564
|
+
init_config();
|
|
19084
19565
|
validate_default$1 = command({
|
|
19085
19566
|
description: "Run all validations across the funkai SDK",
|
|
19086
19567
|
options: lintArgs,
|
|
19087
19568
|
handler(ctx) {
|
|
19088
19569
|
const { silent } = ctx.args;
|
|
19570
|
+
const config = getConfig(ctx);
|
|
19089
19571
|
if (!silent) ctx.logger.info("Running prompts validation...");
|
|
19572
|
+
const lintHandleArgs = { silent: ctx.args.silent };
|
|
19573
|
+
if (ctx.args.includes !== void 0) lintHandleArgs.includes = ctx.args.includes;
|
|
19574
|
+
if (ctx.args.partials !== void 0) lintHandleArgs.partials = ctx.args.partials;
|
|
19090
19575
|
handleLint({
|
|
19091
|
-
args:
|
|
19576
|
+
args: lintHandleArgs,
|
|
19577
|
+
config: config.prompts,
|
|
19092
19578
|
logger: ctx.logger,
|
|
19093
19579
|
fail: ctx.fail
|
|
19094
19580
|
});
|
|
19095
|
-
if (!silent) ctx.logger.success("
|
|
19581
|
+
if (!silent) ctx.logger.success("No errors found.");
|
|
19096
19582
|
}
|
|
19097
19583
|
});
|
|
19098
19584
|
}));
|
|
@@ -19116,6 +19602,7 @@ var init_validate = __esmMin((() => {
|
|
|
19116
19602
|
var createTemplate, create_default;
|
|
19117
19603
|
var init_create = __esmMin((() => {
|
|
19118
19604
|
init_dist();
|
|
19605
|
+
init_config();
|
|
19119
19606
|
createTemplate = (name) => `---
|
|
19120
19607
|
name: ${name}
|
|
19121
19608
|
---
|
|
@@ -19125,15 +19612,28 @@ name: ${name}
|
|
|
19125
19612
|
description: "Create a new .prompt file",
|
|
19126
19613
|
options: z.object({
|
|
19127
19614
|
name: z.string().describe("Prompt name (kebab-case)"),
|
|
19128
|
-
out: z.string().optional().describe("Output directory (defaults to cwd)"),
|
|
19615
|
+
out: z.string().optional().describe("Output directory (defaults to first root in config or cwd)"),
|
|
19129
19616
|
partial: z.boolean().default(false).describe("Create as a partial in .prompts/partials/")
|
|
19130
19617
|
}),
|
|
19131
19618
|
handler(ctx) {
|
|
19132
19619
|
const { name, out, partial } = ctx.args;
|
|
19620
|
+
const promptsConfig = getConfig(ctx).prompts;
|
|
19621
|
+
const firstInclude = match(promptsConfig).with({ includes: P.array(P.string).select() }, (includes) => {
|
|
19622
|
+
if (includes.length > 0) {
|
|
19623
|
+
const [pattern] = includes;
|
|
19624
|
+
if (pattern === void 0) return;
|
|
19625
|
+
const staticParts = pattern.split("/").filter((p) => !p.includes("*") && !p.includes("?"));
|
|
19626
|
+
if (staticParts.length > 0) return staticParts.join("/");
|
|
19627
|
+
return;
|
|
19628
|
+
}
|
|
19629
|
+
}).otherwise(() => void 0);
|
|
19133
19630
|
const dir = match({
|
|
19134
19631
|
partial,
|
|
19135
19632
|
out
|
|
19136
|
-
}).with({ partial: true }, () => resolve(".prompts/partials")).with({ out: P.string }, ({ out: outDir }) => resolve(outDir)).otherwise(() =>
|
|
19633
|
+
}).with({ partial: true }, () => resolve(".prompts/partials")).with({ out: P.string }, ({ out: outDir }) => resolve(outDir)).otherwise(() => {
|
|
19634
|
+
if (firstInclude) return resolve(firstInclude);
|
|
19635
|
+
return process.cwd();
|
|
19636
|
+
});
|
|
19137
19637
|
const filePath = resolve(dir, `${name}.prompt`);
|
|
19138
19638
|
if (existsSync(filePath)) ctx.fail(`File already exists: ${filePath}`);
|
|
19139
19639
|
mkdirSync(dir, { recursive: true });
|
|
@@ -19143,135 +19643,6 @@ name: ${name}
|
|
|
19143
19643
|
});
|
|
19144
19644
|
}));
|
|
19145
19645
|
|
|
19146
|
-
//#endregion
|
|
19147
|
-
//#region src/commands/prompts/setup.ts
|
|
19148
|
-
/** @private */
|
|
19149
|
-
function errorMessage(error) {
|
|
19150
|
-
if (error instanceof Error) return error.message;
|
|
19151
|
-
return String(error);
|
|
19152
|
-
}
|
|
19153
|
-
/** @private */
|
|
19154
|
-
function ensureRecommendation(current, id) {
|
|
19155
|
-
if (current.includes(id)) return [...current];
|
|
19156
|
-
return [...current, id];
|
|
19157
|
-
}
|
|
19158
|
-
/** @private */
|
|
19159
|
-
function readFileOrEmpty(filePath) {
|
|
19160
|
-
if (existsSync(filePath)) return readFileSync(filePath, "utf8");
|
|
19161
|
-
return "";
|
|
19162
|
-
}
|
|
19163
|
-
/** @private */
|
|
19164
|
-
function trailingSeparator(content) {
|
|
19165
|
-
if (content.length > 0 && !content.endsWith("\n")) return "\n";
|
|
19166
|
-
return "";
|
|
19167
|
-
}
|
|
19168
|
-
/**
|
|
19169
|
-
* Read a JSON file, returning an empty object if it doesn't exist.
|
|
19170
|
-
* Throws if the file exists but contains invalid JSON, preventing
|
|
19171
|
-
* silent data loss from overwriting malformed config files.
|
|
19172
|
-
*
|
|
19173
|
-
* @private
|
|
19174
|
-
*/
|
|
19175
|
-
function readJsonFile(filePath) {
|
|
19176
|
-
if (!existsSync(filePath)) return {};
|
|
19177
|
-
const content = readFileSync(filePath, "utf8");
|
|
19178
|
-
try {
|
|
19179
|
-
return JSON.parse(content);
|
|
19180
|
-
} catch (error) {
|
|
19181
|
-
throw new Error(`Failed to parse ${filePath}: ${errorMessage(error)}. Fix the JSON syntax or remove the file before running setup.`, { cause: error });
|
|
19182
|
-
}
|
|
19183
|
-
}
|
|
19184
|
-
var VSCODE_DIR, SETTINGS_FILE, EXTENSIONS_FILE, GITIGNORE_FILE, TSCONFIG_FILE, GITIGNORE_ENTRY, PROMPTS_ALIAS, PROMPTS_ALIAS_PATH, setup_default;
|
|
19185
|
-
var init_setup = __esmMin((() => {
|
|
19186
|
-
init_dist();
|
|
19187
|
-
VSCODE_DIR = ".vscode";
|
|
19188
|
-
SETTINGS_FILE = "settings.json";
|
|
19189
|
-
EXTENSIONS_FILE = "extensions.json";
|
|
19190
|
-
GITIGNORE_FILE = ".gitignore";
|
|
19191
|
-
TSCONFIG_FILE = "tsconfig.json";
|
|
19192
|
-
GITIGNORE_ENTRY = ".prompts/client/";
|
|
19193
|
-
PROMPTS_ALIAS = "~prompts";
|
|
19194
|
-
PROMPTS_ALIAS_PATH = "./.prompts/client/index.ts";
|
|
19195
|
-
setup_default = command({
|
|
19196
|
-
description: "Configure VSCode IDE settings for .prompt files",
|
|
19197
|
-
async handler(ctx) {
|
|
19198
|
-
ctx.logger.intro("Prompt SDK — Project Setup");
|
|
19199
|
-
if (await ctx.prompts.confirm({
|
|
19200
|
-
message: "Configure VSCode to treat .prompt files as Markdown with Liquid syntax?",
|
|
19201
|
-
initialValue: true
|
|
19202
|
-
})) {
|
|
19203
|
-
const vscodeDir = resolve(VSCODE_DIR);
|
|
19204
|
-
mkdirSync(vscodeDir, { recursive: true });
|
|
19205
|
-
const settingsPath = resolve(vscodeDir, SETTINGS_FILE);
|
|
19206
|
-
const settings = readJsonFile(settingsPath);
|
|
19207
|
-
const updatedSettings = {
|
|
19208
|
-
...settings,
|
|
19209
|
-
"files.associations": {
|
|
19210
|
-
...settings["files.associations"] ?? {},
|
|
19211
|
-
"*.prompt": "markdown"
|
|
19212
|
-
},
|
|
19213
|
-
"liquid.engine": "standard"
|
|
19214
|
-
};
|
|
19215
|
-
writeFileSync(settingsPath, `${JSON.stringify(updatedSettings, null, 2)}\n`, "utf8");
|
|
19216
|
-
ctx.logger.success(`Updated ${settingsPath}`);
|
|
19217
|
-
}
|
|
19218
|
-
if (await ctx.prompts.confirm({
|
|
19219
|
-
message: "Add Shopify Liquid extension to VSCode recommendations?",
|
|
19220
|
-
initialValue: true
|
|
19221
|
-
})) {
|
|
19222
|
-
const vscodeDir = resolve(VSCODE_DIR);
|
|
19223
|
-
mkdirSync(vscodeDir, { recursive: true });
|
|
19224
|
-
const extensionsPath = resolve(vscodeDir, EXTENSIONS_FILE);
|
|
19225
|
-
const extensions = readJsonFile(extensionsPath);
|
|
19226
|
-
const recommendations = ensureRecommendation(extensions.recommendations ?? [], "sissel.shopify-liquid");
|
|
19227
|
-
const updatedExtensions = {
|
|
19228
|
-
...extensions,
|
|
19229
|
-
recommendations
|
|
19230
|
-
};
|
|
19231
|
-
writeFileSync(extensionsPath, `${JSON.stringify(updatedExtensions, null, 2)}\n`, "utf8");
|
|
19232
|
-
ctx.logger.success(`Updated ${extensionsPath}`);
|
|
19233
|
-
}
|
|
19234
|
-
if (await ctx.prompts.confirm({
|
|
19235
|
-
message: "Add .prompts/client/ to .gitignore? (generated client should not be committed)",
|
|
19236
|
-
initialValue: true
|
|
19237
|
-
})) {
|
|
19238
|
-
const gitignorePath = resolve(GITIGNORE_FILE);
|
|
19239
|
-
const existing = readFileOrEmpty(gitignorePath);
|
|
19240
|
-
if (existing.includes(GITIGNORE_ENTRY)) ctx.logger.info(`${GITIGNORE_ENTRY} already in ${gitignorePath}`);
|
|
19241
|
-
else {
|
|
19242
|
-
writeFileSync(gitignorePath, `${existing}${`${trailingSeparator(existing)}\n# Generated prompt client (created by \`funkai prompts generate\`)\n${GITIGNORE_ENTRY}\n`}`, "utf8");
|
|
19243
|
-
ctx.logger.success(`Added ${GITIGNORE_ENTRY} to ${gitignorePath}`);
|
|
19244
|
-
}
|
|
19245
|
-
}
|
|
19246
|
-
if (await ctx.prompts.confirm({
|
|
19247
|
-
message: "Add ~prompts path alias to tsconfig.json?",
|
|
19248
|
-
initialValue: true
|
|
19249
|
-
})) {
|
|
19250
|
-
const tsconfigPath = resolve(TSCONFIG_FILE);
|
|
19251
|
-
const tsconfig = readJsonFile(tsconfigPath);
|
|
19252
|
-
const compilerOptions = tsconfig.compilerOptions ?? {};
|
|
19253
|
-
const existingPaths = compilerOptions.paths ?? {};
|
|
19254
|
-
if (existingPaths[PROMPTS_ALIAS]) ctx.logger.info(`${PROMPTS_ALIAS} alias already in ${tsconfigPath}`);
|
|
19255
|
-
else {
|
|
19256
|
-
const updatedTsconfig = {
|
|
19257
|
-
...tsconfig,
|
|
19258
|
-
compilerOptions: {
|
|
19259
|
-
...compilerOptions,
|
|
19260
|
-
paths: {
|
|
19261
|
-
...existingPaths,
|
|
19262
|
-
[PROMPTS_ALIAS]: [PROMPTS_ALIAS_PATH]
|
|
19263
|
-
}
|
|
19264
|
-
}
|
|
19265
|
-
};
|
|
19266
|
-
writeFileSync(tsconfigPath, `${JSON.stringify(updatedTsconfig, null, 2)}\n`, "utf8");
|
|
19267
|
-
ctx.logger.success(`Added ${PROMPTS_ALIAS} alias to ${tsconfigPath}`);
|
|
19268
|
-
}
|
|
19269
|
-
}
|
|
19270
|
-
ctx.logger.outro("Project setup complete.");
|
|
19271
|
-
}
|
|
19272
|
-
});
|
|
19273
|
-
}));
|
|
19274
|
-
|
|
19275
19646
|
//#endregion
|
|
19276
19647
|
//#region \0virtual:kidd-static-commands
|
|
19277
19648
|
var _virtual_kidd_static_commands_exports = /* @__PURE__ */ __exportAll$1({ autoload: () => autoload$1 });
|
|
@@ -19282,29 +19653,29 @@ var commands;
|
|
|
19282
19653
|
var init__virtual_kidd_static_commands = __esmMin((() => {
|
|
19283
19654
|
init_tag();
|
|
19284
19655
|
init_generate();
|
|
19285
|
-
init_setup
|
|
19656
|
+
init_setup();
|
|
19286
19657
|
init_validate$1();
|
|
19287
19658
|
init_validate();
|
|
19288
19659
|
init_create();
|
|
19289
19660
|
init_generate$1();
|
|
19290
19661
|
init_lint();
|
|
19291
|
-
init_setup();
|
|
19662
|
+
init_setup$1();
|
|
19292
19663
|
commands = {
|
|
19293
19664
|
"generate": generate_default,
|
|
19294
|
-
"setup": setup_default
|
|
19665
|
+
"setup": setup_default,
|
|
19295
19666
|
"validate": validate_default$1,
|
|
19296
19667
|
"agents": withTag({ commands: { "validate": validate_default } }, "Command"),
|
|
19297
19668
|
"prompts": withTag({ commands: {
|
|
19298
19669
|
"create": create_default,
|
|
19299
19670
|
"generate": generate_default$1,
|
|
19300
19671
|
"lint": lint_default,
|
|
19301
|
-
"setup": setup_default
|
|
19672
|
+
"setup": setup_default$1
|
|
19302
19673
|
} }, "Command")
|
|
19303
19674
|
};
|
|
19304
19675
|
}));
|
|
19305
19676
|
|
|
19306
19677
|
//#endregion
|
|
19307
|
-
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+
|
|
19678
|
+
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+ap_ac2wgu34upx3xtuhxg7cvmcnvm/node_modules/@kidd-cli/core/dist/cli-DhHGZzjZ.js
|
|
19308
19679
|
async function autoload() {
|
|
19309
19680
|
return (await Promise.resolve().then(() => (init__virtual_kidd_static_commands(), _virtual_kidd_static_commands_exports))).autoload();
|
|
19310
19681
|
}
|
|
@@ -20139,7 +20510,7 @@ function resolveVersion(explicit) {
|
|
|
20139
20510
|
return err(VERSION_ERROR);
|
|
20140
20511
|
}
|
|
20141
20512
|
{
|
|
20142
|
-
const parsed = VersionSchema.safeParse("0.
|
|
20513
|
+
const parsed = VersionSchema.safeParse("0.3.1");
|
|
20143
20514
|
if (parsed.success) return ok(parsed.data);
|
|
20144
20515
|
}
|
|
20145
20516
|
return err(VERSION_ERROR);
|
|
@@ -20314,7 +20685,7 @@ var init_cli_DhHGZzjZ = __esmMin((() => {
|
|
|
20314
20685
|
}));
|
|
20315
20686
|
|
|
20316
20687
|
//#endregion
|
|
20317
|
-
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+
|
|
20688
|
+
//#region ../../node_modules/.pnpm/@kidd-cli+core@0.10.0_chokidar@5.0.0_jiti@2.6.1_magicast@0.5.2_vitest@4.1.0_@opentelemetry+ap_ac2wgu34upx3xtuhxg7cvmcnvm/node_modules/@kidd-cli/core/dist/index.js
|
|
20318
20689
|
var init_dist = __esmMin((() => {
|
|
20319
20690
|
init_cli_DhHGZzjZ();
|
|
20320
20691
|
}));
|
|
@@ -20327,16 +20698,18 @@ init_generate();
|
|
|
20327
20698
|
init_create();
|
|
20328
20699
|
init_generate$1();
|
|
20329
20700
|
init_lint();
|
|
20330
|
-
init_setup();
|
|
20331
20701
|
init_setup$1();
|
|
20702
|
+
init_setup();
|
|
20332
20703
|
init_validate$1();
|
|
20704
|
+
init_config();
|
|
20333
20705
|
await cli({
|
|
20334
20706
|
description: "CLI for the funkai AI SDK framework",
|
|
20335
20707
|
name: "funkai",
|
|
20336
20708
|
version: createRequire(import.meta.url)("../package.json").version,
|
|
20709
|
+
config: { schema: configSchema },
|
|
20337
20710
|
commands: {
|
|
20338
20711
|
generate: generate_default,
|
|
20339
|
-
setup: setup_default
|
|
20712
|
+
setup: setup_default,
|
|
20340
20713
|
validate: validate_default$1,
|
|
20341
20714
|
agents: command({
|
|
20342
20715
|
description: "Agent-related commands",
|
|
@@ -20348,7 +20721,7 @@ await cli({
|
|
|
20348
20721
|
create: create_default,
|
|
20349
20722
|
generate: generate_default$1,
|
|
20350
20723
|
lint: lint_default,
|
|
20351
|
-
setup: setup_default
|
|
20724
|
+
setup: setup_default$1
|
|
20352
20725
|
}
|
|
20353
20726
|
})
|
|
20354
20727
|
}
|