ralphctl 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +58 -24
  2. package/dist/add-HGJCLWED.mjs +14 -0
  3. package/dist/add-MRGCS3US.mjs +14 -0
  4. package/dist/chunk-6PYTKGB5.mjs +316 -0
  5. package/dist/chunk-7TG3EAQ2.mjs +20 -0
  6. package/dist/chunk-EKMZZRWI.mjs +521 -0
  7. package/dist/chunk-JON4GCLR.mjs +59 -0
  8. package/dist/chunk-LOR7QBXX.mjs +3683 -0
  9. package/dist/chunk-MNMQC36F.mjs +556 -0
  10. package/dist/chunk-MRKOFVTM.mjs +537 -0
  11. package/dist/chunk-NTWO2LXB.mjs +52 -0
  12. package/dist/chunk-QBXHAXHI.mjs +562 -0
  13. package/dist/chunk-WGHJI3OI.mjs +214 -0
  14. package/dist/cli.mjs +4245 -0
  15. package/dist/create-MG7E7PLQ.mjs +10 -0
  16. package/dist/handle-UG5M2OON.mjs +22 -0
  17. package/dist/multiline-OHSNFCRG.mjs +40 -0
  18. package/dist/project-NT3L4FTB.mjs +28 -0
  19. package/dist/resolver-WSFWKACM.mjs +153 -0
  20. package/dist/sprint-4VHDLGFN.mjs +37 -0
  21. package/dist/wizard-LRELAN2J.mjs +196 -0
  22. package/package.json +19 -28
  23. package/CHANGELOG.md +0 -94
  24. package/bin/ralphctl +0 -13
  25. package/src/ai/executor.ts +0 -973
  26. package/src/ai/lifecycle.ts +0 -45
  27. package/src/ai/parser.ts +0 -40
  28. package/src/ai/permissions.ts +0 -207
  29. package/src/ai/process-manager.ts +0 -248
  30. package/src/ai/prompts/index.ts +0 -89
  31. package/src/ai/rate-limiter.ts +0 -89
  32. package/src/ai/runner.ts +0 -478
  33. package/src/ai/session.ts +0 -319
  34. package/src/ai/task-context.ts +0 -270
  35. package/src/cli-metadata.ts +0 -7
  36. package/src/cli.ts +0 -65
  37. package/src/commands/completion/index.ts +0 -33
  38. package/src/commands/config/config.ts +0 -58
  39. package/src/commands/config/index.ts +0 -33
  40. package/src/commands/dashboard/dashboard.ts +0 -5
  41. package/src/commands/dashboard/index.ts +0 -6
  42. package/src/commands/doctor/doctor.ts +0 -271
  43. package/src/commands/doctor/index.ts +0 -25
  44. package/src/commands/progress/index.ts +0 -25
  45. package/src/commands/progress/log.ts +0 -64
  46. package/src/commands/progress/show.ts +0 -14
  47. package/src/commands/project/add.ts +0 -336
  48. package/src/commands/project/index.ts +0 -104
  49. package/src/commands/project/list.ts +0 -31
  50. package/src/commands/project/remove.ts +0 -43
  51. package/src/commands/project/repo.ts +0 -118
  52. package/src/commands/project/show.ts +0 -49
  53. package/src/commands/sprint/close.ts +0 -180
  54. package/src/commands/sprint/context.ts +0 -109
  55. package/src/commands/sprint/create.ts +0 -60
  56. package/src/commands/sprint/current.ts +0 -75
  57. package/src/commands/sprint/delete.ts +0 -72
  58. package/src/commands/sprint/health.ts +0 -229
  59. package/src/commands/sprint/ideate.ts +0 -496
  60. package/src/commands/sprint/index.ts +0 -226
  61. package/src/commands/sprint/list.ts +0 -86
  62. package/src/commands/sprint/plan-utils.ts +0 -207
  63. package/src/commands/sprint/plan.ts +0 -549
  64. package/src/commands/sprint/refine.ts +0 -359
  65. package/src/commands/sprint/requirements.ts +0 -58
  66. package/src/commands/sprint/show.ts +0 -140
  67. package/src/commands/sprint/start.ts +0 -119
  68. package/src/commands/sprint/switch.ts +0 -20
  69. package/src/commands/task/add.ts +0 -316
  70. package/src/commands/task/import.ts +0 -150
  71. package/src/commands/task/index.ts +0 -123
  72. package/src/commands/task/list.ts +0 -145
  73. package/src/commands/task/next.ts +0 -45
  74. package/src/commands/task/remove.ts +0 -47
  75. package/src/commands/task/reorder.ts +0 -45
  76. package/src/commands/task/show.ts +0 -111
  77. package/src/commands/task/status.ts +0 -99
  78. package/src/commands/ticket/add.ts +0 -265
  79. package/src/commands/ticket/edit.ts +0 -166
  80. package/src/commands/ticket/index.ts +0 -114
  81. package/src/commands/ticket/list.ts +0 -128
  82. package/src/commands/ticket/refine-utils.ts +0 -89
  83. package/src/commands/ticket/refine.ts +0 -268
  84. package/src/commands/ticket/remove.ts +0 -48
  85. package/src/commands/ticket/show.ts +0 -74
  86. package/src/completion/handle.ts +0 -30
  87. package/src/completion/resolver.ts +0 -241
  88. package/src/interactive/dashboard.ts +0 -268
  89. package/src/interactive/escapable.ts +0 -81
  90. package/src/interactive/file-browser.ts +0 -153
  91. package/src/interactive/index.ts +0 -429
  92. package/src/interactive/menu.ts +0 -403
  93. package/src/interactive/selectors.ts +0 -273
  94. package/src/interactive/wizard.ts +0 -221
  95. package/src/providers/claude.ts +0 -53
  96. package/src/providers/copilot.ts +0 -86
  97. package/src/providers/index.ts +0 -43
  98. package/src/providers/types.ts +0 -85
  99. package/src/schemas/index.ts +0 -130
  100. package/src/store/config.ts +0 -74
  101. package/src/store/progress.ts +0 -230
  102. package/src/store/project.ts +0 -276
  103. package/src/store/sprint.ts +0 -229
  104. package/src/store/task.ts +0 -443
  105. package/src/store/ticket.ts +0 -178
  106. package/src/theme/index.ts +0 -215
  107. package/src/theme/ui.ts +0 -872
  108. package/src/utils/detect-scripts.ts +0 -247
  109. package/src/utils/editor-input.ts +0 -41
  110. package/src/utils/editor.ts +0 -37
  111. package/src/utils/exit-codes.ts +0 -27
  112. package/src/utils/file-lock.ts +0 -135
  113. package/src/utils/git.ts +0 -185
  114. package/src/utils/ids.ts +0 -37
  115. package/src/utils/issue-fetch.ts +0 -244
  116. package/src/utils/json-extract.ts +0 -62
  117. package/src/utils/multiline.ts +0 -61
  118. package/src/utils/path-selector.ts +0 -236
  119. package/src/utils/paths.ts +0 -108
  120. package/src/utils/provider.ts +0 -34
  121. package/src/utils/requirements-export.ts +0 -63
  122. package/src/utils/storage.ts +0 -107
  123. package/tsconfig.json +0 -25
  124. /package/{src/ai → dist}/prompts/ideate-auto.md +0 -0
  125. /package/{src/ai → dist}/prompts/ideate.md +0 -0
  126. /package/{src/ai → dist}/prompts/plan-auto.md +0 -0
  127. /package/{src/ai → dist}/prompts/plan-common.md +0 -0
  128. /package/{src/ai → dist}/prompts/plan-interactive.md +0 -0
  129. /package/{src/ai → dist}/prompts/task-execution.md +0 -0
  130. /package/{src/ai → dist}/prompts/ticket-refine.md +0 -0
