planflow-ai 1.3.4 → 1.4.1
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/.claude/commands/create-plan.md +11 -0
- package/.claude/commands/discovery-plan.md +12 -0
- package/.claude/commands/execute-plan.md +114 -23
- package/.claude/commands/flow.md +30 -5
- package/.claude/commands/resume-work.md +261 -0
- package/.claude/commands/review-code.md +11 -0
- package/.claude/commands/review-pr.md +11 -0
- package/.claude/resources/core/_index.md +45 -2
- package/.claude/resources/core/atomic-commits.md +380 -0
- package/.claude/resources/core/autopilot-mode.md +3 -2
- package/.claude/resources/core/compaction-guide.md +15 -1
- package/.claude/resources/core/heartbeat.md +129 -1
- package/.claude/resources/core/model-routing.md +6 -2
- package/.claude/resources/core/per-task-verification.md +362 -0
- package/.claude/resources/core/phase-isolation.md +192 -4
- package/.claude/resources/core/session-scratchpad.md +1 -0
- package/.claude/resources/core/wave-execution.md +329 -0
- package/.claude/resources/patterns/plans-patterns.md +56 -0
- package/.claude/resources/patterns/plans-templates.md +152 -0
- package/.claude/resources/skills/_index.md +8 -6
- package/.claude/resources/skills/create-plan-skill.md +71 -5
- package/.claude/resources/skills/execute-plan-skill.md +357 -12
- package/.claude/resources/skills/resume-work-skill.md +159 -0
- package/.claude/rules/core/forbidden-patterns.md +38 -0
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/state.d.ts +12 -0
- package/dist/cli/commands/state.d.ts.map +1 -0
- package/dist/cli/commands/state.js +47 -0
- package/dist/cli/commands/state.js.map +1 -0
- package/dist/cli/daemon/desktop-notifier.d.ts +16 -0
- package/dist/cli/daemon/desktop-notifier.d.ts.map +1 -0
- package/dist/cli/daemon/desktop-notifier.js +53 -0
- package/dist/cli/daemon/desktop-notifier.js.map +1 -0
- package/dist/cli/daemon/event-writer.d.ts +22 -0
- package/dist/cli/daemon/event-writer.d.ts.map +1 -0
- package/dist/cli/daemon/event-writer.js +76 -0
- package/dist/cli/daemon/event-writer.js.map +1 -0
- package/dist/cli/daemon/heartbeat-daemon.js +81 -1
- package/dist/cli/daemon/heartbeat-daemon.js.map +1 -1
- package/dist/cli/daemon/log-writer.d.ts +17 -0
- package/dist/cli/daemon/log-writer.d.ts.map +1 -0
- package/dist/cli/daemon/log-writer.js +62 -0
- package/dist/cli/daemon/log-writer.js.map +1 -0
- package/dist/cli/daemon/notification-router.d.ts +17 -0
- package/dist/cli/daemon/notification-router.d.ts.map +1 -0
- package/dist/cli/daemon/notification-router.js +35 -0
- package/dist/cli/daemon/notification-router.js.map +1 -0
- package/dist/cli/daemon/prompt-manager.d.ts +27 -0
- package/dist/cli/daemon/prompt-manager.d.ts.map +1 -0
- package/dist/cli/daemon/prompt-manager.js +107 -0
- package/dist/cli/daemon/prompt-manager.js.map +1 -0
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/state/flowconfig-parser.d.ts +16 -0
- package/dist/cli/state/flowconfig-parser.d.ts.map +1 -0
- package/dist/cli/state/flowconfig-parser.js +166 -0
- package/dist/cli/state/flowconfig-parser.js.map +1 -0
- package/dist/cli/state/heartbeat-state.d.ts +16 -0
- package/dist/cli/state/heartbeat-state.d.ts.map +1 -0
- package/dist/cli/state/heartbeat-state.js +97 -0
- package/dist/cli/state/heartbeat-state.js.map +1 -0
- package/dist/cli/state/model-router.d.ts +8 -0
- package/dist/cli/state/model-router.d.ts.map +1 -0
- package/dist/cli/state/model-router.js +36 -0
- package/dist/cli/state/model-router.js.map +1 -0
- package/dist/cli/state/plan-parser.d.ts +16 -0
- package/dist/cli/state/plan-parser.d.ts.map +1 -0
- package/dist/cli/state/plan-parser.js +124 -0
- package/dist/cli/state/plan-parser.js.map +1 -0
- package/dist/cli/state/session-state.d.ts +21 -0
- package/dist/cli/state/session-state.d.ts.map +1 -0
- package/dist/cli/state/session-state.js +36 -0
- package/dist/cli/state/session-state.js.map +1 -0
- package/dist/cli/state/state-md-parser.d.ts +18 -0
- package/dist/cli/state/state-md-parser.d.ts.map +1 -0
- package/dist/cli/state/state-md-parser.js +222 -0
- package/dist/cli/state/state-md-parser.js.map +1 -0
- package/dist/cli/state/types.d.ts +106 -0
- package/dist/cli/state/types.d.ts.map +1 -0
- package/dist/cli/state/types.js +8 -0
- package/dist/cli/state/types.js.map +1 -0
- package/dist/cli/state/wave-calculator.d.ts +18 -0
- package/dist/cli/state/wave-calculator.d.ts.map +1 -0
- package/dist/cli/state/wave-calculator.js +134 -0
- package/dist/cli/state/wave-calculator.js.map +1 -0
- package/dist/cli/types.d.ts +15 -0
- package/dist/cli/types.d.ts.map +1 -1
- package/package.json +4 -2
- package/templates/shared/CLAUDE.md.template +4 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heartbeat state parser — reads heartbeat events and calculates unread count.
|
|
3
|
+
*
|
|
4
|
+
* Parses `.heartbeat-state.json` for the last read timestamp,
|
|
5
|
+
* `.heartbeat-events.jsonl` for event records, and checks
|
|
6
|
+
* `.heartbeat-prompt.md` for pending prompt status.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
const STATE_FILENAME = '.heartbeat-state.json';
|
|
11
|
+
const EVENTS_FILENAME = '.heartbeat-events.jsonl';
|
|
12
|
+
const PROMPT_FILENAME = '.heartbeat-prompt.md';
|
|
13
|
+
/**
|
|
14
|
+
* Read and parse the heartbeat state file.
|
|
15
|
+
* Returns null if the file is missing, empty, or contains corrupt JSON.
|
|
16
|
+
*/
|
|
17
|
+
function readHeartbeatState(flowDir) {
|
|
18
|
+
const filePath = join(flowDir, STATE_FILENAME);
|
|
19
|
+
if (!existsSync(filePath)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const content = readFileSync(filePath, 'utf-8').trim();
|
|
24
|
+
if (content.length === 0) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const parsed = JSON.parse(content);
|
|
28
|
+
if (!parsed.lastReadTimestamp || typeof parsed.lastReadTimestamp !== 'string') {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Corrupt JSON — treat as missing
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Count unread events from the JSONL events file.
|
|
40
|
+
* An event is unread if its timestamp is after the lastReadTimestamp,
|
|
41
|
+
* or if lastReadTimestamp is null (all events are unread).
|
|
42
|
+
*/
|
|
43
|
+
function countUnreadEvents(flowDir, lastReadTimestamp) {
|
|
44
|
+
const filePath = join(flowDir, EVENTS_FILENAME);
|
|
45
|
+
if (!existsSync(filePath)) {
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
let content;
|
|
49
|
+
try {
|
|
50
|
+
content = readFileSync(filePath, 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
const lines = content.split('\n').filter((line) => line.trim().length > 0);
|
|
56
|
+
if (lines.length === 0) {
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
// If no last read timestamp, all events are unread
|
|
60
|
+
if (lastReadTimestamp === null) {
|
|
61
|
+
return lines.length;
|
|
62
|
+
}
|
|
63
|
+
const lastReadDate = new Date(lastReadTimestamp);
|
|
64
|
+
let unreadCount = 0;
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
try {
|
|
67
|
+
const event = JSON.parse(line);
|
|
68
|
+
const eventDate = new Date(event.timestamp);
|
|
69
|
+
if (eventDate > lastReadDate) {
|
|
70
|
+
unreadCount++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Skip corrupt lines
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return unreadCount;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the heartbeat summary for a flow directory.
|
|
81
|
+
*
|
|
82
|
+
* Returns unread event count, pending prompt status, and last read timestamp.
|
|
83
|
+
* Handles all edge cases: missing files, empty files, corrupt JSON.
|
|
84
|
+
*/
|
|
85
|
+
export function getHeartbeatSummary(flowDir) {
|
|
86
|
+
const state = readHeartbeatState(flowDir);
|
|
87
|
+
const lastReadTimestamp = state?.lastReadTimestamp ?? null;
|
|
88
|
+
const unreadCount = countUnreadEvents(flowDir, lastReadTimestamp);
|
|
89
|
+
const promptPath = join(flowDir, PROMPT_FILENAME);
|
|
90
|
+
const hasPrompt = existsSync(promptPath);
|
|
91
|
+
return {
|
|
92
|
+
unread_count: unreadCount,
|
|
93
|
+
has_prompt: hasPrompt,
|
|
94
|
+
last_read_timestamp: lastReadTimestamp,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=heartbeat-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat-state.js","sourceRoot":"","sources":["../../../src/cli/state/heartbeat-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PlanPhase, ModelTier } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Calculate model tiers for each plan phase based on complexity scores.
|
|
4
|
+
*
|
|
5
|
+
* Returns an empty array when model routing is disabled.
|
|
6
|
+
*/
|
|
7
|
+
export declare function calculateModelTiers(phases: PlanPhase[], modelRoutingEnabled: boolean): ModelTier[];
|
|
8
|
+
//# sourceMappingURL=model-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-router.d.ts","sourceRoot":"","sources":["../../../src/cli/state/model-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAkB,MAAM,YAAY,CAAC;AAmBvE;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EAAE,EACnB,mBAAmB,EAAE,OAAO,GAC3B,SAAS,EAAE,CAcb"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map a complexity score to a model tier and model name.
|
|
3
|
+
*
|
|
4
|
+
* - 0-3 -> fast (haiku)
|
|
5
|
+
* - 4-5 -> standard (sonnet)
|
|
6
|
+
* - 6-10 -> powerful (opus)
|
|
7
|
+
*/
|
|
8
|
+
function complexityToTier(complexity) {
|
|
9
|
+
if (complexity <= 3) {
|
|
10
|
+
return { tier: 'fast', model: 'haiku' };
|
|
11
|
+
}
|
|
12
|
+
if (complexity <= 5) {
|
|
13
|
+
return { tier: 'standard', model: 'sonnet' };
|
|
14
|
+
}
|
|
15
|
+
return { tier: 'powerful', model: 'opus' };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Calculate model tiers for each plan phase based on complexity scores.
|
|
19
|
+
*
|
|
20
|
+
* Returns an empty array when model routing is disabled.
|
|
21
|
+
*/
|
|
22
|
+
export function calculateModelTiers(phases, modelRoutingEnabled) {
|
|
23
|
+
if (!modelRoutingEnabled) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
return phases.map((phase) => {
|
|
27
|
+
const { tier, model } = complexityToTier(phase.complexity);
|
|
28
|
+
return {
|
|
29
|
+
phase: phase.number,
|
|
30
|
+
complexity: phase.complexity,
|
|
31
|
+
tier,
|
|
32
|
+
model,
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=model-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-router.js","sourceRoot":"","sources":["../../../src/cli/state/model-router.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,UAAkB;IAC1C,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC1C,CAAC;IACD,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC/C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAmB,EACnB,mBAA4B;IAE5B,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC1B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC3D,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,IAAI;YACJ,KAAK;SACN,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan markdown parser
|
|
3
|
+
*
|
|
4
|
+
* Parses plan files (flow/plans/plan_*.md) into structured PlanPhase objects.
|
|
5
|
+
* Extracts phase headers, complexity scores, dependencies, tasks, and verify tags.
|
|
6
|
+
*/
|
|
7
|
+
import type { PlanPhase } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Parses a plan markdown file and returns an array of PlanPhase objects.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parsePlan(planPath: string): PlanPhase[];
|
|
12
|
+
/**
|
|
13
|
+
* Parses plan markdown content (for testability without file I/O).
|
|
14
|
+
*/
|
|
15
|
+
export declare function parsePlanContent(content: string): PlanPhase[];
|
|
16
|
+
//# sourceMappingURL=plan-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-parser.d.ts","sourceRoot":"","sources":["../../../src/cli/state/plan-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,YAAY,CAAC;AAWtD;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CAGvD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAiG7D"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan markdown parser
|
|
3
|
+
*
|
|
4
|
+
* Parses plan files (flow/plans/plan_*.md) into structured PlanPhase objects.
|
|
5
|
+
* Extracts phase headers, complexity scores, dependencies, tasks, and verify tags.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
const PHASE_HEADER_RE = /^### Phase (\d+):\s*(.+)$/;
|
|
9
|
+
const COMPLEXITY_RE = /\*\*Complexity\*\*:\s*(\d+)\/10/;
|
|
10
|
+
const DEPENDENCIES_RE = /\*\*Dependencies\*\*:\s*(.+)/;
|
|
11
|
+
const TASK_RE = /^- \[[ x]\]\s+(.+)$/;
|
|
12
|
+
const VERIFY_RE = /<verify>(.+)<\/verify>/;
|
|
13
|
+
const PHASE_REF_RE = /Phase\s+(\d+)/gi;
|
|
14
|
+
const DEFAULT_COMPLEXITY = 5;
|
|
15
|
+
/**
|
|
16
|
+
* Parses a plan markdown file and returns an array of PlanPhase objects.
|
|
17
|
+
*/
|
|
18
|
+
export function parsePlan(planPath) {
|
|
19
|
+
const content = readFileSync(planPath, 'utf-8');
|
|
20
|
+
return parsePlanContent(content);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parses plan markdown content (for testability without file I/O).
|
|
24
|
+
*/
|
|
25
|
+
export function parsePlanContent(content) {
|
|
26
|
+
const phases = [];
|
|
27
|
+
const lines = content.split('\n');
|
|
28
|
+
let currentPhase = null;
|
|
29
|
+
let lastTaskIndex = -1;
|
|
30
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31
|
+
const line = lines[i];
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
// Phase header
|
|
34
|
+
const headerMatch = trimmed.match(PHASE_HEADER_RE);
|
|
35
|
+
if (headerMatch) {
|
|
36
|
+
// Save previous phase
|
|
37
|
+
if (currentPhase) {
|
|
38
|
+
phases.push(finalizePhase(currentPhase));
|
|
39
|
+
}
|
|
40
|
+
currentPhase = {
|
|
41
|
+
number: parseInt(headerMatch[1], 10),
|
|
42
|
+
name: headerMatch[2].trim(),
|
|
43
|
+
complexity: null,
|
|
44
|
+
dependencies: null,
|
|
45
|
+
tasks: [],
|
|
46
|
+
};
|
|
47
|
+
lastTaskIndex = -1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!currentPhase)
|
|
51
|
+
continue;
|
|
52
|
+
// Complexity
|
|
53
|
+
const complexityMatch = trimmed.match(COMPLEXITY_RE);
|
|
54
|
+
if (complexityMatch) {
|
|
55
|
+
currentPhase.complexity = parseInt(complexityMatch[1], 10);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Dependencies
|
|
59
|
+
const depsMatch = trimmed.match(DEPENDENCIES_RE);
|
|
60
|
+
if (depsMatch) {
|
|
61
|
+
const depsValue = depsMatch[1].trim();
|
|
62
|
+
if (/^none$/i.test(depsValue)) {
|
|
63
|
+
currentPhase.dependencies = [];
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const depNumbers = [];
|
|
67
|
+
let match;
|
|
68
|
+
// Reset regex state for each use
|
|
69
|
+
const phaseRefRe = /Phase\s+(\d+)/gi;
|
|
70
|
+
while ((match = phaseRefRe.exec(depsValue)) !== null) {
|
|
71
|
+
depNumbers.push(parseInt(match[1], 10));
|
|
72
|
+
}
|
|
73
|
+
currentPhase.dependencies = depNumbers.length > 0 ? depNumbers : [];
|
|
74
|
+
}
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// Task line
|
|
78
|
+
const taskMatch = trimmed.match(TASK_RE);
|
|
79
|
+
if (taskMatch) {
|
|
80
|
+
const taskName = taskMatch[1].trim();
|
|
81
|
+
currentPhase.tasks.push({ index: currentPhase.tasks.length + 1, name: taskName, verify_command: null });
|
|
82
|
+
lastTaskIndex = currentPhase.tasks.length - 1;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
// Verify tag (must follow a task line)
|
|
86
|
+
if (lastTaskIndex >= 0) {
|
|
87
|
+
const verifyMatch = trimmed.match(VERIFY_RE);
|
|
88
|
+
if (verifyMatch) {
|
|
89
|
+
currentPhase.tasks[lastTaskIndex].verify_command = verifyMatch[1].trim();
|
|
90
|
+
lastTaskIndex = -1;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// If non-empty, non-verify line after a task, reset lastTaskIndex
|
|
94
|
+
if (trimmed !== '') {
|
|
95
|
+
lastTaskIndex = -1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Save the last phase
|
|
100
|
+
if (currentPhase) {
|
|
101
|
+
phases.push(finalizePhase(currentPhase));
|
|
102
|
+
}
|
|
103
|
+
return phases;
|
|
104
|
+
}
|
|
105
|
+
function finalizePhase(phase) {
|
|
106
|
+
const complexity = phase.complexity ?? DEFAULT_COMPLEXITY;
|
|
107
|
+
// Dependencies: if explicitly set, use them; otherwise sequential fallback
|
|
108
|
+
let dependencies;
|
|
109
|
+
if (phase.dependencies !== null) {
|
|
110
|
+
dependencies = phase.dependencies;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Sequential fallback: depend on previous phase, Phase 1 has no deps
|
|
114
|
+
dependencies = phase.number > 1 ? [phase.number - 1] : [];
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
number: phase.number,
|
|
118
|
+
name: phase.name,
|
|
119
|
+
complexity,
|
|
120
|
+
dependencies,
|
|
121
|
+
tasks: phase.tasks,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=plan-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-parser.js","sourceRoot":"","sources":["../../../src/cli/state/plan-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session state checker — detects which session-start files exist
|
|
3
|
+
* in the flow directory, returning a structured SessionState object.
|
|
4
|
+
*/
|
|
5
|
+
import type { SessionState } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Checks existence of all session-start files and returns structured state.
|
|
8
|
+
*
|
|
9
|
+
* Files checked:
|
|
10
|
+
* - ledger.md
|
|
11
|
+
* - brain/index.md
|
|
12
|
+
* - tasklist.md
|
|
13
|
+
* - memory.md
|
|
14
|
+
* - .scratchpad.md
|
|
15
|
+
* - .heartbeat-events.jsonl
|
|
16
|
+
* - .heartbeat-state.json
|
|
17
|
+
* - .heartbeat-prompt.md
|
|
18
|
+
* - STATE.md
|
|
19
|
+
*/
|
|
20
|
+
export declare function getSessionState(flowDir: string): SessionState;
|
|
21
|
+
//# sourceMappingURL=session-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-state.d.ts","sourceRoot":"","sources":["../../../src/cli/state/session-state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAc7D"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session state checker — detects which session-start files exist
|
|
3
|
+
* in the flow directory, returning a structured SessionState object.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
/**
|
|
8
|
+
* Checks existence of all session-start files and returns structured state.
|
|
9
|
+
*
|
|
10
|
+
* Files checked:
|
|
11
|
+
* - ledger.md
|
|
12
|
+
* - brain/index.md
|
|
13
|
+
* - tasklist.md
|
|
14
|
+
* - memory.md
|
|
15
|
+
* - .scratchpad.md
|
|
16
|
+
* - .heartbeat-events.jsonl
|
|
17
|
+
* - .heartbeat-state.json
|
|
18
|
+
* - .heartbeat-prompt.md
|
|
19
|
+
* - STATE.md
|
|
20
|
+
*/
|
|
21
|
+
export function getSessionState(flowDir) {
|
|
22
|
+
return {
|
|
23
|
+
files_present: {
|
|
24
|
+
ledger: existsSync(join(flowDir, 'ledger.md')),
|
|
25
|
+
brain_index: existsSync(join(flowDir, 'brain', 'index.md')),
|
|
26
|
+
tasklist: existsSync(join(flowDir, 'tasklist.md')),
|
|
27
|
+
memory: existsSync(join(flowDir, 'memory.md')),
|
|
28
|
+
scratchpad: existsSync(join(flowDir, '.scratchpad.md')),
|
|
29
|
+
heartbeat_events: existsSync(join(flowDir, '.heartbeat-events.jsonl')),
|
|
30
|
+
heartbeat_state: existsSync(join(flowDir, '.heartbeat-state.json')),
|
|
31
|
+
heartbeat_prompt: existsSync(join(flowDir, '.heartbeat-prompt.md')),
|
|
32
|
+
state_md: existsSync(join(flowDir, 'STATE.md')),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=session-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-state.js","sourceRoot":"","sources":["../../../src/cli/state/session-state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO;QACL,aAAa,EAAE;YACb,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YAC9C,WAAW,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YAC3D,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;YAClD,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YAC9C,UAAU,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;YACvD,gBAAgB,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,yBAAyB,CAAC,CAAC;YACtE,eAAe,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;YACnE,gBAAgB,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;YACnE,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;SAChD;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STATE.md Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses `flow/STATE.md` into a structured ExecutionState object.
|
|
5
|
+
* Uses regex-based line parsing consistent with flowconfig-parser and plan-parser patterns.
|
|
6
|
+
* Returns null if STATE.md doesn't exist.
|
|
7
|
+
*/
|
|
8
|
+
import type { ExecutionState } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Parse `flow/STATE.md` into a structured ExecutionState object.
|
|
11
|
+
* Returns null if STATE.md doesn't exist.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseStateMd(flowDir: string): ExecutionState | null;
|
|
14
|
+
/**
|
|
15
|
+
* Parse STATE.md content string (for testability without file I/O).
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseStateMdContent(content: string): ExecutionState;
|
|
18
|
+
//# sourceMappingURL=state-md-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-md-parser.d.ts","sourceRoot":"","sources":["../../../src/cli/state/state-md-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAqC,cAAc,EAAE,MAAM,YAAY,CAAC;AASpF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAenE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAuEnE"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STATE.md Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses `flow/STATE.md` into a structured ExecutionState object.
|
|
5
|
+
* Uses regex-based line parsing consistent with flowconfig-parser and plan-parser patterns.
|
|
6
|
+
* Returns null if STATE.md doesn't exist.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
const SECTION_RE = /^## (.+)$/;
|
|
11
|
+
const BOLD_KV_RE = /^- \*\*(.+?)\*\*:\s*(.*)$/;
|
|
12
|
+
const BULLET_RE = /^- (.+)$/;
|
|
13
|
+
const UPDATED_RE = /^\*\*Updated\*\*:\s*(.+)$/;
|
|
14
|
+
const COMPLETED_PHASE_RE = /^Phase (\d+):\s*(.+?)\s*—\s*(.+)$/;
|
|
15
|
+
const CURRENT_PHASE_RE = /^(\d+)\s*—\s*(.+)$/;
|
|
16
|
+
/**
|
|
17
|
+
* Parse `flow/STATE.md` into a structured ExecutionState object.
|
|
18
|
+
* Returns null if STATE.md doesn't exist.
|
|
19
|
+
*/
|
|
20
|
+
export function parseStateMd(flowDir) {
|
|
21
|
+
const statePath = join(flowDir, 'STATE.md');
|
|
22
|
+
if (!existsSync(statePath)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
let content;
|
|
26
|
+
try {
|
|
27
|
+
content = readFileSync(statePath, 'utf-8');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return parseStateMdContent(content);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Parse STATE.md content string (for testability without file I/O).
|
|
36
|
+
*/
|
|
37
|
+
export function parseStateMdContent(content) {
|
|
38
|
+
const state = {
|
|
39
|
+
active_skill: null,
|
|
40
|
+
active_plan: null,
|
|
41
|
+
current_phase: null,
|
|
42
|
+
current_task: null,
|
|
43
|
+
completed_phases: [],
|
|
44
|
+
decisions: [],
|
|
45
|
+
blockers: [],
|
|
46
|
+
files_modified: [],
|
|
47
|
+
next_action: null,
|
|
48
|
+
updated_at: null,
|
|
49
|
+
};
|
|
50
|
+
const lines = content.split('\n');
|
|
51
|
+
let currentSection = '';
|
|
52
|
+
let inCompletedPhases = false;
|
|
53
|
+
for (let i = 0; i < lines.length; i++) {
|
|
54
|
+
const line = lines[i];
|
|
55
|
+
const trimmed = line.trim();
|
|
56
|
+
// Updated timestamp (outside sections)
|
|
57
|
+
const updatedMatch = trimmed.match(UPDATED_RE);
|
|
58
|
+
if (updatedMatch) {
|
|
59
|
+
state.updated_at = updatedMatch[1].trim();
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// Section header
|
|
63
|
+
const sectionMatch = trimmed.match(SECTION_RE);
|
|
64
|
+
if (sectionMatch) {
|
|
65
|
+
currentSection = sectionMatch[1].trim();
|
|
66
|
+
inCompletedPhases = false;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (currentSection === 'Execution State') {
|
|
70
|
+
parseExecutionStateLine(trimmed, state, inCompletedPhases);
|
|
71
|
+
// Track if we just entered completed phases sub-list
|
|
72
|
+
const kvMatch = trimmed.match(BOLD_KV_RE);
|
|
73
|
+
if (kvMatch && kvMatch[1] === 'Completed Phases') {
|
|
74
|
+
inCompletedPhases = true;
|
|
75
|
+
}
|
|
76
|
+
else if (kvMatch && kvMatch[1] !== 'Completed Phases') {
|
|
77
|
+
inCompletedPhases = false;
|
|
78
|
+
}
|
|
79
|
+
// Indented bullets under Completed Phases
|
|
80
|
+
if (inCompletedPhases && line.match(/^\s{2,}- /)) {
|
|
81
|
+
const indentedContent = trimmed.replace(/^- /, '');
|
|
82
|
+
const phaseMatch = indentedContent.match(COMPLETED_PHASE_RE);
|
|
83
|
+
if (phaseMatch) {
|
|
84
|
+
state.completed_phases.push({
|
|
85
|
+
number: parseInt(phaseMatch[1], 10),
|
|
86
|
+
name: phaseMatch[2].trim(),
|
|
87
|
+
outcome: phaseMatch[3].trim(),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (currentSection === 'Decisions') {
|
|
93
|
+
parseDecisionLine(trimmed, state);
|
|
94
|
+
}
|
|
95
|
+
else if (currentSection === 'Blockers') {
|
|
96
|
+
parseBlockerLine(trimmed, state);
|
|
97
|
+
}
|
|
98
|
+
else if (currentSection === 'Files Modified') {
|
|
99
|
+
parseFileModifiedLine(trimmed, state);
|
|
100
|
+
}
|
|
101
|
+
else if (currentSection === 'Next Action') {
|
|
102
|
+
parseNextActionLine(trimmed, state);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return state;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Parse a line within the Execution State section.
|
|
109
|
+
*/
|
|
110
|
+
function parseExecutionStateLine(trimmed, state, inCompletedPhases) {
|
|
111
|
+
if (inCompletedPhases)
|
|
112
|
+
return;
|
|
113
|
+
const kvMatch = trimmed.match(BOLD_KV_RE);
|
|
114
|
+
if (!kvMatch)
|
|
115
|
+
return;
|
|
116
|
+
const key = kvMatch[1].trim();
|
|
117
|
+
const value = kvMatch[2].trim();
|
|
118
|
+
switch (key) {
|
|
119
|
+
case 'Active Skill':
|
|
120
|
+
state.active_skill = value;
|
|
121
|
+
break;
|
|
122
|
+
case 'Active Plan':
|
|
123
|
+
state.active_plan = value;
|
|
124
|
+
break;
|
|
125
|
+
case 'Current Phase': {
|
|
126
|
+
const phaseMatch = value.match(CURRENT_PHASE_RE);
|
|
127
|
+
if (phaseMatch) {
|
|
128
|
+
state.current_phase = {
|
|
129
|
+
number: parseInt(phaseMatch[1], 10),
|
|
130
|
+
name: phaseMatch[2].trim(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case 'Current Task':
|
|
136
|
+
state.current_task = value;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Parse a decision bullet line.
|
|
142
|
+
* Format: "- Chose X over Y (reason: Z)" or "- Decision text (reason: explanation)"
|
|
143
|
+
*/
|
|
144
|
+
function parseDecisionLine(trimmed, state) {
|
|
145
|
+
const bulletMatch = trimmed.match(BULLET_RE);
|
|
146
|
+
if (!bulletMatch)
|
|
147
|
+
return;
|
|
148
|
+
const text = bulletMatch[1].trim();
|
|
149
|
+
const reasonMatch = text.match(/^(.+?)\s*\(reason:\s*(.+?)\)$/);
|
|
150
|
+
if (reasonMatch) {
|
|
151
|
+
const decisionText = reasonMatch[1].trim();
|
|
152
|
+
// Try to extract "Chose X over Y" pattern
|
|
153
|
+
const choseMatch = decisionText.match(/^(?:Chose|Used)\s+(.+?)(?:\s+(?:over|for|instead of)\s+(.+))?$/i);
|
|
154
|
+
if (choseMatch) {
|
|
155
|
+
state.decisions.push({
|
|
156
|
+
decision: decisionText,
|
|
157
|
+
choice: choseMatch[1].trim(),
|
|
158
|
+
reason: reasonMatch[2].trim(),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
state.decisions.push({
|
|
163
|
+
decision: decisionText,
|
|
164
|
+
choice: decisionText,
|
|
165
|
+
reason: reasonMatch[2].trim(),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
state.decisions.push({
|
|
171
|
+
decision: text,
|
|
172
|
+
choice: text,
|
|
173
|
+
reason: '',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Parse a blocker bullet line.
|
|
179
|
+
* Format: "- Issue description (status: X, tried: Y)"
|
|
180
|
+
*/
|
|
181
|
+
function parseBlockerLine(trimmed, state) {
|
|
182
|
+
const bulletMatch = trimmed.match(BULLET_RE);
|
|
183
|
+
if (!bulletMatch)
|
|
184
|
+
return;
|
|
185
|
+
const text = bulletMatch[1].trim();
|
|
186
|
+
const detailsMatch = text.match(/^(.+?)\s*\(status:\s*(.+?),\s*tried:\s*(.+?)\)$/);
|
|
187
|
+
if (detailsMatch) {
|
|
188
|
+
state.blockers.push({
|
|
189
|
+
issue: detailsMatch[1].trim(),
|
|
190
|
+
status: detailsMatch[2].trim(),
|
|
191
|
+
tried: detailsMatch[3].trim(),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
state.blockers.push({
|
|
196
|
+
issue: text,
|
|
197
|
+
status: 'unknown',
|
|
198
|
+
tried: '',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Parse a file path bullet line.
|
|
204
|
+
*/
|
|
205
|
+
function parseFileModifiedLine(trimmed, state) {
|
|
206
|
+
const bulletMatch = trimmed.match(BULLET_RE);
|
|
207
|
+
if (!bulletMatch)
|
|
208
|
+
return;
|
|
209
|
+
const filePath = bulletMatch[1].trim();
|
|
210
|
+
if (filePath) {
|
|
211
|
+
state.files_modified.push(filePath);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Parse the next action line (first non-empty line in the section).
|
|
216
|
+
*/
|
|
217
|
+
function parseNextActionLine(trimmed, state) {
|
|
218
|
+
if (trimmed && !state.next_action) {
|
|
219
|
+
state.next_action = trimmed;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=state-md-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-md-parser.js","sourceRoot":"","sources":["../../../src/cli/state/state-md-parser.ts"],"names":[],"mappings":"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"}
|