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
@@ -1,108 +0,0 @@
1
- import { fileURLToPath } from 'node:url';
2
- import { dirname, isAbsolute, join, resolve, sep } from 'node:path';
3
- import { homedir } from 'node:os';
4
- import { lstat, realpath, stat } from 'node:fs/promises';
5
-
6
- // Repo root: always the cloned repo directory (for schemas, etc.)
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = dirname(__filename);
9
-
10
- function getRepoRoot(): string {
11
- return join(__dirname, '..', '..');
12
- }
13
-
14
- // Data directory: RALPHCTL_ROOT env var (if set) or ~/.ralphctl/
15
- export function getDataDir(): string {
16
- return process.env['RALPHCTL_ROOT'] ?? join(homedir(), '.ralphctl');
17
- }
18
-
19
- // Config path (moved to data directory)
20
- export function getConfigPath(): string {
21
- return join(getDataDir(), 'config.json');
22
- }
23
-
24
- // Projects file path
25
- export function getProjectsFilePath(): string {
26
- return join(getDataDir(), 'projects.json');
27
- }
28
-
29
- // Sprint directory and file paths
30
- export function getSprintsDir(): string {
31
- return join(getDataDir(), 'sprints');
32
- }
33
-
34
- export function getSprintDir(sprintId: string): string {
35
- const sprintsDir = getSprintsDir();
36
- const resolved = resolve(sprintsDir, sprintId);
37
- if (!resolved.startsWith(sprintsDir + sep) && resolved !== sprintsDir) {
38
- throw new Error(`Path traversal detected in sprint ID: ${sprintId}`);
39
- }
40
- return resolved;
41
- }
42
-
43
- export function getSprintFilePath(sprintId: string): string {
44
- return join(getSprintDir(sprintId), 'sprint.json');
45
- }
46
-
47
- export function getTasksFilePath(sprintId: string): string {
48
- return join(getSprintDir(sprintId), 'tasks.json');
49
- }
50
-
51
- export function getProgressFilePath(sprintId: string): string {
52
- return join(getSprintDir(sprintId), 'progress.md');
53
- }
54
-
55
- export function getRefinementDir(sprintId: string, ticketId: string): string {
56
- return join(getSprintDir(sprintId), 'refinement', ticketId);
57
- }
58
-
59
- export function getPlanningDir(sprintId: string): string {
60
- return join(getSprintDir(sprintId), 'planning');
61
- }
62
-
63
- export function getIdeateDir(sprintId: string, ticketId: string): string {
64
- return join(getSprintDir(sprintId), 'ideation', ticketId);
65
- }
66
-
67
- export function getSchemaPath(schemaName: string): string {
68
- return join(getRepoRoot(), 'schemas', schemaName);
69
- }
70
-
71
- /**
72
- * Validate a path is safe to use as execSync/spawn cwd.
73
- * Rejects null bytes, newlines, and non-absolute paths.
74
- * @throws Error if path is unsafe
75
- */
76
- export function assertSafeCwd(path: string): void {
77
- if (!path || path.includes('\0') || path.includes('\n') || path.includes('\r')) {
78
- throw new Error('Unsafe path for cwd: contains null bytes or newlines');
79
- }
80
- if (!isAbsolute(path)) {
81
- throw new Error(`Unsafe path for cwd: must be absolute, got: ${path}`);
82
- }
83
- }
84
-
85
- /**
86
- * Validate that a path exists and is a directory.
87
- * Returns `true` if valid, or an error message string if invalid.
88
- */
89
- export async function validateProjectPath(path: string): Promise<string | true> {
90
- try {
91
- const resolved = resolve(path);
92
- const lstats = await lstat(resolved);
93
- if (lstats.isSymbolicLink()) {
94
- const realPath = await realpath(resolved);
95
- const realStats = await stat(realPath);
96
- if (!realStats.isDirectory()) {
97
- return 'Symlink target is not a directory';
98
- }
99
- return true;
100
- }
101
- if (!lstats.isDirectory()) {
102
- return 'Path is not a directory';
103
- }
104
- return true;
105
- } catch {
106
- return 'Directory does not exist';
107
- }
108
- }
@@ -1,34 +0,0 @@
1
- import { select } from '@inquirer/prompts';
2
- import type { AiProvider } from '@src/schemas/index.ts';
3
- import { getAiProvider, setAiProvider } from '@src/store/config.ts';
4
- import { emoji } from '@src/theme/ui.ts';
5
-
6
- /**
7
- * Resolve the active AI provider.
8
- * Reads from config; if not set, prompts the user to choose and saves the selection.
9
- */
10
- export async function resolveProvider(): Promise<AiProvider> {
11
- const stored = await getAiProvider();
12
- if (stored) return stored;
13
-
14
- const choice = await select<AiProvider>({
15
- message: `${emoji.donut} Which AI buddy should help with my homework?`,
16
- choices: [
17
- { name: 'Claude Code', value: 'claude' as const },
18
- { name: 'GitHub Copilot', value: 'copilot' as const },
19
- ],
20
- });
21
-
22
- await setAiProvider(choice);
23
- return choice;
24
- }
25
-
26
- /** Human-readable display name for a provider. */
27
- export function providerDisplayName(provider: AiProvider): string {
28
- return provider === 'claude' ? 'Claude' : 'Copilot';
29
- }
30
-
31
- /** CLI binary name for a provider. */
32
- export function providerBinary(provider: AiProvider): string {
33
- return provider === 'claude' ? 'claude' : 'copilot';
34
- }
@@ -1,63 +0,0 @@
1
- import { writeFile } from 'node:fs/promises';
2
- import { dirname } from 'node:path';
3
- import type { Sprint, Ticket } from '@src/schemas/index.ts';
4
- import { ensureDir } from '@src/utils/storage.ts';
5
-
6
- /**
7
- * Format requirements as a markdown document.
8
- */
9
- function formatRequirementsMarkdown(sprint: Sprint): string {
10
- const lines: string[] = [];
11
-
12
- lines.push(`# Sprint Requirements: ${sprint.name}`);
13
- lines.push('');
14
- lines.push(`Sprint ID: ${sprint.id}`);
15
- lines.push(`Generated: ${new Date().toISOString()}`);
16
- lines.push(`Status: ${sprint.status}`);
17
-
18
- if (sprint.tickets.length === 0) {
19
- lines.push('');
20
- lines.push('---');
21
- lines.push('');
22
- lines.push('_No tickets in this sprint._');
23
- return lines.join('\n') + '\n';
24
- }
25
-
26
- for (const ticket of sprint.tickets) {
27
- lines.push('');
28
- lines.push('---');
29
- lines.push('');
30
- lines.push(formatTicketSection(ticket));
31
- }
32
-
33
- return lines.join('\n') + '\n';
34
- }
35
-
36
- function formatTicketSection(ticket: Ticket): string {
37
- const lines: string[] = [];
38
-
39
- lines.push(`## ${ticket.projectName} - ${ticket.title}`);
40
- lines.push('');
41
- lines.push(`**Ticket ID:** ${ticket.id}`);
42
- lines.push(`**Status:** ${ticket.requirementStatus}`);
43
-
44
- if (ticket.link) {
45
- lines.push(`**Link:** ${ticket.link}`);
46
- }
47
-
48
- lines.push('');
49
- lines.push('### Requirements');
50
- lines.push('');
51
- lines.push(ticket.requirements ?? '_No requirements defined_');
52
-
53
- return lines.join('\n');
54
- }
55
-
56
- /**
57
- * Export sprint requirements to a markdown file.
58
- */
59
- export async function exportRequirementsToMarkdown(sprint: Sprint, outputPath: string): Promise<void> {
60
- const content = formatRequirementsMarkdown(sprint);
61
- await ensureDir(dirname(outputPath));
62
- await writeFile(outputPath, content, 'utf-8');
63
- }
@@ -1,107 +0,0 @@
1
- import { access, appendFile, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
2
- import { dirname } from 'node:path';
3
- import type { ZodType } from 'zod';
4
-
5
- export class ValidationError extends Error {
6
- public readonly path: string;
7
-
8
- constructor(message: string, path: string, cause?: unknown) {
9
- super(message, { cause });
10
- this.name = 'ValidationError';
11
- this.path = path;
12
- }
13
- }
14
-
15
- export class FileNotFoundError extends Error {
16
- public readonly path: string;
17
-
18
- constructor(message: string, path: string) {
19
- super(message);
20
- this.name = 'FileNotFoundError';
21
- this.path = path;
22
- }
23
- }
24
-
25
- export async function ensureDir(dirPath: string): Promise<void> {
26
- await mkdir(dirPath, { recursive: true });
27
- }
28
-
29
- export async function removeDir(dirPath: string): Promise<void> {
30
- await rm(dirPath, { recursive: true, force: true });
31
- }
32
-
33
- export async function fileExists(filePath: string): Promise<boolean> {
34
- try {
35
- await access(filePath);
36
- return true;
37
- } catch {
38
- return false;
39
- }
40
- }
41
-
42
- export async function listDirs(dirPath: string): Promise<string[]> {
43
- try {
44
- const entries = await readdir(dirPath, { withFileTypes: true });
45
- return entries.filter((e) => e.isDirectory()).map((e) => e.name);
46
- } catch {
47
- return [];
48
- }
49
- }
50
-
51
- export async function readValidatedJson<Output>(filePath: string, schema: ZodType<Output>): Promise<Output> {
52
- let content: string;
53
- try {
54
- content = await readFile(filePath, 'utf-8');
55
- } catch (err) {
56
- if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
57
- throw new FileNotFoundError(`File not found: ${filePath}`, filePath);
58
- }
59
- throw err;
60
- }
61
-
62
- let data: unknown;
63
- try {
64
- data = JSON.parse(content);
65
- } catch (err) {
66
- throw new ValidationError(`Invalid JSON in ${filePath}`, filePath, err);
67
- }
68
-
69
- const result = schema.safeParse(data);
70
- if (!result.success) {
71
- const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\n');
72
- throw new ValidationError(`Validation failed for ${filePath}:\n${issues}`, filePath, result.error);
73
- }
74
-
75
- return result.data;
76
- }
77
-
78
- export async function writeValidatedJson<Output>(
79
- filePath: string,
80
- data: Output,
81
- schema: ZodType<Output>
82
- ): Promise<void> {
83
- const result = schema.safeParse(data);
84
- if (!result.success) {
85
- const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\n');
86
- throw new ValidationError(`Validation failed before writing to ${filePath}:\n${issues}`, filePath, result.error);
87
- }
88
-
89
- await ensureDir(dirname(filePath));
90
- await writeFile(filePath, JSON.stringify(result.data, null, 2) + '\n', { encoding: 'utf-8', mode: 0o600 });
91
- }
92
-
93
- export async function appendToFile(filePath: string, content: string): Promise<void> {
94
- await ensureDir(dirname(filePath));
95
- await appendFile(filePath, content, { encoding: 'utf-8', mode: 0o600 });
96
- }
97
-
98
- export async function readTextFile(filePath: string): Promise<string> {
99
- try {
100
- return await readFile(filePath, 'utf-8');
101
- } catch (err) {
102
- if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
103
- throw new FileNotFoundError(`File not found: ${filePath}`, filePath);
104
- }
105
- throw err;
106
- }
107
- }
package/tsconfig.json DELETED
@@ -1,25 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2024",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "strict": true,
7
- "verbatimModuleSyntax": true,
8
- "noEmit": true,
9
- "allowImportingTsExtensions": true,
10
- "skipLibCheck": true,
11
- "esModuleInterop": true,
12
- "resolveJsonModule": true,
13
- "isolatedModules": true,
14
- "noUncheckedIndexedAccess": true,
15
- "noImplicitOverride": true,
16
- "noPropertyAccessFromIndexSignature": true,
17
- "forceConsistentCasingInFileNames": true,
18
- "baseUrl": ".",
19
- "paths": {
20
- "@src/*": ["src/*"]
21
- }
22
- },
23
- "include": ["src/**/*", "bin/*"],
24
- "exclude": ["node_modules", "dist"]
25
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes