@funkai/cli 0.1.4 → 0.3.0
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 +81 -0
- package/dist/index.mjs +939 -3484
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/src/commands/generate.ts +8 -1
- package/src/commands/prompts/create.ts +31 -2
- package/src/commands/prompts/generate.ts +91 -35
- package/src/commands/prompts/lint.ts +63 -20
- package/src/commands/prompts/setup.ts +153 -119
- package/src/commands/setup.ts +129 -4
- package/src/commands/validate.ts +8 -1
- package/src/config.ts +28 -0
- package/src/index.ts +4 -0
- package/src/lib/prompts/__tests__/flatten.test.ts +29 -27
- package/src/lib/prompts/__tests__/frontmatter.test.ts +17 -15
- package/src/lib/prompts/codegen.ts +149 -79
- package/src/lib/prompts/extract-variables.ts +20 -9
- package/src/lib/prompts/flatten.ts +62 -16
- package/src/lib/prompts/frontmatter.ts +97 -53
- package/src/lib/prompts/lint.ts +18 -23
- package/src/lib/prompts/paths.ts +90 -24
- package/src/lib/prompts/pipeline.ts +87 -11
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
|
|
4
|
+
import type { Context } from "@kidd-cli/core";
|
|
4
5
|
import { command } from "@kidd-cli/core";
|
|
5
6
|
|
|
6
7
|
const VSCODE_DIR = ".vscode";
|
|
@@ -12,138 +13,167 @@ const GITIGNORE_ENTRY = ".prompts/client/";
|
|
|
12
13
|
const PROMPTS_ALIAS = "~prompts";
|
|
13
14
|
const PROMPTS_ALIAS_PATH = "./.prompts/client/index.ts";
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Shared prompts setup logic used by both `funkai prompts setup` and `funkai setup`.
|
|
18
|
+
*
|
|
19
|
+
* @param ctx - The CLI context with prompts and logger.
|
|
20
|
+
*/
|
|
21
|
+
export async function setupPrompts(ctx: Pick<Context, "prompts" | "logger">): Promise<void> {
|
|
22
|
+
const shouldConfigure = await ctx.prompts.confirm({
|
|
23
|
+
message: "Configure VSCode to treat .prompt files as Markdown with Liquid syntax?",
|
|
24
|
+
initialValue: true,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (shouldConfigure) {
|
|
28
|
+
const vscodeDir = resolve(VSCODE_DIR);
|
|
29
|
+
mkdirSync(vscodeDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
const settingsPath = resolve(vscodeDir, SETTINGS_FILE);
|
|
32
|
+
const settings = readJsonFile(settingsPath);
|
|
33
|
+
|
|
34
|
+
const updatedSettings = {
|
|
35
|
+
...settings,
|
|
36
|
+
"files.associations": {
|
|
37
|
+
...((settings["files.associations"] ?? {}) as Record<string, string>),
|
|
38
|
+
"*.prompt": "markdown",
|
|
39
|
+
},
|
|
40
|
+
"liquid.engine": "standard",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
writeFileSync(settingsPath, `${JSON.stringify(updatedSettings, null, 2)}\n`, "utf8");
|
|
44
|
+
ctx.logger.success(`Updated ${settingsPath}`);
|
|
45
|
+
}
|
|
19
46
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
47
|
+
const shouldRecommend = await ctx.prompts.confirm({
|
|
48
|
+
message: "Add Shopify Liquid extension to VSCode recommendations?",
|
|
49
|
+
initialValue: true,
|
|
50
|
+
});
|
|
24
51
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
52
|
+
if (shouldRecommend) {
|
|
53
|
+
const vscodeDir = resolve(VSCODE_DIR);
|
|
54
|
+
mkdirSync(vscodeDir, { recursive: true });
|
|
28
55
|
|
|
29
|
-
|
|
30
|
-
|
|
56
|
+
const extensionsPath = resolve(vscodeDir, EXTENSIONS_FILE);
|
|
57
|
+
const extensions = readJsonFile(extensionsPath);
|
|
31
58
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"files.associations": {
|
|
35
|
-
...((settings["files.associations"] ?? {}) as Record<string, string>),
|
|
36
|
-
"*.prompt": "markdown",
|
|
37
|
-
},
|
|
38
|
-
"liquid.engine": "standard",
|
|
39
|
-
};
|
|
59
|
+
const currentRecs = (extensions.recommendations ?? []) as string[];
|
|
60
|
+
const extensionId = "sissel.shopify-liquid";
|
|
40
61
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
const recommendations = ensureRecommendation(currentRecs, extensionId);
|
|
63
|
+
const updatedExtensions = {
|
|
64
|
+
...extensions,
|
|
65
|
+
recommendations,
|
|
66
|
+
};
|
|
44
67
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
if (shouldRecommend) {
|
|
51
|
-
const vscodeDir = resolve(VSCODE_DIR);
|
|
52
|
-
mkdirSync(vscodeDir, { recursive: true });
|
|
53
|
-
|
|
54
|
-
const extensionsPath = resolve(vscodeDir, EXTENSIONS_FILE);
|
|
55
|
-
const extensions = readJsonFile(extensionsPath);
|
|
56
|
-
|
|
57
|
-
const currentRecs = (extensions.recommendations ?? []) as string[];
|
|
58
|
-
const extensionId = "sissel.shopify-liquid";
|
|
59
|
-
|
|
60
|
-
// oxlint-disable-next-line unicorn/prefer-ternary -- no-ternary rule forbids ternaries
|
|
61
|
-
const recommendations: string[] = (() => {
|
|
62
|
-
if (currentRecs.includes(extensionId)) {
|
|
63
|
-
return currentRecs;
|
|
64
|
-
}
|
|
65
|
-
return [...currentRecs, extensionId];
|
|
66
|
-
})();
|
|
67
|
-
const updatedExtensions = {
|
|
68
|
-
...extensions,
|
|
69
|
-
recommendations,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
writeFileSync(extensionsPath, `${JSON.stringify(updatedExtensions, null, 2)}\n`, "utf8");
|
|
73
|
-
ctx.logger.success(`Updated ${extensionsPath}`);
|
|
74
|
-
}
|
|
68
|
+
writeFileSync(extensionsPath, `${JSON.stringify(updatedExtensions, null, 2)}\n`, "utf8");
|
|
69
|
+
ctx.logger.success(`Updated ${extensionsPath}`);
|
|
70
|
+
}
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
ctx.logger.info(`${GITIGNORE_ENTRY} already in ${gitignorePath}`);
|
|
93
|
-
} else {
|
|
94
|
-
// oxlint-disable-next-line unicorn/prefer-ternary -- no-ternary rule forbids ternaries
|
|
95
|
-
const separator: string = (() => {
|
|
96
|
-
if (existing.length > 0 && !existing.endsWith("\n")) {
|
|
97
|
-
return "\n";
|
|
98
|
-
}
|
|
99
|
-
return "";
|
|
100
|
-
})();
|
|
101
|
-
const block = `${separator}\n# Generated prompt client (created by \`funkai prompts generate\`)\n${GITIGNORE_ENTRY}\n`;
|
|
102
|
-
writeFileSync(gitignorePath, `${existing}${block}`, "utf8");
|
|
103
|
-
ctx.logger.success(`Added ${GITIGNORE_ENTRY} to ${gitignorePath}`);
|
|
104
|
-
}
|
|
72
|
+
const shouldGitignore = await ctx.prompts.confirm({
|
|
73
|
+
message: "Add .prompts/client/ to .gitignore? (generated client should not be committed)",
|
|
74
|
+
initialValue: true,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (shouldGitignore) {
|
|
78
|
+
const gitignorePath = resolve(GITIGNORE_FILE);
|
|
79
|
+
const existing = readFileOrEmpty(gitignorePath);
|
|
80
|
+
|
|
81
|
+
if (existing.includes(GITIGNORE_ENTRY)) {
|
|
82
|
+
ctx.logger.info(`${GITIGNORE_ENTRY} already in ${gitignorePath}`);
|
|
83
|
+
} else {
|
|
84
|
+
const separator = trailingSeparator(existing);
|
|
85
|
+
const block = `${separator}\n# Generated prompt client (created by \`funkai prompts generate\`)\n${GITIGNORE_ENTRY}\n`;
|
|
86
|
+
writeFileSync(gitignorePath, `${existing}${block}`, "utf8");
|
|
87
|
+
ctx.logger.success(`Added ${GITIGNORE_ENTRY} to ${gitignorePath}`);
|
|
105
88
|
}
|
|
89
|
+
}
|
|
106
90
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
},
|
|
91
|
+
const shouldTsconfig = await ctx.prompts.confirm({
|
|
92
|
+
message: "Add ~prompts path alias to tsconfig.json?",
|
|
93
|
+
initialValue: true,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (shouldTsconfig) {
|
|
97
|
+
const tsconfigPath = resolve(TSCONFIG_FILE);
|
|
98
|
+
const tsconfig = readJsonFile(tsconfigPath);
|
|
99
|
+
|
|
100
|
+
const compilerOptions = (tsconfig.compilerOptions ?? {}) as Record<string, unknown>;
|
|
101
|
+
const existingPaths = (compilerOptions.paths ?? {}) as Record<string, string[]>;
|
|
102
|
+
|
|
103
|
+
// oxlint-disable-next-line security/detect-object-injection -- safe: PROMPTS_ALIAS is a known constant string
|
|
104
|
+
if (existingPaths[PROMPTS_ALIAS]) {
|
|
105
|
+
ctx.logger.info(`${PROMPTS_ALIAS} alias already in ${tsconfigPath}`);
|
|
106
|
+
} else {
|
|
107
|
+
const updatedTsconfig = {
|
|
108
|
+
...tsconfig,
|
|
109
|
+
compilerOptions: {
|
|
110
|
+
...compilerOptions,
|
|
111
|
+
paths: {
|
|
112
|
+
...existingPaths,
|
|
113
|
+
// oxlint-disable-next-line security/detect-object-injection -- safe: PROMPTS_ALIAS is a known constant string
|
|
114
|
+
[PROMPTS_ALIAS]: [PROMPTS_ALIAS_PATH],
|
|
132
115
|
},
|
|
133
|
-
}
|
|
116
|
+
},
|
|
117
|
+
};
|
|
134
118
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
119
|
+
writeFileSync(tsconfigPath, `${JSON.stringify(updatedTsconfig, null, 2)}\n`, "utf8");
|
|
120
|
+
ctx.logger.success(`Added ${PROMPTS_ALIAS} alias to ${tsconfigPath}`);
|
|
138
121
|
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
139
124
|
|
|
140
|
-
|
|
125
|
+
export default command({
|
|
126
|
+
description: "Configure VSCode IDE settings for .prompt files",
|
|
127
|
+
async handler(ctx) {
|
|
128
|
+
ctx.logger.intro("Prompt SDK — Project Setup");
|
|
129
|
+
await setupPrompts(ctx);
|
|
130
|
+
ctx.logger.outro("Prompts setup complete.");
|
|
141
131
|
},
|
|
142
132
|
});
|
|
143
133
|
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Private
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
/** @private */
|
|
139
|
+
function errorMessage(error: unknown): string {
|
|
140
|
+
if (error instanceof Error) {
|
|
141
|
+
return error.message;
|
|
142
|
+
}
|
|
143
|
+
return String(error);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @private */
|
|
147
|
+
function ensureRecommendation(current: readonly string[], id: string): string[] {
|
|
148
|
+
if (current.includes(id)) {
|
|
149
|
+
return [...current];
|
|
150
|
+
}
|
|
151
|
+
return [...current, id];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** @private */
|
|
155
|
+
function readFileOrEmpty(filePath: string): string {
|
|
156
|
+
// oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: reading file from CLI discovery
|
|
157
|
+
if (existsSync(filePath)) {
|
|
158
|
+
return readFileSync(filePath, "utf8");
|
|
159
|
+
}
|
|
160
|
+
return "";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** @private */
|
|
164
|
+
function trailingSeparator(content: string): string {
|
|
165
|
+
if (content.length > 0 && !content.endsWith("\n")) {
|
|
166
|
+
return "\n";
|
|
167
|
+
}
|
|
168
|
+
return "";
|
|
169
|
+
}
|
|
170
|
+
|
|
144
171
|
/**
|
|
145
|
-
* Read a JSON file, returning an empty object if it doesn't exist
|
|
146
|
-
*
|
|
172
|
+
* Read a JSON file, returning an empty object if it doesn't exist.
|
|
173
|
+
* Throws if the file exists but contains invalid JSON, preventing
|
|
174
|
+
* silent data loss from overwriting malformed config files.
|
|
175
|
+
*
|
|
176
|
+
* @private
|
|
147
177
|
*/
|
|
148
178
|
function readJsonFile(filePath: string): Record<string, unknown> {
|
|
149
179
|
// oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: tsconfig path from CLI discovery
|
|
@@ -151,11 +181,15 @@ function readJsonFile(filePath: string): Record<string, unknown> {
|
|
|
151
181
|
return {};
|
|
152
182
|
}
|
|
153
183
|
|
|
184
|
+
// oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: reading tsconfig file
|
|
185
|
+
const content = readFileSync(filePath, "utf8");
|
|
154
186
|
try {
|
|
155
|
-
// oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: reading tsconfig file
|
|
156
|
-
const content = readFileSync(filePath, "utf8");
|
|
157
187
|
return JSON.parse(content) as Record<string, unknown>;
|
|
158
|
-
} catch {
|
|
159
|
-
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Failed to parse ${filePath}: ${errorMessage(error)}. ` +
|
|
191
|
+
"Fix the JSON syntax or remove the file before running setup.",
|
|
192
|
+
{ cause: error },
|
|
193
|
+
);
|
|
160
194
|
}
|
|
161
195
|
}
|
package/src/commands/setup.ts
CHANGED
|
@@ -1,12 +1,137 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
|
|
1
4
|
import { command } from "@kidd-cli/core";
|
|
5
|
+
import { match } from "ts-pattern";
|
|
6
|
+
|
|
7
|
+
import { setupPrompts } from "@/commands/prompts/setup.js";
|
|
8
|
+
|
|
9
|
+
/** @private */
|
|
10
|
+
const CONFIG_TEMPLATE_AGENTS_ONLY = `import { defineConfig } from "@funkai/config";
|
|
11
|
+
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
agents: {},
|
|
14
|
+
});
|
|
15
|
+
`;
|
|
2
16
|
|
|
3
17
|
export default command({
|
|
4
18
|
description: "Set up your project for the funkai SDK",
|
|
5
19
|
async handler(ctx) {
|
|
6
20
|
ctx.logger.intro("funkai — Project Setup");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ctx.
|
|
10
|
-
|
|
21
|
+
|
|
22
|
+
// --- Domain selection ---
|
|
23
|
+
const domains = await ctx.prompts.multiselect({
|
|
24
|
+
message: "Which domains do you want to set up?",
|
|
25
|
+
options: [
|
|
26
|
+
{
|
|
27
|
+
value: "prompts" as const,
|
|
28
|
+
label: "Prompts",
|
|
29
|
+
hint: "LiquidJS templating, codegen, IDE integration",
|
|
30
|
+
},
|
|
31
|
+
{ value: "agents" as const, label: "Agents", hint: "Agent scaffolding and configuration" },
|
|
32
|
+
],
|
|
33
|
+
initialValues: ["prompts" as const],
|
|
34
|
+
required: true,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const hasPrompts = domains.includes("prompts");
|
|
38
|
+
const hasAgents = domains.includes("agents");
|
|
39
|
+
|
|
40
|
+
// --- Create funkai.config.ts ---
|
|
41
|
+
const shouldCreateConfig = await ctx.prompts.confirm({
|
|
42
|
+
message: "Create funkai.config.ts?",
|
|
43
|
+
initialValue: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (shouldCreateConfig) {
|
|
47
|
+
let includes = ["src/prompts/**"];
|
|
48
|
+
let out = ".prompts/client";
|
|
49
|
+
|
|
50
|
+
if (hasPrompts) {
|
|
51
|
+
const includesInput = await ctx.prompts.text({
|
|
52
|
+
message: "Prompt include patterns (comma-separated)",
|
|
53
|
+
defaultValue: "src/prompts/**",
|
|
54
|
+
placeholder: "src/prompts/**",
|
|
55
|
+
});
|
|
56
|
+
includes = includesInput.split(",").map((r) => r.trim());
|
|
57
|
+
|
|
58
|
+
out = await ctx.prompts.text({
|
|
59
|
+
message: "Output directory for generated prompt modules",
|
|
60
|
+
defaultValue: ".prompts/client",
|
|
61
|
+
placeholder: ".prompts/client",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const template = buildConfigTemplate({ hasPrompts, hasAgents, includes, out });
|
|
66
|
+
const configPath = resolve("funkai.config.ts");
|
|
67
|
+
// oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: writing config file to project root
|
|
68
|
+
writeFileSync(configPath, template, "utf8");
|
|
69
|
+
ctx.logger.success(`Created ${configPath}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- Run domain-specific setup ---
|
|
73
|
+
if (hasPrompts) {
|
|
74
|
+
ctx.logger.info("");
|
|
75
|
+
ctx.logger.info("Configuring Prompts...");
|
|
76
|
+
await setupPrompts(ctx);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (hasAgents) {
|
|
80
|
+
ctx.logger.info("");
|
|
81
|
+
ctx.logger.info("Agents configuration is not yet available.");
|
|
82
|
+
ctx.logger.info("The agents section has been added to your config for future use.");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
ctx.logger.outro("Project setup complete.");
|
|
11
86
|
},
|
|
12
87
|
});
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// Private
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
/** @private */
|
|
94
|
+
interface ConfigTemplateOptions {
|
|
95
|
+
readonly hasPrompts: boolean;
|
|
96
|
+
readonly hasAgents: boolean;
|
|
97
|
+
readonly includes: readonly string[];
|
|
98
|
+
readonly out: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** @private */
|
|
102
|
+
function buildConfigTemplate({
|
|
103
|
+
hasPrompts,
|
|
104
|
+
hasAgents,
|
|
105
|
+
includes,
|
|
106
|
+
out,
|
|
107
|
+
}: ConfigTemplateOptions): string {
|
|
108
|
+
if (hasPrompts && hasAgents) {
|
|
109
|
+
return buildCustomTemplate(includes, out, true);
|
|
110
|
+
}
|
|
111
|
+
if (hasPrompts) {
|
|
112
|
+
return buildCustomTemplate(includes, out, false);
|
|
113
|
+
}
|
|
114
|
+
return CONFIG_TEMPLATE_AGENTS_ONLY;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @private */
|
|
118
|
+
function buildCustomTemplate(
|
|
119
|
+
includes: readonly string[],
|
|
120
|
+
out: string,
|
|
121
|
+
includeAgents: boolean,
|
|
122
|
+
): string {
|
|
123
|
+
const includesStr = includes.map((r) => `"${r}"`).join(", ");
|
|
124
|
+
const agentsBlock = match(includeAgents)
|
|
125
|
+
.with(true, () => "\n agents: {},\n")
|
|
126
|
+
.with(false, () => "\n")
|
|
127
|
+
.exhaustive();
|
|
128
|
+
|
|
129
|
+
return `import { defineConfig } from "@funkai/config";
|
|
130
|
+
|
|
131
|
+
export default defineConfig({
|
|
132
|
+
prompts: {
|
|
133
|
+
includes: [${includesStr}],
|
|
134
|
+
out: "${out}",
|
|
135
|
+
},${agentsBlock}});
|
|
136
|
+
`;
|
|
137
|
+
}
|
package/src/commands/validate.ts
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import { command } from "@kidd-cli/core";
|
|
2
2
|
|
|
3
3
|
import { handleLint, lintArgs } from "./prompts/lint.js";
|
|
4
|
+
import { getConfig } from "@/config.js";
|
|
4
5
|
|
|
5
6
|
export default command({
|
|
6
7
|
description: "Run all validations across the funkai SDK",
|
|
7
8
|
options: lintArgs,
|
|
8
9
|
handler(ctx) {
|
|
9
10
|
const { silent } = ctx.args;
|
|
11
|
+
const config = getConfig(ctx);
|
|
10
12
|
|
|
11
13
|
// --- Prompts validation ---
|
|
12
14
|
if (!silent) {
|
|
13
15
|
ctx.logger.info("Running prompts validation...");
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
handleLint(
|
|
18
|
+
handleLint({
|
|
19
|
+
args: ctx.args,
|
|
20
|
+
config: config.prompts,
|
|
21
|
+
logger: ctx.logger,
|
|
22
|
+
fail: ctx.fail,
|
|
23
|
+
});
|
|
17
24
|
|
|
18
25
|
// --- Future: agents validation ---
|
|
19
26
|
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FunkaiConfig } from "@funkai/config";
|
|
2
|
+
import { configSchema } from "@funkai/config";
|
|
3
|
+
import type { ConfigType, Context } from "@kidd-cli/core";
|
|
4
|
+
|
|
5
|
+
export { configSchema };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Extract the typed funkai config from a command context.
|
|
9
|
+
*
|
|
10
|
+
* kidd-cli's `Merge<CliConfig, TConfig>` erases augmented keys when
|
|
11
|
+
* `TConfig` defaults to `Record<string, unknown>`, so we cast here.
|
|
12
|
+
*
|
|
13
|
+
* @param ctx - The CLI context.
|
|
14
|
+
* @returns The typed funkai configuration.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const config = getConfig(ctx);
|
|
19
|
+
* const promptsConfig = config.prompts;
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function getConfig(ctx: Pick<Context, "config">): Readonly<FunkaiConfig> {
|
|
23
|
+
return ctx.config as unknown as Readonly<FunkaiConfig>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare module "@kidd-cli/core" {
|
|
27
|
+
interface CliConfig extends ConfigType<typeof configSchema> {}
|
|
28
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import promptsLint from "@/commands/prompts/lint.js";
|
|
|
10
10
|
import promptsSetup from "@/commands/prompts/setup.js";
|
|
11
11
|
import setup from "@/commands/setup.js";
|
|
12
12
|
import validate from "@/commands/validate.js";
|
|
13
|
+
import { configSchema } from "@/config.js";
|
|
13
14
|
|
|
14
15
|
const require = createRequire(import.meta.url);
|
|
15
16
|
const packageJson = require("../package.json") as { readonly version: string };
|
|
@@ -18,6 +19,9 @@ await cli({
|
|
|
18
19
|
description: "CLI for the funkai AI SDK framework",
|
|
19
20
|
name: "funkai",
|
|
20
21
|
version: packageJson.version,
|
|
22
|
+
config: {
|
|
23
|
+
schema: configSchema,
|
|
24
|
+
},
|
|
21
25
|
commands: {
|
|
22
26
|
generate,
|
|
23
27
|
setup,
|