codehere 0.2.2 → 0.3.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +88 -68
  2. package/dist/domain/entities/task.js +4 -4
  3. package/dist/error-handler.js +4 -9
  4. package/dist/index.js +253 -1219
  5. package/dist/infrastructure/config/product-context.js +7 -6
  6. package/dist/infrastructure/hooks/claude-sdk-hook.js +335 -63
  7. package/dist/infrastructure/observability/execution-tracer.js +11 -6
  8. package/dist/infrastructure/orchestration/agent-adapter.js +13 -0
  9. package/dist/infrastructure/orchestration/agent-installer.js +101 -0
  10. package/dist/infrastructure/orchestration/agents-config-seed.js +56 -0
  11. package/dist/infrastructure/orchestration/aider-adapter.js +319 -0
  12. package/dist/infrastructure/orchestration/artifacts-extractor.js +175 -0
  13. package/dist/infrastructure/orchestration/claude-code-adapter.js +352 -0
  14. package/dist/infrastructure/orchestration/codex-adapter.js +310 -0
  15. package/dist/infrastructure/orchestration/generic-cli-adapter.js +553 -0
  16. package/dist/infrastructure/orchestration/opencode-adapter.js +275 -0
  17. package/dist/infrastructure/orchestration/progress-aggregator.js +189 -0
  18. package/dist/infrastructure/orchestration/readiness.js +106 -0
  19. package/dist/infrastructure/orchestration/session-manager.js +272 -0
  20. package/dist/infrastructure/orchestration/session-persistence.js +109 -0
  21. package/dist/infrastructure/protocols/mcp-helpers.js +10 -30
  22. package/dist/infrastructure/protocols/mcp-server-impl.js +359 -21
  23. package/dist/infrastructure/protocols/ui-page.js +1827 -0
  24. package/dist/infrastructure/security/file-collection.js +115 -0
  25. package/dist/infrastructure/security/trust-scorer.js +301 -56
  26. package/dist/infrastructure/storage/task-repository.js +8 -7
  27. package/dist/monitoring.js +9 -38
  28. package/dist/presentation/cli/commands/help-command.js +96 -139
  29. package/dist/presentation/cli/commands/hook-command.js +17 -24
  30. package/dist/presentation/cli/commands/report-command.js +59 -72
  31. package/dist/presentation/cli/commands/setup-command.js +4 -4
  32. package/dist/presentation/cli/commands/status-command.js +321 -30
  33. package/dist/presentation/cli/commands/trust-command.js +84 -72
  34. package/dist/presentation/cli/error-display.js +17 -21
  35. package/dist/ui.js +11 -11
  36. package/package.json +24 -62
  37. package/dist/agents/base.js +0 -101
  38. package/dist/agents/orchestrator-model-driven.js +0 -141
  39. package/dist/agents/react-orchestrator-agent.js +0 -54
  40. package/dist/application/agents/execution-agent.js +0 -508
  41. package/dist/application/agents/multi-agent-orchestrator.js +0 -295
  42. package/dist/application/agents/planning-agent.js +0 -433
  43. package/dist/application/agents/validation-agent.js +0 -317
  44. package/dist/application/architectures/plan-and-act.js +0 -242
  45. package/dist/application/index.js +0 -12
  46. package/dist/application/services/agent-factory.js +0 -28
  47. package/dist/application/services/dependency-container.js +0 -596
  48. package/dist/application/services/git-context-service.js +0 -83
  49. package/dist/application/services/intelligent-router.js +0 -934
  50. package/dist/application/services/tool-executor-service.js +0 -94
  51. package/dist/application/use-cases/ask-question-use-case.js +0 -338
  52. package/dist/application/use-cases/edit-file-use-case.js +0 -830
  53. package/dist/application/use-cases/explain-file-use-case.js +0 -50
  54. package/dist/application/use-cases/index-codebase-use-case.js +0 -113
  55. package/dist/application/use-cases/planning-use-case.js +0 -276
  56. package/dist/application/use-cases/react-orchestration-use-case.js +0 -329
  57. package/dist/application/use-cases/reflexion-use-case.js +0 -30
  58. package/dist/application/workflows/deterministic-workflows.js +0 -189
  59. package/dist/ast-parser.js +0 -331
  60. package/dist/cache.js +0 -180
  61. package/dist/chat.js +0 -251
  62. package/dist/code-analysis.js +0 -327
  63. package/dist/context.js +0 -187
  64. package/dist/docs.js +0 -146
  65. package/dist/domain/entities/code-chunk.js +0 -6
  66. package/dist/domain/entities/decision.js +0 -9
  67. package/dist/domain/entities/edit.js +0 -6
  68. package/dist/domain/entities/knowledge-node.js +0 -9
  69. package/dist/domain/entities/knowledge-preservation.js +0 -10
  70. package/dist/domain/entities/memory.js +0 -6
  71. package/dist/domain/entities/pattern.js +0 -7
  72. package/dist/domain/entities/plan.js +0 -6
  73. package/dist/domain/entities/query.js +0 -6
  74. package/dist/domain/errors/embeddings-not-found-error.js +0 -13
  75. package/dist/domain/index.js +0 -19
  76. package/dist/domain/interfaces/ai-service.interface.js +0 -6
  77. package/dist/domain/interfaces/code-analyzer.interface.js +0 -6
  78. package/dist/domain/interfaces/decision-repository.interface.js +0 -9
  79. package/dist/domain/interfaces/embedding-repository.interface.js +0 -6
  80. package/dist/domain/interfaces/knowledge-graph-repository.interface.js +0 -9
  81. package/dist/domain/interfaces/knowledge-preservation-repository.interface.js +0 -8
  82. package/dist/domain/interfaces/memory-repository.interface.js +0 -6
  83. package/dist/domain/interfaces/pattern-repository.interface.js +0 -6
  84. package/dist/domain/interfaces/verification-service.interface.js +0 -6
  85. package/dist/domain/services/cross-team-pattern-sharing.js +0 -125
  86. package/dist/domain/services/decision-recorder.js +0 -223
  87. package/dist/domain/services/decision-retriever.js +0 -133
  88. package/dist/domain/services/environment-mode-manager.js +0 -22
  89. package/dist/domain/services/graded-memory-activation.js +0 -69
  90. package/dist/domain/services/knowledge-access.js +0 -132
  91. package/dist/domain/services/knowledge-extractor.js +0 -247
  92. package/dist/domain/services/knowledge-graph-builder.js +0 -268
  93. package/dist/domain/services/knowledge-graph-query.js +0 -187
  94. package/dist/domain/services/memory-retriever.js +0 -82
  95. package/dist/domain/services/memory-synthesizer.js +0 -91
  96. package/dist/domain/services/pattern-extractor.js +0 -442
  97. package/dist/domain/services/query-intent-classifier.js +0 -140
  98. package/dist/domain/services/react-loop.js +0 -186
  99. package/dist/domain/services/risk-tier-detector.js +0 -78
  100. package/dist/edit.js +0 -319
  101. package/dist/embed.js +0 -459
  102. package/dist/formatter.js +0 -168
  103. package/dist/git.js +0 -198
  104. package/dist/infrastructure/ai/cohere-ai-service.js +0 -607
  105. package/dist/infrastructure/ai/command-r-evaluation.js +0 -206
  106. package/dist/infrastructure/ai/model-config.js +0 -84
  107. package/dist/infrastructure/ai/provider-ai-service.js +0 -477
  108. package/dist/infrastructure/cache/embedding-cache.js +0 -175
  109. package/dist/infrastructure/cache/query-result-cache.js +0 -66
  110. package/dist/infrastructure/cache/response-cache.js +0 -48
  111. package/dist/infrastructure/cache/security-scan-cache.js +0 -47
  112. package/dist/infrastructure/chunking/ast-code-chunker.js +0 -19
  113. package/dist/infrastructure/chunking/code-aware-chunker.js +0 -397
  114. package/dist/infrastructure/context/context-compressor.js +0 -329
  115. package/dist/infrastructure/context/conversation-context.js +0 -128
  116. package/dist/infrastructure/context/session-manager.js +0 -94
  117. package/dist/infrastructure/cost/cost-tracker.js +0 -360
  118. package/dist/infrastructure/file-scanner/file-scanner.js +0 -106
  119. package/dist/infrastructure/governance/nist-ai-rmf.js +0 -292
  120. package/dist/infrastructure/governance/policy-as-code.js +0 -258
  121. package/dist/infrastructure/governance/prompt-to-code-lineage.js +0 -239
  122. package/dist/infrastructure/index.js +0 -7
  123. package/dist/infrastructure/observability/prompt-versioner.js +0 -145
  124. package/dist/infrastructure/protocols/a2a-metadata.js +0 -137
  125. package/dist/infrastructure/protocols/a2a-protocol.js +0 -314
  126. package/dist/infrastructure/protocols/mcp-oauth.js +0 -184
  127. package/dist/infrastructure/protocols/mcp-server.js +0 -333
  128. package/dist/infrastructure/protocols/streaming-handler.js +0 -103
  129. package/dist/infrastructure/providers/cohere-provider.js +0 -444
  130. package/dist/infrastructure/providers/fake-provider.js +0 -130
  131. package/dist/infrastructure/providers/local-llm-provider.js +0 -330
  132. package/dist/infrastructure/providers/model-config.types.js +0 -9
  133. package/dist/infrastructure/providers/model-provider.interface.js +0 -8
  134. package/dist/infrastructure/providers/openai-provider.js +0 -413
  135. package/dist/infrastructure/providers/openrouter-provider.js +0 -399
  136. package/dist/infrastructure/providers/operation-provider-resolver.js +0 -105
  137. package/dist/infrastructure/providers/provider-config-loader.js +0 -120
  138. package/dist/infrastructure/providers/provider-registry.js +0 -88
  139. package/dist/infrastructure/safety/pattern-detector.js +0 -133
  140. package/dist/infrastructure/security/ai-sast-scanner.js +0 -290
  141. package/dist/infrastructure/security/docker-sandbox.js +0 -178
  142. package/dist/infrastructure/security/enhanced-security-gate.js +0 -151
  143. package/dist/infrastructure/security/license-scanner.js +0 -167
  144. package/dist/infrastructure/security/provider-bias-detector.js +0 -164
  145. package/dist/infrastructure/security/sandbox-executor.js +0 -64
  146. package/dist/infrastructure/serialization/toon-serializer.js +0 -119
  147. package/dist/infrastructure/storage/memory-stream-service.js +0 -204
  148. package/dist/infrastructure/storage/plan-repository.js +0 -96
  149. package/dist/infrastructure/storage/sqlite-decision-repository.js +0 -232
  150. package/dist/infrastructure/storage/sqlite-embedding-repository.js +0 -311
  151. package/dist/infrastructure/storage/sqlite-knowledge-graph-repository.js +0 -417
  152. package/dist/infrastructure/storage/sqlite-knowledge-preservation-repository.js +0 -230
  153. package/dist/infrastructure/storage/sqlite-memory-repository.js +0 -229
  154. package/dist/infrastructure/storage/sqlite-pattern-repository.js +0 -249
  155. package/dist/infrastructure/storage/task-helpers.js +0 -197
  156. package/dist/infrastructure/telemetry/telemetry-service.js +0 -167
  157. package/dist/infrastructure/ux/appropriate-friction.js +0 -213
  158. package/dist/infrastructure/ux/bi-directional-learning.js +0 -337
  159. package/dist/infrastructure/ux/capability-boundaries.js +0 -42
  160. package/dist/infrastructure/ux/comprehensive-formatter.js +0 -117
  161. package/dist/infrastructure/ux/contextual-feature-discovery.js +0 -10
  162. package/dist/infrastructure/ux/expectation-management.js +0 -327
  163. package/dist/infrastructure/ux/feature-discovery.js +0 -14
  164. package/dist/infrastructure/ux/feedback-prompt.js +0 -45
  165. package/dist/infrastructure/ux/hitl-validation.js +0 -297
  166. package/dist/infrastructure/ux/progress-indicator.js +0 -116
  167. package/dist/infrastructure/ux/progressive-disclosure.js +0 -236
  168. package/dist/infrastructure/ux/review-checkpoint.js +0 -72
  169. package/dist/infrastructure/ux/staged-feature-intro.js +0 -24
  170. package/dist/infrastructure/ux/stream-markdown.js +0 -151
  171. package/dist/infrastructure/ux/syntax-highlighter.js +0 -91
  172. package/dist/infrastructure/ux/team-standards.js +0 -196
  173. package/dist/infrastructure/ux/transparency.js +0 -71
  174. package/dist/infrastructure/validation/agent-feedback.js +0 -55
  175. package/dist/infrastructure/validation/agent-validation-helper.js +0 -137
  176. package/dist/infrastructure/validation/review-handler.js +0 -40
  177. package/dist/infrastructure/validation/summary-aggregator.js +0 -70
  178. package/dist/infrastructure/validation/summary-extractor.js +0 -45
  179. package/dist/infrastructure/validation/trace-summary.js +0 -52
  180. package/dist/infrastructure/verification/verification-service.js +0 -47
  181. package/dist/infrastructure/xai/attention-visualizer.js +0 -172
  182. package/dist/infrastructure/xai/cot-visualizer.js +0 -283
  183. package/dist/infrastructure/xai/decision-tree-log.js +0 -212
  184. package/dist/infrastructure/xai/interactive-xai.js +0 -260
  185. package/dist/infrastructure/xai/uncertainty-quantifier.js +0 -198
  186. package/dist/intelligent-retrieval.js +0 -272
  187. package/dist/intent/classifier.js +0 -2
  188. package/dist/intent/context.js +0 -2
  189. package/dist/iterative-refinement.js +0 -189
  190. package/dist/learning.js +0 -232
  191. package/dist/log.js +0 -31
  192. package/dist/migrate.js +0 -133
  193. package/dist/parallel-processor.js +0 -534
  194. package/dist/planner.js +0 -198
  195. package/dist/policy.js +0 -26
  196. package/dist/presentation/cli/commands/ask-command.js +0 -314
  197. package/dist/presentation/cli/commands/cost-command.js +0 -191
  198. package/dist/presentation/cli/commands/decision-command.js +0 -207
  199. package/dist/presentation/cli/commands/docs-command.js +0 -25
  200. package/dist/presentation/cli/commands/fix-command.js +0 -53
  201. package/dist/presentation/cli/commands/knowledge-command.js +0 -129
  202. package/dist/presentation/cli/commands/learning-dashboard-command.js +0 -183
  203. package/dist/presentation/cli/commands/models-command.js +0 -194
  204. package/dist/presentation/cli/commands/orchestrate-command.js +0 -207
  205. package/dist/presentation/cli/commands/plan-command.js +0 -246
  206. package/dist/presentation/cli/commands/product-command.js +0 -199
  207. package/dist/presentation/cli/commands/react-command.js +0 -181
  208. package/dist/presentation/cli/commands/review-command.js +0 -154
  209. package/dist/presentation/cli/commands/run-command.js +0 -188
  210. package/dist/presentation/cli/commands/smart-command.js +0 -395
  211. package/dist/presentation/cli/commands/suggest-command.js +0 -138
  212. package/dist/presentation/cli/commands/trace-command.js +0 -129
  213. package/dist/presentation/cli/commands/undo-command.js +0 -150
  214. package/dist/query-optimizer.js +0 -205
  215. package/dist/refactor.js +0 -116
  216. package/dist/review.js +0 -206
  217. package/dist/search.js +0 -163
  218. package/dist/session.js +0 -704
  219. package/dist/task-verification.js +0 -336
  220. package/dist/testgen.js +0 -166
  221. package/dist/token-optimizer.js +0 -277
  222. package/dist/tools/bash.js +0 -213
  223. package/dist/tools/edit.js +0 -235
  224. package/dist/tools/executor.js +0 -153
  225. package/dist/tools/git-tool.js +0 -292
  226. package/dist/tools/parser.js +0 -158
  227. package/dist/tools/registry.js +0 -302
  228. package/dist/tools/search.js +0 -100
  229. package/dist/types.js +0 -2
  230. package/dist/utils/check-api-status.js +0 -100
  231. package/dist/utils/diff-parser.js +0 -162
  232. package/dist/verification/backup.js +0 -259
  233. package/dist/verification/compiler.js +0 -264
  234. package/dist/verification/index.js +0 -273
  235. package/dist/verification/test-runner.js +0 -379
