@gitwhy-cli/whyspec 0.1.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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/dist/adapters/agents-md.d.ts +13 -0
  4. package/dist/adapters/agents-md.d.ts.map +1 -0
  5. package/dist/adapters/agents-md.js +165 -0
  6. package/dist/adapters/agents-md.js.map +1 -0
  7. package/dist/adapters/claude-code.d.ts +13 -0
  8. package/dist/adapters/claude-code.d.ts.map +1 -0
  9. package/dist/adapters/claude-code.js +206 -0
  10. package/dist/adapters/claude-code.js.map +1 -0
  11. package/dist/adapters/cursor.d.ts +12 -0
  12. package/dist/adapters/cursor.d.ts.map +1 -0
  13. package/dist/adapters/cursor.js +220 -0
  14. package/dist/adapters/cursor.js.map +1 -0
  15. package/dist/adapters/types.d.ts +16 -0
  16. package/dist/adapters/types.d.ts.map +1 -0
  17. package/dist/adapters/types.js +19 -0
  18. package/dist/adapters/types.js.map +1 -0
  19. package/dist/cli/index.d.ts +3 -0
  20. package/dist/cli/index.d.ts.map +1 -0
  21. package/dist/cli/index.js +109 -0
  22. package/dist/cli/index.js.map +1 -0
  23. package/dist/commands/capture.d.ts +18 -0
  24. package/dist/commands/capture.d.ts.map +1 -0
  25. package/dist/commands/capture.js +85 -0
  26. package/dist/commands/capture.js.map +1 -0
  27. package/dist/commands/debug.d.ts +16 -0
  28. package/dist/commands/debug.d.ts.map +1 -0
  29. package/dist/commands/debug.js +74 -0
  30. package/dist/commands/debug.js.map +1 -0
  31. package/dist/commands/execute.d.ts +36 -0
  32. package/dist/commands/execute.d.ts.map +1 -0
  33. package/dist/commands/execute.js +110 -0
  34. package/dist/commands/execute.js.map +1 -0
  35. package/dist/commands/init.d.ts +14 -0
  36. package/dist/commands/init.d.ts.map +1 -0
  37. package/dist/commands/init.js +166 -0
  38. package/dist/commands/init.js.map +1 -0
  39. package/dist/commands/list.d.ts +23 -0
  40. package/dist/commands/list.d.ts.map +1 -0
  41. package/dist/commands/list.js +95 -0
  42. package/dist/commands/list.js.map +1 -0
  43. package/dist/commands/plan.d.ts +20 -0
  44. package/dist/commands/plan.d.ts.map +1 -0
  45. package/dist/commands/plan.js +48 -0
  46. package/dist/commands/plan.js.map +1 -0
  47. package/dist/commands/search.d.ts +12 -0
  48. package/dist/commands/search.d.ts.map +1 -0
  49. package/dist/commands/search.js +36 -0
  50. package/dist/commands/search.js.map +1 -0
  51. package/dist/commands/show.d.ts +25 -0
  52. package/dist/commands/show.d.ts.map +1 -0
  53. package/dist/commands/show.js +145 -0
  54. package/dist/commands/show.js.map +1 -0
  55. package/dist/commands/status.d.ts +29 -0
  56. package/dist/commands/status.d.ts.map +1 -0
  57. package/dist/commands/status.js +125 -0
  58. package/dist/commands/status.js.map +1 -0
  59. package/dist/commands/template.d.ts +14 -0
  60. package/dist/commands/template.d.ts.map +1 -0
  61. package/dist/commands/template.js +25 -0
  62. package/dist/commands/template.js.map +1 -0
  63. package/dist/core/categorize.d.ts +30 -0
  64. package/dist/core/categorize.d.ts.map +1 -0
  65. package/dist/core/categorize.js +72 -0
  66. package/dist/core/categorize.js.map +1 -0
  67. package/dist/core/config.d.ts +26 -0
  68. package/dist/core/config.d.ts.map +1 -0
  69. package/dist/core/config.js +52 -0
  70. package/dist/core/config.js.map +1 -0
  71. package/dist/core/context.d.ts +27 -0
  72. package/dist/core/context.d.ts.map +1 -0
  73. package/dist/core/context.js +75 -0
  74. package/dist/core/context.js.map +1 -0
  75. package/dist/core/search.d.ts +51 -0
  76. package/dist/core/search.d.ts.map +1 -0
  77. package/dist/core/search.js +235 -0
  78. package/dist/core/search.js.map +1 -0
  79. package/dist/core/storage.d.ts +36 -0
  80. package/dist/core/storage.d.ts.map +1 -0
  81. package/dist/core/storage.js +80 -0
  82. package/dist/core/storage.js.map +1 -0
  83. package/dist/core/templates.d.ts +28 -0
  84. package/dist/core/templates.d.ts.map +1 -0
  85. package/dist/core/templates.js +176 -0
  86. package/dist/core/templates.js.map +1 -0
  87. package/dist/ui/ascii-logo.d.ts +7 -0
  88. package/dist/ui/ascii-logo.d.ts.map +1 -0
  89. package/dist/ui/ascii-logo.js +15 -0
  90. package/dist/ui/ascii-logo.js.map +1 -0
  91. package/dist/ui/tool-picker.d.ts +13 -0
  92. package/dist/ui/tool-picker.d.ts.map +1 -0
  93. package/dist/ui/tool-picker.js +76 -0
  94. package/dist/ui/tool-picker.js.map +1 -0
  95. package/dist/ui/welcome.d.ts +4 -0
  96. package/dist/ui/welcome.d.ts.map +1 -0
  97. package/dist/ui/welcome.js +43 -0
  98. package/dist/ui/welcome.js.map +1 -0
  99. package/dist/utils/changes.d.ts +19 -0
  100. package/dist/utils/changes.d.ts.map +1 -0
  101. package/dist/utils/changes.js +63 -0
  102. package/dist/utils/changes.js.map +1 -0
  103. package/dist/utils/git.d.ts +28 -0
  104. package/dist/utils/git.d.ts.map +1 -0
  105. package/dist/utils/git.js +104 -0
  106. package/dist/utils/git.js.map +1 -0
  107. package/dist/utils/slugify.d.ts +12 -0
  108. package/dist/utils/slugify.d.ts.map +1 -0
  109. package/dist/utils/slugify.js +21 -0
  110. package/dist/utils/slugify.js.map +1 -0
  111. package/dist/utils/telemetry.d.ts +21 -0
  112. package/dist/utils/telemetry.d.ts.map +1 -0
  113. package/dist/utils/telemetry.js +32 -0
  114. package/dist/utils/telemetry.js.map +1 -0
  115. package/package.json +81 -0
  116. package/skills/whyspec-capture/SKILL.md +154 -0
  117. package/skills/whyspec-debug/SKILL.md +404 -0
  118. package/skills/whyspec-execute/SKILL.md +118 -0
  119. package/skills/whyspec-plan/SKILL.md +170 -0
  120. package/skills/whyspec-search/SKILL.md +69 -0
  121. package/skills/whyspec-show/SKILL.md +90 -0
