issue-flow 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.
Files changed (51) hide show
  1. package/README.md +226 -0
  2. package/dist/analyze-YYQQP64A.js +12 -0
  3. package/dist/analyze-YYQQP64A.js.map +1 -0
  4. package/dist/chunk-64FU2L2T.js +180 -0
  5. package/dist/chunk-64FU2L2T.js.map +1 -0
  6. package/dist/chunk-7APADNBV.js +68 -0
  7. package/dist/chunk-7APADNBV.js.map +1 -0
  8. package/dist/chunk-7FRXZ2XA.js +78 -0
  9. package/dist/chunk-7FRXZ2XA.js.map +1 -0
  10. package/dist/chunk-D4WBCVB4.js +85 -0
  11. package/dist/chunk-D4WBCVB4.js.map +1 -0
  12. package/dist/chunk-G36DIQ4J.js +80 -0
  13. package/dist/chunk-G36DIQ4J.js.map +1 -0
  14. package/dist/chunk-JEUHCE3V.js +123 -0
  15. package/dist/chunk-JEUHCE3V.js.map +1 -0
  16. package/dist/chunk-L7RIGFP6.js +92 -0
  17. package/dist/chunk-L7RIGFP6.js.map +1 -0
  18. package/dist/chunk-OZVHOVDT.js +43 -0
  19. package/dist/chunk-OZVHOVDT.js.map +1 -0
  20. package/dist/chunk-PINC2LST.js +82 -0
  21. package/dist/chunk-PINC2LST.js.map +1 -0
  22. package/dist/chunk-V356G3JS.js +611 -0
  23. package/dist/chunk-V356G3JS.js.map +1 -0
  24. package/dist/chunk-ZOX7M2C2.js +85 -0
  25. package/dist/chunk-ZOX7M2C2.js.map +1 -0
  26. package/dist/cli.js +76 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/execute-UF3IY2MG.js +11 -0
  29. package/dist/execute-UF3IY2MG.js.map +1 -0
  30. package/dist/generate-WMYJEPI4.js +48 -0
  31. package/dist/generate-WMYJEPI4.js.map +1 -0
  32. package/dist/init-QES2HLP5.js +9 -0
  33. package/dist/init-QES2HLP5.js.map +1 -0
  34. package/dist/plan-BKSUDN34.js +12 -0
  35. package/dist/plan-BKSUDN34.js.map +1 -0
  36. package/dist/pr-E2ASXBWE.js +12 -0
  37. package/dist/pr-E2ASXBWE.js.map +1 -0
  38. package/dist/prd-WYW35SGL.js +12 -0
  39. package/dist/prd-WYW35SGL.js.map +1 -0
  40. package/dist/review-YI2WPNC4.js +12 -0
  41. package/dist/review-YI2WPNC4.js.map +1 -0
  42. package/dist/run-J62FIB5O.js +288 -0
  43. package/dist/run-J62FIB5O.js.map +1 -0
  44. package/package.json +58 -0
  45. package/prompts/analyze.md +28 -0
  46. package/prompts/execute.md +124 -0
  47. package/prompts/generate.md +19 -0
  48. package/prompts/plan.md +49 -0
  49. package/prompts/pr.md +19 -0
  50. package/prompts/prd.md +30 -0
  51. package/prompts/review.md +27 -0
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runHeadless
4
+ } from "./chunk-PINC2LST.js";
5
+ import {
6
+ loadTaskPlan,
7
+ saveTaskPlan
8
+ } from "./chunk-64FU2L2T.js";
9
+ import {
10
+ applyPlaceholders,
11
+ loadPrompt
12
+ } from "./chunk-OZVHOVDT.js";
13
+ import {
14
+ printError,
15
+ printInfo,
16
+ printSuccess
17
+ } from "./chunk-L7RIGFP6.js";
18
+
19
+ // src/commands/review.ts
20
+ import { join } from "path";
21
+ function parseReviewResult(output) {
22
+ const match = output.match(/<review-result>([\s\S]*?)<\/review-result>/);
23
+ if (!match) {
24
+ if (/STATUS:\s*PASS/i.test(output)) {
25
+ return { status: "PASS" };
26
+ }
27
+ if (/STATUS:\s*FAIL/i.test(output)) {
28
+ const findingsMatch2 = output.match(/FINDINGS:([\s\S]*?)(?:$|<\/)/);
29
+ return { status: "FAIL", findings: findingsMatch2?.[1]?.trim() ?? "Unknown findings" };
30
+ }
31
+ return { status: "PASS" };
32
+ }
33
+ const block = match[1];
34
+ if (/STATUS:\s*PASS/i.test(block)) {
35
+ return { status: "PASS" };
36
+ }
37
+ const findingsMatch = block.match(/FINDINGS:([\s\S]*)/);
38
+ return {
39
+ status: "FAIL",
40
+ findings: findingsMatch?.[1]?.trim() ?? "Unknown findings"
41
+ };
42
+ }
43
+ async function runReview(issue) {
44
+ const issueNumber = issue.replace(/^#/, "");
45
+ const issueDir = join("issues", issueNumber);
46
+ const tasksPath = join(issueDir, "tasks.json");
47
+ printInfo(`Reviewing issue #${issueNumber} resolution...`);
48
+ const template = await loadPrompt("review");
49
+ const prompt = applyPlaceholders(template, {
50
+ __ISSUE_NUMBER__: issueNumber,
51
+ __TASKS_PATH__: tasksPath
52
+ });
53
+ const result = await runHeadless({
54
+ prompt,
55
+ maxTurns: 20,
56
+ timeout: 18e4,
57
+ outputFormat: "text",
58
+ allowedTools: ["Bash", "Read", "Glob", "Grep"]
59
+ });
60
+ if (!result.success) {
61
+ printError(`Review failed: ${result.error}`);
62
+ return 1;
63
+ }
64
+ const review = parseReviewResult(result.result);
65
+ if (review.status === "PASS") {
66
+ try {
67
+ const plan = await loadTaskPlan(tasksPath);
68
+ plan.pipeline.reviewCompleted = true;
69
+ await saveTaskPlan(tasksPath, plan);
70
+ } catch {
71
+ }
72
+ printSuccess("Review PASSED \u2014 implementation meets acceptance criteria");
73
+ return 0;
74
+ }
75
+ printError("Review FAILED \u2014 issues found:");
76
+ if (review.findings) {
77
+ console.log(review.findings);
78
+ }
79
+ return 1;
80
+ }
81
+
82
+ export {
83
+ runReview
84
+ };
85
+ //# sourceMappingURL=chunk-D4WBCVB4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/review.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { printError, printInfo, printSuccess } from '../ui/logger.js';\n\nexport interface ReviewResult {\n status: 'PASS' | 'FAIL';\n findings?: string;\n}\n\n/**\n * Parse the <review-result> block from headless output.\n */\nfunction parseReviewResult(output: string): ReviewResult {\n const match = output.match(/<review-result>([\\s\\S]*?)<\\/review-result>/);\n if (!match) {\n // Try to detect PASS/FAIL from the raw output\n if (/STATUS:\\s*PASS/i.test(output)) {\n return { status: 'PASS' };\n }\n if (/STATUS:\\s*FAIL/i.test(output)) {\n const findingsMatch = output.match(/FINDINGS:([\\s\\S]*?)(?:$|<\\/)/);\n return { status: 'FAIL', findings: findingsMatch?.[1]?.trim() ?? 'Unknown findings' };\n }\n // Default to PASS if no explicit status found and no errors\n return { status: 'PASS' };\n }\n\n const block = match[1];\n if (/STATUS:\\s*PASS/i.test(block)) {\n return { status: 'PASS' };\n }\n\n const findingsMatch = block.match(/FINDINGS:([\\s\\S]*)/);\n return {\n status: 'FAIL',\n findings: findingsMatch?.[1]?.trim() ?? 'Unknown findings',\n };\n}\n\nexport async function runReview(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const tasksPath = join(issueDir, 'tasks.json');\n\n printInfo(`Reviewing issue #${issueNumber} resolution...`);\n\n const template = await loadPrompt('review');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __TASKS_PATH__: tasksPath,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 20,\n timeout: 180_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],\n });\n\n if (!result.success) {\n printError(`Review failed: ${result.error}`);\n return 1;\n }\n\n const review = parseReviewResult(result.result);\n\n if (review.status === 'PASS') {\n // Update pipeline state\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.reviewCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist\n }\n\n printSuccess('Review PASSED — implementation meets acceptance criteria');\n return 0;\n }\n\n printError('Review FAILED — issues found:');\n if (review.findings) {\n console.log(review.findings);\n }\n return 1;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY;AAcrB,SAAS,kBAAkB,QAA8B;AACvD,QAAM,QAAQ,OAAO,MAAM,4CAA4C;AACvE,MAAI,CAAC,OAAO;AAEV,QAAI,kBAAkB,KAAK,MAAM,GAAG;AAClC,aAAO,EAAE,QAAQ,OAAO;AAAA,IAC1B;AACA,QAAI,kBAAkB,KAAK,MAAM,GAAG;AAClC,YAAMA,iBAAgB,OAAO,MAAM,8BAA8B;AACjE,aAAO,EAAE,QAAQ,QAAQ,UAAUA,iBAAgB,CAAC,GAAG,KAAK,KAAK,mBAAmB;AAAA,IACtF;AAEA,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAEA,QAAM,QAAQ,MAAM,CAAC;AACrB,MAAI,kBAAkB,KAAK,KAAK,GAAG;AACjC,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAEA,QAAM,gBAAgB,MAAM,MAAM,oBAAoB;AACtD,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,UAAU,gBAAgB,CAAC,GAAG,KAAK,KAAK;AAAA,EAC1C;AACF;AAEA,eAAsB,UAAU,OAAgC;AAC9D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAC3C,QAAM,YAAY,KAAK,UAAU,YAAY;AAE7C,YAAU,oBAAoB,WAAW,gBAAgB;AAEzD,QAAM,WAAW,MAAM,WAAW,QAAQ;AAC1C,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,EAClB,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,EAC/C,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,kBAAkB,OAAO,KAAK,EAAE;AAC3C,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,kBAAkB,OAAO,MAAM;AAE9C,MAAI,OAAO,WAAW,QAAQ;AAE5B,QAAI;AACF,YAAM,OAAO,MAAM,aAAa,SAAS;AACzC,WAAK,SAAS,kBAAkB;AAChC,YAAM,aAAa,WAAW,IAAI;AAAA,IACpC,QAAQ;AAAA,IAER;AAEA,iBAAa,+DAA0D;AACvE,WAAO;AAAA,EACT;AAEA,aAAW,oCAA+B;AAC1C,MAAI,OAAO,UAAU;AACnB,YAAQ,IAAI,OAAO,QAAQ;AAAA,EAC7B;AACA,SAAO;AACT;","names":["findingsMatch"]}
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runHeadless
4
+ } from "./chunk-PINC2LST.js";
5
+ import {
6
+ loadTaskPlan,
7
+ saveTaskPlan
8
+ } from "./chunk-64FU2L2T.js";
9
+ import {
10
+ applyPlaceholders,
11
+ loadPrompt
12
+ } from "./chunk-OZVHOVDT.js";
13
+ import {
14
+ printError,
15
+ printInfo,
16
+ printSuccess
17
+ } from "./chunk-L7RIGFP6.js";
18
+
19
+ // src/commands/prd.ts
20
+ import { mkdir, readFile } from "fs/promises";
21
+ import { join } from "path";
22
+ async function runPrd(issue) {
23
+ const issueNumber = issue.replace(/^#/, "");
24
+ const issueDir = join("issues", issueNumber);
25
+ const prdPath = join(issueDir, "prd.md");
26
+ printInfo(`Generating PRD for issue #${issueNumber}...`);
27
+ await mkdir(issueDir, { recursive: true });
28
+ let analysisContext = "";
29
+ const analysisPath = join(issueDir, "analysis.md");
30
+ try {
31
+ const content = await readFile(analysisPath, "utf-8");
32
+ analysisContext = `
33
+
34
+ Here is the existing analysis for this issue:
35
+
36
+ ${content}`;
37
+ } catch {
38
+ }
39
+ const template = await loadPrompt("prd");
40
+ const prompt = applyPlaceholders(template, {
41
+ __ISSUE_NUMBER__: issueNumber,
42
+ __ANALYSIS_CONTEXT__: analysisContext,
43
+ __PRD_PATH__: prdPath
44
+ });
45
+ const result = await runHeadless({
46
+ prompt,
47
+ maxTurns: 15,
48
+ timeout: 12e4,
49
+ outputFormat: "text",
50
+ allowedTools: ["Bash", "Read", "Glob", "Grep", "Write"]
51
+ });
52
+ if (!result.success) {
53
+ printError(`PRD generation failed: ${result.error}`);
54
+ return 1;
55
+ }
56
+ try {
57
+ const content = await readFile(prdPath, "utf-8");
58
+ if (content.length < 10) {
59
+ printError("PRD file was created but appears empty");
60
+ return 1;
61
+ }
62
+ } catch {
63
+ printError(`PRD file was not created at ${prdPath}`);
64
+ return 1;
65
+ }
66
+ const tasksPath = join(issueDir, "tasks.json");
67
+ try {
68
+ const plan = await loadTaskPlan(tasksPath);
69
+ plan.pipeline.prdCompleted = true;
70
+ await saveTaskPlan(tasksPath, plan);
71
+ } catch {
72
+ }
73
+ printSuccess(`PRD saved to ${prdPath}`);
74
+ return 0;
75
+ }
76
+
77
+ export {
78
+ runPrd
79
+ };
80
+ //# sourceMappingURL=chunk-G36DIQ4J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/prd.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { runHeadless } from '../core/headless.js';\nimport { applyPlaceholders, loadPrompt } from '../core/prompt-resolver.js';\nimport { loadTaskPlan, saveTaskPlan } from '../core/state-manager.js';\nimport { printError, printInfo, printSuccess } from '../ui/logger.js';\n\nexport async function runPrd(issue: string): Promise<number> {\n const issueNumber = issue.replace(/^#/, '');\n const issueDir = join('issues', issueNumber);\n const prdPath = join(issueDir, 'prd.md');\n\n printInfo(`Generating PRD for issue #${issueNumber}...`);\n\n await mkdir(issueDir, { recursive: true });\n\n // Read analysis if it exists\n let analysisContext = '';\n const analysisPath = join(issueDir, 'analysis.md');\n try {\n const content = await readFile(analysisPath, 'utf-8');\n analysisContext = `\\n\\nHere is the existing analysis for this issue:\\n\\n${content}`;\n } catch {\n // No analysis available — that's OK\n }\n\n const template = await loadPrompt('prd');\n const prompt = applyPlaceholders(template, {\n __ISSUE_NUMBER__: issueNumber,\n __ANALYSIS_CONTEXT__: analysisContext,\n __PRD_PATH__: prdPath,\n });\n\n const result = await runHeadless({\n prompt,\n maxTurns: 15,\n timeout: 120_000,\n outputFormat: 'text',\n allowedTools: ['Bash', 'Read', 'Glob', 'Grep', 'Write'],\n });\n\n if (!result.success) {\n printError(`PRD generation failed: ${result.error}`);\n return 1;\n }\n\n // Verify the file was created\n try {\n const content = await readFile(prdPath, 'utf-8');\n if (content.length < 10) {\n printError('PRD file was created but appears empty');\n return 1;\n }\n } catch {\n printError(`PRD file was not created at ${prdPath}`);\n return 1;\n }\n\n // Update pipeline state\n const tasksPath = join(issueDir, 'tasks.json');\n try {\n const plan = await loadTaskPlan(tasksPath);\n plan.pipeline.prdCompleted = true;\n await saveTaskPlan(tasksPath, plan);\n } catch {\n // tasks.json may not exist yet\n }\n\n printSuccess(`PRD saved to ${prdPath}`);\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,OAAO,gBAAgB;AAChC,SAAS,YAAY;AAMrB,eAAsB,OAAO,OAAgC;AAC3D,QAAM,cAAc,MAAM,QAAQ,MAAM,EAAE;AAC1C,QAAM,WAAW,KAAK,UAAU,WAAW;AAC3C,QAAM,UAAU,KAAK,UAAU,QAAQ;AAEvC,YAAU,6BAA6B,WAAW,KAAK;AAEvD,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAGzC,MAAI,kBAAkB;AACtB,QAAM,eAAe,KAAK,UAAU,aAAa;AACjD,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,cAAc,OAAO;AACpD,sBAAkB;AAAA;AAAA;AAAA;AAAA,EAAwD,OAAO;AAAA,EACnF,QAAQ;AAAA,EAER;AAEA,QAAM,WAAW,MAAM,WAAW,KAAK;AACvC,QAAM,SAAS,kBAAkB,UAAU;AAAA,IACzC,kBAAkB;AAAA,IAClB,sBAAsB;AAAA,IACtB,cAAc;AAAA,EAChB,CAAC;AAED,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B;AAAA,IACA,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc;AAAA,IACd,cAAc,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,OAAO;AAAA,EACxD,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,0BAA0B,OAAO,KAAK,EAAE;AACnD,WAAO;AAAA,EACT;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,SAAS,OAAO;AAC/C,QAAI,QAAQ,SAAS,IAAI;AACvB,iBAAW,wCAAwC;AACnD,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AACN,eAAW,+BAA+B,OAAO,EAAE;AACnD,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,KAAK,UAAU,YAAY;AAC7C,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,SAAS;AACzC,SAAK,SAAS,eAAe;AAC7B,UAAM,aAAa,WAAW,IAAI;AAAA,EACpC,QAAQ;AAAA,EAER;AAEA,eAAa,gBAAgB,OAAO,EAAE;AACtC,SAAO;AACT;","names":[]}
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ printError,
4
+ printInfo,
5
+ printSuccess
6
+ } from "./chunk-L7RIGFP6.js";
7
+
8
+ // src/commands/init.ts
9
+ import { execa } from "execa";
10
+ async function checkClaude() {
11
+ try {
12
+ const proc = await execa("claude", ["--version"], { reject: false, timeout: 1e4 });
13
+ if (proc.exitCode === 0) {
14
+ const version = proc.stdout?.toString().trim() ?? "unknown";
15
+ return { name: "claude CLI", passed: true, detail: version };
16
+ }
17
+ return {
18
+ name: "claude CLI",
19
+ passed: false,
20
+ detail: "claude command failed",
21
+ hint: "Install Claude Code: https://docs.anthropic.com/en/docs/claude-code"
22
+ };
23
+ } catch {
24
+ return {
25
+ name: "claude CLI",
26
+ passed: false,
27
+ detail: "claude not found",
28
+ hint: "Install Claude Code: https://docs.anthropic.com/en/docs/claude-code"
29
+ };
30
+ }
31
+ }
32
+ async function checkGh() {
33
+ try {
34
+ const proc = await execa("gh", ["--version"], { reject: false, timeout: 1e4 });
35
+ if (proc.exitCode !== 0) {
36
+ return {
37
+ name: "gh CLI",
38
+ passed: false,
39
+ detail: "gh command failed",
40
+ hint: "Install GitHub CLI: https://cli.github.com/"
41
+ };
42
+ }
43
+ const version = proc.stdout?.toString().split("\n")[0]?.trim() ?? "unknown";
44
+ const auth = await execa("gh", ["auth", "status"], { reject: false, timeout: 1e4 });
45
+ if (auth.exitCode !== 0) {
46
+ return {
47
+ name: "gh CLI",
48
+ passed: false,
49
+ detail: `${version} (not authenticated)`,
50
+ hint: "Run: gh auth login"
51
+ };
52
+ }
53
+ return { name: "gh CLI", passed: true, detail: `${version} (authenticated)` };
54
+ } catch {
55
+ return {
56
+ name: "gh CLI",
57
+ passed: false,
58
+ detail: "gh not found",
59
+ hint: "Install GitHub CLI: https://cli.github.com/"
60
+ };
61
+ }
62
+ }
63
+ async function checkGit() {
64
+ try {
65
+ const proc = await execa("git", ["--version"], { reject: false, timeout: 1e4 });
66
+ if (proc.exitCode !== 0) {
67
+ return {
68
+ name: "git",
69
+ passed: false,
70
+ detail: "git command failed",
71
+ hint: "Install git: https://git-scm.com/"
72
+ };
73
+ }
74
+ const version = proc.stdout?.toString().trim() ?? "unknown";
75
+ const repo = await execa("git", ["rev-parse", "--is-inside-work-tree"], {
76
+ reject: false,
77
+ timeout: 5e3
78
+ });
79
+ if (repo.exitCode !== 0) {
80
+ return {
81
+ name: "git",
82
+ passed: false,
83
+ detail: `${version} (not a git repository)`,
84
+ hint: "Run this command inside a git repository"
85
+ };
86
+ }
87
+ return { name: "git", passed: true, detail: `${version} (inside repo)` };
88
+ } catch {
89
+ return {
90
+ name: "git",
91
+ passed: false,
92
+ detail: "git not found",
93
+ hint: "Install git: https://git-scm.com/"
94
+ };
95
+ }
96
+ }
97
+ async function runInit() {
98
+ printInfo("Checking prerequisites...\n");
99
+ const results = await Promise.all([checkClaude(), checkGh(), checkGit()]);
100
+ for (const r of results) {
101
+ if (r.passed) {
102
+ printSuccess(`${r.name}: ${r.detail}`);
103
+ } else {
104
+ printError(`${r.name}: ${r.detail}`);
105
+ if (r.hint) {
106
+ console.log(` ${r.hint}`);
107
+ }
108
+ }
109
+ }
110
+ const allPassed = results.every((r) => r.passed);
111
+ console.log("");
112
+ if (allPassed) {
113
+ printSuccess("All prerequisites met. Ready to run the pipeline.");
114
+ } else {
115
+ printError("Some prerequisites are missing. Please fix the issues above.");
116
+ }
117
+ return allPassed ? 0 : 1;
118
+ }
119
+
120
+ export {
121
+ runInit
122
+ };
123
+ //# sourceMappingURL=chunk-JEUHCE3V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["import { execa } from 'execa';\nimport { printError, printInfo, printSuccess } from '../ui/logger.js';\n\ninterface CheckResult {\n name: string;\n passed: boolean;\n detail: string;\n hint?: string;\n}\n\nasync function checkClaude(): Promise<CheckResult> {\n try {\n const proc = await execa('claude', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode === 0) {\n const version = proc.stdout?.toString().trim() ?? 'unknown';\n return { name: 'claude CLI', passed: true, detail: version };\n }\n return {\n name: 'claude CLI',\n passed: false,\n detail: 'claude command failed',\n hint: 'Install Claude Code: https://docs.anthropic.com/en/docs/claude-code',\n };\n } catch {\n return {\n name: 'claude CLI',\n passed: false,\n detail: 'claude not found',\n hint: 'Install Claude Code: https://docs.anthropic.com/en/docs/claude-code',\n };\n }\n}\n\nasync function checkGh(): Promise<CheckResult> {\n try {\n const proc = await execa('gh', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode !== 0) {\n return {\n name: 'gh CLI',\n passed: false,\n detail: 'gh command failed',\n hint: 'Install GitHub CLI: https://cli.github.com/',\n };\n }\n const version = proc.stdout?.toString().split('\\n')[0]?.trim() ?? 'unknown';\n\n // Check auth status\n const auth = await execa('gh', ['auth', 'status'], { reject: false, timeout: 10_000 });\n if (auth.exitCode !== 0) {\n return {\n name: 'gh CLI',\n passed: false,\n detail: `${version} (not authenticated)`,\n hint: 'Run: gh auth login',\n };\n }\n return { name: 'gh CLI', passed: true, detail: `${version} (authenticated)` };\n } catch {\n return {\n name: 'gh CLI',\n passed: false,\n detail: 'gh not found',\n hint: 'Install GitHub CLI: https://cli.github.com/',\n };\n }\n}\n\nasync function checkGit(): Promise<CheckResult> {\n try {\n const proc = await execa('git', ['--version'], { reject: false, timeout: 10_000 });\n if (proc.exitCode !== 0) {\n return {\n name: 'git',\n passed: false,\n detail: 'git command failed',\n hint: 'Install git: https://git-scm.com/',\n };\n }\n const version = proc.stdout?.toString().trim() ?? 'unknown';\n\n // Check if current directory is a git repo\n const repo = await execa('git', ['rev-parse', '--is-inside-work-tree'], {\n reject: false,\n timeout: 5_000,\n });\n if (repo.exitCode !== 0) {\n return {\n name: 'git',\n passed: false,\n detail: `${version} (not a git repository)`,\n hint: 'Run this command inside a git repository',\n };\n }\n return { name: 'git', passed: true, detail: `${version} (inside repo)` };\n } catch {\n return {\n name: 'git',\n passed: false,\n detail: 'git not found',\n hint: 'Install git: https://git-scm.com/',\n };\n }\n}\n\nexport async function runInit(): Promise<number> {\n printInfo('Checking prerequisites...\\n');\n\n const results = await Promise.all([checkClaude(), checkGh(), checkGit()]);\n\n for (const r of results) {\n if (r.passed) {\n printSuccess(`${r.name}: ${r.detail}`);\n } else {\n printError(`${r.name}: ${r.detail}`);\n if (r.hint) {\n console.log(` ${r.hint}`);\n }\n }\n }\n\n const allPassed = results.every((r) => r.passed);\n console.log('');\n if (allPassed) {\n printSuccess('All prerequisites met. Ready to run the pipeline.');\n } else {\n printError('Some prerequisites are missing. Please fix the issues above.');\n }\n\n return allPassed ? 0 : 1;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/ui/logger.ts
4
+ import chalk from "chalk";
5
+ function useUnicode() {
6
+ if (process.env.NO_COLOR != null && process.env.NO_COLOR !== "") return false;
7
+ if (!process.stdout.isTTY) return false;
8
+ return true;
9
+ }
10
+ function useColor() {
11
+ if (process.env.NO_COLOR != null && process.env.NO_COLOR !== "") return false;
12
+ if (!process.stdout.isTTY) return false;
13
+ return true;
14
+ }
15
+ function getIcons() {
16
+ if (useUnicode()) {
17
+ return {
18
+ success: "\u2713",
19
+ fail: "\u2717",
20
+ pending: "\u23F3",
21
+ retry: "\u21BB",
22
+ warn: "\u26A0",
23
+ start: "\u25B6",
24
+ end: "\u25A0",
25
+ notReached: "\u25CB"
26
+ };
27
+ }
28
+ return {
29
+ success: "[OK]",
30
+ fail: "[FAIL]",
31
+ pending: "[...]",
32
+ retry: "[RETRY]",
33
+ warn: "[WARN]",
34
+ start: "[START]",
35
+ end: "[END]",
36
+ notReached: "[ ]"
37
+ };
38
+ }
39
+ function getTermWidth() {
40
+ return process.stdout.columns ?? 80;
41
+ }
42
+ function printSuccess(message) {
43
+ const icons = getIcons();
44
+ if (useColor()) {
45
+ console.log(chalk.green(`${icons.success} ${message}`));
46
+ } else {
47
+ console.log(`${icons.success} ${message}`);
48
+ }
49
+ }
50
+ function printError(message) {
51
+ const icons = getIcons();
52
+ if (useColor()) {
53
+ console.log(chalk.red(`${icons.fail} ${message}`));
54
+ } else {
55
+ console.log(`${icons.fail} ${message}`);
56
+ }
57
+ }
58
+ function printWarning(message) {
59
+ const icons = getIcons();
60
+ if (useColor()) {
61
+ console.log(chalk.yellow(`${icons.warn} ${message}`));
62
+ } else {
63
+ console.log(`${icons.warn} ${message}`);
64
+ }
65
+ }
66
+ function printRetry(message) {
67
+ const icons = getIcons();
68
+ if (useColor()) {
69
+ console.log(chalk.yellow(`${icons.retry} ${message}`));
70
+ } else {
71
+ console.log(`${icons.retry} ${message}`);
72
+ }
73
+ }
74
+ function printInfo(message) {
75
+ const icons = getIcons();
76
+ if (useColor()) {
77
+ console.log(chalk.blue(`${icons.start} ${message}`));
78
+ } else {
79
+ console.log(`${icons.start} ${message}`);
80
+ }
81
+ }
82
+
83
+ export {
84
+ getIcons,
85
+ getTermWidth,
86
+ printSuccess,
87
+ printError,
88
+ printWarning,
89
+ printRetry,
90
+ printInfo
91
+ };
92
+ //# sourceMappingURL=chunk-L7RIGFP6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/logger.ts"],"sourcesContent":["import chalk from 'chalk';\n\n/**\n * Detect if unicode output is supported.\n */\nfunction useUnicode(): boolean {\n if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '') return false;\n if (!process.stdout.isTTY) return false;\n return true;\n}\n\n/**\n * Detect if color output is supported.\n */\nfunction useColor(): boolean {\n if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '') return false;\n if (!process.stdout.isTTY) return false;\n return true;\n}\n\nexport interface Icons {\n success: string;\n fail: string;\n pending: string;\n retry: string;\n warn: string;\n start: string;\n end: string;\n notReached: string;\n}\n\nexport function getIcons(): Icons {\n if (useUnicode()) {\n return {\n success: '\\u2713',\n fail: '\\u2717',\n pending: '\\u23F3',\n retry: '\\u21BB',\n warn: '\\u26A0',\n start: '\\u25B6',\n end: '\\u25A0',\n notReached: '\\u25CB',\n };\n }\n return {\n success: '[OK]',\n fail: '[FAIL]',\n pending: '[...]',\n retry: '[RETRY]',\n warn: '[WARN]',\n start: '[START]',\n end: '[END]',\n notReached: '[ ]',\n };\n}\n\n/**\n * Get the terminal width.\n */\nexport function getTermWidth(): number {\n return process.stdout.columns ?? 80;\n}\n\nexport function printSuccess(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n console.log(chalk.green(`${icons.success} ${message}`));\n } else {\n console.log(`${icons.success} ${message}`);\n }\n}\n\nexport function printError(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n console.log(chalk.red(`${icons.fail} ${message}`));\n } else {\n console.log(`${icons.fail} ${message}`);\n }\n}\n\nexport function printWarning(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n console.log(chalk.yellow(`${icons.warn} ${message}`));\n } else {\n console.log(`${icons.warn} ${message}`);\n }\n}\n\nexport function printRetry(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n console.log(chalk.yellow(`${icons.retry} ${message}`));\n } else {\n console.log(`${icons.retry} ${message}`);\n }\n}\n\nexport function printInfo(message: string): void {\n const icons = getIcons();\n if (useColor()) {\n console.log(chalk.blue(`${icons.start} ${message}`));\n } else {\n console.log(`${icons.start} ${message}`);\n }\n}\n"],"mappings":";;;AAAA,OAAO,WAAW;AAKlB,SAAS,aAAsB;AAC7B,MAAI,QAAQ,IAAI,YAAY,QAAQ,QAAQ,IAAI,aAAa,GAAI,QAAO;AACxE,MAAI,CAAC,QAAQ,OAAO,MAAO,QAAO;AAClC,SAAO;AACT;AAKA,SAAS,WAAoB;AAC3B,MAAI,QAAQ,IAAI,YAAY,QAAQ,QAAQ,IAAI,aAAa,GAAI,QAAO;AACxE,MAAI,CAAC,QAAQ,OAAO,MAAO,QAAO;AAClC,SAAO;AACT;AAaO,SAAS,WAAkB;AAChC,MAAI,WAAW,GAAG;AAChB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,MACP,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,MACL,YAAY;AAAA,IACd;AAAA,EACF;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,IACP,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,YAAY;AAAA,EACd;AACF;AAKO,SAAS,eAAuB;AACrC,SAAO,QAAQ,OAAO,WAAW;AACnC;AAEO,SAAS,aAAa,SAAuB;AAClD,QAAM,QAAQ,SAAS;AACvB,MAAI,SAAS,GAAG;AACd,YAAQ,IAAI,MAAM,MAAM,GAAG,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;AAAA,EACxD,OAAO;AACL,YAAQ,IAAI,GAAG,MAAM,OAAO,IAAI,OAAO,EAAE;AAAA,EAC3C;AACF;AAEO,SAAS,WAAW,SAAuB;AAChD,QAAM,QAAQ,SAAS;AACvB,MAAI,SAAS,GAAG;AACd,YAAQ,IAAI,MAAM,IAAI,GAAG,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;AAAA,EACnD,OAAO;AACL,YAAQ,IAAI,GAAG,MAAM,IAAI,IAAI,OAAO,EAAE;AAAA,EACxC;AACF;AAEO,SAAS,aAAa,SAAuB;AAClD,QAAM,QAAQ,SAAS;AACvB,MAAI,SAAS,GAAG;AACd,YAAQ,IAAI,MAAM,OAAO,GAAG,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;AAAA,EACtD,OAAO;AACL,YAAQ,IAAI,GAAG,MAAM,IAAI,IAAI,OAAO,EAAE;AAAA,EACxC;AACF;AAEO,SAAS,WAAW,SAAuB;AAChD,QAAM,QAAQ,SAAS;AACvB,MAAI,SAAS,GAAG;AACd,YAAQ,IAAI,MAAM,OAAO,GAAG,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EACvD,OAAO;AACL,YAAQ,IAAI,GAAG,MAAM,KAAK,IAAI,OAAO,EAAE;AAAA,EACzC;AACF;AAEO,SAAS,UAAU,SAAuB;AAC/C,QAAM,QAAQ,SAAS;AACvB,MAAI,SAAS,GAAG;AACd,YAAQ,IAAI,MAAM,KAAK,GAAG,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EACrD,OAAO;AACL,YAAQ,IAAI,GAAG,MAAM,KAAK,IAAI,OAAO,EAAE;AAAA,EACzC;AACF;","names":[]}
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/prompt-resolver.ts
4
+ import { existsSync } from "fs";
5
+ import { readFile } from "fs/promises";
6
+ import { dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+ function getPromptsDir() {
9
+ const currentFile = fileURLToPath(import.meta.url);
10
+ let dir = dirname(currentFile);
11
+ for (let i = 0; i < 5; i++) {
12
+ const candidate = join(dir, "prompts");
13
+ if (existsSync(candidate)) {
14
+ return candidate;
15
+ }
16
+ dir = dirname(dir);
17
+ }
18
+ throw new Error(
19
+ "Could not locate the prompts/ directory. Ensure the package is installed correctly."
20
+ );
21
+ }
22
+ async function loadPrompt(name) {
23
+ const promptsDir = getPromptsDir();
24
+ const filePath = join(promptsDir, `${name}.md`);
25
+ try {
26
+ return await readFile(filePath, "utf-8");
27
+ } catch {
28
+ throw new Error(`Prompt template not found: ${filePath}`);
29
+ }
30
+ }
31
+ function applyPlaceholders(template, vars) {
32
+ let result = template;
33
+ for (const [key, value] of Object.entries(vars)) {
34
+ result = result.replaceAll(key, value);
35
+ }
36
+ return result;
37
+ }
38
+
39
+ export {
40
+ loadPrompt,
41
+ applyPlaceholders
42
+ };
43
+ //# sourceMappingURL=chunk-OZVHOVDT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/prompt-resolver.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Resolve the absolute path to the package's prompts/ directory.\n * Works from both source (src/core/) and compiled (dist/) locations\n * by walking up the directory tree until the prompts/ folder is found.\n */\nfunction getPromptsDir(): string {\n const currentFile = fileURLToPath(import.meta.url);\n let dir = dirname(currentFile);\n\n for (let i = 0; i < 5; i++) {\n const candidate = join(dir, 'prompts');\n if (existsSync(candidate)) {\n return candidate;\n }\n dir = dirname(dir);\n }\n\n throw new Error(\n 'Could not locate the prompts/ directory. Ensure the package is installed correctly.',\n );\n}\n\n/**\n * Load a prompt template by name from the package's prompts/ directory.\n *\n * @param name - Prompt name without extension (e.g., 'execute', 'analyze')\n * @returns The raw template content with placeholders intact\n */\nexport async function loadPrompt(name: string): Promise<string> {\n const promptsDir = getPromptsDir();\n const filePath = join(promptsDir, `${name}.md`);\n\n try {\n return await readFile(filePath, 'utf-8');\n } catch {\n throw new Error(`Prompt template not found: ${filePath}`);\n }\n}\n\n/**\n * Replace placeholders in a prompt template with actual values.\n *\n * Placeholders use the format __KEY__ (e.g., __ISSUE_NUMBER__, __PRD_FILE__).\n */\nexport function applyPlaceholders(template: string, vars: Record<string, string>): string {\n let result = template;\n for (const [key, value] of Object.entries(vars)) {\n result = result.replaceAll(key, value);\n }\n return result;\n}\n"],"mappings":";;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAO9B,SAAS,gBAAwB;AAC/B,QAAM,cAAc,cAAc,YAAY,GAAG;AACjD,MAAI,MAAM,QAAQ,WAAW;AAE7B,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,YAAY,KAAK,KAAK,SAAS;AACrC,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,GAAG;AAAA,EACnB;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAQA,eAAsB,WAAW,MAA+B;AAC9D,QAAM,aAAa,cAAc;AACjC,QAAM,WAAW,KAAK,YAAY,GAAG,IAAI,KAAK;AAE9C,MAAI;AACF,WAAO,MAAM,SAAS,UAAU,OAAO;AAAA,EACzC,QAAQ;AACN,UAAM,IAAI,MAAM,8BAA8B,QAAQ,EAAE;AAAA,EAC1D;AACF;AAOO,SAAS,kBAAkB,UAAkB,MAAsC;AACxF,MAAI,SAAS;AACb,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,aAAS,OAAO,WAAW,KAAK,KAAK;AAAA,EACvC;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/headless.ts
4
+ import { execa } from "execa";
5
+ async function runHeadless(options) {
6
+ const { prompt, maxTurns = 10, timeout = 3e5, outputFormat = "json", allowedTools } = options;
7
+ const args = [
8
+ "-p",
9
+ prompt,
10
+ "--output-format",
11
+ outputFormat,
12
+ "--max-turns",
13
+ String(maxTurns)
14
+ ];
15
+ if (allowedTools && allowedTools.length > 0) {
16
+ for (const tool of allowedTools) {
17
+ args.push("--allowedTools", tool);
18
+ }
19
+ }
20
+ try {
21
+ const proc = await execa("claude", args, {
22
+ reject: false,
23
+ timeout,
24
+ stripFinalNewline: false
25
+ });
26
+ const stdout = proc.stdout?.toString() ?? "";
27
+ const stderr = proc.stderr?.toString() ?? "";
28
+ if (proc.exitCode !== 0) {
29
+ return {
30
+ success: false,
31
+ result: "",
32
+ cost: null,
33
+ error: stderr || stdout || `claude exited with code ${proc.exitCode}`
34
+ };
35
+ }
36
+ if (outputFormat === "json") {
37
+ try {
38
+ const parsed = JSON.parse(stdout);
39
+ return {
40
+ success: true,
41
+ result: parsed.result ?? stdout,
42
+ cost: parsed.cost_usd != null ? { inputTokens: parsed.num_input_tokens, outputTokens: parsed.num_output_tokens } : null,
43
+ error: null
44
+ };
45
+ } catch {
46
+ return {
47
+ success: true,
48
+ result: stdout,
49
+ cost: null,
50
+ error: null
51
+ };
52
+ }
53
+ }
54
+ return {
55
+ success: true,
56
+ result: stdout,
57
+ cost: null,
58
+ error: null
59
+ };
60
+ } catch (err) {
61
+ const message = err instanceof Error ? err.message : String(err);
62
+ if (message.includes("timed out") || message.includes("ETIMEDOUT")) {
63
+ return {
64
+ success: false,
65
+ result: "",
66
+ cost: null,
67
+ error: `Headless invocation timed out after ${timeout}ms`
68
+ };
69
+ }
70
+ return {
71
+ success: false,
72
+ result: "",
73
+ cost: null,
74
+ error: message
75
+ };
76
+ }
77
+ }
78
+
79
+ export {
80
+ runHeadless
81
+ };
82
+ //# sourceMappingURL=chunk-PINC2LST.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/headless.ts"],"sourcesContent":["import { execa } from 'execa';\n\nexport interface HeadlessOptions {\n prompt: string;\n maxTurns?: number;\n timeout?: number;\n outputFormat?: 'json' | 'text' | 'stream-json';\n allowedTools?: string[];\n}\n\nexport interface HeadlessCost {\n inputTokens?: number;\n outputTokens?: number;\n}\n\nexport interface HeadlessResult {\n success: boolean;\n result: string;\n cost: HeadlessCost | null;\n error: string | null;\n}\n\n/**\n * Invoke Claude Code in headless mode via `claude -p`.\n *\n * Each invocation is an isolated session — no context is shared between calls.\n * Output is parsed as JSON when outputFormat is 'json' (default).\n */\nexport async function runHeadless(options: HeadlessOptions): Promise<HeadlessResult> {\n const { prompt, maxTurns = 10, timeout = 300_000, outputFormat = 'json', allowedTools } = options;\n\n const args: string[] = [\n '-p',\n prompt,\n '--output-format',\n outputFormat,\n '--max-turns',\n String(maxTurns),\n ];\n\n if (allowedTools && allowedTools.length > 0) {\n for (const tool of allowedTools) {\n args.push('--allowedTools', tool);\n }\n }\n\n try {\n const proc = await execa('claude', args, {\n reject: false,\n timeout,\n stripFinalNewline: false,\n });\n\n const stdout = proc.stdout?.toString() ?? '';\n const stderr = proc.stderr?.toString() ?? '';\n\n if (proc.exitCode !== 0) {\n return {\n success: false,\n result: '',\n cost: null,\n error: stderr || stdout || `claude exited with code ${proc.exitCode}`,\n };\n }\n\n if (outputFormat === 'json') {\n try {\n const parsed = JSON.parse(stdout);\n return {\n success: true,\n result: parsed.result ?? stdout,\n cost:\n parsed.cost_usd != null\n ? { inputTokens: parsed.num_input_tokens, outputTokens: parsed.num_output_tokens }\n : null,\n error: null,\n };\n } catch {\n // JSON parse failed — return raw output\n return {\n success: true,\n result: stdout,\n cost: null,\n error: null,\n };\n }\n }\n\n return {\n success: true,\n result: stdout,\n cost: null,\n error: null,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n\n if (message.includes('timed out') || message.includes('ETIMEDOUT')) {\n return {\n success: false,\n result: '',\n cost: null,\n error: `Headless invocation timed out after ${timeout}ms`,\n };\n }\n\n return {\n success: false,\n result: '',\n cost: null,\n error: message,\n };\n }\n}\n"],"mappings":";;;AAAA,SAAS,aAAa;AA4BtB,eAAsB,YAAY,SAAmD;AACnF,QAAM,EAAE,QAAQ,WAAW,IAAI,UAAU,KAAS,eAAe,QAAQ,aAAa,IAAI;AAE1F,QAAM,OAAiB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,QAAQ;AAAA,EACjB;AAEA,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,eAAW,QAAQ,cAAc;AAC/B,WAAK,KAAK,kBAAkB,IAAI;AAAA,IAClC;AAAA,EACF;AAEA,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,UAAU,MAAM;AAAA,MACvC,QAAQ;AAAA,MACR;AAAA,MACA,mBAAmB;AAAA,IACrB,CAAC;AAED,UAAM,SAAS,KAAK,QAAQ,SAAS,KAAK;AAC1C,UAAM,SAAS,KAAK,QAAQ,SAAS,KAAK;AAE1C,QAAI,KAAK,aAAa,GAAG;AACvB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,OAAO,UAAU,UAAU,2BAA2B,KAAK,QAAQ;AAAA,MACrE;AAAA,IACF;AAEA,QAAI,iBAAiB,QAAQ;AAC3B,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,MAAM;AAChC,eAAO;AAAA,UACL,SAAS;AAAA,UACT,QAAQ,OAAO,UAAU;AAAA,UACzB,MACE,OAAO,YAAY,OACf,EAAE,aAAa,OAAO,kBAAkB,cAAc,OAAO,kBAAkB,IAC/E;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF,QAAQ;AAEN,eAAO;AAAA,UACL,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAE/D,QAAI,QAAQ,SAAS,WAAW,KAAK,QAAQ,SAAS,WAAW,GAAG;AAClE,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,OAAO,uCAAuC,OAAO;AAAA,MACvD;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}