@funkai/cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.turbo/turbo-build.log +15 -16
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +1 -1
  4. package/dist/index.mjs +5018 -298
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +5 -5
  7. package/src/commands/generate.ts +4 -62
  8. package/src/commands/prompts/generate.ts +75 -48
  9. package/src/commands/prompts/lint.ts +62 -44
  10. package/src/commands/prompts/setup.ts +2 -7
  11. package/src/commands/validate.ts +4 -27
  12. package/src/index.ts +6 -1
  13. package/src/lib/prompts/codegen.ts +12 -16
  14. package/src/lib/prompts/flatten.ts +2 -7
  15. package/src/lib/prompts/frontmatter.ts +28 -33
  16. package/src/lib/prompts/lint.ts +8 -8
  17. package/src/lib/prompts/paths.ts +5 -5
  18. package/src/lib/prompts/pipeline.ts +6 -7
  19. package/.turbo/turbo-test$colon$coverage.log +0 -36
  20. package/.turbo/turbo-test.log +0 -26
  21. package/.turbo/turbo-typecheck.log +0 -4
  22. package/coverage/lcov-report/base.css +0 -224
  23. package/coverage/lcov-report/block-navigation.js +0 -87
  24. package/coverage/lcov-report/commands/create.ts.html +0 -208
  25. package/coverage/lcov-report/commands/generate.ts.html +0 -388
  26. package/coverage/lcov-report/commands/index.html +0 -161
  27. package/coverage/lcov-report/commands/lint.ts.html +0 -331
  28. package/coverage/lcov-report/commands/setup.ts.html +0 -493
  29. package/coverage/lcov-report/favicon.png +0 -0
  30. package/coverage/lcov-report/index.html +0 -131
  31. package/coverage/lcov-report/lib/codegen.ts.html +0 -805
  32. package/coverage/lcov-report/lib/extract-variables.ts.html +0 -181
  33. package/coverage/lcov-report/lib/flatten.ts.html +0 -385
  34. package/coverage/lcov-report/lib/frontmatter.ts.html +0 -487
  35. package/coverage/lcov-report/lib/index.html +0 -191
  36. package/coverage/lcov-report/lib/lint.ts.html +0 -307
  37. package/coverage/lcov-report/lib/paths.ts.html +0 -487
  38. package/coverage/lcov-report/prettify.css +0 -1
  39. package/coverage/lcov-report/prettify.js +0 -2
  40. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  41. package/coverage/lcov-report/sorter.js +0 -210
  42. package/coverage/lcov.info +0 -749
