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,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the deterministic state script (`planflow-ai state`).
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the JSON output shape produced by parsing
|
|
5
|
+
* plan-flow config files, session state, heartbeat events, and plans.
|
|
6
|
+
*/
|
|
7
|
+
export interface FlowConfig {
|
|
8
|
+
autopilot: boolean;
|
|
9
|
+
commit: boolean;
|
|
10
|
+
push: boolean;
|
|
11
|
+
pr: boolean;
|
|
12
|
+
branch: string;
|
|
13
|
+
wave_execution: boolean;
|
|
14
|
+
phase_isolation: boolean;
|
|
15
|
+
model_routing: boolean;
|
|
16
|
+
max_verify_retries: number;
|
|
17
|
+
}
|
|
18
|
+
export interface SessionState {
|
|
19
|
+
files_present: {
|
|
20
|
+
ledger: boolean;
|
|
21
|
+
brain_index: boolean;
|
|
22
|
+
tasklist: boolean;
|
|
23
|
+
memory: boolean;
|
|
24
|
+
scratchpad: boolean;
|
|
25
|
+
heartbeat_events: boolean;
|
|
26
|
+
heartbeat_state: boolean;
|
|
27
|
+
heartbeat_prompt: boolean;
|
|
28
|
+
state_md: boolean;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface HeartbeatSummary {
|
|
32
|
+
unread_count: number;
|
|
33
|
+
has_prompt: boolean;
|
|
34
|
+
last_read_timestamp: string | null;
|
|
35
|
+
}
|
|
36
|
+
export interface TaskCompletion {
|
|
37
|
+
task_number: number;
|
|
38
|
+
task_name: string;
|
|
39
|
+
files_created: string[];
|
|
40
|
+
files_modified: string[];
|
|
41
|
+
}
|
|
42
|
+
export interface PlanTask {
|
|
43
|
+
index: number;
|
|
44
|
+
name: string;
|
|
45
|
+
verify_command: string | null;
|
|
46
|
+
}
|
|
47
|
+
export interface PlanPhase {
|
|
48
|
+
number: number;
|
|
49
|
+
name: string;
|
|
50
|
+
complexity: number;
|
|
51
|
+
dependencies: number[];
|
|
52
|
+
tasks: PlanTask[];
|
|
53
|
+
}
|
|
54
|
+
export interface WaveGroup {
|
|
55
|
+
wave_number: number;
|
|
56
|
+
phase_numbers: number[];
|
|
57
|
+
}
|
|
58
|
+
export type ModelTierLevel = 'fast' | 'standard' | 'powerful';
|
|
59
|
+
export interface ModelTier {
|
|
60
|
+
phase: number;
|
|
61
|
+
complexity: number;
|
|
62
|
+
tier: ModelTierLevel;
|
|
63
|
+
model: string;
|
|
64
|
+
}
|
|
65
|
+
export interface CompletedPhase {
|
|
66
|
+
number: number;
|
|
67
|
+
name: string;
|
|
68
|
+
outcome: string;
|
|
69
|
+
}
|
|
70
|
+
export interface Decision {
|
|
71
|
+
decision: string;
|
|
72
|
+
choice: string;
|
|
73
|
+
reason: string;
|
|
74
|
+
}
|
|
75
|
+
export interface Blocker {
|
|
76
|
+
issue: string;
|
|
77
|
+
status: string;
|
|
78
|
+
tried: string;
|
|
79
|
+
}
|
|
80
|
+
export interface ExecutionState {
|
|
81
|
+
active_skill: string | null;
|
|
82
|
+
active_plan: string | null;
|
|
83
|
+
current_phase: {
|
|
84
|
+
number: number;
|
|
85
|
+
name: string;
|
|
86
|
+
} | null;
|
|
87
|
+
current_task: string | null;
|
|
88
|
+
completed_phases: CompletedPhase[];
|
|
89
|
+
decisions: Decision[];
|
|
90
|
+
blockers: Blocker[];
|
|
91
|
+
files_modified: string[];
|
|
92
|
+
next_action: string | null;
|
|
93
|
+
updated_at: string | null;
|
|
94
|
+
}
|
|
95
|
+
export interface StateOutput {
|
|
96
|
+
config: FlowConfig;
|
|
97
|
+
session: SessionState;
|
|
98
|
+
heartbeat: HeartbeatSummary;
|
|
99
|
+
execution?: ExecutionState;
|
|
100
|
+
plan?: {
|
|
101
|
+
phases: PlanPhase[];
|
|
102
|
+
waves: WaveGroup[];
|
|
103
|
+
model_tiers: ModelTier[];
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/cli/state/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the deterministic state script (`planflow-ai state`).
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the JSON output shape produced by parsing
|
|
5
|
+
* plan-flow config files, session state, heartbeat events, and plans.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/cli/state/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave calculator — topological sort of phase dependencies into wave groups.
|
|
3
|
+
*
|
|
4
|
+
* Phases with no dependencies land in wave 1. Each subsequent phase lands in
|
|
5
|
+
* wave = max(wave of each dependency) + 1. A "Tests" phase (last phase whose
|
|
6
|
+
* name contains "test", case-insensitive) is always isolated in the final wave.
|
|
7
|
+
*
|
|
8
|
+
* If a circular dependency is detected the fallback is sequential execution:
|
|
9
|
+
* one phase per wave in phase-number order.
|
|
10
|
+
*/
|
|
11
|
+
import type { PlanPhase, WaveGroup } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Calculate wave groups from plan phases.
|
|
14
|
+
*
|
|
15
|
+
* @returns Sorted array of WaveGroup objects.
|
|
16
|
+
*/
|
|
17
|
+
export declare function calculateWaves(phases: PlanPhase[]): WaveGroup[];
|
|
18
|
+
//# sourceMappingURL=wave-calculator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wave-calculator.d.ts","sourceRoot":"","sources":["../../../src/cli/state/wave-calculator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA6CvD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAuF/D"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave calculator — topological sort of phase dependencies into wave groups.
|
|
3
|
+
*
|
|
4
|
+
* Phases with no dependencies land in wave 1. Each subsequent phase lands in
|
|
5
|
+
* wave = max(wave of each dependency) + 1. A "Tests" phase (last phase whose
|
|
6
|
+
* name contains "test", case-insensitive) is always isolated in the final wave.
|
|
7
|
+
*
|
|
8
|
+
* If a circular dependency is detected the fallback is sequential execution:
|
|
9
|
+
* one phase per wave in phase-number order.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Detect whether the dependency graph contains a cycle using iterative DFS.
|
|
13
|
+
*/
|
|
14
|
+
function hasCycle(phaseNumbers, adjacency) {
|
|
15
|
+
const WHITE = 0;
|
|
16
|
+
const GRAY = 1;
|
|
17
|
+
const BLACK = 2;
|
|
18
|
+
const color = new Map();
|
|
19
|
+
for (const n of phaseNumbers)
|
|
20
|
+
color.set(n, WHITE);
|
|
21
|
+
for (const start of phaseNumbers) {
|
|
22
|
+
if (color.get(start) !== WHITE)
|
|
23
|
+
continue;
|
|
24
|
+
const stack = [
|
|
25
|
+
{ node: start, idx: 0 },
|
|
26
|
+
];
|
|
27
|
+
color.set(start, GRAY);
|
|
28
|
+
while (stack.length > 0) {
|
|
29
|
+
const top = stack[stack.length - 1];
|
|
30
|
+
const neighbors = adjacency.get(top.node) ?? [];
|
|
31
|
+
if (top.idx < neighbors.length) {
|
|
32
|
+
const neighbor = neighbors[top.idx];
|
|
33
|
+
top.idx++;
|
|
34
|
+
const c = color.get(neighbor);
|
|
35
|
+
if (c === GRAY)
|
|
36
|
+
return true; // back-edge → cycle
|
|
37
|
+
if (c === WHITE) {
|
|
38
|
+
color.set(neighbor, GRAY);
|
|
39
|
+
stack.push({ node: neighbor, idx: 0 });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
color.set(top.node, BLACK);
|
|
44
|
+
stack.pop();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Calculate wave groups from plan phases.
|
|
52
|
+
*
|
|
53
|
+
* @returns Sorted array of WaveGroup objects.
|
|
54
|
+
*/
|
|
55
|
+
export function calculateWaves(phases) {
|
|
56
|
+
if (phases.length === 0)
|
|
57
|
+
return [];
|
|
58
|
+
// Build adjacency list (phase → phases it depends on, i.e. forward edges
|
|
59
|
+
// point from a phase to its dependencies for the purpose of cycle detection).
|
|
60
|
+
// For cycle detection we need "phase → successors" (who depends on me).
|
|
61
|
+
const phaseNumbers = phases.map((p) => p.number);
|
|
62
|
+
const depMap = new Map();
|
|
63
|
+
const successorMap = new Map();
|
|
64
|
+
for (const p of phases) {
|
|
65
|
+
depMap.set(p.number, [...p.dependencies]);
|
|
66
|
+
if (!successorMap.has(p.number))
|
|
67
|
+
successorMap.set(p.number, []);
|
|
68
|
+
for (const dep of p.dependencies) {
|
|
69
|
+
if (!successorMap.has(dep))
|
|
70
|
+
successorMap.set(dep, []);
|
|
71
|
+
successorMap.get(dep).push(p.number);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Cycle detection — use successor adjacency for DFS.
|
|
75
|
+
if (hasCycle(phaseNumbers, successorMap)) {
|
|
76
|
+
// Fallback: sequential, one phase per wave.
|
|
77
|
+
const sorted = [...phaseNumbers].sort((a, b) => a - b);
|
|
78
|
+
return sorted.map((n, i) => ({
|
|
79
|
+
wave_number: i + 1,
|
|
80
|
+
phase_numbers: [n],
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
// Assign wave numbers iteratively.
|
|
84
|
+
const waveOf = new Map();
|
|
85
|
+
const phaseSet = new Set(phaseNumbers);
|
|
86
|
+
// Iterative assignment: keep going until all phases are assigned.
|
|
87
|
+
let changed = true;
|
|
88
|
+
while (changed) {
|
|
89
|
+
changed = false;
|
|
90
|
+
for (const p of phases) {
|
|
91
|
+
if (waveOf.has(p.number))
|
|
92
|
+
continue;
|
|
93
|
+
// Filter dependencies to only those that are actual phases in the plan.
|
|
94
|
+
const deps = (depMap.get(p.number) ?? []).filter((d) => phaseSet.has(d));
|
|
95
|
+
if (deps.length === 0) {
|
|
96
|
+
waveOf.set(p.number, 1);
|
|
97
|
+
changed = true;
|
|
98
|
+
}
|
|
99
|
+
else if (deps.every((d) => waveOf.has(d))) {
|
|
100
|
+
const maxDepWave = Math.max(...deps.map((d) => waveOf.get(d)));
|
|
101
|
+
waveOf.set(p.number, maxDepWave + 1);
|
|
102
|
+
changed = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Detect Tests phase: last phase by number whose name contains "test".
|
|
107
|
+
const sortedPhases = [...phases].sort((a, b) => a.number - b.number);
|
|
108
|
+
const lastPhase = sortedPhases[sortedPhases.length - 1];
|
|
109
|
+
const isTestsPhase = lastPhase && /test/i.test(lastPhase.name) ? lastPhase.number : null;
|
|
110
|
+
// If the Tests phase shares a wave with other phases, move it to its own final wave.
|
|
111
|
+
if (isTestsPhase !== null) {
|
|
112
|
+
const testWave = waveOf.get(isTestsPhase);
|
|
113
|
+
const phasesInSameWave = Array.from(waveOf.entries()).filter(([num, w]) => w === testWave && num !== isTestsPhase);
|
|
114
|
+
if (phasesInSameWave.length > 0) {
|
|
115
|
+
// Move Tests to max_wave + 1
|
|
116
|
+
const maxWave = Math.max(...Array.from(waveOf.values()));
|
|
117
|
+
waveOf.set(isTestsPhase, maxWave + 1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Group phases by wave number.
|
|
121
|
+
const waveMap = new Map();
|
|
122
|
+
for (const [phaseNum, wave] of Array.from(waveOf.entries())) {
|
|
123
|
+
if (!waveMap.has(wave))
|
|
124
|
+
waveMap.set(wave, []);
|
|
125
|
+
waveMap.get(wave).push(phaseNum);
|
|
126
|
+
}
|
|
127
|
+
// Build sorted WaveGroup array.
|
|
128
|
+
const waveNumbers = Array.from(waveMap.keys()).sort((a, b) => a - b);
|
|
129
|
+
return waveNumbers.map((wn) => ({
|
|
130
|
+
wave_number: wn,
|
|
131
|
+
phase_numbers: waveMap.get(wn).sort((a, b) => a - b),
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=wave-calculator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wave-calculator.js","sourceRoot":"","sources":["../../../src/cli/state/wave-calculator.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -45,4 +45,19 @@ export interface HeartbeatTask {
|
|
|
45
45
|
export interface HeartbeatOptions {
|
|
46
46
|
target: string;
|
|
47
47
|
}
|
|
48
|
+
export type NotificationLevel = 'info' | 'warn' | 'error';
|
|
49
|
+
export type NotificationType = 'task_started' | 'phase_complete' | 'task_complete' | 'task_failed' | 'task_blocked';
|
|
50
|
+
export interface NotificationEvent {
|
|
51
|
+
id: string;
|
|
52
|
+
timestamp: Date;
|
|
53
|
+
task: string;
|
|
54
|
+
type: NotificationType;
|
|
55
|
+
level: NotificationLevel;
|
|
56
|
+
phase?: string;
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
export interface HeartbeatState {
|
|
60
|
+
lastReadTimestamp: string;
|
|
61
|
+
lastSessionId?: string;
|
|
62
|
+
}
|
|
48
63
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/cli/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAE9E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAE9E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1D,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,gBAAgB,GAChB,eAAe,GACf,aAAa,GACb,cAAc,CAAC;AAEnB,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "planflow-ai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Structured AI-assisted development workflows for discovery, planning, execution, code reviews, and testing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli/index.js",
|
|
@@ -57,11 +57,13 @@
|
|
|
57
57
|
"author": "Bruno Cardoso",
|
|
58
58
|
"license": "MIT",
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"commander": "^14.0.3"
|
|
60
|
+
"commander": "^14.0.3",
|
|
61
|
+
"node-notifier": "^10.0.1"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@types/jest": "^29.5.14",
|
|
64
65
|
"@types/node": "^22.10.5",
|
|
66
|
+
"@types/node-notifier": "^8.0.5",
|
|
65
67
|
"jest": "^29.7.0",
|
|
66
68
|
"prettier": "^3.4.2",
|
|
67
69
|
"ts-jest": "^29.2.5",
|
|
@@ -90,6 +90,10 @@ During skill execution, the LLM silently buffers patterns and anti-patterns, pre
|
|
|
90
90
|
|
|
91
91
|
When `phase_isolation: true` in `flow/.flowconfig` (default), each `/execute-plan` phase runs in an isolated Agent sub-agent with a clean context window. Sub-agent receives focused context (phase spec, file list, patterns) and returns structured JSON summary. Eliminates context rot. Disable with `/flow phase_isolation=false`. See `.claude/resources/core/phase-isolation.md`.
|
|
92
92
|
|
|
93
|
+
## Per-Task Verification
|
|
94
|
+
|
|
95
|
+
Tasks in plan phases can include optional `<verify>` sections with targeted verification commands. After each task, verification runs immediately. Failed verifications trigger a debug sub-agent (haiku) for diagnosis, followed by an auto-repair loop (up to `max_verify_retries`, default: 2, configurable in `flow/.flowconfig`). `/create-plan` auto-generates `<verify>` sections based on task type heuristics. Backward compatible — tasks without `<verify>` skip verification. See `.claude/resources/core/per-task-verification.md`.
|
|
96
|
+
|
|
93
97
|
## Discovery Sub-Agents
|
|
94
98
|
|
|
95
99
|
During `/discovery-plan`, three parallel haiku sub-agents explore the codebase (similar features, API/data patterns, schema/types). Returns condensed JSON findings merged into a Codebase Analysis section. Always-on. See `.claude/resources/core/discovery-sub-agents.md`.
|