@@ -0,0 +1,176 @@
1
+ /**
2
+ * File templates for WhySpec change artifacts.
3
+ * Templates for: intent.md, design.md, tasks.md, context (SaaS XML format), debug.md
4
+ *
5
+ * The context template MUST match GitWhy SaaS XML format exactly.
6
+ * See: gitwhy/.claude/skills/gitwhy/SKILL.md
7
+ */
8
+ /** Template for intent.md — declares intent before coding. */
9
+ export function intentTemplate(changeName) {
10
+ return `# Intent: ${changeName}
11
+
12
+ ## Why This Change Exists
13
+ <!-- Problem statement: what pain or gap does this address? -->
14
+
15
+ ## What It Enables
16
+ <!-- Capabilities or outcomes this change unlocks -->
17
+
18
+ ## Decisions to Make
19
+ <!-- Checkbox list of pending decisions (the Decision Bridge, "before" side) -->
20
+ - [ ]
21
+
22
+ ## Constraints
23
+ <!-- Technical or business constraints -->
24
+
25
+ ## Success Looks Like
26
+ <!-- Acceptance criteria: how do we know this is done? -->
27
+
28
+ ## Assumptions
29
+ <!-- What we're assuming is true -->
30
+ `;
31
+ }
32
+ /** Template for design.md — technical approach with trade-offs. */
33
+ export function designTemplate(changeName) {
34
+ return `# Design: ${changeName}
35
+
36
+ ## Approach
37
+ <!-- Chosen technical direction -->
38
+
39
+ ## Trade-off Matrix
40
+ <!-- Rows = options, Columns = evaluation criteria -->
41
+ | Option | Pros | Cons | Decision |
42
+ |--------|------|------|----------|
43
+ | | | | |
44
+
45
+ ## Architecture
46
+ <!-- ASCII diagram of the design -->
47
+
48
+ ## Decisions to Make
49
+ <!-- THE DECISION BRIDGE: These checkboxes become "Decisions Made" after /whyspec:capture -->
50
+ - [ ]
51
+
52
+ ## Questions to Resolve
53
+ <!-- Open questions before coding -->
54
+ - [ ]
55
+
56
+ ## Risks & Unknowns
57
+ <!-- What could go wrong -->
58
+
59
+ ## Dependencies
60
+ <!-- External dependencies -->
61
+ `;
62
+ }
63
+ /** Template for tasks.md — implementation checklist with goal-backward verification. */
64
+ export function tasksTemplate(changeName) {
65
+ return `# Tasks: ${changeName}
66
+
67
+ ## Verification
68
+ <!-- What proves this works? Defined FIRST (goal-backward from GSD) -->
69
+ - [ ]
70
+
71
+ ## Tasks
72
+ <!-- Implementation checklist with sub-tasks -->
73
+ - [ ]
74
+ `;
75
+ }
76
+ /**
77
+ * Context template in GitWhy SaaS XML format.
78
+ * This is what agents fill in when capturing reasoning after coding.
79
+ * The CLI parses this XML and renders it into rich markdown for storage.
80
+ */
81
+ export function contextTemplate(options) {
82
+ const title = options?.title ?? "Short title of what was done";
83
+ const agent = options?.agent ?? "claude-code (model)";
84
+ const tags = options?.tags ?? "keyword1, keyword2";
85
+ return `<context>
86
+ <title>${title}</title>
87
+ <story>
88
+ Phase 1 — Setup:
89
+ What was asked, what was done, challenges faced, how they were resolved.
90
+
91
+ Phase 2 — Implementation:
92
+ Technical details, decisions made during coding, problems solved.
93
+ </story>
94
+ <reasoning>
95
+ Why this approach was chosen.
96
+
97
+ <decisions>
98
+ - Decision — rationale
99
+ </decisions>
100
+
101
+ <rejected>
102
+ - Alternative — why rejected
103
+ </rejected>
104
+
105
+ <tradeoffs>
106
+ - Trade-off accepted — justification
107
+ </tradeoffs>
108
+ </reasoning>
109
+ <files>
110
+ path/to/file — new — Description
111
+ path/to/other — modified — Description
112
+ </files>
113
+ <agent>${agent}</agent>
114
+ <tags>${tags}</tags>
115
+ <verification>Test/build results</verification>
116
+ <risks>Open questions or risks</risks>
117
+ </context>
118
+ `;
119
+ }
120
+ /** Template for debug.md — structured debugging process. */
121
+ export function debugTemplate(bugName) {
122
+ return `# Debug: ${bugName}
123
+
124
+ ## Symptoms
125
+ <!-- Expected vs actual behavior -->
126
+ **Expected:**
127
+
128
+ **Actual:**
129
+
130
+ **Error Messages:**
131
+
132
+ **Reproduction Steps:**
133
+ 1.
134
+
135
+ **Timeline:** When did this start?
136
+
137
+ ## Hypotheses
138
+ <!-- 3+ falsifiable hypotheses with "what would prove this wrong?" -->
139
+ 1. **Hypothesis:**
140
+ **Disproof:**
141
+
142
+ 2. **Hypothesis:**
143
+ **Disproof:**
144
+
145
+ 3. **Hypothesis:**
146
+ **Disproof:**
147
+
148
+ ## Investigation
149
+ <!-- Evidence trail: what was tested, what was found -->
150
+
151
+ ## Root Cause
152
+ <!-- The verified root cause. No fix without root cause (Iron Law). -->
153
+
154
+ ## Fix
155
+ <!-- What was done to fix the issue -->
156
+
157
+ ## Prevention
158
+ <!-- How to avoid this in the future -->
159
+ `;
160
+ }
161
+ /** Returns a template by type name. */
162
+ export function getTemplate(type, name = "") {
163
+ switch (type) {
164
+ case "intent":
165
+ return intentTemplate(name);
166
+ case "design":
167
+ return designTemplate(name);
168
+ case "tasks":
169
+ return tasksTemplate(name);
170
+ case "context":
171
+ return contextTemplate({ title: name || undefined });
172
+ case "debug":
173
+ return debugTemplate(name);
174
+ }
175
+ }
176
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/core/templates.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8DAA8D;AAC9D,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,OAAO,aAAa,UAAU;;;;;;;;;;;;;;;;;;;;CAoB/B,CAAC;AACF,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,OAAO,aAAa,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2B/B,CAAC;AACF,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,OAAO,YAAY,UAAU;;;;;;;;;CAS9B,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAI/B;IACC,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,8BAA8B,CAAC;IAC/D,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,qBAAqB,CAAC;IACtD,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,oBAAoB,CAAC;IAEnD,OAAO;WACE,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;WA2BL,KAAK;UACN,IAAI;;;;CAIb,CAAC;AACF,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO,YAAY,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqC3B,CAAC;AACF,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,WAAW,CACzB,IAAyD,EACzD,IAAI,GAAG,EAAE;IAET,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;QAC9B,KAAK,QAAQ;YACX,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;QAC9B,KAAK,OAAO;YACV,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;QAC7B,KAAK,SAAS;YACZ,OAAO,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,IAAI,SAAS,EAAE,CAAC,CAAC;QACvD,KAAK,OAAO;YACV,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * WhySpec ASCII logo — figlet "small" style.
3
+ * Raw string without color — callers apply styling.
4
+ */
5
+ export declare const WHYSPEC_LOGO = " __ ___ ____\n \\ \\ / / |_ _ _ __| _ \\ _ __ ___ ___\n \\ \\/\\/ /| ' \\ || (_-< ___/| '_ \\/ -_)/ _|\n \\_/\\_/ |_||_\\_, /__/|_| | .__/\\___|\\__|\n |__/ |_|";
6
+ export declare function renderLogo(): string;
7
+ //# sourceMappingURL=ascii-logo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ascii-logo.d.ts","sourceRoot":"","sources":["../../src/ui/ascii-logo.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,YAAY,0NAKQ,CAAC;AAElC,wBAAgB,UAAU,IAAI,MAAM,CAEnC"}
@@ -0,0 +1,15 @@
1
+ import chalk from "chalk";
2
+ /**
3
+ * WhySpec ASCII logo — figlet "small" style.
4
+ * Raw string without color — callers apply styling.
5
+ */
6
+ export const WHYSPEC_LOGO = `\
7
+ __ ___ ____
8
+ \\ \\ / / |_ _ _ __| _ \\ _ __ ___ ___
9
+ \\ \\/\\/ /| ' \\ || (_-< ___/| '_ \\/ -_)/ _|
10
+ \\_/\\_/ |_||_\\_, /__/|_| | .__/\\___|\\__|
11
+ |__/ |_|`;
12
+ export function renderLogo() {
13
+ return chalk.cyan(WHYSPEC_LOGO);
14
+ }
15
+ //# sourceMappingURL=ascii-logo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ascii-logo.js","sourceRoot":"","sources":["../../src/ui/ascii-logo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;iCAKK,CAAC;AAElC,MAAM,UAAU,UAAU;IACxB,OAAO,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface ToolChoice {
2
+ name: string;
3
+ value: string;
4
+ /** Tools with skill support get .claude/skills/ or equivalent */
5
+ skillSupport: boolean;
6
+ /** Tools that read AGENTS.md for instructions */
7
+ usesAgentsMd: boolean;
8
+ }
9
+ export declare const AVAILABLE_TOOLS: ToolChoice[];
10
+ export declare function promptToolPicker(): Promise<string[]>;
11
+ export declare function getToolMeta(toolId: string): ToolChoice | undefined;
12
+ export declare function needsAgentsMd(selectedTools: string[]): boolean;
13
+ //# sourceMappingURL=tool-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-picker.d.ts","sourceRoot":"","sources":["../../src/ui/tool-picker.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,YAAY,EAAE,OAAO,CAAC;IACtB,iDAAiD;IACjD,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,eAAe,EAAE,UAAU,EAiDvC,CAAC;AAEF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAc1D;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAElE;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAK9D"}
@@ -0,0 +1,76 @@
1
+ import { checkbox } from "@inquirer/prompts";
2
+ import chalk from "chalk";
3
+ export const AVAILABLE_TOOLS = [
4
+ {
5
+ name: "Claude Code",
6
+ value: "claude-code",
7
+ skillSupport: true,
8
+ usesAgentsMd: false,
9
+ },
10
+ {
11
+ name: "Cursor",
12
+ value: "cursor",
13
+ skillSupport: true,
14
+ usesAgentsMd: false,
15
+ },
16
+ {
17
+ name: "GitHub Copilot",
18
+ value: "copilot",
19
+ skillSupport: false,
20
+ usesAgentsMd: true,
21
+ },
22
+ {
23
+ name: "Codex CLI",
24
+ value: "codex",
25
+ skillSupport: false,
26
+ usesAgentsMd: true,
27
+ },
28
+ {
29
+ name: "Windsurf",
30
+ value: "windsurf",
31
+ skillSupport: false,
32
+ usesAgentsMd: true,
33
+ },
34
+ {
35
+ name: "Cline",
36
+ value: "cline",
37
+ skillSupport: false,
38
+ usesAgentsMd: true,
39
+ },
40
+ {
41
+ name: "Amazon Q",
42
+ value: "amazon-q",
43
+ skillSupport: false,
44
+ usesAgentsMd: true,
45
+ },
46
+ {
47
+ name: "RooCode",
48
+ value: "roocode",
49
+ skillSupport: false,
50
+ usesAgentsMd: true,
51
+ },
52
+ ];
53
+ export async function promptToolPicker() {
54
+ const selected = await checkbox({
55
+ message: "Select your AI tools (space to toggle, enter to confirm)",
56
+ choices: AVAILABLE_TOOLS.map((tool) => ({
57
+ name: tool.name,
58
+ value: tool.value,
59
+ checked: tool.value === "claude-code",
60
+ })),
61
+ theme: {
62
+ prefix: chalk.cyan("?"),
63
+ },
64
+ });
65
+ return selected;
66
+ }
67
+ export function getToolMeta(toolId) {
68
+ return AVAILABLE_TOOLS.find((t) => t.value === toolId);
69
+ }
70
+ export function needsAgentsMd(selectedTools) {
71
+ return selectedTools.some((id) => {
72
+ const tool = getToolMeta(id);
73
+ return tool?.usesAgentsMd;
74
+ });
75
+ }
76
+ //# sourceMappingURL=tool-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-picker.js","sourceRoot":"","sources":["../../src/ui/tool-picker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAW1B,MAAM,CAAC,MAAM,eAAe,GAAiB;IAC3C;QACE,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,aAAa;QACpB,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,KAAK;KACpB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,QAAQ;QACf,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,KAAK;KACpB;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,SAAS;QAChB,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,IAAI;KACnB;IACD;QACE,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,OAAO;QACd,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,IAAI;KACnB;IACD;QACE,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;QACjB,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,IAAI;KACnB;IACD;QACE,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO;QACd,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,IAAI;KACnB;IACD;QACE,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;QACjB,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,IAAI;KACnB;IACD;QACE,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,IAAI;KACnB;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC;QAC9B,OAAO,EAAE,0DAA0D;QACnE,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACtC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,KAAK,KAAK,aAAa;SACtC,CAAC,CAAC;QACH,KAAK,EAAE;YACL,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SACxB;KACF,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,aAAuB;IACnD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;QAC/B,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC7B,OAAO,IAAI,EAAE,YAAY,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function renderWelcomeScreen(): string;
2
+ export declare function renderTelemetryNotice(): string;
3
+ export declare function renderSuccessMessage(tools: string[]): string;
4
+ //# sourceMappingURL=welcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/ui/welcome.ts"],"names":[],"mappings":"AAGA,wBAAgB,mBAAmB,IAAI,MAAM,CAqB5C;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAI9C;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAkB5D"}
@@ -0,0 +1,43 @@
1
+ import chalk from "chalk";
2
+ import { renderLogo } from "./ascii-logo.js";
3
+ export function renderWelcomeScreen() {
4
+ const lines = [];
5
+ lines.push("");
6
+ lines.push(renderLogo());
7
+ lines.push("");
8
+ lines.push(chalk.bold(" The reasoning layer for AI coding"));
9
+ lines.push("");
10
+ lines.push(chalk.dim(" This will create:"));
11
+ lines.push(chalk.white(" .gitwhy/"));
12
+ lines.push(chalk.white(" \u251C\u2500\u2500 config.yaml"));
13
+ lines.push(chalk.white(" \u2514\u2500\u2500 changes/"));
14
+ lines.push("");
15
+ lines.push(chalk.dim(" Quick start after setup:"));
16
+ lines.push(` ${chalk.green("/whyspec:plan")} Plan before coding`);
17
+ lines.push(` ${chalk.green("/whyspec:execute")} Implement with context`);
18
+ lines.push(` ${chalk.green("/whyspec:capture")} Save your reasoning`);
19
+ lines.push(` ${chalk.green("/whyspec:debug")} Debug with science`);
20
+ lines.push("");
21
+ return lines.join("\n");
22
+ }
23
+ export function renderTelemetryNotice() {
24
+ return chalk.dim(" Telemetry: anonymous usage stats (opt-out: WHYSPEC_TELEMETRY=0)");
25
+ }
26
+ export function renderSuccessMessage(tools) {
27
+ const lines = [];
28
+ lines.push("");
29
+ lines.push(chalk.green.bold(" \u2713 WhySpec initialized!"));
30
+ lines.push("");
31
+ lines.push(chalk.dim(" Created:"));
32
+ lines.push(chalk.white(" .gitwhy/config.yaml"));
33
+ lines.push(chalk.white(" .gitwhy/changes/"));
34
+ if (tools.length > 0) {
35
+ lines.push("");
36
+ lines.push(chalk.dim(` Tools configured: ${tools.join(", ")}`));
37
+ }
38
+ lines.push("");
39
+ lines.push(` Ready! Try: ${chalk.cyan.bold("/whyspec:plan")}`);
40
+ lines.push("");
41
+ return lines.join("\n");
42
+ }
43
+ //# sourceMappingURL=welcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"welcome.js","sourceRoot":"","sources":["../../src/ui/welcome.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Change folder operations for .gitwhy/changes/ directories.
3
+ */
4
+ export interface ResolvedChange {
5
+ name: string;
6
+ path: string;
7
+ }
8
+ /**
9
+ * List all change folder names in .gitwhy/changes/.
10
+ */
11
+ export declare function listChanges(gitwhyDir: string): string[];
12
+ /**
13
+ * Resolve a change by name, or auto-detect if only one exists.
14
+ * - If name provided: validate the folder exists.
15
+ * - If no name + exactly 1 change: auto-select it.
16
+ * - If no name + 0 or multiple changes: throw with guidance.
17
+ */
18
+ export declare function resolveChange(gitwhyDir: string, name?: string): ResolvedChange;
19
+ //# sourceMappingURL=changes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../../src/utils/changes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAsBD;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAOvD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,cAAc,CA8BhB"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Change folder operations for .gitwhy/changes/ directories.
3
+ */
4
+ import { existsSync, readdirSync, statSync } from "node:fs";
5
+ import { join, normalize, relative, sep } from "node:path";
6
+ import { slugify } from "./slugify.js";
7
+ function normalizeChangeName(name) {
8
+ const trimmed = name.trim();
9
+ if (!trimmed) {
10
+ throw new Error("Invalid change name.");
11
+ }
12
+ const normalizedPath = normalize(trimmed);
13
+ if (normalizedPath === ".." ||
14
+ normalizedPath.startsWith(`..${sep}`) ||
15
+ normalizedPath.includes(`${sep}..${sep}`) ||
16
+ normalizedPath.endsWith(`${sep}..`) ||
17
+ normalizedPath.startsWith(sep)) {
18
+ throw new Error(`Invalid change name "${name}".`);
19
+ }
20
+ return slugify(trimmed);
21
+ }
22
+ /**
23
+ * List all change folder names in .gitwhy/changes/.
24
+ */
25
+ export function listChanges(gitwhyDir) {
26
+ const changesDir = join(gitwhyDir, "changes");
27
+ if (!existsSync(changesDir))
28
+ return [];
29
+ return readdirSync(changesDir).filter((entry) => {
30
+ const full = join(changesDir, entry);
31
+ return statSync(full).isDirectory();
32
+ });
33
+ }
34
+ /**
35
+ * Resolve a change by name, or auto-detect if only one exists.
36
+ * - If name provided: validate the folder exists.
37
+ * - If no name + exactly 1 change: auto-select it.
38
+ * - If no name + 0 or multiple changes: throw with guidance.
39
+ */
40
+ export function resolveChange(gitwhyDir, name) {
41
+ const changesDir = join(gitwhyDir, "changes");
42
+ if (name) {
43
+ const normalizedName = normalizeChangeName(name);
44
+ const changePath = join(changesDir, normalizedName);
45
+ const rel = relative(changesDir, changePath);
46
+ if (rel.startsWith("..") || rel === "") {
47
+ throw new Error(`Invalid change name "${name}".`);
48
+ }
49
+ if (!existsSync(changePath)) {
50
+ throw new Error(`Change "${normalizedName}" not found. Run \`whyspec plan "${normalizedName}"\` first.`);
51
+ }
52
+ return { name: normalizedName, path: changePath };
53
+ }
54
+ const changes = listChanges(gitwhyDir);
55
+ if (changes.length === 0) {
56
+ throw new Error("No changes found. Run `whyspec plan <name>` to create one.");
57
+ }
58
+ if (changes.length === 1) {
59
+ return { name: changes[0], path: join(changesDir, changes[0]) };
60
+ }
61
+ throw new Error(`Multiple changes found. Specify one:\n${changes.map((c) => ` - ${c}`).join("\n")}`);
62
+ }
63
+ //# sourceMappingURL=changes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changes.js","sourceRoot":"","sources":["../../src/utils/changes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Git utility functions for WhySpec.
3
+ * Wraps git CLI commands via execFileSync (no shell — safe from injection).
4
+ *
5
+ * Reference: GitWhy Go cmd/git-why/enable.go:512 (findRepoRoot)
6
+ */
7
+ export interface Commit {
8
+ hash: string;
9
+ message: string;
10
+ }
11
+ /** Returns the git repository root directory. Throws if not in a git repo. */
12
+ export declare function findRepoRoot(): string;
13
+ /** Returns the current branch name. */
14
+ export declare function getCurrentBranch(): string;
15
+ /** Returns recent commits as { hash, message } pairs. */
16
+ export declare function getRecentCommits(count?: number): Commit[];
17
+ /** Returns commit SHAs since a given date. Used by capture to find commits since change creation. */
18
+ export declare function getCommitsSince(since: Date): string[];
19
+ /** Returns files changed since a given date. Used by capture to detect changed files. */
20
+ export declare function getFilesChanged(since: Date): string[];
21
+ /** Returns the remote origin URL (owner/repo format if GitHub, raw URL otherwise). */
22
+ export declare function getRemoteUrl(): string;
23
+ /**
24
+ * Adds an entry to .gitignore if not already present.
25
+ * Creates .gitignore if it doesn't exist.
26
+ */
27
+ export declare function addToGitignore(repoRoot: string, entry: string): void;
28
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,IAAI,MAAM,CAWrC;AAED,uCAAuC;AACvC,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED,yDAAyD;AACzD,wBAAgB,gBAAgB,CAAC,KAAK,SAAK,GAAG,MAAM,EAAE,CAmBrD;AAED,qGAAqG;AACrG,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAAE,CAWrD;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAAE,CAWrD;AAED,sFAAsF;AACtF,wBAAgB,YAAY,IAAI,MAAM,CAarC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAcpE"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Git utility functions for WhySpec.
3
+ * Wraps git CLI commands via execFileSync (no shell — safe from injection).
4
+ *
5
+ * Reference: GitWhy Go cmd/git-why/enable.go:512 (findRepoRoot)
6
+ */
7
+ import { execFileSync } from "node:child_process";
8
+ import { readFileSync, writeFileSync, existsSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ /** Returns the git repository root directory. Throws if not in a git repo. */
11
+ export function findRepoRoot() {
12
+ try {
13
+ const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
14
+ encoding: "utf-8",
15
+ stdio: ["pipe", "pipe", "pipe"],
16
+ });
17
+ return out.trim();
18
+ }
19
+ catch (err) {
20
+ const msg = err instanceof Error ? err.message : String(err);
21
+ throw new Error(`Not a git repository: ${msg}`, { cause: err });
22
+ }
23
+ }
24
+ /** Returns the current branch name. */
25
+ export function getCurrentBranch() {
26
+ const out = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
27
+ encoding: "utf-8",
28
+ stdio: ["pipe", "pipe", "pipe"],
29
+ });
30
+ return out.trim();
31
+ }
32
+ /** Returns recent commits as { hash, message } pairs. */
33
+ export function getRecentCommits(count = 10) {
34
+ const out = execFileSync("git", ["log", `--oneline`, `-${count}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
35
+ return out
36
+ .trim()
37
+ .split("\n")
38
+ .filter((line) => line.length > 0)
39
+ .map((line) => {
40
+ const spaceIdx = line.indexOf(" ");
41
+ if (spaceIdx === -1)
42
+ return { hash: line, message: "" };
43
+ return {
44
+ hash: line.slice(0, spaceIdx),
45
+ message: line.slice(spaceIdx + 1),
46
+ };
47
+ });
48
+ }
49
+ /** Returns commit SHAs since a given date. Used by capture to find commits since change creation. */
50
+ export function getCommitsSince(since) {
51
+ try {
52
+ const out = execFileSync("git", ["log", `--since=${since.toISOString()}`, "--format=%H", "--max-count=100"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
53
+ return out.trim().split("\n").filter((l) => l.length > 0);
54
+ }
55
+ catch {
56
+ return [];
57
+ }
58
+ }
59
+ /** Returns files changed since a given date. Used by capture to detect changed files. */
60
+ export function getFilesChanged(since) {
61
+ try {
62
+ const out = execFileSync("git", ["log", `--since=${since.toISOString()}`, "--format=", "--name-only", "--max-count=100"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
63
+ return [...new Set(out.trim().split("\n").filter((l) => l.length > 0))];
64
+ }
65
+ catch {
66
+ return [];
67
+ }
68
+ }
69
+ /** Returns the remote origin URL (owner/repo format if GitHub, raw URL otherwise). */
70
+ export function getRemoteUrl() {
71
+ try {
72
+ const out = execFileSync("git", ["remote", "get-url", "origin"], {
73
+ encoding: "utf-8",
74
+ stdio: ["pipe", "pipe", "pipe"],
75
+ });
76
+ const url = out.trim();
77
+ // Extract owner/repo from GitHub URLs
78
+ const match = url.match(/github\.com[:/](.+?)(?:\.git)?$/);
79
+ return match ? match[1] : url;
80
+ }
81
+ catch {
82
+ return "";
83
+ }
84
+ }
85
+ /**
86
+ * Adds an entry to .gitignore if not already present.
87
+ * Creates .gitignore if it doesn't exist.
88
+ */
89
+ export function addToGitignore(repoRoot, entry) {
90
+ const gitignorePath = join(repoRoot, ".gitignore");
91
+ if (existsSync(gitignorePath)) {
92
+ const content = readFileSync(gitignorePath, "utf-8");
93
+ const lines = content.split("\n");
94
+ if (lines.some((line) => line.trim() === entry.trim())) {
95
+ return; // Already present
96
+ }
97
+ const suffix = content.endsWith("\n") ? "" : "\n";
98
+ writeFileSync(gitignorePath, content + suffix + entry + "\n", "utf-8");
99
+ }
100
+ else {
101
+ writeFileSync(gitignorePath, entry + "\n", "utf-8");
102
+ }
103
+ }
104
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Slugify converts a string to a URL-friendly slug.
3
+ * Ported from GitWhy Go: internal/storage/categorize.go
4
+ *
5
+ * - Lowercase
6
+ * - Spaces replaced with hyphens
7
+ * - Special characters removed (only a-z, 0-9, hyphens kept)
8
+ * - Leading/trailing hyphens trimmed
9
+ * - Returns "general" for empty input
10
+ */
11
+ export declare function slugify(input: string): string;
12
+ //# sourceMappingURL=slugify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugify.d.ts","sourceRoot":"","sources":["../../src/utils/slugify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS7C"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Slugify converts a string to a URL-friendly slug.
3
+ * Ported from GitWhy Go: internal/storage/categorize.go
4
+ *
5
+ * - Lowercase
6
+ * - Spaces replaced with hyphens
7
+ * - Special characters removed (only a-z, 0-9, hyphens kept)
8
+ * - Leading/trailing hyphens trimmed
9
+ * - Returns "general" for empty input
10
+ */
11
+ const SLUG_RE = /[^a-z0-9-]/g;
12
+ export function slugify(input) {
13
+ if (!input)
14
+ return "general";
15
+ let s = input.toLowerCase();
16
+ s = s.replace(/ /g, "-");
17
+ s = s.replace(SLUG_RE, "");
18
+ s = s.replace(/^-+|-+$/g, "");
19
+ return s || "general";
20
+ }
21
+ //# sourceMappingURL=slugify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugify.js","sourceRoot":"","sources":["../../src/utils/slugify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,OAAO,GAAG,aAAa,CAAC;AAE9B,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAE7B,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACzB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC3B,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAE9B,OAAO,CAAC,IAAI,SAAS,CAAC;AACxB,CAAC"}