@artyfacts/claude 1.3.4 → 1.3.6

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.
@@ -0,0 +1,787 @@
1
+ // src/auth.ts
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ import * as readline from "readline";
6
+ var CREDENTIALS_DIR = path.join(os.homedir(), ".artyfacts");
7
+ var CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, "credentials.json");
8
+ var DEFAULT_BASE_URL = "https://artyfacts.dev/api/v1";
9
+ function loadCredentials() {
10
+ try {
11
+ if (!fs.existsSync(CREDENTIALS_FILE)) {
12
+ return null;
13
+ }
14
+ const data = fs.readFileSync(CREDENTIALS_FILE, "utf-8");
15
+ const credentials = JSON.parse(data);
16
+ if (credentials.expiresAt) {
17
+ const expiresAt = new Date(credentials.expiresAt);
18
+ if (expiresAt < /* @__PURE__ */ new Date()) {
19
+ console.log("\u26A0\uFE0F Credentials have expired");
20
+ return null;
21
+ }
22
+ }
23
+ return credentials;
24
+ } catch (error) {
25
+ console.error("Failed to load credentials:", error);
26
+ return null;
27
+ }
28
+ }
29
+ function saveCredentials(credentials) {
30
+ try {
31
+ if (!fs.existsSync(CREDENTIALS_DIR)) {
32
+ fs.mkdirSync(CREDENTIALS_DIR, { mode: 448, recursive: true });
33
+ }
34
+ fs.writeFileSync(
35
+ CREDENTIALS_FILE,
36
+ JSON.stringify(credentials, null, 2),
37
+ { mode: 384 }
38
+ );
39
+ } catch (error) {
40
+ throw new Error(`Failed to save credentials: ${error}`);
41
+ }
42
+ }
43
+ function clearCredentials() {
44
+ try {
45
+ if (fs.existsSync(CREDENTIALS_FILE)) {
46
+ fs.unlinkSync(CREDENTIALS_FILE);
47
+ }
48
+ } catch (error) {
49
+ console.error("Failed to clear credentials:", error);
50
+ }
51
+ }
52
+ async function runDeviceAuth(baseUrl = DEFAULT_BASE_URL) {
53
+ console.log("\u{1F510} Starting device authentication...\n");
54
+ const deviceAuth = await requestDeviceCode(baseUrl);
55
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
56
+ console.log("\u{1F4CB} To authenticate, visit:");
57
+ console.log(` ${deviceAuth.verificationUri}`);
58
+ console.log("");
59
+ console.log("\u{1F511} Enter this code:");
60
+ console.log(` ${deviceAuth.userCode}`);
61
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
62
+ console.log("\u23F3 Waiting for authentication...\n");
63
+ const credentials = await pollForToken(
64
+ baseUrl,
65
+ deviceAuth.deviceCode,
66
+ deviceAuth.interval,
67
+ deviceAuth.expiresIn
68
+ );
69
+ saveCredentials(credentials);
70
+ console.log("\u2705 Authentication successful!");
71
+ console.log(` Agent ID: ${credentials.agentId}`);
72
+ if (credentials.agentName) {
73
+ console.log(` Agent Name: ${credentials.agentName}`);
74
+ }
75
+ console.log("");
76
+ return credentials;
77
+ }
78
+ async function requestDeviceCode(baseUrl) {
79
+ const response = await fetch(`${baseUrl}/auth/device`, {
80
+ method: "POST",
81
+ headers: {
82
+ "Content-Type": "application/json"
83
+ },
84
+ body: JSON.stringify({
85
+ client_id: "artyfacts-claude",
86
+ scope: "agent:execute"
87
+ })
88
+ });
89
+ if (!response.ok) {
90
+ const error = await response.text();
91
+ throw new Error(`Failed to start device auth: ${error}`);
92
+ }
93
+ const data = await response.json();
94
+ return {
95
+ deviceCode: data.deviceCode || data.device_code || "",
96
+ userCode: data.userCode || data.user_code || "",
97
+ verificationUri: data.verificationUri || data.verification_uri || `https://artyfacts.dev/auth/device`,
98
+ expiresIn: data.expiresIn || data.expires_in || 600,
99
+ interval: data.interval || 5
100
+ };
101
+ }
102
+ async function pollForToken(baseUrl, deviceCode, interval, expiresIn) {
103
+ const startTime = Date.now();
104
+ const timeoutMs = expiresIn * 1e3;
105
+ while (true) {
106
+ if (Date.now() - startTime > timeoutMs) {
107
+ throw new Error("Device authentication timed out");
108
+ }
109
+ await sleep(interval * 1e3);
110
+ const response = await fetch(`${baseUrl}/auth/device/token`, {
111
+ method: "POST",
112
+ headers: {
113
+ "Content-Type": "application/json"
114
+ },
115
+ body: JSON.stringify({
116
+ device_code: deviceCode,
117
+ client_id: "artyfacts-claude"
118
+ })
119
+ });
120
+ if (response.ok) {
121
+ const data = await response.json();
122
+ return {
123
+ apiKey: data.apiKey,
124
+ agentId: data.agentId,
125
+ agentName: data.agentName,
126
+ expiresAt: data.expiresAt
127
+ };
128
+ }
129
+ const errorData = await response.json().catch(() => ({}));
130
+ const errorCode = errorData.error || errorData.code;
131
+ if (errorCode === "authorization_pending") {
132
+ process.stdout.write(".");
133
+ continue;
134
+ }
135
+ if (errorCode === "slow_down") {
136
+ interval = Math.min(interval * 2, 30);
137
+ continue;
138
+ }
139
+ if (errorCode === "expired_token") {
140
+ throw new Error("Device code expired. Please try again.");
141
+ }
142
+ if (errorCode === "access_denied") {
143
+ throw new Error("Authorization was denied.");
144
+ }
145
+ throw new Error(`Authentication failed: ${errorData.message || errorCode || response.statusText}`);
146
+ }
147
+ }
148
+ async function promptForApiKey() {
149
+ const rl = readline.createInterface({
150
+ input: process.stdin,
151
+ output: process.stdout
152
+ });
153
+ const question = (prompt) => {
154
+ return new Promise((resolve) => {
155
+ rl.question(prompt, resolve);
156
+ });
157
+ };
158
+ console.log("\u{1F511} Manual Configuration\n");
159
+ console.log("Enter your Artyfacts credentials:\n");
160
+ const apiKey = await question("API Key: ");
161
+ const agentId = await question("Agent ID: ");
162
+ const agentName = await question("Agent Name (optional): ");
163
+ rl.close();
164
+ if (!apiKey || !agentId) {
165
+ throw new Error("API Key and Agent ID are required");
166
+ }
167
+ const credentials = {
168
+ apiKey: apiKey.trim(),
169
+ agentId: agentId.trim(),
170
+ agentName: agentName.trim() || void 0
171
+ };
172
+ saveCredentials(credentials);
173
+ console.log("\n\u2705 Credentials saved!");
174
+ return credentials;
175
+ }
176
+ function sleep(ms) {
177
+ return new Promise((resolve) => setTimeout(resolve, ms));
178
+ }
179
+ async function getCredentials(options) {
180
+ if (!options?.forceAuth) {
181
+ const existing = loadCredentials();
182
+ if (existing) {
183
+ return existing;
184
+ }
185
+ }
186
+ return runDeviceAuth(options?.baseUrl);
187
+ }
188
+
189
+ // src/context.ts
190
+ var ContextFetcher = class {
191
+ config;
192
+ constructor(config) {
193
+ this.config = config;
194
+ }
195
+ /**
196
+ * Fetch full context for a task
197
+ */
198
+ async fetchTaskContext(taskId) {
199
+ const response = await fetch(
200
+ `${this.config.baseUrl}/tasks/${taskId}/context`,
201
+ {
202
+ headers: {
203
+ "Authorization": `Bearer ${this.config.apiKey}`,
204
+ "Accept": "application/json"
205
+ }
206
+ }
207
+ );
208
+ if (!response.ok) {
209
+ const errorText = await response.text().catch(() => "Unknown error");
210
+ throw new Error(`Failed to fetch task context: ${response.status} - ${errorText}`);
211
+ }
212
+ const data = await response.json();
213
+ return data;
214
+ }
215
+ };
216
+ function buildPromptWithContext(context) {
217
+ const parts = [];
218
+ parts.push(`You are an AI agent working within the Artyfacts task management system.
219
+
220
+ Your job is to complete the assigned task. You have full context about the organization, project, and related work.
221
+
222
+ ## Available Tools
223
+
224
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
225
+
226
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
227
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
228
+ - **update_section** - Update existing sections
229
+ - **create_agent** - Create new AI agents with specific roles
230
+ - **list_artifacts** - Query existing artifacts
231
+ - **list_sections** - Query sections within an artifact
232
+ - **complete_task** - Mark a task as complete
233
+ - **block_task** - Block a task with a reason
234
+ - **create_blocker** - Create a decision blocker
235
+
236
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it with the tools.
237
+
238
+ ## Guidelines
239
+
240
+ - Be thorough but concise
241
+ - USE THE TOOLS to take action, don't just analyze
242
+ - If the task requires creating something, use create_artifact or create_section
243
+ - If the task requires creating agents, use create_agent
244
+ - If you cannot complete the task, explain why
245
+
246
+ Format your response as follows:
247
+ 1. First, use the tools to complete the task
248
+ 2. Then summarize what you did
249
+ 3. End with a brief summary line starting with "SUMMARY:"`);
250
+ parts.push("");
251
+ parts.push("---");
252
+ parts.push("");
253
+ parts.push("## Organization Context");
254
+ parts.push(`**${context.organization.name}**`);
255
+ if (context.organization.context) {
256
+ parts.push("");
257
+ parts.push(formatOrgContext(context.organization.context));
258
+ }
259
+ parts.push("");
260
+ if (context.project) {
261
+ parts.push(`## Project: ${context.project.name}`);
262
+ if (context.project.description) {
263
+ parts.push(context.project.description);
264
+ }
265
+ parts.push("");
266
+ }
267
+ parts.push(`## Artifact: ${context.artifact.title}`);
268
+ if (context.artifact.summary) {
269
+ parts.push(context.artifact.summary);
270
+ }
271
+ if (context.artifact.description) {
272
+ parts.push("");
273
+ parts.push(context.artifact.description);
274
+ }
275
+ parts.push("");
276
+ const relatedSections = context.artifact.sections.filter(
277
+ (s) => s.id !== context.task.id
278
+ );
279
+ if (relatedSections.length > 0) {
280
+ parts.push("### Related Sections:");
281
+ for (const section of relatedSections) {
282
+ const preview = section.content ? section.content.substring(0, 200) + (section.content.length > 200 ? "..." : "") : "No content";
283
+ const statusBadge = section.task_status ? ` [${section.task_status}]` : "";
284
+ parts.push(`- **${section.heading}**${statusBadge}: ${preview}`);
285
+ }
286
+ parts.push("");
287
+ }
288
+ parts.push("---");
289
+ parts.push("");
290
+ parts.push(`## Your Task: ${context.task.heading}`);
291
+ if (context.task.priority) {
292
+ const priorityLabels = ["\u{1F534} High", "\u{1F7E1} Medium", "\u{1F7E2} Low"];
293
+ parts.push(`**Priority:** ${priorityLabels[context.task.priority - 1] || "Medium"}`);
294
+ }
295
+ parts.push("");
296
+ parts.push("### Description");
297
+ parts.push(context.task.content || "No additional description provided.");
298
+ parts.push("");
299
+ if (context.task.expected_output) {
300
+ parts.push("### Expected Output");
301
+ if (context.task.expected_output.format) {
302
+ parts.push(`**Format:** ${context.task.expected_output.format}`);
303
+ }
304
+ if (context.task.expected_output.requirements && context.task.expected_output.requirements.length > 0) {
305
+ parts.push("**Requirements:**");
306
+ for (const req of context.task.expected_output.requirements) {
307
+ parts.push(`- ${req}`);
308
+ }
309
+ }
310
+ parts.push("");
311
+ }
312
+ parts.push("---");
313
+ parts.push("");
314
+ parts.push("Complete this task and provide your output below.");
315
+ return parts.join("\n");
316
+ }
317
+ function formatOrgContext(context) {
318
+ const trimmed = context.trim();
319
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
320
+ try {
321
+ const parsed = JSON.parse(trimmed);
322
+ return formatContextObject(parsed);
323
+ } catch {
324
+ return context;
325
+ }
326
+ }
327
+ return context;
328
+ }
329
+ function formatContextObject(obj, indent = "") {
330
+ if (typeof obj !== "object" || obj === null) {
331
+ return String(obj);
332
+ }
333
+ if (Array.isArray(obj)) {
334
+ return obj.map((item) => `${indent}- ${formatContextObject(item, indent + " ")}`).join("\n");
335
+ }
336
+ const lines = [];
337
+ for (const [key, value] of Object.entries(obj)) {
338
+ const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
339
+ if (typeof value === "object" && value !== null) {
340
+ lines.push(`${indent}**${label}:**`);
341
+ lines.push(formatContextObject(value, indent + " "));
342
+ } else {
343
+ lines.push(`${indent}- **${label}:** ${value}`);
344
+ }
345
+ }
346
+ return lines.join("\n");
347
+ }
348
+ function createContextFetcher(config) {
349
+ return new ContextFetcher(config);
350
+ }
351
+
352
+ // src/executor.ts
353
+ import { spawn } from "child_process";
354
+ var DEFAULT_TIMEOUT = 5 * 60 * 1e3;
355
+ var DEFAULT_SYSTEM_PROMPT = `You are an AI agent working within the Artyfacts task management system.
356
+
357
+ Your job is to complete tasks assigned to you using the available tools.
358
+
359
+ ## Available Tools
360
+
361
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
362
+
363
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
364
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
365
+ - **update_section** - Update existing sections
366
+ - **create_agent** - Create new AI agents with specific roles
367
+ - **list_artifacts** - Query existing artifacts
368
+ - **list_sections** - Query sections within an artifact
369
+ - **complete_task** - Mark a task as complete
370
+ - **block_task** - Block a task with a reason
371
+ - **create_blocker** - Create a decision blocker
372
+
373
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it.
374
+
375
+ ## Guidelines
376
+
377
+ - USE THE TOOLS to take action
378
+ - If creating something, use create_artifact or create_section
379
+ - If creating agents, use create_agent
380
+ - If you cannot complete the task, explain why
381
+
382
+ Format your response as follows:
383
+ 1. First, use the tools to complete the task
384
+ 2. Summarize what you accomplished
385
+ 3. End with a brief summary line starting with "SUMMARY:"`;
386
+ var ClaudeExecutor = class {
387
+ config;
388
+ contextFetcher = null;
389
+ constructor(config = {}) {
390
+ this.config = {
391
+ ...config,
392
+ timeout: config.timeout || DEFAULT_TIMEOUT,
393
+ claudePath: config.claudePath || "claude"
394
+ };
395
+ if (config.baseUrl && config.apiKey) {
396
+ this.contextFetcher = createContextFetcher({
397
+ baseUrl: config.baseUrl,
398
+ apiKey: config.apiKey
399
+ });
400
+ }
401
+ }
402
+ /**
403
+ * Execute a task using Claude Code CLI
404
+ *
405
+ * If full context is available (baseUrl + apiKey configured), fetches
406
+ * organization, project, artifact, and related sections for a rich prompt.
407
+ */
408
+ async execute(task) {
409
+ try {
410
+ let prompt;
411
+ let fullContext = null;
412
+ const useFullContext = this.config.useFullContext !== false && this.contextFetcher;
413
+ if (useFullContext) {
414
+ try {
415
+ fullContext = await this.contextFetcher.fetchTaskContext(task.taskId);
416
+ prompt = buildPromptWithContext(fullContext);
417
+ console.log(" \u{1F4DA} Using full context (org, project, artifact, related sections)");
418
+ } catch (contextError) {
419
+ console.warn(" \u26A0\uFE0F Could not fetch full context, using minimal prompt");
420
+ console.warn(` ${contextError instanceof Error ? contextError.message : contextError}`);
421
+ prompt = this.buildTaskPrompt(task);
422
+ }
423
+ } else {
424
+ prompt = this.buildTaskPrompt(task);
425
+ }
426
+ const output = await this.runClaude(prompt);
427
+ const { content, summary } = this.parseResponse(output, task.heading);
428
+ return {
429
+ success: true,
430
+ output: content,
431
+ summary,
432
+ promptUsed: prompt
433
+ };
434
+ } catch (error) {
435
+ const errorMessage = error instanceof Error ? error.message : String(error);
436
+ return {
437
+ success: false,
438
+ output: "",
439
+ summary: `Failed: ${errorMessage}`,
440
+ error: errorMessage
441
+ };
442
+ }
443
+ }
444
+ /**
445
+ * Run Claude Code CLI with the given prompt
446
+ */
447
+ runClaude(prompt) {
448
+ return new Promise((resolve, reject) => {
449
+ const claudePath = this.config.claudePath || "claude";
450
+ const proc = spawn(claudePath, ["--print"], {
451
+ stdio: ["pipe", "pipe", "pipe"],
452
+ timeout: this.config.timeout
453
+ });
454
+ let stdout = "";
455
+ let stderr = "";
456
+ proc.stdout.on("data", (data) => {
457
+ stdout += data.toString();
458
+ });
459
+ proc.stderr.on("data", (data) => {
460
+ stderr += data.toString();
461
+ });
462
+ proc.on("close", (code) => {
463
+ if (code === 0) {
464
+ resolve(stdout.trim());
465
+ } else {
466
+ reject(new Error(stderr || `Claude exited with code ${code}`));
467
+ }
468
+ });
469
+ proc.on("error", (err) => {
470
+ if (err.code === "ENOENT") {
471
+ reject(new Error(
472
+ "Claude Code CLI not found. Please install it:\n npm install -g @anthropic-ai/claude-code"
473
+ ));
474
+ } else {
475
+ reject(err);
476
+ }
477
+ });
478
+ proc.stdin.write(prompt);
479
+ proc.stdin.end();
480
+ });
481
+ }
482
+ /**
483
+ * Build the task prompt
484
+ */
485
+ buildTaskPrompt(task) {
486
+ const parts = [];
487
+ const systemPrompt = this.config.systemPromptPrefix ? `${this.config.systemPromptPrefix}
488
+
489
+ ${DEFAULT_SYSTEM_PROMPT}` : DEFAULT_SYSTEM_PROMPT;
490
+ parts.push(systemPrompt);
491
+ parts.push("");
492
+ parts.push("---");
493
+ parts.push("");
494
+ parts.push(`# Task: ${task.heading}`);
495
+ parts.push("");
496
+ if (task.artifactTitle) {
497
+ parts.push(`**Artifact:** ${task.artifactTitle}`);
498
+ }
499
+ if (task.priority) {
500
+ const priorityLabels = ["High", "Medium", "Low"];
501
+ parts.push(`**Priority:** ${priorityLabels[task.priority - 1] || "Medium"}`);
502
+ }
503
+ parts.push("");
504
+ parts.push("## Description");
505
+ parts.push(task.content || "No additional description provided.");
506
+ parts.push("");
507
+ if (task.context && Object.keys(task.context).length > 0) {
508
+ parts.push("## Additional Context");
509
+ parts.push("```json");
510
+ parts.push(JSON.stringify(task.context, null, 2));
511
+ parts.push("```");
512
+ parts.push("");
513
+ }
514
+ parts.push("## Instructions");
515
+ parts.push("Complete this task and provide your output below.");
516
+ return parts.join("\n");
517
+ }
518
+ /**
519
+ * Parse the response to extract output and summary
520
+ */
521
+ parseResponse(fullOutput, taskHeading) {
522
+ const summaryMatch = fullOutput.match(/SUMMARY:\s*(.+?)(?:\n|$)/i);
523
+ if (summaryMatch) {
524
+ const summary2 = summaryMatch[1].trim();
525
+ const content = fullOutput.replace(/SUMMARY:\s*.+?(?:\n|$)/i, "").trim();
526
+ return { content, summary: summary2 };
527
+ }
528
+ const lines = fullOutput.split("\n").filter((l) => l.trim());
529
+ const firstLine = lines[0] || "";
530
+ const summary = firstLine.length > 100 ? `${firstLine.substring(0, 97)}...` : firstLine || `Completed: ${taskHeading}`;
531
+ return { content: fullOutput, summary };
532
+ }
533
+ /**
534
+ * Test that Claude Code CLI is available and working
535
+ */
536
+ async testConnection() {
537
+ try {
538
+ const output = await this.runClaude('Say "connected" and nothing else.');
539
+ return output.toLowerCase().includes("connected");
540
+ } catch {
541
+ return false;
542
+ }
543
+ }
544
+ /**
545
+ * Check if Claude Code CLI is installed
546
+ */
547
+ async isInstalled() {
548
+ return new Promise((resolve) => {
549
+ const proc = spawn(this.config.claudePath || "claude", ["--version"], {
550
+ stdio: ["ignore", "pipe", "pipe"]
551
+ });
552
+ proc.on("close", (code) => {
553
+ resolve(code === 0);
554
+ });
555
+ proc.on("error", () => {
556
+ resolve(false);
557
+ });
558
+ });
559
+ }
560
+ };
561
+ function createExecutor(config) {
562
+ return new ClaudeExecutor(config);
563
+ }
564
+
565
+ // src/listener.ts
566
+ import EventSource from "eventsource";
567
+ var DEFAULT_BASE_URL2 = "https://artyfacts.dev/api/v1";
568
+ var EVENT_TYPES = [
569
+ "connected",
570
+ "heartbeat",
571
+ "task_assigned",
572
+ "task_unblocked",
573
+ "blocker_resolved",
574
+ "notification"
575
+ ];
576
+ var ArtyfactsListener = class {
577
+ config;
578
+ eventSource = null;
579
+ callbacks = /* @__PURE__ */ new Map();
580
+ allCallbacks = /* @__PURE__ */ new Set();
581
+ state = "disconnected";
582
+ reconnectAttempts = 0;
583
+ maxReconnectAttempts = 10;
584
+ reconnectDelay = 1e3;
585
+ constructor(config) {
586
+ if (!config.apiKey) {
587
+ throw new Error("API key is required");
588
+ }
589
+ if (!config.agentId) {
590
+ throw new Error("Agent ID is required");
591
+ }
592
+ this.config = {
593
+ ...config,
594
+ baseUrl: config.baseUrl || DEFAULT_BASE_URL2
595
+ };
596
+ }
597
+ /**
598
+ * Get current connection state
599
+ */
600
+ get connectionState() {
601
+ return this.state;
602
+ }
603
+ /**
604
+ * Check if connected
605
+ */
606
+ get isConnected() {
607
+ return this.state === "connected";
608
+ }
609
+ /**
610
+ * Subscribe to all events
611
+ */
612
+ subscribe(callback) {
613
+ this.allCallbacks.add(callback);
614
+ return () => {
615
+ this.allCallbacks.delete(callback);
616
+ };
617
+ }
618
+ /**
619
+ * Subscribe to a specific event type
620
+ */
621
+ on(type, callback) {
622
+ if (!this.callbacks.has(type)) {
623
+ this.callbacks.set(type, /* @__PURE__ */ new Set());
624
+ }
625
+ this.callbacks.get(type).add(callback);
626
+ return () => {
627
+ const typeCallbacks = this.callbacks.get(type);
628
+ if (typeCallbacks) {
629
+ typeCallbacks.delete(callback);
630
+ if (typeCallbacks.size === 0) {
631
+ this.callbacks.delete(type);
632
+ }
633
+ }
634
+ };
635
+ }
636
+ /**
637
+ * Connect to the SSE stream
638
+ */
639
+ connect() {
640
+ if (this.eventSource) {
641
+ return;
642
+ }
643
+ this.setState("connecting");
644
+ const url = new URL(`${this.config.baseUrl}/events/stream`);
645
+ url.searchParams.set("apiKey", this.config.apiKey);
646
+ url.searchParams.set("agentId", this.config.agentId);
647
+ this.eventSource = new EventSource(url.toString(), {
648
+ headers: {
649
+ "Authorization": `Bearer ${this.config.apiKey}`
650
+ }
651
+ });
652
+ this.eventSource.onopen = () => {
653
+ this.reconnectAttempts = 0;
654
+ this.reconnectDelay = 1e3;
655
+ this.setState("connected");
656
+ };
657
+ this.eventSource.onmessage = (event) => {
658
+ this.handleMessage(event);
659
+ };
660
+ this.eventSource.onerror = (event) => {
661
+ this.handleError(event);
662
+ };
663
+ for (const eventType of EVENT_TYPES) {
664
+ this.eventSource.addEventListener(eventType, (event) => {
665
+ this.handleMessage(event, eventType);
666
+ });
667
+ }
668
+ }
669
+ /**
670
+ * Disconnect from the SSE stream
671
+ */
672
+ disconnect() {
673
+ if (this.eventSource) {
674
+ this.eventSource.close();
675
+ this.eventSource = null;
676
+ }
677
+ this.setState("disconnected");
678
+ }
679
+ /**
680
+ * Reconnect to the SSE stream
681
+ */
682
+ reconnect() {
683
+ this.disconnect();
684
+ this.connect();
685
+ }
686
+ /**
687
+ * Handle incoming SSE message
688
+ */
689
+ handleMessage(event, eventType) {
690
+ try {
691
+ const data = JSON.parse(event.data);
692
+ const rawData = data.data || data;
693
+ const normalizedData = rawData.task_id ? {
694
+ taskId: rawData.task_id,
695
+ sectionId: rawData.section_id,
696
+ artifactId: rawData.artifact_id,
697
+ artifactTitle: rawData.artifact_title,
698
+ heading: rawData.heading,
699
+ content: rawData.content,
700
+ assignedTo: rawData.assigned_to,
701
+ assignedAt: rawData.assigned_at,
702
+ priority: rawData.priority,
703
+ ...rawData
704
+ // Keep original fields too
705
+ } : rawData;
706
+ const artyfactsEvent = {
707
+ type: eventType || data.type || "unknown",
708
+ timestamp: data.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
709
+ data: normalizedData
710
+ };
711
+ const typeCallbacks = this.callbacks.get(artyfactsEvent.type);
712
+ if (typeCallbacks) {
713
+ for (const callback of typeCallbacks) {
714
+ this.safeCallCallback(callback, artyfactsEvent);
715
+ }
716
+ }
717
+ for (const callback of this.allCallbacks) {
718
+ this.safeCallCallback(callback, artyfactsEvent);
719
+ }
720
+ } catch (err) {
721
+ console.error("[Listener] Failed to parse SSE message:", event.data, err);
722
+ }
723
+ }
724
+ /**
725
+ * Safely call a callback, handling async and errors
726
+ */
727
+ async safeCallCallback(callback, event) {
728
+ try {
729
+ await callback(event);
730
+ } catch (err) {
731
+ console.error(`[Listener] Error in event callback for '${event.type}':`, err);
732
+ }
733
+ }
734
+ /**
735
+ * Handle SSE error
736
+ */
737
+ handleError(event) {
738
+ if (this.eventSource?.readyState === EventSource.CONNECTING) {
739
+ this.setState("reconnecting");
740
+ } else if (this.eventSource?.readyState === EventSource.CLOSED) {
741
+ this.setState("disconnected");
742
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
743
+ this.reconnectAttempts++;
744
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, 3e4);
745
+ console.log(
746
+ `[Listener] Connection lost, reconnecting in ${this.reconnectDelay / 1e3}s (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`
747
+ );
748
+ setTimeout(() => {
749
+ if (this.state === "disconnected") {
750
+ this.connect();
751
+ }
752
+ }, this.reconnectDelay);
753
+ } else {
754
+ const error = new Error("Max reconnection attempts reached");
755
+ this.config.onError?.(error);
756
+ }
757
+ }
758
+ }
759
+ /**
760
+ * Update connection state
761
+ */
762
+ setState(state) {
763
+ if (this.state !== state) {
764
+ this.state = state;
765
+ this.config.onStateChange?.(state);
766
+ }
767
+ }
768
+ };
769
+ function createListener(config) {
770
+ return new ArtyfactsListener(config);
771
+ }
772
+
773
+ export {
774
+ loadCredentials,
775
+ saveCredentials,
776
+ clearCredentials,
777
+ runDeviceAuth,
778
+ promptForApiKey,
779
+ getCredentials,
780
+ ContextFetcher,
781
+ buildPromptWithContext,
782
+ createContextFetcher,
783
+ ClaudeExecutor,
784
+ createExecutor,
785
+ ArtyfactsListener,
786
+ createListener
787
+ };
package/dist/cli.js CHANGED
@@ -251,16 +251,34 @@ function buildPromptWithContext(context) {
251
251
 
252
252
  Your job is to complete the assigned task. You have full context about the organization, project, and related work.
253
253
 
254
- Guidelines:
254
+ ## Available Tools
255
+
256
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
257
+
258
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
259
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
260
+ - **update_section** - Update existing sections
261
+ - **create_agent** - Create new AI agents with specific roles
262
+ - **list_artifacts** - Query existing artifacts
263
+ - **list_sections** - Query sections within an artifact
264
+ - **complete_task** - Mark a task as complete
265
+ - **block_task** - Block a task with a reason
266
+ - **create_blocker** - Create a decision blocker
267
+
268
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it with the tools.
269
+
270
+ ## Guidelines
271
+
255
272
  - Be thorough but concise
256
- - If the task requires code, provide working code
257
- - If the task requires analysis, provide structured findings
258
- - If the task requires a decision, explain your reasoning
273
+ - USE THE TOOLS to take action, don't just analyze
274
+ - If the task requires creating something, use create_artifact or create_section
275
+ - If the task requires creating agents, use create_agent
259
276
  - If you cannot complete the task, explain why
260
277
 
261
278
  Format your response as follows:
262
- 1. First, provide your main output (the task deliverable)
263
- 2. End with a brief summary line starting with "SUMMARY:"`);
279
+ 1. First, use the tools to complete the task
280
+ 2. Then summarize what you did
281
+ 3. End with a brief summary line starting with "SUMMARY:"`);
264
282
  parts.push("");
