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.
- package/README.md +58 -24
- package/dist/add-HGJCLWED.mjs +14 -0
- package/dist/add-MRGCS3US.mjs +14 -0
- package/dist/chunk-6PYTKGB5.mjs +316 -0
- package/dist/chunk-7TG3EAQ2.mjs +20 -0
- package/dist/chunk-EKMZZRWI.mjs +521 -0
- package/dist/chunk-JON4GCLR.mjs +59 -0
- package/dist/chunk-LOR7QBXX.mjs +3683 -0
- package/dist/chunk-MNMQC36F.mjs +556 -0
- package/dist/chunk-MRKOFVTM.mjs +537 -0
- package/dist/chunk-NTWO2LXB.mjs +52 -0
- package/dist/chunk-QBXHAXHI.mjs +562 -0
- package/dist/chunk-WGHJI3OI.mjs +214 -0
- package/dist/cli.mjs +4245 -0
- package/dist/create-MG7E7PLQ.mjs +10 -0
- package/dist/handle-UG5M2OON.mjs +22 -0
- package/dist/multiline-OHSNFCRG.mjs +40 -0
- package/dist/project-NT3L4FTB.mjs +28 -0
- package/dist/resolver-WSFWKACM.mjs +153 -0
- package/dist/sprint-4VHDLGFN.mjs +37 -0
- package/dist/wizard-LRELAN2J.mjs +196 -0
- package/package.json +19 -28
- package/CHANGELOG.md +0 -94
- package/bin/ralphctl +0 -13
- package/src/ai/executor.ts +0 -973
- package/src/ai/lifecycle.ts +0 -45
- package/src/ai/parser.ts +0 -40
- package/src/ai/permissions.ts +0 -207
- package/src/ai/process-manager.ts +0 -248
- package/src/ai/prompts/index.ts +0 -89
- package/src/ai/rate-limiter.ts +0 -89
- package/src/ai/runner.ts +0 -478
- package/src/ai/session.ts +0 -319
- package/src/ai/task-context.ts +0 -270
- package/src/cli-metadata.ts +0 -7
- package/src/cli.ts +0 -65
- package/src/commands/completion/index.ts +0 -33
- package/src/commands/config/config.ts +0 -58
- package/src/commands/config/index.ts +0 -33
- package/src/commands/dashboard/dashboard.ts +0 -5
- package/src/commands/dashboard/index.ts +0 -6
- package/src/commands/doctor/doctor.ts +0 -271
- package/src/commands/doctor/index.ts +0 -25
- package/src/commands/progress/index.ts +0 -25
- package/src/commands/progress/log.ts +0 -64
- package/src/commands/progress/show.ts +0 -14
- package/src/commands/project/add.ts +0 -336
- package/src/commands/project/index.ts +0 -104
- package/src/commands/project/list.ts +0 -31
- package/src/commands/project/remove.ts +0 -43
- package/src/commands/project/repo.ts +0 -118
- package/src/commands/project/show.ts +0 -49
- package/src/commands/sprint/close.ts +0 -180
- package/src/commands/sprint/context.ts +0 -109
- package/src/commands/sprint/create.ts +0 -60
- package/src/commands/sprint/current.ts +0 -75
- package/src/commands/sprint/delete.ts +0 -72
- package/src/commands/sprint/health.ts +0 -229
- package/src/commands/sprint/ideate.ts +0 -496
- package/src/commands/sprint/index.ts +0 -226
- package/src/commands/sprint/list.ts +0 -86
- package/src/commands/sprint/plan-utils.ts +0 -207
- package/src/commands/sprint/plan.ts +0 -549
- package/src/commands/sprint/refine.ts +0 -359
- package/src/commands/sprint/requirements.ts +0 -58
- package/src/commands/sprint/show.ts +0 -140
- package/src/commands/sprint/start.ts +0 -119
- package/src/commands/sprint/switch.ts +0 -20
- package/src/commands/task/add.ts +0 -316
- package/src/commands/task/import.ts +0 -150
- package/src/commands/task/index.ts +0 -123
- package/src/commands/task/list.ts +0 -145
- package/src/commands/task/next.ts +0 -45
- package/src/commands/task/remove.ts +0 -47
- package/src/commands/task/reorder.ts +0 -45
- package/src/commands/task/show.ts +0 -111
- package/src/commands/task/status.ts +0 -99
- package/src/commands/ticket/add.ts +0 -265
- package/src/commands/ticket/edit.ts +0 -166
- package/src/commands/ticket/index.ts +0 -114
- package/src/commands/ticket/list.ts +0 -128
- package/src/commands/ticket/refine-utils.ts +0 -89
- package/src/commands/ticket/refine.ts +0 -268
- package/src/commands/ticket/remove.ts +0 -48
- package/src/commands/ticket/show.ts +0 -74
- package/src/completion/handle.ts +0 -30
- package/src/completion/resolver.ts +0 -241
- package/src/interactive/dashboard.ts +0 -268
- package/src/interactive/escapable.ts +0 -81
- package/src/interactive/file-browser.ts +0 -153
- package/src/interactive/index.ts +0 -429
- package/src/interactive/menu.ts +0 -403
- package/src/interactive/selectors.ts +0 -273
- package/src/interactive/wizard.ts +0 -221
- package/src/providers/claude.ts +0 -53
- package/src/providers/copilot.ts +0 -86
- package/src/providers/index.ts +0 -43
- package/src/providers/types.ts +0 -85
- package/src/schemas/index.ts +0 -130
- package/src/store/config.ts +0 -74
- package/src/store/progress.ts +0 -230
- package/src/store/project.ts +0 -276
- package/src/store/sprint.ts +0 -229
- package/src/store/task.ts +0 -443
- package/src/store/ticket.ts +0 -178
- package/src/theme/index.ts +0 -215
- package/src/theme/ui.ts +0 -872
- package/src/utils/detect-scripts.ts +0 -247
- package/src/utils/editor-input.ts +0 -41
- package/src/utils/editor.ts +0 -37
- package/src/utils/exit-codes.ts +0 -27
- package/src/utils/file-lock.ts +0 -135
- package/src/utils/git.ts +0 -185
- package/src/utils/ids.ts +0 -37
- package/src/utils/issue-fetch.ts +0 -244
- package/src/utils/json-extract.ts +0 -62
- package/src/utils/multiline.ts +0 -61
- package/src/utils/path-selector.ts +0 -236
- package/src/utils/paths.ts +0 -108
- package/src/utils/provider.ts +0 -34
- package/src/utils/requirements-export.ts +0 -63
- package/src/utils/storage.ts +0 -107
- package/tsconfig.json +0 -25
- /package/{src/ai → dist}/prompts/ideate-auto.md +0 -0
- /package/{src/ai → dist}/prompts/ideate.md +0 -0
- /package/{src/ai → dist}/prompts/plan-auto.md +0 -0
- /package/{src/ai → dist}/prompts/plan-common.md +0 -0
- /package/{src/ai → dist}/prompts/plan-interactive.md +0 -0
- /package/{src/ai → dist}/prompts/task-execution.md +0 -0
- /package/{src/ai → dist}/prompts/ticket-refine.md +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/completion/handle.ts
|
|
4
|
+
async function handleCompletionRequest(program) {
|
|
5
|
+
const env = process.env;
|
|
6
|
+
if (!env["COMP_CWORD"] || !env["COMP_POINT"] || !env["COMP_LINE"]) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const tabtab = (await import("tabtab")).default;
|
|
10
|
+
const { resolveCompletions } = await import("./resolver-WSFWKACM.mjs");
|
|
11
|
+
const tabEnv = tabtab.parseEnv(env);
|
|
12
|
+
const completions = await resolveCompletions(program, {
|
|
13
|
+
line: tabEnv.line,
|
|
14
|
+
last: tabEnv.last,
|
|
15
|
+
prev: tabEnv.prev
|
|
16
|
+
});
|
|
17
|
+
tabtab.log(completions);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
handleCompletionRequest
|
|
22
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
icons,
|
|
4
|
+
muted
|
|
5
|
+
} from "./chunk-QBXHAXHI.mjs";
|
|
6
|
+
|
|
7
|
+
// src/utils/multiline.ts
|
|
8
|
+
import * as readline from "readline";
|
|
9
|
+
async function multilineInput(options) {
|
|
10
|
+
const { message, default: defaultValue, hint = "Ctrl+D to finish" } = options;
|
|
11
|
+
console.log(`${icons.edit} ${message} ${muted(`(${hint})`)}`);
|
|
12
|
+
if (defaultValue) {
|
|
13
|
+
console.log(muted(" Default:"));
|
|
14
|
+
for (const line of defaultValue.split("\n")) {
|
|
15
|
+
console.log(muted(` ${line}`));
|
|
16
|
+
}
|
|
17
|
+
console.log("");
|
|
18
|
+
}
|
|
19
|
+
const lines = [];
|
|
20
|
+
const rl = readline.createInterface({
|
|
21
|
+
input: process.stdin,
|
|
22
|
+
output: process.stdout,
|
|
23
|
+
terminal: process.stdin.isTTY
|
|
24
|
+
});
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
rl.on("line", (line) => {
|
|
27
|
+
lines.push(line);
|
|
28
|
+
});
|
|
29
|
+
rl.on("close", () => {
|
|
30
|
+
console.log("");
|
|
31
|
+
while (lines.length > 0 && lines.at(-1)?.trim() === "") {
|
|
32
|
+
lines.pop();
|
|
33
|
+
}
|
|
34
|
+
resolve(lines.join("\n"));
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
multilineInput
|
|
40
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ProjectExistsError,
|
|
4
|
+
ProjectNotFoundError,
|
|
5
|
+
addProjectRepo,
|
|
6
|
+
createProject,
|
|
7
|
+
getProject,
|
|
8
|
+
getProjectRepos,
|
|
9
|
+
listProjects,
|
|
10
|
+
projectExists,
|
|
11
|
+
removeProject,
|
|
12
|
+
removeProjectRepo,
|
|
13
|
+
updateProject
|
|
14
|
+
} from "./chunk-WGHJI3OI.mjs";
|
|
15
|
+
import "./chunk-6PYTKGB5.mjs";
|
|
16
|
+
export {
|
|
17
|
+
ProjectExistsError,
|
|
18
|
+
ProjectNotFoundError,
|
|
19
|
+
addProjectRepo,
|
|
20
|
+
createProject,
|
|
21
|
+
getProject,
|
|
22
|
+
getProjectRepos,
|
|
23
|
+
listProjects,
|
|
24
|
+
projectExists,
|
|
25
|
+
removeProject,
|
|
26
|
+
removeProjectRepo,
|
|
27
|
+
updateProject
|
|
28
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/completion/resolver.ts
|
|
4
|
+
var dynamicResolvers = {
|
|
5
|
+
"--project": async () => {
|
|
6
|
+
try {
|
|
7
|
+
const { listProjects } = await import("./project-NT3L4FTB.mjs");
|
|
8
|
+
const projects = await listProjects();
|
|
9
|
+
return projects.map((p) => ({ name: p.name, description: p.displayName }));
|
|
10
|
+
} catch {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"--status": () => {
|
|
15
|
+
return Promise.resolve([
|
|
16
|
+
{ name: "draft", description: "Draft sprints" },
|
|
17
|
+
{ name: "active", description: "Active sprints" },
|
|
18
|
+
{ name: "closed", description: "Closed sprints" },
|
|
19
|
+
{ name: "todo", description: "Todo tasks" },
|
|
20
|
+
{ name: "in_progress", description: "In-progress tasks" },
|
|
21
|
+
{ name: "done", description: "Done tasks" },
|
|
22
|
+
{ name: "pending", description: "Pending requirements" },
|
|
23
|
+
{ name: "approved", description: "Approved requirements" }
|
|
24
|
+
]);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var configKeyCompletions = [
|
|
28
|
+
{ name: "provider", description: "AI provider (claude or copilot)" },
|
|
29
|
+
{ name: "editor", description: "External editor for multiline input" }
|
|
30
|
+
];
|
|
31
|
+
var configValueCompletions = {
|
|
32
|
+
provider: [
|
|
33
|
+
{ name: "claude", description: "Claude Code CLI" },
|
|
34
|
+
{ name: "copilot", description: "GitHub Copilot CLI" }
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
async function getSprintCompletions() {
|
|
38
|
+
try {
|
|
39
|
+
const { listSprints } = await import("./sprint-4VHDLGFN.mjs");
|
|
40
|
+
const sprints = await listSprints();
|
|
41
|
+
return sprints.map((s) => ({
|
|
42
|
+
name: s.id,
|
|
43
|
+
description: `${s.name} (${s.status})`
|
|
44
|
+
}));
|
|
45
|
+
} catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function getSubcommands(cmd) {
|
|
50
|
+
return cmd.commands.map((sub) => ({
|
|
51
|
+
name: sub.name(),
|
|
52
|
+
description: sub.description()
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
function getOptions(cmd) {
|
|
56
|
+
const items = [];
|
|
57
|
+
for (const opt of cmd.options) {
|
|
58
|
+
const flag = opt.long ?? opt.short;
|
|
59
|
+
if (flag) {
|
|
60
|
+
items.push({ name: flag, description: opt.description });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return items;
|
|
64
|
+
}
|
|
65
|
+
function findSubcommand(cmd, name) {
|
|
66
|
+
return cmd.commands.find((sub) => sub.name() === name);
|
|
67
|
+
}
|
|
68
|
+
function optionExpectsValue(cmd, flag) {
|
|
69
|
+
const opt = cmd.options.find((o) => o.long === flag || o.short === flag);
|
|
70
|
+
if (!opt) return false;
|
|
71
|
+
return opt.required || opt.optional;
|
|
72
|
+
}
|
|
73
|
+
function parseWords(line) {
|
|
74
|
+
return line.trim().split(/\s+/).slice(1);
|
|
75
|
+
}
|
|
76
|
+
async function resolveCompletions(program, ctx) {
|
|
77
|
+
const words = parseWords(ctx.line);
|
|
78
|
+
let currentCmd = program;
|
|
79
|
+
let wordIndex = 0;
|
|
80
|
+
while (wordIndex < words.length) {
|
|
81
|
+
const word = words[wordIndex];
|
|
82
|
+
if (!word) break;
|
|
83
|
+
if (word.startsWith("-")) {
|
|
84
|
+
wordIndex++;
|
|
85
|
+
if (optionExpectsValue(currentCmd, word) && wordIndex < words.length) {
|
|
86
|
+
wordIndex++;
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const sub = findSubcommand(currentCmd, word);
|
|
91
|
+
if (sub) {
|
|
92
|
+
currentCmd = sub;
|
|
93
|
+
wordIndex++;
|
|
94
|
+
} else {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const cmdPath = getCommandPath(currentCmd);
|
|
99
|
+
if (cmdPath === "config set") {
|
|
100
|
+
return resolveConfigSetCompletions(words, ctx);
|
|
101
|
+
}
|
|
102
|
+
if (ctx.prev.startsWith("-")) {
|
|
103
|
+
const resolver = dynamicResolvers[ctx.prev];
|
|
104
|
+
if (resolver) {
|
|
105
|
+
return resolver();
|
|
106
|
+
}
|
|
107
|
+
if (optionExpectsValue(currentCmd, ctx.prev)) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (ctx.last.startsWith("-")) {
|
|
112
|
+
return getOptions(currentCmd);
|
|
113
|
+
}
|
|
114
|
+
const subs = getSubcommands(currentCmd);
|
|
115
|
+
if (subs.length > 0) {
|
|
116
|
+
return subs;
|
|
117
|
+
}
|
|
118
|
+
if (cmdPath.startsWith("sprint ") && acceptsPositionalArg(currentCmd)) {
|
|
119
|
+
return getSprintCompletions();
|
|
120
|
+
}
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
function getCommandPath(cmd) {
|
|
124
|
+
const parts = [];
|
|
125
|
+
let current = cmd;
|
|
126
|
+
while (current?.parent) {
|
|
127
|
+
parts.unshift(current.name());
|
|
128
|
+
current = current.parent;
|
|
129
|
+
}
|
|
130
|
+
return parts.join(" ");
|
|
131
|
+
}
|
|
132
|
+
function acceptsPositionalArg(cmd) {
|
|
133
|
+
return cmd.registeredArguments.length > 0;
|
|
134
|
+
}
|
|
135
|
+
function resolveConfigSetCompletions(words, ctx) {
|
|
136
|
+
const setIndex = words.indexOf("set");
|
|
137
|
+
if (setIndex === -1) {
|
|
138
|
+
return Promise.resolve(configKeyCompletions);
|
|
139
|
+
}
|
|
140
|
+
const argsAfterSet = words.slice(setIndex + 1).filter((w) => !w.startsWith("-"));
|
|
141
|
+
if (argsAfterSet.length === 0) {
|
|
142
|
+
return Promise.resolve(configKeyCompletions);
|
|
143
|
+
}
|
|
144
|
+
if (argsAfterSet.length === 1 && ctx.last !== "") {
|
|
145
|
+
return Promise.resolve(configKeyCompletions);
|
|
146
|
+
}
|
|
147
|
+
const key = argsAfterSet[0];
|
|
148
|
+
const values = key ? configValueCompletions[key] : void 0;
|
|
149
|
+
return Promise.resolve(values ?? []);
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
resolveCompletions
|
|
153
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
NoCurrentSprintError,
|
|
4
|
+
SprintNotFoundError,
|
|
5
|
+
SprintStatusError,
|
|
6
|
+
activateSprint,
|
|
7
|
+
assertSprintStatus,
|
|
8
|
+
closeSprint,
|
|
9
|
+
createSprint,
|
|
10
|
+
deleteSprint,
|
|
11
|
+
findActiveSprint,
|
|
12
|
+
getActiveSprintOrThrow,
|
|
13
|
+
getCurrentSprintOrThrow,
|
|
14
|
+
getSprint,
|
|
15
|
+
listSprints,
|
|
16
|
+
resolveSprintId,
|
|
17
|
+
saveSprint
|
|
18
|
+
} from "./chunk-EKMZZRWI.mjs";
|
|
19
|
+
import "./chunk-6PYTKGB5.mjs";
|
|
20
|
+
import "./chunk-QBXHAXHI.mjs";
|
|
21
|
+
export {
|
|
22
|
+
NoCurrentSprintError,
|
|
23
|
+
SprintNotFoundError,
|
|
24
|
+
SprintStatusError,
|
|
25
|
+
activateSprint,
|
|
26
|
+
assertSprintStatus,
|
|
27
|
+
closeSprint,
|
|
28
|
+
createSprint,
|
|
29
|
+
deleteSprint,
|
|
30
|
+
findActiveSprint,
|
|
31
|
+
getActiveSprintOrThrow,
|
|
32
|
+
getCurrentSprintOrThrow,
|
|
33
|
+
getSprint,
|
|
34
|
+
listSprints,
|
|
35
|
+
resolveSprintId,
|
|
36
|
+
saveSprint
|
|
37
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
sprintPlanCommand,
|
|
4
|
+
sprintRefineCommand,
|
|
5
|
+
sprintStartCommand
|
|
6
|
+
} from "./chunk-LOR7QBXX.mjs";
|
|
7
|
+
import "./chunk-NTWO2LXB.mjs";
|
|
8
|
+
import {
|
|
9
|
+
sprintCreateCommand
|
|
10
|
+
} from "./chunk-JON4GCLR.mjs";
|
|
11
|
+
import {
|
|
12
|
+
addSingleTicketInteractive
|
|
13
|
+
} from "./chunk-MNMQC36F.mjs";
|
|
14
|
+
import "./chunk-7TG3EAQ2.mjs";
|
|
15
|
+
import "./chunk-WGHJI3OI.mjs";
|
|
16
|
+
import {
|
|
17
|
+
getCurrentSprint,
|
|
18
|
+
getSprint
|
|
19
|
+
} from "./chunk-EKMZZRWI.mjs";
|
|
20
|
+
import "./chunk-6PYTKGB5.mjs";
|
|
21
|
+
import {
|
|
22
|
+
colors,
|
|
23
|
+
emoji,
|
|
24
|
+
icons,
|
|
25
|
+
log,
|
|
26
|
+
printHeader,
|
|
27
|
+
printSeparator,
|
|
28
|
+
progressBar,
|
|
29
|
+
showSuccess,
|
|
30
|
+
showWarning
|
|
31
|
+
} from "./chunk-QBXHAXHI.mjs";
|
|
32
|
+
|
|
33
|
+
// src/interactive/wizard.ts
|
|
34
|
+
import { confirm } from "@inquirer/prompts";
|
|
35
|
+
var TOTAL_STEPS = 5;
|
|
36
|
+
function showStepProgress(step, title) {
|
|
37
|
+
const bar = progressBar(step - 1, TOTAL_STEPS, { width: 10, showPercent: false });
|
|
38
|
+
log.newline();
|
|
39
|
+
printSeparator();
|
|
40
|
+
console.log(` ${colors.highlight(`Step ${String(step)} of ${String(TOTAL_STEPS)}`)} ${bar} ${title}`);
|
|
41
|
+
log.newline();
|
|
42
|
+
}
|
|
43
|
+
async function runWizard() {
|
|
44
|
+
try {
|
|
45
|
+
printHeader("Sprint Setup Wizard", emoji.donut);
|
|
46
|
+
log.dim("This wizard will guide you through setting up a new sprint.");
|
|
47
|
+
log.dim("You can skip optional steps along the way.");
|
|
48
|
+
log.newline();
|
|
49
|
+
showStepProgress(1, "Create Sprint");
|
|
50
|
+
try {
|
|
51
|
+
await sprintCreateCommand({ interactive: true });
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (err instanceof Error) {
|
|
54
|
+
log.error(`Sprint creation failed: ${err.message}`);
|
|
55
|
+
}
|
|
56
|
+
log.newline();
|
|
57
|
+
showWarning("Cannot continue without a sprint. Wizard aborted.");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const sprintId = await getCurrentSprint();
|
|
61
|
+
if (!sprintId) {
|
|
62
|
+
showWarning("No current sprint set. Wizard aborted.");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
showStepProgress(2, "Add Tickets");
|
|
66
|
+
let ticketCount = 0;
|
|
67
|
+
let addMore = true;
|
|
68
|
+
while (addMore) {
|
|
69
|
+
try {
|
|
70
|
+
const ticket = await addSingleTicketInteractive({});
|
|
71
|
+
if (ticket) {
|
|
72
|
+
ticketCount++;
|
|
73
|
+
} else {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (err instanceof Error) {
|
|
78
|
+
log.error(`Failed to add ticket: ${err.message}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
log.newline();
|
|
82
|
+
addMore = await confirm({
|
|
83
|
+
message: `${emoji.donut} Add another ticket?`,
|
|
84
|
+
default: true
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (ticketCount === 0) {
|
|
88
|
+
log.newline();
|
|
89
|
+
showWarning("No tickets added. You can add them later with: ralphctl ticket add");
|
|
90
|
+
}
|
|
91
|
+
showStepProgress(3, "Refine Requirements");
|
|
92
|
+
if (ticketCount === 0) {
|
|
93
|
+
log.dim("Skipped -- no tickets to refine.");
|
|
94
|
+
} else {
|
|
95
|
+
const shouldRefine = await confirm({
|
|
96
|
+
message: `${emoji.donut} Refine requirements now?`,
|
|
97
|
+
default: true
|
|
98
|
+
});
|
|
99
|
+
if (shouldRefine) {
|
|
100
|
+
try {
|
|
101
|
+
await sprintRefineCommand([]);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
if (err instanceof Error) {
|
|
104
|
+
log.error(`Refinement failed: ${err.message}`);
|
|
105
|
+
}
|
|
106
|
+
log.dim("You can refine later with: ralphctl sprint refine");
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
log.dim("Skipped. You can refine later with: ralphctl sprint refine");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
showStepProgress(4, "Plan Tasks");
|
|
113
|
+
let canPlan = false;
|
|
114
|
+
try {
|
|
115
|
+
const sprint = await getSprint(sprintId);
|
|
116
|
+
const hasTickets = sprint.tickets.length > 0;
|
|
117
|
+
const allApproved = hasTickets && sprint.tickets.every((t) => t.requirementStatus === "approved");
|
|
118
|
+
canPlan = allApproved;
|
|
119
|
+
if (!hasTickets) {
|
|
120
|
+
log.dim("Skipped -- no tickets to plan.");
|
|
121
|
+
} else if (!allApproved) {
|
|
122
|
+
log.dim("Skipped -- not all requirements are approved yet.");
|
|
123
|
+
log.dim("Refine first with: ralphctl sprint refine");
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
log.dim("Skipped -- could not read sprint state.");
|
|
127
|
+
}
|
|
128
|
+
if (canPlan) {
|
|
129
|
+
const shouldPlan = await confirm({
|
|
130
|
+
message: `${emoji.donut} Generate tasks now?`,
|
|
131
|
+
default: true
|
|
132
|
+
});
|
|
133
|
+
if (shouldPlan) {
|
|
134
|
+
try {
|
|
135
|
+
await sprintPlanCommand([]);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (err instanceof Error) {
|
|
138
|
+
log.error(`Planning failed: ${err.message}`);
|
|
139
|
+
}
|
|
140
|
+
log.dim("You can plan later with: ralphctl sprint plan");
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
log.dim("Skipped. You can plan later with: ralphctl sprint plan");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
showStepProgress(5, "Start Execution");
|
|
147
|
+
const shouldStart = await confirm({
|
|
148
|
+
message: `${emoji.donut} Start execution now?`,
|
|
149
|
+
default: false
|
|
150
|
+
});
|
|
151
|
+
if (shouldStart) {
|
|
152
|
+
try {
|
|
153
|
+
await sprintStartCommand([]);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
if (err instanceof Error) {
|
|
156
|
+
log.error(`Execution failed: ${err.message}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
log.newline();
|
|
162
|
+
printSeparator();
|
|
163
|
+
showSuccess("Wizard complete!");
|
|
164
|
+
log.newline();
|
|
165
|
+
try {
|
|
166
|
+
const sprint = await getSprint(sprintId);
|
|
167
|
+
log.info(`Sprint "${sprint.name}" is ready.`);
|
|
168
|
+
log.item(`${icons.ticket} ${String(sprint.tickets.length)} ticket(s)`);
|
|
169
|
+
const approvedCount = sprint.tickets.filter((t) => t.requirementStatus === "approved").length;
|
|
170
|
+
if (sprint.tickets.length > 0) {
|
|
171
|
+
log.item(`${icons.success} ${String(approvedCount)}/${String(sprint.tickets.length)} requirements approved`);
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
log.newline();
|
|
176
|
+
log.dim("Next steps:");
|
|
177
|
+
if (ticketCount === 0) {
|
|
178
|
+
log.item("ralphctl ticket add --project <name>");
|
|
179
|
+
}
|
|
180
|
+
log.item("ralphctl sprint refine");
|
|
181
|
+
log.item("ralphctl sprint plan");
|
|
182
|
+
log.item("ralphctl sprint start");
|
|
183
|
+
log.newline();
|
|
184
|
+
} catch (err) {
|
|
185
|
+
if (err.name === "ExitPromptError") {
|
|
186
|
+
log.newline();
|
|
187
|
+
showWarning("Wizard cancelled");
|
|
188
|
+
log.newline();
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
throw err;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export {
|
|
195
|
+
runWizard
|
|
196
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralphctl",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Sprint and task management CLI for AI-assisted coding",
|
|
5
5
|
"homepage": "https://github.com/lukas-grigis/ralphctl",
|
|
6
6
|
"type": "module",
|
|
@@ -24,37 +24,15 @@
|
|
|
24
24
|
"developer-tools"
|
|
25
25
|
],
|
|
26
26
|
"bin": {
|
|
27
|
-
"ralphctl": "./
|
|
27
|
+
"ralphctl": "./dist/cli.mjs"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"!src/**/*.test.ts",
|
|
33
|
-
"!src/test-utils/",
|
|
34
|
-
"!src/integration/",
|
|
35
|
-
"schemas/",
|
|
36
|
-
"tsconfig.json",
|
|
37
|
-
"README.md",
|
|
38
|
-
"LICENSE",
|
|
39
|
-
"CHANGELOG.md"
|
|
30
|
+
"dist/",
|
|
31
|
+
"schemas/"
|
|
40
32
|
],
|
|
41
33
|
"publishConfig": {
|
|
42
34
|
"access": "public"
|
|
43
35
|
},
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "tsc --noEmit",
|
|
46
|
-
"dev": "tsx src/cli.ts",
|
|
47
|
-
"lint": "eslint .",
|
|
48
|
-
"lint:fix": "eslint . --fix",
|
|
49
|
-
"format": "prettier --write .",
|
|
50
|
-
"format:check": "prettier --check .",
|
|
51
|
-
"typecheck": "tsc --noEmit",
|
|
52
|
-
"test": "vitest run",
|
|
53
|
-
"test:watch": "vitest",
|
|
54
|
-
"test:coverage": "vitest run --coverage",
|
|
55
|
-
"prepare": "husky"
|
|
56
|
-
},
|
|
57
|
-
"packageManager": "pnpm@10.29.3",
|
|
58
36
|
"engines": {
|
|
59
37
|
"node": ">=24.0.0"
|
|
60
38
|
},
|
|
@@ -65,7 +43,6 @@
|
|
|
65
43
|
"gradient-string": "^3.0.0",
|
|
66
44
|
"ora": "^9.3.0",
|
|
67
45
|
"tabtab": "^3.0.2",
|
|
68
|
-
"tsx": "^4.21.0",
|
|
69
46
|
"zod": "^4.3.6"
|
|
70
47
|
},
|
|
71
48
|
"devDependencies": {
|
|
@@ -78,6 +55,8 @@
|
|
|
78
55
|
"husky": "^9.1.7",
|
|
79
56
|
"lint-staged": "^16.3.1",
|
|
80
57
|
"prettier": "^3.8.1",
|
|
58
|
+
"tsup": "^8.5.1",
|
|
59
|
+
"tsx": "^4.21.0",
|
|
81
60
|
"typescript": "^5.9.3",
|
|
82
61
|
"typescript-eslint": "^8.56.1",
|
|
83
62
|
"vitest": "^4.0.18"
|
|
@@ -88,5 +67,17 @@
|
|
|
88
67
|
"prettier --write"
|
|
89
68
|
],
|
|
90
69
|
"*.{md,json,yml,yaml}": "prettier --write"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "tsup && mkdir -p dist/prompts && cp src/ai/prompts/*.md dist/prompts/",
|
|
73
|
+
"dev": "tsx src/cli.ts",
|
|
74
|
+
"lint": "eslint .",
|
|
75
|
+
"lint:fix": "eslint . --fix",
|
|
76
|
+
"format": "prettier --write .",
|
|
77
|
+
"format:check": "prettier --check .",
|
|
78
|
+
"typecheck": "tsc --noEmit",
|
|
79
|
+
"test": "vitest run",
|
|
80
|
+
"test:watch": "vitest",
|
|
81
|
+
"test:coverage": "vitest run --coverage"
|
|
91
82
|
}
|
|
92
|
-
}
|
|
83
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to RalphCTL will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
-
|
|
7
|
-
## [0.1.0] - 2026-03-07
|
|
8
|
-
|
|
9
|
-
### Added
|
|
10
|
-
|
|
11
|
-
- **npm publishing** — `ralphctl` is now available via `npm install -g ralphctl`
|
|
12
|
-
- Release pipeline publishes to npm and creates GitHub Releases with changelog-based notes
|
|
13
|
-
|
|
14
|
-
### Changed
|
|
15
|
-
|
|
16
|
-
- Streamlined README for end-user onboarding with `npm install -g` as primary install method
|
|
17
|
-
- Added release process documentation to CONTRIBUTING.md
|
|
18
|
-
- Version bumped to 0.1.0 for first public npm release
|
|
19
|
-
|
|
20
|
-
## [0.0.3] - 2026-03-06
|
|
21
|
-
|
|
22
|
-
### Changed
|
|
23
|
-
|
|
24
|
-
- Normalized git author identity across commit history
|
|
25
|
-
- Updated package metadata for open-source release (description, homepage, private flag)
|
|
26
|
-
- Moved `tsx` from devDependencies to dependencies (runtime requirement for `bin/ralphctl`)
|
|
27
|
-
- Fixed stale path references in SECURITY.md, CONTRIBUTING.md, and agent memory files
|
|
28
|
-
- Fixed changelog compare link in release workflow to include `v` prefix
|
|
29
|
-
- Corrected documentation table descriptions in README.md
|
|
30
|
-
- Cleaned up stale `dist/` build artifacts
|
|
31
|
-
- Edited documentation for public release
|
|
32
|
-
|
|
33
|
-
## [0.0.2] - 2026-03-03
|
|
34
|
-
|
|
35
|
-
### Added
|
|
36
|
-
|
|
37
|
-
- **Doctor command** — `ralphctl doctor` checks Node.js version, git, AI provider binary, data directory, project repos, and current sprint health
|
|
38
|
-
- **Shell tab-completion** — `ralphctl completion install` for bash, zsh, and fish via tabtab
|
|
39
|
-
- **Branch management** — `sprint start` prompts for branch strategy (keep current, auto, custom); `--branch` and `--branch-name` flags; pre-flight verification; `sprint close --create-pr` creates PRs
|
|
40
|
-
- **Provider abstraction** — `config set provider claude|copilot` with adapter layer; experimental Copilot CLI support with headless execution and session ID capture
|
|
41
|
-
- **Draft re-plan** — running `sprint plan` on a draft with existing tasks passes all tickets + tasks as AI context for atomic replacement
|
|
42
|
-
- **Check script model** — single idempotent `checkScript` per repo replaces old `setupScript`/`verifyScript`; runs at sprint start and as a post-task gate
|
|
43
|
-
- **Lifecycle hooks** — `runLifecycleHook()` abstraction in `src/ai/lifecycle.ts` with `RALPHCTL_LIFECYCLE_EVENT` env var
|
|
44
|
-
- **Ecosystem detection** — `EcosystemDetector[]` registry (node, python, go, rust, gradle, maven, makefile) for check script suggestions during project setup
|
|
45
|
-
- **Sprint health** — duplicate task order and pending requirements diagnostics; branch consistency checks across repos
|
|
46
|
-
- **Interactive mode** — Escape key navigation, styled section titles, flat workflow section, provider config in REPL, refined/planned counts in status header, guards for unrefined/unplanned tickets
|
|
47
|
-
- **Inline multiline editor** — replaced with `@inquirer/editor` and configurable editor settings via `config set editor`
|
|
48
|
-
- **CI/CD** — GitHub Actions pipeline with lint, typecheck, test, format check; Dependabot; automated GitHub Release pipeline
|
|
49
|
-
- **Schema sync tests** — JSON schema ↔ Zod schema validation
|
|
50
|
-
|
|
51
|
-
### Changed
|
|
52
|
-
|
|
53
|
-
- Renamed `claude` module to `ai` for provider-agnostic naming
|
|
54
|
-
- Replaced tsup build with bash wrapper approach for CLI outside repo root
|
|
55
|
-
- Default data directory changed to `~/.ralphctl` (was `ralphctl-data/`)
|
|
56
|
-
- Separated repo root from data directory with smart `RALPHCTL_ROOT` handling
|
|
57
|
-
- Removed `externalId` field and `--id`/`--editor` CLI flags from ticket command
|
|
58
|
-
- Documentation restructured — moved to `.claude/docs/`, slimmed CLAUDE.md from 613 to 160 lines with skill-based reference material
|
|
59
|
-
- Replaced raw color functions with theme helpers across all commands
|
|
60
|
-
- Improved card rendering and terminal width awareness
|
|
61
|
-
|
|
62
|
-
### Fixed
|
|
63
|
-
|
|
64
|
-
- Sanitize session IDs and harden file operations against path traversal
|
|
65
|
-
- Fixed pre-flight execution checks for security and correctness
|
|
66
|
-
- Preserve error cause in re-thrown errors
|
|
67
|
-
- Thread provider through `checkTaskPermissions()`
|
|
68
|
-
- Branch management error handling and retry logic
|
|
69
|
-
- Interactive mode duplicate quote, closed sprint status header, and dashboard duplication
|
|
70
|
-
- ANSI code handling in CLI test field extraction
|
|
71
|
-
- Removed redundant file reads in interactive menu context loading
|
|
72
|
-
|
|
73
|
-
### Dependencies
|
|
74
|
-
|
|
75
|
-
- Bumped `zod` from 3.x to 4.x
|
|
76
|
-
- Bumped `@inquirer/prompts` from 7.x to 8.x
|
|
77
|
-
- Bumped `@types/node`, `globals`, `ora`, `typescript-eslint`, and other dev dependencies
|
|
78
|
-
|
|
79
|
-
## [0.0.1] - 2026-02-15
|
|
80
|
-
|
|
81
|
-
### Added
|
|
82
|
-
|
|
83
|
-
- **Project management** — register multi-repo projects with named paths
|
|
84
|
-
- **Sprint lifecycle** — create, activate, close sprints with state machine enforcement (draft -> active -> closed)
|
|
85
|
-
- **Ticket tracking** — add work items linked to projects, with optional external IDs
|
|
86
|
-
- **Two-phase planning** — refine requirements (WHAT) then generate tasks (HOW) with human approval gates
|
|
87
|
-
- **Task dependencies** — `blockedBy` references with topological sort and cycle detection
|
|
88
|
-
- **Task execution** — headless, watch, session, and interactive modes via Claude CLI
|
|
89
|
-
- **Parallel execution** — one task per repo concurrently, with rate limit backoff and session resume
|
|
90
|
-
- **Interactive menu mode** — context-aware REPL with persistent status header and Quick Start wizard
|
|
91
|
-
- **Sprint health checks** — diagnose blockers, stale tasks, and missing dependencies
|
|
92
|
-
- **Requirements export** — markdown export of refined requirements
|
|
93
|
-
- **Progress logging** — append-only timestamped progress log per sprint
|
|
94
|
-
- **Ralph Wiggum personality** — themed UI with donut spinners, random quotes, and gradient banner
|
package/bin/ralphctl
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Resolve symlinks so this works via npm link / pnpm link --global
|
|
3
|
-
SOURCE="${BASH_SOURCE[0]}"
|
|
4
|
-
while [ -L "$SOURCE" ]; do
|
|
5
|
-
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
6
|
-
SOURCE="$(readlink "$SOURCE")"
|
|
7
|
-
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
|
|
8
|
-
done
|
|
9
|
-
SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
10
|
-
REPO_DIR="$SCRIPT_DIR/.."
|
|
11
|
-
|
|
12
|
-
export TSX_TSCONFIG_PATH="$REPO_DIR/tsconfig.json"
|
|
13
|
-
exec node --import tsx "$REPO_DIR/src/cli.ts" "$@"
|