@stackmemoryai/stackmemory 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/core/agent-task-manager.js +5 -5
- package/dist/agents/core/agent-task-manager.js.map +2 -2
- package/dist/agents/verifiers/base-verifier.js +2 -2
- package/dist/agents/verifiers/base-verifier.js.map +2 -2
- package/dist/cli/claude-sm.js +0 -11
- package/dist/cli/claude-sm.js.map +2 -2
- package/dist/cli/codex-sm.js +0 -11
- package/dist/cli/codex-sm.js.map +2 -2
- package/dist/cli/commands/chromadb.js +64 -34
- package/dist/cli/commands/chromadb.js.map +2 -2
- package/dist/cli/commands/clear.js +9 -13
- package/dist/cli/commands/clear.js.map +2 -2
- package/dist/cli/commands/config.js +43 -33
- package/dist/cli/commands/config.js.map +2 -2
- package/dist/cli/commands/context.js.map +2 -2
- package/dist/cli/commands/dashboard.js +41 -13
- package/dist/cli/commands/dashboard.js.map +2 -2
- package/dist/cli/commands/gc.js +69 -20
- package/dist/cli/commands/gc.js.map +2 -2
- package/dist/cli/commands/handoff.js.map +2 -2
- package/dist/cli/commands/infinite-storage.js +60 -19
- package/dist/cli/commands/infinite-storage.js.map +2 -2
- package/dist/cli/commands/linear-create.js +36 -8
- package/dist/cli/commands/linear-create.js.map +2 -2
- package/dist/cli/commands/linear-list.js +33 -10
- package/dist/cli/commands/linear-list.js.map +2 -2
- package/dist/cli/commands/linear-migrate.js +17 -4
- package/dist/cli/commands/linear-migrate.js.map +2 -2
- package/dist/cli/commands/linear-test.js +14 -6
- package/dist/cli/commands/linear-test.js.map +2 -2
- package/dist/cli/commands/linear-unified.js +123 -35
- package/dist/cli/commands/linear-unified.js.map +2 -2
- package/dist/cli/commands/linear.js.map +2 -2
- package/dist/cli/commands/monitor.js.map +2 -2
- package/dist/cli/commands/onboard.js +35 -8
- package/dist/cli/commands/onboard.js.map +2 -2
- package/dist/cli/commands/quality.js +2 -7
- package/dist/cli/commands/quality.js.map +2 -2
- package/dist/cli/commands/session.js +23 -6
- package/dist/cli/commands/session.js.map +2 -2
- package/dist/cli/commands/skills.js +72 -27
- package/dist/cli/commands/skills.js.map +2 -2
- package/dist/cli/commands/storage.js +108 -38
- package/dist/cli/commands/storage.js.map +2 -2
- package/dist/cli/commands/tui.js.map +2 -2
- package/dist/cli/commands/webhook.js +57 -18
- package/dist/cli/commands/webhook.js.map +2 -2
- package/dist/cli/commands/workflow.js +8 -15
- package/dist/cli/commands/workflow.js.map +2 -2
- package/dist/cli/commands/worktree.js +34 -13
- package/dist/cli/commands/worktree.js.map +2 -2
- package/dist/cli/index.js +0 -11
- package/dist/cli/index.js.map +2 -2
- package/dist/core/config/types.js.map +1 -1
- package/dist/core/context/auto-context.js +10 -6
- package/dist/core/context/auto-context.js.map +2 -2
- package/dist/core/context/context-bridge.js.map +2 -2
- package/dist/core/context/frame-database.js +13 -3
- package/dist/core/context/frame-database.js.map +2 -2
- package/dist/core/context/frame-digest.js +7 -5
- package/dist/core/context/frame-digest.js.map +2 -2
- package/dist/core/context/frame-manager.js.map +2 -2
- package/dist/core/context/frame-stack.js +16 -5
- package/dist/core/context/frame-stack.js.map +2 -2
- package/dist/core/context/incremental-gc.js +10 -3
- package/dist/core/context/incremental-gc.js.map +2 -2
- package/dist/core/context/index.js.map +1 -1
- package/dist/core/context/permission-manager.js.map +2 -2
- package/dist/core/context/recursive-context-manager.js +582 -0
- package/dist/core/context/recursive-context-manager.js.map +7 -0
- package/dist/core/context/refactored-frame-manager.js +12 -3
- package/dist/core/context/refactored-frame-manager.js.map +2 -2
- package/dist/core/context/shared-context-layer.js +4 -2
- package/dist/core/context/shared-context-layer.js.map +2 -2
- package/dist/core/database/batch-operations.js +112 -86
- package/dist/core/database/batch-operations.js.map +2 -2
- package/dist/core/database/query-cache.js +19 -9
- package/dist/core/database/query-cache.js.map +2 -2
- package/dist/core/database/sqlite-adapter.js +1 -1
- package/dist/core/database/sqlite-adapter.js.map +2 -2
- package/dist/core/digest/enhanced-hybrid-digest.js +8 -2
- package/dist/core/digest/enhanced-hybrid-digest.js.map +2 -2
- package/dist/core/errors/recovery.js +9 -2
- package/dist/core/errors/recovery.js.map +2 -2
- package/dist/core/execution/parallel-executor.js +254 -0
- package/dist/core/execution/parallel-executor.js.map +7 -0
- package/dist/core/frame/workflow-templates-stub.js.map +1 -1
- package/dist/core/frame/workflow-templates.js +40 -1
- package/dist/core/frame/workflow-templates.js.map +2 -2
- package/dist/core/monitoring/logger.js +6 -1
- package/dist/core/monitoring/logger.js.map +2 -2
- package/dist/core/monitoring/metrics.js.map +2 -2
- package/dist/core/monitoring/progress-tracker.js.map +2 -2
- package/dist/core/performance/context-cache.js.map +2 -2
- package/dist/core/performance/lazy-context-loader.js +24 -20
- package/dist/core/performance/lazy-context-loader.js.map +2 -2
- package/dist/core/performance/optimized-frame-context.js +27 -12
- package/dist/core/performance/optimized-frame-context.js.map +2 -2
- package/dist/core/performance/performance-benchmark.js +10 -6
- package/dist/core/performance/performance-benchmark.js.map +2 -2
- package/dist/core/performance/performance-profiler.js +51 -14
- package/dist/core/performance/performance-profiler.js.map +2 -2
- package/dist/core/performance/streaming-jsonl-parser.js +5 -1
- package/dist/core/performance/streaming-jsonl-parser.js.map +2 -2
- package/dist/core/projects/project-manager.js +14 -20
- package/dist/core/projects/project-manager.js.map +2 -2
- package/dist/core/retrieval/context-retriever.js.map +1 -1
- package/dist/core/retrieval/llm-context-retrieval.js.map +2 -2
- package/dist/core/session/clear-survival-stub.js +5 -1
- package/dist/core/session/clear-survival-stub.js.map +2 -2
- package/dist/core/session/clear-survival.js +35 -0
- package/dist/core/session/clear-survival.js.map +2 -2
- package/dist/core/session/index.js.map +1 -1
- package/dist/core/session/session-manager.js.map +2 -2
- package/dist/core/storage/chromadb-adapter.js +6 -2
- package/dist/core/storage/chromadb-adapter.js.map +2 -2
- package/dist/core/storage/chromadb-simple.js +17 -5
- package/dist/core/storage/chromadb-simple.js.map +2 -2
- package/dist/core/storage/infinite-storage.js +109 -46
- package/dist/core/storage/infinite-storage.js.map +2 -2
- package/dist/core/storage/railway-optimized-storage.js +48 -22
- package/dist/core/storage/railway-optimized-storage.js.map +2 -2
- package/dist/core/storage/remote-storage.js +41 -23
- package/dist/core/storage/remote-storage.js.map +2 -2
- package/dist/core/trace/cli-trace-wrapper.js +9 -2
- package/dist/core/trace/cli-trace-wrapper.js.map +2 -2
- package/dist/core/trace/db-trace-wrapper.js +96 -68
- package/dist/core/trace/db-trace-wrapper.js.map +2 -2
- package/dist/core/trace/debug-trace.js +25 -8
- package/dist/core/trace/debug-trace.js.map +2 -2
- package/dist/core/trace/index.js +6 -2
- package/dist/core/trace/index.js.map +2 -2
- package/dist/core/trace/linear-api-wrapper.js +10 -5
- package/dist/core/trace/linear-api-wrapper.js.map +2 -2
- package/dist/core/trace/trace-demo.js +14 -10
- package/dist/core/trace/trace-demo.js.map +2 -2
- package/dist/core/trace/trace-detector.js +9 -2
- package/dist/core/trace/trace-detector.js.map +2 -2
- package/dist/core/trace/types.js.map +1 -1
- package/dist/core/utils/compression.js.map +1 -1
- package/dist/core/utils/update-checker.js.map +1 -1
- package/dist/core/worktree/worktree-manager.js +18 -7
- package/dist/core/worktree/worktree-manager.js.map +2 -2
- package/dist/features/analytics/core/analytics-service.js.map +2 -2
- package/dist/features/analytics/queries/metrics-queries.js +1 -1
- package/dist/features/analytics/queries/metrics-queries.js.map +2 -2
- package/dist/features/tasks/pebbles-task-store.js.map +1 -1
- package/dist/features/tui/components/analytics-panel.js +36 -15
- package/dist/features/tui/components/analytics-panel.js.map +2 -2
- package/dist/features/tui/components/pr-tracker.js +19 -7
- package/dist/features/tui/components/pr-tracker.js.map +2 -2
- package/dist/features/tui/components/session-monitor.js +22 -9
- package/dist/features/tui/components/session-monitor.js.map +2 -2
- package/dist/features/tui/components/subagent-fleet.js +20 -13
- package/dist/features/tui/components/subagent-fleet.js.map +2 -2
- package/dist/features/tui/components/task-board.js +26 -10
- package/dist/features/tui/components/task-board.js.map +2 -2
- package/dist/features/tui/index.js.map +2 -2
- package/dist/features/tui/services/data-service.js +6 -2
- package/dist/features/tui/services/data-service.js.map +2 -2
- package/dist/features/tui/services/linear-task-reader.js +3 -1
- package/dist/features/tui/services/linear-task-reader.js.map +2 -2
- package/dist/features/tui/services/websocket-client.js +3 -1
- package/dist/features/tui/services/websocket-client.js.map +2 -2
- package/dist/features/tui/terminal-compat.js +6 -2
- package/dist/features/tui/terminal-compat.js.map +2 -2
- package/dist/features/web/client/stores/task-store.js.map +2 -2
- package/dist/features/web/server/index.js +18 -10
- package/dist/features/web/server/index.js.map +2 -2
- package/dist/integrations/anthropic/client.js +259 -0
- package/dist/integrations/anthropic/client.js.map +7 -0
- package/dist/integrations/claude-code/subagent-client.js +404 -0
- package/dist/integrations/claude-code/subagent-client.js.map +7 -0
- package/dist/integrations/linear/sync-service.js +12 -13
- package/dist/integrations/linear/sync-service.js.map +2 -2
- package/dist/integrations/linear/sync.js +174 -12
- package/dist/integrations/linear/sync.js.map +2 -2
- package/dist/integrations/linear/unified-sync.js +1 -1
- package/dist/integrations/linear/unified-sync.js.map +1 -1
- package/dist/integrations/linear/webhook-server.js +15 -16
- package/dist/integrations/linear/webhook-server.js.map +2 -2
- package/dist/mcp/stackmemory-mcp-server.js +0 -11
- package/dist/mcp/stackmemory-mcp-server.js.map +2 -2
- package/dist/servers/production/auth-middleware.js.map +2 -2
- package/dist/servers/railway/index.js.map +2 -2
- package/dist/services/config-service.js +6 -7
- package/dist/services/config-service.js.map +2 -2
- package/dist/services/context-service.js +11 -12
- package/dist/services/context-service.js.map +2 -2
- package/dist/skills/claude-skills.js +101 -2
- package/dist/skills/claude-skills.js.map +2 -2
- package/dist/skills/dashboard-launcher.js.map +2 -2
- package/dist/skills/recursive-agent-orchestrator.js +559 -0
- package/dist/skills/recursive-agent-orchestrator.js.map +7 -0
- package/dist/skills/repo-ingestion-skill.js.map +2 -2
- package/dist/skills/security-secrets-scanner.js +265 -0
- package/dist/skills/security-secrets-scanner.js.map +7 -0
- package/dist/utils/env.js +46 -0
- package/dist/utils/env.js.map +7 -0
- package/dist/utils/logger.js +0 -11
- package/dist/utils/logger.js.map +2 -2
- package/package.json +1 -1
package/dist/cli/commands/gc.js
CHANGED
|
@@ -15,7 +15,9 @@ function createGCCommand() {
|
|
|
15
15
|
const collector = new IncrementalGarbageCollector(frameManager);
|
|
16
16
|
const stats = collector.getStats();
|
|
17
17
|
spinner.stop();
|
|
18
|
-
console.log(
|
|
18
|
+
console.log(
|
|
19
|
+
chalk.cyan("\n\u{1F5D1}\uFE0F Incremental Garbage Collection Status\n")
|
|
20
|
+
);
|
|
19
21
|
const table = new Table({
|
|
20
22
|
head: ["Metric", "Value"],
|
|
21
23
|
colWidths: [25, 20]
|
|
@@ -26,7 +28,10 @@ function createGCCommand() {
|
|
|
26
28
|
["Collection Cycles", stats.cycleCount.toString()],
|
|
27
29
|
["Avg Cycle Time", `${stats.avgCycleTime.toFixed(2)}ms`],
|
|
28
30
|
["Protected Frames", stats.protectedFrames.toString()],
|
|
29
|
-
[
|
|
31
|
+
[
|
|
32
|
+
"Last Run",
|
|
33
|
+
stats.lastRunTime ? new Date(stats.lastRunTime).toLocaleString() : "Never"
|
|
34
|
+
]
|
|
30
35
|
);
|
|
31
36
|
console.log(table.toString());
|
|
32
37
|
if (stats.totalFrames > 0) {
|
|
@@ -39,10 +44,15 @@ function createGCCommand() {
|
|
|
39
44
|
} catch (error) {
|
|
40
45
|
spinner.fail("Failed to get GC status");
|
|
41
46
|
logger.error("GC status error", error);
|
|
42
|
-
console.error(
|
|
47
|
+
console.error(
|
|
48
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
49
|
+
);
|
|
43
50
|
}
|
|
44
51
|
});
|
|
45
|
-
gc.command("collect").description("Run manual garbage collection cycle").option(
|
|
52
|
+
gc.command("collect").description("Run manual garbage collection cycle").option(
|
|
53
|
+
"--dry-run",
|
|
54
|
+
"Show what would be collected without actually collecting"
|
|
55
|
+
).action(async (options) => {
|
|
46
56
|
const spinner = ora("Running garbage collection...").start();
|
|
47
57
|
try {
|
|
48
58
|
const frameManager = new FrameManager();
|
|
@@ -53,7 +63,9 @@ function createGCCommand() {
|
|
|
53
63
|
} else {
|
|
54
64
|
await collector.forceCollection();
|
|
55
65
|
const stats = collector.getStats();
|
|
56
|
-
spinner.succeed(
|
|
66
|
+
spinner.succeed(
|
|
67
|
+
`Collection completed - collected ${stats.collectedFrames} frames`
|
|
68
|
+
);
|
|
57
69
|
console.log(chalk.green("\n\u2705 Manual collection completed"));
|
|
58
70
|
console.log(` Processed: ${stats.totalFrames} frames`);
|
|
59
71
|
console.log(` Collected: ${stats.collectedFrames} frames`);
|
|
@@ -62,7 +74,9 @@ function createGCCommand() {
|
|
|
62
74
|
} catch (error) {
|
|
63
75
|
spinner.fail("Collection failed");
|
|
64
76
|
logger.error("GC collection error", error);
|
|
65
|
-
console.error(
|
|
77
|
+
console.error(
|
|
78
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
79
|
+
);
|
|
66
80
|
}
|
|
67
81
|
});
|
|
68
82
|
gc.command("start").description("Start incremental GC daemon").option("--interval <seconds>", "Collection interval in seconds", "60").option("--frames-per-cycle <number>", "Frames to process per cycle", "100").action(async (options) => {
|
|
@@ -89,7 +103,9 @@ function createGCCommand() {
|
|
|
89
103
|
} catch (error) {
|
|
90
104
|
spinner.fail("Failed to start GC daemon");
|
|
91
105
|
logger.error("GC start error", error);
|
|
92
|
-
console.error(
|
|
106
|
+
console.error(
|
|
107
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
108
|
+
);
|
|
93
109
|
}
|
|
94
110
|
});
|
|
95
111
|
gc.command("config").description("View or update GC configuration").option("--set-interval <seconds>", "Set collection interval").option("--set-frames-per-cycle <number>", "Set frames per cycle").option("--set-max-age <days>", "Set max frame age before collection").action(async (options) => {
|
|
@@ -98,9 +114,12 @@ function createGCCommand() {
|
|
|
98
114
|
const collector = new IncrementalGarbageCollector(frameManager);
|
|
99
115
|
if (options.setInterval || options.setFramesPerCycle || options.setMaxAge) {
|
|
100
116
|
const newConfig = {};
|
|
101
|
-
if (options.setInterval)
|
|
102
|
-
|
|
103
|
-
if (options.
|
|
117
|
+
if (options.setInterval)
|
|
118
|
+
newConfig.cycleInterval = parseInt(options.setInterval) * 1e3;
|
|
119
|
+
if (options.setFramesPerCycle)
|
|
120
|
+
newConfig.framesPerCycle = parseInt(options.setFramesPerCycle);
|
|
121
|
+
if (options.setMaxAge)
|
|
122
|
+
newConfig.maxAge = parseInt(options.setMaxAge) * 24 * 60 * 60 * 1e3;
|
|
104
123
|
collector.updateConfig(newConfig);
|
|
105
124
|
console.log(chalk.green("\u2705 Configuration updated"));
|
|
106
125
|
}
|
|
@@ -114,7 +133,9 @@ function createGCCommand() {
|
|
|
114
133
|
console.log(" Old: 7-30 days");
|
|
115
134
|
} catch (error) {
|
|
116
135
|
logger.error("GC config error", error);
|
|
117
|
-
console.error(
|
|
136
|
+
console.error(
|
|
137
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
138
|
+
);
|
|
118
139
|
}
|
|
119
140
|
});
|
|
120
141
|
gc.command("analyze").description("Analyze frame distribution and collection opportunities").action(async () => {
|
|
@@ -168,30 +189,58 @@ function createGCCommand() {
|
|
|
168
189
|
["Closed Frames", analysis.closed.toString(), pct(analysis.closed)],
|
|
169
190
|
["", "", ""],
|
|
170
191
|
["Young (< 1 day)", analysis.young.toString(), pct(analysis.young)],
|
|
171
|
-
[
|
|
192
|
+
[
|
|
193
|
+
"Mature (1-7 days)",
|
|
194
|
+
analysis.mature.toString(),
|
|
195
|
+
pct(analysis.mature)
|
|
196
|
+
],
|
|
172
197
|
["Old (> 7 days)", analysis.old.toString(), pct(analysis.old)],
|
|
173
198
|
["", "", ""],
|
|
174
|
-
[
|
|
175
|
-
|
|
199
|
+
[
|
|
200
|
+
"With Outputs",
|
|
201
|
+
analysis.withOutputs.toString(),
|
|
202
|
+
pct(analysis.withOutputs)
|
|
203
|
+
],
|
|
204
|
+
[
|
|
205
|
+
"Without Outputs",
|
|
206
|
+
analysis.withoutOutputs.toString(),
|
|
207
|
+
pct(analysis.withoutOutputs)
|
|
208
|
+
],
|
|
176
209
|
["", "", ""],
|
|
177
|
-
[
|
|
178
|
-
|
|
210
|
+
[
|
|
211
|
+
"Root Frames",
|
|
212
|
+
analysis.rootFrames.toString(),
|
|
213
|
+
pct(analysis.rootFrames)
|
|
214
|
+
],
|
|
215
|
+
[
|
|
216
|
+
"Leaf Frames",
|
|
217
|
+
analysis.leafFrames.toString(),
|
|
218
|
+
pct(analysis.leafFrames)
|
|
219
|
+
]
|
|
179
220
|
);
|
|
180
221
|
console.log(stateTable.toString());
|
|
181
222
|
console.log(chalk.yellow("\n\u{1F4A1} Collection Recommendations:\n"));
|
|
182
223
|
const candidatesForCollection = analysis.closed + analysis.withoutOutputs;
|
|
183
|
-
console.log(
|
|
184
|
-
|
|
224
|
+
console.log(
|
|
225
|
+
`\u2022 Potential collection candidates: ${candidatesForCollection} frames`
|
|
226
|
+
);
|
|
227
|
+
console.log(
|
|
228
|
+
`\u2022 Estimated space savings: ${(candidatesForCollection / analysis.total * 100).toFixed(1)}%`
|
|
229
|
+
);
|
|
185
230
|
if (analysis.old > 0) {
|
|
186
231
|
console.log(`\u2022 ${analysis.old} old frames ready for collection`);
|
|
187
232
|
}
|
|
188
233
|
if (analysis.withoutOutputs > 0) {
|
|
189
|
-
console.log(
|
|
234
|
+
console.log(
|
|
235
|
+
`\u2022 ${analysis.withoutOutputs} frames without outputs can be collected`
|
|
236
|
+
);
|
|
190
237
|
}
|
|
191
238
|
} catch (error) {
|
|
192
239
|
spinner.fail("Analysis failed");
|
|
193
240
|
logger.error("GC analysis error", error);
|
|
194
|
-
console.error(
|
|
241
|
+
console.error(
|
|
242
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
243
|
+
);
|
|
195
244
|
}
|
|
196
245
|
});
|
|
197
246
|
return gc;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/cli/commands/gc.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * CLI commands for Incremental Garbage Collection management\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport Table from 'cli-table3';\nimport { IncrementalGarbageCollector } from '../../core/context/incremental-gc.js';\nimport { FrameManager } from '../../core/context/frame-manager.js';\nimport { Logger } from '../../core/monitoring/logger.js';\n\nconst logger = new Logger('GC-CLI');\n\nexport function createGCCommand(): Command {\n const gc = new Command('gc')\n .description('Manage incremental garbage collection system')\n .alias('garbage-collect');\n\n // Status command\n gc.command('status')\n .description('Show garbage collection statistics')\n .action(async () => {\n const spinner = ora('Getting GC status...').start();\n\n try {\n const frameManager = new FrameManager();\n const collector = new IncrementalGarbageCollector(frameManager);\n \n const stats = collector.getStats();\n spinner.stop();\n\n console.log(chalk.cyan('\\n\uD83D\uDDD1\uFE0F Incremental Garbage Collection Status\\n'));\n\n const table = new Table({\n head: ['Metric', 'Value'],\n colWidths: [25, 20],\n });\n\n table.push(\n ['Total Frames', stats.totalFrames.toString()],\n ['Collected Frames', stats.collectedFrames.toString()],\n ['Collection Cycles', stats.cycleCount.toString()],\n ['Avg Cycle Time', `${stats.avgCycleTime.toFixed(2)}ms`],\n ['Protected Frames', stats.protectedFrames.toString()],\n ['Last Run', stats.lastRunTime ? new Date(stats.lastRunTime).toLocaleString() : 'Never']\n );\n\n console.log(table.toString());\n\n // Show efficiency metrics\n if (stats.totalFrames > 0) {\n const collectionRate = ((stats.collectedFrames / stats.totalFrames) * 100).toFixed(1);\n const protectionRate = ((stats.protectedFrames / stats.totalFrames) * 100).toFixed(1);\n \n console.log('\\n\uD83D\uDCCA Collection Efficiency:');\n console.log(` Collection Rate: ${collectionRate}%`);\n console.log(` Protection Rate: ${protectionRate}%`);\n }\n\n } catch (error: unknown) {\n spinner.fail('Failed to get GC status');\n logger.error('GC status error', error);\n console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));\n }\n });\n\n // Manual collection command \n gc.command('collect')\n .description('Run manual garbage collection cycle')\n .option('--dry-run', 'Show what would be collected without actually collecting')\n .action(async (options) => {\n const spinner = ora('Running garbage collection...').start();\n\n try {\n const frameManager = new FrameManager();\n const collector = new IncrementalGarbageCollector(frameManager);\n \n if (options.dryRun) {\n spinner.text = 'Analyzing collection candidates (dry run)...';\n // TODO: Implement dry run mode\n spinner.succeed('Dry run completed - check logs for details');\n } else {\n await collector.forceCollection();\n const stats = collector.getStats();\n \n spinner.succeed(`Collection completed - collected ${stats.collectedFrames} frames`);\n \n console.log(chalk.green('\\n\u2705 Manual collection completed'));\n console.log(` Processed: ${stats.totalFrames} frames`);\n console.log(` Collected: ${stats.collectedFrames} frames`);\n console.log(` Protected: ${stats.protectedFrames} frames`);\n }\n\n } catch (error: unknown) {\n spinner.fail('Collection failed');\n logger.error('GC collection error', error);\n console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));\n }\n });\n\n // Start/stop daemon commands\n gc.command('start')\n .description('Start incremental GC daemon')\n .option('--interval <seconds>', 'Collection interval in seconds', '60')\n .option('--frames-per-cycle <number>', 'Frames to process per cycle', '100')\n .action(async (options) => {\n const spinner = ora('Starting GC daemon...').start();\n\n try {\n const frameManager = new FrameManager();\n const collector = new IncrementalGarbageCollector(frameManager, {\n cycleInterval: parseInt(options.interval) * 1000,\n framesPerCycle: parseInt(options.framesPerCycle)\n });\n \n collector.start();\n \n spinner.succeed('GC daemon started');\n console.log(chalk.green('\\n\uD83D\uDE80 Incremental GC daemon is running'));\n console.log(` Interval: ${options.interval}s`);\n console.log(` Frames per cycle: ${options.framesPerCycle}`);\n console.log('\\nPress Ctrl+C to stop');\n \n // Keep process alive\n process.on('SIGINT', () => {\n console.log('\\n\u23F9\uFE0F Stopping GC daemon...');\n collector.stop();\n process.exit(0);\n });\n \n // Keep the process running\n await new Promise(() => {}); // Run forever\n\n } catch (error: unknown) {\n spinner.fail('Failed to start GC daemon');\n logger.error('GC start error', error);\n console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));\n }\n });\n\n // Configuration command\n gc.command('config')\n .description('View or update GC configuration')\n .option('--set-interval <seconds>', 'Set collection interval')\n .option('--set-frames-per-cycle <number>', 'Set frames per cycle')\n .option('--set-max-age <days>', 'Set max frame age before collection')\n .action(async (options) => {\n try {\n const frameManager = new FrameManager();\n const collector = new IncrementalGarbageCollector(frameManager);\n \n if (options.setInterval || options.setFramesPerCycle || options.setMaxAge) {\n // Update configuration\n const newConfig: any = {};\n if (options.setInterval) newConfig.cycleInterval = parseInt(options.setInterval) * 1000;\n if (options.setFramesPerCycle) newConfig.framesPerCycle = parseInt(options.setFramesPerCycle);\n if (options.setMaxAge) newConfig.maxAge = parseInt(options.setMaxAge) * 24 * 60 * 60 * 1000;\n \n collector.updateConfig(newConfig);\n console.log(chalk.green('\u2705 Configuration updated'));\n }\n\n // Show current config\n console.log(chalk.cyan('\\n\u2699\uFE0F Current GC Configuration\\n'));\n console.log('Cycle Interval: 60s');\n console.log('Frames per Cycle: 100');\n console.log('Max Age: 30 days');\n console.log('\\nGenerations:');\n console.log(' Young: < 1 day');\n console.log(' Mature: 1-7 days');\n console.log(' Old: 7-30 days');\n\n } catch (error: unknown) {\n logger.error('GC config error', error);\n console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));\n }\n });\n\n // Analysis command\n gc.command('analyze')\n .description('Analyze frame distribution and collection opportunities')\n .action(async () => {\n const spinner = ora('Analyzing frames...').start();\n\n try {\n const frameManager = new FrameManager();\n const allFrames = await frameManager.getAllFrames();\n \n if (allFrames.length === 0) {\n spinner.succeed('No frames to analyze');\n return;\n }\n\n // Analyze frame distribution\n const now = Date.now();\n const analysis = {\n total: allFrames.length,\n active: 0,\n closed: 0,\n young: 0,\n mature: 0,\n old: 0,\n withOutputs: 0,\n withoutOutputs: 0,\n rootFrames: 0,\n leafFrames: 0\n };\n\n for (const frame of allFrames) {\n const age = now - frame.created_at;\n \n // State analysis\n if (frame.state === 'active') analysis.active++;\n else analysis.closed++;\n \n // Age analysis\n if (age < 24 * 60 * 60 * 1000) analysis.young++;\n else if (age < 7 * 24 * 60 * 60 * 1000) analysis.mature++;\n else analysis.old++;\n \n // Output analysis\n if (frame.outputs && Object.keys(frame.outputs).length > 0) {\n analysis.withOutputs++;\n } else {\n analysis.withoutOutputs++;\n }\n \n // Hierarchy analysis\n if (frame.depth === 0) analysis.rootFrames++;\n if (!allFrames.some(f => f.parent_frame_id === frame.frame_id)) {\n analysis.leafFrames++;\n }\n }\n\n spinner.stop();\n\n console.log(chalk.cyan('\\n\uD83D\uDCCA Frame Distribution Analysis\\n'));\n\n const stateTable = new Table({\n head: ['Category', 'Count', 'Percentage'],\n colWidths: [20, 10, 15],\n });\n\n const pct = (count: number) => `${((count / analysis.total) * 100).toFixed(1)}%`;\n\n stateTable.push(\n ['Active Frames', analysis.active.toString(), pct(analysis.active)],\n ['Closed Frames', analysis.closed.toString(), pct(analysis.closed)],\n ['', '', ''],\n ['Young (< 1 day)', analysis.young.toString(), pct(analysis.young)],\n ['Mature (1-7 days)', analysis.mature.toString(), pct(analysis.mature)],\n ['Old (> 7 days)', analysis.old.toString(), pct(analysis.old)],\n ['', '', ''],\n ['With Outputs', analysis.withOutputs.toString(), pct(analysis.withOutputs)],\n ['Without Outputs', analysis.withoutOutputs.toString(), pct(analysis.withoutOutputs)],\n ['', '', ''],\n ['Root Frames', analysis.rootFrames.toString(), pct(analysis.rootFrames)],\n ['Leaf Frames', analysis.leafFrames.toString(), pct(analysis.leafFrames)]\n );\n\n console.log(stateTable.toString());\n\n // Collection recommendations\n console.log(chalk.yellow('\\n\uD83D\uDCA1 Collection Recommendations:\\n'));\n const candidatesForCollection = analysis.closed + analysis.withoutOutputs;\n console.log(`\u2022 Potential collection candidates: ${candidatesForCollection} frames`);\n console.log(`\u2022 Estimated space savings: ${((candidatesForCollection / analysis.total) * 100).toFixed(1)}%`);\n \n if (analysis.old > 0) {\n console.log(`\u2022 ${analysis.old} old frames ready for collection`);\n }\n if (analysis.withoutOutputs > 0) {\n console.log(`\u2022 ${analysis.withoutOutputs} frames without outputs can be collected`);\n }\n\n } catch (error: unknown) {\n spinner.fail('Analysis failed');\n logger.error('GC analysis error', error);\n console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));\n }\n });\n\n return gc;\n}"],
|
|
5
|
-
"mappings": "AAIA,SAAS,eAAe;AACxB,OAAO,WAAW;AAClB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,SAAS,mCAAmC;AAC5C,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AAEvB,MAAM,SAAS,IAAI,OAAO,QAAQ;AAE3B,SAAS,kBAA2B;AACzC,QAAM,KAAK,IAAI,QAAQ,IAAI,EACxB,YAAY,8CAA8C,EAC1D,MAAM,iBAAiB;AAG1B,KAAG,QAAQ,QAAQ,EAChB,YAAY,oCAAoC,EAChD,OAAO,YAAY;AAClB,UAAM,UAAU,IAAI,sBAAsB,EAAE,MAAM;AAElD,QAAI;AACF,YAAM,eAAe,IAAI,aAAa;AACtC,YAAM,YAAY,IAAI,4BAA4B,YAAY;AAE9D,YAAM,QAAQ,UAAU,SAAS;AACjC,cAAQ,KAAK;AAEb,cAAQ,
|
|
4
|
+
"sourcesContent": ["/**\n * CLI commands for Incremental Garbage Collection management\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport Table from 'cli-table3';\nimport { IncrementalGarbageCollector } from '../../core/context/incremental-gc.js';\nimport { FrameManager } from '../../core/context/frame-manager.js';\nimport { Logger } from '../../core/monitoring/logger.js';\n\nconst logger = new Logger('GC-CLI');\n\nexport function createGCCommand(): Command {\n const gc = new Command('gc')\n .description('Manage incremental garbage collection system')\n .alias('garbage-collect');\n\n // Status command\n gc.command('status')\n .description('Show garbage collection statistics')\n .action(async () => {\n const spinner = ora('Getting GC status...').start();\n\n try {\n const frameManager = new FrameManager();\n const collector = new IncrementalGarbageCollector(frameManager);\n\n const stats = collector.getStats();\n spinner.stop();\n\n console.log(\n chalk.cyan('\\n\uD83D\uDDD1\uFE0F Incremental Garbage Collection Status\\n')\n );\n\n const table = new Table({\n head: ['Metric', 'Value'],\n colWidths: [25, 20],\n });\n\n table.push(\n ['Total Frames', stats.totalFrames.toString()],\n ['Collected Frames', stats.collectedFrames.toString()],\n ['Collection Cycles', stats.cycleCount.toString()],\n ['Avg Cycle Time', `${stats.avgCycleTime.toFixed(2)}ms`],\n ['Protected Frames', stats.protectedFrames.toString()],\n [\n 'Last Run',\n stats.lastRunTime\n ? new Date(stats.lastRunTime).toLocaleString()\n : 'Never',\n ]\n );\n\n console.log(table.toString());\n\n // Show efficiency metrics\n if (stats.totalFrames > 0) {\n const collectionRate = (\n (stats.collectedFrames / stats.totalFrames) *\n 100\n ).toFixed(1);\n const protectionRate = (\n (stats.protectedFrames / stats.totalFrames) *\n 100\n ).toFixed(1);\n\n console.log('\\n\uD83D\uDCCA Collection Efficiency:');\n console.log(` Collection Rate: ${collectionRate}%`);\n console.log(` Protection Rate: ${protectionRate}%`);\n }\n } catch (error: unknown) {\n spinner.fail('Failed to get GC status');\n logger.error('GC status error', error);\n console.error(\n chalk.red(error instanceof Error ? error.message : 'Unknown error')\n );\n }\n });\n\n // Manual collection command\n gc.command('collect')\n .description('Run manual garbage collection cycle')\n .option(\n '--dry-run',\n 'Show what would be collected without actually collecting'\n )\n .action(async (options) => {\n const spinner = ora('Running garbage collection...').start();\n\n try {\n const frameManager = new FrameManager();\n const collector = new IncrementalGarbageCollector(frameManager);\n\n if (options.dryRun) {\n spinner.text = 'Analyzing collection candidates (dry run)...';\n // TODO: Implement dry run mode\n spinner.succeed('Dry run completed - check logs for details');\n } else {\n await collector.forceCollection();\n const stats = collector.getStats();\n\n spinner.succeed(\n `Collection completed - collected ${stats.collectedFrames} frames`\n );\n\n console.log(chalk.green('\\n\u2705 Manual collection completed'));\n console.log(` Processed: ${stats.totalFrames} frames`);\n console.log(` Collected: ${stats.collectedFrames} frames`);\n console.log(` Protected: ${stats.protectedFrames} frames`);\n }\n } catch (error: unknown) {\n spinner.fail('Collection failed');\n logger.error('GC collection error', error);\n console.error(\n chalk.red(error instanceof Error ? error.message : 'Unknown error')\n );\n }\n });\n\n // Start/stop daemon commands\n gc.command('start')\n .description('Start incremental GC daemon')\n .option('--interval <seconds>', 'Collection interval in seconds', '60')\n .option('--frames-per-cycle <number>', 'Frames to process per cycle', '100')\n .action(async (options) => {\n const spinner = ora('Starting GC daemon...').start();\n\n try {\n const frameManager = new FrameManager();\n const collector = new IncrementalGarbageCollector(frameManager, {\n cycleInterval: parseInt(options.interval) * 1000,\n framesPerCycle: parseInt(options.framesPerCycle),\n });\n\n collector.start();\n\n spinner.succeed('GC daemon started');\n console.log(chalk.green('\\n\uD83D\uDE80 Incremental GC daemon is running'));\n console.log(` Interval: ${options.interval}s`);\n console.log(` Frames per cycle: ${options.framesPerCycle}`);\n console.log('\\nPress Ctrl+C to stop');\n\n // Keep process alive\n process.on('SIGINT', () => {\n console.log('\\n\u23F9\uFE0F Stopping GC daemon...');\n collector.stop();\n process.exit(0);\n });\n\n // Keep the process running\n await new Promise(() => {}); // Run forever\n } catch (error: unknown) {\n spinner.fail('Failed to start GC daemon');\n logger.error('GC start error', error);\n console.error(\n chalk.red(error instanceof Error ? error.message : 'Unknown error')\n );\n }\n });\n\n // Configuration command\n gc.command('config')\n .description('View or update GC configuration')\n .option('--set-interval <seconds>', 'Set collection interval')\n .option('--set-frames-per-cycle <number>', 'Set frames per cycle')\n .option('--set-max-age <days>', 'Set max frame age before collection')\n .action(async (options) => {\n try {\n const frameManager = new FrameManager();\n const collector = new IncrementalGarbageCollector(frameManager);\n\n if (\n options.setInterval ||\n options.setFramesPerCycle ||\n options.setMaxAge\n ) {\n // Update configuration\n const newConfig: any = {};\n if (options.setInterval)\n newConfig.cycleInterval = parseInt(options.setInterval) * 1000;\n if (options.setFramesPerCycle)\n newConfig.framesPerCycle = parseInt(options.setFramesPerCycle);\n if (options.setMaxAge)\n newConfig.maxAge =\n parseInt(options.setMaxAge) * 24 * 60 * 60 * 1000;\n\n collector.updateConfig(newConfig);\n console.log(chalk.green('\u2705 Configuration updated'));\n }\n\n // Show current config\n console.log(chalk.cyan('\\n\u2699\uFE0F Current GC Configuration\\n'));\n console.log('Cycle Interval: 60s');\n console.log('Frames per Cycle: 100');\n console.log('Max Age: 30 days');\n console.log('\\nGenerations:');\n console.log(' Young: < 1 day');\n console.log(' Mature: 1-7 days');\n console.log(' Old: 7-30 days');\n } catch (error: unknown) {\n logger.error('GC config error', error);\n console.error(\n chalk.red(error instanceof Error ? error.message : 'Unknown error')\n );\n }\n });\n\n // Analysis command\n gc.command('analyze')\n .description('Analyze frame distribution and collection opportunities')\n .action(async () => {\n const spinner = ora('Analyzing frames...').start();\n\n try {\n const frameManager = new FrameManager();\n const allFrames = await frameManager.getAllFrames();\n\n if (allFrames.length === 0) {\n spinner.succeed('No frames to analyze');\n return;\n }\n\n // Analyze frame distribution\n const now = Date.now();\n const analysis = {\n total: allFrames.length,\n active: 0,\n closed: 0,\n young: 0,\n mature: 0,\n old: 0,\n withOutputs: 0,\n withoutOutputs: 0,\n rootFrames: 0,\n leafFrames: 0,\n };\n\n for (const frame of allFrames) {\n const age = now - frame.created_at;\n\n // State analysis\n if (frame.state === 'active') analysis.active++;\n else analysis.closed++;\n\n // Age analysis\n if (age < 24 * 60 * 60 * 1000) analysis.young++;\n else if (age < 7 * 24 * 60 * 60 * 1000) analysis.mature++;\n else analysis.old++;\n\n // Output analysis\n if (frame.outputs && Object.keys(frame.outputs).length > 0) {\n analysis.withOutputs++;\n } else {\n analysis.withoutOutputs++;\n }\n\n // Hierarchy analysis\n if (frame.depth === 0) analysis.rootFrames++;\n if (!allFrames.some((f) => f.parent_frame_id === frame.frame_id)) {\n analysis.leafFrames++;\n }\n }\n\n spinner.stop();\n\n console.log(chalk.cyan('\\n\uD83D\uDCCA Frame Distribution Analysis\\n'));\n\n const stateTable = new Table({\n head: ['Category', 'Count', 'Percentage'],\n colWidths: [20, 10, 15],\n });\n\n const pct = (count: number) =>\n `${((count / analysis.total) * 100).toFixed(1)}%`;\n\n stateTable.push(\n ['Active Frames', analysis.active.toString(), pct(analysis.active)],\n ['Closed Frames', analysis.closed.toString(), pct(analysis.closed)],\n ['', '', ''],\n ['Young (< 1 day)', analysis.young.toString(), pct(analysis.young)],\n [\n 'Mature (1-7 days)',\n analysis.mature.toString(),\n pct(analysis.mature),\n ],\n ['Old (> 7 days)', analysis.old.toString(), pct(analysis.old)],\n ['', '', ''],\n [\n 'With Outputs',\n analysis.withOutputs.toString(),\n pct(analysis.withOutputs),\n ],\n [\n 'Without Outputs',\n analysis.withoutOutputs.toString(),\n pct(analysis.withoutOutputs),\n ],\n ['', '', ''],\n [\n 'Root Frames',\n analysis.rootFrames.toString(),\n pct(analysis.rootFrames),\n ],\n [\n 'Leaf Frames',\n analysis.leafFrames.toString(),\n pct(analysis.leafFrames),\n ]\n );\n\n console.log(stateTable.toString());\n\n // Collection recommendations\n console.log(chalk.yellow('\\n\uD83D\uDCA1 Collection Recommendations:\\n'));\n const candidatesForCollection =\n analysis.closed + analysis.withoutOutputs;\n console.log(\n `\u2022 Potential collection candidates: ${candidatesForCollection} frames`\n );\n console.log(\n `\u2022 Estimated space savings: ${((candidatesForCollection / analysis.total) * 100).toFixed(1)}%`\n );\n\n if (analysis.old > 0) {\n console.log(`\u2022 ${analysis.old} old frames ready for collection`);\n }\n if (analysis.withoutOutputs > 0) {\n console.log(\n `\u2022 ${analysis.withoutOutputs} frames without outputs can be collected`\n );\n }\n } catch (error: unknown) {\n spinner.fail('Analysis failed');\n logger.error('GC analysis error', error);\n console.error(\n chalk.red(error instanceof Error ? error.message : 'Unknown error')\n );\n }\n });\n\n return gc;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/cli/commands/handoff.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Handoff command - Commits work and generates a prompt for the next session\n */\n\nimport { Command } from 'commander';\nimport { execSync } from 'child_process';\nimport { existsSync, readFileSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport Database from 'better-sqlite3';\nimport { FrameManager } from '../../core/context/frame-manager.js';\nimport { PebblesTaskStore } from '../../features/tasks/pebbles-task-store.js';\nimport { logger } from '../../core/monitoring/logger.js';\n// Type-safe environment variable access\nfunction getEnv(key: string, defaultValue?: string): string {\n const value = process.env[key];\n if (value === undefined) {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Environment variable ${key} is required`);\n }\n return value;\n}\n\nfunction getOptionalEnv(key: string): string | undefined {\n return process.env[key];\n}\n\n\nexport function createHandoffCommand(): Command {\n const cmd = new Command('handoff');\n\n cmd.description('Session handoff for continuity between Claude sessions');\n\n // Default action - capture handoff\n cmd\n .command('capture', { isDefault: true })\n .description('Commit current work and generate a handoff prompt')\n .option('-m, --message <message>', 'Custom commit message')\n .option('--no-commit', 'Skip git commit')\n .option('--copy', 'Copy the handoff prompt to clipboard')\n .action(async (options) => {\n try {\n const projectRoot = process.cwd();\n const dbPath = join(projectRoot, '.stackmemory', 'context.db');\n\n // 1. Check git status\n let gitStatus = '';\n let hasChanges = false;\n\n try {\n gitStatus = execSync('git status --short', {\n encoding: 'utf-8',\n cwd: projectRoot,\n });\n hasChanges = gitStatus.trim().length > 0;\n } catch (err: unknown) {\n console.log('\u26A0\uFE0F Not in a git repository');\n }\n\n // 2. Commit if there are changes and not skipped\n if (hasChanges && options.commit !== false) {\n try {\n // Get current branch\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf-8',\n cwd: projectRoot,\n }).trim();\n\n // Stage all changes\n execSync('git add -A', { cwd: projectRoot });\n\n // Generate or use custom commit message\n const commitMessage =\n options.message ||\n `chore: handoff checkpoint on ${currentBranch}`;\n\n // Commit\n execSync(`git commit -m \"${commitMessage}\"`, { cwd: projectRoot });\n\n console.log(`\u2705 Committed changes: \"${commitMessage}\"`);\n console.log(` Branch: ${currentBranch}`);\n } catch (err: unknown) {\n console.error(\n '\u274C Failed to commit changes:',\n (err as Error).message\n );\n }\n } else if (!hasChanges) {\n console.log('\u2139\uFE0F No changes to commit');\n }\n\n // 3. Gather context for handoff prompt\n let contextSummary = '';\n let tasksSummary = '';\n let recentWork = '';\n\n if (existsSync(dbPath)) {\n const db = new Database(dbPath);\n\n // Get recent context\n const frameManager = new FrameManager(db, 'cli-project');\n const activeFrames = frameManager.getActiveFramePath();\n\n if (activeFrames.length > 0) {\n contextSummary = 'Active context frames:\\n';\n activeFrames.forEach((frame) => {\n contextSummary += ` - ${frame.name} [${frame.type}]\\n`;\n });\n }\n\n // Get task status\n const taskStore = new PebblesTaskStore(projectRoot, db);\n const activeTasks = taskStore.getActiveTasks();\n\n const inProgress = activeTasks.filter(\n (t: any) => t.status === 'in_progress'\n );\n const todo = activeTasks.filter((t: any) => t.status === 'pending');\n const recentlyCompleted = activeTasks\n .filter((t: any) => t.status === 'completed' && t.completed_at)\n .sort(\n (a: any, b: any) => (b.completed_at || 0) - (a.completed_at || 0)\n )\n .slice(0, 3);\n\n if (inProgress.length > 0 || todo.length > 0) {\n tasksSummary = '\\nTasks:\\n';\n\n if (inProgress.length > 0) {\n tasksSummary += 'In Progress:\\n';\n inProgress.forEach((t: any) => {\n const externalId = t.external_refs?.linear?.id;\n tasksSummary += ` - ${t.title}${externalId ? ` [${externalId}]` : ''}\\n`;\n });\n }\n\n if (todo.length > 0) {\n tasksSummary += 'TODO:\\n';\n todo.slice(0, 5).forEach((t: any) => {\n const externalId = t.external_refs?.linear?.id;\n tasksSummary += ` - ${t.title}${externalId ? ` [${externalId}]` : ''}\\n`;\n });\n if (todo.length > 5) {\n tasksSummary += ` ... and ${todo.length - 5} more\\n`;\n }\n }\n }\n\n if (recentlyCompleted.length > 0) {\n recentWork = '\\nRecently Completed:\\n';\n recentlyCompleted.forEach((t: any) => {\n recentWork += ` \u2713 ${t.title}\\n`;\n });\n }\n\n // Get recent events\n const recentEvents = db\n .prepare(\n `\n SELECT event_type as type, payload as data, datetime(ts, 'unixepoch') as time\n FROM events\n ORDER BY ts DESC\n LIMIT 5\n `\n )\n .all() as any[];\n\n if (recentEvents.length > 0) {\n recentWork += '\\nRecent Activity:\\n';\n recentEvents.forEach((event) => {\n const data = JSON.parse(event.data);\n recentWork += ` - ${event.type}: ${data.message || data.name || 'activity'}\\n`;\n });\n }\n\n db.close();\n }\n\n // 4. Get current git info\n let gitInfo = '';\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf-8',\n cwd: projectRoot,\n }).trim();\n\n const lastCommit = execSync('git log -1 --oneline', {\n encoding: 'utf-8',\n cwd: projectRoot,\n }).trim();\n\n gitInfo = `\\nGit Status:\\n Branch: ${branch}\\n Last commit: ${lastCommit}\\n`;\n } catch (err: unknown) {\n // Ignore git errors\n }\n\n // 5. Check for any blockers or notes\n let notes = '';\n const notesPath = join(projectRoot, '.stackmemory', 'handoff.md');\n if (existsSync(notesPath)) {\n const handoffNotes = readFileSync(notesPath, 'utf-8');\n if (handoffNotes.trim()) {\n notes = `\\nNotes from previous handoff:\\n${handoffNotes}\\n`;\n }\n }\n\n // 6. Generate the handoff prompt\n const timestamp = new Date().toISOString();\n const handoffPrompt = `# Session Handoff - ${timestamp}\n\n## Project: ${projectRoot.split('/').pop()}\n\n${gitInfo}\n${contextSummary}\n${tasksSummary}\n${recentWork}\n${notes}\n\n## Continue from here:\n\n1. Run \\`stackmemory status\\` to check the current state\n2. Review any in-progress tasks above\n3. Check for any uncommitted changes with \\`git status\\`\n4. Resume work on the active context\n\n## Quick Commands:\n- \\`stackmemory context load --recent\\` - Load recent context\n- \\`stackmemory task list --state in_progress\\` - Show in-progress tasks\n- \\`stackmemory linear sync\\` - Sync with Linear if configured\n- \\`stackmemory log recent\\` - View recent activity\n\n---\nGenerated by stackmemory handoff at ${timestamp}\n`;\n\n // 7. Save handoff prompt\n const handoffPath = join(\n projectRoot,\n '.stackmemory',\n 'last-handoff.md'\n );\n writeFileSync(handoffPath, handoffPrompt);\n\n // 8. Display the prompt\n console.log('\\n' + '='.repeat(60));\n console.log(handoffPrompt);\n console.log('='.repeat(60));\n\n // 9. Copy to clipboard if requested\n if (options.copy) {\n try {\n const copyCommand =\n process.platform === 'darwin'\n ? 'pbcopy'\n : process.platform === 'win32'\n ? 'clip'\n : 'xclip -selection clipboard';\n\n execSync(copyCommand, {\n input: handoffPrompt,\n cwd: projectRoot,\n });\n\n console.log('\\n\u2705 Handoff prompt copied to clipboard!');\n } catch (err: unknown) {\n console.log('\\n\u26A0\uFE0F Could not copy to clipboard');\n }\n }\n\n console.log(`\\n\uD83D\uDCBE Handoff saved to: ${handoffPath}`);\n console.log('\uD83D\uDCCB Use this prompt when starting your next session');\n } catch (error: unknown) {\n logger.error('Handoff command failed', error as Error);\n console.error('\u274C Handoff failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\n // Restore command\n cmd\n .command('restore')\n .description('Restore context from last handoff')\n .option('--no-copy', 'Do not copy prompt to clipboard')\n .action(async (options) => {\n try {\n const projectRoot = process.cwd();\n const handoffPath = join(\n projectRoot,\n '.stackmemory',\n 'last-handoff.md'\n );\n const metaPath = join(\n process.env['HOME'] || '~',\n '.stackmemory',\n 'handoffs',\n 'last-handoff-meta.json'\n );\n\n if (!existsSync(handoffPath)) {\n console.log('\u274C No handoff found in this project');\n console.log('\uD83D\uDCA1 Run \"stackmemory handoff\" to create one');\n return;\n }\n\n // Read handoff prompt\n const handoffPrompt = readFileSync(handoffPath, 'utf-8');\n\n // Display the prompt\n console.log('\\n' + '='.repeat(60));\n console.log('\uD83D\uDCCB RESTORED HANDOFF');\n console.log('='.repeat(60));\n console.log(handoffPrompt);\n console.log('='.repeat(60));\n\n // Check for metadata\n if (existsSync(metaPath)) {\n const metadata = JSON.parse(readFileSync(metaPath, 'utf-8'));\n console.log('\\n\uD83D\uDCCA Session Metadata:');\n console.log(` Timestamp: ${metadata.timestamp}`);\n console.log(` Reason: ${metadata.reason}`);\n console.log(` Duration: ${metadata.session_duration}s`);\n console.log(` Command: ${metadata.command}`);\n }\n\n // Check current git status\n try {\n const gitStatus = execSync('git status --short', {\n encoding: 'utf-8',\n }).trim();\n if (gitStatus) {\n console.log('\\n\u26A0\uFE0F Current uncommitted changes:');\n console.log(gitStatus);\n }\n } catch (err: unknown) {\n // Not a git repo\n }\n\n // Copy to clipboard unless disabled\n if (options.copy !== false) {\n try {\n const copyCommand =\n process.platform === 'darwin'\n ? 'pbcopy'\n : process.platform === 'win32'\n ? 'clip'\n : 'xclip -selection clipboard';\n\n execSync(copyCommand, {\n input: handoffPrompt,\n cwd: projectRoot,\n });\n\n console.log('\\n\u2705 Handoff prompt copied to clipboard!');\n } catch (err: unknown) {\n console.log('\\n\u26A0\uFE0F Could not copy to clipboard');\n }\n }\n\n console.log('\\n\uD83D\uDE80 Ready to continue where you left off!');\n } catch (error: unknown) {\n logger.error('Handoff restore failed', error as Error);\n console.error('\u274C Restore failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\n // Auto command - enable auto-capture\n cmd\n .command('auto')\n .description('Enable auto-capture on session termination')\n .option('--command <command>', 'Command to wrap with auto-handoff')\n .action(async (options) => {\n const scriptPath = join(\n __dirname,\n '..',\n '..',\n '..',\n 'scripts',\n 'stackmemory-auto-handoff.sh'\n );\n\n if (!existsSync(scriptPath)) {\n console.error('\u274C Auto-handoff script not found');\n console.log('\uD83D\uDCE6 Please ensure StackMemory is properly installed');\n return;\n }\n\n console.log('\uD83D\uDEE1\uFE0F StackMemory Auto-Handoff');\n console.log('\u2500'.repeat(50));\n\n if (options.command) {\n // Wrap specific command\n console.log(`Wrapping command: ${options.command}`);\n execSync(`${scriptPath} ${options.command}`, {\n stdio: 'inherit',\n env: { ...process.env, AUTO_CAPTURE_ON_EXIT: 'true' },\n });\n } else {\n // Interactive mode\n console.log('To enable auto-handoff for your current session:');\n console.log('');\n console.log(' For bash/zsh:');\n console.log(` source <(${scriptPath} --shell)`);\n console.log('');\n console.log(' Or wrap a command:');\n console.log(` ${scriptPath} claude`);\n console.log(` ${scriptPath} npm run dev`);\n console.log('');\n console.log(' Add to your shell profile for permanent setup:');\n console.log(\n ` echo 'alias claude=\"${scriptPath} claude\"' >> ~/.bashrc`\n );\n }\n });\n\n return cmd;\n}\n"],
|
|
5
|
-
"mappings": "AAIA,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,YAAY;AACrB,OAAO,cAAc;AACrB,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC,SAAS,cAAc;AAEvB,SAAS,OAAO,KAAa,cAA+B;AAC1D,QAAM,QAAQ,QAAQ,IAAI,GAAG;AAC7B,MAAI,UAAU,QAAW;AACvB,QAAI,iBAAiB,OAAW,QAAO;AACvC,UAAM,IAAI,MAAM,wBAAwB,GAAG,cAAc;AAAA,EAC3D;AACA,SAAO;AACT;AAEA,SAAS,eAAe,KAAiC;AACvD,SAAO,QAAQ,IAAI,GAAG;AACxB;
|
|
4
|
+
"sourcesContent": ["/**\n * Handoff command - Commits work and generates a prompt for the next session\n */\n\nimport { Command } from 'commander';\nimport { execSync } from 'child_process';\nimport { existsSync, readFileSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport Database from 'better-sqlite3';\nimport { FrameManager } from '../../core/context/frame-manager.js';\nimport { PebblesTaskStore } from '../../features/tasks/pebbles-task-store.js';\nimport { logger } from '../../core/monitoring/logger.js';\n// Type-safe environment variable access\nfunction getEnv(key: string, defaultValue?: string): string {\n const value = process.env[key];\n if (value === undefined) {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Environment variable ${key} is required`);\n }\n return value;\n}\n\nfunction getOptionalEnv(key: string): string | undefined {\n return process.env[key];\n}\n\nexport function createHandoffCommand(): Command {\n const cmd = new Command('handoff');\n\n cmd.description('Session handoff for continuity between Claude sessions');\n\n // Default action - capture handoff\n cmd\n .command('capture', { isDefault: true })\n .description('Commit current work and generate a handoff prompt')\n .option('-m, --message <message>', 'Custom commit message')\n .option('--no-commit', 'Skip git commit')\n .option('--copy', 'Copy the handoff prompt to clipboard')\n .action(async (options) => {\n try {\n const projectRoot = process.cwd();\n const dbPath = join(projectRoot, '.stackmemory', 'context.db');\n\n // 1. Check git status\n let gitStatus = '';\n let hasChanges = false;\n\n try {\n gitStatus = execSync('git status --short', {\n encoding: 'utf-8',\n cwd: projectRoot,\n });\n hasChanges = gitStatus.trim().length > 0;\n } catch (err: unknown) {\n console.log('\u26A0\uFE0F Not in a git repository');\n }\n\n // 2. Commit if there are changes and not skipped\n if (hasChanges && options.commit !== false) {\n try {\n // Get current branch\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf-8',\n cwd: projectRoot,\n }).trim();\n\n // Stage all changes\n execSync('git add -A', { cwd: projectRoot });\n\n // Generate or use custom commit message\n const commitMessage =\n options.message ||\n `chore: handoff checkpoint on ${currentBranch}`;\n\n // Commit\n execSync(`git commit -m \"${commitMessage}\"`, { cwd: projectRoot });\n\n console.log(`\u2705 Committed changes: \"${commitMessage}\"`);\n console.log(` Branch: ${currentBranch}`);\n } catch (err: unknown) {\n console.error(\n '\u274C Failed to commit changes:',\n (err as Error).message\n );\n }\n } else if (!hasChanges) {\n console.log('\u2139\uFE0F No changes to commit');\n }\n\n // 3. Gather context for handoff prompt\n let contextSummary = '';\n let tasksSummary = '';\n let recentWork = '';\n\n if (existsSync(dbPath)) {\n const db = new Database(dbPath);\n\n // Get recent context\n const frameManager = new FrameManager(db, 'cli-project');\n const activeFrames = frameManager.getActiveFramePath();\n\n if (activeFrames.length > 0) {\n contextSummary = 'Active context frames:\\n';\n activeFrames.forEach((frame) => {\n contextSummary += ` - ${frame.name} [${frame.type}]\\n`;\n });\n }\n\n // Get task status\n const taskStore = new PebblesTaskStore(projectRoot, db);\n const activeTasks = taskStore.getActiveTasks();\n\n const inProgress = activeTasks.filter(\n (t: any) => t.status === 'in_progress'\n );\n const todo = activeTasks.filter((t: any) => t.status === 'pending');\n const recentlyCompleted = activeTasks\n .filter((t: any) => t.status === 'completed' && t.completed_at)\n .sort(\n (a: any, b: any) => (b.completed_at || 0) - (a.completed_at || 0)\n )\n .slice(0, 3);\n\n if (inProgress.length > 0 || todo.length > 0) {\n tasksSummary = '\\nTasks:\\n';\n\n if (inProgress.length > 0) {\n tasksSummary += 'In Progress:\\n';\n inProgress.forEach((t: any) => {\n const externalId = t.external_refs?.linear?.id;\n tasksSummary += ` - ${t.title}${externalId ? ` [${externalId}]` : ''}\\n`;\n });\n }\n\n if (todo.length > 0) {\n tasksSummary += 'TODO:\\n';\n todo.slice(0, 5).forEach((t: any) => {\n const externalId = t.external_refs?.linear?.id;\n tasksSummary += ` - ${t.title}${externalId ? ` [${externalId}]` : ''}\\n`;\n });\n if (todo.length > 5) {\n tasksSummary += ` ... and ${todo.length - 5} more\\n`;\n }\n }\n }\n\n if (recentlyCompleted.length > 0) {\n recentWork = '\\nRecently Completed:\\n';\n recentlyCompleted.forEach((t: any) => {\n recentWork += ` \u2713 ${t.title}\\n`;\n });\n }\n\n // Get recent events\n const recentEvents = db\n .prepare(\n `\n SELECT event_type as type, payload as data, datetime(ts, 'unixepoch') as time\n FROM events\n ORDER BY ts DESC\n LIMIT 5\n `\n )\n .all() as any[];\n\n if (recentEvents.length > 0) {\n recentWork += '\\nRecent Activity:\\n';\n recentEvents.forEach((event) => {\n const data = JSON.parse(event.data);\n recentWork += ` - ${event.type}: ${data.message || data.name || 'activity'}\\n`;\n });\n }\n\n db.close();\n }\n\n // 4. Get current git info\n let gitInfo = '';\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf-8',\n cwd: projectRoot,\n }).trim();\n\n const lastCommit = execSync('git log -1 --oneline', {\n encoding: 'utf-8',\n cwd: projectRoot,\n }).trim();\n\n gitInfo = `\\nGit Status:\\n Branch: ${branch}\\n Last commit: ${lastCommit}\\n`;\n } catch (err: unknown) {\n // Ignore git errors\n }\n\n // 5. Check for any blockers or notes\n let notes = '';\n const notesPath = join(projectRoot, '.stackmemory', 'handoff.md');\n if (existsSync(notesPath)) {\n const handoffNotes = readFileSync(notesPath, 'utf-8');\n if (handoffNotes.trim()) {\n notes = `\\nNotes from previous handoff:\\n${handoffNotes}\\n`;\n }\n }\n\n // 6. Generate the handoff prompt\n const timestamp = new Date().toISOString();\n const handoffPrompt = `# Session Handoff - ${timestamp}\n\n## Project: ${projectRoot.split('/').pop()}\n\n${gitInfo}\n${contextSummary}\n${tasksSummary}\n${recentWork}\n${notes}\n\n## Continue from here:\n\n1. Run \\`stackmemory status\\` to check the current state\n2. Review any in-progress tasks above\n3. Check for any uncommitted changes with \\`git status\\`\n4. Resume work on the active context\n\n## Quick Commands:\n- \\`stackmemory context load --recent\\` - Load recent context\n- \\`stackmemory task list --state in_progress\\` - Show in-progress tasks\n- \\`stackmemory linear sync\\` - Sync with Linear if configured\n- \\`stackmemory log recent\\` - View recent activity\n\n---\nGenerated by stackmemory handoff at ${timestamp}\n`;\n\n // 7. Save handoff prompt\n const handoffPath = join(\n projectRoot,\n '.stackmemory',\n 'last-handoff.md'\n );\n writeFileSync(handoffPath, handoffPrompt);\n\n // 8. Display the prompt\n console.log('\\n' + '='.repeat(60));\n console.log(handoffPrompt);\n console.log('='.repeat(60));\n\n // 9. Copy to clipboard if requested\n if (options.copy) {\n try {\n const copyCommand =\n process.platform === 'darwin'\n ? 'pbcopy'\n : process.platform === 'win32'\n ? 'clip'\n : 'xclip -selection clipboard';\n\n execSync(copyCommand, {\n input: handoffPrompt,\n cwd: projectRoot,\n });\n\n console.log('\\n\u2705 Handoff prompt copied to clipboard!');\n } catch (err: unknown) {\n console.log('\\n\u26A0\uFE0F Could not copy to clipboard');\n }\n }\n\n console.log(`\\n\uD83D\uDCBE Handoff saved to: ${handoffPath}`);\n console.log('\uD83D\uDCCB Use this prompt when starting your next session');\n } catch (error: unknown) {\n logger.error('Handoff command failed', error as Error);\n console.error('\u274C Handoff failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\n // Restore command\n cmd\n .command('restore')\n .description('Restore context from last handoff')\n .option('--no-copy', 'Do not copy prompt to clipboard')\n .action(async (options) => {\n try {\n const projectRoot = process.cwd();\n const handoffPath = join(\n projectRoot,\n '.stackmemory',\n 'last-handoff.md'\n );\n const metaPath = join(\n process.env['HOME'] || '~',\n '.stackmemory',\n 'handoffs',\n 'last-handoff-meta.json'\n );\n\n if (!existsSync(handoffPath)) {\n console.log('\u274C No handoff found in this project');\n console.log('\uD83D\uDCA1 Run \"stackmemory handoff\" to create one');\n return;\n }\n\n // Read handoff prompt\n const handoffPrompt = readFileSync(handoffPath, 'utf-8');\n\n // Display the prompt\n console.log('\\n' + '='.repeat(60));\n console.log('\uD83D\uDCCB RESTORED HANDOFF');\n console.log('='.repeat(60));\n console.log(handoffPrompt);\n console.log('='.repeat(60));\n\n // Check for metadata\n if (existsSync(metaPath)) {\n const metadata = JSON.parse(readFileSync(metaPath, 'utf-8'));\n console.log('\\n\uD83D\uDCCA Session Metadata:');\n console.log(` Timestamp: ${metadata.timestamp}`);\n console.log(` Reason: ${metadata.reason}`);\n console.log(` Duration: ${metadata.session_duration}s`);\n console.log(` Command: ${metadata.command}`);\n }\n\n // Check current git status\n try {\n const gitStatus = execSync('git status --short', {\n encoding: 'utf-8',\n }).trim();\n if (gitStatus) {\n console.log('\\n\u26A0\uFE0F Current uncommitted changes:');\n console.log(gitStatus);\n }\n } catch (err: unknown) {\n // Not a git repo\n }\n\n // Copy to clipboard unless disabled\n if (options.copy !== false) {\n try {\n const copyCommand =\n process.platform === 'darwin'\n ? 'pbcopy'\n : process.platform === 'win32'\n ? 'clip'\n : 'xclip -selection clipboard';\n\n execSync(copyCommand, {\n input: handoffPrompt,\n cwd: projectRoot,\n });\n\n console.log('\\n\u2705 Handoff prompt copied to clipboard!');\n } catch (err: unknown) {\n console.log('\\n\u26A0\uFE0F Could not copy to clipboard');\n }\n }\n\n console.log('\\n\uD83D\uDE80 Ready to continue where you left off!');\n } catch (error: unknown) {\n logger.error('Handoff restore failed', error as Error);\n console.error('\u274C Restore failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\n // Auto command - enable auto-capture\n cmd\n .command('auto')\n .description('Enable auto-capture on session termination')\n .option('--command <command>', 'Command to wrap with auto-handoff')\n .action(async (options) => {\n const scriptPath = join(\n __dirname,\n '..',\n '..',\n '..',\n 'scripts',\n 'stackmemory-auto-handoff.sh'\n );\n\n if (!existsSync(scriptPath)) {\n console.error('\u274C Auto-handoff script not found');\n console.log('\uD83D\uDCE6 Please ensure StackMemory is properly installed');\n return;\n }\n\n console.log('\uD83D\uDEE1\uFE0F StackMemory Auto-Handoff');\n console.log('\u2500'.repeat(50));\n\n if (options.command) {\n // Wrap specific command\n console.log(`Wrapping command: ${options.command}`);\n execSync(`${scriptPath} ${options.command}`, {\n stdio: 'inherit',\n env: { ...process.env, AUTO_CAPTURE_ON_EXIT: 'true' },\n });\n } else {\n // Interactive mode\n console.log('To enable auto-handoff for your current session:');\n console.log('');\n console.log(' For bash/zsh:');\n console.log(` source <(${scriptPath} --shell)`);\n console.log('');\n console.log(' Or wrap a command:');\n console.log(` ${scriptPath} claude`);\n console.log(` ${scriptPath} npm run dev`);\n console.log('');\n console.log(' Add to your shell profile for permanent setup:');\n console.log(\n ` echo 'alias claude=\"${scriptPath} claude\"' >> ~/.bashrc`\n );\n }\n });\n\n return cmd;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,7 +2,9 @@ import { Command } from "commander";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import ora from "ora";
|
|
4
4
|
import Table from "cli-table3";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
InfiniteStorageSystem
|
|
7
|
+
} from "../../core/storage/infinite-storage.js";
|
|
6
8
|
import { FrameManager } from "../../core/context/frame-manager.js";
|
|
7
9
|
import { Logger } from "../../core/monitoring/logger.js";
|
|
8
10
|
import dotenv from "dotenv";
|
|
@@ -70,12 +72,17 @@ function createInfiniteStorageCommand() {
|
|
|
70
72
|
}
|
|
71
73
|
if (updates.length > 0) {
|
|
72
74
|
const fs = await import("fs");
|
|
73
|
-
fs.appendFileSync(
|
|
75
|
+
fs.appendFileSync(
|
|
76
|
+
envPath,
|
|
77
|
+
"\n# Infinite Storage Configuration\n" + updates.join("\n") + "\n"
|
|
78
|
+
);
|
|
74
79
|
}
|
|
75
80
|
} catch (error) {
|
|
76
81
|
spinner.fail("Failed to initialize storage");
|
|
77
82
|
logger.error("Initialization error", error);
|
|
78
|
-
console.error(
|
|
83
|
+
console.error(
|
|
84
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
85
|
+
);
|
|
79
86
|
}
|
|
80
87
|
});
|
|
81
88
|
storage.command("store").description("Store current frames in infinite storage").option("--project <name>", "Project name").option("--user <id>", "User ID").action(async (options) => {
|
|
@@ -94,7 +101,9 @@ function createInfiniteStorageCommand() {
|
|
|
94
101
|
} catch (error) {
|
|
95
102
|
spinner.fail("Failed to store frames");
|
|
96
103
|
logger.error("Store error", error);
|
|
97
|
-
console.error(
|
|
104
|
+
console.error(
|
|
105
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
106
|
+
);
|
|
98
107
|
}
|
|
99
108
|
});
|
|
100
109
|
storage.command("retrieve <frameId>").description("Retrieve a frame from storage").option("--user <id>", "User ID").action(async (frameId, options) => {
|
|
@@ -115,7 +124,9 @@ function createInfiniteStorageCommand() {
|
|
|
115
124
|
} catch (error) {
|
|
116
125
|
spinner.fail("Failed to retrieve frame");
|
|
117
126
|
logger.error("Retrieve error", error);
|
|
118
|
-
console.error(
|
|
127
|
+
console.error(
|
|
128
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
129
|
+
);
|
|
119
130
|
}
|
|
120
131
|
});
|
|
121
132
|
storage.command("metrics").description("Show storage system metrics").action(async () => {
|
|
@@ -141,20 +152,30 @@ function createInfiniteStorageCommand() {
|
|
|
141
152
|
console.log(table.toString());
|
|
142
153
|
if (Object.keys(metrics.tierDistribution).length > 0) {
|
|
143
154
|
console.log("\nTier Distribution:");
|
|
144
|
-
for (const [tier, count] of Object.entries(
|
|
145
|
-
|
|
155
|
+
for (const [tier, count] of Object.entries(
|
|
156
|
+
metrics.tierDistribution
|
|
157
|
+
)) {
|
|
158
|
+
const percentage = (count / metrics.totalObjects * 100).toFixed(
|
|
159
|
+
1
|
|
160
|
+
);
|
|
146
161
|
console.log(` ${tier}: ${count} objects (${percentage}%)`);
|
|
147
162
|
}
|
|
148
163
|
}
|
|
149
164
|
console.log(chalk.cyan("\n\u{1F3AF} STA-287 Performance Targets:"));
|
|
150
165
|
const p50Target = metrics.p50LatencyMs <= 50;
|
|
151
166
|
const p99Target = metrics.p99LatencyMs <= 500;
|
|
152
|
-
console.log(
|
|
153
|
-
|
|
167
|
+
console.log(
|
|
168
|
+
` P50 \u2264 50ms: ${p50Target ? chalk.green("\u2705 PASS") : chalk.red("\u274C FAIL")} (${metrics.p50LatencyMs}ms)`
|
|
169
|
+
);
|
|
170
|
+
console.log(
|
|
171
|
+
` P99 \u2264 500ms: ${p99Target ? chalk.green("\u2705 PASS") : chalk.red("\u274C FAIL")} (${metrics.p99LatencyMs}ms)`
|
|
172
|
+
);
|
|
154
173
|
} catch (error) {
|
|
155
174
|
spinner.fail("Failed to get metrics");
|
|
156
175
|
logger.error("Metrics error", error);
|
|
157
|
-
console.error(
|
|
176
|
+
console.error(
|
|
177
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
178
|
+
);
|
|
158
179
|
}
|
|
159
180
|
});
|
|
160
181
|
storage.command("migrate").description("Manually trigger tier migration").action(async () => {
|
|
@@ -168,7 +189,9 @@ function createInfiniteStorageCommand() {
|
|
|
168
189
|
} catch (error) {
|
|
169
190
|
spinner.fail("Migration failed");
|
|
170
191
|
logger.error("Migration error", error);
|
|
171
|
-
console.error(
|
|
192
|
+
console.error(
|
|
193
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
194
|
+
);
|
|
172
195
|
}
|
|
173
196
|
});
|
|
174
197
|
storage.command("status").description("Check storage system status").action(async () => {
|
|
@@ -194,26 +217,44 @@ function createInfiniteStorageCommand() {
|
|
|
194
217
|
if (config.timeseries?.connectionString) {
|
|
195
218
|
try {
|
|
196
219
|
const { Pool } = await import("pg");
|
|
197
|
-
const pool = new Pool({
|
|
220
|
+
const pool = new Pool({
|
|
221
|
+
connectionString: config.timeseries.connectionString
|
|
222
|
+
});
|
|
198
223
|
await pool.query("SELECT 1");
|
|
199
224
|
await pool.end();
|
|
200
|
-
console.log(
|
|
225
|
+
console.log(
|
|
226
|
+
"TimeSeries DB (Warm Tier): " + chalk.green("\u2705 Connected")
|
|
227
|
+
);
|
|
201
228
|
} catch {
|
|
202
|
-
console.log(
|
|
229
|
+
console.log(
|
|
230
|
+
"TimeSeries DB (Warm Tier): " + chalk.red("\u274C Not connected")
|
|
231
|
+
);
|
|
203
232
|
}
|
|
204
233
|
} else {
|
|
205
|
-
console.log(
|
|
234
|
+
console.log(
|
|
235
|
+
"TimeSeries DB (Warm Tier): " + chalk.yellow("\u26A0\uFE0F Not configured")
|
|
236
|
+
);
|
|
206
237
|
}
|
|
207
238
|
if (config.s3?.bucket) {
|
|
208
|
-
console.log(
|
|
239
|
+
console.log(
|
|
240
|
+
`S3 (Cold/Archive Tier): ${chalk.green("\u2705")} Bucket: ${config.s3.bucket}`
|
|
241
|
+
);
|
|
209
242
|
} else {
|
|
210
|
-
console.log(
|
|
243
|
+
console.log(
|
|
244
|
+
"S3 (Cold/Archive Tier): " + chalk.yellow("\u26A0\uFE0F Not configured")
|
|
245
|
+
);
|
|
211
246
|
}
|
|
212
|
-
console.log(
|
|
247
|
+
console.log(
|
|
248
|
+
"\n" + chalk.gray(
|
|
249
|
+
"Configure missing tiers with: stackmemory infinite-storage init"
|
|
250
|
+
)
|
|
251
|
+
);
|
|
213
252
|
} catch (error) {
|
|
214
253
|
spinner.fail("Failed to check status");
|
|
215
254
|
logger.error("Status error", error);
|
|
216
|
-
console.error(
|
|
255
|
+
console.error(
|
|
256
|
+
chalk.red(error instanceof Error ? error.message : "Unknown error")
|
|
257
|
+
);
|
|
217
258
|
}
|
|
218
259
|
});
|
|
219
260
|
return storage;
|