@@ -0,0 +1,272 @@
1
+ /**
2
+ * In-memory registry of active task sessions + audit-log derived observation.
3
+ *
4
+ * Two kinds of session surfaced to the UI:
5
+ * 1. spawned — started via codehere.spawn; we own the subprocess, can kill.
6
+ * 2. observed — derived from the audit log. Claude Code sessions running
7
+ * in the user's terminals, IDE, or elsewhere on this machine.
8
+ * codehere didn't start them and can't kill them, but shows
9
+ * them so the dashboard reflects reality from first load.
10
+ *
11
+ * Durable state lives in the audit log (F0). On daemon restart, spawned
12
+ * sessions are gone but observed ones remain (they're re-derived from the log).
13
+ */
14
+ import { existsSync, readFileSync } from 'fs';
15
+ import { execFileSync } from 'child_process';
16
+ import { ClaudeCodeAdapter } from './claude-code-adapter.js';
17
+ import { CodexAdapter } from './codex-adapter.js';
18
+ import { AiderAdapter } from './aider-adapter.js';
19
+ import { OpenCodeAdapter } from './opencode-adapter.js';
20
+ import { buildGenericAdapter, loadAgentsConfig } from './generic-cli-adapter.js';
21
+ import { seedAgentsConfig } from './agents-config-seed.js';
22
+ import { AUDIT_FILE } from '../hooks/claude-sdk-hook.js';
23
+ import { appendSpawned, appendCompleted, loadRecent } from './session-persistence.js';
24
+ // ── Agent registry ─────────────────────────────────────────────────────────────
25
+ // All backends codehere can spawn. Order here drives the default UI dropdown
26
+ // order. Built-in adapters first; user-config-driven ones appended from
27
+ // ~/.codehere/agents.yaml (generic-cli-adapter). isInstalled() gates visibility
28
+ // so an adapter registering for an un-installed CLI is harmless.
29
+ const BUILTIN_REGISTRY = [ClaudeCodeAdapter, CodexAdapter, AiderAdapter, OpenCodeAdapter];
30
+ // Seed the default agents.yaml if this user has never configured one, so Gemini
31
+ // / Cursor / Pi / Antigravity / Goose appear in the registry pre-wired. They
32
+ // stay hidden in the UI until isInstalled() flips to true. Idempotent.
33
+ seedAgentsConfig();
34
+ const REGISTRY = [
35
+ ...BUILTIN_REGISTRY,
36
+ // User-configurable adapters — "plug any CLI" escape hatch, seeded with
37
+ // popular CLI agents above. Users can add/edit/remove entries freely.
38
+ ...loadAgentsConfig().map(buildGenericAdapter),
39
+ ];
40
+ export function availableAgents() {
41
+ return REGISTRY.map((a) => ({ id: a.id, label: a.label, installed: a.isInstalled() }));
42
+ }
43
+ /** Per-agent readiness across the full registry. Powers the setup-aware UI. */
44
+ export function readinessForAllAgents() {
45
+ return REGISTRY.map((a) => {
46
+ if (a.checkReadiness)
47
+ return a.checkReadiness();
48
+ // Fallback for adapters that haven't implemented checkReadiness yet: treat
49
+ // "installed" as "ready", "not installed" as "needs_install without hint".
50
+ const installed = a.isInstalled();
51
+ return installed
52
+ ? { id: a.id, label: a.label, state: 'ready', installed: true }
53
+ : { id: a.id, label: a.label, state: 'needs_install', installed: false };
54
+ });
55
+ }
56
+ export function getAdapter(id) {
57
+ return REGISTRY.find((a) => a.id === id);
58
+ }
59
+ export function allAdapters() {
60
+ return REGISTRY;
61
+ }
62
+ export function agentProcessPatterns() {
63
+ return REGISTRY.map((a) => ({
64
+ regex: a.processPattern,
65
+ agent: a.id,
66
+ label: a.label,
67
+ }));
68
+ }
69
+ const DEFAULT_OBSERVED_WINDOW_MS = 10 * 60 * 1000; // 10 minutes
70
+ function parseEtime(s) {
71
+ // ps etime formats: "MM:SS", "HH:MM:SS", "D-HH:MM:SS"
72
+ let rest = s;
73
+ let days = 0;
74
+ if (rest.includes('-')) {
75
+ const [d, r] = rest.split('-');
76
+ days = parseInt(d, 10) || 0;
77
+ rest = r;
78
+ }
79
+ const parts = rest.split(':').map((p) => parseInt(p, 10) || 0);
80
+ if (parts.length === 2)
81
+ return parts[0] * 60 + parts[1];
82
+ if (parts.length === 3)
83
+ return days * 86400 + parts[0] * 3600 + parts[1] * 60 + parts[2];
84
+ return 0;
85
+ }
86
+ export class SessionManager {
87
+ sessions = new Map();
88
+ /** Read-only sessions replayed from ~/.codehere/sessions.jsonl on daemon start.
89
+ * These subprocesses are long-dead; we surface them so the user doesn't lose
90
+ * their Final reports across restarts. */
91
+ persistedCache = loadRecent(20);
92
+ spawn(prompt, agentId = 'claude-code', options) {
93
+ const adapter = getAdapter(agentId);
94
+ if (!adapter) {
95
+ throw new Error(`Unknown agent: ${agentId}. Registered: ${REGISTRY.map((a) => a.id).join(', ')}`);
96
+ }
97
+ if (!adapter.isInstalled()) {
98
+ throw new Error(`Agent ${adapter.label} (${adapter.id}) is not installed on this machine`);
99
+ }
100
+ const session = adapter.spawn(prompt, options);
101
+ this.sessions.set(session.sessionId, session);
102
+ // Persistence: write the spawned record immediately so a daemon crash still
103
+ // leaves an audit trail of what was attempted. Subscribe to exit so we can
104
+ // capture the final status + steps + artifacts when the session settles.
105
+ const cwd = options?.cwd ?? process.cwd();
106
+ appendSpawned({
107
+ id: session.sessionId,
108
+ agent: session.agent,
109
+ prompt,
110
+ cwd,
111
+ startedAt: session.startedAt,
112
+ });
113
+ const unsubscribe = session.subscribe((event) => {
114
+ if (event.type !== 'exit')
115
+ return;
116
+ const data = event.data;
117
+ appendCompleted({
118
+ id: session.sessionId,
119
+ status: session.status,
120
+ durationMs: Number(data?.durationMs ?? Date.now() - session.startedAt),
121
+ completedAt: Date.now(),
122
+ steps: session.steps ? [...session.steps] : undefined,
123
+ artifacts: session.artifacts,
124
+ });
125
+ unsubscribe();
126
+ });
127
+ return session;
128
+ }
129
+ /** Persisted (read-only) sessions from prior daemon runs. Cached at boot. */
130
+ listPersisted() {
131
+ return this.persistedCache;
132
+ }
133
+ get(sessionId) {
134
+ return this.sessions.get(sessionId);
135
+ }
136
+ list() {
137
+ return Array.from(this.sessions.values());
138
+ }
139
+ kill(sessionId) {
140
+ const session = this.sessions.get(sessionId);
141
+ if (!session)
142
+ return false;
143
+ session.kill();
144
+ return true;
145
+ }
146
+ /**
147
+ * Active Claude Code sessions on this machine, derived from the audit log.
148
+ * A session is "active" if any tool call from it was recorded within the
149
+ * last `maxAgeMs` milliseconds. Returns most-recent first.
150
+ *
151
+ * Note: codehere-spawned sessions may also appear here because they produce
152
+ * audit entries just like any other Claude Code session. The UI distinguishes
153
+ * by provenance.
154
+ */
155
+ listObserved(maxAgeMs = DEFAULT_OBSERVED_WINDOW_MS) {
156
+ if (!existsSync(AUDIT_FILE))
157
+ return [];
158
+ const now = Date.now();
159
+ const cutoff = now - maxAgeMs;
160
+ const byId = new Map();
161
+ let content;
162
+ try {
163
+ content = readFileSync(AUDIT_FILE, 'utf-8');
164
+ }
165
+ catch {
166
+ return [];
167
+ }
168
+ const lines = content.split('\n');
169
+ // Walk backwards — most recent lines first — and stop early when we
170
+ // pass the window. Audit log appends chronologically.
171
+ for (let i = lines.length - 1; i >= 0; i--) {
172
+ const line = lines[i].trim();
173
+ if (!line)
174
+ continue;
175
+ let entry;
176
+ try {
177
+ entry = JSON.parse(line);
178
+ }
179
+ catch {
180
+ continue;
181
+ }
182
+ if (!entry.sessionId)
183
+ continue;
184
+ const ts = typeof entry.timestamp === 'number'
185
+ ? entry.timestamp
186
+ : entry.timestamp
187
+ ? Date.parse(String(entry.timestamp))
188
+ : 0;
189
+ if (!ts)
190
+ continue;
191
+ if (ts < cutoff)
192
+ break; // past the window; older entries can't qualify
193
+ const existing = byId.get(entry.sessionId);
194
+ if (existing) {
195
+ existing.eventCount += 1;
196
+ if (ts < existing.firstEventAt)
197
+ existing.firstEventAt = ts;
198
+ }
199
+ else {
200
+ byId.set(entry.sessionId, {
201
+ sessionId: entry.sessionId,
202
+ firstEventAt: ts,
203
+ lastEventAt: ts,
204
+ eventCount: 1,
205
+ recentTool: entry.toolName,
206
+ provenance: 'observed',
207
+ });
208
+ }
209
+ }
210
+ return Array.from(byId.values()).sort((a, b) => b.lastEventAt - a.lastEventAt);
211
+ }
212
+ /**
213
+ * Agent processes running on this machine right now.
214
+ * Enumerates via `ps`, filters to known agent executables, returns most
215
+ * recently started first. This is the primary F1d surface — it reflects
216
+ * "what AI agents are alive on your machine" independent of whether
217
+ * they've produced audit events yet.
218
+ */
219
+ listActiveAgents() {
220
+ let out;
221
+ try {
222
+ out = execFileSync('ps', ['-A', '-o', 'pid,tty,etime,args'], {
223
+ encoding: 'utf-8',
224
+ timeout: 2000,
225
+ });
226
+ }
227
+ catch {
228
+ return [];
229
+ }
230
+ const agents = [];
231
+ const lines = out.trim().split('\n').slice(1); // drop header
232
+ for (const line of lines) {
233
+ const m = line.trim().match(/^(\d+)\s+(\S+)\s+(\S+)\s+(.+)$/);
234
+ if (!m)
235
+ continue;
236
+ const [, pidStr, tty, etime, args] = m;
237
+ // Skip ourselves — the codehere daemon would otherwise show up repeatedly
238
+ if (/\bcodehere\b|\bch\s+start\b/.test(args))
239
+ continue;
240
+ // Skip the audit tailer from other codehere commands
241
+ if (/scan-tool|codehere-audit/.test(args))
242
+ continue;
243
+ let matched = null;
244
+ for (const p of agentProcessPatterns()) {
245
+ if (p.regex.test(args)) {
246
+ matched = { agent: p.agent, label: p.label };
247
+ break;
248
+ }
249
+ }
250
+ if (!matched)
251
+ continue;
252
+ const uptimeSeconds = parseEtime(etime);
253
+ const startedAt = Date.now() - uptimeSeconds * 1000;
254
+ agents.push({
255
+ pid: parseInt(pidStr, 10),
256
+ tty,
257
+ agent: matched.agent,
258
+ label: matched.label,
259
+ args: args.length > 200 ? args.slice(0, 200) + '…' : args,
260
+ startedAt,
261
+ uptimeSeconds,
262
+ provenance: 'detected',
263
+ });
264
+ }
265
+ return agents.sort((a, b) => b.startedAt - a.startedAt);
266
+ }
267
+ static _instance;
268
+ static getInstance() {
269
+ return (this._instance ??= new SessionManager());
270
+ }
271
+ }
272
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Session persistence — survives daemon restart.
3
+ *
4
+ * Beta.3 dogfood surfaced a reliability gap: after `ch start` exited and was
5
+ * relaunched, the task UI was empty even when completed sessions had produced
6
+ * valuable final reports. The audit log (hook-events.jsonl) persisted every
7
+ * tool call but not the session-level PM state (prompt, artifacts, steps).
8
+ *
9
+ * This module adds a minimal two-record schema:
10
+ * { t: 'spawned', id, agent, prompt, cwd, startedAt }
11
+ * { t: 'completed', id, status, durationMs, steps, artifacts, completedAt }
12
+ *
13
+ * On spawn, we write the first line. On exit, we write the second. On daemon
14
+ * start, we read the last N pairs and surface them as read-only "persisted"
15
+ * sessions (no kill button, no live stream; the subprocess died with the
16
+ * previous daemon).
17
+ *
18
+ * Write path stays append-only — never rewrites history. Lock-free because
19
+ * single writer (the daemon). If two daemons ran concurrently the worst case
20
+ * is interleaved lines, which the reader tolerates.
21
+ */
22
+ import { existsSync, mkdirSync, appendFileSync, readFileSync } from 'fs';
23
+ import { homedir } from 'os';
24
+ import { join, dirname } from 'path';
25
+ const SESSIONS_FILE = join(homedir(), '.codehere', 'sessions.jsonl');
26
+ function ensureDir() {
27
+ const dir = dirname(SESSIONS_FILE);
28
+ if (!existsSync(dir))
29
+ mkdirSync(dir, { recursive: true });
30
+ }
31
+ export function appendSpawned(rec) {
32
+ try {
33
+ ensureDir();
34
+ appendFileSync(SESSIONS_FILE, JSON.stringify({ t: 'spawned', ...rec }) + '\n', 'utf-8');
35
+ }
36
+ catch {
37
+ /* persistence is best-effort; never break the spawn path */
38
+ }
39
+ }
40
+ export function appendCompleted(rec) {
41
+ try {
42
+ ensureDir();
43
+ appendFileSync(SESSIONS_FILE, JSON.stringify({ t: 'completed', ...rec }) + '\n', 'utf-8');
44
+ }
45
+ catch {
46
+ /* best-effort */
47
+ }
48
+ }
49
+ /**
50
+ * Walk the session log backward, pair spawned+completed records by id, return
51
+ * the most recent `limit` completed pairs sorted newest-first.
52
+ *
53
+ * We only surface fully-completed sessions because an orphaned `spawned` with
54
+ * no `completed` means either: (a) the daemon died mid-task (subprocess also
55
+ * died, no artifacts), or (b) a concurrent daemon is mid-flight (not ours to
56
+ * show). Either way, not useful in the UI.
57
+ */
58
+ export function loadRecent(limit = 20) {
59
+ if (!existsSync(SESSIONS_FILE))
60
+ return [];
61
+ let content;
62
+ try {
63
+ content = readFileSync(SESSIONS_FILE, 'utf-8');
64
+ }
65
+ catch {
66
+ return [];
67
+ }
68
+ const spawned = new Map();
69
+ const completed = [];
70
+ for (const line of content.split('\n')) {
71
+ const trimmed = line.trim();
72
+ if (!trimmed)
73
+ continue;
74
+ let rec;
75
+ try {
76
+ rec = JSON.parse(trimmed);
77
+ }
78
+ catch {
79
+ continue;
80
+ }
81
+ if (rec.t === 'spawned')
82
+ spawned.set(rec.id, rec);
83
+ else if (rec.t === 'completed')
84
+ completed.push(rec);
85
+ }
86
+ const merged = [];
87
+ // Walk completed in reverse so we return newest-first
88
+ for (let i = completed.length - 1; i >= 0 && merged.length < limit; i--) {
89
+ const c = completed[i];
90
+ const s = spawned.get(c.id);
91
+ if (!s)
92
+ continue; // orphan completed (shouldn't happen but harmless)
93
+ merged.push({
94
+ sessionId: s.id,
95
+ agent: s.agent,
96
+ prompt: s.prompt,
97
+ cwd: s.cwd,
98
+ startedAt: s.startedAt,
99
+ status: c.status,
100
+ durationMs: c.durationMs,
101
+ completedAt: c.completedAt,
102
+ steps: c.steps,
103
+ artifacts: c.artifacts,
104
+ provenance: 'persisted',
105
+ });
106
+ }
107
+ return merged;
108
+ }
109
+ //# sourceMappingURL=session-persistence.js.map
@@ -2,41 +2,21 @@
2
2
  * MCP helper functions — shared between MCP server and trust command.