package/README.md CHANGED
@@ -1,9 +1,15 @@
1
1
  [![npm version](https://img.shields.io/npm/v/ralphctl?style=flat&logo=npm&logoColor=white&color=cb3837)](https://www.npmjs.com/package/ralphctl)
2
+ [![npm downloads](https://img.shields.io/npm/dm/ralphctl?style=flat&logo=npm&logoColor=white&color=cb3837)](https://www.npmjs.com/package/ralphctl)
2
3
  [![CI](https://github.com/lukas-grigis/ralphctl/actions/workflows/ci.yml/badge.svg)](https://github.com/lukas-grigis/ralphctl/actions/workflows/ci.yml)
3
4
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat&logo=opensourceinitiative&logoColor=white)](./LICENSE)
4
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178c6?style=flat&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
5
6
  [![Node.js](https://img.shields.io/badge/node-%E2%89%A5_24-5fa04e?style=flat&logo=nodedotjs&logoColor=white)](https://nodejs.org/)
7
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4?style=flat&logo=prettier&logoColor=white)](https://prettier.io/)
8
+ [![ESLint](https://img.shields.io/badge/ESLint-4b32c3?style=flat&logo=eslint&logoColor=white)](https://eslint.org/)
6
9
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat&logo=git&logoColor=white)](./CONTRIBUTING.md)
10
+ [![Claude Code](https://img.shields.io/badge/Claude_Code-191919?style=flat&logo=anthropic&logoColor=white)](https://docs.anthropic.com/en/docs/claude-code)
11
+ [![GitHub Copilot](https://img.shields.io/badge/GitHub_Copilot-000?style=flat&logo=githubcopilot&logoColor=white)](https://docs.github.com/en/copilot/github-copilot-in-the-cli)
12
+ [![Built with Donuts](https://img.shields.io/badge/%F0%9F%8D%A9-Built_with_Donuts-ff6f00?style=flat)](https://github.com/lukas-grigis/ralphctl)
7
13
 
8
14
  ```
9
15
  🍩 ██████╗ █████╗ ██╗ ██████╗ ██╗ ██╗ ██████╗████████╗██╗ 🍩
@@ -87,30 +93,47 @@ Or just run `ralphctl` with no arguments for an interactive menu that walks you
87
93
 
88
94
  ## CLI Overview
89
95
 
96
+ ### Getting Started
97
+
98
+ | Command | Description |
99
+ | ------------------------------------------------ | ----------------------------------- |
100
+ | `ralphctl` | Interactive menu mode (recommended) |
101
+ | `ralphctl doctor` | Check environment health |
102
+ | `ralphctl config set provider <claude\|copilot>` | Set AI provider |
103
+ | `ralphctl config show` | Show current configuration |
104
+ | `ralphctl completion install` | Enable shell tab-completion |
105
+
106
+ ### Project & Sprint Setup
107
+
108
+ | Command | Description |
109
+ | ------------------------ | -------------------------------- |
110
+ | `ralphctl project add` | Register a project and its repos |
111
+ | `ralphctl sprint create` | Create a new sprint (draft) |
112
+ | `ralphctl sprint list` | List all sprints |
113
+ | `ralphctl sprint show` | Show current sprint details |
114
+ | `ralphctl sprint switch` | Quick sprint switcher |
115
+ | `ralphctl ticket add` | Add a work item to a sprint |
116
+
117
+ ### AI-Assisted Planning
118
+
90
119
  | Command | Description |
91
120
  | ------------------------------ | --------------------------------------- |
92
- | `ralphctl` | Interactive menu mode |
93
- | `ralphctl doctor` | Check environment health and setup |
94
- | `ralphctl config show` | Show current configuration |
95
- | `ralphctl config set` | Set configuration values |
96
- | `ralphctl project add` | Register a project and its repos |
97
- | `ralphctl sprint create` | Create a new sprint |
98
- | `ralphctl sprint list` | List all sprints |
99
- | `ralphctl sprint show` | Show current sprint details |
100
- | `ralphctl sprint switch` | Quick sprint switcher |
101
- | `ralphctl ticket add` | Add a work item to a sprint |
102
- | `ralphctl sprint refine` | Refine requirements with AI |
103
- | `ralphctl sprint plan` | Generate tasks from requirements |
121
+ | `ralphctl sprint refine` | Clarify requirements with AI (WHAT) |
122
+ | `ralphctl sprint plan` | Generate tasks from requirements (HOW) |
104
123
  | `ralphctl sprint ideate` | Quick single-session refine + plan |
105
124
  | `ralphctl sprint requirements` | Export refined requirements to markdown |
106
- | `ralphctl sprint start` | Execute tasks with AI |
107
- | `ralphctl sprint health` | Diagnose blockers and stale tasks |
108
- | `ralphctl sprint close` | Close an active sprint |
109
- | `ralphctl sprint delete` | Delete a sprint permanently |
110
- | `ralphctl task list` | List tasks in the current sprint |
111
- | `ralphctl task next` | Show the next unblocked task |
112
- | `ralphctl dashboard` | Sprint overview with progress bar |
113
- | `ralphctl completion install` | Enable shell tab-completion |
125
+
126
+ ### Execution & Monitoring
127
+
128
+ | Command | Description |
129
+ | ------------------------ | --------------------------------- |
130
+ | `ralphctl sprint start` | Execute tasks with AI |
131
+ | `ralphctl sprint health` | Diagnose blockers and stale tasks |
132
+ | `ralphctl dashboard` | Sprint overview with progress bar |
133
+ | `ralphctl task list` | List tasks in the current sprint |
134
+ | `ralphctl task next` | Show the next unblocked task |
135
+ | `ralphctl sprint close` | Close an active sprint |
136
+ | `ralphctl sprint delete` | Delete a sprint permanently |
114
137
 
115
138
  Run `ralphctl <command> --help` for details on any command.
116
139
 
@@ -127,6 +150,18 @@ ralphctl config set provider copilot # Use GitHub Copilot
127
150
 
128
151
  Auto-prompts on first AI command if not set. Both CLIs must be in your PATH and authenticated.
129
152
 
153
+ ### Provider Differences
154
+
155
+ | Feature | Claude Code | GitHub Copilot |
156
+ | --------------------------- | ------------------------------------ | -------------------------------------------------------------------- |
157
+ | Status | GA | Public preview |
158
+ | Headless execution | `-p --output-format json` | `-p -s --autopilot --no-ask-user` |
159
+ | Session IDs | In JSON output (`session_id`) | Captured via `--share` output file |
160
+ | Session resume (`--resume`) | Full support | Supported when session ID is available |
161
+ | Per-tool permissions | Settings files + `--permission-mode` | `--allow-all-tools` (all-or-nothing by default) |
162
+ | Fine-grained tool control | `allow`/`deny` in settings files | `--allow-tool`, `--deny-tool` flags (not yet used) |
163
+ | Rate limit detection | Validated patterns | Borrowed from Claude — not yet validated against real Copilot errors |
164
+
130
165
  ---
131
166
 
132
167
  ## Documentation
@@ -158,14 +193,13 @@ export RALPHCTL_ROOT="/path/to/custom/data-dir"
158
193
  git clone https://github.com/lukas-grigis/ralphctl.git
159
194
  cd ralphctl
160
195
  pnpm install
161
- pnpm dev --help # Run CLI in dev mode
162
- pnpm build # Type check
196
+ pnpm dev --help # Run CLI in dev mode (tsx, no build needed)
197
+ pnpm build # Compile for npm distribution (tsup)
198
+ pnpm typecheck # Type check
163
199
  pnpm test # Run tests
164
200
  pnpm lint # Lint
165
201
  ```
166
202
 
167
- No compilation step — the CLI runs TypeScript source directly via [tsx](https://tsx.is/).
168
-
169
203
  ---
170
204
 
171
205
  ## Contributing
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ addSingleTicketInteractive,
4
+ ticketAddCommand
5
+ } from "./chunk-MNMQC36F.mjs";
6
+ import "./chunk-7TG3EAQ2.mjs";
7
+ import "./chunk-WGHJI3OI.mjs";
8
+ import "./chunk-EKMZZRWI.mjs";
9
+ import "./chunk-6PYTKGB5.mjs";
10
+ import "./chunk-QBXHAXHI.mjs";
11
+ export {
12
+ addSingleTicketInteractive,
13
+ ticketAddCommand
14
+ };
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ addCheckScriptToRepository,
4
+ projectAddCommand
5
+ } from "./chunk-MRKOFVTM.mjs";
6
+ import "./chunk-NTWO2LXB.mjs";
7
+ import "./chunk-7TG3EAQ2.mjs";
8
+ import "./chunk-WGHJI3OI.mjs";
9
+ import "./chunk-6PYTKGB5.mjs";
10
+ import "./chunk-QBXHAXHI.mjs";
11
+ export {
12
+ addCheckScriptToRepository,
13
+ projectAddCommand
14
+ };
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/paths.ts
4
+ import { fileURLToPath } from "url";
5
+ import { dirname, isAbsolute, join, resolve, sep } from "path";
6
+ import { existsSync } from "fs";
7
+ import { homedir } from "os";
8
+ import { lstat, realpath, stat } from "fs/promises";
9
+ var __filename = fileURLToPath(import.meta.url);
10
+ var __dirname = dirname(__filename);
11
+ function getRepoRoot() {
12
+ let dir = __dirname;
13
+ while (dir !== dirname(dir)) {
14
+ if (existsSync(join(dir, "package.json"))) {
15
+ return dir;
16
+ }
17
+ dir = dirname(dir);
18
+ }
19
+ return join(__dirname, "..", "..");
20
+ }
21
+ function getDataDir() {
22
+ return process.env["RALPHCTL_ROOT"] ?? join(homedir(), ".ralphctl");
23
+ }
24
+ function getConfigPath() {
25
+ return join(getDataDir(), "config.json");
26
+ }
27
+ function getProjectsFilePath() {
28
+ return join(getDataDir(), "projects.json");
29
+ }
30
+ function getSprintsDir() {
31
+ return join(getDataDir(), "sprints");
32
+ }
33
+ function getSprintDir(sprintId) {
34
+ const sprintsDir = getSprintsDir();
35
+ const resolved = resolve(sprintsDir, sprintId);
36
+ if (!resolved.startsWith(sprintsDir + sep) && resolved !== sprintsDir) {
37
+ throw new Error(`Path traversal detected in sprint ID: ${sprintId}`);
38
+ }
39
+ return resolved;
40
+ }
41
+ function getSprintFilePath(sprintId) {
42
+ return join(getSprintDir(sprintId), "sprint.json");
43
+ }
44
+ function getTasksFilePath(sprintId) {
45
+ return join(getSprintDir(sprintId), "tasks.json");
46
+ }
47
+ function getProgressFilePath(sprintId) {
48
+ return join(getSprintDir(sprintId), "progress.md");
49
+ }
50
+ function getRefinementDir(sprintId, ticketId) {
51
+ return join(getSprintDir(sprintId), "refinement", ticketId);
52
+ }
53
+ function getPlanningDir(sprintId) {
54
+ return join(getSprintDir(sprintId), "planning");
55
+ }
56
+ function getIdeateDir(sprintId, ticketId) {
57
+ return join(getSprintDir(sprintId), "ideation", ticketId);
58
+ }
59
+ function getSchemaPath(schemaName) {
60
+ return join(getRepoRoot(), "schemas", schemaName);
61
+ }
62
+ function assertSafeCwd(path) {
63
+ if (!path || path.includes("\0") || path.includes("\n") || path.includes("\r")) {
64
+ throw new Error("Unsafe path for cwd: contains null bytes or newlines");
65
+ }
66
+ if (!isAbsolute(path)) {
67
+ throw new Error(`Unsafe path for cwd: must be absolute, got: ${path}`);
68
+ }
69
+ }
70
+ function expandTilde(path) {
71
+ if (path === "~") return homedir();
72
+ if (path.startsWith("~/")) return homedir() + path.slice(1);
73
+ return path;
74
+ }
75
+ async function validateProjectPath(path) {
76
+ try {
77
+ const resolved = resolve(expandTilde(path));
78
+ const lstats = await lstat(resolved);
79
+ if (lstats.isSymbolicLink()) {
80
+ const realPath = await realpath(resolved);
81
+ const realStats = await stat(realPath);
82
+ if (!realStats.isDirectory()) {
83
+ return "Symlink target is not a directory";
84
+ }
85
+ return true;
86
+ }
87
+ if (!lstats.isDirectory()) {
88
+ return "Path is not a directory";
89
+ }
90
+ return true;
91
+ } catch {
92
+ return "Directory does not exist";
93
+ }
94
+ }
95
+
96
+ // src/utils/storage.ts
97
+ import { access, appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
98
+ import { dirname as dirname2 } from "path";
99
+ var ValidationError = class extends Error {
100
+ path;
101
+ constructor(message, path, cause) {
102
+ super(message, { cause });
103
+ this.name = "ValidationError";
104
+ this.path = path;
105
+ }
106
+ };
107
+ var FileNotFoundError = class extends Error {
108
+ path;
109
+ constructor(message, path) {
110
+ super(message);
111
+ this.name = "FileNotFoundError";
112
+ this.path = path;
113
+ }
114
+ };
115
+ async function ensureDir(dirPath) {
116
+ await mkdir(dirPath, { recursive: true });
117
+ }
118
+ async function removeDir(dirPath) {
119
+ await rm(dirPath, { recursive: true, force: true });
120
+ }
121
+ async function fileExists(filePath) {
122
+ try {
123
+ await access(filePath);
124
+ return true;
125
+ } catch {
126
+ return false;
127
+ }
128
+ }
129
+ async function listDirs(dirPath) {
130
+ try {
131
+ const entries = await readdir(dirPath, { withFileTypes: true });
132
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
133
+ } catch {
134
+ return [];
135
+ }
136
+ }
137
+ async function readValidatedJson(filePath, schema) {
138
+ let content;
139
+ try {
140
+ content = await readFile(filePath, "utf-8");
141
+ } catch (err) {
142
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
143
+ throw new FileNotFoundError(`File not found: ${filePath}`, filePath);
144
+ }
145
+ throw err;
146
+ }
147
+ let data;
148
+ try {
149
+ data = JSON.parse(content);
150
+ } catch (err) {
151
+ throw new ValidationError(`Invalid JSON in ${filePath}`, filePath, err);
152
+ }
153
+ const result = schema.safeParse(data);
154
+ if (!result.success) {
155
+ const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
156
+ throw new ValidationError(`Validation failed for ${filePath}:
157
+ ${issues}`, filePath, result.error);
158
+ }
159
+ return result.data;
160
+ }
161
+ async function writeValidatedJson(filePath, data, schema) {
162
+ const result = schema.safeParse(data);
163
+ if (!result.success) {
164
+ const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
165
+ throw new ValidationError(`Validation failed before writing to ${filePath}:
166
+ ${issues}`, filePath, result.error);
167
+ }
168
+ await ensureDir(dirname2(filePath));
169
+ await writeFile(filePath, JSON.stringify(result.data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
170
+ }
171
+ async function appendToFile(filePath, content) {
172
+ await ensureDir(dirname2(filePath));
173
+ await appendFile(filePath, content, { encoding: "utf-8", mode: 384 });
174
+ }
175
+ async function readTextFile(filePath) {
176
+ try {
177
+ return await readFile(filePath, "utf-8");
178
+ } catch (err) {
179
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
180
+ throw new FileNotFoundError(`File not found: ${filePath}`, filePath);
181
+ }
182
+ throw err;
183
+ }
184
+ }
185
+
186
+ // src/schemas/index.ts
187
+ import { z } from "zod";
188
+ var SprintStatusSchema = z.enum(["draft", "active", "closed"]);
189
+ var TaskStatusSchema = z.enum(["todo", "in_progress", "done"]);
190
+ var RequirementStatusSchema = z.enum(["pending", "approved"]);
191
+ var RepositorySchema = z.object({
192
+ name: z.string().min(1),
193
+ // Auto-derived from basename(path)
194
+ path: z.string().min(1),
195
+ // Absolute path
196
+ checkScript: z.string().optional()
197
+ // e.g., "pnpm install && pnpm typecheck && pnpm lint && pnpm test"
198
+ });
199
+ var ProjectSchema = z.object({
200
+ name: z.string().min(1).regex(/^[a-z0-9-]+$/, "Project name must be a slug (lowercase, numbers, hyphens only)"),
201
+ displayName: z.string().min(1),
202
+ repositories: z.array(RepositorySchema).min(1),
203
+ description: z.string().optional()
204
+ });
205
+ var ProjectsSchema = z.array(ProjectSchema);
206
+ var TicketSchema = z.object({
207
+ id: z.string().min(1),
208
+ // Internal UUID8 (auto-generated)
209
+ title: z.string().min(1),
210
+ description: z.string().optional(),
211
+ link: z.url().optional(),
212
+ projectName: z.string().min(1),
213
+ // References Project.name
214
+ affectedRepositories: z.array(z.string()).optional(),
215
+ // Repository paths selected during planning
216
+ requirementStatus: RequirementStatusSchema.default("pending"),
217
+ requirements: z.string().optional()
218
+ // Refined requirements (set during sprint refine)
219
+ });
220
+ var TaskSchema = z.object({
221
+ id: z.string().min(1),
222
+ // UUID8
223
+ name: z.string().min(1),
224
+ description: z.string().optional(),
225
+ steps: z.array(z.string()).default([]),
226
+ status: TaskStatusSchema.default("todo"),
227
+ order: z.number().int().positive(),
228
+ ticketId: z.string().optional(),
229
+ // References Ticket.id (internal)
230
+ blockedBy: z.array(z.string()).default([]),
231
+ projectPath: z.string().min(1),
232
+ // Single path for execution
233
+ verified: z.boolean().default(false),
234
+ // Whether verification passed
235
+ verificationOutput: z.string().optional()
236
+ // Output from verification run
237
+ });
238
+ var TasksSchema = z.array(TaskSchema);
239
+ var ImportTaskSchema = z.object({
240
+ id: z.string().optional(),
241
+ // Local ID for referencing in blockedBy
242
+ name: z.string().min(1),
243
+ // Required
244
+ description: z.string().optional(),
245
+ steps: z.array(z.string()).optional(),
246
+ ticketId: z.string().optional(),
247
+ blockedBy: z.array(z.string()).optional(),
248
+ projectPath: z.string().min(1)
249
+ // Required - execution directory
250
+ });
251
+ var ImportTasksSchema = z.array(ImportTaskSchema);
252
+ var RefinedRequirementSchema = z.object({
253
+ ref: z.string().min(1),
254
+ requirements: z.string().min(1)
255
+ });
256
+ var RefinedRequirementsSchema = z.array(RefinedRequirementSchema);
257
+ var IdeateOutputSchema = z.object({
258
+ requirements: z.string().min(1),
259
+ tasks: ImportTasksSchema
260
+ });
261
+ var SprintSchema = z.object({
262
+ id: z.string().regex(/^\d{8}-\d{6}-[a-z0-9-]+$/, "Invalid sprint ID format"),
263
+ name: z.string().min(1),
264
+ status: SprintStatusSchema.default("draft"),
265
+ createdAt: z.iso.datetime(),
266
+ activatedAt: z.iso.datetime().nullable().default(null),
267
+ closedAt: z.iso.datetime().nullable().default(null),
268
+ tickets: z.array(TicketSchema).default([]),
269
+ checkRanAt: z.record(z.string(), z.iso.datetime()).default({}),
270
+ branch: z.string().nullable().default(null)
271
+ });
272
+ var AiProviderSchema = z.enum(["claude", "copilot"]);
273
+ var ConfigSchema = z.object({
274
+ currentSprint: z.string().nullable().default(null),
275
+ aiProvider: AiProviderSchema.nullable().default(null),
276
+ editor: z.string().nullable().default(null)
277
+ });
278
+
279
+ export {
280
+ getDataDir,
281
+ getConfigPath,
282
+ getProjectsFilePath,
283
+ getSprintsDir,
284
+ getSprintDir,
285
+ getSprintFilePath,
286
+ getTasksFilePath,
287
+ getProgressFilePath,
288
+ getRefinementDir,
289
+ getPlanningDir,
290
+ getIdeateDir,
291
+ getSchemaPath,
292
+ assertSafeCwd,
293
+ expandTilde,
294
+ validateProjectPath,
295
+ ValidationError,
296
+ FileNotFoundError,
297
+ ensureDir,
298
+ removeDir,
299
+ fileExists,
300
+ listDirs,
301
+ readValidatedJson,
302
+ writeValidatedJson,
303
+ appendToFile,
304
+ readTextFile,
305
+ SprintStatusSchema,
306
+ TaskStatusSchema,
307
+ RequirementStatusSchema,
308
+ ProjectsSchema,
309
+ TasksSchema,
310
+ ImportTasksSchema,
311
+ RefinedRequirementsSchema,
312
+ IdeateOutputSchema,
313
+ SprintSchema,
314
+ AiProviderSchema,
315
+ ConfigSchema
316
+ };
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/exit-codes.ts
4
+ var EXIT_SUCCESS = 0;
5
+ var EXIT_ERROR = 1;
6
+ var EXIT_NO_TASKS = 2;
7
+ var EXIT_ALL_BLOCKED = 3;
8
+ var EXIT_INTERRUPTED = 130;
9
+ function exitWithCode(code) {
10
+ process.exit(code);
11
+ }
12
+
13
+ export {
14
+ EXIT_SUCCESS,
15
+ EXIT_ERROR,
16
+ EXIT_NO_TASKS,
17
+ EXIT_ALL_BLOCKED,
18
+ EXIT_INTERRUPTED,
19
+ exitWithCode
20
+ };