265
283
  parts.push("---");
266
284
  parts.push("");
@@ -367,21 +385,35 @@ function createContextFetcher(config) {
367
385
  var DEFAULT_TIMEOUT = 5 * 60 * 1e3;
368
386
  var DEFAULT_SYSTEM_PROMPT = `You are an AI agent working within the Artyfacts task management system.
369
387
 
370
- Your job is to complete tasks assigned to you. For each task:
371
- 1. Understand the requirements from the task heading and content
372
- 2. Complete the task to the best of your ability
373
- 3. Provide a clear, actionable output
388
+ Your job is to complete tasks assigned to you using the available tools.
374
389
 
375
- Guidelines:
376
- - Be thorough but concise
377
- - If the task requires code, provide working code
378
- - If the task requires analysis, provide structured findings
379
- - If the task requires a decision, explain your reasoning
390
+ ## Available Tools
391
+
392
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
393
+
394
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
395
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
396
+ - **update_section** - Update existing sections
397
+ - **create_agent** - Create new AI agents with specific roles
398
+ - **list_artifacts** - Query existing artifacts
399
+ - **list_sections** - Query sections within an artifact
400
+ - **complete_task** - Mark a task as complete
401
+ - **block_task** - Block a task with a reason
402
+ - **create_blocker** - Create a decision blocker
403
+
404
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it.
405
+
406
+ ## Guidelines
407
+
408
+ - USE THE TOOLS to take action
409
+ - If creating something, use create_artifact or create_section
410
+ - If creating agents, use create_agent
380
411
  - If you cannot complete the task, explain why
381
412
 
382
413
  Format your response as follows:
383
- 1. First, provide your main output (the task deliverable)
384
- 2. End with a brief summary line starting with "SUMMARY:"`;
414
+ 1. First, use the tools to complete the task
415
+ 2. Summarize what you accomplished
416
+ 3. End with a brief summary line starting with "SUMMARY:"`;
385
417
  var ClaudeExecutor = class {
386
418
  config;
387
419
  contextFetcher = null;
@@ -897,7 +929,7 @@ async function checkAndClaimTasks(baseUrl, apiKey, agentId, activeTasks, executo
897
929
  }
898
930
  console.log(`\u{1F4EC} Found ${tasks.length} claimable task(s)`);
899
931
  for (const task of tasks) {
900
- if (activeTasks.has(task.section_id)) {
932
+ if (activeTasks.has(task.id)) {
901
933
  continue;
902
934
  }
903
935
  console.log(`
@@ -920,11 +952,11 @@ async function checkAndClaimTasks(baseUrl, apiKey, agentId, activeTasks, executo
920
952
  });
