flight-rules 0.15.0 → 0.15.3
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/dist/commands/ralph.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export interface TaskGroupPlan {
|
|
|
16
16
|
status: string;
|
|
17
17
|
}>;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Format a timestamp for verbose output: [HH:MM:SS]
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatTimestamp(): string;
|
|
19
23
|
/**
|
|
20
24
|
* Parse the discovery response from Claude into TaskGroupPlan objects.
|
|
21
25
|
* Returns null if the response cannot be parsed (tags not found).
|
package/dist/commands/ralph.js
CHANGED
|
@@ -80,6 +80,16 @@ function buildAreaConstraint(area) {
|
|
|
80
80
|
- If no incomplete task groups exist in Area ${area}, respond with ALL_COMPLETE
|
|
81
81
|
`;
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Format a timestamp for verbose output: [HH:MM:SS]
|
|
85
|
+
*/
|
|
86
|
+
export function formatTimestamp() {
|
|
87
|
+
const now = new Date();
|
|
88
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
89
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
90
|
+
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
91
|
+
return `[${hours}:${minutes}:${seconds}]`;
|
|
92
|
+
}
|
|
83
93
|
/**
|
|
84
94
|
* Check if Claude CLI is available
|
|
85
95
|
*/
|
|
@@ -113,43 +123,62 @@ async function runClaudeWithPrompt(promptContent, verbose) {
|
|
|
113
123
|
], {
|
|
114
124
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
115
125
|
});
|
|
116
|
-
let
|
|
126
|
+
let reassembledText = ''; // Plain text reassembled from stream-json fragments
|
|
127
|
+
let resultText = ''; // Full text from the final result event (fallback)
|
|
117
128
|
let errorOutput = '';
|
|
118
129
|
let lineBuffer = ''; // Buffer for incomplete JSON lines
|
|
130
|
+
let needsTimestamp = true; // Track whether next text output needs a timestamp
|
|
119
131
|
claude.stdout?.on('data', (data) => {
|
|
120
132
|
const text = data.toString();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (
|
|
133
|
+
// Parse stream-json lines to reassemble plain text output
|
|
134
|
+
const fullText = lineBuffer + text;
|
|
135
|
+
const lines = fullText.split('\n');
|
|
136
|
+
// Last element might be incomplete - save it for next chunk
|
|
137
|
+
lineBuffer = lines.pop() || '';
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
if (!line.trim())
|
|
140
|
+
continue;
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(line);
|
|
143
|
+
// Handle different message types in stream-json format
|
|
144
|
+
if (parsed.type === 'assistant' && parsed.message?.content) {
|
|
145
|
+
for (const block of parsed.message.content) {
|
|
146
|
+
if (block.type === 'text' && block.text) {
|
|
147
|
+
reassembledText += block.text;
|
|
148
|
+
if (verbose) {
|
|
149
|
+
if (needsTimestamp) {
|
|
150
|
+
process.stdout.write(`${formatTimestamp()} `);
|
|
151
|
+
needsTimestamp = false;
|
|
152
|
+
}
|
|
137
153
|
process.stdout.write(block.text);
|
|
138
154
|
}
|
|
139
155
|
}
|
|
140
156
|
}
|
|
141
|
-
|
|
157
|
+
}
|
|
158
|
+
else if (parsed.type === 'content_block_delta' && parsed.delta?.text) {
|
|
159
|
+
reassembledText += parsed.delta.text;
|
|
160
|
+
if (verbose) {
|
|
161
|
+
if (needsTimestamp) {
|
|
162
|
+
process.stdout.write(`${formatTimestamp()} `);
|
|
163
|
+
needsTimestamp = false;
|
|
164
|
+
}
|
|
142
165
|
process.stdout.write(parsed.delta.text);
|
|
143
166
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
167
|
+
}
|
|
168
|
+
else if (parsed.type === 'content_block_stop') {
|
|
169
|
+
if (verbose) {
|
|
170
|
+
// Add newline + blank line after each content block ends
|
|
171
|
+
process.stdout.write('\n\n');
|
|
172
|
+
needsTimestamp = true;
|
|
147
173
|
}
|
|
148
174
|
}
|
|
149
|
-
|
|
150
|
-
|
|
175
|
+
else if (parsed.type === 'result' && typeof parsed.result === 'string') {
|
|
176
|
+
resultText = parsed.result;
|
|
151
177
|
}
|
|
152
178
|
}
|
|
179
|
+
catch {
|
|
180
|
+
// Not valid JSON, skip
|
|
181
|
+
}
|
|
153
182
|
}
|
|
154
183
|
});
|
|
155
184
|
claude.stderr?.on('data', (data) => {
|
|
@@ -160,6 +189,30 @@ async function runClaudeWithPrompt(promptContent, verbose) {
|
|
|
160
189
|
}
|
|
161
190
|
});
|
|
162
191
|
claude.on('close', (code) => {
|
|
192
|
+
// Flush any remaining data in lineBuffer
|
|
193
|
+
if (lineBuffer.trim()) {
|
|
194
|
+
try {
|
|
195
|
+
const parsed = JSON.parse(lineBuffer);
|
|
196
|
+
if (parsed.type === 'assistant' && parsed.message?.content) {
|
|
197
|
+
for (const block of parsed.message.content) {
|
|
198
|
+
if (block.type === 'text' && block.text) {
|
|
199
|
+
reassembledText += block.text;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else if (parsed.type === 'content_block_delta' && parsed.delta?.text) {
|
|
204
|
+
reassembledText += parsed.delta.text;
|
|
205
|
+
}
|
|
206
|
+
else if (parsed.type === 'result' && typeof parsed.result === 'string') {
|
|
207
|
+
resultText = parsed.result;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// Not valid JSON, skip
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Prefer reassembled streaming text; fall back to result event text
|
|
215
|
+
const output = reassembledText || resultText;
|
|
163
216
|
resolve({ output, exitCode: code ?? 0 });
|
|
164
217
|
});
|
|
165
218
|
claude.on('error', (err) => {
|
package/package.json
CHANGED
package/payload/AGENTS.md
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
# Flight Rules Discovery Agent
|
|
2
2
|
|
|
3
|
-
You are a discovery agent for Flight Rules. Your ONLY job is to scan implementation docs and report which task groups have incomplete tasks. Do NOT implement anything.
|
|
3
|
+
You are a discovery agent for Flight Rules. Your ONLY job is to scan implementation docs and report which task groups have **incomplete** (not-yet-done) tasks. Do NOT implement anything.
|
|
4
4
|
|
|
5
5
|
## Instructions
|
|
6
6
|
|
|
7
7
|
1. Read `docs/implementation/overview.md` to understand the area/task-group structure
|
|
8
8
|
2. Scan each Area directory in `docs/implementation/`
|
|
9
|
-
3. For each Task Group file (.md), check every task's
|
|
10
|
-
4.
|
|
9
|
+
3. For each Task Group file (.md), check every task's `**Status**:` field
|
|
10
|
+
4. A task is **incomplete** if its status is 🔵 Planned, 🟡 In Progress, or ⏸️ Blocked
|
|
11
|
+
5. A task is **complete** only if its status is ✅ Complete
|
|
12
|
+
6. Report all task groups that contain any incomplete task
|
|
11
13
|
|
|
12
14
|
## Response Format
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Output ONLY the `<ralph-discovery>` block below. No commentary, no summary, no interpretation before or after the tags. Your entire response must be the block and nothing else.
|
|
17
|
+
|
|
18
|
+
When incomplete tasks exist (the common case), use this format:
|
|
15
19
|
|
|
16
20
|
```
|
|
17
21
|
<ralph-discovery>
|
|
22
|
+
INCOMPLETE|{totalIncompleteTaskCount}
|
|
18
23
|
TASK_GROUP|{id}|{title}|{filePath}|{areaDir}
|
|
19
24
|
TASK|{taskId}|{taskTitle}|{status}
|
|
20
25
|
TASK|{taskId}|{taskTitle}|{status}
|
|
@@ -23,16 +28,17 @@ TASK|{taskId}|{taskTitle}|{status}
|
|
|
23
28
|
</ralph-discovery>
|
|
24
29
|
```
|
|
25
30
|
|
|
26
|
-
-
|
|
31
|
+
- First line is `INCOMPLETE|{N}` where N = total number of incomplete tasks across all task groups
|
|
32
|
+
- Each `TASK_GROUP` line is followed by its incomplete `TASK` lines
|
|
27
33
|
- `{id}` = task group ID as written in the file (e.g., "1.1", "2.3")
|
|
28
34
|
- `{title}` = task group title
|
|
29
35
|
- `{filePath}` = relative path from project root to the task group file
|
|
30
36
|
- `{areaDir}` = area directory name (e.g., "1-project-setup", "2-cli-core")
|
|
31
37
|
- `{taskId}` = individual task ID (e.g., "1.1.1", "2.3.2")
|
|
32
38
|
- `{taskTitle}` = individual task title
|
|
33
|
-
- `{status}` = one of: planned
|
|
39
|
+
- `{status}` = one of: `planned`, `in_progress`, `blocked` — all three mean the task is NOT done
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
Only if every single task in every single task group has ✅ Complete status (this is rare during active development), respond with:
|
|
36
42
|
|
|
37
43
|
```
|
|
38
44
|
<ralph-discovery>
|
|
@@ -40,11 +46,21 @@ ALL_COMPLETE
|
|
|
40
46
|
</ralph-discovery>
|
|
41
47
|
```
|
|
42
48
|
|
|
49
|
+
## Field Reference
|
|
50
|
+
|
|
51
|
+
| Status in doc | Meaning | Output value |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| 🔵 Planned | Not started, work remains | `planned` |
|
|
54
|
+
| 🟡 In Progress | Started but not finished | `in_progress` |
|
|
55
|
+
| ⏸️ Blocked | Cannot proceed, work remains | `blocked` |
|
|
56
|
+
| ✅ Complete | Done, do NOT include in output | (omit) |
|
|
57
|
+
|
|
43
58
|
## Rules
|
|
44
59
|
|
|
45
60
|
- Do NOT implement or modify any code
|
|
46
61
|
- Do NOT create or modify any files
|
|
47
62
|
- Do NOT run any scripts or quality checks
|
|
48
63
|
- ONLY read implementation files and report status
|
|
49
|
-
-
|
|
50
|
-
- List task groups in
|
|
64
|
+
- Your response must contain ONLY the `<ralph-discovery>` block — no other text
|
|
65
|
+
- List task groups in order by area number, then task group number
|
|
66
|
+
- Never output `ALL_COMPLETE` if any task has a status of planned, in_progress, or blocked
|