spets 0.1.10 → 0.1.12

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.
@@ -302,21 +302,6 @@ function loadTaskMetadata(taskId, cwd = process.cwd()) {
302
302
  }
303
303
  return JSON.parse(readFileSync2(metaPath, "utf-8"));
304
304
  }
305
- function extractPlanSummary(taskId, cwd = process.cwd()) {
306
- const doc = loadDocument(taskId, "01-plan", cwd);
307
- if (!doc) {
308
- return null;
309
- }
310
- const summaryMatch = doc.content.match(/## Summary\n\n(.+?)(?=\n\n##|\n\n$|$)/s);
311
- if (summaryMatch) {
312
- return summaryMatch[1].trim();
313
- }
314
- const goalMatch = doc.content.match(/## Goal\n\n(.+?)(?=\n\n##|\n\n$|$)/s);
315
- if (goalMatch) {
316
- return goalMatch[1].trim();
317
- }
318
- return null;
319
- }
320
305
 
321
306
  export {
322
307
  getSpetsDir,
@@ -338,6 +323,5 @@ export {
338
323
  listTasks,
339
324
  getWorkflowState,
340
325
  saveTaskMetadata,
341
- loadTaskMetadata,
342
- extractPlanSummary
326
+ loadTaskMetadata
343
327
  };
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  saveTaskMetadata,
17
17
  spetsExists,
18
18
  updateDocumentStatus
19
- } from "./chunk-NOS3N4GT.js";
19
+ } from "./chunk-L3K6T5GO.js";
20
20
 
21
21
  // src/index.ts
22
22
  import { Command } from "commander";
@@ -121,19 +121,19 @@ function createDefaultSteps(spetsDir) {
121
121
  writeFileSync(join(implementDir, "template.md"), getImplementTemplate());
122
122
  }
123
123
  function getPlanInstruction() {
124
- const fullTemplate = readFileSync(join(__dirname, "..", ".spets", "steps", "01-plan", "instruction.md"), "utf-8");
124
+ const fullTemplate = readFileSync(join(__dirname, "..", "templates", "steps", "01-plan", "instruction.md"), "utf-8");
125
125
  return fullTemplate;
126
126
  }
127
127
  function getPlanTemplate() {
128
- const fullTemplate = readFileSync(join(__dirname, "..", ".spets", "steps", "01-plan", "template.md"), "utf-8");
128
+ const fullTemplate = readFileSync(join(__dirname, "..", "templates", "steps", "01-plan", "template.md"), "utf-8");
129
129
  return fullTemplate;
130
130
  }
131
131
  function getImplementInstruction() {
132
- const fullTemplate = readFileSync(join(__dirname, "..", ".spets", "steps", "02-implement", "instruction.md"), "utf-8");
132
+ const fullTemplate = readFileSync(join(__dirname, "..", "templates", "steps", "02-implement", "instruction.md"), "utf-8");
133
133
  return fullTemplate;
134
134
  }
135
135
  function getImplementTemplate() {
136
- const fullTemplate = readFileSync(join(__dirname, "..", ".spets", "steps", "02-implement", "template.md"), "utf-8");
136
+ const fullTemplate = readFileSync(join(__dirname, "..", "templates", "steps", "02-implement", "template.md"), "utf-8");
137
137
  return fullTemplate;
138
138
  }
139
139
  function createClaudeCommand(cwd) {
@@ -503,17 +503,12 @@ jobs:
503
503
  run: npm ci
504
504
 
505
505
  - name: Run Spets command
506
- id: spets
507
506
  run: |
508
507
  npx spets github --issue ${gh("github.event.issue.number")} --comment "$COMMENT"
509
- # Check if PR should be created
510
- if [[ "$COMMENT" == "/approve --pr"* ]]; then
511
- echo "create_pr=true" >> $GITHUB_OUTPUT
512
- fi
513
508
  env:
514
509
  COMMENT: ${gh("github.event.comment.body")}
515
510
  CLAUDE_CODE_OAUTH_TOKEN: ${gh("secrets.CLAUDE_CODE_OAUTH_TOKEN")}
516
- GH_TOKEN: ${gh("secrets.GITHUB_TOKEN")}
511
+ GH_TOKEN: ${gh("secrets.PAT_TOKEN")}
517
512
 
518
513
  - name: Push changes
519
514
  run: |
@@ -521,29 +516,6 @@ jobs:
521
516
  git add -A
522
517
  git diff --staged --quiet || git commit -m "Spets: Update from #${gh("github.event.issue.number")}"
523
518
  git push
524
-
525
- - name: Create PR
526
- if: steps.spets.outputs.create_pr == 'true'
527
- run: |
528
- PR_BODY="Closes #${gh("github.event.issue.number")}
529
-
530
- ---
531
-
532
- ## Spets Commands
533
-
534
- | Command | Description |
535
- |---------|-------------|
536
- | \\\`/approve\\\` | Approve current step and continue |
537
- | \\\`/approve --pr\\\` | Approve and create PR |
538
- | \\\`/revise <feedback>\\\` | Request changes with feedback |
539
- | \\\`/reject\\\` | Reject and stop workflow |"
540
-
541
- gh pr create \\
542
- --title "Spets: Issue #${gh("github.event.issue.number")}" \\
543
- --body "$PR_BODY" \\
544
- --repo ${gh("github.repository")}
545
- env:
546
- GH_TOKEN: ${gh("secrets.PAT_TOKEN")}
547
519
  `;
548
520
  }
549
521
 
@@ -1834,7 +1806,9 @@ request: The user's task description for the workflow
1834
1806
  }
1835
1807
 
1836
1808
  // src/commands/github.ts
1837
- import { execSync as execSync4 } from "child_process";
1809
+ import { execSync as execSync4, spawn as spawn4 } from "child_process";
1810
+ import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync4 } from "fs";
1811
+ import { join as join4 } from "path";
1838
1812
  function getGitHubInfo2(cwd) {
1839
1813
  const config = getGitHubConfig(cwd);
1840
1814
  if (config?.owner && config?.repo) {
@@ -1904,7 +1878,7 @@ async function githubCommand(options) {
1904
1878
  }
1905
1879
  if (!taskId) {
1906
1880
  const config2 = loadConfig(cwd);
1907
- const { listTasks: listTasks2 } = await import("./state-54IS3PZH.js");
1881
+ const { listTasks: listTasks2 } = await import("./state-3SI6H5IW.js");
1908
1882
  const tasks = listTasks2(cwd);
1909
1883
  for (const tid of tasks) {
1910
1884
  const state2 = getWorkflowState(tid, config2, cwd);
@@ -1939,7 +1913,8 @@ async function githubCommand(options) {
1939
1913
  console.log(`Approving step: ${state.currentStepName}`);
1940
1914
  updateDocumentStatus(outputPath, "approved");
1941
1915
  if (parsed.createPR) {
1942
- console.log("PR will be created after changes are committed.");
1916
+ console.log("Creating PR with AI-generated content...");
1917
+ await createPullRequestWithAI(githubConfig, taskId, userQuery, cwd);
1943
1918
  }
1944
1919
  if (parsed.createIssue) {
1945
1920
  console.log("Creating/Updating Issue...");
@@ -2024,11 +1999,11 @@ async function postRejectionComment(config, taskId, stepName) {
2024
1999
  await postComment(config, comment);
2025
2000
  }
2026
2001
  async function postComment(config, body) {
2027
- const { spawn: spawn4 } = await import("child_process");
2002
+ const { spawn: spawn5 } = await import("child_process");
2028
2003
  const { owner, repo, prNumber, issueNumber } = config;
2029
2004
  const args = prNumber ? ["pr", "comment", String(prNumber), "--body", body, "-R", `${owner}/${repo}`] : ["issue", "comment", String(issueNumber), "--body", body, "-R", `${owner}/${repo}`];
2030
2005
  return new Promise((resolve, reject) => {
2031
- const proc = spawn4("gh", args, { stdio: "inherit" });
2006
+ const proc = spawn5("gh", args, { stdio: "inherit" });
2032
2007
  proc.on("close", (code) => {
2033
2008
  if (code !== 0) reject(new Error(`gh failed with code ${code}`));
2034
2009
  else resolve();
@@ -2036,6 +2011,158 @@ async function postComment(config, body) {
2036
2011
  proc.on("error", reject);
2037
2012
  });
2038
2013
  }
2014
+ async function createPullRequestWithAI(config, taskId, userQuery, cwd) {
2015
+ const { spawnSync } = await import("child_process");
2016
+ const { owner, repo, issueNumber } = config;
2017
+ console.log(`Creating PR with taskId: ${taskId}`);
2018
+ const outputsDir = join4(cwd, ".spets", "outputs", taskId);
2019
+ const outputs = [];
2020
+ if (existsSync4(outputsDir)) {
2021
+ const files = readdirSync2(outputsDir).filter((f) => f.endsWith(".md"));
2022
+ for (const file of files) {
2023
+ const content = readFileSync2(join4(outputsDir, file), "utf-8");
2024
+ outputs.push({ step: file.replace(".md", ""), content });
2025
+ }
2026
+ }
2027
+ const prContent = await generatePRContent(userQuery, outputs, issueNumber);
2028
+ const result = spawnSync("gh", [
2029
+ "pr",
2030
+ "create",
2031
+ "--repo",
2032
+ `${owner}/${repo}`,
2033
+ "--title",
2034
+ prContent.title,
2035
+ "--body",
2036
+ prContent.body,
2037
+ "--head",
2038
+ config.branch || "HEAD"
2039
+ ], { encoding: "utf-8" });
2040
+ if (result.status !== 0) {
2041
+ throw new Error(`Failed to create PR: ${result.stderr}`);
2042
+ }
2043
+ console.log(`PR created: ${result.stdout.trim()}`);
2044
+ const match = result.stdout.match(/\/pull\/(\d+)/);
2045
+ if (!match) {
2046
+ throw new Error("Failed to parse PR number from gh output");
2047
+ }
2048
+ return parseInt(match[1], 10);
2049
+ }
2050
+ async function generatePRContent(userQuery, outputs, issueNumber) {
2051
+ const outputsSummary = outputs.map((o) => `### ${o.step}
2052
+ ${o.content}`).join("\n\n---\n\n");
2053
+ const prompt = `You are generating a Pull Request title and body based on the following workflow outputs.
2054
+
2055
+ ## Original Request
2056
+ ${userQuery}
2057
+
2058
+ ## Workflow Outputs
2059
+ ${outputsSummary}
2060
+
2061
+ ---
2062
+
2063
+ Generate a PR title and body. Requirements:
2064
+ 1. Title: Concise (max 72 chars), describes what was done (e.g., "feat: add user authentication", "fix: resolve login timeout issue")
2065
+ 2. Body:
2066
+ - Start with "## Summary" section (2-3 sentences max)
2067
+ - Include "## Changes" section with bullet points of key changes
2068
+ ${issueNumber ? `- End with "Closes #${issueNumber}"` : ""}
2069
+
2070
+ Output format (use exactly this format):
2071
+ \`\`\`pr
2072
+ TITLE: <your title here>
2073
+ BODY:
2074
+ <your body here>
2075
+ \`\`\`
2076
+
2077
+ Generate the PR content now:`;
2078
+ try {
2079
+ const response = await callClaude(prompt);
2080
+ return parsePRResponse(response, userQuery, issueNumber);
2081
+ } catch (error) {
2082
+ console.warn("Failed to generate PR content with AI, using fallback:", error);
2083
+ return getFallbackPRContent(userQuery, issueNumber);
2084
+ }
2085
+ }
2086
+ function parsePRResponse(response, userQuery, issueNumber) {
2087
+ const prMatch = response.match(/```pr\n([\s\S]*?)```/);
2088
+ if (!prMatch) {
2089
+ return getFallbackPRContent(userQuery, issueNumber);
2090
+ }
2091
+ const content = prMatch[1];
2092
+ const titleMatch = content.match(/TITLE:\s*(.+)/);
2093
+ const bodyMatch = content.match(/BODY:\n([\s\S]*)/);
2094
+ if (!titleMatch || !bodyMatch) {
2095
+ return getFallbackPRContent(userQuery, issueNumber);
2096
+ }
2097
+ let body = bodyMatch[1].trim();
2098
+ body += `
2099
+
2100
+ ---
2101
+
2102
+ ## Spets Commands
2103
+
2104
+ | Command | Description |
2105
+ |---------|-------------|
2106
+ | \`/approve\` | Approve current step and continue |
2107
+ | \`/approve --pr\` | Approve and create PR |
2108
+ | \`/revise <feedback>\` | Request changes with feedback |
2109
+ | \`/reject\` | Reject and stop workflow |`;
2110
+ return {
2111
+ title: titleMatch[1].trim(),
2112
+ body
2113
+ };
2114
+ }
2115
+ function getFallbackPRContent(userQuery, issueNumber) {
2116
+ const title = userQuery.slice(0, 72) + (userQuery.length > 72 ? "..." : "");
2117
+ let body = `## Summary
2118
+
2119
+ ${userQuery}
2120
+
2121
+ ---
2122
+
2123
+ ## Spets Commands
2124
+
2125
+ | Command | Description |
2126
+ |---------|-------------|
2127
+ | \`/approve\` | Approve current step and continue |
2128
+ | \`/approve --pr\` | Approve and create PR |
2129
+ | \`/revise <feedback>\` | Request changes with feedback |
2130
+ | \`/reject\` | Reject and stop workflow |`;
2131
+ if (issueNumber) {
2132
+ body += `
2133
+
2134
+ Closes #${issueNumber}`;
2135
+ }
2136
+ return { title, body };
2137
+ }
2138
+ async function callClaude(prompt) {
2139
+ return new Promise((resolve, reject) => {
2140
+ const proc = spawn4("claude", [
2141
+ "--permission-mode",
2142
+ "bypassPermissions"
2143
+ ], { stdio: ["pipe", "pipe", "pipe"] });
2144
+ let stdout = "";
2145
+ let stderr = "";
2146
+ proc.stdout.on("data", (data) => {
2147
+ stdout += data.toString();
2148
+ });
2149
+ proc.stderr.on("data", (data) => {
2150
+ stderr += data.toString();
2151
+ });
2152
+ proc.stdin.write(prompt);
2153
+ proc.stdin.end();
2154
+ proc.on("close", (code) => {
2155
+ if (code !== 0) {
2156
+ reject(new Error(`Claude CLI failed (code ${code}): ${stderr}`));
2157
+ } else {
2158
+ resolve(stdout);
2159
+ }
2160
+ });
2161
+ proc.on("error", (err) => {
2162
+ reject(new Error(`Failed to run Claude: ${err.message}`));
2163
+ });
2164
+ });
2165
+ }
2039
2166
  async function createOrUpdateIssue(config, taskId, userQuery, stepName) {
2040
2167
  const { spawnSync } = await import("child_process");
2041
2168
  const { owner, repo, issueNumber } = config;
@@ -2083,8 +2210,8 @@ _Updated by [Spets](https://github.com/eatnug/spets)_`;
2083
2210
  }
2084
2211
 
2085
2212
  // src/orchestrator/index.ts
2086
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
2087
- import { join as join4, dirname as dirname2 } from "path";
2213
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
2214
+ import { join as join5, dirname as dirname2 } from "path";
2088
2215
  import matter from "gray-matter";
2089
2216
  var Orchestrator = class {
2090
2217
  cwd;
@@ -2102,33 +2229,33 @@ var Orchestrator = class {
2102
2229
  return getOutputsDir(this.cwd);
2103
2230
  }
2104
2231
  getStatePath(taskId) {
2105
- return join4(this.getOutputPath(), taskId, ".state.json");
2232
+ return join5(this.getOutputPath(), taskId, ".state.json");
2106
2233
  }
2107
2234
  getSpecPath(taskId, step) {
2108
- return join4(this.getOutputPath(), taskId, `${step}.md`);
2235
+ return join5(this.getOutputPath(), taskId, `${step}.md`);
2109
2236
  }
2110
2237
  getStepInstructionPath(step) {
2111
- return join4(getStepsDir(this.cwd), step, "instruction.md");
2238
+ return join5(getStepsDir(this.cwd), step, "instruction.md");
2112
2239
  }
2113
2240
  getStepTemplatePath(step) {
2114
- return join4(getStepsDir(this.cwd), step, "template.md");
2241
+ return join5(getStepsDir(this.cwd), step, "template.md");
2115
2242
  }
2116
2243
  // ===========================================================================
2117
2244
  // State Management
2118
2245
  // ===========================================================================
2119
2246
  loadState(taskId) {
2120
2247
  const statePath = this.getStatePath(taskId);
2121
- if (!existsSync4(statePath)) {
2248
+ if (!existsSync5(statePath)) {
2122
2249
  return null;
2123
2250
  }
2124
- const data = JSON.parse(readFileSync2(statePath, "utf-8"));
2251
+ const data = JSON.parse(readFileSync3(statePath, "utf-8"));
2125
2252
  return data;
2126
2253
  }
2127
2254
  saveState(state) {
2128
2255
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2129
2256
  const statePath = this.getStatePath(state.taskId);
2130
2257
  const dir = dirname2(statePath);
2131
- if (!existsSync4(dir)) {
2258
+ if (!existsSync5(dir)) {
2132
2259
  mkdirSync3(dir, { recursive: true });
2133
2260
  }
2134
2261
  writeFileSync3(statePath, JSON.stringify(state, null, 2));
@@ -2137,10 +2264,10 @@ var Orchestrator = class {
2137
2264
  // Spec Helpers
2138
2265
  // ===========================================================================
2139
2266
  checkUnresolvedQuestions(specPath) {
2140
- if (!existsSync4(specPath)) {
2267
+ if (!existsSync5(specPath)) {
2141
2268
  return [];
2142
2269
  }
2143
- const content = readFileSync2(specPath, "utf-8");
2270
+ const content = readFileSync3(specPath, "utf-8");
2144
2271
  const { data } = matter(content);
2145
2272
  const questions = [];
2146
2273
  if (data.open_questions && Array.isArray(data.open_questions)) {
@@ -2168,13 +2295,13 @@ var Orchestrator = class {
2168
2295
  let previousOutput;
2169
2296
  if (state.stepIndex > 1) {
2170
2297
  const prevStep = steps[state.stepIndex - 2];
2171
- const prevPath = join4(outputPath, state.taskId, `${prevStep}.md`);
2172
- if (existsSync4(prevPath)) {
2298
+ const prevPath = join5(outputPath, state.taskId, `${prevStep}.md`);
2299
+ if (existsSync5(prevPath)) {
2173
2300
  previousOutput = prevPath;
2174
2301
  }
2175
2302
  }
2176
2303
  const templatePath = this.getStepTemplatePath(state.currentStep);
2177
- const hasTemplate = existsSync4(templatePath);
2304
+ const hasTemplate = existsSync5(templatePath);
2178
2305
  return {
2179
2306
  type: "step",
2180
2307
  step: state.currentStep,
@@ -2186,7 +2313,7 @@ var Orchestrator = class {
2186
2313
  instruction: this.getStepInstructionPath(state.currentStep),
2187
2314
  template: hasTemplate ? templatePath : void 0,
2188
2315
  previousOutput,
2189
- output: join4(outputPath, state.taskId, `${state.currentStep}.md`),
2316
+ output: join5(outputPath, state.taskId, `${state.currentStep}.md`),
2190
2317
  revisionFeedback: state.revisionFeedback
2191
2318
  },
2192
2319
  onComplete: `done ${state.taskId}`
@@ -2216,7 +2343,7 @@ var Orchestrator = class {
2216
2343
  step: state.currentStep,
2217
2344
  stepIndex: state.stepIndex,
2218
2345
  totalSteps: state.totalSteps,
2219
- specPath: join4(outputPath, state.taskId, `${state.currentStep}.md`),
2346
+ specPath: join5(outputPath, state.taskId, `${state.currentStep}.md`),
2220
2347
  options: ["approve", "revise", "reject", "stop"],
2221
2348
  onComplete: {
2222
2349
  approve: `approve ${state.taskId}`,
@@ -2231,8 +2358,8 @@ var Orchestrator = class {
2231
2358
  const outputPath = this.getOutputPath();
2232
2359
  const outputs = [];
2233
2360
  for (let i = 0; i < state.stepIndex; i++) {
2234
- const specPath = join4(outputPath, state.taskId, `${steps[i]}.md`);
2235
- if (existsSync4(specPath)) {
2361
+ const specPath = join5(outputPath, state.taskId, `${steps[i]}.md`);
2362
+ if (existsSync5(specPath)) {
2236
2363
  outputs.push(specPath);
2237
2364
  }
2238
2365
  }
@@ -2289,7 +2416,7 @@ var Orchestrator = class {
2289
2416
  return this.responseError(`No workflow found: ${taskId}`, taskId);
2290
2417
  }
2291
2418
  const specPath = this.getSpecPath(taskId, state.currentStep);
2292
- if (!existsSync4(specPath)) {
2419
+ if (!existsSync5(specPath)) {
2293
2420
  return this.responseError(`Spec not found: ${specPath}`, taskId, state.currentStep);
2294
2421
  }
2295
2422
  const questions = this.checkUnresolvedQuestions(specPath);
@@ -2311,8 +2438,8 @@ var Orchestrator = class {
2311
2438
  return this.responseError(`No workflow found: ${taskId}`, taskId);
2312
2439
  }
2313
2440
  const specPath = this.getSpecPath(taskId, state.currentStep);
2314
- if (existsSync4(specPath)) {
2315
- const content = readFileSync2(specPath, "utf-8");
2441
+ if (existsSync5(specPath)) {
2442
+ const content = readFileSync3(specPath, "utf-8");
2316
2443
  const { content: body, data } = matter(content);
2317
2444
  if (data.open_questions && Array.isArray(data.open_questions)) {
2318
2445
  data.open_questions = data.open_questions.map((q, i) => ({
@@ -2338,8 +2465,8 @@ var Orchestrator = class {
2338
2465
  }
2339
2466
  const steps = this.getSteps();
2340
2467
  const specPath = this.getSpecPath(taskId, state.currentStep);
2341
- if (existsSync4(specPath)) {
2342
- const content = readFileSync2(specPath, "utf-8");
2468
+ if (existsSync5(specPath)) {
2469
+ const content = readFileSync3(specPath, "utf-8");
2343
2470
  const { content: body, data } = matter(content);
2344
2471
  data.status = "approved";
2345
2472
  data.updated_at = (/* @__PURE__ */ new Date()).toISOString();
@@ -2380,8 +2507,8 @@ var Orchestrator = class {
2380
2507
  return this.responseError(`No workflow found: ${taskId}`, taskId);
2381
2508
  }
2382
2509
  const specPath = this.getSpecPath(taskId, state.currentStep);
2383
- if (existsSync4(specPath)) {
2384
- const content = readFileSync2(specPath, "utf-8");
2510
+ if (existsSync5(specPath)) {
2511
+ const content = readFileSync3(specPath, "utf-8");
2385
2512
  const { content: body, data } = matter(content);
2386
2513
  data.status = "rejected";
2387
2514
  data.updated_at = (/* @__PURE__ */ new Date()).toISOString();
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  createDocument,
3
3
  ensureTaskDir,
4
- extractPlanSummary,
5
4
  generateTaskId,
6
5
  getOutputPath,
7
6
  getTaskDir,
@@ -13,11 +12,10 @@ import {
13
12
  saveDocument,
14
13
  saveTaskMetadata,
15
14
  updateDocumentStatus
16
- } from "./chunk-NOS3N4GT.js";
15
+ } from "./chunk-L3K6T5GO.js";
17
16
  export {
18
17
  createDocument,
19
18
  ensureTaskDir,
20
- extractPlanSummary,
21
19
  generateTaskId,
22
20
  getOutputPath,
23
21
  getTaskDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spets",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Spec Driven Development Execution Framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,7 +8,8 @@
8
8
  "spets": "./dist/index.js"
9
9
  },
10
10
  "files": [
11
- "dist"
11
+ "dist",
12
+ "templates"
12
13
  ],
13
14
  "scripts": {
14
15
  "dev": "tsx src/index.ts",