claude-comms 1.0.46 → 2.0.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 (54) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +12 -13
  3. package/package.json +6 -10
  4. package/.agents/AGENTS.md +0 -61
  5. package/.agents/repo.md +0 -18
  6. package/.agents/tools/README.md +0 -45
  7. package/.agents/tools/agent-job/README.md +0 -208
  8. package/.agents/tools/agent-job/__pycache__/agent_monitor.cpython-312.pyc +0 -0
  9. package/.agents/tools/agent-job/agent_job.py +0 -936
  10. package/.agents/tools/agent-job/agent_monitor.py +0 -575
  11. package/.agents/tools/agent-job/consultant_template.txt +0 -73
  12. package/.agents/tools/chrome-devtools/README.md +0 -101
  13. package/.agents/tools/chrome-devtools/browsertools.py +0 -839
  14. package/.agents/tools/chrome-devtools/config.json +0 -24
  15. package/.agents/tools/workflow/CONSUMER_SETUP.md +0 -114
  16. package/.agents/tools/workflow/ONBOARD.md +0 -167
  17. package/.agents/tools/workflow/README.md +0 -169
  18. package/.agents/tools/workflow/cli.ts +0 -726
  19. package/.agents/tools/workflow/config.example.json +0 -16
  20. package/.agents/tools/workflow/init.ts +0 -76
  21. package/.agents/tools/workflow/lib/file-tracker.test.ts +0 -81
  22. package/.agents/tools/workflow/lib/file-tracker.ts +0 -387
  23. package/.agents/tools/workflow/lib/harness-executor.ts +0 -649
  24. package/.agents/tools/workflow/lib/prompts.scenario.test.ts +0 -262
  25. package/.agents/tools/workflow/lib/prompts.ts +0 -548
  26. package/.agents/tools/workflow/lib/streams.test.ts +0 -140
  27. package/.agents/tools/workflow/lib/streams.ts +0 -254
  28. package/.agents/tools/workflow/runner.ts +0 -994
  29. package/.agents/tools/workflow/templates/document.md +0 -42
  30. package/.agents/tools/workflow/templates/implement.md +0 -117
  31. package/.agents/tools/workflow/templates/plan.md +0 -54
  32. package/.agents/tools/workflow/templates/pm.md +0 -84
  33. package/.agents/tools/workflow/templates/product-owner.md +0 -211
  34. package/.agents/tools/workflow/templates/review.md +0 -56
  35. package/.agents/tools/workflow/templates/uat.md +0 -71
  36. package/.claude/agents/core/engineer.md +0 -146
  37. package/.claude/commands/action-feedback-item.md +0 -27
  38. package/.claude/commands/install-feedback-widget.md +0 -599
  39. package/.claude/commands/learn.md +0 -32
  40. package/.claude/hooks/comms/__pycache__/register_subagent.cpython-312.pyc +0 -0
  41. package/.claude/hooks/comms/__pycache__/update_subagent_completion.cpython-312.pyc +0 -0
  42. package/.claude/hooks/comms/get_unread_messages.py +0 -71
  43. package/.claude/hooks/comms/register_subagent.py +0 -74
  44. package/.claude/hooks/comms/send_message.py +0 -87
  45. package/.claude/hooks/comms/update_subagent_completion.py +0 -126
  46. package/.claude/hooks/context/session_id.py +0 -35
  47. package/.claude/hooks/observability/send_event.py +0 -110
  48. package/.claude/hooks/session-data/get_session_data.py +0 -261
  49. package/.claude/hooks/session-data/intercept_session_id.py +0 -69
  50. package/.claude/hooks/utils/__pycache__/server_config.cpython-312.pyc +0 -0
  51. package/.claude/hooks/utils/server_config.py +0 -17
  52. package/.claude/settings.json +0 -108
  53. package/.claude/settings.local.json +0 -46
  54. package/CLAUDE.md +0 -240