@@ -1,208 +0,0 @@
1
-
2
- <!doctype html>
3
- <html lang="en">
4
-
5
- <head>
6
- <title>Code coverage report for commands/create.ts</title>
7
- <meta charset="utf-8" />
8
- <link rel="stylesheet" href="../prettify.css" />
9
- <link rel="stylesheet" href="../base.css" />
10
- <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
- <meta name="viewport" content="width=device-width, initial-scale=1" />
12
- <style type='text/css'>
13
- .coverage-summary .sorter {
14
- background-image: url(../sort-arrow-sprite.png);
15
- }
16
- </style>
17
- </head>
18
-
19
- <body>
20
- <div class='wrapper'>
21
- <div class='pad1'>
22
- <h1><a href="../index.html">All files</a> / <a href="index.html">commands</a> create.ts</h1>
23
- <div class='clearfix'>
24
-
25
- <div class='fl pad1y space-right2'>
26
- <span class="strong">0% </span>
27
- <span class="quiet">Statements</span>
28
- <span class='fraction'>0/13</span>
29
- </div>
30
-
31
-
32
- <div class='fl pad1y space-right2'>
33
- <span class="strong">0% </span>
34
- <span class="quiet">Branches</span>
35
- <span class='fraction'>0/2</span>
36
- </div>
37
-
38
-
39
- <div class='fl pad1y space-right2'>
40
- <span class="strong">0% </span>
41
- <span class="quiet">Functions</span>
42
- <span class='fraction'>0/5</span>
43
- </div>
44
-
45
-
46
- <div class='fl pad1y space-right2'>
47
- <span class="strong">0% </span>
48
- <span class="quiet">Lines</span>
49
- <span class='fraction'>0/12</span>
50
- </div>
51
-
52
-
53
- </div>
54
- <p class="quiet">
55
- Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
- </p>
57
- <template id="filterTemplate">
58
- <div class="quiet">
59
- Filter:
60
- <input type="search" id="fileSearch">
61
- </div>
62
- </template>
63
- </div>
64
- <div class='status-line low'></div>
65
- <pre><table class="coverage">
66
- <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
- <a name='L2'></a><a href='#L2'>2</a>
68
- <a name='L3'></a><a href='#L3'>3</a>
69
- <a name='L4'></a><a href='#L4'>4</a>
70
- <a name='L5'></a><a href='#L5'>5</a>
71
- <a name='L6'></a><a href='#L6'>6</a>
72
- <a name='L7'></a><a href='#L7'>7</a>
73
- <a name='L8'></a><a href='#L8'>8</a>
74
- <a name='L9'></a><a href='#L9'>9</a>
75
- <a name='L10'></a><a href='#L10'>10</a>
76
- <a name='L11'></a><a href='#L11'>11</a>
77
- <a name='L12'></a><a href='#L12'>12</a>
78
- <a name='L13'></a><a href='#L13'>13</a>
79
- <a name='L14'></a><a href='#L14'>14</a>
80
- <a name='L15'></a><a href='#L15'>15</a>
81
- <a name='L16'></a><a href='#L16'>16</a>
82
- <a name='L17'></a><a href='#L17'>17</a>
83
- <a name='L18'></a><a href='#L18'>18</a>
84
- <a name='L19'></a><a href='#L19'>19</a>
85
- <a name='L20'></a><a href='#L20'>20</a>
86
- <a name='L21'></a><a href='#L21'>21</a>
87
- <a name='L22'></a><a href='#L22'>22</a>
88
- <a name='L23'></a><a href='#L23'>23</a>
89
- <a name='L24'></a><a href='#L24'>24</a>
90
- <a name='L25'></a><a href='#L25'>25</a>
91
- <a name='L26'></a><a href='#L26'>26</a>
92
- <a name='L27'></a><a href='#L27'>27</a>
93
- <a name='L28'></a><a href='#L28'>28</a>
94
- <a name='L29'></a><a href='#L29'>29</a>
95
- <a name='L30'></a><a href='#L30'>30</a>
96
- <a name='L31'></a><a href='#L31'>31</a>
97
- <a name='L32'></a><a href='#L32'>32</a>
98
- <a name='L33'></a><a href='#L33'>33</a>
99
- <a name='L34'></a><a href='#L34'>34</a>
100
- <a name='L35'></a><a href='#L35'>35</a>
101
- <a name='L36'></a><a href='#L36'>36</a>
102
- <a name='L37'></a><a href='#L37'>37</a>
103
- <a name='L38'></a><a href='#L38'>38</a>
104
- <a name='L39'></a><a href='#L39'>39</a>
105
- <a name='L40'></a><a href='#L40'>40</a>
106
- <a name='L41'></a><a href='#L41'>41</a>
107
- <a name='L42'></a><a href='#L42'>42</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
108
- <span class="cline-any cline-neutral">&nbsp;</span>
109
- <span class="cline-any cline-neutral">&nbsp;</span>
110
- <span class="cline-any cline-neutral">&nbsp;</span>
111
- <span class="cline-any cline-neutral">&nbsp;</span>
112
- <span class="cline-any cline-neutral">&nbsp;</span>
113
- <span class="cline-any cline-neutral">&nbsp;</span>
114
- <span class="cline-any cline-no">&nbsp;</span>
115
- <span class="cline-any cline-neutral">&nbsp;</span>
116
- <span class="cline-any cline-neutral">&nbsp;</span>
117
- <span class="cline-any cline-neutral">&nbsp;</span>
118
- <span class="cline-any cline-neutral">&nbsp;</span>
119
- <span class="cline-any cline-neutral">&nbsp;</span>
120
- <span class="cline-any cline-neutral">&nbsp;</span>
121
- <span class="cline-any cline-neutral">&nbsp;</span>
122
- <span class="cline-any cline-neutral">&nbsp;</span>
123
- <span class="cline-any cline-neutral">&nbsp;</span>
124
- <span class="cline-any cline-neutral">&nbsp;</span>
125
- <span class="cline-any cline-neutral">&nbsp;</span>
126
- <span class="cline-any cline-neutral">&nbsp;</span>
127
- <span class="cline-any cline-neutral">&nbsp;</span>
128
- <span class="cline-any cline-no">&nbsp;</span>
129
- <span class="cline-any cline-no">&nbsp;</span>
130
- <span class="cline-any cline-no">&nbsp;</span>
131
- <span class="cline-any cline-no">&nbsp;</span>
132
- <span class="cline-any cline-no">&nbsp;</span>
133
- <span class="cline-any cline-no">&nbsp;</span>
134
- <span class="cline-any cline-neutral">&nbsp;</span>
135
- <span class="cline-any cline-neutral">&nbsp;</span>
136
- <span class="cline-any cline-no">&nbsp;</span>
137
- <span class="cline-any cline-no">&nbsp;</span>
138
- <span class="cline-any cline-neutral">&nbsp;</span>
139
- <span class="cline-any cline-neutral">&nbsp;</span>
140
- <span class="cline-any cline-neutral">&nbsp;</span>
141
- <span class="cline-any cline-no">&nbsp;</span>
142
- <span class="cline-any cline-neutral">&nbsp;</span>
143
- <span class="cline-any cline-no">&nbsp;</span>
144
- <span class="cline-any cline-neutral">&nbsp;</span>
145
- <span class="cline-any cline-no">&nbsp;</span>
146
- <span class="cline-any cline-neutral">&nbsp;</span>
147
- <span class="cline-any cline-neutral">&nbsp;</span>
148
- <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { existsSync, mkdirSync, writeFileSync } from "node:fs";
149
- import { resolve } from "node:path";
150
- &nbsp;
151
- import { command } from "@kidd-cli/core";
152
- import { match, P } from "ts-pattern";
153
- import { z } from "zod";
154
- &nbsp;
155
- const TEMPLATE = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(n</span>ame: string) =&gt; <span class="cstat-no" title="statement not covered" >`---</span></span>
156
- name: ${name}
157
- ---
158
- &nbsp;
159
- `;
160
- &nbsp;
161
- export default command({
162
- description: "Create a new .prompt file",
163
- args: z.object({
164
- name: z.string().describe("Prompt name (kebab-case)"),
165
- out: z.string().optional().describe("Output directory (defaults to cwd)"),
166
- partial: z.boolean().default(false).describe("Create as a partial in .prompts/partials/"),
167
- }),
168
- <span class="fstat-no" title="function not covered" > handler(c</span>tx) {
169
- const { name, out, partial } = <span class="cstat-no" title="statement not covered" >ctx.args;</span>
170
- const <span class="cstat-no" title="statement not covered" >dir = match({ partial, out })</span>
171
- .with({ partial: true }, <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >() =&gt; r</span>esolve(".prompts/partials"))</span>
172
- .with({ out: P.string }, <span class="fstat-no" title="function not covered" >({</span> out: outDir <span class="cstat-no" title="statement not covered" >}) =&gt; resolve(outDir))</span>
173
- .otherwise(<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >p</span>rocess.cwd())</span>;
174
- const <span class="cstat-no" title="statement not covered" >filePath = resolve(dir, `${name}.prompt`);</span>
175
- &nbsp;
176
- // oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: user-provided CLI argument for prompt file creation
177
- <span class="cstat-no" title="statement not covered" > if (existsSync(filePath)) {</span>
178
- <span class="cstat-no" title="statement not covered" > ctx.fail(`File already exists: ${filePath}`);</span>
179
- }
180
- &nbsp;
181
- // oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: directory derived from user CLI path argument
182
- <span class="cstat-no" title="statement not covered" > mkdirSync(dir, { recursive: true });</span>
183
- // oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: file path derived from user CLI path argument
184
- <span class="cstat-no" title="statement not covered" > writeFileSync(filePath, TEMPLATE(name), "utf-8");</span>
185
- &nbsp;
186
- <span class="cstat-no" title="statement not covered" > ctx.logger.success(`Created ${filePath}`);</span>
187
- },
188
- });
189
- &nbsp;</pre></td></tr></table></pre>
190
-
191
- <div class='push'></div><!-- for sticky footer -->
192
- </div><!-- /wrapper -->
193
- <div class='footer quiet pad2 space-top1 center small'>
194
- Code coverage generated by
195
- <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
196
- at 2026-03-13T00:28:56.897Z
197
- </div>
198
- <script src="../prettify.js"></script>
199
- <script>
200
- window.onload = function () {
201
- prettyPrint();
202
- };
203
- </script>
204
- <script src="../sorter.js"></script>
205
- <script src="../block-navigation.js"></script>
206
- </body>
207
- </html>
208
-
@@ -1,388 +0,0 @@
1
-
2
- <!doctype html>
3
- <html lang="en">
4
-
5
- <head>
6
- <title>Code coverage report for commands/generate.ts</title>
7
- <meta charset="utf-8" />
8
- <link rel="stylesheet" href="../prettify.css" />
9
- <link rel="stylesheet" href="../base.css" />
10
- <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
- <meta name="viewport" content="width=device-width, initial-scale=1" />
12
- <style type='text/css'>
13
- .coverage-summary .sorter {
14
- background-image: url(../sort-arrow-sprite.png);
15
- }
16
- </style>
17
- </head>
18
-
19
- <body>
20
- <div class='wrapper'>
21
- <div class='pad1'>
22
- <h1><a href="../index.html">All files</a> / <a href="index.html">commands</a> generate.ts</h1>
23
- <div class='clearfix'>
24
-
25
- <div class='fl pad1y space-right2'>
26
- <span class="strong">0% </span>
27
- <span class="quiet">Statements</span>
28
- <span class='fraction'>0/41</span>
29
- </div>
30
-
31
-
32
- <div class='fl pad1y space-right2'>
33
- <span class="strong">0% </span>
34
- <span class="quiet">Branches</span>
35
- <span class='fraction'>0/12</span>
36
- </div>
37
-
38
-
39
- <div class='fl pad1y space-right2'>
40
- <span class="strong">0% </span>
41
- <span class="quiet">Functions</span>
42
- <span class='fraction'>0/6</span>
43
- </div>
44
-
45
-
46
- <div class='fl pad1y space-right2'>
47
- <span class="strong">0% </span>
48
- <span class="quiet">Lines</span>
49
- <span class='fraction'>0/40</span>
50
- </div>
51
-
52
-
53
- </div>
54
- <p class="quiet">
55
- Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
- </p>
57
- <template id="filterTemplate">
58
- <div class="quiet">
59
- Filter:
60
- <input type="search" id="fileSearch">
61
- </div>
62
- </template>
63
- </div>
64
- <div class='status-line low'></div>
65
- <pre><table class="coverage">
66
- <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
- <a name='L2'></a><a href='#L2'>2</a>
68
- <a name='L3'></a><a href='#L3'>3</a>
69
- <a name='L4'></a><a href='#L4'>4</a>
70
- <a name='L5'></a><a href='#L5'>5</a>
71
- <a name='L6'></a><a href='#L6'>6</a>
72
- <a name='L7'></a><a href='#L7'>7</a>
73
- <a name='L8'></a><a href='#L8'>8</a>
74
- <a name='L9'></a><a href='#L9'>9</a>
75
- <a name='L10'></a><a href='#L10'>10</a>
76
- <a name='L11'></a><a href='#L11'>11</a>
77
- <a name='L12'></a><a href='#L12'>12</a>
78
- <a name='L13'></a><a href='#L13'>13</a>
79
- <a name='L14'></a><a href='#L14'>14</a>
80
- <a name='L15'></a><a href='#L15'>15</a>
81
- <a name='L16'></a><a href='#L16'>16</a>
82
- <a name='L17'></a><a href='#L17'>17</a>
83
- <a name='L18'></a><a href='#L18'>18</a>
84
- <a name='L19'></a><a href='#L19'>19</a>
85
- <a name='L20'></a><a href='#L20'>20</a>
86
- <a name='L21'></a><a href='#L21'>21</a>
87
- <a name='L22'></a><a href='#L22'>22</a>
88
- <a name='L23'></a><a href='#L23'>23</a>
89
- <a name='L24'></a><a href='#L24'>24</a>
90
- <a name='L25'></a><a href='#L25'>25</a>
91
- <a name='L26'></a><a href='#L26'>26</a>
92
- <a name='L27'></a><a href='#L27'>27</a>
93
- <a name='L28'></a><a href='#L28'>28</a>
94
- <a name='L29'></a><a href='#L29'>29</a>
95
- <a name='L30'></a><a href='#L30'>30</a>
96
- <a name='L31'></a><a href='#L31'>31</a>
97
- <a name='L32'></a><a href='#L32'>32</a>
98
- <a name='L33'></a><a href='#L33'>33</a>
99
- <a name='L34'></a><a href='#L34'>34</a>
100
- <a name='L35'></a><a href='#L35'>35</a>
101
- <a name='L36'></a><a href='#L36'>36</a>
102
- <a name='L37'></a><a href='#L37'>37</a>
103
- <a name='L38'></a><a href='#L38'>38</a>
104
- <a name='L39'></a><a href='#L39'>39</a>
105
- <a name='L40'></a><a href='#L40'>40</a>
106
- <a name='L41'></a><a href='#L41'>41</a>
107
- <a name='L42'></a><a href='#L42'>42</a>
108
- <a name='L43'></a><a href='#L43'>43</a>
109
- <a name='L44'></a><a href='#L44'>44</a>
110
- <a name='L45'></a><a href='#L45'>45</a>
111
- <a name='L46'></a><a href='#L46'>46</a>
112
- <a name='L47'></a><a href='#L47'>47</a>
113
- <a name='L48'></a><a href='#L48'>48</a>
114
- <a name='L49'></a><a href='#L49'>49</a>
115
- <a name='L50'></a><a href='#L50'>50</a>
116
- <a name='L51'></a><a href='#L51'>51</a>
117
- <a name='L52'></a><a href='#L52'>52</a>
118
- <a name='L53'></a><a href='#L53'>53</a>
119
- <a name='L54'></a><a href='#L54'>54</a>
120
- <a name='L55'></a><a href='#L55'>55</a>
121
- <a name='L56'></a><a href='#L56'>56</a>
122
- <a name='L57'></a><a href='#L57'>57</a>
123
- <a name='L58'></a><a href='#L58'>58</a>
124
- <a name='L59'></a><a href='#L59'>59</a>
125
- <a name='L60'></a><a href='#L60'>60</a>
126
- <a name='L61'></a><a href='#L61'>61</a>
127
- <a name='L62'></a><a href='#L62'>62</a>
128
- <a name='L63'></a><a href='#L63'>63</a>
129
- <a name='L64'></a><a href='#L64'>64</a>
130
- <a name='L65'></a><a href='#L65'>65</a>
131
- <a name='L66'></a><a href='#L66'>66</a>
132
- <a name='L67'></a><a href='#L67'>67</a>
133
- <a name='L68'></a><a href='#L68'>68</a>
134
- <a name='L69'></a><a href='#L69'>69</a>
135
- <a name='L70'></a><a href='#L70'>70</a>
136
- <a name='L71'></a><a href='#L71'>71</a>
137
- <a name='L72'></a><a href='#L72'>72</a>
138
- <a name='L73'></a><a href='#L73'>73</a>
139
- <a name='L74'></a><a href='#L74'>74</a>
140
- <a name='L75'></a><a href='#L75'>75</a>
141
- <a name='L76'></a><a href='#L76'>76</a>
142
- <a name='L77'></a><a href='#L77'>77</a>
143
- <a name='L78'></a><a href='#L78'>78</a>
144
- <a name='L79'></a><a href='#L79'>79</a>
145
- <a name='L80'></a><a href='#L80'>80</a>
146
- <a name='L81'></a><a href='#L81'>81</a>
147
- <a name='L82'></a><a href='#L82'>82</a>
148
- <a name='L83'></a><a href='#L83'>83</a>
149
- <a name='L84'></a><a href='#L84'>84</a>
150
- <a name='L85'></a><a href='#L85'>85</a>
151
- <a name='L86'></a><a href='#L86'>86</a>
152
- <a name='L87'></a><a href='#L87'>87</a>
153
- <a name='L88'></a><a href='#L88'>88</a>
154
- <a name='L89'></a><a href='#L89'>89</a>
155
- <a name='L90'></a><a href='#L90'>90</a>
156
- <a name='L91'></a><a href='#L91'>91</a>
157
- <a name='L92'></a><a href='#L92'>92</a>
158
- <a name='L93'></a><a href='#L93'>93</a>
159
- <a name='L94'></a><a href='#L94'>94</a>
160
- <a name='L95'></a><a href='#L95'>95</a>
161
- <a name='L96'></a><a href='#L96'>96</a>
162
- <a name='L97'></a><a href='#L97'>97</a>
163
- <a name='L98'></a><a href='#L98'>98</a>
164
- <a name='L99'></a><a href='#L99'>99</a>
165
- <a name='L100'></a><a href='#L100'>100</a>
166
- <a name='L101'></a><a href='#L101'>101</a>
167
- <a name='L102'></a><a href='#L102'>102</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
168
- <span class="cline-any cline-neutral">&nbsp;</span>
169
- <span class="cline-any cline-neutral">&nbsp;</span>
170
- <span class="cline-any cline-neutral">&nbsp;</span>
171
- <span class="cline-any cline-neutral">&nbsp;</span>
172
- <span class="cline-any cline-neutral">&nbsp;</span>
173
- <span class="cline-any cline-neutral">&nbsp;</span>
174
- <span class="cline-any cline-neutral">&nbsp;</span>
175
- <span class="cline-any cline-neutral">&nbsp;</span>
176
- <span class="cline-any cline-neutral">&nbsp;</span>
177
- <span class="cline-any cline-neutral">&nbsp;</span>
178
- <span class="cline-any cline-neutral">&nbsp;</span>
179
- <span class="cline-any cline-neutral">&nbsp;</span>
180
- <span class="cline-any cline-neutral">&nbsp;</span>
181
- <span class="cline-any cline-neutral">&nbsp;</span>
182
- <span class="cline-any cline-neutral">&nbsp;</span>
183
- <span class="cline-any cline-neutral">&nbsp;</span>
184
- <span class="cline-any cline-neutral">&nbsp;</span>
185
- <span class="cline-any cline-neutral">&nbsp;</span>
186
- <span class="cline-any cline-neutral">&nbsp;</span>
187
- <span class="cline-any cline-neutral">&nbsp;</span>
188
- <span class="cline-any cline-neutral">&nbsp;</span>
189
- <span class="cline-any cline-neutral">&nbsp;</span>
190
- <span class="cline-any cline-neutral">&nbsp;</span>
191
- <span class="cline-any cline-no">&nbsp;</span>
192
- <span class="cline-any cline-neutral">&nbsp;</span>
193
- <span class="cline-any cline-no">&nbsp;</span>
194
- <span class="cline-any cline-neutral">&nbsp;</span>
195
- <span class="cline-any cline-no">&nbsp;</span>
196
- <span class="cline-any cline-no">&nbsp;</span>
197
- <span class="cline-any cline-neutral">&nbsp;</span>
198
- <span class="cline-any cline-neutral">&nbsp;</span>
199
- <span class="cline-any cline-no">&nbsp;</span>
200
- <span class="cline-any cline-neutral">&nbsp;</span>
201
- <span class="cline-any cline-no">&nbsp;</span>
202
- <span class="cline-any cline-no">&nbsp;</span>
203
- <span class="cline-any cline-no">&nbsp;</span>
204
- <span class="cline-any cline-neutral">&nbsp;</span>
205
- <span class="cline-any cline-no">&nbsp;</span>
206
- <span class="cline-any cline-no">&nbsp;</span>
207
- <span class="cline-any cline-neutral">&nbsp;</span>
208
- <span class="cline-any cline-no">&nbsp;</span>
209
- <span class="cline-any cline-neutral">&nbsp;</span>
210
- <span class="cline-any cline-no">&nbsp;</span>
211
- <span class="cline-any cline-no">&nbsp;</span>
212
- <span class="cline-any cline-no">&nbsp;</span>
213
- <span class="cline-any cline-no">&nbsp;</span>
214
- <span class="cline-any cline-neutral">&nbsp;</span>
215
- <span class="cline-any cline-no">&nbsp;</span>
216
- <span class="cline-any cline-no">&nbsp;</span>
217
- <span class="cline-any cline-neutral">&nbsp;</span>
218
- <span class="cline-any cline-no">&nbsp;</span>
219
- <span class="cline-any cline-no">&nbsp;</span>
220
- <span class="cline-any cline-no">&nbsp;</span>
221
- <span class="cline-any cline-no">&nbsp;</span>
222
- <span class="cline-any cline-no">&nbsp;</span>
223
- <span class="cline-any cline-no">&nbsp;</span>
224
- <span class="cline-any cline-neutral">&nbsp;</span>
225
- <span class="cline-any cline-neutral">&nbsp;</span>
226
- <span class="cline-any cline-no">&nbsp;</span>
227
- <span class="cline-any cline-neutral">&nbsp;</span>
228
- <span class="cline-any cline-neutral">&nbsp;</span>
229
- <span class="cline-any cline-neutral">&nbsp;</span>
230
- <span class="cline-any cline-neutral">&nbsp;</span>
231
- <span class="cline-any cline-neutral">&nbsp;</span>
232
- <span class="cline-any cline-neutral">&nbsp;</span>
233
- <span class="cline-any cline-neutral">&nbsp;</span>
234
- <span class="cline-any cline-neutral">&nbsp;</span>
235
- <span class="cline-any cline-no">&nbsp;</span>
236
- <span class="cline-any cline-no">&nbsp;</span>
237
- <span class="cline-any cline-no">&nbsp;</span>
238
- <span class="cline-any cline-no">&nbsp;</span>
239
- <span class="cline-any cline-neutral">&nbsp;</span>
240
- <span class="cline-any cline-no">&nbsp;</span>
241
- <span class="cline-any cline-neutral">&nbsp;</span>
242
- <span class="cline-any cline-neutral">&nbsp;</span>
243
- <span class="cline-any cline-neutral">&nbsp;</span>
244
- <span class="cline-any cline-neutral">&nbsp;</span>
245
- <span class="cline-any cline-no">&nbsp;</span>
246
- <span class="cline-any cline-no">&nbsp;</span>
247
- <span class="cline-any cline-neutral">&nbsp;</span>
248
- <span class="cline-any cline-neutral">&nbsp;</span>
249
- <span class="cline-any cline-no">&nbsp;</span>
250
- <span class="cline-any cline-neutral">&nbsp;</span>
251
- <span class="cline-any cline-no">&nbsp;</span>
252
- <span class="cline-any cline-neutral">&nbsp;</span>
253
- <span class="cline-any cline-no">&nbsp;</span>
254
- <span class="cline-any cline-no">&nbsp;</span>
255
- <span class="cline-any cline-neutral">&nbsp;</span>
256
- <span class="cline-any cline-no">&nbsp;</span>
257
- <span class="cline-any cline-neutral">&nbsp;</span>
258
- <span class="cline-any cline-neutral">&nbsp;</span>
259
- <span class="cline-any cline-no">&nbsp;</span>
260
- <span class="cline-any cline-neutral">&nbsp;</span>
261
- <span class="cline-any cline-no">&nbsp;</span>
262
- <span class="cline-any cline-neutral">&nbsp;</span>
263
- <span class="cline-any cline-no">&nbsp;</span>
264
- <span class="cline-any cline-no">&nbsp;</span>
265
- <span class="cline-any cline-neutral">&nbsp;</span>
266
- <span class="cline-any cline-neutral">&nbsp;</span>
267
- <span class="cline-any cline-neutral">&nbsp;</span>
268
- <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
269
- import { resolve } from "node:path";
270
- &nbsp;
271
- import { clean, PARTIALS_DIR } from "@funkai/prompts";
272
- import { command } from "@kidd-cli/core";
273
- import { match } from "ts-pattern";
274
- import { z } from "zod";
275
- &nbsp;
276
- import { generatePromptModule, generateRegistry, type ParsedPrompt } from "@/lib/codegen.js";
277
- import { extractVariables } from "@/lib/extract-variables.js";
278
- import { flattenPartials } from "@/lib/flatten.js";
279
- import { parseFrontmatter } from "@/lib/frontmatter.js";
280
- import { hasLintErrors, lintPrompt, type LintResult } from "@/lib/lint.js";
281
- import { discoverPrompts } from "@/lib/paths.js";
282
- &nbsp;
283
- export default command({
284
- description: "Generate TypeScript modules from .prompt files",
285
- args: z.object({
286
- out: z.string().describe("Output directory for generated files"),
287
- roots: z.array(z.string()).describe("Root directories to scan for .prompt files"),
288
- partials: z.string().optional().describe("Custom partials directory"),
289
- silent: z.boolean().default(false).describe("Suppress output except errors"),
290
- }),
291
- <span class="fstat-no" title="function not covered" > handler(c</span>tx) {
292
- const { out, roots, partials, silent } = <span class="cstat-no" title="statement not covered" >ctx.args;</span>
293
- &nbsp;
294
- const <span class="cstat-no" title="statement not covered" >discovered = discoverPrompts([...roots]);</span>
295
- &nbsp;
296
- <span class="cstat-no" title="statement not covered" > if (!silent) {</span>
297
- <span class="cstat-no" title="statement not covered" > ctx.logger.info(`Found ${discovered.length} prompt(s)`);</span>
298
- }
299
- &nbsp;
300
- const <span class="cstat-no" title="statement not covered" >customPartialsDir = resolve(partials ?? resolve(out, "../partials"));</span>
301
- // oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: checking custom partials directory from CLI config
302
- const <span class="cstat-no" title="statement not covered" >partialsDirs = match(existsSync(customPartialsDir))</span>
303
- .with(true, <span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >[</span>customPartialsDir, PARTIALS_DIR])</span>
304
- .otherwise(<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >[</span>PARTIALS_DIR])</span>;
305
- &nbsp;
306
- const prompts: ParsedPrompt[] = <span class="cstat-no" title="statement not covered" >[];</span>
307
- const lintResults: LintResult[] = <span class="cstat-no" title="statement not covered" >[];</span>
308
- &nbsp;
309
- <span class="cstat-no" title="statement not covered" > for (const d of discovered) {</span>
310
- // oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: reading discovered prompt file
311
- const <span class="cstat-no" title="statement not covered" >raw = readFileSync(d.filePath, "utf-8");</span>
312
- const <span class="cstat-no" title="statement not covered" >frontmatter = parseFrontmatter(raw, d.filePath);</span>
313
- const <span class="cstat-no" title="statement not covered" >template = flattenPartials(clean(raw), partialsDirs);</span>
314
- const <span class="cstat-no" title="statement not covered" >templateVars = extractVariables(template);</span>
315
- &nbsp;
316
- const <span class="cstat-no" title="statement not covered" >result = lintPrompt(frontmatter.name, d.filePath, frontmatter.schema, templateVars);</span>
317
- <span class="cstat-no" title="statement not covered" > lintResults.push(result);</span>
318
- &nbsp;
319
- <span class="cstat-no" title="statement not covered" > if (!silent) {</span>
320
- const varCount = <span class="cstat-no" title="statement not covered" >frontmatter.schema.length;</span>
321
- const <span class="cstat-no" title="statement not covered" >varList = match(varCount &gt; 0)</span>
322
- .with(true, <span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >`</span> (${frontmatter.schema.map(<span class="fstat-no" title="function not covered" >(v</span>) =&gt; <span class="cstat-no" title="statement not covered" >v.name)</span>.join(", ")})`)</span>
323
- .otherwise(<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >"</span>")</span>;
324
- <span class="cstat-no" title="statement not covered" > ctx.logger.step(`${frontmatter.name}${varList}`);</span>
325
- }
326
- &nbsp;
327
- <span class="cstat-no" title="statement not covered" > prompts.push({</span>
328
- name: frontmatter.name,
329
- group: frontmatter.group,
330
- schema: frontmatter.schema,
331
- template,
332
- sourcePath: d.filePath,
333
- });
334
- }
335
- &nbsp;
336
- <span class="cstat-no" title="statement not covered" > for (const result of lintResults) {</span>
337
- <span class="cstat-no" title="statement not covered" > for (const diag of result.diagnostics) {</span>
338
- <span class="cstat-no" title="statement not covered" > if (diag.level === "error") {</span>
339
- <span class="cstat-no" title="statement not covered" > ctx.logger.error(diag.message);</span>
340
- } else {
341
- <span class="cstat-no" title="statement not covered" > ctx.logger.warn(diag.message);</span>
342
- }
343
- }
344
- }
345
- &nbsp;
346
- <span class="cstat-no" title="statement not covered" > if (hasLintErrors(lintResults)) {</span>
347
- <span class="cstat-no" title="statement not covered" > ctx.fail("Lint errors found. Fix them before generating.");</span>
348
- }
349
- &nbsp;
350
- const <span class="cstat-no" title="statement not covered" >outDir = resolve(out);</span>
351
- // oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: output directory from CLI config
352
- <span class="cstat-no" title="statement not covered" > mkdirSync(outDir, { recursive: true });</span>
353
- &nbsp;
354
- <span class="cstat-no" title="statement not covered" > for (const prompt of prompts) {</span>
355
- const <span class="cstat-no" title="statement not covered" >content = generatePromptModule(prompt);</span>
356
- // oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: writing generated module to output directory
357
- <span class="cstat-no" title="statement not covered" > writeFileSync(resolve(outDir, `${prompt.name}.ts`), content, "utf-8");</span>
358
- }
359
- &nbsp;
360
- const <span class="cstat-no" title="statement not covered" >registryContent = generateRegistry(prompts);</span>
361
- // oxlint-disable-next-line security/detect-non-literal-fs-filename -- safe: writing generated registry to output directory
362
- <span class="cstat-no" title="statement not covered" > writeFileSync(resolve(outDir, "index.ts"), registryContent, "utf-8");</span>
363
- &nbsp;
364
- <span class="cstat-no" title="statement not covered" > if (!silent) {</span>
365
- <span class="cstat-no" title="statement not covered" > ctx.logger.success(`Generated ${prompts.length} prompt module(s) + registry → ${outDir}`);</span>
366
- }
367
- },
368
- });
369
- &nbsp;</pre></td></tr></table></pre>
370
-
371
- <div class='push'></div><!-- for sticky footer -->
372
- </div><!-- /wrapper -->
373
- <div class='footer quiet pad2 space-top1 center small'>
374
- Code coverage generated by
375
- <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
376
- at 2026-03-13T00:28:56.897Z
377
- </div>
378
- <script src="../prettify.js"></script>
379
- <script>
380
- window.onload = function () {
381
- prettyPrint();
382
- };
383
- </script>
384
- <script src="../sorter.js"></script>
385
- <script src="../block-navigation.js"></script>
386
- </body>
387
- </html>
388
-