@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.
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/adapters/agents-md.d.ts +13 -0
- package/dist/adapters/agents-md.d.ts.map +1 -0
- package/dist/adapters/agents-md.js +165 -0
- package/dist/adapters/agents-md.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +13 -0
- package/dist/adapters/claude-code.d.ts.map +1 -0
- package/dist/adapters/claude-code.js +206 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/cursor.d.ts +12 -0
- package/dist/adapters/cursor.d.ts.map +1 -0
- package/dist/adapters/cursor.js +220 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/types.d.ts +16 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +19 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +109 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/capture.d.ts +18 -0
- package/dist/commands/capture.d.ts.map +1 -0
- package/dist/commands/capture.js +85 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/debug.d.ts +16 -0
- package/dist/commands/debug.d.ts.map +1 -0
- package/dist/commands/debug.js +74 -0
- package/dist/commands/debug.js.map +1 -0
- package/dist/commands/execute.d.ts +36 -0
- package/dist/commands/execute.d.ts.map +1 -0
- package/dist/commands/execute.js +110 -0
- package/dist/commands/execute.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +166 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +23 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +95 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/plan.d.ts +20 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +48 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/search.d.ts +12 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +36 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/show.d.ts +25 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +145 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/status.d.ts +29 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +125 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/template.d.ts +14 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +25 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/core/categorize.d.ts +30 -0
- package/dist/core/categorize.d.ts.map +1 -0
- package/dist/core/categorize.js +72 -0
- package/dist/core/categorize.js.map +1 -0
- package/dist/core/config.d.ts +26 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +52 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/context.d.ts +27 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +75 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/search.d.ts +51 -0
- package/dist/core/search.d.ts.map +1 -0
- package/dist/core/search.js +235 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/storage.d.ts +36 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +80 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/templates.d.ts +28 -0
- package/dist/core/templates.d.ts.map +1 -0
- package/dist/core/templates.js +176 -0
- package/dist/core/templates.js.map +1 -0
- package/dist/ui/ascii-logo.d.ts +7 -0
- package/dist/ui/ascii-logo.d.ts.map +1 -0
- package/dist/ui/ascii-logo.js +15 -0
- package/dist/ui/ascii-logo.js.map +1 -0
- package/dist/ui/tool-picker.d.ts +13 -0
- package/dist/ui/tool-picker.d.ts.map +1 -0
- package/dist/ui/tool-picker.js +76 -0
- package/dist/ui/tool-picker.js.map +1 -0
- package/dist/ui/welcome.d.ts +4 -0
- package/dist/ui/welcome.d.ts.map +1 -0
- package/dist/ui/welcome.js +43 -0
- package/dist/ui/welcome.js.map +1 -0
- package/dist/utils/changes.d.ts +19 -0
- package/dist/utils/changes.d.ts.map +1 -0
- package/dist/utils/changes.js +63 -0
- package/dist/utils/changes.js.map +1 -0
- package/dist/utils/git.d.ts +28 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +104 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/slugify.d.ts +12 -0
- package/dist/utils/slugify.d.ts.map +1 -0
- package/dist/utils/slugify.js +21 -0
- package/dist/utils/slugify.js.map +1 -0
- package/dist/utils/telemetry.d.ts +21 -0
- package/dist/utils/telemetry.d.ts.map +1 -0
- package/dist/utils/telemetry.js +32 -0
- package/dist/utils/telemetry.js.map +1 -0
- package/package.json +81 -0
- package/skills/whyspec-capture/SKILL.md +154 -0
- package/skills/whyspec-debug/SKILL.md +404 -0
- package/skills/whyspec-execute/SKILL.md +118 -0
- package/skills/whyspec-plan/SKILL.md +170 -0
- package/skills/whyspec-search/SKILL.md +69 -0
- 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 @@
|
|
|
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"}
|