@boshu2/vibe-check 1.5.0 → 1.6.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/.agents/bundles/insight-mining-dashboard-research-2025-11-30.md +400 -0
- package/.agents/bundles/storage-enhancement-research-2025-11-30.md +292 -0
- package/.agents/bundles/timeline-feature-research-complete-2025-11-30.md +301 -0
- package/.agents/plans/insight-dashboard-plan-2025-11-30.md +1130 -0
- package/.agents/plans/json-storage-enhancement-plan.md +717 -0
- package/.agents/plans/storage-hardening-and-cache-plan.md +592 -0
- package/.agents/plans/test-coverage-gaps-plan.md +1117 -0
- package/.agents/plans/timeline-feature-plan.md +193 -0
- package/.agents/plans/vibe_timeline_research_findings.md +553 -0
- package/.claude/settings.local.json +1 -0
- package/.vibe-check/.gitignore +6 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +24 -0
- package/CONTRIBUTING.md +227 -0
- package/README.md +200 -143
- package/claude-progress.json +191 -9
- package/claude-progress.txt +257 -0
- package/dashboard/app.js +75 -2
- package/dashboard/dashboard-data.json +653 -0
- package/dashboard/index.html +13 -0
- package/dashboard/styles.css +61 -0
- package/dist/analysis/cross-session-analysis.d.ts +68 -0
- package/dist/analysis/cross-session-analysis.d.ts.map +1 -0
- package/dist/analysis/cross-session-analysis.js +174 -0
- package/dist/analysis/cross-session-analysis.js.map +1 -0
- package/dist/analysis/index.d.ts +2 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +12 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/cli.js +10 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +2 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +105 -2
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/cache.d.ts +6 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +168 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/dashboard.d.ts +8 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +109 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +8 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/timeline.d.ts +14 -0
- package/dist/commands/timeline.d.ts.map +1 -0
- package/dist/commands/timeline.js +462 -0
- package/dist/commands/timeline.js.map +1 -0
- package/dist/git.d.ts +24 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +94 -0
- package/dist/git.js.map +1 -1
- package/dist/insights/generators.d.ts +44 -0
- package/dist/insights/generators.d.ts.map +1 -0
- package/dist/insights/generators.js +289 -0
- package/dist/insights/generators.js.map +1 -0
- package/dist/insights/index.d.ts +16 -0
- package/dist/insights/index.d.ts.map +1 -0
- package/dist/insights/index.js +171 -0
- package/dist/insights/index.js.map +1 -0
- package/dist/insights/types.d.ts +93 -0
- package/dist/insights/types.d.ts.map +1 -0
- package/dist/insights/types.js +6 -0
- package/dist/insights/types.js.map +1 -0
- package/dist/output/timeline-html.d.ts +6 -0
- package/dist/output/timeline-html.d.ts.map +1 -0
- package/dist/output/timeline-html.js +389 -0
- package/dist/output/timeline-html.js.map +1 -0
- package/dist/output/timeline-markdown.d.ts +6 -0
- package/dist/output/timeline-markdown.d.ts.map +1 -0
- package/dist/output/timeline-markdown.js +167 -0
- package/dist/output/timeline-markdown.js.map +1 -0
- package/dist/output/timeline.d.ts +9 -0
- package/dist/output/timeline.d.ts.map +1 -0
- package/dist/output/timeline.js +318 -0
- package/dist/output/timeline.js.map +1 -0
- package/dist/patterns/detour.d.ts +32 -0
- package/dist/patterns/detour.d.ts.map +1 -0
- package/dist/patterns/detour.js +137 -0
- package/dist/patterns/detour.js.map +1 -0
- package/dist/patterns/flow-state.d.ts +16 -0
- package/dist/patterns/flow-state.d.ts.map +1 -0
- package/dist/patterns/flow-state.js +40 -0
- package/dist/patterns/flow-state.js.map +1 -0
- package/dist/patterns/index.d.ts +8 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +22 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/intervention-effectiveness.d.ts +42 -0
- package/dist/patterns/intervention-effectiveness.d.ts.map +1 -0
- package/dist/patterns/intervention-effectiveness.js +196 -0
- package/dist/patterns/intervention-effectiveness.js.map +1 -0
- package/dist/patterns/late-night.d.ts +30 -0
- package/dist/patterns/late-night.d.ts.map +1 -0
- package/dist/patterns/late-night.js +141 -0
- package/dist/patterns/late-night.js.map +1 -0
- package/dist/patterns/post-delete-sprint.d.ts +28 -0
- package/dist/patterns/post-delete-sprint.d.ts.map +1 -0
- package/dist/patterns/post-delete-sprint.js +85 -0
- package/dist/patterns/post-delete-sprint.js.map +1 -0
- package/dist/patterns/spiral-regression.d.ts +49 -0
- package/dist/patterns/spiral-regression.d.ts.map +1 -0
- package/dist/patterns/spiral-regression.js +219 -0
- package/dist/patterns/spiral-regression.js.map +1 -0
- package/dist/patterns/thrashing.d.ts +25 -0
- package/dist/patterns/thrashing.d.ts.map +1 -0
- package/dist/patterns/thrashing.js +111 -0
- package/dist/patterns/thrashing.js.map +1 -0
- package/dist/storage/atomic.d.ts +40 -0
- package/dist/storage/atomic.d.ts.map +1 -0
- package/dist/storage/atomic.js +155 -0
- package/dist/storage/atomic.js.map +1 -0
- package/dist/storage/commit-log.d.ts +35 -0
- package/dist/storage/commit-log.d.ts.map +1 -0
- package/dist/storage/commit-log.js +128 -0
- package/dist/storage/commit-log.js.map +1 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +33 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/schema.d.ts +32 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +37 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/timeline-store.d.ts +117 -0
- package/dist/storage/timeline-store.d.ts.map +1 -0
- package/dist/storage/timeline-store.js +438 -0
- package/dist/storage/timeline-store.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/ARCHITECTURE.md +458 -0
- package/docs/DATA-ARCHITECTURE.md +565 -0
- package/docs/GAMIFICATION.md +564 -0
- package/docs/JSON-STORAGE-PATTERNS.md +512 -0
- package/docs/METRICS-EXPLAINED.md +394 -0
- package/docs/UNIFIED-ECOSYSTEM.md +560 -0
- package/docs/VIBE-ECOSYSTEM.md +406 -0
- package/feature-list.json +48 -0
- package/package.json +2 -1
- package/vitest.config.ts +1 -5
- package/.vibe-check/calibration.json +0 -38
- package/.vibe-check/latest.json +0 -114
- package/.vibe-check/sessions.json +0 -44
- package/PLAN-ultimate-game.md +0 -1362
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
# Storage Hardening & Cache Command Plan
|
|
2
|
+
|
|
3
|
+
**Type:** Plan
|
|
4
|
+
**Created:** 2025-11-30
|
|
5
|
+
**Depends On:** storage-enhancement-research-2025-11-30.md
|
|
6
|
+
**Loop:** Middle (bridges research to implementation)
|
|
7
|
+
**Tags:** security, storage, cache, hardening
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Implement Phase 1 (hardening) and partial Phase 2 (cache command) from the storage enhancement research:
|
|
14
|
+
|
|
15
|
+
1. **Fix schema versioning** - Use CURRENT_SCHEMA_VERSION instead of hardcoded '1.0.0'
|
|
16
|
+
2. **Improve NDJSON error handling** - Skip bad lines instead of failing entire file
|
|
17
|
+
3. **Add .gitignore enforcement** - Warn if .vibe-check/ not in .gitignore
|
|
18
|
+
4. **Add cache command** - `vibe-check cache status` and `vibe-check cache clear`
|
|
19
|
+
|
|
20
|
+
**Scope:** Security hardening + cache management UX. No analytics features yet.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Approach Selected
|
|
25
|
+
|
|
26
|
+
From research, implement these quick wins:
|
|
27
|
+
- Schema version fix (5 min)
|
|
28
|
+
- NDJSON error recovery (15 min)
|
|
29
|
+
- Cache status/clear commands (30 min)
|
|
30
|
+
- .gitignore warning (10 min)
|
|
31
|
+
|
|
32
|
+
**Total estimated:** ~1 hour
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## PDC Strategy
|
|
37
|
+
|
|
38
|
+
### Prevent
|
|
39
|
+
- [x] Research completed (3-agent synthesis)
|
|
40
|
+
- [ ] All changes are additive (backward compatible)
|
|
41
|
+
|
|
42
|
+
### Detect
|
|
43
|
+
- [ ] Build passes after each change
|
|
44
|
+
- [ ] Tests pass after all changes
|
|
45
|
+
- [ ] Manual test of cache commands
|
|
46
|
+
|
|
47
|
+
### Correct
|
|
48
|
+
- [ ] Rollback procedure documented
|
|
49
|
+
- [ ] No breaking changes to existing data
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Files to Create
|
|
54
|
+
|
|
55
|
+
### 1. `src/commands/cache.ts`
|
|
56
|
+
|
|
57
|
+
**Purpose:** Cache management commands (status, clear)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { Command } from 'commander';
|
|
61
|
+
import chalk from 'chalk';
|
|
62
|
+
import * as fs from 'fs';
|
|
63
|
+
import * as path from 'path';
|
|
64
|
+
import { getStoreDir, getStorePath, loadStore } from '../storage';
|
|
65
|
+
import { getCommitLogPath } from '../storage/commit-log';
|
|
66
|
+
|
|
67
|
+
export interface CacheOptions {
|
|
68
|
+
repo: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function createCacheCommand(): Command {
|
|
72
|
+
const cmd = new Command('cache')
|
|
73
|
+
.description('Manage vibe-check cache (.vibe-check/ directory)')
|
|
74
|
+
.option('-r, --repo <path>', 'Repository path', process.cwd());
|
|
75
|
+
|
|
76
|
+
// Subcommand: status
|
|
77
|
+
cmd
|
|
78
|
+
.command('status')
|
|
79
|
+
.description('Show cache status and size')
|
|
80
|
+
.action(async () => {
|
|
81
|
+
const options = cmd.opts() as CacheOptions;
|
|
82
|
+
await showCacheStatus(options);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Subcommand: clear
|
|
86
|
+
cmd
|
|
87
|
+
.command('clear')
|
|
88
|
+
.description('Clear all cached data')
|
|
89
|
+
.option('--force', 'Skip confirmation prompt')
|
|
90
|
+
.action(async (clearOptions) => {
|
|
91
|
+
const options = cmd.opts() as CacheOptions;
|
|
92
|
+
await clearCache(options, clearOptions.force);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Default action: show status
|
|
96
|
+
cmd.action(async () => {
|
|
97
|
+
const options = cmd.opts() as CacheOptions;
|
|
98
|
+
await showCacheStatus(options);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return cmd;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function showCacheStatus(options: CacheOptions): Promise<void> {
|
|
105
|
+
const { repo } = options;
|
|
106
|
+
const storeDir = getStoreDir(repo);
|
|
107
|
+
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log(chalk.bold.cyan('═'.repeat(50)));
|
|
110
|
+
console.log(chalk.bold.cyan(' VIBE-CHECK CACHE STATUS'));
|
|
111
|
+
console.log(chalk.bold.cyan('═'.repeat(50)));
|
|
112
|
+
console.log('');
|
|
113
|
+
|
|
114
|
+
if (!fs.existsSync(storeDir)) {
|
|
115
|
+
console.log(chalk.yellow(' No cache directory found.'));
|
|
116
|
+
console.log(chalk.gray(` Run 'vibe-check timeline' to create cache.`));
|
|
117
|
+
console.log('');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Calculate directory size
|
|
122
|
+
const files = fs.readdirSync(storeDir);
|
|
123
|
+
let totalSize = 0;
|
|
124
|
+
const fileInfo: { name: string; size: number }[] = [];
|
|
125
|
+
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
const filePath = path.join(storeDir, file);
|
|
128
|
+
const stats = fs.statSync(filePath);
|
|
129
|
+
if (stats.isFile()) {
|
|
130
|
+
totalSize += stats.size;
|
|
131
|
+
fileInfo.push({ name: file, size: stats.size });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(chalk.white(` 📁 Location: ${storeDir}`));
|
|
136
|
+
console.log(chalk.white(` 📊 Total size: ${formatBytes(totalSize)}`));
|
|
137
|
+
console.log('');
|
|
138
|
+
|
|
139
|
+
// Show file breakdown
|
|
140
|
+
console.log(chalk.gray(' Files:'));
|
|
141
|
+
for (const file of fileInfo) {
|
|
142
|
+
console.log(chalk.gray(` ${file.name}: ${formatBytes(file.size)}`));
|
|
143
|
+
}
|
|
144
|
+
console.log('');
|
|
145
|
+
|
|
146
|
+
// Show store details if timeline.json exists
|
|
147
|
+
const storePath = getStorePath(repo);
|
|
148
|
+
if (fs.existsSync(storePath)) {
|
|
149
|
+
try {
|
|
150
|
+
const store = loadStore(repo);
|
|
151
|
+
console.log(chalk.white(' 📈 Timeline data:'));
|
|
152
|
+
console.log(chalk.gray(` Sessions: ${store.sessions.length}`));
|
|
153
|
+
console.log(chalk.gray(` Insights: ${store.insights.length}`));
|
|
154
|
+
console.log(chalk.gray(` Last updated: ${store.lastUpdated}`));
|
|
155
|
+
console.log(chalk.gray(` Last commit: ${store.lastCommitHash || 'none'}`));
|
|
156
|
+
|
|
157
|
+
if (store.sessions.length > 0) {
|
|
158
|
+
const dates = store.sessions.map(s => s.date).sort();
|
|
159
|
+
console.log(chalk.gray(` Date range: ${dates[0]} to ${dates[dates.length - 1]}`));
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
console.log(chalk.yellow(' ⚠ Could not read timeline data'));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Check .gitignore status
|
|
167
|
+
console.log('');
|
|
168
|
+
const gitignorePath = path.join(repo, '.gitignore');
|
|
169
|
+
if (fs.existsSync(gitignorePath)) {
|
|
170
|
+
const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
|
|
171
|
+
if (gitignore.includes('.vibe-check')) {
|
|
172
|
+
console.log(chalk.green(' ✓ .vibe-check/ is in .gitignore'));
|
|
173
|
+
} else {
|
|
174
|
+
console.log(chalk.yellow(' ⚠ .vibe-check/ is NOT in .gitignore'));
|
|
175
|
+
console.log(chalk.gray(' Add ".vibe-check/" to .gitignore to avoid committing cache'));
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
console.log(chalk.yellow(' ⚠ No .gitignore file found'));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log('');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function clearCache(options: CacheOptions, force: boolean): Promise<void> {
|
|
185
|
+
const { repo } = options;
|
|
186
|
+
const storeDir = getStoreDir(repo);
|
|
187
|
+
|
|
188
|
+
if (!fs.existsSync(storeDir)) {
|
|
189
|
+
console.log(chalk.yellow('No cache to clear.'));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!force) {
|
|
194
|
+
// Show what will be deleted
|
|
195
|
+
const files = fs.readdirSync(storeDir);
|
|
196
|
+
console.log('');
|
|
197
|
+
console.log(chalk.yellow('The following files will be deleted:'));
|
|
198
|
+
for (const file of files) {
|
|
199
|
+
console.log(chalk.gray(` - ${file}`));
|
|
200
|
+
}
|
|
201
|
+
console.log('');
|
|
202
|
+
console.log(chalk.yellow('Run with --force to confirm deletion.'));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Delete all files in .vibe-check/
|
|
207
|
+
const files = fs.readdirSync(storeDir);
|
|
208
|
+
for (const file of files) {
|
|
209
|
+
const filePath = path.join(storeDir, file);
|
|
210
|
+
fs.unlinkSync(filePath);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Remove directory
|
|
214
|
+
fs.rmdirSync(storeDir);
|
|
215
|
+
|
|
216
|
+
console.log(chalk.green('✓ Cache cleared successfully.'));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function formatBytes(bytes: number): string {
|
|
220
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
221
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
222
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Validation:**
|
|
227
|
+
```bash
|
|
228
|
+
npm run build
|
|
229
|
+
npm run dev -- cache status
|
|
230
|
+
npm run dev -- cache clear
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Files to Modify
|
|
236
|
+
|
|
237
|
+
### 1. `src/storage/timeline-store.ts:139`
|
|
238
|
+
|
|
239
|
+
**Purpose:** Use CURRENT_SCHEMA_VERSION instead of hardcoded '1.0.0'
|
|
240
|
+
|
|
241
|
+
**Before:**
|
|
242
|
+
```typescript
|
|
243
|
+
export function createInitialStore(): TimelineStore {
|
|
244
|
+
return {
|
|
245
|
+
version: '1.0.0',
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**After:**
|
|
249
|
+
```typescript
|
|
250
|
+
import { CURRENT_SCHEMA_VERSION } from './schema';
|
|
251
|
+
|
|
252
|
+
// ... (in createInitialStore)
|
|
253
|
+
export function createInitialStore(): TimelineStore {
|
|
254
|
+
return {
|
|
255
|
+
version: CURRENT_SCHEMA_VERSION,
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Reason:** Schema versioning exists but isn't used correctly
|
|
259
|
+
**Validation:** `npm run build`
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
### 2. `src/storage/timeline-store.ts:1-8` (add import)
|
|
264
|
+
|
|
265
|
+
**Purpose:** Import CURRENT_SCHEMA_VERSION
|
|
266
|
+
|
|
267
|
+
**Before:**
|
|
268
|
+
```typescript
|
|
269
|
+
import * as path from 'path';
|
|
270
|
+
import {
|
|
271
|
+
TimelineResult,
|
|
272
|
+
TimelineSession,
|
|
273
|
+
TimelineDay,
|
|
274
|
+
TimelineEvent,
|
|
275
|
+
} from '../types';
|
|
276
|
+
import { atomicWriteSync, safeReadJsonSync } from './atomic';
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**After:**
|
|
280
|
+
```typescript
|
|
281
|
+
import * as path from 'path';
|
|
282
|
+
import {
|
|
283
|
+
TimelineResult,
|
|
284
|
+
TimelineSession,
|
|
285
|
+
TimelineDay,
|
|
286
|
+
TimelineEvent,
|
|
287
|
+
} from '../types';
|
|
288
|
+
import { atomicWriteSync, safeReadJsonSync } from './atomic';
|
|
289
|
+
import { CURRENT_SCHEMA_VERSION } from './schema';
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Reason:** Need schema version constant
|
|
293
|
+
**Validation:** `npm run build`
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
### 3. `src/storage/atomic.ts:47-56`
|
|
298
|
+
|
|
299
|
+
**Purpose:** Improve NDJSON error handling - skip bad lines instead of failing
|
|
300
|
+
|
|
301
|
+
**Before:**
|
|
302
|
+
```typescript
|
|
303
|
+
export function readNdjsonSync<T>(filePath: string): T[] {
|
|
304
|
+
if (!fs.existsSync(filePath)) {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
309
|
+
const lines = content.split('\n').filter(line => line.trim().length > 0);
|
|
310
|
+
|
|
311
|
+
return lines.map(line => JSON.parse(line) as T);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**After:**
|
|
316
|
+
```typescript
|
|
317
|
+
export function readNdjsonSync<T>(filePath: string): T[] {
|
|
318
|
+
if (!fs.existsSync(filePath)) {
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
323
|
+
const lines = content.split('\n').filter(line => line.trim().length > 0);
|
|
324
|
+
|
|
325
|
+
const results: T[] = [];
|
|
326
|
+
for (const line of lines) {
|
|
327
|
+
try {
|
|
328
|
+
results.push(JSON.parse(line) as T);
|
|
329
|
+
} catch {
|
|
330
|
+
// Skip malformed lines instead of failing entire file
|
|
331
|
+
console.warn(`Warning: Skipping malformed NDJSON line: ${line.substring(0, 50)}...`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return results;
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Reason:** One bad line shouldn't corrupt entire file read
|
|
339
|
+
**Validation:** `npm run build`
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
### 4. `src/commands/index.ts`
|
|
344
|
+
|
|
345
|
+
**Purpose:** Export cache command
|
|
346
|
+
|
|
347
|
+
**Before:**
|
|
348
|
+
```typescript
|
|
349
|
+
export { createAnalyzeCommand, runAnalyze, AnalyzeOptions } from './analyze';
|
|
350
|
+
export { createStartCommand } from './start';
|
|
351
|
+
export { createProfileCommand } from './profile';
|
|
352
|
+
export { createInitHookCommand } from './init-hook';
|
|
353
|
+
export { createWatchCommand } from './watch';
|
|
354
|
+
export { createInterveneCommand } from './intervene';
|
|
355
|
+
export { createTimelineCommand, runTimeline, TimelineOptions } from './timeline';
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**After:**
|
|
359
|
+
```typescript
|
|
360
|
+
export { createAnalyzeCommand, runAnalyze, AnalyzeOptions } from './analyze';
|
|
361
|
+
export { createStartCommand } from './start';
|
|
362
|
+
export { createProfileCommand } from './profile';
|
|
363
|
+
export { createInitHookCommand } from './init-hook';
|
|
364
|
+
export { createWatchCommand } from './watch';
|
|
365
|
+
export { createInterveneCommand } from './intervene';
|
|
366
|
+
export { createTimelineCommand, runTimeline, TimelineOptions } from './timeline';
|
|
367
|
+
export { createCacheCommand } from './cache';
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Reason:** Register new cache command
|
|
371
|
+
**Validation:** `npm run build`
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
### 5. `src/cli.ts:4`
|
|
376
|
+
|
|
377
|
+
**Purpose:** Import cache command
|
|
378
|
+
|
|
379
|
+
**Before:**
|
|
380
|
+
```typescript
|
|
381
|
+
import { createAnalyzeCommand, createStartCommand, createProfileCommand, createInitHookCommand, createWatchCommand, createInterveneCommand, createTimelineCommand, runAnalyze } from './commands';
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**After:**
|
|
385
|
+
```typescript
|
|
386
|
+
import { createAnalyzeCommand, createStartCommand, createProfileCommand, createInitHookCommand, createWatchCommand, createInterveneCommand, createTimelineCommand, createCacheCommand, runAnalyze } from './commands';
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Reason:** Import new command
|
|
390
|
+
**Validation:** `npm run build`
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
### 6. `src/cli.ts:25` (add command registration)
|
|
395
|
+
|
|
396
|
+
**Purpose:** Register cache command with CLI
|
|
397
|
+
|
|
398
|
+
**Before:**
|
|
399
|
+
```typescript
|
|
400
|
+
program.addCommand(createTimelineCommand());
|
|
401
|
+
|
|
402
|
+
// Default behavior: if no subcommand, run analyze with passed options
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**After:**
|
|
406
|
+
```typescript
|
|
407
|
+
program.addCommand(createTimelineCommand());
|
|
408
|
+
program.addCommand(createCacheCommand());
|
|
409
|
+
|
|
410
|
+
// Default behavior: if no subcommand, run analyze with passed options
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**Reason:** Make cache command available
|
|
414
|
+
**Validation:** `npm run dev -- cache --help`
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
### 7. `src/commands/timeline.ts:43-52`
|
|
419
|
+
|
|
420
|
+
**Purpose:** Improve help text for --no-cache and --insights flags
|
|
421
|
+
|
|
422
|
+
**Before:**
|
|
423
|
+
```typescript
|
|
424
|
+
export function createTimelineCommand(): Command {
|
|
425
|
+
const cmd = new Command('timeline')
|
|
426
|
+
.description('View your coding journey as a timeline with sessions and patterns')
|
|
427
|
+
.option('--since <date>', 'Start date for analysis (default: "1 week ago")', '1 week ago')
|
|
428
|
+
.option('--until <date>', 'End date for analysis (default: now)')
|
|
429
|
+
.option('-f, --format <type>', 'Output format: terminal, json, markdown, html', 'terminal')
|
|
430
|
+
.option('-r, --repo <path>', 'Repository path', process.cwd())
|
|
431
|
+
.option('-v, --verbose', 'Show verbose output', false)
|
|
432
|
+
.option('--expand [date]', 'Expand day details (all or specific date like "Nov-29")')
|
|
433
|
+
.option('--no-cache', 'Skip cache and fetch fresh data from git')
|
|
434
|
+
.option('--insights', 'Show compounding insights from historical data')
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**After:**
|
|
438
|
+
```typescript
|
|
439
|
+
export function createTimelineCommand(): Command {
|
|
440
|
+
const cmd = new Command('timeline')
|
|
441
|
+
.description('View your coding journey as a timeline with sessions and patterns')
|
|
442
|
+
.option('--since <date>', 'Start date for analysis (default: "1 week ago")', '1 week ago')
|
|
443
|
+
.option('--until <date>', 'End date for analysis (default: now)')
|
|
444
|
+
.option('-f, --format <type>', 'Output format: terminal, json, markdown, html', 'terminal')
|
|
445
|
+
.option('-r, --repo <path>', 'Repository path', process.cwd())
|
|
446
|
+
.option('-v, --verbose', 'Show verbose output', false)
|
|
447
|
+
.option('--expand [date]', 'Expand day details (all or specific date like "Nov-29")')
|
|
448
|
+
.option('--no-cache', 'Force fresh git analysis (bypass .vibe-check/ cache)')
|
|
449
|
+
.option('--insights', 'Show compounding insights (patterns detected over 3+ sessions)')
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**Reason:** Better documentation of features
|
|
453
|
+
**Validation:** `npm run dev -- timeline --help`
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Implementation Order
|
|
458
|
+
|
|
459
|
+
**CRITICAL: Sequence matters. Do not reorder.**
|
|
460
|
+
|
|
461
|
+
| Step | Action | Validation | Rollback |
|
|
462
|
+
|------|--------|------------|----------|
|
|
463
|
+
| 1 | Modify `timeline-store.ts` imports | `npm run build` | Revert |
|
|
464
|
+
| 2 | Fix schema version in `createInitialStore` | `npm run build` | Revert |
|
|
465
|
+
| 3 | Improve NDJSON error handling in `atomic.ts` | `npm run build` | Revert |
|
|
466
|
+
| 4 | Create `src/commands/cache.ts` | `npm run build` | Delete |
|
|
467
|
+
| 5 | Update `src/commands/index.ts` exports | `npm run build` | Revert |
|
|
468
|
+
| 6 | Update `src/cli.ts` imports | `npm run build` | Revert |
|
|
469
|
+
| 7 | Register cache command in `cli.ts` | `npm run build` | Revert |
|
|
470
|
+
| 8 | Improve timeline help text | `npm run build` | Revert |
|
|
471
|
+
| 9 | Run full test suite | `npm test` | Revert all |
|
|
472
|
+
| 10 | Manual test cache commands | See below | Revert all |
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Validation Strategy
|
|
477
|
+
|
|
478
|
+
### Syntax Validation
|
|
479
|
+
```bash
|
|
480
|
+
npm run build
|
|
481
|
+
# Expected: No errors
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Unit Tests
|
|
485
|
+
```bash
|
|
486
|
+
npm test
|
|
487
|
+
# Expected: All 26 tests pass
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Manual Integration Tests
|
|
491
|
+
```bash
|
|
492
|
+
# Test cache status
|
|
493
|
+
npm run dev -- cache status
|
|
494
|
+
# Expected: Shows cache info or "no cache"
|
|
495
|
+
|
|
496
|
+
# Test cache status with existing cache
|
|
497
|
+
npm run dev -- timeline --since "1 week ago"
|
|
498
|
+
npm run dev -- cache status
|
|
499
|
+
# Expected: Shows sessions count, file sizes, date range
|
|
500
|
+
|
|
501
|
+
# Test cache clear (dry run)
|
|
502
|
+
npm run dev -- cache clear
|
|
503
|
+
# Expected: Lists files that would be deleted, asks for --force
|
|
504
|
+
|
|
505
|
+
# Test cache clear (actual)
|
|
506
|
+
npm run dev -- cache clear --force
|
|
507
|
+
# Expected: "Cache cleared successfully"
|
|
508
|
+
npm run dev -- cache status
|
|
509
|
+
# Expected: "No cache directory found"
|
|
510
|
+
|
|
511
|
+
# Test timeline help
|
|
512
|
+
npm run dev -- timeline --help
|
|
513
|
+
# Expected: Shows improved descriptions for --no-cache and --insights
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## Rollback Procedure
|
|
519
|
+
|
|
520
|
+
**Time to rollback:** ~2 minutes
|
|
521
|
+
|
|
522
|
+
### Full Rollback
|
|
523
|
+
```bash
|
|
524
|
+
# Step 1: Revert all modifications
|
|
525
|
+
git checkout src/storage/timeline-store.ts
|
|
526
|
+
git checkout src/storage/atomic.ts
|
|
527
|
+
git checkout src/commands/index.ts
|
|
528
|
+
git checkout src/commands/timeline.ts
|
|
529
|
+
git checkout src/cli.ts
|
|
530
|
+
|
|
531
|
+
# Step 2: Remove new file
|
|
532
|
+
rm -f src/commands/cache.ts
|
|
533
|
+
|
|
534
|
+
# Step 3: Verify
|
|
535
|
+
npm run build
|
|
536
|
+
npm test
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## Failure Pattern Risks
|
|
542
|
+
|
|
543
|
+
| Pattern | Risk | Prevention in Plan |
|
|
544
|
+
|---------|------|-------------------|
|
|
545
|
+
| Tests Passing Lie | LOW | Manual cache command tests |
|
|
546
|
+
| Instruction Drift | LOW | Precise file:line specs |
|
|
547
|
+
| Bridge Torching | LOW | Backward compatible changes |
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Risk Assessment
|
|
552
|
+
|
|
553
|
+
### Low Risk: Import Ordering
|
|
554
|
+
- **What:** Circular imports
|
|
555
|
+
- **Mitigation:** New cache.ts imports from storage (same as other commands)
|
|
556
|
+
- **Detection:** Build fails
|
|
557
|
+
- **Recovery:** Fix import order
|
|
558
|
+
|
|
559
|
+
### Low Risk: fs operations
|
|
560
|
+
- **What:** Permission errors on cache clear
|
|
561
|
+
- **Mitigation:** Only operates on .vibe-check/ we created
|
|
562
|
+
- **Detection:** Error message
|
|
563
|
+
- **Recovery:** User fixes permissions manually
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Approval Checklist
|
|
568
|
+
|
|
569
|
+
**Human must verify before /implement:**
|
|
570
|
+
|
|
571
|
+
- [ ] Every file specified precisely (file:line)
|
|
572
|
+
- [ ] All templates complete (no placeholders)
|
|
573
|
+
- [ ] Validation commands provided
|
|
574
|
+
- [ ] Rollback procedure complete
|
|
575
|
+
- [ ] Implementation order is correct
|
|
576
|
+
- [ ] Risks identified and mitigated
|
|
577
|
+
- [ ] Cache command is additive (no breaking changes)
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Next Step
|
|
582
|
+
|
|
583
|
+
Once approved: `/implement storage-hardening-and-cache-plan.md`
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Future Work (Not in This Plan)
|
|
588
|
+
|
|
589
|
+
1. **Cross-session queries** - `vibe-check analyze --all-time`
|
|
590
|
+
2. **Scope analysis** - `vibe-check analyze --scope auth`
|
|
591
|
+
3. **Regression detection** - Alert when spirals return
|
|
592
|
+
4. **Intervention effectiveness** - Score what breaks spirals
|