921
953
  const taskUuid = claimData.task?.id || task.id;
922
954
  console.log(` \u{1F4CE} Using task UUID: ${taskUuid} (from queue: ${task.id})`);
923
- activeTasks.add(task.section_id);
955
+ activeTasks.add(task.id);
924
956
  console.log(" \u2713 Claimed!");
925
957
  if (dryRun) {
926
958
  console.log(" \u{1F4CB} Dry run - not executing");
927
- activeTasks.delete(task.section_id);
959
+ activeTasks.delete(task.id);
928
960
  continue;
929
961
  }
930
962
  console.log(" \u2192 Executing with Claude...");
@@ -961,7 +993,7 @@ async function checkAndClaimTasks(baseUrl, apiKey, agentId, activeTasks, executo
961
993
  } catch (error) {
962
994
  console.error(` \u2192 \u274C Error:`, error instanceof Error ? error.message : error);
963
995
  } finally {
964
- activeTasks.delete(task.section_id);
996
+ activeTasks.delete(task.id);
965
997
  }
966
998
  break;
967
999
  }
package/dist/cli.mjs CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  getCredentials,
7
7
  loadCredentials,
8
8
  promptForApiKey
9
- } from "./chunk-7ULDABOE.mjs";
9
+ } from "./chunk-CNJCMX2D.mjs";
10
10
 
