wogiflow 1.0.0
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/.workflow/agents/reviewer.md +81 -0
- package/.workflow/agents/security.md +94 -0
- package/.workflow/agents/story-writer.md +58 -0
- package/.workflow/bridges/base-bridge.js +395 -0
- package/.workflow/bridges/claude-bridge.js +434 -0
- package/.workflow/bridges/index.js +130 -0
- package/.workflow/lib/assumption-detector.js +481 -0
- package/.workflow/lib/config-substitution.js +371 -0
- package/.workflow/lib/failure-categories.js +478 -0
- package/.workflow/state/app-map.md.template +15 -0
- package/.workflow/state/architecture.md.template +24 -0
- package/.workflow/state/component-index.json.template +5 -0
- package/.workflow/state/decisions.md.template +15 -0
- package/.workflow/state/feedback-patterns.md.template +9 -0
- package/.workflow/state/knowledge-sync.json.template +6 -0
- package/.workflow/state/progress.md.template +14 -0
- package/.workflow/state/ready.json.template +7 -0
- package/.workflow/state/request-log.md.template +14 -0
- package/.workflow/state/session-state.json.template +11 -0
- package/.workflow/state/stack.md.template +33 -0
- package/.workflow/state/testing.md.template +36 -0
- package/.workflow/templates/claude-md.hbs +257 -0
- package/.workflow/templates/correction-report.md +67 -0
- package/.workflow/templates/gemini-md.hbs +52 -0
- package/README.md +1802 -0
- package/bin/flow +205 -0
- package/lib/index.js +33 -0
- package/lib/installer.js +467 -0
- package/lib/release-channel.js +269 -0
- package/lib/skill-registry.js +526 -0
- package/lib/upgrader.js +401 -0
- package/lib/utils.js +305 -0
- package/package.json +64 -0
- package/scripts/flow +985 -0
- package/scripts/flow-adaptive-learning.js +1259 -0
- package/scripts/flow-aggregate.js +488 -0
- package/scripts/flow-archive +133 -0
- package/scripts/flow-auto-context.js +1015 -0
- package/scripts/flow-auto-learn.js +615 -0
- package/scripts/flow-bridge.js +223 -0
- package/scripts/flow-browser-suggest.js +316 -0
- package/scripts/flow-bug.js +247 -0
- package/scripts/flow-cascade.js +711 -0
- package/scripts/flow-changelog +85 -0
- package/scripts/flow-checkpoint.js +483 -0
- package/scripts/flow-cli.js +403 -0
- package/scripts/flow-code-intelligence.js +760 -0
- package/scripts/flow-complexity.js +502 -0
- package/scripts/flow-config-set.js +152 -0
- package/scripts/flow-constants.js +157 -0
- package/scripts/flow-context +152 -0
- package/scripts/flow-context-init.js +482 -0
- package/scripts/flow-context-monitor.js +384 -0
- package/scripts/flow-context-scoring.js +886 -0
- package/scripts/flow-correct.js +458 -0
- package/scripts/flow-damage-control.js +985 -0
- package/scripts/flow-deps +101 -0
- package/scripts/flow-diff.js +700 -0
- package/scripts/flow-done +151 -0
- package/scripts/flow-done.js +489 -0
- package/scripts/flow-durable-session.js +1541 -0
- package/scripts/flow-entropy-monitor.js +345 -0
- package/scripts/flow-export-profile +349 -0
- package/scripts/flow-export-scanner.js +1046 -0
- package/scripts/flow-figma-confirm.js +400 -0
- package/scripts/flow-figma-extract.js +496 -0
- package/scripts/flow-figma-generate.js +683 -0
- package/scripts/flow-figma-index.js +909 -0
- package/scripts/flow-figma-match.js +617 -0
- package/scripts/flow-figma-mcp-server.js +518 -0
- package/scripts/flow-figma-pipeline.js +414 -0
- package/scripts/flow-file-ops.js +301 -0
- package/scripts/flow-gate-confidence.js +825 -0
- package/scripts/flow-guided-edit.js +659 -0
- package/scripts/flow-health +185 -0
- package/scripts/flow-health.js +413 -0
- package/scripts/flow-hooks.js +556 -0
- package/scripts/flow-http-client.js +249 -0
- package/scripts/flow-hybrid-detect.js +167 -0
- package/scripts/flow-hybrid-interactive.js +591 -0
- package/scripts/flow-hybrid-test.js +152 -0
- package/scripts/flow-import-profile +439 -0
- package/scripts/flow-init +253 -0
- package/scripts/flow-instruction-richness.js +827 -0
- package/scripts/flow-jira-integration.js +579 -0
- package/scripts/flow-knowledge-router.js +522 -0
- package/scripts/flow-knowledge-sync.js +589 -0
- package/scripts/flow-linear-integration.js +631 -0
- package/scripts/flow-links.js +774 -0
- package/scripts/flow-log-manager.js +559 -0
- package/scripts/flow-loop-enforcer.js +1246 -0
- package/scripts/flow-loop-retry-learning.js +630 -0
- package/scripts/flow-lsp.js +923 -0
- package/scripts/flow-map-index +348 -0
- package/scripts/flow-map-sync +201 -0
- package/scripts/flow-memory-blocks.js +668 -0
- package/scripts/flow-memory-compactor.js +350 -0
- package/scripts/flow-memory-db.js +1110 -0
- package/scripts/flow-memory-sync.js +484 -0
- package/scripts/flow-metrics.js +353 -0
- package/scripts/flow-migrate-ids.js +370 -0
- package/scripts/flow-model-adapter.js +802 -0
- package/scripts/flow-model-router.js +884 -0
- package/scripts/flow-models.js +1231 -0
- package/scripts/flow-morning.js +517 -0
- package/scripts/flow-multi-approach.js +660 -0
- package/scripts/flow-new-feature +86 -0
- package/scripts/flow-onboard +1042 -0
- package/scripts/flow-orchestrate-llm.js +459 -0
- package/scripts/flow-orchestrate.js +3592 -0
- package/scripts/flow-output.js +123 -0
- package/scripts/flow-parallel-detector.js +399 -0
- package/scripts/flow-parallel-dispatch.js +987 -0
- package/scripts/flow-parallel.js +428 -0
- package/scripts/flow-pattern-enforcer.js +600 -0
- package/scripts/flow-prd-manager.js +282 -0
- package/scripts/flow-progress.js +323 -0
- package/scripts/flow-project-analyzer.js +975 -0
- package/scripts/flow-prompt-composer.js +487 -0
- package/scripts/flow-providers.js +1381 -0
- package/scripts/flow-queue.js +308 -0
- package/scripts/flow-ready +82 -0
- package/scripts/flow-ready.js +189 -0
- package/scripts/flow-regression.js +396 -0
- package/scripts/flow-response-parser.js +450 -0
- package/scripts/flow-resume.js +284 -0
- package/scripts/flow-rules-sync.js +439 -0
- package/scripts/flow-run-trace.js +718 -0
- package/scripts/flow-safety.js +587 -0
- package/scripts/flow-search +104 -0
- package/scripts/flow-security.js +481 -0
- package/scripts/flow-session-end +106 -0
- package/scripts/flow-session-end.js +437 -0
- package/scripts/flow-session-state.js +671 -0
- package/scripts/flow-setup-hooks +216 -0
- package/scripts/flow-setup-hooks.js +377 -0
- package/scripts/flow-skill-create.js +329 -0
- package/scripts/flow-skill-creator.js +572 -0
- package/scripts/flow-skill-generator.js +1046 -0
- package/scripts/flow-skill-learn.js +880 -0
- package/scripts/flow-skill-matcher.js +578 -0
- package/scripts/flow-spec-generator.js +820 -0
- package/scripts/flow-stack-wizard.js +895 -0
- package/scripts/flow-standup +162 -0
- package/scripts/flow-start +74 -0
- package/scripts/flow-start.js +235 -0
- package/scripts/flow-status +110 -0
- package/scripts/flow-status.js +301 -0
- package/scripts/flow-step-browser.js +83 -0
- package/scripts/flow-step-changelog.js +217 -0
- package/scripts/flow-step-comments.js +306 -0
- package/scripts/flow-step-complexity.js +234 -0
- package/scripts/flow-step-coverage.js +218 -0
- package/scripts/flow-step-knowledge.js +193 -0
- package/scripts/flow-step-pr-tests.js +364 -0
- package/scripts/flow-step-regression.js +89 -0
- package/scripts/flow-step-review.js +516 -0
- package/scripts/flow-step-security.js +162 -0
- package/scripts/flow-step-silent-failures.js +290 -0
- package/scripts/flow-step-simplifier.js +346 -0
- package/scripts/flow-story +105 -0
- package/scripts/flow-story.js +500 -0
- package/scripts/flow-suspend.js +252 -0
- package/scripts/flow-sync-daemon.js +654 -0
- package/scripts/flow-task-analyzer.js +606 -0
- package/scripts/flow-team-dashboard.js +748 -0
- package/scripts/flow-team-sync.js +752 -0
- package/scripts/flow-team.js +977 -0
- package/scripts/flow-tech-options.js +528 -0
- package/scripts/flow-templates.js +812 -0
- package/scripts/flow-tiered-learning.js +728 -0
- package/scripts/flow-trace +204 -0
- package/scripts/flow-transcript-chunking.js +1106 -0
- package/scripts/flow-transcript-digest.js +7918 -0
- package/scripts/flow-transcript-language.js +465 -0
- package/scripts/flow-transcript-parsing.js +1085 -0
- package/scripts/flow-transcript-stories.js +2194 -0
- package/scripts/flow-update-map +224 -0
- package/scripts/flow-utils.js +2242 -0
- package/scripts/flow-verification.js +644 -0
- package/scripts/flow-verify.js +1177 -0
- package/scripts/flow-voice-input.js +638 -0
- package/scripts/flow-watch +168 -0
- package/scripts/flow-workflow-steps.js +521 -0
- package/scripts/flow-workflow.js +1029 -0
- package/scripts/flow-worktree.js +489 -0
- package/scripts/hooks/adapters/base-adapter.js +102 -0
- package/scripts/hooks/adapters/claude-code.js +359 -0
- package/scripts/hooks/adapters/index.js +79 -0
- package/scripts/hooks/core/component-check.js +341 -0
- package/scripts/hooks/core/index.js +35 -0
- package/scripts/hooks/core/loop-check.js +241 -0
- package/scripts/hooks/core/session-context.js +294 -0
- package/scripts/hooks/core/task-gate.js +177 -0
- package/scripts/hooks/core/validation.js +230 -0
- package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
- package/scripts/hooks/entry/claude-code/session-end.js +87 -0
- package/scripts/hooks/entry/claude-code/session-start.js +46 -0
- package/scripts/hooks/entry/claude-code/stop.js +43 -0
- package/scripts/postinstall.js +139 -0
- package/templates/browser-test-flow.json +56 -0
- package/templates/bug-report.md +43 -0
- package/templates/component-detail.md +42 -0
- package/templates/component.stories.tsx +49 -0
- package/templates/context/constraints.md +83 -0
- package/templates/context/conventions.md +177 -0
- package/templates/context/stack.md +60 -0
- package/templates/correction-report.md +90 -0
- package/templates/feature-proposal.md +35 -0
- package/templates/hybrid/_base.md +254 -0
- package/templates/hybrid/_patterns.md +45 -0
- package/templates/hybrid/create-component.md +127 -0
- package/templates/hybrid/create-file.md +56 -0
- package/templates/hybrid/create-hook.md +145 -0
- package/templates/hybrid/create-service.md +70 -0
- package/templates/hybrid/fix-bug.md +33 -0
- package/templates/hybrid/modify-file.md +55 -0
- package/templates/story.md +68 -0
- package/templates/task.json +56 -0
- package/templates/trace.md +69 -0
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Memory Blocks Manager
|
|
5
|
+
*
|
|
6
|
+
* Handles structured memory blocks within progress.md.
|
|
7
|
+
* Allows programmatic access while maintaining human readability.
|
|
8
|
+
*
|
|
9
|
+
* Memory blocks are JSON stored in a markdown comment block,
|
|
10
|
+
* allowing both machine parsing and human readability.
|
|
11
|
+
*
|
|
12
|
+
* Part of v1.7.0 Context Memory Management
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const {
|
|
18
|
+
getConfig,
|
|
19
|
+
PATHS,
|
|
20
|
+
STATE_DIR,
|
|
21
|
+
colors,
|
|
22
|
+
color,
|
|
23
|
+
warn,
|
|
24
|
+
success,
|
|
25
|
+
error,
|
|
26
|
+
readFile,
|
|
27
|
+
writeFile,
|
|
28
|
+
fileExists,
|
|
29
|
+
printHeader
|
|
30
|
+
} = require('./flow-utils');
|
|
31
|
+
|
|
32
|
+
// ============================================================
|
|
33
|
+
// Constants
|
|
34
|
+
// ============================================================
|
|
35
|
+
|
|
36
|
+
const PROGRESS_PATH = PATHS.progress;
|
|
37
|
+
const BLOCK_START = '<!-- MEMORY-BLOCKS-START -->';
|
|
38
|
+
const BLOCK_END = '<!-- MEMORY-BLOCKS-END -->';
|
|
39
|
+
|
|
40
|
+
// ============================================================
|
|
41
|
+
// Default Structures
|
|
42
|
+
// ============================================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Default memory blocks structure
|
|
46
|
+
*/
|
|
47
|
+
function getDefaultBlocks() {
|
|
48
|
+
return {
|
|
49
|
+
currentTask: null,
|
|
50
|
+
sessionContext: {
|
|
51
|
+
filesModified: [],
|
|
52
|
+
decisionsThisSession: [],
|
|
53
|
+
blockers: []
|
|
54
|
+
},
|
|
55
|
+
keyFacts: [],
|
|
56
|
+
lastUpdated: new Date().toISOString()
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Default progress.md template with memory blocks section
|
|
62
|
+
*/
|
|
63
|
+
function getDefaultProgress() {
|
|
64
|
+
const blocks = getDefaultBlocks();
|
|
65
|
+
const now = new Date().toISOString().split('T')[0];
|
|
66
|
+
|
|
67
|
+
return `# Progress & Handoff Notes
|
|
68
|
+
|
|
69
|
+
Session handoff notes for human readability.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Last Updated
|
|
74
|
+
${now}
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Memory Blocks
|
|
79
|
+
${BLOCK_START}
|
|
80
|
+
\`\`\`json
|
|
81
|
+
${JSON.stringify(blocks, null, 2)}
|
|
82
|
+
\`\`\`
|
|
83
|
+
${BLOCK_END}
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Last Session
|
|
88
|
+
_No previous session recorded_
|
|
89
|
+
|
|
90
|
+
## In Progress
|
|
91
|
+
_Nothing in progress_
|
|
92
|
+
|
|
93
|
+
## Next
|
|
94
|
+
_No next steps defined_
|
|
95
|
+
|
|
96
|
+
## Blockers
|
|
97
|
+
_None_
|
|
98
|
+
|
|
99
|
+
## Notes
|
|
100
|
+
_No notes_
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ============================================================
|
|
107
|
+
// Core Operations
|
|
108
|
+
// ============================================================
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Read memory blocks from progress.md
|
|
112
|
+
* Returns null if blocks section doesn't exist or is invalid
|
|
113
|
+
*/
|
|
114
|
+
function readMemoryBlocks() {
|
|
115
|
+
if (!fileExists(PROGRESS_PATH)) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const content = fs.readFileSync(PROGRESS_PATH, 'utf-8');
|
|
121
|
+
|
|
122
|
+
const startIdx = content.indexOf(BLOCK_START);
|
|
123
|
+
const endIdx = content.indexOf(BLOCK_END);
|
|
124
|
+
|
|
125
|
+
if (startIdx === -1 || endIdx === -1) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Extract JSON from within the markdown code block
|
|
130
|
+
const blockSection = content.slice(startIdx + BLOCK_START.length, endIdx);
|
|
131
|
+
const jsonMatch = blockSection.match(/```json\n([\s\S]*?)\n```/);
|
|
132
|
+
|
|
133
|
+
if (!jsonMatch) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return JSON.parse(jsonMatch[1].trim());
|
|
138
|
+
} catch (err) {
|
|
139
|
+
if (process.env.DEBUG) {
|
|
140
|
+
console.error(`[DEBUG] Failed to read memory blocks: ${err.message}`);
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Write memory blocks to progress.md
|
|
148
|
+
* Creates the section if it doesn't exist
|
|
149
|
+
*/
|
|
150
|
+
function writeMemoryBlocks(blocks) {
|
|
151
|
+
// Clone to avoid mutating caller's data
|
|
152
|
+
const toWrite = { ...blocks, lastUpdated: new Date().toISOString() };
|
|
153
|
+
|
|
154
|
+
let content;
|
|
155
|
+
if (!fileExists(PROGRESS_PATH)) {
|
|
156
|
+
content = getDefaultProgress();
|
|
157
|
+
} else {
|
|
158
|
+
content = fs.readFileSync(PROGRESS_PATH, 'utf-8');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const blockContent = `${BLOCK_START}
|
|
162
|
+
\`\`\`json
|
|
163
|
+
${JSON.stringify(toWrite, null, 2)}
|
|
164
|
+
\`\`\`
|
|
165
|
+
${BLOCK_END}`;
|
|
166
|
+
|
|
167
|
+
const startIdx = content.indexOf(BLOCK_START);
|
|
168
|
+
const endIdx = content.indexOf(BLOCK_END);
|
|
169
|
+
|
|
170
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
171
|
+
// Replace existing blocks
|
|
172
|
+
content = content.slice(0, startIdx) + blockContent + content.slice(endIdx + BLOCK_END.length);
|
|
173
|
+
} else {
|
|
174
|
+
// Insert after "## Memory Blocks" header if it exists
|
|
175
|
+
const headerIdx = content.indexOf('## Memory Blocks');
|
|
176
|
+
if (headerIdx !== -1) {
|
|
177
|
+
const insertPoint = content.indexOf('\n', headerIdx) + 1;
|
|
178
|
+
content = content.slice(0, insertPoint) + blockContent + '\n' + content.slice(insertPoint);
|
|
179
|
+
} else {
|
|
180
|
+
// Insert after "## Last Updated" section
|
|
181
|
+
const lastUpdatedIdx = content.indexOf('## Last Updated');
|
|
182
|
+
if (lastUpdatedIdx !== -1) {
|
|
183
|
+
// Find end of Last Updated section (next ---)
|
|
184
|
+
const nextDivider = content.indexOf('---', lastUpdatedIdx + 10);
|
|
185
|
+
if (nextDivider !== -1) {
|
|
186
|
+
const insertPoint = nextDivider + 4;
|
|
187
|
+
content = content.slice(0, insertPoint) + '\n## Memory Blocks\n' + blockContent + '\n\n---\n' + content.slice(insertPoint);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
fs.writeFileSync(PROGRESS_PATH, content);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get memory blocks, creating defaults if needed
|
|
199
|
+
*/
|
|
200
|
+
function getOrCreateBlocks() {
|
|
201
|
+
let blocks = readMemoryBlocks();
|
|
202
|
+
if (!blocks) {
|
|
203
|
+
blocks = getDefaultBlocks();
|
|
204
|
+
writeMemoryBlocks(blocks);
|
|
205
|
+
}
|
|
206
|
+
return blocks;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ============================================================
|
|
210
|
+
// Update Operations
|
|
211
|
+
// ============================================================
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Update a specific block by key
|
|
215
|
+
*/
|
|
216
|
+
function updateBlock(key, value) {
|
|
217
|
+
const blocks = getOrCreateBlocks();
|
|
218
|
+
blocks[key] = value;
|
|
219
|
+
writeMemoryBlocks(blocks);
|
|
220
|
+
return blocks;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Update current task
|
|
225
|
+
*/
|
|
226
|
+
function setCurrentTask(taskId, taskTitle, metadata = {}) {
|
|
227
|
+
return updateBlock('currentTask', {
|
|
228
|
+
id: taskId,
|
|
229
|
+
title: taskTitle,
|
|
230
|
+
startedAt: new Date().toISOString(),
|
|
231
|
+
...metadata
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Clear current task
|
|
237
|
+
*/
|
|
238
|
+
function clearCurrentTask() {
|
|
239
|
+
return updateBlock('currentTask', null);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Add a key fact (max 10 kept)
|
|
244
|
+
*/
|
|
245
|
+
function addKeyFact(fact) {
|
|
246
|
+
const blocks = getOrCreateBlocks();
|
|
247
|
+
|
|
248
|
+
if (!blocks.keyFacts.includes(fact)) {
|
|
249
|
+
blocks.keyFacts.push(fact);
|
|
250
|
+
|
|
251
|
+
// Keep only last 10 facts
|
|
252
|
+
if (blocks.keyFacts.length > 10) {
|
|
253
|
+
blocks.keyFacts = blocks.keyFacts.slice(-10);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
writeMemoryBlocks(blocks);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return blocks;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Remove a key fact
|
|
264
|
+
*/
|
|
265
|
+
function removeKeyFact(fact) {
|
|
266
|
+
const blocks = getOrCreateBlocks();
|
|
267
|
+
blocks.keyFacts = blocks.keyFacts.filter(f => f !== fact);
|
|
268
|
+
writeMemoryBlocks(blocks);
|
|
269
|
+
return blocks;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Track a file modification
|
|
274
|
+
*/
|
|
275
|
+
function trackFileModified(filePath) {
|
|
276
|
+
const blocks = getOrCreateBlocks();
|
|
277
|
+
const relPath = path.relative(process.cwd(), filePath);
|
|
278
|
+
|
|
279
|
+
if (!blocks.sessionContext.filesModified.includes(relPath)) {
|
|
280
|
+
blocks.sessionContext.filesModified.push(relPath);
|
|
281
|
+
|
|
282
|
+
// Keep only last 20 files
|
|
283
|
+
if (blocks.sessionContext.filesModified.length > 20) {
|
|
284
|
+
blocks.sessionContext.filesModified = blocks.sessionContext.filesModified.slice(-20);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
writeMemoryBlocks(blocks);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return blocks;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Track a decision made this session
|
|
295
|
+
*/
|
|
296
|
+
function trackDecision(decision) {
|
|
297
|
+
const blocks = getOrCreateBlocks();
|
|
298
|
+
|
|
299
|
+
if (!blocks.sessionContext.decisionsThisSession.includes(decision)) {
|
|
300
|
+
blocks.sessionContext.decisionsThisSession.push(decision);
|
|
301
|
+
|
|
302
|
+
// Keep only last 10 decisions
|
|
303
|
+
if (blocks.sessionContext.decisionsThisSession.length > 10) {
|
|
304
|
+
blocks.sessionContext.decisionsThisSession = blocks.sessionContext.decisionsThisSession.slice(-10);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
writeMemoryBlocks(blocks);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return blocks;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Add a blocker
|
|
315
|
+
*/
|
|
316
|
+
function addBlocker(blocker) {
|
|
317
|
+
const blocks = getOrCreateBlocks();
|
|
318
|
+
|
|
319
|
+
if (!blocks.sessionContext.blockers.includes(blocker)) {
|
|
320
|
+
blocks.sessionContext.blockers.push(blocker);
|
|
321
|
+
writeMemoryBlocks(blocks);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return blocks;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Remove a blocker
|
|
329
|
+
*/
|
|
330
|
+
function removeBlocker(blocker) {
|
|
331
|
+
const blocks = getOrCreateBlocks();
|
|
332
|
+
blocks.sessionContext.blockers = blocks.sessionContext.blockers.filter(b => b !== blocker);
|
|
333
|
+
writeMemoryBlocks(blocks);
|
|
334
|
+
return blocks;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Clear all blockers
|
|
339
|
+
*/
|
|
340
|
+
function clearBlockers() {
|
|
341
|
+
const blocks = getOrCreateBlocks();
|
|
342
|
+
blocks.sessionContext.blockers = [];
|
|
343
|
+
writeMemoryBlocks(blocks);
|
|
344
|
+
return blocks;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Reset session context (for new session)
|
|
349
|
+
*/
|
|
350
|
+
function resetSessionContext() {
|
|
351
|
+
const blocks = getOrCreateBlocks();
|
|
352
|
+
blocks.sessionContext = {
|
|
353
|
+
filesModified: [],
|
|
354
|
+
decisionsThisSession: [],
|
|
355
|
+
blockers: []
|
|
356
|
+
};
|
|
357
|
+
// Don't clear currentTask or keyFacts - those persist
|
|
358
|
+
writeMemoryBlocks(blocks);
|
|
359
|
+
return blocks;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ============================================================
|
|
363
|
+
// Query Operations
|
|
364
|
+
// ============================================================
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Get current task info
|
|
368
|
+
*/
|
|
369
|
+
function getCurrentTask() {
|
|
370
|
+
const blocks = readMemoryBlocks();
|
|
371
|
+
return blocks?.currentTask || null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Get all key facts
|
|
376
|
+
*/
|
|
377
|
+
function getKeyFacts() {
|
|
378
|
+
const blocks = readMemoryBlocks();
|
|
379
|
+
return blocks?.keyFacts || [];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get session context
|
|
384
|
+
*/
|
|
385
|
+
function getSessionContext() {
|
|
386
|
+
const blocks = readMemoryBlocks();
|
|
387
|
+
return blocks?.sessionContext || {
|
|
388
|
+
filesModified: [],
|
|
389
|
+
decisionsThisSession: [],
|
|
390
|
+
blockers: []
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get summary for Claude (useful for resume context)
|
|
396
|
+
*/
|
|
397
|
+
function getResumeContext() {
|
|
398
|
+
const blocks = readMemoryBlocks();
|
|
399
|
+
if (!blocks) return null;
|
|
400
|
+
|
|
401
|
+
const lines = [];
|
|
402
|
+
|
|
403
|
+
if (blocks.currentTask) {
|
|
404
|
+
lines.push(`**Resuming task**: ${blocks.currentTask.id} - ${blocks.currentTask.title}`);
|
|
405
|
+
if (blocks.currentTask.startedAt) {
|
|
406
|
+
const started = new Date(blocks.currentTask.startedAt);
|
|
407
|
+
lines.push(` Started: ${started.toLocaleString()}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (blocks.sessionContext?.filesModified?.length > 0) {
|
|
412
|
+
lines.push(`**Recently modified**: ${blocks.sessionContext.filesModified.slice(-5).join(', ')}`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (blocks.keyFacts?.length > 0) {
|
|
416
|
+
lines.push(`**Key context**:`);
|
|
417
|
+
for (const fact of blocks.keyFacts.slice(-5)) {
|
|
418
|
+
lines.push(` - ${fact}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (blocks.sessionContext?.blockers?.length > 0) {
|
|
423
|
+
lines.push(`**Blockers**: ${blocks.sessionContext.blockers.join(', ')}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return lines.length > 0 ? lines.join('\n') : null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ============================================================
|
|
430
|
+
// Migration
|
|
431
|
+
// ============================================================
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Migrate existing progress.md to include memory blocks
|
|
435
|
+
* Only adds blocks section if it doesn't exist
|
|
436
|
+
*/
|
|
437
|
+
function migrateProgressFile() {
|
|
438
|
+
if (!fileExists(PROGRESS_PATH)) {
|
|
439
|
+
// Create with template
|
|
440
|
+
fs.writeFileSync(PROGRESS_PATH, getDefaultProgress());
|
|
441
|
+
return { migrated: true, action: 'created' };
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const content = fs.readFileSync(PROGRESS_PATH, 'utf-8');
|
|
445
|
+
|
|
446
|
+
if (content.includes(BLOCK_START)) {
|
|
447
|
+
return { migrated: false, reason: 'already has memory blocks' };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Add memory blocks section
|
|
451
|
+
writeMemoryBlocks(getDefaultBlocks());
|
|
452
|
+
return { migrated: true, action: 'updated' };
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ============================================================
|
|
456
|
+
// CLI Interface
|
|
457
|
+
// ============================================================
|
|
458
|
+
|
|
459
|
+
function printUsage() {
|
|
460
|
+
console.log(`
|
|
461
|
+
Usage: flow-memory-blocks.js [command] [args]
|
|
462
|
+
|
|
463
|
+
Commands:
|
|
464
|
+
show Show current memory blocks
|
|
465
|
+
migrate Add memory blocks to existing progress.md
|
|
466
|
+
task <id> <title> Set current task
|
|
467
|
+
task clear Clear current task
|
|
468
|
+
fact add <fact> Add a key fact
|
|
469
|
+
fact remove <idx> Remove a key fact by index
|
|
470
|
+
fact list List all key facts
|
|
471
|
+
file <path> Track file modification
|
|
472
|
+
decision <text> Track a decision
|
|
473
|
+
blocker add <b> Add a blocker
|
|
474
|
+
blocker clear Clear all blockers
|
|
475
|
+
reset-session Reset session context
|
|
476
|
+
resume Show resume context
|
|
477
|
+
--help Show this help
|
|
478
|
+
|
|
479
|
+
Examples:
|
|
480
|
+
node scripts/flow-memory-blocks.js show
|
|
481
|
+
node scripts/flow-memory-blocks.js task TASK-042 "Add login form"
|
|
482
|
+
node scripts/flow-memory-blocks.js fact add "Using React 18 with TypeScript"
|
|
483
|
+
`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Main CLI handler
|
|
487
|
+
if (require.main === module) {
|
|
488
|
+
const args = process.argv.slice(2);
|
|
489
|
+
const command = args[0];
|
|
490
|
+
|
|
491
|
+
switch (command) {
|
|
492
|
+
case 'show': {
|
|
493
|
+
const blocks = readMemoryBlocks();
|
|
494
|
+
if (blocks) {
|
|
495
|
+
printHeader('Memory Blocks');
|
|
496
|
+
console.log(JSON.stringify(blocks, null, 2));
|
|
497
|
+
} else {
|
|
498
|
+
warn('No memory blocks found. Run "migrate" to add them.');
|
|
499
|
+
}
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
case 'migrate': {
|
|
504
|
+
const result = migrateProgressFile();
|
|
505
|
+
if (result.migrated) {
|
|
506
|
+
success(`Progress file ${result.action} with memory blocks`);
|
|
507
|
+
} else {
|
|
508
|
+
console.log(`No migration needed: ${result.reason}`);
|
|
509
|
+
}
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
case 'task': {
|
|
514
|
+
if (args[1] === 'clear') {
|
|
515
|
+
clearCurrentTask();
|
|
516
|
+
success('Current task cleared');
|
|
517
|
+
} else if (args[1] && args[2]) {
|
|
518
|
+
setCurrentTask(args[1], args.slice(2).join(' '));
|
|
519
|
+
success(`Set current task: ${args[1]}`);
|
|
520
|
+
} else {
|
|
521
|
+
const task = getCurrentTask();
|
|
522
|
+
if (task) {
|
|
523
|
+
console.log(`Current task: ${task.id} - ${task.title}`);
|
|
524
|
+
} else {
|
|
525
|
+
console.log('No current task');
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
case 'fact': {
|
|
532
|
+
if (args[1] === 'add' && args[2]) {
|
|
533
|
+
addKeyFact(args.slice(2).join(' '));
|
|
534
|
+
success('Key fact added');
|
|
535
|
+
} else if (args[1] === 'remove' && args[2]) {
|
|
536
|
+
const facts = getKeyFacts();
|
|
537
|
+
const idx = parseInt(args[2], 10);
|
|
538
|
+
if (idx >= 0 && idx < facts.length) {
|
|
539
|
+
removeKeyFact(facts[idx]);
|
|
540
|
+
success(`Removed fact: "${facts[idx]}"`);
|
|
541
|
+
} else {
|
|
542
|
+
error(`Invalid index: ${args[2]}`);
|
|
543
|
+
}
|
|
544
|
+
} else if (args[1] === 'list') {
|
|
545
|
+
const facts = getKeyFacts();
|
|
546
|
+
if (facts.length > 0) {
|
|
547
|
+
console.log('Key facts:');
|
|
548
|
+
facts.forEach((f, i) => console.log(` ${i}. ${f}`));
|
|
549
|
+
} else {
|
|
550
|
+
console.log('No key facts');
|
|
551
|
+
}
|
|
552
|
+
} else {
|
|
553
|
+
error('Usage: fact [add|remove|list] [args]');
|
|
554
|
+
}
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
case 'file': {
|
|
559
|
+
if (args[1]) {
|
|
560
|
+
trackFileModified(args[1]);
|
|
561
|
+
success(`Tracked file: ${args[1]}`);
|
|
562
|
+
} else {
|
|
563
|
+
const ctx = getSessionContext();
|
|
564
|
+
console.log('Modified files:', ctx.filesModified.join(', ') || 'none');
|
|
565
|
+
}
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
case 'decision': {
|
|
570
|
+
if (args[1]) {
|
|
571
|
+
trackDecision(args.slice(1).join(' '));
|
|
572
|
+
success('Decision tracked');
|
|
573
|
+
} else {
|
|
574
|
+
const ctx = getSessionContext();
|
|
575
|
+
console.log('Decisions:', ctx.decisionsThisSession.join(', ') || 'none');
|
|
576
|
+
}
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
case 'blocker': {
|
|
581
|
+
if (args[1] === 'add' && args[2]) {
|
|
582
|
+
addBlocker(args.slice(2).join(' '));
|
|
583
|
+
success('Blocker added');
|
|
584
|
+
} else if (args[1] === 'clear') {
|
|
585
|
+
clearBlockers();
|
|
586
|
+
success('Blockers cleared');
|
|
587
|
+
} else {
|
|
588
|
+
const ctx = getSessionContext();
|
|
589
|
+
console.log('Blockers:', ctx.blockers.join(', ') || 'none');
|
|
590
|
+
}
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
case 'reset-session': {
|
|
595
|
+
resetSessionContext();
|
|
596
|
+
success('Session context reset');
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
case 'resume': {
|
|
601
|
+
const ctx = getResumeContext();
|
|
602
|
+
if (ctx) {
|
|
603
|
+
printHeader('Resume Context');
|
|
604
|
+
console.log(ctx);
|
|
605
|
+
} else {
|
|
606
|
+
console.log('No resume context available');
|
|
607
|
+
}
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
case '--help':
|
|
612
|
+
case '-h':
|
|
613
|
+
printUsage();
|
|
614
|
+
break;
|
|
615
|
+
|
|
616
|
+
default:
|
|
617
|
+
if (command) {
|
|
618
|
+
error(`Unknown command: ${command}`);
|
|
619
|
+
}
|
|
620
|
+
printUsage();
|
|
621
|
+
process.exit(command ? 1 : 0);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// ============================================================
|
|
626
|
+
// Exports
|
|
627
|
+
// ============================================================
|
|
628
|
+
|
|
629
|
+
module.exports = {
|
|
630
|
+
// Core operations
|
|
631
|
+
readMemoryBlocks,
|
|
632
|
+
writeMemoryBlocks,
|
|
633
|
+
getOrCreateBlocks,
|
|
634
|
+
updateBlock,
|
|
635
|
+
|
|
636
|
+
// Task operations
|
|
637
|
+
setCurrentTask,
|
|
638
|
+
clearCurrentTask,
|
|
639
|
+
getCurrentTask,
|
|
640
|
+
|
|
641
|
+
// Key facts
|
|
642
|
+
addKeyFact,
|
|
643
|
+
removeKeyFact,
|
|
644
|
+
getKeyFacts,
|
|
645
|
+
|
|
646
|
+
// Session tracking
|
|
647
|
+
trackFileModified,
|
|
648
|
+
trackDecision,
|
|
649
|
+
addBlocker,
|
|
650
|
+
removeBlocker,
|
|
651
|
+
clearBlockers,
|
|
652
|
+
getSessionContext,
|
|
653
|
+
resetSessionContext,
|
|
654
|
+
|
|
655
|
+
// Resume context
|
|
656
|
+
getResumeContext,
|
|
657
|
+
|
|
658
|
+
// Migration
|
|
659
|
+
migrateProgressFile,
|
|
660
|
+
|
|
661
|
+
// Defaults
|
|
662
|
+
getDefaultBlocks,
|
|
663
|
+
getDefaultProgress,
|
|
664
|
+
|
|
665
|
+
// Constants
|
|
666
|
+
BLOCK_START,
|
|
667
|
+
BLOCK_END
|
|
668
|
+
};
|