3
3
  * Extracted to avoid circular imports.
4
4
  */
5
- import { readFileSync, statSync, readdirSync } from 'fs';
6
- import { join, extname } from 'path';
5
+ import { readFileSync } from 'fs';
6
+ import { collectCodeFiles } from '../security/file-collection.js';
7
7
  // ── File collection ──────────────────────────────────────────────────────────
8
- const CODE_EXTENSIONS = new Set([
9
- '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
10
- '.py', '.rb', '.go', '.java', '.cs', '.php',
11
- '.sh', '.bash', '.zsh',
12
- '.sql', '.html', '.vue', '.svelte',
13
- ]);
14
- const IGNORE_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '.cache', '.codehere', '.codehere-cache']);
8
+ // Uses the shared walker — same exclusion rules as trust and report.
15
9
  export function collectFilesForMCP(target) {
10
+ const paths = collectCodeFiles(target);
16
11
  const result = [];
17
- function walk(dir) {
18
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
19
- if (IGNORE_DIRS.has(entry.name))
20
- continue;
21
- const full = join(dir, entry.name);
22
- if (entry.isDirectory()) {
23
- walk(full);
24
- }
25
- else if (CODE_EXTENSIONS.has(extname(entry.name))) {
26
- try {
27
- result.push({ filepath: full, code: readFileSync(full, 'utf-8') });
28
- }
29
- catch {
30
- // skip unreadable files
31
- }
32
- }
12
+ for (const filepath of paths) {
13
+ try {
14
+ result.push({ filepath, code: readFileSync(filepath, 'utf-8') });
15
+ }
16
+ catch {
17
+ // skip unreadable files (iCloud-evicted, permissions, too large)
33
18
  }
34
19
  }
35
- const stat = statSync(target);
36
- if (stat.isFile()) {
37
- return [{ filepath: target, code: readFileSync(target, 'utf-8') }];
38
- }
39
- walk(target);
40
20
  return result;
41
21
  }
42
22
  // ── Scoring ──────────────────────────────────────────────────────────────────