11
11
  // src/cli.ts
12
12
  import { Command } from "commander";
@@ -138,7 +138,7 @@ async function checkAndClaimTasks(baseUrl, apiKey, agentId, activeTasks, executo
138
138
  }
139
139
  console.log(`\u{1F4EC} Found ${tasks.length} claimable task(s)`);
140
140
  for (const task of tasks) {
141
- if (activeTasks.has(task.section_id)) {
141
+ if (activeTasks.has(task.id)) {
142
142
  continue;
143
143
  }
144
144
  console.log(`
@@ -161,11 +161,11 @@ async function checkAndClaimTasks(baseUrl, apiKey, agentId, activeTasks, executo
161
161
  });
162
162
  const taskUuid = claimData.task?.id || task.id;
163
163
  console.log(` \u{1F4CE} Using task UUID: ${taskUuid} (from queue: ${task.id})`);
164
- activeTasks.add(task.section_id);
164
+ activeTasks.add(task.id);
165
165
  console.log(" \u2713 Claimed!");
166
166
  if (dryRun) {
167
167
  console.log(" \u{1F4CB} Dry run - not executing");
168
- activeTasks.delete(task.section_id);
168
+ activeTasks.delete(task.id);
169
169
  continue;
170
170
  }
171
171
  console.log(" \u2192 Executing with Claude...");
@@ -202,7 +202,7 @@ async function checkAndClaimTasks(baseUrl, apiKey, agentId, activeTasks, executo
202
202
  } catch (error) {
203
203
  console.error(` \u2192 \u274C Error:`, error instanceof Error ? error.message : error);
204
204
  } finally {
205
- activeTasks.delete(task.section_id);
205
+ activeTasks.delete(task.id);
206
206
  }
207
207
  break;
208
208
  }
package/dist/index.js CHANGED
@@ -281,16 +281,34 @@ function buildPromptWithContext(context) {
281
281
 
282
282
  Your job is to complete the assigned task. You have full context about the organization, project, and related work.
283
283
 
284
- Guidelines:
284
+ ## Available Tools
285
+
286
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
287
+
288
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
289
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
290
+ - **update_section** - Update existing sections
291
+ - **create_agent** - Create new AI agents with specific roles
292
+ - **list_artifacts** - Query existing artifacts
293
+ - **list_sections** - Query sections within an artifact
294
+ - **complete_task** - Mark a task as complete
295
+ - **block_task** - Block a task with a reason
296
+ - **create_blocker** - Create a decision blocker
297
+
298
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it with the tools.
299
+
300
+ ## Guidelines
301
+
285
302
  - Be thorough but concise
286
- - If the task requires code, provide working code
287
- - If the task requires analysis, provide structured findings
288
- - If the task requires a decision, explain your reasoning
303
+ - USE THE TOOLS to take action, don't just analyze
304
+ - If the task requires creating something, use create_artifact or create_section
305
+ - If the task requires creating agents, use create_agent
289
306
  - If you cannot complete the task, explain why
290
307
 
291
308
  Format your response as follows:
292
- 1. First, provide your main output (the task deliverable)
293
- 2. End with a brief summary line starting with "SUMMARY:"`);
309
+ 1. First, use the tools to complete the task
310
+ 2. Then summarize what you did
311
+ 3. End with a brief summary line starting with "SUMMARY:"`);
294
312
  parts.push("");
295
313
  parts.push("---");
296
314
  parts.push("");
@@ -397,21 +415,35 @@ function createContextFetcher(config) {
397
415
  var DEFAULT_TIMEOUT = 5 * 60 * 1e3;
398
416
  var DEFAULT_SYSTEM_PROMPT = `You are an AI agent working within the Artyfacts task management system.
399
417
 
400
- Your job is to complete tasks assigned to you. For each task:
401
- 1. Understand the requirements from the task heading and content
402
- 2. Complete the task to the best of your ability
403
- 3. Provide a clear, actionable output
418
+ Your job is to complete tasks assigned to you using the available tools.
404
419
 
405
- Guidelines:
406
- - Be thorough but concise
407
- - If the task requires code, provide working code
408
- - If the task requires analysis, provide structured findings
409
- - If the task requires a decision, explain your reasoning
420
+ ## Available Tools
421
+
422
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
423
+
424
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
425
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
426
+ - **update_section** - Update existing sections
427
+ - **create_agent** - Create new AI agents with specific roles
428
+ - **list_artifacts** - Query existing artifacts
429
+ - **list_sections** - Query sections within an artifact
430
+ - **complete_task** - Mark a task as complete
431
+ - **block_task** - Block a task with a reason
432
+ - **create_blocker** - Create a decision blocker
433
+
434
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it.
435
+
436
+ ## Guidelines
437
+
438
+ - USE THE TOOLS to take action
439
+ - If creating something, use create_artifact or create_section
440
+ - If creating agents, use create_agent
410
441
  - If you cannot complete the task, explain why
411
442
 
412
443
  Format your response as follows:
413
- 1. First, provide your main output (the task deliverable)
414
- 2. End with a brief summary line starting with "SUMMARY:"`;
444
+ 1. First, use the tools to complete the task
445
+ 2. Summarize what you accomplished
446
+ 3. End with a brief summary line starting with "SUMMARY:"`;
415
447
  var ClaudeExecutor = class {
416
448
  config;
417
449
  contextFetcher = null;
package/dist/index.mjs CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  promptForApiKey,
13
13
  runDeviceAuth,
14
14
  saveCredentials
15
- } from "./chunk-7ULDABOE.mjs";
15
+ } from "./chunk-CNJCMX2D.mjs";
16
16
 
17
17
  // node_modules/@anthropic-ai/sdk/internal/tslib.mjs
18
18
  function __classPrivateFieldSet(receiver, state, value, kind, f) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artyfacts/claude",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "Claude adapter for Artyfacts - Execute tasks using Claude Code CLI",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/cli.ts CHANGED
@@ -245,7 +245,8 @@ async function checkAndClaimTasks(
245
245
 
246
246
  // Process first available task
247
247
  for (const task of tasks) {
248
- if (activeTasks.has(task.section_id)) {
248
+ // Use task.id (UUID) for deduplication - matches SSE taskId
249
+ if (activeTasks.has(task.id)) {
249
250
  continue; // Already processing
250
251
  }
251
252
 
@@ -281,12 +282,12 @@ async function checkAndClaimTasks(
281
282
  console.log(` 📎 Using task UUID: ${taskUuid} (from queue: ${task.id})`); // DEBUG
282
283
 
283
284
  // Successfully claimed - now execute
284
- activeTasks.add(task.section_id);
285
+ activeTasks.add(task.id);
285
286
  console.log(' ✓ Claimed!');
286
287
 
287
288
  if (dryRun) {
288
289
  console.log(' 📋 Dry run - not executing');
289
- activeTasks.delete(task.section_id);
290
+ activeTasks.delete(task.id);
290
291
  continue;
291
292
  }
292
293
 
@@ -324,7 +325,7 @@ async function checkAndClaimTasks(
324
325
  } catch (error) {
325
326
  console.error(` → ❌ Error:`, error instanceof Error ? error.message : error);
326
327
  } finally {
327
- activeTasks.delete(task.section_id);
328
+ activeTasks.delete(task.id);
328
329
  }
329
330
 
330
331
  // Only process one task at a time for now
package/src/context.ts CHANGED
@@ -108,16 +108,34 @@ export function buildPromptWithContext(context: TaskFullContext): string {
108
108
 
109
109
  Your job is to complete the assigned task. You have full context about the organization, project, and related work.
110
110
 
111
- Guidelines:
111
+ ## Available Tools
112
+
113
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
114
+
115
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
116
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
117
+ - **update_section** - Update existing sections
118
+ - **create_agent** - Create new AI agents with specific roles
119
+ - **list_artifacts** - Query existing artifacts
120
+ - **list_sections** - Query sections within an artifact
121
+ - **complete_task** - Mark a task as complete
122
+ - **block_task** - Block a task with a reason
123
+ - **create_blocker** - Create a decision blocker
124
+
125
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it with the tools.
126
+
127
+ ## Guidelines
128
+
112
129
  - Be thorough but concise
113
- - If the task requires code, provide working code
114
- - If the task requires analysis, provide structured findings
115
- - If the task requires a decision, explain your reasoning
130
+ - USE THE TOOLS to take action, don't just analyze
131
+ - If the task requires creating something, use create_artifact or create_section
132
+ - If the task requires creating agents, use create_agent
116
133
  - If you cannot complete the task, explain why
117
134
 
118
135
  Format your response as follows:
119
- 1. First, provide your main output (the task deliverable)
120
- 2. End with a brief summary line starting with "SUMMARY:"`);
136
+ 1. First, use the tools to complete the task
137
+ 2. Then summarize what you did
138
+ 3. End with a brief summary line starting with "SUMMARY:"`);
121
139
 
122
140
  parts.push('');
123
141
  parts.push('---');
package/src/executor.ts CHANGED
@@ -68,21 +68,35 @@ const DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes
68
68
 
69
69
  const DEFAULT_SYSTEM_PROMPT = `You are an AI agent working within the Artyfacts task management system.
70
70
 
71
- Your job is to complete tasks assigned to you. For each task:
72
- 1. Understand the requirements from the task heading and content
73
- 2. Complete the task to the best of your ability
74
- 3. Provide a clear, actionable output
75
-
76
- Guidelines:
77
- - Be thorough but concise
78
- - If the task requires code, provide working code
79
- - If the task requires analysis, provide structured findings
80
- - If the task requires a decision, explain your reasoning
71
+ Your job is to complete tasks assigned to you using the available tools.
72
+
73
+ ## Available Tools
74
+
75
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
76
+
77
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
78
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
79
+ - **update_section** - Update existing sections
80
+ - **create_agent** - Create new AI agents with specific roles
81
+ - **list_artifacts** - Query existing artifacts
82
+ - **list_sections** - Query sections within an artifact
83
+ - **complete_task** - Mark a task as complete
84
+ - **block_task** - Block a task with a reason
85
+ - **create_blocker** - Create a decision blocker
86
+
87
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it.
88
+
89
+ ## Guidelines
90
+
91
+ - USE THE TOOLS to take action
92
+ - If creating something, use create_artifact or create_section
93
+ - If creating agents, use create_agent
81
94
  - If you cannot complete the task, explain why
82
95
 
83
96
  Format your response as follows:
84
- 1. First, provide your main output (the task deliverable)
85
- 2. End with a brief summary line starting with "SUMMARY:"`;
97
+ 1. First, use the tools to complete the task
98
+ 2. Summarize what you accomplished
99
+ 3. End with a brief summary line starting with "SUMMARY:"`;
86
100
 
87
101
  // ============================================================================
88
102
  // Executor Class