agent-bober 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/.claude-plugin/plugin.json +9 -0
- package/LICENSE +21 -0
- package/README.md +495 -0
- package/agents/bober-evaluator.md +323 -0
- package/agents/bober-generator.md +245 -0
- package/agents/bober-planner.md +248 -0
- package/dist/cli/commands/eval.d.ts +6 -0
- package/dist/cli/commands/eval.d.ts.map +1 -0
- package/dist/cli/commands/eval.js +129 -0
- package/dist/cli/commands/eval.js.map +1 -0
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +547 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +5 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +87 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/run.d.ts +5 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +120 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/sprint.d.ts +6 -0
- package/dist/cli/commands/sprint.d.ts.map +1 -0
- package/dist/cli/commands/sprint.js +206 -0
- package/dist/cli/commands/sprint.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 +124 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/defaults.d.ts +15 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +226 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +8 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +18 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +189 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +904 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +181 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/contracts/eval-result.d.ts +205 -0
- package/dist/contracts/eval-result.d.ts.map +1 -0
- package/dist/contracts/eval-result.js +87 -0
- package/dist/contracts/eval-result.js.map +1 -0
- package/dist/contracts/index.d.ts +4 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +16 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/spec.d.ts +101 -0
- package/dist/contracts/spec.d.ts.map +1 -0
- package/dist/contracts/spec.js +51 -0
- package/dist/contracts/spec.js.map +1 -0
- package/dist/contracts/sprint-contract.d.ts +141 -0
- package/dist/contracts/sprint-contract.d.ts.map +1 -0
- package/dist/contracts/sprint-contract.js +80 -0
- package/dist/contracts/sprint-contract.js.map +1 -0
- package/dist/evaluators/builtin/api-check.d.ts +13 -0
- package/dist/evaluators/builtin/api-check.d.ts.map +1 -0
- package/dist/evaluators/builtin/api-check.js +152 -0
- package/dist/evaluators/builtin/api-check.js.map +1 -0
- package/dist/evaluators/builtin/build-check.d.ts +17 -0
- package/dist/evaluators/builtin/build-check.d.ts.map +1 -0
- package/dist/evaluators/builtin/build-check.js +155 -0
- package/dist/evaluators/builtin/build-check.js.map +1 -0
- package/dist/evaluators/builtin/command-runner.d.ts +26 -0
- package/dist/evaluators/builtin/command-runner.d.ts.map +1 -0
- package/dist/evaluators/builtin/command-runner.js +114 -0
- package/dist/evaluators/builtin/command-runner.js.map +1 -0
- package/dist/evaluators/builtin/lint.d.ts +17 -0
- package/dist/evaluators/builtin/lint.d.ts.map +1 -0
- package/dist/evaluators/builtin/lint.js +264 -0
- package/dist/evaluators/builtin/lint.js.map +1 -0
- package/dist/evaluators/builtin/playwright.d.ts +16 -0
- package/dist/evaluators/builtin/playwright.d.ts.map +1 -0
- package/dist/evaluators/builtin/playwright.js +238 -0
- package/dist/evaluators/builtin/playwright.js.map +1 -0
- package/dist/evaluators/builtin/typescript-check.d.ts +12 -0
- package/dist/evaluators/builtin/typescript-check.d.ts.map +1 -0
- package/dist/evaluators/builtin/typescript-check.js +155 -0
- package/dist/evaluators/builtin/typescript-check.js.map +1 -0
- package/dist/evaluators/builtin/unit-test.d.ts +18 -0
- package/dist/evaluators/builtin/unit-test.d.ts.map +1 -0
- package/dist/evaluators/builtin/unit-test.js +279 -0
- package/dist/evaluators/builtin/unit-test.js.map +1 -0
- package/dist/evaluators/index.d.ts +11 -0
- package/dist/evaluators/index.d.ts.map +1 -0
- package/dist/evaluators/index.js +13 -0
- package/dist/evaluators/index.js.map +1 -0
- package/dist/evaluators/plugin-interface.d.ts +50 -0
- package/dist/evaluators/plugin-interface.d.ts.map +1 -0
- package/dist/evaluators/plugin-interface.js +2 -0
- package/dist/evaluators/plugin-interface.js.map +1 -0
- package/dist/evaluators/plugin-loader.d.ts +18 -0
- package/dist/evaluators/plugin-loader.d.ts.map +1 -0
- package/dist/evaluators/plugin-loader.js +107 -0
- package/dist/evaluators/plugin-loader.js.map +1 -0
- package/dist/evaluators/registry.d.ts +78 -0
- package/dist/evaluators/registry.d.ts.map +1 -0
- package/dist/evaluators/registry.js +238 -0
- package/dist/evaluators/registry.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/context-handoff.d.ts +543 -0
- package/dist/orchestrator/context-handoff.d.ts.map +1 -0
- package/dist/orchestrator/context-handoff.js +133 -0
- package/dist/orchestrator/context-handoff.js.map +1 -0
- package/dist/orchestrator/evaluator-agent.d.ts +15 -0
- package/dist/orchestrator/evaluator-agent.d.ts.map +1 -0
- package/dist/orchestrator/evaluator-agent.js +233 -0
- package/dist/orchestrator/evaluator-agent.js.map +1 -0
- package/dist/orchestrator/generator-agent.d.ts +16 -0
- package/dist/orchestrator/generator-agent.d.ts.map +1 -0
- package/dist/orchestrator/generator-agent.js +147 -0
- package/dist/orchestrator/generator-agent.js.map +1 -0
- package/dist/orchestrator/pipeline.d.ts +24 -0
- package/dist/orchestrator/pipeline.d.ts.map +1 -0
- package/dist/orchestrator/pipeline.js +290 -0
- package/dist/orchestrator/pipeline.js.map +1 -0
- package/dist/orchestrator/planner-agent.d.ts +10 -0
- package/dist/orchestrator/planner-agent.d.ts.map +1 -0
- package/dist/orchestrator/planner-agent.js +187 -0
- package/dist/orchestrator/planner-agent.js.map +1 -0
- package/dist/state/helpers.d.ts +5 -0
- package/dist/state/helpers.d.ts.map +1 -0
- package/dist/state/helpers.js +8 -0
- package/dist/state/helpers.js.map +1 -0
- package/dist/state/history.d.ts +39 -0
- package/dist/state/history.d.ts.map +1 -0
- package/dist/state/history.js +162 -0
- package/dist/state/history.js.map +1 -0
- package/dist/state/index.d.ts +8 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +22 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/plan-state.d.ts +21 -0
- package/dist/state/plan-state.d.ts.map +1 -0
- package/dist/state/plan-state.js +108 -0
- package/dist/state/plan-state.js.map +1 -0
- package/dist/state/sprint-state.d.ts +20 -0
- package/dist/state/sprint-state.d.ts.map +1 -0
- package/dist/state/sprint-state.js +98 -0
- package/dist/state/sprint-state.js.map +1 -0
- package/dist/utils/fs.d.ts +31 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +67 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/git.d.ts +35 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +84 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +73 -0
- package/dist/utils/logger.js.map +1 -0
- package/hooks/hooks.json +10 -0
- package/package.json +67 -0
- package/scripts/detect-stack.sh +287 -0
- package/scripts/init-project.sh +206 -0
- package/scripts/run-eval.sh +175 -0
- package/skills/bober.anchor/SKILL.md +365 -0
- package/skills/bober.anchor/references/anchor-guide.md +567 -0
- package/skills/bober.brownfield/SKILL.md +422 -0
- package/skills/bober.brownfield/references/codebase-analysis.md +304 -0
- package/skills/bober.eval/SKILL.md +235 -0
- package/skills/bober.eval/references/eval-strategies.md +407 -0
- package/skills/bober.eval/references/feedback-format.md +182 -0
- package/skills/bober.plan/SKILL.md +244 -0
- package/skills/bober.plan/references/clarification-guide.md +124 -0
- package/skills/bober.plan/references/spec-schema.md +253 -0
- package/skills/bober.react/SKILL.md +330 -0
- package/skills/bober.react/references/react-scaffold.md +344 -0
- package/skills/bober.run/SKILL.md +303 -0
- package/skills/bober.solidity/SKILL.md +416 -0
- package/skills/bober.solidity/references/solidity-guide.md +487 -0
- package/skills/bober.sprint/SKILL.md +280 -0
- package/skills/bober.sprint/references/contract-schema.md +251 -0
- package/templates/base/CLAUDE.md +20 -0
- package/templates/base/bober.config.json +35 -0
- package/templates/brownfield/CLAUDE.md +34 -0
- package/templates/brownfield/bober.config.json +37 -0
- package/templates/presets/anchor/CLAUDE.md +163 -0
- package/templates/presets/anchor/bober.config.json +9 -0
- package/templates/presets/api-node/CLAUDE.md +153 -0
- package/templates/presets/api-node/bober.config.json +10 -0
- package/templates/presets/nextjs/CLAUDE.md +82 -0
- package/templates/presets/nextjs/bober.config.json +14 -0
- package/templates/presets/python-api/CLAUDE.md +202 -0
- package/templates/presets/python-api/bober.config.json +9 -0
- package/templates/presets/react-vite/CLAUDE.md +71 -0
- package/templates/presets/react-vite/bober.config.json +53 -0
- package/templates/presets/react-vite/scaffold/package.json +45 -0
- package/templates/presets/react-vite/scaffold/server/index.ts +38 -0
- package/templates/presets/react-vite/scaffold/server/tsconfig.json +24 -0
- package/templates/presets/react-vite/scaffold/src/App.tsx +37 -0
- package/templates/presets/react-vite/scaffold/src/index.html +12 -0
- package/templates/presets/react-vite/scaffold/src/main.tsx +12 -0
- package/templates/presets/react-vite/scaffold/tsconfig.json +27 -0
- package/templates/presets/react-vite/scaffold/vite.config.ts +34 -0
- package/templates/presets/solidity/CLAUDE.md +106 -0
- package/templates/presets/solidity/bober.config.json +9 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { readFile, writeFile, appendFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { ensureDir } from "./helpers.js";
|
|
5
|
+
// ── Constants ───────────────────────────────────────────────────────
|
|
6
|
+
const BOBER_DIR = ".bober";
|
|
7
|
+
const HISTORY_FILE = "history.jsonl";
|
|
8
|
+
const PROGRESS_FILE = "progress.md";
|
|
9
|
+
function historyPath(projectRoot) {
|
|
10
|
+
return join(projectRoot, BOBER_DIR, HISTORY_FILE);
|
|
11
|
+
}
|
|
12
|
+
function progressPath(projectRoot) {
|
|
13
|
+
return join(projectRoot, BOBER_DIR, PROGRESS_FILE);
|
|
14
|
+
}
|
|
15
|
+
// ── History Entry ───────────────────────────────────────────────────
|
|
16
|
+
export const PhaseSchema = z.enum([
|
|
17
|
+
"init",
|
|
18
|
+
"planning",
|
|
19
|
+
"generating",
|
|
20
|
+
"evaluating",
|
|
21
|
+
"rework",
|
|
22
|
+
"complete",
|
|
23
|
+
"failed",
|
|
24
|
+
]);
|
|
25
|
+
export const HistoryEntrySchema = z.object({
|
|
26
|
+
timestamp: z.string().datetime(),
|
|
27
|
+
event: z.string().min(1),
|
|
28
|
+
phase: PhaseSchema,
|
|
29
|
+
sprintId: z.string().optional(),
|
|
30
|
+
details: z.record(z.string(), z.unknown()),
|
|
31
|
+
});
|
|
32
|
+
// ── History Operations ──────────────────────────────────────────────
|
|
33
|
+
/**
|
|
34
|
+
* Append a history entry to the JSONL log file.
|
|
35
|
+
*/
|
|
36
|
+
export async function appendHistory(projectRoot, entry) {
|
|
37
|
+
const boberDir = join(projectRoot, BOBER_DIR);
|
|
38
|
+
await ensureDir(boberDir);
|
|
39
|
+
const validation = HistoryEntrySchema.safeParse(entry);
|
|
40
|
+
if (!validation.success) {
|
|
41
|
+
const issues = validation.error.issues
|
|
42
|
+
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
|
43
|
+
.join("\n");
|
|
44
|
+
throw new Error(`Invalid history entry:\n${issues}`);
|
|
45
|
+
}
|
|
46
|
+
const line = JSON.stringify(entry) + "\n";
|
|
47
|
+
await appendFile(historyPath(projectRoot), line, "utf-8");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Load all history entries from the JSONL log.
|
|
51
|
+
* Skips malformed lines.
|
|
52
|
+
*/
|
|
53
|
+
export async function loadHistory(projectRoot) {
|
|
54
|
+
let content;
|
|
55
|
+
try {
|
|
56
|
+
content = await readFile(historyPath(projectRoot), "utf-8");
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// File doesn't exist yet
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
63
|
+
const entries = [];
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(line);
|
|
67
|
+
const result = HistoryEntrySchema.safeParse(parsed);
|
|
68
|
+
if (result.success) {
|
|
69
|
+
entries.push(result.data);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Skip malformed lines
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return entries;
|
|
77
|
+
}
|
|
78
|
+
// ── Progress Markdown ───────────────────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* Update the human-readable progress.md file with current state.
|
|
81
|
+
*/
|
|
82
|
+
export async function updateProgress(projectRoot, contracts, spec) {
|
|
83
|
+
const boberDir = join(projectRoot, BOBER_DIR);
|
|
84
|
+
await ensureDir(boberDir);
|
|
85
|
+
const lines = [];
|
|
86
|
+
lines.push("# Bober Progress");
|
|
87
|
+
lines.push("");
|
|
88
|
+
lines.push(`Last updated: ${new Date().toISOString()}`);
|
|
89
|
+
lines.push("");
|
|
90
|
+
// Plan summary
|
|
91
|
+
if (spec) {
|
|
92
|
+
lines.push("## Plan");
|
|
93
|
+
lines.push("");
|
|
94
|
+
lines.push(`**${spec.title}**`);
|
|
95
|
+
lines.push("");
|
|
96
|
+
lines.push(spec.description);
|
|
97
|
+
lines.push("");
|
|
98
|
+
lines.push(`- Features: ${spec.features.length}`);
|
|
99
|
+
lines.push(`- Tech stack: ${spec.techStack.join(", ") || "not specified"}`);
|
|
100
|
+
lines.push("");
|
|
101
|
+
}
|
|
102
|
+
// Sprint summary
|
|
103
|
+
lines.push("## Sprints");
|
|
104
|
+
lines.push("");
|
|
105
|
+
if (contracts.length === 0) {
|
|
106
|
+
lines.push("No sprints yet.");
|
|
107
|
+
lines.push("");
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const passed = contracts.filter((c) => c.status === "passed").length;
|
|
111
|
+
const failed = contracts.filter((c) => c.status === "failed").length;
|
|
112
|
+
const inProgress = contracts.filter((c) => c.status === "in-progress" || c.status === "evaluating").length;
|
|
113
|
+
const pending = contracts.filter((c) => c.status === "proposed" ||
|
|
114
|
+
c.status === "negotiating" ||
|
|
115
|
+
c.status === "agreed").length;
|
|
116
|
+
lines.push(`| Status | Count |`);
|
|
117
|
+
lines.push(`| --- | --- |`);
|
|
118
|
+
lines.push(`| Passed | ${passed} |`);
|
|
119
|
+
lines.push(`| Failed | ${failed} |`);
|
|
120
|
+
lines.push(`| In Progress | ${inProgress} |`);
|
|
121
|
+
lines.push(`| Pending | ${pending} |`);
|
|
122
|
+
lines.push(`| **Total** | **${contracts.length}** |`);
|
|
123
|
+
lines.push("");
|
|
124
|
+
// Individual sprint status
|
|
125
|
+
lines.push("### Sprint Details");
|
|
126
|
+
lines.push("");
|
|
127
|
+
for (const contract of contracts) {
|
|
128
|
+
const statusIcon = getStatusIcon(contract.status);
|
|
129
|
+
lines.push(`- ${statusIcon} **${contract.feature}** (${contract.id})`);
|
|
130
|
+
lines.push(` - Status: ${contract.status}`);
|
|
131
|
+
const criteriaTotal = contract.successCriteria.length;
|
|
132
|
+
const criteriaPassed = contract.successCriteria.filter((c) => c.passed).length;
|
|
133
|
+
if (criteriaTotal > 0) {
|
|
134
|
+
lines.push(` - Criteria: ${criteriaPassed}/${criteriaTotal} passed`);
|
|
135
|
+
}
|
|
136
|
+
if (contract.startedAt) {
|
|
137
|
+
lines.push(` - Started: ${contract.startedAt}`);
|
|
138
|
+
}
|
|
139
|
+
if (contract.completedAt) {
|
|
140
|
+
lines.push(` - Completed: ${contract.completedAt}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
lines.push("");
|
|
144
|
+
}
|
|
145
|
+
await writeFile(progressPath(projectRoot), lines.join("\n"), "utf-8");
|
|
146
|
+
}
|
|
147
|
+
function getStatusIcon(status) {
|
|
148
|
+
switch (status) {
|
|
149
|
+
case "passed":
|
|
150
|
+
return "[PASS]";
|
|
151
|
+
case "failed":
|
|
152
|
+
return "[FAIL]";
|
|
153
|
+
case "in-progress":
|
|
154
|
+
case "evaluating":
|
|
155
|
+
return "[WIP]";
|
|
156
|
+
case "needs-rework":
|
|
157
|
+
return "[REWORK]";
|
|
158
|
+
default:
|
|
159
|
+
return "[PENDING]";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/state/history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { saveContract, loadContract, listContracts, updateContract, } from "./sprint-state.js";
|
|
2
|
+
export { saveSpec, loadSpec, loadLatestSpec, listSpecs, } from "./plan-state.js";
|
|
3
|
+
export { PhaseSchema, HistoryEntrySchema, type Phase, type HistoryEntry, appendHistory, loadHistory, updateProgress, } from "./history.js";
|
|
4
|
+
/**
|
|
5
|
+
* Ensure the `.bober/` directory and all required subdirectories exist.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ensureBoberDir(projectRoot: string): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,SAAS,GACV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAEL,WAAW,EACX,kBAAkB,EAElB,KAAK,KAAK,EACV,KAAK,YAAY,EAEjB,aAAa,EACb,WAAW,EACX,cAAc,GACf,MAAM,cAAc,CAAC;AAKtB;;GAEG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { ensureDir } from "./helpers.js";
|
|
3
|
+
export { saveContract, loadContract, listContracts, updateContract, } from "./sprint-state.js";
|
|
4
|
+
export { saveSpec, loadSpec, loadLatestSpec, listSpecs, } from "./plan-state.js";
|
|
5
|
+
export {
|
|
6
|
+
// Zod schemas
|
|
7
|
+
PhaseSchema, HistoryEntrySchema,
|
|
8
|
+
// Functions
|
|
9
|
+
appendHistory, loadHistory, updateProgress, } from "./history.js";
|
|
10
|
+
const BOBER_DIR = ".bober";
|
|
11
|
+
const SUBDIRS = ["contracts", "specs"];
|
|
12
|
+
/**
|
|
13
|
+
* Ensure the `.bober/` directory and all required subdirectories exist.
|
|
14
|
+
*/
|
|
15
|
+
export async function ensureBoberDir(projectRoot) {
|
|
16
|
+
const boberRoot = join(projectRoot, BOBER_DIR);
|
|
17
|
+
await ensureDir(boberRoot);
|
|
18
|
+
for (const sub of SUBDIRS) {
|
|
19
|
+
await ensureDir(join(boberRoot, sub));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,SAAS,GACV,MAAM,iBAAiB,CAAC;AAEzB,OAAO;AACL,cAAc;AACd,WAAW,EACX,kBAAkB;AAIlB,YAAY;AACZ,aAAa,EACb,WAAW,EACX,cAAc,GACf,MAAM,cAAc,CAAC;AAEtB,MAAM,SAAS,GAAG,QAAQ,CAAC;AAC3B,MAAM,OAAO,GAAG,CAAC,WAAW,EAAE,OAAO,CAAU,CAAC;AAEhD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,WAAmB;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC/C,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IAE3B,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;IACxC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type PlanSpec } from "../contracts/spec.js";
|
|
2
|
+
/**
|
|
3
|
+
* Save a plan spec to disk.
|
|
4
|
+
* Overwrites any existing spec with the same id.
|
|
5
|
+
*/
|
|
6
|
+
export declare function saveSpec(projectRoot: string, spec: PlanSpec): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Load a plan spec by id.
|
|
9
|
+
* Throws if not found or invalid.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadSpec(projectRoot: string, id: string): Promise<PlanSpec>;
|
|
12
|
+
/**
|
|
13
|
+
* Load the most recently created spec (by `createdAt` field).
|
|
14
|
+
* Returns null if no specs exist.
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadLatestSpec(projectRoot: string): Promise<PlanSpec | null>;
|
|
17
|
+
/**
|
|
18
|
+
* List all saved specs, sorted by filename.
|
|
19
|
+
*/
|
|
20
|
+
export declare function listSpecs(projectRoot: string): Promise<PlanSpec[]>;
|
|
21
|
+
//# sourceMappingURL=plan-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-state.d.ts","sourceRoot":"","sources":["../../src/state/plan-state.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAcrE;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,QAAQ,CAAC,CAgCnB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAc1B;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgCrB"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { readFile, writeFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { PlanSpecSchema } from "../contracts/spec.js";
|
|
4
|
+
import { ensureDir } from "./helpers.js";
|
|
5
|
+
const SPECS_DIR = ".bober/specs";
|
|
6
|
+
function specsDir(projectRoot) {
|
|
7
|
+
return join(projectRoot, SPECS_DIR);
|
|
8
|
+
}
|
|
9
|
+
function specPath(projectRoot, id) {
|
|
10
|
+
const safeId = id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
11
|
+
return join(specsDir(projectRoot), `${safeId}.json`);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Save a plan spec to disk.
|
|
15
|
+
* Overwrites any existing spec with the same id.
|
|
16
|
+
*/
|
|
17
|
+
export async function saveSpec(projectRoot, spec) {
|
|
18
|
+
await ensureDir(specsDir(projectRoot));
|
|
19
|
+
const validation = PlanSpecSchema.safeParse(spec);
|
|
20
|
+
if (!validation.success) {
|
|
21
|
+
const issues = validation.error.issues
|
|
22
|
+
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
|
23
|
+
.join("\n");
|
|
24
|
+
throw new Error(`Invalid spec:\n${issues}`);
|
|
25
|
+
}
|
|
26
|
+
const filePath = specPath(projectRoot, spec.id);
|
|
27
|
+
await writeFile(filePath, JSON.stringify(spec, null, 2), "utf-8");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load a plan spec by id.
|
|
31
|
+
* Throws if not found or invalid.
|
|
32
|
+
*/
|
|
33
|
+
export async function loadSpec(projectRoot, id) {
|
|
34
|
+
const filePath = specPath(projectRoot, id);
|
|
35
|
+
let content;
|
|
36
|
+
try {
|
|
37
|
+
content = await readFile(filePath, "utf-8");
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
throw new Error(`Spec "${id}" not found: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
41
|
+
}
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = JSON.parse(content);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
throw new Error(`Invalid JSON in spec file for "${id}": ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
48
|
+
}
|
|
49
|
+
const result = PlanSpecSchema.safeParse(parsed);
|
|
50
|
+
if (!result.success) {
|
|
51
|
+
const issues = result.error.issues
|
|
52
|
+
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
|
53
|
+
.join("\n");
|
|
54
|
+
throw new Error(`Spec "${id}" failed validation:\n${issues}`);
|
|
55
|
+
}
|
|
56
|
+
return result.data;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Load the most recently created spec (by `createdAt` field).
|
|
60
|
+
* Returns null if no specs exist.
|
|
61
|
+
*/
|
|
62
|
+
export async function loadLatestSpec(projectRoot) {
|
|
63
|
+
const specs = await listSpecs(projectRoot);
|
|
64
|
+
if (specs.length === 0) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
// Sort by createdAt descending and return the newest
|
|
68
|
+
specs.sort((a, b) => {
|
|
69
|
+
const dateA = new Date(a.createdAt).getTime();
|
|
70
|
+
const dateB = new Date(b.createdAt).getTime();
|
|
71
|
+
return dateB - dateA;
|
|
72
|
+
});
|
|
73
|
+
return specs[0];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* List all saved specs, sorted by filename.
|
|
77
|
+
*/
|
|
78
|
+
export async function listSpecs(projectRoot) {
|
|
79
|
+
const dir = specsDir(projectRoot);
|
|
80
|
+
let entries;
|
|
81
|
+
try {
|
|
82
|
+
entries = await readdir(dir);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Directory doesn't exist yet
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
const jsonFiles = entries
|
|
89
|
+
.filter((f) => f.endsWith(".json"))
|
|
90
|
+
.sort();
|
|
91
|
+
const specs = [];
|
|
92
|
+
for (const file of jsonFiles) {
|
|
93
|
+
const filePath = join(dir, file);
|
|
94
|
+
try {
|
|
95
|
+
const content = await readFile(filePath, "utf-8");
|
|
96
|
+
const parsed = JSON.parse(content);
|
|
97
|
+
const result = PlanSpecSchema.safeParse(parsed);
|
|
98
|
+
if (result.success) {
|
|
99
|
+
specs.push(result.data);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Skip malformed files
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return specs;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=plan-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-state.js","sourceRoot":"","sources":["../../src/state/plan-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type SprintContract } from "../contracts/sprint-contract.js";
|
|
2
|
+
/**
|
|
3
|
+
* Save a sprint contract to disk.
|
|
4
|
+
* Overwrites any existing contract with the same id.
|
|
5
|
+
*/
|
|
6
|
+
export declare function saveContract(projectRoot: string, contract: SprintContract): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Load a sprint contract by id.
|
|
9
|
+
* Throws if not found or invalid.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadContract(projectRoot: string, id: string): Promise<SprintContract>;
|
|
12
|
+
/**
|
|
13
|
+
* List all saved contracts, sorted by filename.
|
|
14
|
+
*/
|
|
15
|
+
export declare function listContracts(projectRoot: string): Promise<SprintContract[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Update an existing contract (save with the same id).
|
|
18
|
+
*/
|
|
19
|
+
export declare function updateContract(projectRoot: string, contract: SprintContract): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=sprint-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sprint-state.d.ts","sourceRoot":"","sources":["../../src/state/sprint-state.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,iCAAiC,CAAC;AAezC;;;GAGG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,cAAc,CAAC,CAgCzB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,EAAE,CAAC,CAgC3B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAEf"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { readFile, writeFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { SprintContractSchema, } from "../contracts/sprint-contract.js";
|
|
4
|
+
import { ensureDir } from "./helpers.js";
|
|
5
|
+
const CONTRACTS_DIR = ".bober/contracts";
|
|
6
|
+
function contractsDir(projectRoot) {
|
|
7
|
+
return join(projectRoot, CONTRACTS_DIR);
|
|
8
|
+
}
|
|
9
|
+
function contractPath(projectRoot, id) {
|
|
10
|
+
// Sanitize the id to be a safe filename
|
|
11
|
+
const safeId = id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
12
|
+
return join(contractsDir(projectRoot), `${safeId}.json`);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Save a sprint contract to disk.
|
|
16
|
+
* Overwrites any existing contract with the same id.
|
|
17
|
+
*/
|
|
18
|
+
export async function saveContract(projectRoot, contract) {
|
|
19
|
+
await ensureDir(contractsDir(projectRoot));
|
|
20
|
+
const validation = SprintContractSchema.safeParse(contract);
|
|
21
|
+
if (!validation.success) {
|
|
22
|
+
const issues = validation.error.issues
|
|
23
|
+
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
|
24
|
+
.join("\n");
|
|
25
|
+
throw new Error(`Invalid contract:\n${issues}`);
|
|
26
|
+
}
|
|
27
|
+
const filePath = contractPath(projectRoot, contract.id);
|
|
28
|
+
await writeFile(filePath, JSON.stringify(contract, null, 2), "utf-8");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Load a sprint contract by id.
|
|
32
|
+
* Throws if not found or invalid.
|
|
33
|
+
*/
|
|
34
|
+
export async function loadContract(projectRoot, id) {
|
|
35
|
+
const filePath = contractPath(projectRoot, id);
|
|
36
|
+
let content;
|
|
37
|
+
try {
|
|
38
|
+
content = await readFile(filePath, "utf-8");
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
throw new Error(`Contract "${id}" not found: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
42
|
+
}
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(content);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
throw new Error(`Invalid JSON in contract file for "${id}": ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
49
|
+
}
|
|
50
|
+
const result = SprintContractSchema.safeParse(parsed);
|
|
51
|
+
if (!result.success) {
|
|
52
|
+
const issues = result.error.issues
|
|
53
|
+
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
|
54
|
+
.join("\n");
|
|
55
|
+
throw new Error(`Contract "${id}" failed validation:\n${issues}`);
|
|
56
|
+
}
|
|
57
|
+
return result.data;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* List all saved contracts, sorted by filename.
|
|
61
|
+
*/
|
|
62
|
+
export async function listContracts(projectRoot) {
|
|
63
|
+
const dir = contractsDir(projectRoot);
|
|
64
|
+
let entries;
|
|
65
|
+
try {
|
|
66
|
+
entries = await readdir(dir);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Directory doesn't exist yet — no contracts
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
const jsonFiles = entries
|
|
73
|
+
.filter((f) => f.endsWith(".json"))
|
|
74
|
+
.sort();
|
|
75
|
+
const contracts = [];
|
|
76
|
+
for (const file of jsonFiles) {
|
|
77
|
+
const filePath = join(dir, file);
|
|
78
|
+
try {
|
|
79
|
+
const content = await readFile(filePath, "utf-8");
|
|
80
|
+
const parsed = JSON.parse(content);
|
|
81
|
+
const result = SprintContractSchema.safeParse(parsed);
|
|
82
|
+
if (result.success) {
|
|
83
|
+
contracts.push(result.data);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Skip malformed files
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return contracts;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Update an existing contract (save with the same id).
|
|
94
|
+
*/
|
|
95
|
+
export async function updateContract(projectRoot, contract) {
|
|
96
|
+
await saveContract(projectRoot, contract);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=sprint-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sprint-state.js","sourceRoot":"","sources":["../../src/state/sprint-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async check whether a file exists and is readable.
|
|
3
|
+
*/
|
|
4
|
+
export declare function fileExists(path: string): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* Read a JSON file and parse it with an optional type parameter.
|
|
7
|
+
*
|
|
8
|
+
* Throws if the file does not exist or contains invalid JSON.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readJson<T = unknown>(path: string): Promise<T>;
|
|
11
|
+
/**
|
|
12
|
+
* Write data as pretty-printed JSON to the given path.
|
|
13
|
+
*
|
|
14
|
+
* Parent directories are created automatically.
|
|
15
|
+
*/
|
|
16
|
+
export declare function writeJson(path: string, data: unknown): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Create a directory (and any missing parents) if it does not already exist.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ensureDir(path: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Walk up the directory tree from `startDir` looking for a project root.
|
|
23
|
+
*
|
|
24
|
+
* A directory is considered a project root if it contains
|
|
25
|
+
* `bober.config.json` or `package.json`.
|
|
26
|
+
*
|
|
27
|
+
* @param startDir Starting directory (defaults to `process.cwd()`).
|
|
28
|
+
* @returns Absolute path to the project root, or `null` if none found.
|
|
29
|
+
*/
|
|
30
|
+
export declare function findProjectRoot(startDir?: string): Promise<string | null>;
|
|
31
|
+
//# sourceMappingURL=fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAGpE;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmBxB"}
|
package/dist/utils/fs.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { access, readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { join, dirname, resolve } from "node:path";
|
|
4
|
+
// ── File-System Helpers ────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Async check whether a file exists and is readable.
|
|
7
|
+
*/
|
|
8
|
+
export async function fileExists(path) {
|
|
9
|
+
try {
|
|
10
|
+
await access(path, constants.R_OK);
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Read a JSON file and parse it with an optional type parameter.
|
|
19
|
+
*
|
|
20
|
+
* Throws if the file does not exist or contains invalid JSON.
|
|
21
|
+
*/
|
|
22
|
+
export async function readJson(path) {
|
|
23
|
+
const raw = await readFile(path, "utf-8");
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Write data as pretty-printed JSON to the given path.
|
|
28
|
+
*
|
|
29
|
+
* Parent directories are created automatically.
|
|
30
|
+
*/
|
|
31
|
+
export async function writeJson(path, data) {
|
|
32
|
+
await ensureDir(dirname(path));
|
|
33
|
+
await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create a directory (and any missing parents) if it does not already exist.
|
|
37
|
+
*/
|
|
38
|
+
export async function ensureDir(path) {
|
|
39
|
+
await mkdir(path, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Walk up the directory tree from `startDir` looking for a project root.
|
|
43
|
+
*
|
|
44
|
+
* A directory is considered a project root if it contains
|
|
45
|
+
* `bober.config.json` or `package.json`.
|
|
46
|
+
*
|
|
47
|
+
* @param startDir Starting directory (defaults to `process.cwd()`).
|
|
48
|
+
* @returns Absolute path to the project root, or `null` if none found.
|
|
49
|
+
*/
|
|
50
|
+
export async function findProjectRoot(startDir) {
|
|
51
|
+
let dir = resolve(startDir ?? process.cwd());
|
|
52
|
+
const markers = ["bober.config.json", "package.json"];
|
|
53
|
+
for (;;) {
|
|
54
|
+
for (const marker of markers) {
|
|
55
|
+
if (await fileExists(join(dir, marker))) {
|
|
56
|
+
return dir;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const parent = dirname(dir);
|
|
60
|
+
if (parent === dir) {
|
|
61
|
+
// Reached filesystem root without finding a marker.
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
dir = parent;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,sEAAsE;AAEtE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAc,IAAY;IACtD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAY,EACZ,IAAa;IAEb,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACvE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAiB;IAEjB,IAAI,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAE7C,MAAM,OAAO,GAAG,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;IAEtD,SAAS,CAAC;QACR,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;gBACxC,OAAO,GAAG,CAAC;YACb,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACnB,oDAAoD;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the name of the currently checked-out branch.
|
|
3
|
+
*/
|
|
4
|
+
export declare function getCurrentBranch(cwd: string): Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Create a new branch and check it out.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createBranch(cwd: string, name: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Stage all changes and create a commit.
|
|
11
|
+
*
|
|
12
|
+
* @returns The short hash of the new commit.
|
|
13
|
+
*/
|
|
14
|
+
export declare function commitAll(cwd: string, message: string): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Get a list of files changed since a given ref (defaults to HEAD).
|
|
17
|
+
*
|
|
18
|
+
* Returns relative paths as reported by git.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getChangedFiles(cwd: string, since?: string): Promise<string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Get the unified diff since a given ref (defaults to HEAD).
|
|
23
|
+
*/
|
|
24
|
+
export declare function getDiff(cwd: string, since?: string): Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Check whether the working tree has uncommitted changes (staged or unstaged).
|
|
27
|
+
*/
|
|
28
|
+
export declare function hasUncommittedChanges(cwd: string): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Stash any current changes, run the provided function, then restore.
|
|
31
|
+
*
|
|
32
|
+
* If the stash is empty (nothing to save) the restore step is skipped.
|
|
33
|
+
*/
|
|
34
|
+
export declare function stashAndRestore<T>(cwd: string, fn: () => Promise<T>): Promise<T>;
|
|
35
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKnE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,EAAE,CAAC,CAWnB;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMzE;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAcZ"}
|