ralphctl 0.4.6 → 0.6.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/README.md +29 -16
- package/dist/absolute-path-WUTZQ37D.mjs +8 -0
- package/dist/chunk-6RDMCLWU.mjs +108 -0
- package/dist/chunk-HIU74KTO.mjs +1046 -0
- package/dist/chunk-S3PTDH57.mjs +78 -0
- package/dist/chunk-WV4D2CPG.mjs +26 -0
- package/dist/cli.mjs +22413 -717
- package/dist/manifest.json +24 -0
- package/dist/prompt-adapter-JQICGVX7.mjs +7 -0
- package/dist/prompts/ideate.md +3 -1
- package/dist/prompts/plan-auto.md +23 -8
- package/dist/prompts/plan-common-examples.md +3 -3
- package/dist/prompts/plan-common.md +6 -5
- package/dist/prompts/plan-interactive.md +30 -7
- package/dist/prompts/repo-onboard.md +154 -64
- package/dist/prompts/signals-task.md +3 -0
- package/dist/prompts/sprint-feedback.md +3 -0
- package/dist/prompts/task-evaluation.md +74 -53
- package/dist/prompts/task-execution.md +65 -21
- package/dist/prompts/ticket-refine.md +11 -8
- package/dist/prompts/validation-checklist.md +3 -2
- package/dist/skills/default/abstraction-first/SKILL.md +45 -0
- package/dist/skills/default/alignment/SKILL.md +46 -0
- package/dist/skills/default/iterative-review/SKILL.md +48 -0
- package/dist/skills/exec/.gitkeep +0 -0
- package/dist/skills/plan/.gitkeep +0 -0
- package/dist/skills/refine/.gitkeep +0 -0
- package/dist/storage-paths-IPNZZM5D.mjs +15 -0
- package/dist/validation-error-QT6Q7FYU.mjs +7 -0
- package/package.json +9 -4
- package/dist/add-DVPVHENV.mjs +0 -18
- package/dist/add-YVXM34RP.mjs +0 -17
- package/dist/bootstrap-FMHG6DRY.mjs +0 -11
- package/dist/chunk-747KW2RW.mjs +0 -24
- package/dist/chunk-B3RCOHW3.mjs +0 -5519
- package/dist/chunk-BSB4EDGR.mjs +0 -260
- package/dist/chunk-CBMFRQ4Y.mjs +0 -441
- package/dist/chunk-CFUVE2BP.mjs +0 -16
- package/dist/chunk-FNAAA32W.mjs +0 -103
- package/dist/chunk-GQ2WFKBN.mjs +0 -269
- package/dist/chunk-IWXBJD2D.mjs +0 -27
- package/dist/chunk-O566EEDL.mjs +0 -5542
- package/dist/chunk-OGEXYSFS.mjs +0 -228
- package/dist/chunk-PYZEQ2VK.mjs +0 -787
- package/dist/chunk-VAZ3LJBI.mjs +0 -179
- package/dist/chunk-WDMLPXOD.mjs +0 -363
- package/dist/chunk-XN2UIHBY.mjs +0 -589
- package/dist/chunk-ZLWSPLWI.mjs +0 -1117
- package/dist/create-Z635FQKO.mjs +0 -15
- package/dist/handle-23EFF3BE.mjs +0 -22
- package/dist/mount-B3MLHNVY.mjs +0 -7434
- package/dist/project-DQHF4ISP.mjs +0 -34
- package/dist/prompts/check-script-discover.md +0 -69
- package/dist/prompts/ideate-auto.md +0 -195
- package/dist/prompts/task-evaluation-resume.md +0 -41
- package/dist/resolver-OVPYVW6Q.mjs +0 -163
- package/dist/sprint-4E26AB5F.mjs +0 -38
- package/dist/start-FP7MVN5P.mjs +0 -19
package/dist/chunk-VAZ3LJBI.mjs
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/domain/errors.ts
|
|
4
|
-
var DomainError = class extends Error {
|
|
5
|
-
cause;
|
|
6
|
-
constructor(message, cause) {
|
|
7
|
-
super(message, cause ? { cause } : void 0);
|
|
8
|
-
this.name = this.constructor.name;
|
|
9
|
-
this.cause = cause;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
var IOError = class extends DomainError {
|
|
13
|
-
code = "IO_ERROR";
|
|
14
|
-
};
|
|
15
|
-
var StorageError = class extends DomainError {
|
|
16
|
-
code = "STORAGE_ERROR";
|
|
17
|
-
};
|
|
18
|
-
var LockError = class extends DomainError {
|
|
19
|
-
code = "LOCK_ERROR";
|
|
20
|
-
lockPath;
|
|
21
|
-
constructor(message, lockPath, cause) {
|
|
22
|
-
super(message, cause);
|
|
23
|
-
this.lockPath = lockPath;
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
var ParseError = class extends DomainError {
|
|
27
|
-
code = "PARSE_ERROR";
|
|
28
|
-
};
|
|
29
|
-
var ValidationError = class extends DomainError {
|
|
30
|
-
code = "VALIDATION_ERROR";
|
|
31
|
-
path;
|
|
32
|
-
constructor(message, path, cause) {
|
|
33
|
-
super(message, cause);
|
|
34
|
-
this.path = path;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
function detectSpawnRateLimit(stderr) {
|
|
38
|
-
const patterns = [/rate.?limit/i, /\b429\b/, /too many requests/i, /overloaded/i, /\b529\b/];
|
|
39
|
-
const isRateLimited = patterns.some((p) => p.test(stderr));
|
|
40
|
-
if (!isRateLimited) return { rateLimited: false, retryAfterMs: null };
|
|
41
|
-
const retryMatch = /retry.?after:?\s*(\d+)/i.exec(stderr);
|
|
42
|
-
const retryAfterMs = retryMatch?.[1] ? parseInt(retryMatch[1], 10) * 1e3 : null;
|
|
43
|
-
return { rateLimited: true, retryAfterMs };
|
|
44
|
-
}
|
|
45
|
-
var SpawnError = class extends DomainError {
|
|
46
|
-
code = "SPAWN_ERROR";
|
|
47
|
-
stderr;
|
|
48
|
-
exitCode;
|
|
49
|
-
rateLimited;
|
|
50
|
-
retryAfterMs;
|
|
51
|
-
sessionId;
|
|
52
|
-
constructor(message, stderr, exitCode, sessionId, cause) {
|
|
53
|
-
super(message, cause);
|
|
54
|
-
this.stderr = stderr;
|
|
55
|
-
this.exitCode = exitCode;
|
|
56
|
-
this.sessionId = sessionId ?? null;
|
|
57
|
-
const rl = detectSpawnRateLimit(stderr);
|
|
58
|
-
this.rateLimited = rl.rateLimited;
|
|
59
|
-
this.retryAfterMs = rl.retryAfterMs;
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
var SprintNotFoundError = class extends DomainError {
|
|
63
|
-
code = "SPRINT_NOT_FOUND";
|
|
64
|
-
sprintId;
|
|
65
|
-
constructor(sprintId) {
|
|
66
|
-
super(`Sprint not found: ${sprintId}`);
|
|
67
|
-
this.sprintId = sprintId;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
var TaskNotFoundError = class extends DomainError {
|
|
71
|
-
code = "TASK_NOT_FOUND";
|
|
72
|
-
taskId;
|
|
73
|
-
constructor(taskId) {
|
|
74
|
-
super(`Task not found: ${taskId}`);
|
|
75
|
-
this.taskId = taskId;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
var TicketNotFoundError = class extends DomainError {
|
|
79
|
-
code = "TICKET_NOT_FOUND";
|
|
80
|
-
ticketId;
|
|
81
|
-
constructor(ticketId) {
|
|
82
|
-
super(`Ticket not found: ${ticketId}`);
|
|
83
|
-
this.ticketId = ticketId;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
var ProjectNotFoundError = class extends DomainError {
|
|
87
|
-
code = "PROJECT_NOT_FOUND";
|
|
88
|
-
projectName;
|
|
89
|
-
constructor(projectName) {
|
|
90
|
-
super(`Project not found: ${projectName}`);
|
|
91
|
-
this.projectName = projectName;
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
var ProjectExistsError = class extends DomainError {
|
|
95
|
-
code = "PROJECT_EXISTS";
|
|
96
|
-
projectName;
|
|
97
|
-
constructor(projectName) {
|
|
98
|
-
super(`Project already exists: ${projectName}`);
|
|
99
|
-
this.projectName = projectName;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
var StatusError = class extends DomainError {
|
|
103
|
-
code = "STATUS_ERROR";
|
|
104
|
-
};
|
|
105
|
-
var SprintStatusError = class extends StatusError {
|
|
106
|
-
currentStatus;
|
|
107
|
-
operation;
|
|
108
|
-
constructor(message, currentStatus, operation) {
|
|
109
|
-
super(message);
|
|
110
|
-
this.currentStatus = currentStatus;
|
|
111
|
-
this.operation = operation;
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
var NoCurrentSprintError = class extends StatusError {
|
|
115
|
-
constructor() {
|
|
116
|
-
super("No sprint specified and no current sprint set.");
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
var DependencyCycleError = class extends DomainError {
|
|
120
|
-
code = "DEPENDENCY_CYCLE";
|
|
121
|
-
cycle;
|
|
122
|
-
constructor(cycle) {
|
|
123
|
-
super(`Dependency cycle detected: ${cycle.join(" \u2192 ")}`);
|
|
124
|
-
this.cycle = cycle;
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
var IssueFetchError = class extends DomainError {
|
|
128
|
-
code = "ISSUE_FETCH_ERROR";
|
|
129
|
-
};
|
|
130
|
-
var StepError = class extends DomainError {
|
|
131
|
-
code = "STEP_ERROR";
|
|
132
|
-
stepName;
|
|
133
|
-
constructor(message, stepName, cause) {
|
|
134
|
-
super(message, cause);
|
|
135
|
-
this.stepName = stepName;
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
var BranchPreflightError = class extends DomainError {
|
|
139
|
-
code = "BRANCH_PREFLIGHT_ERROR";
|
|
140
|
-
projectPath;
|
|
141
|
-
expectedBranch;
|
|
142
|
-
constructor(projectPath, expectedBranch, cause) {
|
|
143
|
-
super(`Branch verification failed: expected '${expectedBranch}' in ${projectPath}`, cause);
|
|
144
|
-
this.projectPath = projectPath;
|
|
145
|
-
this.expectedBranch = expectedBranch;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
var ExecutionAlreadyRunningError = class extends DomainError {
|
|
149
|
-
code = "EXECUTION_ALREADY_RUNNING";
|
|
150
|
-
projectName;
|
|
151
|
-
existingExecutionId;
|
|
152
|
-
constructor(projectName, existingExecutionId) {
|
|
153
|
-
super(`An execution is already running for project '${projectName}' (id: ${existingExecutionId}).`);
|
|
154
|
-
this.projectName = projectName;
|
|
155
|
-
this.existingExecutionId = existingExecutionId;
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
export {
|
|
160
|
-
DomainError,
|
|
161
|
-
IOError,
|
|
162
|
-
StorageError,
|
|
163
|
-
LockError,
|
|
164
|
-
ParseError,
|
|
165
|
-
ValidationError,
|
|
166
|
-
SpawnError,
|
|
167
|
-
SprintNotFoundError,
|
|
168
|
-
TaskNotFoundError,
|
|
169
|
-
TicketNotFoundError,
|
|
170
|
-
ProjectNotFoundError,
|
|
171
|
-
ProjectExistsError,
|
|
172
|
-
SprintStatusError,
|
|
173
|
-
NoCurrentSprintError,
|
|
174
|
-
DependencyCycleError,
|
|
175
|
-
IssueFetchError,
|
|
176
|
-
StepError,
|
|
177
|
-
BranchPreflightError,
|
|
178
|
-
ExecutionAlreadyRunningError
|
|
179
|
-
};
|
package/dist/chunk-WDMLPXOD.mjs
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
IOError,
|
|
4
|
-
StorageError,
|
|
5
|
-
ValidationError
|
|
6
|
-
} from "./chunk-VAZ3LJBI.mjs";
|
|
7
|
-
|
|
8
|
-
// src/integration/persistence/paths.ts
|
|
9
|
-
import { isAbsolute, join, resolve, sep } from "path";
|
|
10
|
-
import { homedir } from "os";
|
|
11
|
-
import { lstat, realpath, stat } from "fs/promises";
|
|
12
|
-
import { Result } from "typescript-result";
|
|
13
|
-
function getDataDir() {
|
|
14
|
-
return process.env["RALPHCTL_ROOT"] ?? join(homedir(), ".ralphctl");
|
|
15
|
-
}
|
|
16
|
-
function getConfigPath() {
|
|
17
|
-
return join(getDataDir(), "config.json");
|
|
18
|
-
}
|
|
19
|
-
function getProjectsFilePath() {
|
|
20
|
-
return join(getDataDir(), "projects.json");
|
|
21
|
-
}
|
|
22
|
-
function getSprintsDir() {
|
|
23
|
-
return join(getDataDir(), "sprints");
|
|
24
|
-
}
|
|
25
|
-
function getSprintDir(sprintId) {
|
|
26
|
-
const sprintsDir = getSprintsDir();
|
|
27
|
-
const resolved = resolve(sprintsDir, sprintId);
|
|
28
|
-
if (!resolved.startsWith(sprintsDir + sep) && resolved !== sprintsDir) {
|
|
29
|
-
throw new Error(`Path traversal detected in sprint ID: ${sprintId}`);
|
|
30
|
-
}
|
|
31
|
-
return resolved;
|
|
32
|
-
}
|
|
33
|
-
function getSprintFilePath(sprintId) {
|
|
34
|
-
return join(getSprintDir(sprintId), "sprint.json");
|
|
35
|
-
}
|
|
36
|
-
function getTasksFilePath(sprintId) {
|
|
37
|
-
return join(getSprintDir(sprintId), "tasks.json");
|
|
38
|
-
}
|
|
39
|
-
function getProgressFilePath(sprintId) {
|
|
40
|
-
return join(getSprintDir(sprintId), "progress.md");
|
|
41
|
-
}
|
|
42
|
-
function getEvaluationsDir(sprintId) {
|
|
43
|
-
return join(getSprintDir(sprintId), "evaluations");
|
|
44
|
-
}
|
|
45
|
-
function getEvaluationFilePath(sprintId, taskId) {
|
|
46
|
-
assertSafeSegment(taskId, "task ID");
|
|
47
|
-
return join(getEvaluationsDir(sprintId), `${taskId}.md`);
|
|
48
|
-
}
|
|
49
|
-
function assertSafeSegment(segment, label) {
|
|
50
|
-
if (!segment || segment.includes("/") || segment.includes("\\") || segment.includes("..") || segment.includes("\0")) {
|
|
51
|
-
throw new Error(`Path traversal detected in ${label}: ${segment}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function getRefinementDir(sprintId, ticketId) {
|
|
55
|
-
assertSafeSegment(ticketId, "ticket ID");
|
|
56
|
-
return join(getSprintDir(sprintId), "refinement", ticketId);
|
|
57
|
-
}
|
|
58
|
-
function getPlanningDir(sprintId) {
|
|
59
|
-
return join(getSprintDir(sprintId), "planning");
|
|
60
|
-
}
|
|
61
|
-
function getIdeationDir(sprintId, ticketId) {
|
|
62
|
-
assertSafeSegment(ticketId, "ticket ID");
|
|
63
|
-
return join(getSprintDir(sprintId), "ideation", ticketId);
|
|
64
|
-
}
|
|
65
|
-
function assertSafeCwd(path) {
|
|
66
|
-
if (!path || path.includes("\0") || path.includes("\n") || path.includes("\r")) {
|
|
67
|
-
throw new Error("Unsafe path for cwd: contains null bytes or newlines");
|
|
68
|
-
}
|
|
69
|
-
if (!isAbsolute(path)) {
|
|
70
|
-
throw new Error(`Unsafe path for cwd: must be absolute, got: ${path}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
function expandTilde(path) {
|
|
74
|
-
if (path === "~") return homedir();
|
|
75
|
-
if (path.startsWith("~/")) return homedir() + path.slice(1);
|
|
76
|
-
return path;
|
|
77
|
-
}
|
|
78
|
-
async function validateProjectPath(path) {
|
|
79
|
-
try {
|
|
80
|
-
const resolved = resolve(expandTilde(path));
|
|
81
|
-
const lstats = await lstat(resolved);
|
|
82
|
-
if (lstats.isSymbolicLink()) {
|
|
83
|
-
const realPath = await realpath(resolved);
|
|
84
|
-
const realStats = await stat(realPath);
|
|
85
|
-
if (!realStats.isDirectory()) {
|
|
86
|
-
return Result.error(new IOError("Symlink target is not a directory"));
|
|
87
|
-
}
|
|
88
|
-
return Result.ok(true);
|
|
89
|
-
}
|
|
90
|
-
if (!lstats.isDirectory()) {
|
|
91
|
-
return Result.error(new IOError("Path is not a directory"));
|
|
92
|
-
}
|
|
93
|
-
return Result.ok(true);
|
|
94
|
-
} catch (err) {
|
|
95
|
-
const code = err.code;
|
|
96
|
-
const message = code === "EACCES" ? "Permission denied" : "Directory does not exist";
|
|
97
|
-
return Result.error(new IOError(message, err instanceof Error ? err : void 0));
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// src/integration/persistence/storage.ts
|
|
102
|
-
import { access, appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
|
|
103
|
-
import { dirname } from "path";
|
|
104
|
-
import { Result as Result2 } from "typescript-result";
|
|
105
|
-
async function ensureDir(dirPath) {
|
|
106
|
-
await mkdir(dirPath, { recursive: true });
|
|
107
|
-
}
|
|
108
|
-
async function removeDir(dirPath) {
|
|
109
|
-
await rm(dirPath, { recursive: true, force: true });
|
|
110
|
-
}
|
|
111
|
-
async function fileExists(filePath) {
|
|
112
|
-
try {
|
|
113
|
-
await access(filePath);
|
|
114
|
-
return true;
|
|
115
|
-
} catch {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
async function listDirs(dirPath) {
|
|
120
|
-
try {
|
|
121
|
-
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
122
|
-
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
123
|
-
} catch {
|
|
124
|
-
return [];
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
async function readValidatedJson(filePath, schema) {
|
|
128
|
-
let content;
|
|
129
|
-
try {
|
|
130
|
-
content = await readFile(filePath, "utf-8");
|
|
131
|
-
} catch (err) {
|
|
132
|
-
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
133
|
-
return Result2.error(new StorageError(`File not found: ${filePath}`, err instanceof Error ? err : void 0));
|
|
134
|
-
}
|
|
135
|
-
return Result2.error(new StorageError(`Failed to read ${filePath}`, err instanceof Error ? err : void 0));
|
|
136
|
-
}
|
|
137
|
-
let data;
|
|
138
|
-
try {
|
|
139
|
-
data = JSON.parse(content);
|
|
140
|
-
} catch (err) {
|
|
141
|
-
return Result2.error(
|
|
142
|
-
new ValidationError(`Invalid JSON in ${filePath}`, filePath, err instanceof Error ? err : void 0)
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
const result = schema.safeParse(data);
|
|
146
|
-
if (!result.success) {
|
|
147
|
-
const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
148
|
-
return Result2.error(new ValidationError(`Validation failed for ${filePath}:
|
|
149
|
-
${issues}`, filePath, result.error));
|
|
150
|
-
}
|
|
151
|
-
return Result2.ok(result.data);
|
|
152
|
-
}
|
|
153
|
-
async function writeValidatedJson(filePath, data, schema) {
|
|
154
|
-
const result = schema.safeParse(data);
|
|
155
|
-
if (!result.success) {
|
|
156
|
-
const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
157
|
-
return Result2.error(
|
|
158
|
-
new ValidationError(`Validation failed before writing to ${filePath}:
|
|
159
|
-
${issues}`, filePath, result.error)
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
try {
|
|
163
|
-
await ensureDir(dirname(filePath));
|
|
164
|
-
await writeFile(filePath, JSON.stringify(result.data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
165
|
-
} catch (err) {
|
|
166
|
-
return Result2.error(new StorageError(`Failed to write ${filePath}`, err instanceof Error ? err : void 0));
|
|
167
|
-
}
|
|
168
|
-
return Result2.ok(void 0);
|
|
169
|
-
}
|
|
170
|
-
async function appendToFile(filePath, content) {
|
|
171
|
-
try {
|
|
172
|
-
await ensureDir(dirname(filePath));
|
|
173
|
-
await appendFile(filePath, content, { encoding: "utf-8", mode: 384 });
|
|
174
|
-
return Result2.ok(void 0);
|
|
175
|
-
} catch (err) {
|
|
176
|
-
return Result2.error(new StorageError(`Failed to append to ${filePath}`, err instanceof Error ? err : void 0));
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
async function readTextFile(filePath) {
|
|
180
|
-
try {
|
|
181
|
-
const content = await readFile(filePath, "utf-8");
|
|
182
|
-
return Result2.ok(content);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
185
|
-
return Result2.error(new StorageError(`File not found: ${filePath}`, err instanceof Error ? err : void 0));
|
|
186
|
-
}
|
|
187
|
-
return Result2.error(new StorageError(`Failed to read ${filePath}`, err instanceof Error ? err : void 0));
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// src/domain/models.ts
|
|
192
|
-
import { z } from "zod";
|
|
193
|
-
var SprintStatusSchema = z.enum(["draft", "active", "closed"]);
|
|
194
|
-
var TaskStatusSchema = z.enum(["todo", "in_progress", "done", "cancelled"]);
|
|
195
|
-
var RequirementStatusSchema = z.enum(["pending", "approved"]);
|
|
196
|
-
var EvaluationStatusSchema = z.enum(["passed", "failed", "malformed", "plateau"]);
|
|
197
|
-
var IdSchema = z.string().min(1);
|
|
198
|
-
var CURRENT_ONBOARDING_VERSION = 1;
|
|
199
|
-
var RepositorySchema = z.object({
|
|
200
|
-
id: IdSchema,
|
|
201
|
-
// UUID8, stable across renames
|
|
202
|
-
name: z.string().min(1),
|
|
203
|
-
// Auto-derived from basename(path)
|
|
204
|
-
path: z.string().min(1),
|
|
205
|
-
// Absolute path
|
|
206
|
-
checkScript: z.string().optional(),
|
|
207
|
-
// e.g., "pnpm install && pnpm typecheck && pnpm lint && pnpm test"
|
|
208
|
-
checkTimeout: z.number().positive().optional(),
|
|
209
|
-
// Per-repo timeout in ms (overrides RALPHCTL_SETUP_TIMEOUT_MS)
|
|
210
|
-
// Stamped by `project onboard` on success. Absence means the repo was never
|
|
211
|
-
// onboarded; `doctor` surfaces a hint. A value < CURRENT_ONBOARDING_VERSION
|
|
212
|
-
// means the contract has drifted and re-onboarding is recommended.
|
|
213
|
-
onboardingVersion: z.number().int().nonnegative().optional()
|
|
214
|
-
});
|
|
215
|
-
var ProjectSchema = z.object({
|
|
216
|
-
id: IdSchema,
|
|
217
|
-
name: z.string().min(1).regex(/^[a-z0-9-]+$/, "Project name must be a slug (lowercase, numbers, hyphens only)"),
|
|
218
|
-
displayName: z.string().min(1),
|
|
219
|
-
repositories: z.array(RepositorySchema).min(1),
|
|
220
|
-
description: z.string().optional()
|
|
221
|
-
});
|
|
222
|
-
var ProjectsSchema = z.array(ProjectSchema);
|
|
223
|
-
var TicketSchema = z.object({
|
|
224
|
-
id: IdSchema,
|
|
225
|
-
// UUID8
|
|
226
|
-
title: z.string().min(1),
|
|
227
|
-
description: z.string().optional(),
|
|
228
|
-
link: z.url().optional(),
|
|
229
|
-
affectedRepoIds: z.array(IdSchema).optional(),
|
|
230
|
-
// Subset of sprint's project's repos
|
|
231
|
-
requirementStatus: RequirementStatusSchema.default("pending"),
|
|
232
|
-
requirements: z.string().optional()
|
|
233
|
-
// Set during sprint refine
|
|
234
|
-
});
|
|
235
|
-
var TaskSchema = z.object({
|
|
236
|
-
id: IdSchema,
|
|
237
|
-
// UUID8
|
|
238
|
-
name: z.string().min(1),
|
|
239
|
-
description: z.string().optional(),
|
|
240
|
-
steps: z.array(z.string()).default([]),
|
|
241
|
-
verificationCriteria: z.array(z.string()).default([]),
|
|
242
|
-
status: TaskStatusSchema.default("todo"),
|
|
243
|
-
order: z.number().int().positive(),
|
|
244
|
-
ticketId: IdSchema.optional(),
|
|
245
|
-
blockedBy: z.array(IdSchema).default([]),
|
|
246
|
-
repoId: IdSchema,
|
|
247
|
-
// Required — resolves to an absolute path at runtime
|
|
248
|
-
verified: z.boolean().default(false),
|
|
249
|
-
verificationOutput: z.string().optional(),
|
|
250
|
-
evaluated: z.boolean().default(false),
|
|
251
|
-
evaluationOutput: z.string().optional(),
|
|
252
|
-
evaluationStatus: EvaluationStatusSchema.optional(),
|
|
253
|
-
evaluationFile: z.string().optional(),
|
|
254
|
-
// Planner-emitted extra evaluator dimensions; floor-only when undefined.
|
|
255
|
-
extraDimensions: z.array(z.string().min(1)).optional()
|
|
256
|
-
});
|
|
257
|
-
var TasksSchema = z.array(TaskSchema);
|
|
258
|
-
var ImportTaskSchema = z.object({
|
|
259
|
-
id: z.string().optional(),
|
|
260
|
-
// Local ID for referencing in blockedBy
|
|
261
|
-
name: z.string().min(1),
|
|
262
|
-
description: z.string().optional(),
|
|
263
|
-
steps: z.array(z.string()).optional(),
|
|
264
|
-
verificationCriteria: z.array(z.string()).optional(),
|
|
265
|
-
ticketId: z.string().optional(),
|
|
266
|
-
blockedBy: z.array(z.string()).optional(),
|
|
267
|
-
repoId: IdSchema,
|
|
268
|
-
extraDimensions: z.array(z.string().min(1)).optional()
|
|
269
|
-
});
|
|
270
|
-
var ImportTasksSchema = z.array(ImportTaskSchema);
|
|
271
|
-
var RefinedRequirementSchema = z.object({
|
|
272
|
-
ref: z.string().min(1),
|
|
273
|
-
requirements: z.string().min(1)
|
|
274
|
-
});
|
|
275
|
-
var RefinedRequirementsSchema = z.array(RefinedRequirementSchema);
|
|
276
|
-
var IdeateOutputSchema = z.object({
|
|
277
|
-
requirements: z.string().min(1),
|
|
278
|
-
tasks: ImportTasksSchema
|
|
279
|
-
});
|
|
280
|
-
var SprintSchema = z.object({
|
|
281
|
-
id: z.string().regex(/^\d{8}-\d{6}-[a-z0-9-]+$/, "Invalid sprint ID format"),
|
|
282
|
-
name: z.string().min(1),
|
|
283
|
-
projectId: IdSchema,
|
|
284
|
-
// Every sprint belongs to exactly one project
|
|
285
|
-
status: SprintStatusSchema.default("draft"),
|
|
286
|
-
createdAt: z.iso.datetime(),
|
|
287
|
-
activatedAt: z.iso.datetime().nullable().default(null),
|
|
288
|
-
closedAt: z.iso.datetime().nullable().default(null),
|
|
289
|
-
tickets: z.array(TicketSchema).default([]),
|
|
290
|
-
checkRanAt: z.record(IdSchema, z.iso.datetime()).default({}),
|
|
291
|
-
branch: z.string().nullable().default(null)
|
|
292
|
-
});
|
|
293
|
-
var AiProviderSchema = z.enum(["claude", "copilot"]);
|
|
294
|
-
var ConfigSchema = z.object({
|
|
295
|
-
currentSprint: z.string().nullable().default(null),
|
|
296
|
-
aiProvider: AiProviderSchema.nullable().default(null),
|
|
297
|
-
editor: z.string().nullable().default(null),
|
|
298
|
-
evaluationIterations: z.number().int().min(0).optional(),
|
|
299
|
-
aiCheckScriptDiscovery: z.boolean().optional()
|
|
300
|
-
});
|
|
301
|
-
function getRequirementsOutputJsonSchema() {
|
|
302
|
-
return JSON.stringify(z.toJSONSchema(RefinedRequirementsSchema), null, 2);
|
|
303
|
-
}
|
|
304
|
-
function getTaskImportJsonSchema() {
|
|
305
|
-
return JSON.stringify(z.toJSONSchema(ImportTasksSchema), null, 2);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// src/domain/ids.ts
|
|
309
|
-
import { randomBytes } from "crypto";
|
|
310
|
-
function generateUuid8() {
|
|
311
|
-
return randomBytes(4).toString("hex");
|
|
312
|
-
}
|
|
313
|
-
function slugify(input, maxLength = 40) {
|
|
314
|
-
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, maxLength).replace(/-$/, "");
|
|
315
|
-
}
|
|
316
|
-
function generateSprintId(name) {
|
|
317
|
-
const now = /* @__PURE__ */ new Date();
|
|
318
|
-
const date = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
319
|
-
const time = now.toISOString().slice(11, 19).replace(/:/g, "");
|
|
320
|
-
const slug = name ? slugify(name) : generateUuid8();
|
|
321
|
-
return `${date}-${time}-${slug || generateUuid8()}`;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export {
|
|
325
|
-
getDataDir,
|
|
326
|
-
getConfigPath,
|
|
327
|
-
getProjectsFilePath,
|
|
328
|
-
getSprintsDir,
|
|
329
|
-
getSprintDir,
|
|
330
|
-
getSprintFilePath,
|
|
331
|
-
getTasksFilePath,
|
|
332
|
-
getProgressFilePath,
|
|
333
|
-
getEvaluationFilePath,
|
|
334
|
-
getRefinementDir,
|
|
335
|
-
getPlanningDir,
|
|
336
|
-
getIdeationDir,
|
|
337
|
-
assertSafeCwd,
|
|
338
|
-
expandTilde,
|
|
339
|
-
validateProjectPath,
|
|
340
|
-
ensureDir,
|
|
341
|
-
removeDir,
|
|
342
|
-
fileExists,
|
|
343
|
-
listDirs,
|
|
344
|
-
readValidatedJson,
|
|
345
|
-
writeValidatedJson,
|
|
346
|
-
appendToFile,
|
|
347
|
-
readTextFile,
|
|
348
|
-
SprintStatusSchema,
|
|
349
|
-
TaskStatusSchema,
|
|
350
|
-
RequirementStatusSchema,
|
|
351
|
-
CURRENT_ONBOARDING_VERSION,
|
|
352
|
-
ProjectsSchema,
|
|
353
|
-
TasksSchema,
|
|
354
|
-
ImportTasksSchema,
|
|
355
|
-
RefinedRequirementsSchema,
|
|
356
|
-
IdeateOutputSchema,
|
|
357
|
-
SprintSchema,
|
|
358
|
-
ConfigSchema,
|
|
359
|
-
getRequirementsOutputJsonSchema,
|
|
360
|
-
getTaskImportJsonSchema,
|
|
361
|
-
generateUuid8,
|
|
362
|
-
generateSprintId
|
|
363
|
-
};
|