@@ -1,16 +0,0 @@
1
- {
2
- "convexUrl": "https://your-project.convex.cloud",
3
- "namespace": "my-project",
4
- "timeoutMs": 3600000,
5
- "idleTimeoutMs": 600000,
6
- "harnessDefaults": {
7
- "default": "claude",
8
- "plan": "claude",
9
- "implement": "claude",
10
- "review": "claude",
11
- "uat": "claude",
12
- "document": "claude",
13
- "pm": "claude",
14
- "chat": "claude"
15
- }
16
- }
@@ -1,76 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * Workflow Init Script
4
- *
5
- * Initializes the namespace in the Convex database based on config.json.
6
- * This script is idempotent - running it multiple times won't create duplicates.
7
- *
8
- * Usage:
9
- * npx tsx init.ts
10
- */
11
-
12
- import { ConvexClient } from "convex/browser";
13
- import { anyApi } from "convex/server";
14
- import { readFileSync, existsSync } from "fs";
15
- import { dirname, join } from "path";
16
- import { fileURLToPath } from "url";
17
-
18
- const api = anyApi;
19
- const __filename = fileURLToPath(import.meta.url);
20
- const __dirname = dirname(__filename);
21
-
22
- interface Config {
23
- convexUrl: string;
24
- namespace: string;
25
- timeoutMs?: number;
26
- harnessDefaults?: {
27
- default: "claude" | "codex" | "gemini";
28
- [jobType: string]: "claude" | "codex" | "gemini";
29
- };
30
- }
31
-
32
- function loadConfig(): Config {
33
- const configPath = join(__dirname, "config.json");
34
- if (!existsSync(configPath)) {
35
- console.error("Error: config.json not found at", configPath);
36
- console.error("Please copy config.example.json to config.json and configure it.");
37
- process.exit(1);
38
- }
39
- return JSON.parse(readFileSync(configPath, "utf-8"));
40
- }
41
-
42
- async function main() {
43
- const config = loadConfig();
44
- console.log(`Initializing namespace: ${config.namespace}`);
45
- console.log(`Convex URL: ${config.convexUrl}`);
46
-
47
- const client = new ConvexClient(config.convexUrl);
48
-
49
- try {
50
- // Check if namespace already exists
51
- const existing = await client.query(api.namespaces.getByName, {
52
- name: config.namespace,
53
- });
54
-
55
- if (existing) {
56
- console.log(`Namespace "${config.namespace}" already exists with ID: ${existing._id}`);
57
- console.log("No action needed - idempotent check passed.");
58
- } else {
59
- // Create the namespace
60
- const namespaceId = await client.mutation(api.namespaces.create, {
61
- name: config.namespace,
62
- description: `Namespace for ${config.namespace} repo`,
63
- });
64
- console.log(`Created namespace "${config.namespace}" with ID: ${namespaceId}`);
65
- }
66
-
67
- console.log("\nInit complete!");
68
- } catch (error) {
69
- console.error("Error during init:", error);
70
- process.exit(1);
71
- } finally {
72
- await client.close();
73
- }
74
- }
75
-
76
- main();
@@ -1,81 +0,0 @@
1
- /**
2
- * Tests for file-based job tracking module
3
- *
4
- * Run with: npx tsx --test lib/file-tracker.test.ts
5
- */
6
-
7
- import { describe, it, before, after } from "node:test";
8
- import assert from "node:assert";
9
- import { rmSync, existsSync, readFileSync } from "node:fs";
10
- import { join } from "node:path";
11
- import { tmpdir } from "node:os";
12
- import { JobTracker } from "./file-tracker.js";
13
-
14
- // ============================================================================
15
- // JobTracker tests - the high-level API that matters
16
- // ============================================================================
17
-
18
- describe("JobTracker", () => {
19
- const TEST_ROOT = join(tmpdir(), "agent_jobs_test_" + Date.now());
20
-
21
- before(() => {
22
- process.env.AGENT_JOBS_ROOT = TEST_ROOT;
23
- });
24
-
25
- after(() => {
26
- delete process.env.AGENT_JOBS_ROOT;
27
- if (existsSync(TEST_ROOT)) {
28
- rmSync(TEST_ROOT, { recursive: true, force: true });
29
- }
30
- });
31
-
32
- it("creates status.json with correct initial state", () => {
33
- const tracker = new JobTracker("init-test", "claude", 12345);
34
- const status = tracker.getStatus();
35
-
36
- assert.strictEqual(status.job_id, "init-test");
37
- assert.strictEqual(status.harness, "claude");
38
- assert.strictEqual(status.pid, 12345);
39
- assert.strictEqual(status.status, "running");
40
- assert.ok(status.logs?.endsWith("agent.log"));
41
- });
42
-
43
- it("records events with operations count and status reason", () => {
44
- const tracker = new JobTracker("events-test", "codex", 99999);
45
-
46
- tracker.recordEvent("assistant");
47
- tracker.recordEvent("tool_use");
48
- tracker.recordEvent();
49
-
50
- const status = tracker.getStatus();
51
- assert.strictEqual(status.operations, 3);
52
- assert.strictEqual(status.status_reason, "tool_use"); // last explicit
53
- });
54
-
55
- it("logs lines to agent.log", () => {
56
- const tracker = new JobTracker("log-test", "gemini", 11111);
57
-
58
- tracker.logLine('{"type":"init"}');
59
- tracker.logLine('{"type":"result"}');
60
-
61
- const content = readFileSync(tracker.getLogPath(), "utf-8");
62
- assert.ok(content.includes("init"));
63
- assert.ok(content.includes("result"));
64
- });
65
-
66
- it("marks complete/fail/timeout with end_time", () => {
67
- const t1 = new JobTracker("complete-test", "claude", 1);
68
- t1.complete("Done");
69
- assert.strictEqual(t1.getStatus().status, "complete");
70
- assert.ok(t1.getStatus().end_time);
71
-
72
- const t2 = new JobTracker("fail-test", "claude", 2);
73
- t2.fail("exit_1");
74
- assert.strictEqual(t2.getStatus().status, "error");
75
- assert.strictEqual(t2.getStatus().status_reason, "exit_1");
76
-
77
- const t3 = new JobTracker("timeout-test", "claude", 3);
78
- t3.timeout();
79
- assert.strictEqual(t3.getStatus().status, "timeout");
80
- });
81
- });
@@ -1,387 +0,0 @@
1
- /**
2
- * File-based Job Tracking
3
- *
4
- * Compatible with agent_monitor.py TUI. Writes status.json and agent.log
5
- * files in the same format as agent_job.py for process-level monitoring.
6
- *
7
- * This is separate from Convex tracking - used for debugging/monitoring
8
- * when you need to see verbose JSON streams and process-level telemetry.
9
- *
10
- * Enhanced to support:
11
- * - read_position tracking for recovery after runner restart
12
- * - Orphan detection and reconciliation
13
- * - Reading status from disk
14
- */
15
-
16
- import {
17
- writeFileSync,
18
- mkdirSync,
19
- appendFileSync,
20
- renameSync,
21
- existsSync,
22
- readFileSync,
23
- readdirSync,
24
- statSync,
25
- } from "fs";
26
- import { join } from "path";
27
- import { tmpdir } from "os";
28
-
29
- // ============================================================================
30
- // Types
31
- // ============================================================================
32
-
33
- export interface FileJobStatus {
34
- job_id: string;
35
- harness: string;
36
- agent_id: string | null;
37
- pid: number | null;
38
- logs: string | null;
39
- status: "running" | "complete" | "error" | "timeout";
40
- status_reason: string | null;
41
- start_time: string | null;
42
- last_event_time: string | null;
43
- end_time: string | null;
44
- operations: number;
45
- /** Byte position in log file that has been processed (for recovery) */
46
- read_position?: number;
47
- completion: {
48
- messages: string[];
49
- final_message: string | null;
50
- tokens: {
51
- input: number | null;
52
- output: number | null;
53
- total: number | null;
54
- };
55
- duration_ms: number | null;
56
- };
57
- }
58
-
59
- export interface JobPaths {
60
- statusPath: string;
61
- logPath: string;
62
- }
63
-
64
- // ============================================================================
65
- // Utilities
66
- // ============================================================================
67
-
68
- /**
69
- * Get the root directory for job files.
70
- * Respects AGENT_JOBS_ROOT env var, defaults to /tmp/agent_jobs/$USER
71
- */
72
- export function getJobsRoot(): string {
73
- if (process.env.AGENT_JOBS_ROOT) {
74
- return process.env.AGENT_JOBS_ROOT;
75
- }
76
- const user = process.env.USER || "unknown";
77
- return join(tmpdir(), "agent_jobs", user);
78
- }
79
-
80
- /**
81
- * Get UTC timestamp in ISO format (matches agent_job.py format)
82
- */
83
- export function utcNowIso(): string {
84
- return new Date().toISOString().replace(/\.\d{3}Z$/, ".000Z");
85
- }
86
-
87
- /**
88
- * Get the directory path for a specific job
89
- */
90
- export function getJobDir(jobId: string): string {
91
- return join(getJobsRoot(), jobId);
92
- }
93
-
94
- /**
95
- * Check if a process is alive by PID
96
- */
97
- export function isPidAlive(pid: number | null): boolean {
98
- if (!pid) return false;
99
- try {
100
- process.kill(pid, 0); // Signal 0 = check existence
101
- return true;
102
- } catch {
103
- return false;
104
- }
105
- }
106
-
107
- /**
108
- * List all job directories in the jobs root
109
- */
110
- export function listJobDirs(): string[] {
111
- const root = getJobsRoot();
112
- if (!existsSync(root)) return [];
113
-
114
- return readdirSync(root).filter((name) => {
115
- const dir = join(root, name);
116
- try {
117
- return statSync(dir).isDirectory();
118
- } catch {
119
- return false;
120
- }
121
- });
122
- }
123
-
124
- /**
125
- * Read status.json for a job, returns null if not found or invalid
126
- */
127
- export function readJobStatus(jobId: string): FileJobStatus | null {
128
- const statusPath = join(getJobDir(jobId), "status.json");
129
- if (!existsSync(statusPath)) return null;
130
-
131
- try {
132
- const content = readFileSync(statusPath, "utf-8");
133
- return JSON.parse(content) as FileJobStatus;
134
- } catch {
135
- return null;
136
- }
137
- }
138
-
139
- /**
140
- * Get paths for a job (without creating the directory)
141
- */
142
- export function getJobPaths(jobId: string): JobPaths {
143
- const jobDir = getJobDir(jobId);
144
- return {
145
- statusPath: join(jobDir, "status.json"),
146
- logPath: join(jobDir, "agent.log"),
147
- };
148
- }
149
-
150
- /**
151
- * Find orphaned jobs: status="running" but process is dead
152
- */
153
- export function findOrphanedJobs(): Array<{
154
- jobId: string;
155
- status: FileJobStatus;
156
- paths: JobPaths;
157
- }> {
158
- const orphans: Array<{ jobId: string; status: FileJobStatus; paths: JobPaths }> = [];
159
-
160
- for (const jobId of listJobDirs()) {
161
- const status = readJobStatus(jobId);
162
- if (!status) continue;
163
-
164
- // Only care about "running" jobs
165
- if (status.status !== "running") continue;
166
-
167
- // Check if process is still alive
168
- if (!isPidAlive(status.pid)) {
169
- orphans.push({
170
- jobId,
171
- status,
172
- paths: getJobPaths(jobId),
173
- });
174
- }
175
- }
176
-
177
- return orphans;
178
- }
179
-
180
- // ============================================================================
181
- // Job Status Management
182
- // ============================================================================
183
-
184
- /**
185
- * Create initial job status object
186
- */
187
- export function createFileJobStatus(
188
- jobId: string,
189
- harness: string,
190
- pid: number
191
- ): FileJobStatus {
192
- return {
193
- job_id: jobId,
194
- harness,
195
- agent_id: jobId,
196
- pid,
197
- logs: null, // Set after dir creation
198
- status: "running",
199
- status_reason: "initializing",
200
- start_time: utcNowIso(),
201
- last_event_time: utcNowIso(),
202
- end_time: null,
203
- operations: 0,
204
- read_position: 0,
205
- completion: {
206
- messages: [],
207
- final_message: null,
208
- tokens: { input: null, output: null, total: null },
209
- duration_ms: null,
210
- },
211
- };
212
- }
213
-
214
- /**
215
- * Create job directory and return paths to status.json and agent.log
216
- */
217
- export function ensureJobDir(jobId: string): JobPaths {
218
- const jobDir = getJobDir(jobId);
219
- mkdirSync(jobDir, { recursive: true });
220
- return {
221
- statusPath: join(jobDir, "status.json"),
222
- logPath: join(jobDir, "agent.log"),
223
- };
224
- }
225
-
226
- /**
227
- * Write status.json atomically (via temp file + rename)
228
- */
229
- export function writeJobStatus(statusPath: string, status: FileJobStatus): void {
230
- const tmpPath = statusPath + ".tmp";
231
- writeFileSync(tmpPath, JSON.stringify(status, null, 2));
232
- renameSync(tmpPath, statusPath);
233
- }
234
-
235
- /**
236
- * Append a line to the agent.log file
237
- */
238
- export function appendToLog(logPath: string, line: string): void {
239
- appendFileSync(logPath, line + "\n");
240
- }
241
-
242
- // ============================================================================
243
- // High-level Job Tracker
244
- // ============================================================================
245
-
246
- /**
247
- * JobTracker manages file-based status for a single job.
248
- * Use this to track process-level telemetry alongside Convex.
249
- */
250
- export class JobTracker {
251
- private status: FileJobStatus;
252
- private paths: JobPaths;
253
-
254
- constructor(jobId: string, harness: string, pid: number) {
255
- this.paths = ensureJobDir(jobId);
256
- this.status = createFileJobStatus(jobId, harness, pid);
257
- this.status.logs = this.paths.logPath;
258
- this.writeStatus();
259
- }
260
-
261
- /**
262
- * Create a JobTracker from an existing status on disk.
263
- * Used for recovery/reconciliation of orphaned jobs.
264
- */
265
- static fromExisting(
266
- jobId: string,
267
- status: FileJobStatus,
268
- paths: JobPaths
269
- ): JobTracker {
270
- const tracker = Object.create(JobTracker.prototype) as JobTracker;
271
- tracker.status = { ...status };
272
- tracker.paths = paths;
273
- return tracker;
274
- }
275
-
276
- /** Update status reason (current activity) */
277
- updateStatusReason(reason: string): void {
278
- this.status.status_reason = reason;
279
- this.status.last_event_time = utcNowIso();
280
- this.writeStatus();
281
- }
282
-
283
- /** Increment operation count */
284
- incrementOperations(): void {
285
- this.status.operations++;
286
- this.status.last_event_time = utcNowIso();
287
- this.writeStatus();
288
- }
289
-
290
- /** Record an event (increments ops, updates timestamp, optionally updates reason) */
291
- recordEvent(statusReason?: string): void {
292
- this.status.operations++;
293
- this.status.last_event_time = utcNowIso();
294
- if (statusReason) {
295
- this.status.status_reason = statusReason;
296
- }
297
- this.writeStatus();
298
- }
299
-
300
- /** Append raw JSON line to log file */
301
- logLine(line: string): void {
302
- appendToLog(this.paths.logPath, line);
303
- }
304
-
305
- /** Mark job as complete */
306
- complete(finalMessage?: string): void {
307
- this.status.status = "complete";
308
- this.status.status_reason = "completed";
309
- this.status.end_time = utcNowIso();
310
- if (finalMessage) {
311
- this.status.completion.final_message = finalMessage;
312
- this.status.completion.messages.push(finalMessage);
313
- }
314
- this.writeStatus();
315
- }
316
-
317
- /** Mark job as failed */
318
- fail(reason: string): void {
319
- this.status.status = "error";
320
- this.status.status_reason = reason;
321
- this.status.end_time = utcNowIso();
322
- this.writeStatus();
323
- }
324
-
325
- /** Mark job as timed out */
326
- timeout(): void {
327
- this.status.status = "timeout";
328
- this.status.status_reason = "idle_timeout";
329
- this.status.end_time = utcNowIso();
330
- this.writeStatus();
331
- }
332
-
333
- /** Update token stats */
334
- updateTokens(input: number | null, output: number | null): void {
335
- this.status.completion.tokens.input = input;
336
- this.status.completion.tokens.output = output;
337
- if (input != null && output != null) {
338
- this.status.completion.tokens.total = input + output;
339
- }
340
- this.writeStatus();
341
- }
342
-
343
- /** Get current status */
344
- getStatus(): FileJobStatus {
345
- return { ...this.status };
346
- }
347
-
348
- /** Get log path */
349
- getLogPath(): string {
350
- return this.paths.logPath;
351
- }
352
-
353
- /** Get status path */
354
- getStatusPath(): string {
355
- return this.paths.statusPath;
356
- }
357
-
358
- /** Get current read position in log file */
359
- getReadPosition(): number {
360
- return this.status.read_position ?? 0;
361
- }
362
-
363
- /** Update read position (bytes processed in log file) */
364
- setReadPosition(position: number): void {
365
- this.status.read_position = position;
366
- this.writeStatus();
367
- }
368
-
369
- /** Get job ID */
370
- getJobId(): string {
371
- return this.status.job_id;
372
- }
373
-
374
- /** Get harness type */
375
- getHarness(): string {
376
- return this.status.harness;
377
- }
378
-
379
- /** Check if job is in a terminal state */
380
- isTerminal(): boolean {
381
- return this.status.status !== "running";
382
- }
383
-
384
- private writeStatus(): void {
385
- writeJobStatus(this.paths.statusPath, this.status);
386
- }
387
- }