@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
|
@@ -54,13 +54,17 @@ class PRTracker extends EventEmitter {
|
|
|
54
54
|
}
|
|
55
55
|
getChecksStatus(checks) {
|
|
56
56
|
if (!checks) return "{gray-fg}no checks{/}";
|
|
57
|
-
if (checks.failed > 0)
|
|
58
|
-
|
|
57
|
+
if (checks.failed > 0)
|
|
58
|
+
return `{red-fg}\u2717 ${checks.failed}/${checks.total}{/}`;
|
|
59
|
+
if (checks.pending > 0)
|
|
60
|
+
return `{yellow-fg}\u23F3 ${checks.pending}/${checks.total}{/}`;
|
|
59
61
|
return `{green-fg}\u2713 ${checks.passed}/${checks.total}{/}`;
|
|
60
62
|
}
|
|
61
63
|
getReviewStatus(reviews) {
|
|
62
64
|
const approved = reviews.filter((r) => r.state === "approved").length;
|
|
63
|
-
const changes = reviews.filter(
|
|
65
|
+
const changes = reviews.filter(
|
|
66
|
+
(r) => r.state === "changes_requested"
|
|
67
|
+
).length;
|
|
64
68
|
if (changes > 0) return `{red-fg}\u{1F44E}${changes}{/}`;
|
|
65
69
|
if (approved > 0) return `{green-fg}\u{1F44D}${approved}{/}`;
|
|
66
70
|
return "{gray-fg}no reviews{/}";
|
|
@@ -80,12 +84,20 @@ class PRTracker extends EventEmitter {
|
|
|
80
84
|
let items = [];
|
|
81
85
|
let label = "";
|
|
82
86
|
if (this.viewMode === "prs") {
|
|
83
|
-
items = Array.from(this.prs.values()).map(
|
|
84
|
-
|
|
87
|
+
items = Array.from(this.prs.values()).map(
|
|
88
|
+
(pr) => this.formatPRItem(pr)
|
|
89
|
+
);
|
|
90
|
+
const open = Array.from(this.prs.values()).filter(
|
|
91
|
+
(pr) => pr.state === "open"
|
|
92
|
+
).length;
|
|
85
93
|
label = ` \u{1F500} Pull Requests (${open}/${this.prs.size}) [Tab] Issues `;
|
|
86
94
|
} else {
|
|
87
|
-
items = Array.from(this.issues.values()).map(
|
|
88
|
-
|
|
95
|
+
items = Array.from(this.issues.values()).map(
|
|
96
|
+
(issue) => this.formatIssueItem(issue)
|
|
97
|
+
);
|
|
98
|
+
const open = Array.from(this.issues.values()).filter(
|
|
99
|
+
(i) => i.state === "open"
|
|
100
|
+
).length;
|
|
89
101
|
label = ` \u{1F41B} Issues (${open}/${this.issues.size}) [Tab] PRs `;
|
|
90
102
|
}
|
|
91
103
|
this.list.setItems(items);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/features/tui/components/pr-tracker.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * PR/Issue Tracker Component\n * Displays GitHub PRs and issues with status indicators\n */\n\nimport blessed from 'blessed';\nimport { EventEmitter } from 'events';\nimport type { PRData, IssueData } from '../types.js';\n\nexport class PRTracker extends EventEmitter {\n private list: blessed.Widgets.ListElement;\n private prs: Map<string, PRData>;\n private issues: Map<string, IssueData>;\n private selectedItem: string | null = null;\n private viewMode: 'prs' | 'issues' = 'prs';\n\n constructor(list: blessed.Widgets.ListElement) {\n super();\n this.list = list;\n this.prs = new Map();\n this.issues = new Map();\n this.initializeUI();\n }\n\n private initializeUI(): void {\n this.list.on('select', (item, index) => {\n if (this.viewMode === 'prs') {\n const prId = Array.from(this.prs.keys())[index];\n this.selectPR(prId);\n } else {\n const issueId = Array.from(this.issues.keys())[index];\n this.selectIssue(issueId);\n }\n });\n\n // Toggle between PRs and Issues\n this.list.key(['tab'], () => {\n this.toggleView();\n });\n }\n\n private formatPRItem(pr: PRData): string {\n const status = this.getPRStatusIcon(pr);\n const checks = this.getChecksStatus(pr.checks);\n const reviews = this.getReviewStatus(pr.reviews);\n
|
|
5
|
-
"mappings": "AAMA,SAAS,oBAAoB;AAGtB,MAAM,kBAAkB,aAAa;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAA8B;AAAA,EAC9B,WAA6B;AAAA,EAErC,YAAY,MAAmC;AAC7C,UAAM;AACN,SAAK,OAAO;AACZ,SAAK,MAAM,oBAAI,IAAI;AACnB,SAAK,SAAS,oBAAI,IAAI;AACtB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,eAAqB;AAC3B,SAAK,KAAK,GAAG,UAAU,CAAC,MAAM,UAAU;AACtC,UAAI,KAAK,aAAa,OAAO;AAC3B,cAAM,OAAO,MAAM,KAAK,KAAK,IAAI,KAAK,CAAC,EAAE,KAAK;AAC9C,aAAK,SAAS,IAAI;AAAA,MACpB,OAAO;AACL,cAAM,UAAU,MAAM,KAAK,KAAK,OAAO,KAAK,CAAC,EAAE,KAAK;AACpD,aAAK,YAAY,OAAO;AAAA,MAC1B;AAAA,IACF,CAAC;AAGD,SAAK,KAAK,IAAI,CAAC,KAAK,GAAG,MAAM;AAC3B,WAAK,WAAW;AAAA,IAClB,CAAC;AAAA,EACH;AAAA,EAEQ,aAAa,IAAoB;AACvC,UAAM,SAAS,KAAK,gBAAgB,EAAE;AACtC,UAAM,SAAS,KAAK,gBAAgB,GAAG,MAAM;AAC7C,UAAM,UAAU,KAAK,gBAAgB,GAAG,OAAO;AAE/C,QAAI,OAAO,GAAG,MAAM,KAAK,GAAG,MAAM,KAAK,GAAG,KAAK;AAAA;AAC/C,YAAQ,gBAAgB,GAAG,OAAO,KAAK,MAAM,MAAM,MAAM,OAAO,OAAO,GAAG,SAAS,KAAK,GAAG,SAAS;AAEpG,QAAI,GAAG,cAAc,QAAQ;AAC3B,cAAQ,wBAAiB,GAAG,aAAa,MAAM;AAAA,IACjD;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,gBAAgB,OAA0B;AAChD,UAAM,SAAS,MAAM,UAAU,SAAS,wBAAmB;AAC3D,UAAM,
|
|
4
|
+
"sourcesContent": ["/**\n * PR/Issue Tracker Component\n * Displays GitHub PRs and issues with status indicators\n */\n\nimport blessed from 'blessed';\nimport { EventEmitter } from 'events';\nimport type { PRData, IssueData } from '../types.js';\n\nexport class PRTracker extends EventEmitter {\n private list: blessed.Widgets.ListElement;\n private prs: Map<string, PRData>;\n private issues: Map<string, IssueData>;\n private selectedItem: string | null = null;\n private viewMode: 'prs' | 'issues' = 'prs';\n\n constructor(list: blessed.Widgets.ListElement) {\n super();\n this.list = list;\n this.prs = new Map();\n this.issues = new Map();\n this.initializeUI();\n }\n\n private initializeUI(): void {\n this.list.on('select', (item, index) => {\n if (this.viewMode === 'prs') {\n const prId = Array.from(this.prs.keys())[index];\n this.selectPR(prId);\n } else {\n const issueId = Array.from(this.issues.keys())[index];\n this.selectIssue(issueId);\n }\n });\n\n // Toggle between PRs and Issues\n this.list.key(['tab'], () => {\n this.toggleView();\n });\n }\n\n private formatPRItem(pr: PRData): string {\n const status = this.getPRStatusIcon(pr);\n const checks = this.getChecksStatus(pr.checks);\n const reviews = this.getReviewStatus(pr.reviews);\n\n let item = `${status} #${pr.number}: ${pr.title}\\n`;\n item += ` {gray-fg}@${pr.author.login} | ${checks} | ${reviews} | +${pr.additions}/-${pr.deletions}{/}`;\n\n if (pr.linkedIssues?.length) {\n item += ` | {cyan-fg}\uD83D\uDD17${pr.linkedIssues.length}{/}`;\n }\n\n return item;\n }\n\n private formatIssueItem(issue: IssueData): string {\n const status = issue.state === 'open' ? '{green-fg}\u25CF{/}' : '{gray-fg}\u25CF{/}';\n const assignees =\n issue.assignees?.map((a: any) => `@${a}`).join(', ') || 'unassigned';\n\n let item = `${status} #${issue.number}: ${issue.title}\\n`;\n item += ` {gray-fg}@${issue.author.login} | ${assignees} | \uD83D\uDCAC${issue.comments}{/}`;\n\n return item;\n }\n\n private getPRStatusIcon(pr: PRData): string {\n if (pr.state === 'merged') return '{magenta-fg}\u2B24{/}';\n if (pr.state === 'closed') return '{red-fg}\u2B24{/}';\n if (pr.draft) return '{gray-fg}\u25CB{/}';\n return '{green-fg}\u25CF{/}';\n }\n\n private getChecksStatus(checks?: PRData['checks']): string {\n if (!checks) return '{gray-fg}no checks{/}';\n if (checks.failed > 0)\n return `{red-fg}\u2717 ${checks.failed}/${checks.total}{/}`;\n if (checks.pending > 0)\n return `{yellow-fg}\u23F3 ${checks.pending}/${checks.total}{/}`;\n return `{green-fg}\u2713 ${checks.passed}/${checks.total}{/}`;\n }\n\n private getReviewStatus(reviews: PRData['reviews']): string {\n const approved = reviews.filter((r: any) => r.state === 'approved').length;\n const changes = reviews.filter(\n (r: any) => r.state === 'changes_requested'\n ).length;\n\n if (changes > 0) return `{red-fg}\uD83D\uDC4E${changes}{/}`;\n if (approved > 0) return `{green-fg}\uD83D\uDC4D${approved}{/}`;\n return '{gray-fg}no reviews{/}';\n }\n\n public update(data: { prs?: PRData[]; issues?: IssueData[] }): void {\n if (data.prs) {\n this.prs.clear();\n data.prs.forEach((pr) => this.prs.set(pr.id, pr));\n }\n\n if (data.issues) {\n this.issues.clear();\n data.issues.forEach((issue) => this.issues.set(issue.id, issue));\n }\n\n this.refreshDisplay();\n }\n\n private refreshDisplay(): void {\n let items: string[] = [];\n let label = '';\n\n if (this.viewMode === 'prs') {\n items = Array.from(this.prs.values()).map((pr: any) =>\n this.formatPRItem(pr)\n );\n const open = Array.from(this.prs.values()).filter(\n (pr: any) => pr.state === 'open'\n ).length;\n label = ` \uD83D\uDD00 Pull Requests (${open}/${this.prs.size}) [Tab] Issues `;\n } else {\n items = Array.from(this.issues.values()).map((issue: any) =>\n this.formatIssueItem(issue)\n );\n const open = Array.from(this.issues.values()).filter(\n (i: any) => i.state === 'open'\n ).length;\n label = ` \uD83D\uDC1B Issues (${open}/${this.issues.size}) [Tab] PRs `;\n }\n\n this.list.setItems(items);\n if (\n this.list.parent &&\n typeof (this.list.parent as any).setLabel === 'function'\n ) {\n (this.list.parent as any).setLabel(label);\n }\n\n this.list.screen.render();\n }\n\n private toggleView(): void {\n this.viewMode = this.viewMode === 'prs' ? 'issues' : 'prs';\n this.refreshDisplay();\n }\n\n private selectPR(prId: string): void {\n const pr = this.prs.get(prId);\n if (pr) {\n this.emit('pr:selected', pr);\n }\n }\n\n private selectIssue(issueId: string): void {\n const issue = this.issues.get(issueId);\n if (issue) {\n this.emit('issue:selected', issue);\n }\n }\n\n public focus(): void {\n this.list.focus();\n }\n\n public hasFocus(): boolean {\n return this.list === this.list.screen.focused;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -60,11 +60,15 @@ class SessionMonitor extends EventEmitter {
|
|
|
60
60
|
if (session.recentActivities) {
|
|
61
61
|
const files = session.recentActivities.filter((a) => a.type === "file_edit").map((a) => a.data?.path || "");
|
|
62
62
|
if (files.some((f) => f.includes("test"))) tags.push("testing");
|
|
63
|
-
if (files.some((f) => f.includes(".tsx") || f.includes(".jsx")))
|
|
64
|
-
|
|
65
|
-
if (files.some((f) => f.includes("
|
|
63
|
+
if (files.some((f) => f.includes(".tsx") || f.includes(".jsx")))
|
|
64
|
+
tags.push("frontend");
|
|
65
|
+
if (files.some((f) => f.includes("api") || f.includes("server")))
|
|
66
|
+
tags.push("backend");
|
|
67
|
+
if (files.some((f) => f.includes("db") || f.includes("migration")))
|
|
68
|
+
tags.push("database");
|
|
66
69
|
const commands = session.recentActivities.filter((a) => a.type === "command").map((a) => a.data?.command || "");
|
|
67
|
-
if (commands.some((c) => c.includes("npm test") || c.includes("jest")))
|
|
70
|
+
if (commands.some((c) => c.includes("npm test") || c.includes("jest")))
|
|
71
|
+
tags.push("testing");
|
|
68
72
|
if (commands.some((c) => c.includes("git"))) tags.push("git-ops");
|
|
69
73
|
if (commands.some((c) => c.includes("deploy"))) tags.push("deployment");
|
|
70
74
|
if (commands.some((c) => c.includes("debug"))) tags.push("debugging");
|
|
@@ -72,13 +76,16 @@ class SessionMonitor extends EventEmitter {
|
|
|
72
76
|
if (session.linearTask) {
|
|
73
77
|
tags.push(`linear:${session.linearTask.identifier}`);
|
|
74
78
|
if (session.linearTask.labels) {
|
|
75
|
-
tags.push(
|
|
79
|
+
tags.push(
|
|
80
|
+
...session.linearTask.labels.map((l) => l.toLowerCase())
|
|
81
|
+
);
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
84
|
if (session.gitBranch) {
|
|
79
85
|
const branch = session.gitBranch;
|
|
80
86
|
if (branch.includes("feature/")) tags.push("feature");
|
|
81
|
-
if (branch.includes("fix/") || branch.includes("bugfix/"))
|
|
87
|
+
if (branch.includes("fix/") || branch.includes("bugfix/"))
|
|
88
|
+
tags.push("bugfix");
|
|
82
89
|
if (branch.includes("refactor/")) tags.push("refactor");
|
|
83
90
|
if (branch.includes("docs/")) tags.push("documentation");
|
|
84
91
|
}
|
|
@@ -157,10 +164,16 @@ class SessionMonitor extends EventEmitter {
|
|
|
157
164
|
sessions.forEach((session) => {
|
|
158
165
|
this.sessions.set(session.id, session);
|
|
159
166
|
});
|
|
160
|
-
const items = sessions.map(
|
|
167
|
+
const items = sessions.map(
|
|
168
|
+
(session) => this.formatSessionItem(session)
|
|
169
|
+
);
|
|
161
170
|
this.sessionList.setItems(items);
|
|
162
|
-
const active = sessions.filter(
|
|
163
|
-
|
|
171
|
+
const active = sessions.filter(
|
|
172
|
+
(s) => this.getSessionStatus(s) === "active"
|
|
173
|
+
).length;
|
|
174
|
+
const idle = sessions.filter(
|
|
175
|
+
(s) => this.getSessionStatus(s) === "idle"
|
|
176
|
+
).length;
|
|
164
177
|
const total = sessions.length;
|
|
165
178
|
const footer = this.container.children[1];
|
|
166
179
|
if (footer) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/features/tui/components/session-monitor.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Session Monitor Component\n * Displays active sessions with auto-tagging based on work context\n */\n\nimport blessed from 'blessed';\nimport { EventEmitter } from 'events';\nimport type { SessionData, SessionMetrics } from '../types.js';\n\nexport class SessionMonitor extends EventEmitter {\n private container: blessed.Widgets.BoxElement;\n private sessionList: blessed.Widgets.ListElement;\n private sessions: Map<string, SessionData>;\n private selectedSession: string | null = null;\n\n constructor(container: blessed.Widgets.BoxElement) {\n super();\n this.container = container;\n this.sessions = new Map();\n this.initializeUI();\n }\n\n private initializeUI(): void {\n // Session list with auto-tagged names\n this.sessionList = blessed.list({\n parent: this.container,\n top: 0,\n left: 0,\n width: '100%',\n height: '100%-3',\n style: {\n selected: {\n bg: 'blue',\n fg: 'white',\n bold: true\n },\n item: {\n fg: 'cyan'\n }\n },\n mouse: true,\n keys: true,\n vi: true,\n scrollable: true,\n tags: true\n });\n\n // Summary footer\n const footer = blessed.box({\n parent: this.container,\n bottom: 0,\n left: 0,\n width: '100%',\n height: 3,\n content: 'Active: 0 | Idle: 0 | Total: 0',\n tags: true,\n style: {\n fg: 'white',\n bg: 'black'\n }\n });\n\n this.sessionList.on('select', (item, index) => {\n const sessionId = Array.from(this.sessions.keys())[index];\n this.selectSession(sessionId);\n });\n }\n\n /**\n * Auto-tag session based on work context\n */\n private autoTagSession(session: SessionData): string {\n const tags: string[] = [];\n \n // Analyze recent activities for context\n if (session.recentActivities) {\n // File-based tagging\n const files = session.recentActivities\n .filter((a: any) => a.type === 'file_edit')\n .map((a: any) => a.data?.path || '');\n \n if (files.some(f => f.includes('test'))) tags.push('testing');\n if (files.some(f => f.includes('.tsx') || f.includes('.jsx'))) tags.push('frontend');\n if (files.some(f => f.includes('api') || f.includes('server'))) tags.push('backend');\n if (files.some(f => f.includes('db') || f.includes('migration'))) tags.push('database');\n \n // Command-based tagging\n const commands = session.recentActivities\n .filter((a: any) => a.type === 'command')\n .map((a: any) => a.data?.command || '');\n \n if (commands.some(c => c.includes('npm test') || c.includes('jest'))) tags.push('testing');\n if (commands.some(c => c.includes('git'))) tags.push('git-ops');\n if (commands.some(c => c.includes('deploy'))) tags.push('deployment');\n if (commands.some(c => c.includes('debug'))) tags.push('debugging');\n }\n\n // Task-based tagging from Linear\n if (session.linearTask) {\n tags.push(`linear:${session.linearTask.identifier}`);\n if (session.linearTask.labels) {\n tags.push(...session.linearTask.labels.map((l: any) => l.toLowerCase()));\n }\n }\n\n // Branch-based tagging\n if (session.gitBranch) {\n const branch = session.gitBranch;\n if (branch.includes('feature/')) tags.push('feature');\n if (branch.includes('fix/') || branch.includes('bugfix/')) tags.push('bugfix');\n if (branch.includes('refactor/')) tags.push('refactor');\n if (branch.includes('docs/')) tags.push('documentation');\n }\n\n // AI agent type tagging\n if (session.agentType) {\n tags.push(`agent:${session.agentType}`);\n }\n\n return tags.length > 0 ? tags.join(' \u2022 ') : 'general';\n }\n\n /**\n * Generate session display name with context\n */\n private generateSessionName(session: SessionData): string {\n const autoTags = this.autoTagSession(session);\n const timestamp = new Date(session.startTime).toLocaleTimeString();\n \n // Create descriptive name based on primary activity\n let primaryActivity = 'Session';\n \n if (session.linearTask) {\n primaryActivity = session.linearTask.title || session.linearTask.identifier;\n } else if (session.gitBranch && session.gitBranch !== 'main' && session.gitBranch !== 'master') {\n primaryActivity = session.gitBranch.split('/').pop() || session.gitBranch;\n } else if (session.primaryFile) {\n primaryActivity = session.primaryFile.split('/').pop() || 'Code';\n }\n\n return `${primaryActivity} [${autoTags}] - ${timestamp}`;\n }\n\n /**\n * Format session item for display\n */\n private formatSessionItem(session: SessionData): string {\n const status = this.getSessionStatus(session);\n const statusIcon = this.getStatusIcon(status);\n const name = this.generateSessionName(session);\n const metrics = this.getSessionMetrics(session);\n \n const contextUsage = Math.round(session.contextUsage * 100);\n const contextColor = contextUsage > 80 ? 'red' : contextUsage > 60 ? 'yellow' : 'green';\n \n return `${statusIcon} ${name}\\n` +\n ` {gray-fg}Tokens: ${metrics.tokens} | Context: {${contextColor}-fg}${contextUsage}%{/} | Duration: ${metrics.duration}{/}`;\n }\n\n private getSessionStatus(session: SessionData): 'active' | 'idle' | 'completed' | 'error' {\n if (session.error) return 'error';\n if (session.completed) return 'completed';\n \n const now = Date.now();\n const lastActivity = session.lastActivity || session.startTime;\n const idleTime = now - lastActivity;\n \n if (idleTime > 5 * 60 * 1000) return 'idle'; // 5 minutes\n return 'active';\n }\n\n private getStatusIcon(status: string): string {\n switch (status) {\n case 'active': return '{green-fg}\u25CF{/}';\n case 'idle': return '{yellow-fg}\u25CF{/}';\n case 'completed': return '{gray-fg}\u25CF{/}';\n case 'error': return '{red-fg}\u25CF{/}';\n default: return '{white-fg}\u25CB{/}';\n }\n }\n\n private getSessionMetrics(session: SessionData): SessionMetrics {\n const now = Date.now();\n const duration = now - session.startTime;\n \n const hours = Math.floor(duration / (1000 * 60 * 60));\n const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60));\n \n return {\n tokens: session.totalTokens || 0,\n duration: hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`,\n filesEdited: session.filesEdited?.length || 0,\n commandsRun: session.commandsRun || 0,\n errorsEncountered: session.errors?.length || 0\n };\n }\n\n public update(sessions: SessionData[]): void {\n // Update session map\n this.sessions.clear();\n sessions.forEach(session => {\n this.sessions.set(session.id, session);\n });\n\n // Update list display\n const items = sessions.map((session: any) => this.formatSessionItem(session));\n this.sessionList.setItems(items);\n\n // Update footer statistics\n const active = sessions.filter((s: any) => this.getSessionStatus(s) === 'active').length;\n const idle = sessions.filter((s: any) => this.getSessionStatus(s) === 'idle').length;\n const total = sessions.length;\n \n const footer = this.container.children[1] as blessed.Widgets.BoxElement;\n if (footer) {\n footer.setContent(\n `{bold}Active:{/} {green-fg}${active}{/} | ` +\n `{bold}Idle:{/} {yellow-fg}${idle}{/} | ` +\n `{bold}Total:{/} ${total}`\n );\n }\n\n this.container.screen.render();\n }\n\n private selectSession(sessionId: string): void {\n this.selectedSession = sessionId;\n const session = this.sessions.get(sessionId);\n if (session) {\n this.emit('session:selected', session);\n this.showSessionDetails(session);\n }\n }\n\n private showSessionDetails(session: SessionData): void {\n const details = blessed.box({\n parent: this.container.screen,\n top: 'center',\n left: 'center',\n width: '80%',\n height: '80%',\n content: this.formatSessionDetails(session),\n tags: true,\n border: {\n type: 'line'\n },\n style: {\n border: {\n fg: 'cyan'\n }\n },\n scrollable: true,\n keys: true,\n vi: true,\n mouse: true,\n hidden: false,\n label: ` Session: ${this.generateSessionName(session)} `\n });\n\n details.key(['escape', 'q'], () => {\n details.destroy();\n this.container.screen.render();\n });\n\n details.focus();\n this.container.screen.render();\n }\n\n private formatSessionDetails(session: SessionData): string {\n const metrics = this.getSessionMetrics(session);\n const status = this.getSessionStatus(session);\n const tags = this.autoTagSession(session);\n \n let details = `{bold}Session ID:{/} ${session.id}\\n`;\n details += `{bold}Status:{/} ${status}\\n`;\n details += `{bold}Tags:{/} ${tags}\\n`;\n details += `{bold}Started:{/} ${new Date(session.startTime).toLocaleString()}\\n`;\n \n if (session.lastActivity) {\n details += `{bold}Last Activity:{/} ${new Date(session.lastActivity).toLocaleString()}\\n`;\n }\n \n details += `\\n{bold}Metrics:{/}\\n`;\n details += ` Tokens Used: ${metrics.tokens}\\n`;\n details += ` Context Usage: ${Math.round(session.contextUsage * 100)}%\\n`;\n details += ` Duration: ${metrics.duration}\\n`;\n details += ` Files Edited: ${metrics.filesEdited}\\n`;\n details += ` Commands Run: ${metrics.commandsRun}\\n`;\n details += ` Errors: ${metrics.errorsEncountered}\\n`;\n \n if (session.linearTask) {\n details += `\\n{bold}Linear Task:{/}\\n`;\n details += ` ID: ${session.linearTask.identifier}\\n`;\n details += ` Title: ${session.linearTask.title}\\n`;\n details += ` State: ${session.linearTask.state}\\n`;\n }\n \n if (session.gitBranch) {\n details += `\\n{bold}Git:{/}\\n`;\n details += ` Branch: ${session.gitBranch}\\n`;\n if (session.lastCommit) {\n details += ` Last Commit: ${session.lastCommit}\\n`;\n }\n }\n \n if (session.recentActivities && session.recentActivities.length > 0) {\n details += `\\n{bold}Recent Activities:{/}\\n`;\n session.recentActivities.slice(-10).forEach(activity => {\n details += ` ${new Date(activity.timestamp).toLocaleTimeString()} - ${activity.type}: ${activity.description}\\n`;\n });\n }\n \n return details;\n }\n\n public focus(): void {\n this.sessionList.focus();\n }\n\n public hasFocus(): boolean {\n return this.sessionList === this.container.screen.focused;\n }\n}"],
|
|
5
|
-
"mappings": "AAKA,OAAO,aAAa;AACpB,SAAS,oBAAoB;AAGtB,MAAM,uBAAuB,aAAa;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAiC;AAAA,EAEzC,YAAY,WAAuC;AACjD,UAAM;AACN,SAAK,YAAY;AACjB,SAAK,WAAW,oBAAI,IAAI;AACxB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,eAAqB;AAE3B,SAAK,cAAc,QAAQ,KAAK;AAAA,MAC9B,QAAQ,KAAK;AAAA,MACb,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,QACL,UAAU;AAAA,UACR,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,MAAM;AAAA,QACR;AAAA,QACA,MAAM;AAAA,UACJ,IAAI;AAAA,QACN;AAAA,MACF;AAAA,MACA,OAAO;AAAA,MACP,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,YAAY;AAAA,MACZ,MAAM;AAAA,IACR,CAAC;AAGD,UAAM,SAAS,QAAQ,IAAI;AAAA,MACzB,QAAQ,KAAK;AAAA,MACb,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF,CAAC;AAED,SAAK,YAAY,GAAG,UAAU,CAAC,MAAM,UAAU;AAC7C,YAAM,YAAY,MAAM,KAAK,KAAK,SAAS,KAAK,CAAC,EAAE,KAAK;AACxD,WAAK,cAAc,SAAS;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,SAA8B;AACnD,UAAM,OAAiB,CAAC;AAGxB,QAAI,QAAQ,kBAAkB;AAE5B,YAAM,QAAQ,QAAQ,iBACnB,OAAO,CAAC,MAAW,EAAE,SAAS,WAAW,EACzC,IAAI,CAAC,MAAW,EAAE,MAAM,QAAQ,EAAE;AAErC,UAAI,MAAM,KAAK,
|
|
4
|
+
"sourcesContent": ["/**\n * Session Monitor Component\n * Displays active sessions with auto-tagging based on work context\n */\n\nimport blessed from 'blessed';\nimport { EventEmitter } from 'events';\nimport type { SessionData, SessionMetrics } from '../types.js';\n\nexport class SessionMonitor extends EventEmitter {\n private container: blessed.Widgets.BoxElement;\n private sessionList: blessed.Widgets.ListElement;\n private sessions: Map<string, SessionData>;\n private selectedSession: string | null = null;\n\n constructor(container: blessed.Widgets.BoxElement) {\n super();\n this.container = container;\n this.sessions = new Map();\n this.initializeUI();\n }\n\n private initializeUI(): void {\n // Session list with auto-tagged names\n this.sessionList = blessed.list({\n parent: this.container,\n top: 0,\n left: 0,\n width: '100%',\n height: '100%-3',\n style: {\n selected: {\n bg: 'blue',\n fg: 'white',\n bold: true,\n },\n item: {\n fg: 'cyan',\n },\n },\n mouse: true,\n keys: true,\n vi: true,\n scrollable: true,\n tags: true,\n });\n\n // Summary footer\n const footer = blessed.box({\n parent: this.container,\n bottom: 0,\n left: 0,\n width: '100%',\n height: 3,\n content: 'Active: 0 | Idle: 0 | Total: 0',\n tags: true,\n style: {\n fg: 'white',\n bg: 'black',\n },\n });\n\n this.sessionList.on('select', (item, index) => {\n const sessionId = Array.from(this.sessions.keys())[index];\n this.selectSession(sessionId);\n });\n }\n\n /**\n * Auto-tag session based on work context\n */\n private autoTagSession(session: SessionData): string {\n const tags: string[] = [];\n\n // Analyze recent activities for context\n if (session.recentActivities) {\n // File-based tagging\n const files = session.recentActivities\n .filter((a: any) => a.type === 'file_edit')\n .map((a: any) => a.data?.path || '');\n\n if (files.some((f) => f.includes('test'))) tags.push('testing');\n if (files.some((f) => f.includes('.tsx') || f.includes('.jsx')))\n tags.push('frontend');\n if (files.some((f) => f.includes('api') || f.includes('server')))\n tags.push('backend');\n if (files.some((f) => f.includes('db') || f.includes('migration')))\n tags.push('database');\n\n // Command-based tagging\n const commands = session.recentActivities\n .filter((a: any) => a.type === 'command')\n .map((a: any) => a.data?.command || '');\n\n if (commands.some((c) => c.includes('npm test') || c.includes('jest')))\n tags.push('testing');\n if (commands.some((c) => c.includes('git'))) tags.push('git-ops');\n if (commands.some((c) => c.includes('deploy'))) tags.push('deployment');\n if (commands.some((c) => c.includes('debug'))) tags.push('debugging');\n }\n\n // Task-based tagging from Linear\n if (session.linearTask) {\n tags.push(`linear:${session.linearTask.identifier}`);\n if (session.linearTask.labels) {\n tags.push(\n ...session.linearTask.labels.map((l: any) => l.toLowerCase())\n );\n }\n }\n\n // Branch-based tagging\n if (session.gitBranch) {\n const branch = session.gitBranch;\n if (branch.includes('feature/')) tags.push('feature');\n if (branch.includes('fix/') || branch.includes('bugfix/'))\n tags.push('bugfix');\n if (branch.includes('refactor/')) tags.push('refactor');\n if (branch.includes('docs/')) tags.push('documentation');\n }\n\n // AI agent type tagging\n if (session.agentType) {\n tags.push(`agent:${session.agentType}`);\n }\n\n return tags.length > 0 ? tags.join(' \u2022 ') : 'general';\n }\n\n /**\n * Generate session display name with context\n */\n private generateSessionName(session: SessionData): string {\n const autoTags = this.autoTagSession(session);\n const timestamp = new Date(session.startTime).toLocaleTimeString();\n\n // Create descriptive name based on primary activity\n let primaryActivity = 'Session';\n\n if (session.linearTask) {\n primaryActivity =\n session.linearTask.title || session.linearTask.identifier;\n } else if (\n session.gitBranch &&\n session.gitBranch !== 'main' &&\n session.gitBranch !== 'master'\n ) {\n primaryActivity = session.gitBranch.split('/').pop() || session.gitBranch;\n } else if (session.primaryFile) {\n primaryActivity = session.primaryFile.split('/').pop() || 'Code';\n }\n\n return `${primaryActivity} [${autoTags}] - ${timestamp}`;\n }\n\n /**\n * Format session item for display\n */\n private formatSessionItem(session: SessionData): string {\n const status = this.getSessionStatus(session);\n const statusIcon = this.getStatusIcon(status);\n const name = this.generateSessionName(session);\n const metrics = this.getSessionMetrics(session);\n\n const contextUsage = Math.round(session.contextUsage * 100);\n const contextColor =\n contextUsage > 80 ? 'red' : contextUsage > 60 ? 'yellow' : 'green';\n\n return (\n `${statusIcon} ${name}\\n` +\n ` {gray-fg}Tokens: ${metrics.tokens} | Context: {${contextColor}-fg}${contextUsage}%{/} | Duration: ${metrics.duration}{/}`\n );\n }\n\n private getSessionStatus(\n session: SessionData\n ): 'active' | 'idle' | 'completed' | 'error' {\n if (session.error) return 'error';\n if (session.completed) return 'completed';\n\n const now = Date.now();\n const lastActivity = session.lastActivity || session.startTime;\n const idleTime = now - lastActivity;\n\n if (idleTime > 5 * 60 * 1000) return 'idle'; // 5 minutes\n return 'active';\n }\n\n private getStatusIcon(status: string): string {\n switch (status) {\n case 'active':\n return '{green-fg}\u25CF{/}';\n case 'idle':\n return '{yellow-fg}\u25CF{/}';\n case 'completed':\n return '{gray-fg}\u25CF{/}';\n case 'error':\n return '{red-fg}\u25CF{/}';\n default:\n return '{white-fg}\u25CB{/}';\n }\n }\n\n private getSessionMetrics(session: SessionData): SessionMetrics {\n const now = Date.now();\n const duration = now - session.startTime;\n\n const hours = Math.floor(duration / (1000 * 60 * 60));\n const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60));\n\n return {\n tokens: session.totalTokens || 0,\n duration: hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`,\n filesEdited: session.filesEdited?.length || 0,\n commandsRun: session.commandsRun || 0,\n errorsEncountered: session.errors?.length || 0,\n };\n }\n\n public update(sessions: SessionData[]): void {\n // Update session map\n this.sessions.clear();\n sessions.forEach((session) => {\n this.sessions.set(session.id, session);\n });\n\n // Update list display\n const items = sessions.map((session: any) =>\n this.formatSessionItem(session)\n );\n this.sessionList.setItems(items);\n\n // Update footer statistics\n const active = sessions.filter(\n (s: any) => this.getSessionStatus(s) === 'active'\n ).length;\n const idle = sessions.filter(\n (s: any) => this.getSessionStatus(s) === 'idle'\n ).length;\n const total = sessions.length;\n\n const footer = this.container.children[1] as blessed.Widgets.BoxElement;\n if (footer) {\n footer.setContent(\n `{bold}Active:{/} {green-fg}${active}{/} | ` +\n `{bold}Idle:{/} {yellow-fg}${idle}{/} | ` +\n `{bold}Total:{/} ${total}`\n );\n }\n\n this.container.screen.render();\n }\n\n private selectSession(sessionId: string): void {\n this.selectedSession = sessionId;\n const session = this.sessions.get(sessionId);\n if (session) {\n this.emit('session:selected', session);\n this.showSessionDetails(session);\n }\n }\n\n private showSessionDetails(session: SessionData): void {\n const details = blessed.box({\n parent: this.container.screen,\n top: 'center',\n left: 'center',\n width: '80%',\n height: '80%',\n content: this.formatSessionDetails(session),\n tags: true,\n border: {\n type: 'line',\n },\n style: {\n border: {\n fg: 'cyan',\n },\n },\n scrollable: true,\n keys: true,\n vi: true,\n mouse: true,\n hidden: false,\n label: ` Session: ${this.generateSessionName(session)} `,\n });\n\n details.key(['escape', 'q'], () => {\n details.destroy();\n this.container.screen.render();\n });\n\n details.focus();\n this.container.screen.render();\n }\n\n private formatSessionDetails(session: SessionData): string {\n const metrics = this.getSessionMetrics(session);\n const status = this.getSessionStatus(session);\n const tags = this.autoTagSession(session);\n\n let details = `{bold}Session ID:{/} ${session.id}\\n`;\n details += `{bold}Status:{/} ${status}\\n`;\n details += `{bold}Tags:{/} ${tags}\\n`;\n details += `{bold}Started:{/} ${new Date(session.startTime).toLocaleString()}\\n`;\n\n if (session.lastActivity) {\n details += `{bold}Last Activity:{/} ${new Date(session.lastActivity).toLocaleString()}\\n`;\n }\n\n details += `\\n{bold}Metrics:{/}\\n`;\n details += ` Tokens Used: ${metrics.tokens}\\n`;\n details += ` Context Usage: ${Math.round(session.contextUsage * 100)}%\\n`;\n details += ` Duration: ${metrics.duration}\\n`;\n details += ` Files Edited: ${metrics.filesEdited}\\n`;\n details += ` Commands Run: ${metrics.commandsRun}\\n`;\n details += ` Errors: ${metrics.errorsEncountered}\\n`;\n\n if (session.linearTask) {\n details += `\\n{bold}Linear Task:{/}\\n`;\n details += ` ID: ${session.linearTask.identifier}\\n`;\n details += ` Title: ${session.linearTask.title}\\n`;\n details += ` State: ${session.linearTask.state}\\n`;\n }\n\n if (session.gitBranch) {\n details += `\\n{bold}Git:{/}\\n`;\n details += ` Branch: ${session.gitBranch}\\n`;\n if (session.lastCommit) {\n details += ` Last Commit: ${session.lastCommit}\\n`;\n }\n }\n\n if (session.recentActivities && session.recentActivities.length > 0) {\n details += `\\n{bold}Recent Activities:{/}\\n`;\n session.recentActivities.slice(-10).forEach((activity) => {\n details += ` ${new Date(activity.timestamp).toLocaleTimeString()} - ${activity.type}: ${activity.description}\\n`;\n });\n }\n\n return details;\n }\n\n public focus(): void {\n this.sessionList.focus();\n }\n\n public hasFocus(): boolean {\n return this.sessionList === this.container.screen.focused;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -96,7 +96,9 @@ class SubagentFleet extends EventEmitter {
|
|
|
96
96
|
`;
|
|
97
97
|
if (agent.currentTask) {
|
|
98
98
|
const progress = Math.round(agent.currentTask.progress * 100);
|
|
99
|
-
const elapsed = this.formatDuration(
|
|
99
|
+
const elapsed = this.formatDuration(
|
|
100
|
+
Date.now() - agent.currentTask.startTime
|
|
101
|
+
);
|
|
100
102
|
item += ` {cyan-fg}\u25B6 ${agent.currentTask.description.substring(0, 30)}...{/}
|
|
101
103
|
`;
|
|
102
104
|
item += ` Progress: ${this.createProgressBar(agent.currentTask.progress)} ${progress}% (${elapsed})`;
|
|
@@ -120,7 +122,9 @@ class SubagentFleet extends EventEmitter {
|
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
if (agent.lastError) {
|
|
123
|
-
const errorAge = this.formatDuration(
|
|
125
|
+
const errorAge = this.formatDuration(
|
|
126
|
+
Date.now() - agent.lastError.timestamp
|
|
127
|
+
);
|
|
124
128
|
item += `
|
|
125
129
|
{red-fg}\u26A0 Error ${errorAge} ago: ${agent.lastError.message.substring(0, 40)}{/}`;
|
|
126
130
|
}
|
|
@@ -142,16 +146,16 @@ class SubagentFleet extends EventEmitter {
|
|
|
142
146
|
}
|
|
143
147
|
getAgentTypeIcon(type) {
|
|
144
148
|
const icons = {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
analyzer: "\u{1F50D}",
|
|
150
|
+
builder: "\u{1F528}",
|
|
151
|
+
debugger: "\u{1F41B}",
|
|
152
|
+
tester: "\u{1F9EA}",
|
|
153
|
+
reviewer: "\u{1F441}\uFE0F",
|
|
154
|
+
refactorer: "\u267B\uFE0F",
|
|
155
|
+
documenter: "\u{1F4DD}",
|
|
156
|
+
security: "\u{1F512}",
|
|
157
|
+
performance: "\u26A1",
|
|
158
|
+
general: "\u{1F916}"
|
|
155
159
|
};
|
|
156
160
|
return icons[type.toLowerCase()] || "\u{1F916}";
|
|
157
161
|
}
|
|
@@ -186,7 +190,10 @@ class SubagentFleet extends EventEmitter {
|
|
|
186
190
|
active: agents.filter((a) => a.status === "active").length,
|
|
187
191
|
idle: agents.filter((a) => a.status === "idle").length,
|
|
188
192
|
error: agents.filter((a) => a.status === "error").length,
|
|
189
|
-
totalTasks: agents.reduce(
|
|
193
|
+
totalTasks: agents.reduce(
|
|
194
|
+
(sum, a) => sum + a.tasksCompleted + a.tasksFailed,
|
|
195
|
+
0
|
|
196
|
+
),
|
|
190
197
|
successfulTasks: agents.reduce((sum, a) => sum + a.tasksCompleted, 0),
|
|
191
198
|
failedTasks: agents.reduce((sum, a) => sum + a.tasksFailed, 0),
|
|
192
199
|
avgSuccessRate: agents.reduce((sum, a) => sum + a.successRate, 0) / agents.length,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/features/tui/components/subagent-fleet.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Subagent Fleet Component\n * Monitors and displays status of all active subagents\n */\n\nimport blessed from 'blessed';\nimport { EventEmitter } from 'events';\nimport type { SubagentData } from '../types.js';\n\nexport class SubagentFleet extends EventEmitter {\n private container: blessed.Widgets.BoxElement;\n private agentList: blessed.Widgets.ListElement;\n private statsBox: blessed.Widgets.BoxElement;\n private agents: Map<string, SubagentData>;\n private selectedAgent: string | null = null;\n\n constructor(container: blessed.Widgets.BoxElement) {\n super();\n this.container = container;\n this.agents = new Map();\n this.initializeUI();\n }\n\n private initializeUI(): void {\n // Agent list (left side)\n this.agentList = blessed.list({\n parent: this.container,\n top: 0,\n left: 0,\n width: '60%',\n height: '100%-3',\n style: {\n selected: {\n bg: 'magenta',\n fg: 'white',\n bold: true\n },\n item: {\n fg: 'white'\n }\n },\n mouse: true,\n keys: true,\n vi: true,\n scrollable: true,\n tags: true\n });\n\n // Stats panel (right side)\n this.statsBox = blessed.box({\n parent: this.container,\n top: 0,\n right: 0,\n width: '40%',\n height: '100%-3',\n content: this.getFleetStats(),\n tags: true,\n scrollable: true,\n style: {\n fg: 'white'\n }\n });\n\n // Fleet summary footer\n const footer = blessed.box({\n parent: this.container,\n bottom: 0,\n left: 0,\n width: '100%',\n height: 3,\n content: 'Fleet: 0 agents | Active: 0 | Idle: 0',\n tags: true,\n style: {\n fg: 'white',\n bg: 'black'\n }\n });\n\n this.agentList.on('select', (item, index) => {\n const agentId = Array.from(this.agents.keys())[index];\n this.selectAgent(agentId);\n });\n\n // Keyboard shortcuts\n this.setupKeyboardShortcuts();\n }\n\n private setupKeyboardShortcuts(): void {\n this.container.key(['t'], () => {\n if (this.selectedAgent) {\n this.terminateAgent(this.selectedAgent);\n }\n });\n\n this.container.key(['r'], () => {\n if (this.selectedAgent) {\n this.restartAgent(this.selectedAgent);\n }\n });\n\n this.container.key(['l'], () => {\n if (this.selectedAgent) {\n this.showAgentLogs(this.selectedAgent);\n }\n });\n }\n\n /**\n * Format agent item for display\n */\n private formatAgentItem(agent: SubagentData): string {\n const status = this.getStatusIcon(agent.status);\n const type = this.getAgentTypeIcon(agent.type);\n const successRate = Math.round(agent.successRate * 100);\n const rateColor = successRate >= 90 ? 'green' : successRate >= 70 ? 'yellow' : 'red';\n \n let item = `${status} ${type} ${agent.type} (${agent.id.substring(0, 8)})\\n`;\n \n if (agent.currentTask) {\n const progress = Math.round(agent.currentTask.progress * 100);\n const elapsed = this.formatDuration(Date.now() - agent.currentTask.startTime);\n item += ` {cyan-fg}\u25B6 ${agent.currentTask.description.substring(0, 30)}...{/}\\n`;\n item += ` Progress: ${this.createProgressBar(agent.currentTask.progress)} ${progress}% (${elapsed})`;\n } else {\n item += ` Completed: ${agent.tasksCompleted} | Failed: ${agent.tasksFailed}\\n`;\n item += ` Success Rate: {${rateColor}-fg}${successRate}%{/} | Avg Time: ${this.formatDuration(agent.averageTime)}`;\n }\n \n // Resource usage indicators\n if (agent.cpuUsage !== undefined || agent.memoryUsage !== undefined) {\n item += '\\n ';\n if (agent.cpuUsage !== undefined) {\n const cpuColor = agent.cpuUsage > 80 ? 'red' : agent.cpuUsage > 50 ? 'yellow' : 'green';\n item += `CPU: {${cpuColor}-fg}${Math.round(agent.cpuUsage)}%{/} `;\n }\n if (agent.memoryUsage !== undefined) {\n const memColor = agent.memoryUsage > 80 ? 'red' : agent.memoryUsage > 50 ? 'yellow' : 'green';\n item += `MEM: {${memColor}-fg}${Math.round(agent.memoryUsage)}%{/} `;\n }\n if (agent.tokenUsage !== undefined) {\n item += `Tokens: ${this.formatTokens(agent.tokenUsage)}`;\n }\n }\n \n // Error indicator\n if (agent.lastError) {\n const errorAge = this.formatDuration(Date.now() - agent.lastError.timestamp);\n item += `\\n {red-fg}\u26A0 Error ${errorAge} ago: ${agent.lastError.message.substring(0, 40)}{/}`;\n }\n \n return item;\n }\n\n private getStatusIcon(status: string): string {\n switch (status) {\n case 'active': return '{green-fg}\u25CF{/}';\n case 'idle': return '{cyan-fg}\u25CF{/}';\n case 'error': return '{red-fg}\u25CF{/}';\n case 'completed': return '{gray-fg}\u25CF{/}';\n default: return '{white-fg}\u25CB{/}';\n }\n }\n\n private getAgentTypeIcon(type: string): string {\n const icons: Record<string, string> = {\n 'analyzer': '\uD83D\uDD0D',\n 'builder': '\uD83D\uDD28',\n 'debugger': '\uD83D\uDC1B',\n 'tester': '\uD83E\uDDEA',\n 'reviewer': '\uD83D\uDC41\uFE0F',\n 'refactorer': '\u267B\uFE0F',\n 'documenter': '\uD83D\uDCDD',\n 'security': '\uD83D\uDD12',\n 'performance': '\u26A1',\n 'general': '\uD83E\uDD16'\n };\n return icons[type.toLowerCase()] || '\uD83E\uDD16';\n }\n\n private createProgressBar(progress: number): string {\n const width = 10;\n const filled = Math.round(progress * width);\n const empty = width - filled;\n \n const color = progress >= 0.8 ? 'green' : progress >= 0.5 ? 'yellow' : 'cyan';\n return `{${color}-fg}${'\u2588'.repeat(filled)}{/}{gray-fg}${'\u2591'.repeat(empty)}{/}`;\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${Math.round(ms / 1000)}s`;\n if (ms < 3600000) return `${Math.round(ms / 60000)}m`;\n return `${Math.round(ms / 3600000)}h`;\n }\n\n private formatTokens(tokens: number): string {\n if (tokens < 1000) return `${tokens}`;\n if (tokens < 1000000) return `${(tokens / 1000).toFixed(1)}K`;\n return `${(tokens / 1000000).toFixed(1)}M`;\n }\n\n /**\n * Generate fleet statistics\n */\n private getFleetStats(): string {\n const agents = Array.from(this.agents.values());\n \n if (agents.length === 0) {\n return '{gray-fg}No agents active{/}';\n }\n \n // Calculate statistics\n const stats = {\n total: agents.length,\n active: agents.filter((a: any) => a.status === 'active').length,\n idle: agents.filter((a: any) => a.status === 'idle').length,\n error: agents.filter((a: any) => a.status === 'error').length,\n totalTasks: agents.reduce((sum, a) => sum + a.tasksCompleted + a.tasksFailed, 0),\n successfulTasks: agents.reduce((sum, a) => sum + a.tasksCompleted, 0),\n failedTasks: agents.reduce((sum, a) => sum + a.tasksFailed, 0),\n avgSuccessRate: agents.reduce((sum, a) => sum + a.successRate, 0) / agents.length,\n totalTokens: agents.reduce((sum, a) => sum + (a.tokenUsage || 0), 0),\n avgCpu: agents.reduce((sum, a) => sum + (a.cpuUsage || 0), 0) / agents.length,\n avgMemory: agents.reduce((sum, a) => sum + (a.memoryUsage || 0), 0) / agents.length\n };\n \n // Group by type\n const typeGroups = new Map<string, number>();\n agents.forEach(agent => {\n typeGroups.set(agent.type, (typeGroups.get(agent.type) || 0) + 1);\n });\n \n let output = '{bold}Fleet Statistics{/}\\n\\n';\n \n output += '{bold}Status:{/}\\n';\n output += ` Total: ${stats.total}\\n`;\n output += ` Active: {green-fg}${stats.active}{/}\\n`;\n output += ` Idle: {cyan-fg}${stats.idle}{/}\\n`;\n output += ` Error: {red-fg}${stats.error}{/}\\n`;\n \n output += '\\n{bold}Performance:{/}\\n';\n output += ` Tasks: ${stats.successfulTasks}/${stats.totalTasks}\\n`;\n const rateColor = stats.avgSuccessRate >= 0.9 ? 'green' : stats.avgSuccessRate >= 0.7 ? 'yellow' : 'red';\n output += ` Success: {${rateColor}-fg}${Math.round(stats.avgSuccessRate * 100)}%{/}\\n`;\n output += ` Tokens: ${this.formatTokens(stats.totalTokens)}\\n`;\n \n output += '\\n{bold}Resources:{/}\\n';\n const cpuColor = stats.avgCpu > 80 ? 'red' : stats.avgCpu > 50 ? 'yellow' : 'green';\n const memColor = stats.avgMemory > 80 ? 'red' : stats.avgMemory > 50 ? 'yellow' : 'green';\n output += ` CPU: {${cpuColor}-fg}${Math.round(stats.avgCpu)}%{/}\\n`;\n output += ` Memory: {${memColor}-fg}${Math.round(stats.avgMemory)}%{/}\\n`;\n \n output += '\\n{bold}Types:{/}\\n';\n Array.from(typeGroups.entries())\n .sort((a, b) => b[1] - a[1])\n .forEach(([type, count]) => {\n const icon = this.getAgentTypeIcon(type);\n output += ` ${icon} ${type}: ${count}\\n`;\n });\n \n return output;\n }\n\n public update(agents: SubagentData[]): void {\n // Update agent map\n this.agents.clear();\n agents.forEach(agent => {\n this.agents.set(agent.id, agent);\n });\n\n // Update list display\n const items = agents.map((agent: any) => this.formatAgentItem(agent));\n this.agentList.setItems(items);\n\n // Update stats panel\n this.statsBox.setContent(this.getFleetStats());\n\n // Update footer\n const active = agents.filter((a: any) => a.status === 'active').length;\n const idle = agents.filter((a: any) => a.status === 'idle').length;\n const total = agents.length;\n \n const footer = this.container.children[2] as blessed.Widgets.BoxElement;\n if (footer) {\n footer.setContent(\n `{bold}Fleet:{/} ${total} agents | ` +\n `{bold}Active:{/} {green-fg}${active}{/} | ` +\n `{bold}Idle:{/} {cyan-fg}${idle}{/}`\n );\n }\n\n this.container.screen.render();\n }\n\n private selectAgent(agentId: string): void {\n this.selectedAgent = agentId;\n const agent = this.agents.get(agentId);\n if (agent) {\n this.emit('agent:selected', agent);\n this.showAgentDetails(agent);\n }\n }\n\n private showAgentDetails(agent: SubagentData): void {\n const details = blessed.box({\n parent: this.container.screen,\n top: 'center',\n left: 'center',\n width: '70%',\n height: '70%',\n content: this.formatAgentDetails(agent),\n tags: true,\n border: {\n type: 'line'\n },\n style: {\n border: {\n fg: 'magenta'\n }\n },\n scrollable: true,\n keys: true,\n vi: true,\n mouse: true,\n hidden: false,\n label: ` Agent: ${agent.type} (${agent.id}) `\n });\n\n details.key(['escape', 'q'], () => {\n details.destroy();\n this.container.screen.render();\n });\n\n details.focus();\n this.container.screen.render();\n }\n\n private formatAgentDetails(agent: SubagentData): string {\n let details = `{bold}Agent ID:{/} ${agent.id}\\n`;\n details += `{bold}Type:{/} ${this.getAgentTypeIcon(agent.type)} ${agent.type}\\n`;\n details += `{bold}Status:{/} ${this.getStatusIcon(agent.status)} ${agent.status}\\n`;\n \n if (agent.currentTask) {\n details += `\\n{bold}Current Task:{/}\\n`;\n details += ` ID: ${agent.currentTask.id}\\n`;\n details += ` Description: ${agent.currentTask.description}\\n`;\n details += ` Progress: ${Math.round(agent.currentTask.progress * 100)}%\\n`;\n details += ` Started: ${new Date(agent.currentTask.startTime).toLocaleString()}\\n`;\n details += ` Elapsed: ${this.formatDuration(Date.now() - agent.currentTask.startTime)}\\n`;\n }\n \n details += `\\n{bold}Performance Metrics:{/}\\n`;\n details += ` Tasks Completed: ${agent.tasksCompleted}\\n`;\n details += ` Tasks Failed: ${agent.tasksFailed}\\n`;\n details += ` Success Rate: ${Math.round(agent.successRate * 100)}%\\n`;\n details += ` Average Time: ${this.formatDuration(agent.averageTime)}\\n`;\n \n if (agent.cpuUsage !== undefined || agent.memoryUsage !== undefined || agent.tokenUsage !== undefined) {\n details += `\\n{bold}Resource Usage:{/}\\n`;\n if (agent.cpuUsage !== undefined) {\n details += ` CPU: ${Math.round(agent.cpuUsage)}%\\n`;\n }\n if (agent.memoryUsage !== undefined) {\n details += ` Memory: ${Math.round(agent.memoryUsage)}%\\n`;\n }\n if (agent.tokenUsage !== undefined) {\n details += ` Tokens: ${this.formatTokens(agent.tokenUsage)}\\n`;\n }\n }\n \n if (agent.lastError) {\n details += `\\n{bold}Last Error:{/}\\n`;\n details += ` Message: ${agent.lastError.message}\\n`;\n details += ` Time: ${new Date(agent.lastError.timestamp).toLocaleString()}\\n`;\n details += ` Recoverable: ${agent.lastError.recoverable ? 'Yes' : 'No'}\\n`;\n }\n \n details += `\\n{gray-fg}[t] Terminate | [r] Restart | [l] View Logs{/}\\n`;\n \n return details;\n }\n\n private terminateAgent(agentId: string): void {\n this.emit('agent:terminate', { agentId });\n // Optimistically update UI\n const agent = this.agents.get(agentId);\n if (agent) {\n agent.status = 'completed';\n this.update(Array.from(this.agents.values()));\n }\n }\n\n private restartAgent(agentId: string): void {\n this.emit('agent:restart', { agentId });\n // Optimistically update UI\n const agent = this.agents.get(agentId);\n if (agent) {\n agent.status = 'idle';\n agent.lastError = undefined;\n this.update(Array.from(this.agents.values()));\n }\n }\n\n private showAgentLogs(agentId: string): void {\n this.emit('agent:logs', { agentId });\n }\n\n public focus(): void {\n this.agentList.focus();\n }\n\n public hasFocus(): boolean {\n return this.agentList === this.container.screen.focused;\n }\n}"],
|
|
5
|
-
"mappings": "AAKA,OAAO,aAAa;AACpB,SAAS,oBAAoB;AAGtB,MAAM,sBAAsB,aAAa;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAA+B;AAAA,EAEvC,YAAY,WAAuC;AACjD,UAAM;AACN,SAAK,YAAY;AACjB,SAAK,SAAS,oBAAI,IAAI;AACtB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,eAAqB;AAE3B,SAAK,YAAY,QAAQ,KAAK;AAAA,MAC5B,QAAQ,KAAK;AAAA,MACb,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,QACL,UAAU;AAAA,UACR,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,MAAM;AAAA,QACR;AAAA,QACA,MAAM;AAAA,UACJ,IAAI;AAAA,QACN;AAAA,MACF;AAAA,MACA,OAAO;AAAA,MACP,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,YAAY;AAAA,MACZ,MAAM;AAAA,IACR,CAAC;AAGD,SAAK,WAAW,QAAQ,IAAI;AAAA,MAC1B,QAAQ,KAAK;AAAA,MACb,KAAK;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAS,KAAK,cAAc;AAAA,MAC5B,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,OAAO;AAAA,QACL,IAAI;AAAA,MACN;AAAA,IACF,CAAC;AAGD,UAAM,SAAS,QAAQ,IAAI;AAAA,MACzB,QAAQ,KAAK;AAAA,MACb,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF,CAAC;AAED,SAAK,UAAU,GAAG,UAAU,CAAC,MAAM,UAAU;AAC3C,YAAM,UAAU,MAAM,KAAK,KAAK,OAAO,KAAK,CAAC,EAAE,KAAK;AACpD,WAAK,YAAY,OAAO;AAAA,IAC1B,CAAC;AAGD,SAAK,uBAAuB;AAAA,EAC9B;AAAA,EAEQ,yBAA+B;AACrC,SAAK,UAAU,IAAI,CAAC,GAAG,GAAG,MAAM;AAC9B,UAAI,KAAK,eAAe;AACtB,aAAK,eAAe,KAAK,aAAa;AAAA,MACxC;AAAA,IACF,CAAC;AAED,SAAK,UAAU,IAAI,CAAC,GAAG,GAAG,MAAM;AAC9B,UAAI,KAAK,eAAe;AACtB,aAAK,aAAa,KAAK,aAAa;AAAA,MACtC;AAAA,IACF,CAAC;AAED,SAAK,UAAU,IAAI,CAAC,GAAG,GAAG,MAAM;AAC9B,UAAI,KAAK,eAAe;AACtB,aAAK,cAAc,KAAK,aAAa;AAAA,MACvC;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,OAA6B;AACnD,UAAM,SAAS,KAAK,cAAc,MAAM,MAAM;AAC9C,UAAM,OAAO,KAAK,iBAAiB,MAAM,IAAI;AAC7C,UAAM,cAAc,KAAK,MAAM,MAAM,cAAc,GAAG;AACtD,UAAM,
|
|
4
|
+
"sourcesContent": ["/**\n * Subagent Fleet Component\n * Monitors and displays status of all active subagents\n */\n\nimport blessed from 'blessed';\nimport { EventEmitter } from 'events';\nimport type { SubagentData } from '../types.js';\n\nexport class SubagentFleet extends EventEmitter {\n private container: blessed.Widgets.BoxElement;\n private agentList: blessed.Widgets.ListElement;\n private statsBox: blessed.Widgets.BoxElement;\n private agents: Map<string, SubagentData>;\n private selectedAgent: string | null = null;\n\n constructor(container: blessed.Widgets.BoxElement) {\n super();\n this.container = container;\n this.agents = new Map();\n this.initializeUI();\n }\n\n private initializeUI(): void {\n // Agent list (left side)\n this.agentList = blessed.list({\n parent: this.container,\n top: 0,\n left: 0,\n width: '60%',\n height: '100%-3',\n style: {\n selected: {\n bg: 'magenta',\n fg: 'white',\n bold: true,\n },\n item: {\n fg: 'white',\n },\n },\n mouse: true,\n keys: true,\n vi: true,\n scrollable: true,\n tags: true,\n });\n\n // Stats panel (right side)\n this.statsBox = blessed.box({\n parent: this.container,\n top: 0,\n right: 0,\n width: '40%',\n height: '100%-3',\n content: this.getFleetStats(),\n tags: true,\n scrollable: true,\n style: {\n fg: 'white',\n },\n });\n\n // Fleet summary footer\n const footer = blessed.box({\n parent: this.container,\n bottom: 0,\n left: 0,\n width: '100%',\n height: 3,\n content: 'Fleet: 0 agents | Active: 0 | Idle: 0',\n tags: true,\n style: {\n fg: 'white',\n bg: 'black',\n },\n });\n\n this.agentList.on('select', (item, index) => {\n const agentId = Array.from(this.agents.keys())[index];\n this.selectAgent(agentId);\n });\n\n // Keyboard shortcuts\n this.setupKeyboardShortcuts();\n }\n\n private setupKeyboardShortcuts(): void {\n this.container.key(['t'], () => {\n if (this.selectedAgent) {\n this.terminateAgent(this.selectedAgent);\n }\n });\n\n this.container.key(['r'], () => {\n if (this.selectedAgent) {\n this.restartAgent(this.selectedAgent);\n }\n });\n\n this.container.key(['l'], () => {\n if (this.selectedAgent) {\n this.showAgentLogs(this.selectedAgent);\n }\n });\n }\n\n /**\n * Format agent item for display\n */\n private formatAgentItem(agent: SubagentData): string {\n const status = this.getStatusIcon(agent.status);\n const type = this.getAgentTypeIcon(agent.type);\n const successRate = Math.round(agent.successRate * 100);\n const rateColor =\n successRate >= 90 ? 'green' : successRate >= 70 ? 'yellow' : 'red';\n\n let item = `${status} ${type} ${agent.type} (${agent.id.substring(0, 8)})\\n`;\n\n if (agent.currentTask) {\n const progress = Math.round(agent.currentTask.progress * 100);\n const elapsed = this.formatDuration(\n Date.now() - agent.currentTask.startTime\n );\n item += ` {cyan-fg}\u25B6 ${agent.currentTask.description.substring(0, 30)}...{/}\\n`;\n item += ` Progress: ${this.createProgressBar(agent.currentTask.progress)} ${progress}% (${elapsed})`;\n } else {\n item += ` Completed: ${agent.tasksCompleted} | Failed: ${agent.tasksFailed}\\n`;\n item += ` Success Rate: {${rateColor}-fg}${successRate}%{/} | Avg Time: ${this.formatDuration(agent.averageTime)}`;\n }\n\n // Resource usage indicators\n if (agent.cpuUsage !== undefined || agent.memoryUsage !== undefined) {\n item += '\\n ';\n if (agent.cpuUsage !== undefined) {\n const cpuColor =\n agent.cpuUsage > 80\n ? 'red'\n : agent.cpuUsage > 50\n ? 'yellow'\n : 'green';\n item += `CPU: {${cpuColor}-fg}${Math.round(agent.cpuUsage)}%{/} `;\n }\n if (agent.memoryUsage !== undefined) {\n const memColor =\n agent.memoryUsage > 80\n ? 'red'\n : agent.memoryUsage > 50\n ? 'yellow'\n : 'green';\n item += `MEM: {${memColor}-fg}${Math.round(agent.memoryUsage)}%{/} `;\n }\n if (agent.tokenUsage !== undefined) {\n item += `Tokens: ${this.formatTokens(agent.tokenUsage)}`;\n }\n }\n\n // Error indicator\n if (agent.lastError) {\n const errorAge = this.formatDuration(\n Date.now() - agent.lastError.timestamp\n );\n item += `\\n {red-fg}\u26A0 Error ${errorAge} ago: ${agent.lastError.message.substring(0, 40)}{/}`;\n }\n\n return item;\n }\n\n private getStatusIcon(status: string): string {\n switch (status) {\n case 'active':\n return '{green-fg}\u25CF{/}';\n case 'idle':\n return '{cyan-fg}\u25CF{/}';\n case 'error':\n return '{red-fg}\u25CF{/}';\n case 'completed':\n return '{gray-fg}\u25CF{/}';\n default:\n return '{white-fg}\u25CB{/}';\n }\n }\n\n private getAgentTypeIcon(type: string): string {\n const icons: Record<string, string> = {\n analyzer: '\uD83D\uDD0D',\n builder: '\uD83D\uDD28',\n debugger: '\uD83D\uDC1B',\n tester: '\uD83E\uDDEA',\n reviewer: '\uD83D\uDC41\uFE0F',\n refactorer: '\u267B\uFE0F',\n documenter: '\uD83D\uDCDD',\n security: '\uD83D\uDD12',\n performance: '\u26A1',\n general: '\uD83E\uDD16',\n };\n return icons[type.toLowerCase()] || '\uD83E\uDD16';\n }\n\n private createProgressBar(progress: number): string {\n const width = 10;\n const filled = Math.round(progress * width);\n const empty = width - filled;\n\n const color =\n progress >= 0.8 ? 'green' : progress >= 0.5 ? 'yellow' : 'cyan';\n return `{${color}-fg}${'\u2588'.repeat(filled)}{/}{gray-fg}${'\u2591'.repeat(empty)}{/}`;\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${Math.round(ms / 1000)}s`;\n if (ms < 3600000) return `${Math.round(ms / 60000)}m`;\n return `${Math.round(ms / 3600000)}h`;\n }\n\n private formatTokens(tokens: number): string {\n if (tokens < 1000) return `${tokens}`;\n if (tokens < 1000000) return `${(tokens / 1000).toFixed(1)}K`;\n return `${(tokens / 1000000).toFixed(1)}M`;\n }\n\n /**\n * Generate fleet statistics\n */\n private getFleetStats(): string {\n const agents = Array.from(this.agents.values());\n\n if (agents.length === 0) {\n return '{gray-fg}No agents active{/}';\n }\n\n // Calculate statistics\n const stats = {\n total: agents.length,\n active: agents.filter((a: any) => a.status === 'active').length,\n idle: agents.filter((a: any) => a.status === 'idle').length,\n error: agents.filter((a: any) => a.status === 'error').length,\n totalTasks: agents.reduce(\n (sum, a) => sum + a.tasksCompleted + a.tasksFailed,\n 0\n ),\n successfulTasks: agents.reduce((sum, a) => sum + a.tasksCompleted, 0),\n failedTasks: agents.reduce((sum, a) => sum + a.tasksFailed, 0),\n avgSuccessRate:\n agents.reduce((sum, a) => sum + a.successRate, 0) / agents.length,\n totalTokens: agents.reduce((sum, a) => sum + (a.tokenUsage || 0), 0),\n avgCpu:\n agents.reduce((sum, a) => sum + (a.cpuUsage || 0), 0) / agents.length,\n avgMemory:\n agents.reduce((sum, a) => sum + (a.memoryUsage || 0), 0) /\n agents.length,\n };\n\n // Group by type\n const typeGroups = new Map<string, number>();\n agents.forEach((agent) => {\n typeGroups.set(agent.type, (typeGroups.get(agent.type) || 0) + 1);\n });\n\n let output = '{bold}Fleet Statistics{/}\\n\\n';\n\n output += '{bold}Status:{/}\\n';\n output += ` Total: ${stats.total}\\n`;\n output += ` Active: {green-fg}${stats.active}{/}\\n`;\n output += ` Idle: {cyan-fg}${stats.idle}{/}\\n`;\n output += ` Error: {red-fg}${stats.error}{/}\\n`;\n\n output += '\\n{bold}Performance:{/}\\n';\n output += ` Tasks: ${stats.successfulTasks}/${stats.totalTasks}\\n`;\n const rateColor =\n stats.avgSuccessRate >= 0.9\n ? 'green'\n : stats.avgSuccessRate >= 0.7\n ? 'yellow'\n : 'red';\n output += ` Success: {${rateColor}-fg}${Math.round(stats.avgSuccessRate * 100)}%{/}\\n`;\n output += ` Tokens: ${this.formatTokens(stats.totalTokens)}\\n`;\n\n output += '\\n{bold}Resources:{/}\\n';\n const cpuColor =\n stats.avgCpu > 80 ? 'red' : stats.avgCpu > 50 ? 'yellow' : 'green';\n const memColor =\n stats.avgMemory > 80 ? 'red' : stats.avgMemory > 50 ? 'yellow' : 'green';\n output += ` CPU: {${cpuColor}-fg}${Math.round(stats.avgCpu)}%{/}\\n`;\n output += ` Memory: {${memColor}-fg}${Math.round(stats.avgMemory)}%{/}\\n`;\n\n output += '\\n{bold}Types:{/}\\n';\n Array.from(typeGroups.entries())\n .sort((a, b) => b[1] - a[1])\n .forEach(([type, count]) => {\n const icon = this.getAgentTypeIcon(type);\n output += ` ${icon} ${type}: ${count}\\n`;\n });\n\n return output;\n }\n\n public update(agents: SubagentData[]): void {\n // Update agent map\n this.agents.clear();\n agents.forEach((agent) => {\n this.agents.set(agent.id, agent);\n });\n\n // Update list display\n const items = agents.map((agent: any) => this.formatAgentItem(agent));\n this.agentList.setItems(items);\n\n // Update stats panel\n this.statsBox.setContent(this.getFleetStats());\n\n // Update footer\n const active = agents.filter((a: any) => a.status === 'active').length;\n const idle = agents.filter((a: any) => a.status === 'idle').length;\n const total = agents.length;\n\n const footer = this.container.children[2] as blessed.Widgets.BoxElement;\n if (footer) {\n footer.setContent(\n `{bold}Fleet:{/} ${total} agents | ` +\n `{bold}Active:{/} {green-fg}${active}{/} | ` +\n `{bold}Idle:{/} {cyan-fg}${idle}{/}`\n );\n }\n\n this.container.screen.render();\n }\n\n private selectAgent(agentId: string): void {\n this.selectedAgent = agentId;\n const agent = this.agents.get(agentId);\n if (agent) {\n this.emit('agent:selected', agent);\n this.showAgentDetails(agent);\n }\n }\n\n private showAgentDetails(agent: SubagentData): void {\n const details = blessed.box({\n parent: this.container.screen,\n top: 'center',\n left: 'center',\n width: '70%',\n height: '70%',\n content: this.formatAgentDetails(agent),\n tags: true,\n border: {\n type: 'line',\n },\n style: {\n border: {\n fg: 'magenta',\n },\n },\n scrollable: true,\n keys: true,\n vi: true,\n mouse: true,\n hidden: false,\n label: ` Agent: ${agent.type} (${agent.id}) `,\n });\n\n details.key(['escape', 'q'], () => {\n details.destroy();\n this.container.screen.render();\n });\n\n details.focus();\n this.container.screen.render();\n }\n\n private formatAgentDetails(agent: SubagentData): string {\n let details = `{bold}Agent ID:{/} ${agent.id}\\n`;\n details += `{bold}Type:{/} ${this.getAgentTypeIcon(agent.type)} ${agent.type}\\n`;\n details += `{bold}Status:{/} ${this.getStatusIcon(agent.status)} ${agent.status}\\n`;\n\n if (agent.currentTask) {\n details += `\\n{bold}Current Task:{/}\\n`;\n details += ` ID: ${agent.currentTask.id}\\n`;\n details += ` Description: ${agent.currentTask.description}\\n`;\n details += ` Progress: ${Math.round(agent.currentTask.progress * 100)}%\\n`;\n details += ` Started: ${new Date(agent.currentTask.startTime).toLocaleString()}\\n`;\n details += ` Elapsed: ${this.formatDuration(Date.now() - agent.currentTask.startTime)}\\n`;\n }\n\n details += `\\n{bold}Performance Metrics:{/}\\n`;\n details += ` Tasks Completed: ${agent.tasksCompleted}\\n`;\n details += ` Tasks Failed: ${agent.tasksFailed}\\n`;\n details += ` Success Rate: ${Math.round(agent.successRate * 100)}%\\n`;\n details += ` Average Time: ${this.formatDuration(agent.averageTime)}\\n`;\n\n if (\n agent.cpuUsage !== undefined ||\n agent.memoryUsage !== undefined ||\n agent.tokenUsage !== undefined\n ) {\n details += `\\n{bold}Resource Usage:{/}\\n`;\n if (agent.cpuUsage !== undefined) {\n details += ` CPU: ${Math.round(agent.cpuUsage)}%\\n`;\n }\n if (agent.memoryUsage !== undefined) {\n details += ` Memory: ${Math.round(agent.memoryUsage)}%\\n`;\n }\n if (agent.tokenUsage !== undefined) {\n details += ` Tokens: ${this.formatTokens(agent.tokenUsage)}\\n`;\n }\n }\n\n if (agent.lastError) {\n details += `\\n{bold}Last Error:{/}\\n`;\n details += ` Message: ${agent.lastError.message}\\n`;\n details += ` Time: ${new Date(agent.lastError.timestamp).toLocaleString()}\\n`;\n details += ` Recoverable: ${agent.lastError.recoverable ? 'Yes' : 'No'}\\n`;\n }\n\n details += `\\n{gray-fg}[t] Terminate | [r] Restart | [l] View Logs{/}\\n`;\n\n return details;\n }\n\n private terminateAgent(agentId: string): void {\n this.emit('agent:terminate', { agentId });\n // Optimistically update UI\n const agent = this.agents.get(agentId);\n if (agent) {\n agent.status = 'completed';\n this.update(Array.from(this.agents.values()));\n }\n }\n\n private restartAgent(agentId: string): void {\n this.emit('agent:restart', { agentId });\n // Optimistically update UI\n const agent = this.agents.get(agentId);\n if (agent) {\n agent.status = 'idle';\n agent.lastError = undefined;\n this.update(Array.from(this.agents.values()));\n }\n }\n\n private showAgentLogs(agentId: string): void {\n this.emit('agent:logs', { agentId });\n }\n\n public focus(): void {\n this.agentList.focus();\n }\n\n public hasFocus(): boolean {\n return this.agentList === this.container.screen.focused;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -523,7 +523,14 @@ ${task.description}
|
|
|
523
523
|
mouse: true
|
|
524
524
|
});
|
|
525
525
|
dialog.on("select", (item, index) => {
|
|
526
|
-
const statusMap = [
|
|
526
|
+
const statusMap = [
|
|
527
|
+
"backlog",
|
|
528
|
+
"todo",
|
|
529
|
+
"in_progress",
|
|
530
|
+
"review",
|
|
531
|
+
"completed",
|
|
532
|
+
"cancelled"
|
|
533
|
+
];
|
|
527
534
|
if (index < statusMap.length) {
|
|
528
535
|
this.updateTaskStatus(taskId, statusMap[index]);
|
|
529
536
|
}
|
|
@@ -578,9 +585,16 @@ ${task.description}
|
|
|
578
585
|
const fs = await import("fs/promises");
|
|
579
586
|
const os = await import("os");
|
|
580
587
|
const path = await import("path");
|
|
581
|
-
const contextDir = path.join(
|
|
588
|
+
const contextDir = path.join(
|
|
589
|
+
os.homedir(),
|
|
590
|
+
".stackmemory",
|
|
591
|
+
"task-contexts"
|
|
592
|
+
);
|
|
582
593
|
await fs.mkdir(contextDir, { recursive: true });
|
|
583
|
-
const contextFile = path.join(
|
|
594
|
+
const contextFile = path.join(
|
|
595
|
+
contextDir,
|
|
596
|
+
`${task.identifier}-context.md`
|
|
597
|
+
);
|
|
584
598
|
const contextContent = `# Task: ${task.identifier} - ${task.title}
|
|
585
599
|
|
|
586
600
|
## Description
|
|
@@ -1074,7 +1088,9 @@ Command: ${command}`
|
|
|
1074
1088
|
const util = await import("util");
|
|
1075
1089
|
const execAsync = util.promisify(exec);
|
|
1076
1090
|
await execAsync("cd /Users/jwu/Dev/stackmemory && npm run linear:sync");
|
|
1077
|
-
notification.setContent(
|
|
1091
|
+
notification.setContent(
|
|
1092
|
+
"{center}\u2713 Sync complete! Refreshing...{/center}"
|
|
1093
|
+
);
|
|
1078
1094
|
notification.style.border.fg = "green";
|
|
1079
1095
|
setTimeout(() => {
|
|
1080
1096
|
notification.destroy();
|
|
@@ -1107,12 +1123,12 @@ Command: ${command}`
|
|
|
1107
1123
|
}
|
|
1108
1124
|
mapStatusToLinearState(status) {
|
|
1109
1125
|
const mapping = {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1126
|
+
backlog: "Backlog",
|
|
1127
|
+
todo: "Todo",
|
|
1128
|
+
in_progress: "In Progress",
|
|
1129
|
+
review: "In Review",
|
|
1130
|
+
completed: "Done",
|
|
1131
|
+
cancelled: "Canceled"
|
|
1116
1132
|
};
|
|
1117
1133
|
return mapping[status] || "Backlog";
|
|
1118
1134
|
}
|