@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,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whyspec show — Display the full story of a change with Decision Bridge delta.
|
|
3
|
+
*
|
|
4
|
+
* --json mode: returns all file contents + decision bridge delta.
|
|
5
|
+
* Non-JSON mode: formatted markdown output with sections and color-coded Decision Bridge.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { resolveChange } from "../utils/changes.js";
|
|
11
|
+
import { extractDecisions } from "../core/context.js";
|
|
12
|
+
import { parseTasks } from "./execute.js";
|
|
13
|
+
/**
|
|
14
|
+
* Extract decisions from a ctx_*.md context file.
|
|
15
|
+
* Parses the <decisions> XML tag within <reasoning>.
|
|
16
|
+
* Each line starting with "- " is a decision.
|
|
17
|
+
*/
|
|
18
|
+
function extractContextDecisions(ctxContent) {
|
|
19
|
+
const match = ctxContent.match(/<decisions>([\s\S]*?)<\/decisions>/);
|
|
20
|
+
if (!match)
|
|
21
|
+
return [];
|
|
22
|
+
return match[1]
|
|
23
|
+
.split("\n")
|
|
24
|
+
.map((l) => l.trim())
|
|
25
|
+
.filter((l) => l.startsWith("- "))
|
|
26
|
+
.map((l) => l.slice(2).trim());
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Detect "surprises" — decisions in context files that were NOT planned in design.md.
|
|
30
|
+
* Uses case-insensitive substring matching for fuzzy comparison.
|
|
31
|
+
*/
|
|
32
|
+
function detectSurprises(planned, decided) {
|
|
33
|
+
const lowerPlanned = planned.map((p) => p.toLowerCase());
|
|
34
|
+
return decided.filter((d) => {
|
|
35
|
+
const lowerD = d.toLowerCase();
|
|
36
|
+
// A decision is a surprise if no planned decision contains a key overlap.
|
|
37
|
+
// We check if any planned decision shares significant words with this decision.
|
|
38
|
+
return !lowerPlanned.some((p) => {
|
|
39
|
+
// Extract key words (>3 chars) from both.
|
|
40
|
+
const dWords = lowerD.split(/\W+/).filter((w) => w.length > 3);
|
|
41
|
+
return dWords.some((w) => p.includes(w));
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function readFileOrEmpty(filePath) {
|
|
46
|
+
if (!existsSync(filePath))
|
|
47
|
+
return "";
|
|
48
|
+
return readFileSync(filePath, "utf-8");
|
|
49
|
+
}
|
|
50
|
+
export async function showCommand(name, options) {
|
|
51
|
+
const gitwhyDir = join(process.cwd(), ".gitwhy");
|
|
52
|
+
const change = resolveChange(gitwhyDir, name);
|
|
53
|
+
// Read all files.
|
|
54
|
+
const intent = readFileOrEmpty(join(change.path, "intent.md"));
|
|
55
|
+
const design = readFileOrEmpty(join(change.path, "design.md"));
|
|
56
|
+
const tasks = readFileOrEmpty(join(change.path, "tasks.md"));
|
|
57
|
+
// Find all ctx_*.md files.
|
|
58
|
+
const ctxFiles = existsSync(change.path)
|
|
59
|
+
? readdirSync(change.path)
|
|
60
|
+
.filter((f) => f.startsWith("ctx_") && f.endsWith(".md"))
|
|
61
|
+
.sort()
|
|
62
|
+
: [];
|
|
63
|
+
const contexts = ctxFiles.map((f) => ({
|
|
64
|
+
id: f.replace(/\.md$/, ""),
|
|
65
|
+
content: readFileOrEmpty(join(change.path, f)),
|
|
66
|
+
}));
|
|
67
|
+
// Compute Decision Bridge delta.
|
|
68
|
+
const planned = extractDecisions(design);
|
|
69
|
+
// Also extract from intent.md if it has decisions.
|
|
70
|
+
const intentDecisions = extractDecisions(intent);
|
|
71
|
+
const allPlanned = [...new Set([...planned, ...intentDecisions])];
|
|
72
|
+
// Collect all decided items from all context files.
|
|
73
|
+
const allDecided = [];
|
|
74
|
+
for (const ctx of contexts) {
|
|
75
|
+
allDecided.push(...extractContextDecisions(ctx.content));
|
|
76
|
+
}
|
|
77
|
+
const surprises = detectSurprises(allPlanned, allDecided);
|
|
78
|
+
if (options.json) {
|
|
79
|
+
const output = {
|
|
80
|
+
change_name: change.name,
|
|
81
|
+
intent,
|
|
82
|
+
design,
|
|
83
|
+
tasks,
|
|
84
|
+
contexts,
|
|
85
|
+
decision_bridge_delta: {
|
|
86
|
+
planned: allPlanned,
|
|
87
|
+
decided: allDecided,
|
|
88
|
+
surprises,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
console.log(JSON.stringify(output, null, 2));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Non-JSON mode: formatted output.
|
|
95
|
+
console.log(chalk.bold.underline(`Change: ${change.name}`));
|
|
96
|
+
console.log();
|
|
97
|
+
// Intent section.
|
|
98
|
+
if (intent) {
|
|
99
|
+
console.log(chalk.bold.blue("── Intent ──────────────────────────────────────"));
|
|
100
|
+
console.log(intent.trim());
|
|
101
|
+
console.log();
|
|
102
|
+
}
|
|
103
|
+
// Design section.
|
|
104
|
+
if (design) {
|
|
105
|
+
console.log(chalk.bold.blue("── Design ──────────────────────────────────────"));
|
|
106
|
+
console.log(design.trim());
|
|
107
|
+
console.log();
|
|
108
|
+
}
|
|
109
|
+
// Tasks section.
|
|
110
|
+
if (tasks) {
|
|
111
|
+
const { progress } = parseTasks(tasks);
|
|
112
|
+
const pct = progress.total > 0 ? Math.round((progress.completed / progress.total) * 100) : 0;
|
|
113
|
+
console.log(chalk.bold.blue("── Tasks ───────────────────────────────────────"));
|
|
114
|
+
console.log(`Progress: ${pct}% (${progress.completed}/${progress.total})`);
|
|
115
|
+
console.log(tasks.trim());
|
|
116
|
+
console.log();
|
|
117
|
+
}
|
|
118
|
+
// Contexts section.
|
|
119
|
+
if (contexts.length > 0) {
|
|
120
|
+
console.log(chalk.bold.blue("── Contexts ────────────────────────────────────"));
|
|
121
|
+
for (const ctx of contexts) {
|
|
122
|
+
console.log(chalk.dim(`--- ${ctx.id} ---`));
|
|
123
|
+
console.log(ctx.content.trim());
|
|
124
|
+
console.log();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Decision Bridge delta.
|
|
128
|
+
console.log(chalk.bold.magenta("── Decision Bridge ─────────────────────────────"));
|
|
129
|
+
if (allPlanned.length > 0) {
|
|
130
|
+
console.log(chalk.bold("Planned decisions:"));
|
|
131
|
+
allPlanned.forEach((d) => console.log(` ${chalk.dim("○")} ${d}`));
|
|
132
|
+
}
|
|
133
|
+
if (allDecided.length > 0) {
|
|
134
|
+
console.log(chalk.bold("\nDecisions made:"));
|
|
135
|
+
allDecided.forEach((d) => console.log(` ${chalk.green("●")} ${d}`));
|
|
136
|
+
}
|
|
137
|
+
if (surprises.length > 0) {
|
|
138
|
+
console.log(chalk.bold.yellow("\nSurprises (not in plan):"));
|
|
139
|
+
surprises.forEach((d) => console.log(` ${chalk.yellow("⚡")} ${d}`));
|
|
140
|
+
}
|
|
141
|
+
if (allPlanned.length === 0 && allDecided.length === 0) {
|
|
142
|
+
console.log(chalk.dim(" No decisions tracked yet."));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=show.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/commands/show.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whyspec status — Get detailed status for a change.
|
|
3
|
+
*
|
|
4
|
+
* --json mode: returns file presence, task progress, commits, Decision Bridge state.
|
|
5
|
+
* Non-JSON mode: rich formatted status card.
|
|
6
|
+
*/
|
|
7
|
+
export interface StatusJsonOutput {
|
|
8
|
+
name: string;
|
|
9
|
+
files: {
|
|
10
|
+
intent: boolean;
|
|
11
|
+
design: boolean;
|
|
12
|
+
tasks: boolean;
|
|
13
|
+
contexts: string[];
|
|
14
|
+
debug: boolean;
|
|
15
|
+
};
|
|
16
|
+
progress: {
|
|
17
|
+
completed: number;
|
|
18
|
+
total: number;
|
|
19
|
+
};
|
|
20
|
+
commits: number;
|
|
21
|
+
decision_bridge: {
|
|
22
|
+
to_make: number;
|
|
23
|
+
made: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare function statusCommand(name: string, options: {
|
|
27
|
+
json?: boolean;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACL,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,OAAO,CAAC;QAChB,KAAK,EAAE,OAAO,CAAC;QACf,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;AAcD,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CA0Gf"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whyspec status — Get detailed status for a change.
|
|
3
|
+
*
|
|
4
|
+
* --json mode: returns file presence, task progress, commits, Decision Bridge state.
|
|
5
|
+
* Non-JSON mode: rich formatted status card.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { resolveChange } from "../utils/changes.js";
|
|
11
|
+
import { readMarkdown } from "../core/storage.js";
|
|
12
|
+
import { extractDecisions, getChangeFolderCreatedAt } from "../core/context.js";
|
|
13
|
+
import { getCommitsSince } from "../utils/git.js";
|
|
14
|
+
import { parseTasks } from "./execute.js";
|
|
15
|
+
/**
|
|
16
|
+
* Extract decisions from context file <decisions> tags.
|
|
17
|
+
*/
|
|
18
|
+
function extractContextDecisionCount(ctxContent) {
|
|
19
|
+
const match = ctxContent.match(/<decisions>([\s\S]*?)<\/decisions>/);
|
|
20
|
+
if (!match)
|
|
21
|
+
return 0;
|
|
22
|
+
return match[1]
|
|
23
|
+
.split("\n")
|
|
24
|
+
.map((l) => l.trim())
|
|
25
|
+
.filter((l) => l.startsWith("- ")).length;
|
|
26
|
+
}
|
|
27
|
+
export async function statusCommand(name, options) {
|
|
28
|
+
const gitwhyDir = join(process.cwd(), ".gitwhy");
|
|
29
|
+
const change = resolveChange(gitwhyDir, name);
|
|
30
|
+
// Check file presence.
|
|
31
|
+
const hasIntent = existsSync(join(change.path, "intent.md"));
|
|
32
|
+
const hasDesign = existsSync(join(change.path, "design.md"));
|
|
33
|
+
const hasTasks = existsSync(join(change.path, "tasks.md"));
|
|
34
|
+
const hasDebug = existsSync(join(change.path, "debug.md"));
|
|
35
|
+
// Find ctx_*.md files.
|
|
36
|
+
const ctxFiles = existsSync(change.path)
|
|
37
|
+
? readdirSync(change.path)
|
|
38
|
+
.filter((f) => f.startsWith("ctx_") && f.endsWith(".md"))
|
|
39
|
+
.sort()
|
|
40
|
+
: [];
|
|
41
|
+
// Parse task progress.
|
|
42
|
+
const tasksContent = readMarkdown(change.path, "tasks.md");
|
|
43
|
+
const { progress } = parseTasks(tasksContent ?? "");
|
|
44
|
+
// Count commits since creation.
|
|
45
|
+
let commitCount = 0;
|
|
46
|
+
try {
|
|
47
|
+
const createdAt = getChangeFolderCreatedAt(change.path);
|
|
48
|
+
const commits = getCommitsSince(createdAt);
|
|
49
|
+
commitCount = commits.length;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Gracefully handle if git operations fail.
|
|
53
|
+
}
|
|
54
|
+
// Decision Bridge state.
|
|
55
|
+
const designContent = readMarkdown(change.path, "design.md") ?? "";
|
|
56
|
+
const intentContent = readMarkdown(change.path, "intent.md") ?? "";
|
|
57
|
+
const plannedDecisions = [
|
|
58
|
+
...new Set([...extractDecisions(designContent), ...extractDecisions(intentContent)]),
|
|
59
|
+
];
|
|
60
|
+
let madeDecisions = 0;
|
|
61
|
+
for (const ctxFile of ctxFiles) {
|
|
62
|
+
const ctxContent = readMarkdown(change.path, ctxFile) ?? "";
|
|
63
|
+
madeDecisions += extractContextDecisionCount(ctxContent);
|
|
64
|
+
}
|
|
65
|
+
if (options.json) {
|
|
66
|
+
const output = {
|
|
67
|
+
name: change.name,
|
|
68
|
+
files: {
|
|
69
|
+
intent: hasIntent,
|
|
70
|
+
design: hasDesign,
|
|
71
|
+
tasks: hasTasks,
|
|
72
|
+
contexts: ctxFiles.map((f) => f.replace(/\.md$/, "")),
|
|
73
|
+
debug: hasDebug,
|
|
74
|
+
},
|
|
75
|
+
progress: { completed: progress.completed, total: progress.total },
|
|
76
|
+
commits: commitCount,
|
|
77
|
+
decision_bridge: { to_make: plannedDecisions.length, made: madeDecisions },
|
|
78
|
+
};
|
|
79
|
+
console.log(JSON.stringify(output, null, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Non-JSON mode: rich status card.
|
|
83
|
+
console.log(chalk.bold.underline(`Status: ${change.name}`));
|
|
84
|
+
console.log();
|
|
85
|
+
// Files.
|
|
86
|
+
const check = (present) => (present ? chalk.green("✓") : chalk.red("✗"));
|
|
87
|
+
console.log(chalk.bold("Files:"));
|
|
88
|
+
console.log(` ${check(hasIntent)} intent.md`);
|
|
89
|
+
console.log(` ${check(hasDesign)} design.md`);
|
|
90
|
+
console.log(` ${check(hasTasks)} tasks.md`);
|
|
91
|
+
console.log(` ${check(hasDebug)} debug.md`);
|
|
92
|
+
if (ctxFiles.length > 0) {
|
|
93
|
+
for (const f of ctxFiles) {
|
|
94
|
+
console.log(` ${chalk.green("✓")} ${f}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(` ${chalk.dim("○")} no context files yet`);
|
|
99
|
+
}
|
|
100
|
+
console.log();
|
|
101
|
+
// Progress.
|
|
102
|
+
if (progress.total > 0) {
|
|
103
|
+
const pct = Math.round((progress.completed / progress.total) * 100);
|
|
104
|
+
const barLen = 30;
|
|
105
|
+
const filled = Math.round((pct / 100) * barLen);
|
|
106
|
+
const bar = chalk.green("█".repeat(filled)) + chalk.dim("░".repeat(barLen - filled));
|
|
107
|
+
console.log(chalk.bold("Tasks:"));
|
|
108
|
+
console.log(` ${bar} ${pct}% (${progress.completed}/${progress.total})`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log(chalk.bold("Tasks:") + chalk.dim(" none defined"));
|
|
112
|
+
}
|
|
113
|
+
console.log();
|
|
114
|
+
// Commits.
|
|
115
|
+
console.log(chalk.bold("Commits:") + ` ${commitCount} since creation`);
|
|
116
|
+
console.log();
|
|
117
|
+
// Decision Bridge.
|
|
118
|
+
console.log(chalk.bold("Decision Bridge:"));
|
|
119
|
+
console.log(` ${chalk.dim("To make:")} ${plannedDecisions.length}`);
|
|
120
|
+
console.log(` ${chalk.dim("Made:")} ${madeDecisions}`);
|
|
121
|
+
if (plannedDecisions.length > 0 && madeDecisions === 0) {
|
|
122
|
+
console.log(chalk.yellow(" → Run `whyspec capture` to record decisions"));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whyspec template — Return a raw file template by type.
|
|
3
|
+
*
|
|
4
|
+
* --json mode: returns { type, content }.
|
|
5
|
+
* Non-JSON mode: prints the raw template content.
|
|
6
|
+
*/
|
|
7
|
+
export interface TemplateJsonOutput {
|
|
8
|
+
type: string;
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function templateCommand(type: string, options: {
|
|
12
|
+
json?: boolean;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/commands/template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAef"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whyspec template — Return a raw file template by type.
|
|
3
|
+
*
|
|
4
|
+
* --json mode: returns { type, content }.
|
|
5
|
+
* Non-JSON mode: prints the raw template content.
|
|
6
|
+
*/
|
|
7
|
+
import { getTemplate } from "../core/templates.js";
|
|
8
|
+
const VALID_TYPES = ["intent", "design", "tasks", "context", "debug"];
|
|
9
|
+
function isValidType(type) {
|
|
10
|
+
return VALID_TYPES.includes(type);
|
|
11
|
+
}
|
|
12
|
+
export async function templateCommand(type, options) {
|
|
13
|
+
if (!isValidType(type)) {
|
|
14
|
+
const valid = VALID_TYPES.join(", ");
|
|
15
|
+
throw new Error(`Invalid template type "${type}". Valid types: ${valid}`);
|
|
16
|
+
}
|
|
17
|
+
const content = getTemplate(type);
|
|
18
|
+
if (options.json) {
|
|
19
|
+
const output = { type, content };
|
|
20
|
+
console.log(JSON.stringify(output, null, 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log(content);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/commands/template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAU,CAAC;AAG/E,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAQ,WAAiC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY,EACZ,OAA2B;IAE3B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,mBAAmB,KAAK,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAElC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,MAAM,GAAuB,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain/topic detection from file paths.
|
|
3
|
+
* Ported from GitWhy Go: internal/storage/categorize.go
|
|
4
|
+
*
|
|
5
|
+
* AutoCategorize uses majority-vote across file paths to determine
|
|
6
|
+
* a domain, and slugifies the title to produce a topic.
|
|
7
|
+
*/
|
|
8
|
+
interface CategoryRule {
|
|
9
|
+
pattern: RegExp;
|
|
10
|
+
domain: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Rules ordered: file-suffix patterns first (most specific),
|
|
14
|
+
* then directory-prefix patterns.
|
|
15
|
+
*/
|
|
16
|
+
declare const categoryRules: CategoryRule[];
|
|
17
|
+
/** Classifies a single file path into a domain, or empty string if no match. */
|
|
18
|
+
declare function classifyPath(filePath: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* AutoCategorize determines a domain and topic from a title and file paths.
|
|
21
|
+
*
|
|
22
|
+
* Domain: chosen by majority vote across all file paths. "uncategorized" if none match.
|
|
23
|
+
* Topic: derived from title via slugify. "general" if title is empty.
|
|
24
|
+
*/
|
|
25
|
+
export declare function autoCategorize(title: string, filePaths: string[]): {
|
|
26
|
+
domain: string;
|
|
27
|
+
topic: string;
|
|
28
|
+
};
|
|
29
|
+
export { classifyPath, categoryRules };
|
|
30
|
+
//# sourceMappingURL=categorize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"categorize.d.ts","sourceRoot":"","sources":["../../src/core/categorize.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,QAAA,MAAM,aAAa,EAAE,YAAY,EAehC,CAAC;AAEF,gFAAgF;AAChF,iBAAS,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO9C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EAAE,GAClB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA+BnC;AAED,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain/topic detection from file paths.
|
|
3
|
+
* Ported from GitWhy Go: internal/storage/categorize.go
|
|
4
|
+
*
|
|
5
|
+
* AutoCategorize uses majority-vote across file paths to determine
|
|
6
|
+
* a domain, and slugifies the title to produce a topic.
|
|
7
|
+
*/
|
|
8
|
+
import { slugify } from "../utils/slugify.js";
|
|
9
|
+
/**
|
|
10
|
+
* Rules ordered: file-suffix patterns first (most specific),
|
|
11
|
+
* then directory-prefix patterns.
|
|
12
|
+
*/
|
|
13
|
+
const categoryRules = [
|
|
14
|
+
// File-suffix rules first (most specific)
|
|
15
|
+
{ pattern: /_test\.go$/, domain: "testing" },
|
|
16
|
+
{ pattern: /\.test\.(ts|tsx|js|jsx)$/, domain: "testing" },
|
|
17
|
+
{ pattern: /\.spec\.(ts|tsx|js|jsx)$/, domain: "testing" },
|
|
18
|
+
{ pattern: /\.md$/, domain: "documentation" },
|
|
19
|
+
{ pattern: /(^|\/)Dockerfile$/, domain: "infrastructure" },
|
|
20
|
+
// Directory-prefix rules
|
|
21
|
+
{ pattern: /^(src\/auth|internal\/auth|auth)\//, domain: "authentication" },
|
|
22
|
+
{ pattern: /^(src\/db|internal\/db|db|migrations)\//, domain: "database" },
|
|
23
|
+
{ pattern: /^(src\/api|api|routes)\//, domain: "api-design" },
|
|
24
|
+
{ pattern: /^(src\/ui|web|frontend|components)\//, domain: "frontend" },
|
|
25
|
+
{ pattern: /^(test|tests)\//, domain: "testing" },
|
|
26
|
+
{ pattern: /^docs\//, domain: "documentation" },
|
|
27
|
+
{ pattern: /^(infra|deploy|\.github)\//, domain: "infrastructure" },
|
|
28
|
+
];
|
|
29
|
+
/** Classifies a single file path into a domain, or empty string if no match. */
|
|
30
|
+
function classifyPath(filePath) {
|
|
31
|
+
for (const rule of categoryRules) {
|
|
32
|
+
if (rule.pattern.test(filePath)) {
|
|
33
|
+
return rule.domain;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* AutoCategorize determines a domain and topic from a title and file paths.
|
|
40
|
+
*
|
|
41
|
+
* Domain: chosen by majority vote across all file paths. "uncategorized" if none match.
|
|
42
|
+
* Topic: derived from title via slugify. "general" if title is empty.
|
|
43
|
+
*/
|
|
44
|
+
export function autoCategorize(title, filePaths) {
|
|
45
|
+
const topic = slugify(title);
|
|
46
|
+
if (!filePaths || filePaths.length === 0) {
|
|
47
|
+
return { domain: "uncategorized", topic };
|
|
48
|
+
}
|
|
49
|
+
// Count votes for each domain
|
|
50
|
+
const votes = {};
|
|
51
|
+
for (const fp of filePaths) {
|
|
52
|
+
const d = classifyPath(fp);
|
|
53
|
+
if (d) {
|
|
54
|
+
votes[d] = (votes[d] ?? 0) + 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (Object.keys(votes).length === 0) {
|
|
58
|
+
return { domain: "uncategorized", topic };
|
|
59
|
+
}
|
|
60
|
+
// Pick domain with highest vote count
|
|
61
|
+
let bestDomain = "";
|
|
62
|
+
let bestCount = 0;
|
|
63
|
+
for (const [domain, count] of Object.entries(votes)) {
|
|
64
|
+
if (count > bestCount) {
|
|
65
|
+
bestDomain = domain;
|
|
66
|
+
bestCount = count;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { domain: bestDomain, topic };
|
|
70
|
+
}
|
|
71
|
+
export { classifyPath, categoryRules };
|
|
72
|
+
//# sourceMappingURL=categorize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"categorize.js","sourceRoot":"","sources":["../../src/core/categorize.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAO9C;;;GAGG;AACH,MAAM,aAAa,GAAmB;IACpC,0CAA0C;IAC1C,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE;IAC5C,EAAE,OAAO,EAAE,0BAA0B,EAAE,MAAM,EAAE,SAAS,EAAE;IAC1D,EAAE,OAAO,EAAE,0BAA0B,EAAE,MAAM,EAAE,SAAS,EAAE;IAC1D,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE;IAC7C,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,gBAAgB,EAAE;IAC1D,yBAAyB;IACzB,EAAE,OAAO,EAAE,oCAAoC,EAAE,MAAM,EAAE,gBAAgB,EAAE;IAC3E,EAAE,OAAO,EAAE,yCAAyC,EAAE,MAAM,EAAE,UAAU,EAAE;IAC1E,EAAE,OAAO,EAAE,0BAA0B,EAAE,MAAM,EAAE,YAAY,EAAE;IAC7D,EAAE,OAAO,EAAE,sCAAsC,EAAE,MAAM,EAAE,UAAU,EAAE;IACvE,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,SAAS,EAAE;IACjD,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE;IAC/C,EAAE,OAAO,EAAE,4BAA4B,EAAE,MAAM,EAAE,gBAAgB,EAAE;CACpE,CAAC;AAEF,gFAAgF;AAChF,SAAS,YAAY,CAAC,QAAgB;IACpC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAa,EACb,SAAmB;IAEnB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAE7B,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,8BAA8B;IAC9B,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;QAC3B,IAAI,CAAC,EAAE,CAAC;YACN,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,sCAAsC;IACtC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;YACtB,UAAU,GAAG,MAAM,CAAC;YACpB,SAAS,GAAG,KAAK,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACvC,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WhySpec config.yaml reader/writer with Zod validation.
|
|
3
|
+
* Reads/writes .gitwhy/config.yaml in the project root.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
/** Zod schema for .gitwhy/config.yaml */
|
|
7
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
8
|
+
version: z.ZodDefault<z.ZodString>;
|
|
9
|
+
project: z.ZodDefault<z.ZodObject<{
|
|
10
|
+
name: z.ZodDefault<z.ZodString>;
|
|
11
|
+
description: z.ZodDefault<z.ZodString>;
|
|
12
|
+
}, z.core.$strip>>;
|
|
13
|
+
telemetry: z.ZodDefault<z.ZodBoolean>;
|
|
14
|
+
default_agent: z.ZodDefault<z.ZodString>;
|
|
15
|
+
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
16
|
+
context: z.ZodDefault<z.ZodString>;
|
|
17
|
+
rules: z.ZodDefault<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export type WhySpecConfig = z.infer<typeof ConfigSchema>;
|
|
20
|
+
/** Reads and validates .gitwhy/config.yaml. Returns defaults if file doesn't exist. */
|
|
21
|
+
export declare function readConfig(repoRoot: string): WhySpecConfig;
|
|
22
|
+
/** Writes a validated config to .gitwhy/config.yaml. */
|
|
23
|
+
export declare function writeConfig(repoRoot: string, config: Partial<WhySpecConfig>): void;
|
|
24
|
+
/** Returns the default config as a YAML string (for init). */
|
|
25
|
+
export declare function defaultConfigYaml(projectName?: string): string;
|
|
26
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,yCAAyC;AACzC,eAAO,MAAM,YAAY;;;;;;;;;;;iBAavB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AASzD,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAS1D;AAED,wDAAwD;AACxD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAIlF;AAED,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAAC,WAAW,SAAK,GAAG,MAAM,CAK1D"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WhySpec config.yaml reader/writer with Zod validation.
|
|
3
|
+
* Reads/writes .gitwhy/config.yaml in the project root.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import YAML from "yaml";
|
|
9
|
+
/** Zod schema for .gitwhy/config.yaml */
|
|
10
|
+
export const ConfigSchema = z.object({
|
|
11
|
+
version: z.string().default("1.0"),
|
|
12
|
+
project: z
|
|
13
|
+
.object({
|
|
14
|
+
name: z.string().default(""),
|
|
15
|
+
description: z.string().default(""),
|
|
16
|
+
})
|
|
17
|
+
.default(() => ({ name: "", description: "" })),
|
|
18
|
+
telemetry: z.boolean().default(true),
|
|
19
|
+
default_agent: z.string().default("claude-code"),
|
|
20
|
+
tools: z.array(z.string()).default(["claude-code"]),
|
|
21
|
+
context: z.string().default(""),
|
|
22
|
+
rules: z.string().default(""),
|
|
23
|
+
});
|
|
24
|
+
const CONFIG_DIR = ".gitwhy";
|
|
25
|
+
const CONFIG_FILE = "config.yaml";
|
|
26
|
+
function configPath(repoRoot) {
|
|
27
|
+
return join(repoRoot, CONFIG_DIR, CONFIG_FILE);
|
|
28
|
+
}
|
|
29
|
+
/** Reads and validates .gitwhy/config.yaml. Returns defaults if file doesn't exist. */
|
|
30
|
+
export function readConfig(repoRoot) {
|
|
31
|
+
const path = configPath(repoRoot);
|
|
32
|
+
if (!existsSync(path)) {
|
|
33
|
+
return ConfigSchema.parse({});
|
|
34
|
+
}
|
|
35
|
+
const raw = readFileSync(path, "utf-8");
|
|
36
|
+
const parsed = YAML.parse(raw) ?? {};
|
|
37
|
+
return ConfigSchema.parse(parsed);
|
|
38
|
+
}
|
|
39
|
+
/** Writes a validated config to .gitwhy/config.yaml. */
|
|
40
|
+
export function writeConfig(repoRoot, config) {
|
|
41
|
+
const validated = ConfigSchema.parse(config);
|
|
42
|
+
const yamlStr = YAML.stringify(validated);
|
|
43
|
+
writeFileSync(configPath(repoRoot), yamlStr, "utf-8");
|
|
44
|
+
}
|
|
45
|
+
/** Returns the default config as a YAML string (for init). */
|
|
46
|
+
export function defaultConfigYaml(projectName = "") {
|
|
47
|
+
const config = ConfigSchema.parse({
|
|
48
|
+
project: { name: projectName },
|
|
49
|
+
});
|
|
50
|
+
return YAML.stringify(config);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,yCAAyC;AACzC,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAClC,OAAO,EAAE,CAAC;SACP,MAAM,CAAC;QACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;KACpC,CAAC;SACD,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;IACjD,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACpC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC;IAChD,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC;IACnD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC9B,CAAC,CAAC;AAIH,MAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,MAAM,WAAW,GAAG,aAAa,CAAC;AAElC,SAAS,UAAU,CAAC,QAAgB;IAClC,OAAO,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;AACjD,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IACrC,OAAO,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,MAA8B;IAC1E,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,iBAAiB,CAAC,WAAW,GAAG,EAAE;IAChD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC;QAChC,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;KAC/B,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context generation for WhySpec capture command.
|
|
3
|
+
* - Context ID: ctx_ + 8-char alphanumeric
|
|
4
|
+
* - SaaS XML format (GitWhy compatible)
|
|
5
|
+
* - Decision Bridge extraction from design.md
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Generate a context ID: ctx_ + 8 random alphanumeric characters.
|
|
9
|
+
* Matches GitWhy convention (FR-20).
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateContextId(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Render the SaaS-compatible XML context template.
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderContextXml(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Extract decision items from design.md's "## Decisions to Make" section.
|
|
18
|
+
* Parses checkbox lines: `- [ ] description` and `- [x] description`.
|
|
19
|
+
* Returns the description text of each decision item.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractDecisions(designContent: string): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Get the start time of a change (used for commit range detection).
|
|
24
|
+
* Reads .started file written by `whyspec plan`. Falls back to folder birthtime.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getChangeFolderCreatedAt(changePath: string): Date;
|
|
27
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAQ1C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,CAyBhE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAUjE"}
|