@artyfacts/claude 1.3.30 → 1.3.31

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,1104 @@
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
+ - **list_agents** - List all agents in the organization (call this FIRST when creating tasks)
227
+ - **create_task** - Create a new task under a goal (requires goal_id, should include assigned_to)
228
+ - **create_goal** - Create a new goal (top-level objective)
229
+ - **create_artifact** - Create a new artifact (document output \u2014 use for any written content, specs, reports, copy, frameworks, research)
230
+ - **create_section** - Add a chapter/section to an artifact (use to add the actual content body)
231
+ - **claim_task** - Claim a task for execution
232
+ - **complete_task** - Mark a task as complete (returns unblocked tasks)
233
+ - **block_task** - Block a task with a reason
234
+ - **list_tasks** - Query tasks from the queue
235
+ - **list_inbox** - Check pending decisions/approvals
236
+ - **resolve_inbox** - Resolve an inbox item
237
+
238
+ ## When to create an artifact
239
+
240
+ If the task involves producing ANY written content \u2014 copy, documentation, a framework, a spec, a report, research, a plan, a strategy, or any other document \u2014 you MUST:
241
+ 1. Call **create_artifact** to create the document (use an appropriate type: 'doc', 'spec', 'report', 'research', 'runbook')
242
+ 2. Call **create_section** one or more times to write the actual content into the artifact
243
+ 3. When calling **complete_task**, pass the artifact's ID as 'output_artifact_id' so it is linked to this task
244
+
245
+ Do NOT just summarize what you did in the completion note. The artifact IS the deliverable.
246
+
247
+ ## Task type decision guide
248
+
249
+ - Task asks to **write, draft, create, document, design, research, analyze, plan** \u2192 create an artifact with sections containing the full content
250
+ - Task asks to **generate tasks, break down, plan work** \u2192 call list_agents then create_task for each subtask (do NOT create an artifact)
251
+ - Task asks to **validate, test, verify** \u2192 complete the work directly, report findings in the completion note
252
+
253
+ ## Other rules
254
+
255
+ - Tasks and goals are SEPARATE from artifacts. Use **create_task** to create tasks, **create_goal** to create goals.
256
+ - When asked to generate tasks for a goal, FIRST call **list_agents** to see available agents, then use **create_task** with the goal's ID and assign each task to the most appropriate agent based on their role.
257
+ - Always assign tasks to an agent using the agent's UUID in the **assigned_to** field. Match tasks to agents by their role description.
258
+ - **CRITICAL: Only use 'create_task' when the task explicitly asks you to generate, plan, or decompose work into subtasks.** Do NOT create new tasks as a way to demonstrate, test, or validate your work \u2014 that causes infinite loops.
259
+ - USE THE TOOLS to take action \u2014 don't just describe what you would do.
260
+
261
+ Format your response as follows:
262
+ 1. First, use the tools to complete the task
263
+ 2. Then summarize what you did
264
+ 3. End with a brief summary line starting with "SUMMARY:"`);
265
+ parts.push("");
266
+ parts.push("---");
267
+ parts.push("");
268
+ parts.push("## Organization Context");
269
+ parts.push(`**${context.organization.name}**`);
270
+ if (context.organization.context) {
271
+ parts.push("");
272
+ parts.push(formatOrgContext(context.organization.context));
273
+ }
274
+ parts.push("");
275
+ if (context.project) {
276
+ parts.push(`## Project: ${context.project.name}`);
277
+ if (context.project.description) {
278
+ parts.push(context.project.description);
279
+ }
280
+ parts.push("");
281
+ }
282
+ const goal = context.goal || context.artifact;
283
+ if (goal) {
284
+ parts.push(`## Goal: ${goal.title}`);
285
+ parts.push(`**Goal ID:** ${goal.id}`);
286
+ if (goal.objective) {
287
+ parts.push(`**Objective:** ${goal.objective}`);
288
+ }
289
+ if (goal.summary) {
290
+ parts.push(goal.summary);
291
+ }
292
+ if (goal.description) {
293
+ parts.push("");
294
+ parts.push(goal.description);
295
+ }
296
+ parts.push("");
297
+ if (goal.tasks && goal.tasks.length > 0) {
298
+ const relatedTasks = goal.tasks.filter((t) => t.id !== context.task.id);
299
+ if (relatedTasks.length > 0) {
300
+ parts.push("### Related Tasks:");
301
+ for (const task of relatedTasks) {
302
+ const statusEmoji = {
303
+ pending: "\u23F3",
304
+ in_progress: "\u{1F504}",
305
+ blocked: "\u{1F6AB}",
306
+ done: "\u2705"
307
+ }[task.status] || "\u2753";
308
+ const priorityBadge = task.priority ? ` [${task.priority}]` : "";
309
+ parts.push(`- ${statusEmoji} **${task.title}**${priorityBadge}`);
310
+ }
311
+ parts.push("");
312
+ }
313
+ }
314
+ if (goal.sections && goal.sections.length > 0) {
315
+ const relatedSections = goal.sections.filter((s) => s.id !== context.task.id);
316
+ if (relatedSections.length > 0) {
317
+ parts.push("### Related Sections:");
318
+ for (const section of relatedSections) {
319
+ const preview = section.content ? section.content.substring(0, 200) + (section.content.length > 200 ? "..." : "") : "No content";
320
+ const statusBadge = section.task_status ? ` [${section.task_status}]` : "";
321
+ parts.push(`- **${section.heading}**${statusBadge}: ${preview}`);
322
+ }
323
+ parts.push("");
324
+ }
325
+ }
326
+ }
327
+ parts.push("---");
328
+ parts.push("");
329
+ const taskTitle = context.task.title || context.task.heading;
330
+ parts.push(`## Your Task: ${taskTitle}`);
331
+ if (context.task.priority) {
332
+ const priorityEmoji = {
333
+ high: "\u{1F534} High",
334
+ medium: "\u{1F7E1} Medium",
335
+ low: "\u{1F7E2} Low"
336
+ }[context.task.priority] || "\u{1F7E1} Medium";
337
+ parts.push(`**Priority:** ${priorityEmoji}`);
338
+ }
339
+ if (context.task.depends_on && context.task.depends_on.length > 0) {
340
+ parts.push(`**Dependencies:** ${context.task.depends_on.length} task(s)`);
341
+ }
342
+ parts.push("");
343
+ parts.push("### Description");
344
+ const taskDescription = context.task.description || context.task.content;
345
+ parts.push(taskDescription || "No additional description provided.");
346
+ parts.push("");
347
+ if (context.task.expected_output) {
348
+ parts.push("### Expected Output");
349
+ if (context.task.expected_output.format) {
350
+ parts.push(`**Format:** ${context.task.expected_output.format}`);
351
+ }
352
+ if (context.task.expected_output.requirements && context.task.expected_output.requirements.length > 0) {
353
+ parts.push("**Requirements:**");
354
+ for (const req of context.task.expected_output.requirements) {
355
+ parts.push(`- ${req}`);
356
+ }
357
+ }
358
+ parts.push("");
359
+ }
360
+ parts.push("---");
361
+ parts.push("");
362
+ parts.push("Complete this task and provide your output below.");
363
+ return parts.join("\n");
364
+ }
365
+ function formatOrgContext(context) {
366
+ const trimmed = context.trim();
367
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
368
+ try {
369
+ const parsed = JSON.parse(trimmed);
370
+ return formatContextObject(parsed);
371
+ } catch {
372
+ return context;
373
+ }
374
+ }
375
+ return context;
376
+ }
377
+ function formatContextObject(obj, indent = "") {
378
+ if (typeof obj !== "object" || obj === null) {
379
+ return String(obj);
380
+ }
381
+ if (Array.isArray(obj)) {
382
+ return obj.map((item) => `${indent}- ${formatContextObject(item, indent + " ")}`).join("\n");
383
+ }
384
+ const lines = [];
385
+ for (const [key, value] of Object.entries(obj)) {
386
+ const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
387
+ if (typeof value === "object" && value !== null) {
388
+ lines.push(`${indent}**${label}:**`);
389
+ lines.push(formatContextObject(value, indent + " "));
390
+ } else {
391
+ lines.push(`${indent}- **${label}:** ${value}`);
392
+ }
393
+ }
394
+ return lines.join("\n");
395
+ }
396
+ function createContextFetcher(config) {
397
+ return new ContextFetcher(config);
398
+ }
399
+
400
+ // src/executor.ts
401
+ import { spawn } from "child_process";
402
+ var DEFAULT_TIMEOUT = 5 * 60 * 1e3;
403
+ var DEFAULT_SYSTEM_PROMPT = `You are an AI agent working within the Artyfacts task management system.
404
+
405
+ Your job is to complete tasks assigned to you using the available tools.
406
+
407
+ ## Available Tools
408
+
409
+ You have access to Artyfacts MCP tools. USE THEM to complete your task:
410
+
411
+ - **create_artifact** - Create new artifacts (documents, specs, reports)
412
+ - **create_section** - Add sections to artifacts (content, tasks, decisions)
413
+ - **update_section** - Update existing sections
414
+ - **create_agent** - Create new AI agents with specific roles
415
+ - **list_artifacts** - Query existing artifacts
416
+ - **list_sections** - Query sections within an artifact
417
+ - **complete_task** - Mark a task as complete
418
+ - **block_task** - Block a task with a reason
419
+ - **create_blocker** - Create a decision blocker
420
+
421
+ IMPORTANT: When asked to create agents or update artifacts, USE THE TOOLS. Don't just describe what you would do - actually do it.
422
+
423
+ ## Guidelines
424
+
425
+ - USE THE TOOLS to take action
426
+ - If creating something, use create_artifact or create_section
427
+ - If creating agents, use create_agent
428
+ - If you cannot complete the task, explain why
429
+
430
+ Format your response as follows:
431
+ 1. First, use the tools to complete the task
432
+ 2. Summarize what you accomplished
433
+ 3. End with a brief summary line starting with "SUMMARY:"`;
434
+ var ClaudeExecutor = class {
435
+ config;
436
+ contextFetcher = null;
437
+ constructor(config = {}) {
438
+ this.config = {
439
+ ...config,
440
+ timeout: config.timeout || DEFAULT_TIMEOUT,
441
+ claudePath: config.claudePath || "claude"
442
+ };
443
+ if (config.baseUrl && config.apiKey) {
444
+ this.contextFetcher = createContextFetcher({
445
+ baseUrl: config.baseUrl,
446
+ apiKey: config.apiKey
447
+ });
448
+ }
449
+ }
450
+ /**
451
+ * Execute a task using Claude Code CLI
452
+ *
453
+ * If full context is available (baseUrl + apiKey configured), fetches
454
+ * organization, project, artifact, and related sections for a rich prompt.
455
+ */
456
+ async execute(task) {
457
+ try {
458
+ let prompt;
459
+ let fullContext = null;
460
+ const useFullContext = this.config.useFullContext !== false && this.contextFetcher;
461
+ if (useFullContext) {
462
+ try {
463
+ const taskId = task.id || task.taskId;
464
+ if (!taskId) {
465
+ throw new Error("Task ID required for context fetch");
466
+ }
467
+ fullContext = await this.contextFetcher.fetchTaskContext(taskId);
468
+ prompt = buildPromptWithContext(fullContext);
469
+ console.log(" \u{1F4DA} Using full context (org, project, goal, related tasks)");
470
+ } catch (contextError) {
471
+ console.warn(" \u26A0\uFE0F Could not fetch full context, using minimal prompt");
472
+ console.warn(` ${contextError instanceof Error ? contextError.message : contextError}`);
473
+ prompt = this.buildTaskPrompt(task);
474
+ }
475
+ } else {
476
+ prompt = this.buildTaskPrompt(task);
477
+ }
478
+ const output = await this.runClaude(prompt);
479
+ const taskTitle = task.title || task.heading || "Task";
480
+ const { content, summary } = this.parseResponse(output, taskTitle);
481
+ return {
482
+ success: true,
483
+ output: content,
484
+ summary,
485
+ promptUsed: prompt
486
+ };
487
+ } catch (error) {
488
+ const errorMessage = error instanceof Error ? error.message : String(error);
489
+ return {
490
+ success: false,
491
+ output: "",
492
+ summary: `Failed: ${errorMessage}`,
493
+ error: errorMessage
494
+ };
495
+ }
496
+ }
497
+ /**
498
+ * Run Claude Code CLI with the given prompt
499
+ */
500
+ runClaude(prompt) {
501
+ return new Promise((resolve, reject) => {
502
+ const claudePath = this.config.claudePath || "claude";
503
+ const mcpConfig = {
504
+ mcpServers: {
505
+ artyfacts: {
506
+ command: "npx",
507
+ args: ["-y", "@artyfacts/mcp-server"],
508
+ env: {
509
+ ARTYFACTS_API_KEY: this.config.apiKey || process.env.ARTYFACTS_API_KEY || "",
510
+ ARTYFACTS_BASE_URL: this.config.baseUrl || "https://artyfacts.dev/api/v1"
511
+ }
512
+ }
513
+ }
514
+ };
515
+ const args = [
516
+ "--print",
517
+ "--mcp-config",
518
+ JSON.stringify(mcpConfig),
519
+ "--permission-mode",
520
+ "bypassPermissions"
521
+ ];
522
+ const proc = spawn(claudePath, args, {
523
+ stdio: ["pipe", "pipe", "pipe"],
524
+ timeout: this.config.timeout
525
+ });
526
+ let stdout = "";
527
+ let stderr = "";
528
+ proc.stdout.on("data", (data) => {
529
+ stdout += data.toString();
530
+ });
531
+ proc.stderr.on("data", (data) => {
532
+ stderr += data.toString();
533
+ });
534
+ proc.on("close", (code) => {
535
+ if (code === 0) {
536
+ resolve(stdout.trim());
537
+ } else {
538
+ reject(new Error(stderr || `Claude exited with code ${code}`));
539
+ }
540
+ });
541
+ proc.on("error", (err) => {
542
+ if (err.code === "ENOENT") {
543
+ reject(new Error(
544
+ "Claude Code CLI not found. Please install it:\n npm install -g @anthropic-ai/claude-code"
545
+ ));
546
+ } else {
547
+ reject(err);
548
+ }
549
+ });
550
+ proc.stdin.write(prompt);
551
+ proc.stdin.end();
552
+ });
553
+ }
554
+ /**
555
+ * Build the task prompt (v2)
556
+ */
557
+ buildTaskPrompt(task) {
558
+ const parts = [];
559
+ const systemPrompt = this.config.systemPromptPrefix ? `${this.config.systemPromptPrefix}
560
+
561
+ ${DEFAULT_SYSTEM_PROMPT}` : DEFAULT_SYSTEM_PROMPT;
562
+ parts.push(systemPrompt);
563
+ parts.push("");
564
+ parts.push("---");
565
+ parts.push("");
566
+ const taskTitle = task.title || task.heading;
567
+ parts.push(`# Task: ${taskTitle}`);
568
+ parts.push("");
569
+ const goalTitle = task.goalTitle || task.artifactTitle;
570
+ const goalId = task.goalId || task.artifactId;
571
+ if (goalTitle) {
572
+ parts.push(`**Goal:** ${goalTitle}`);
573
+ }
574
+ if (goalId) {
575
+ parts.push(`**Goal ID:** ${goalId}`);
576
+ }
577
+ if (task.priority) {
578
+ const priorityEmoji = {
579
+ high: "\u{1F534} High",
580
+ medium: "\u{1F7E1} Medium",
581
+ low: "\u{1F7E2} Low"
582
+ }[task.priority] || "\u{1F7E1} Medium";
583
+ parts.push(`**Priority:** ${priorityEmoji}`);
584
+ }
585
+ parts.push("");
586
+ parts.push("## Description");
587
+ const taskDescription = task.description || task.content;
588
+ parts.push(taskDescription || "No additional description provided.");
589
+ parts.push("");
590
+ if (task.context && Object.keys(task.context).length > 0) {
591
+ parts.push("## Additional Context");
592
+ parts.push("```json");
593
+ parts.push(JSON.stringify(task.context, null, 2));
594
+ parts.push("```");
595
+ parts.push("");
596
+ }
597
+ parts.push("## Instructions");
598
+ parts.push("Complete this task and provide your output below.");
599
+ return parts.join("\n");
600
+ }
601
+ /**
602
+ * Parse the response to extract output and summary
603
+ */
604
+ parseResponse(fullOutput, taskHeading) {
605
+ const summaryMatch = fullOutput.match(/SUMMARY:\s*(.+?)(?:\n|$)/i);
606
+ if (summaryMatch) {
607
+ const summary2 = summaryMatch[1].trim();
608
+ const content = fullOutput.replace(/SUMMARY:\s*.+?(?:\n|$)/i, "").trim();
609
+ return { content, summary: summary2 };
610
+ }
611
+ const lines = fullOutput.split("\n").filter((l) => l.trim());
612
+ const firstLine = lines[0] || "";
613
+ const summary = firstLine.length > 100 ? `${firstLine.substring(0, 97)}...` : firstLine || `Completed: ${taskHeading}`;
614
+ return { content: fullOutput, summary };
615
+ }
616
+ /**
617
+ * Test that Claude Code CLI is available and working
618
+ */
619
+ async testConnection() {
620
+ try {
621
+ const output = await this.runClaude('Say "connected" and nothing else.');
622
+ return output.toLowerCase().includes("connected");
623
+ } catch {
624
+ return false;
625
+ }
626
+ }
627
+ /**
628
+ * Check if Claude Code CLI is installed
629
+ */
630
+ async isInstalled() {
631
+ return new Promise((resolve) => {
632
+ const proc = spawn(this.config.claudePath || "claude", ["--version"], {
633
+ stdio: ["ignore", "pipe", "pipe"]
634
+ });
635
+ proc.on("close", (code) => {
636
+ resolve(code === 0);
637
+ });
638
+ proc.on("error", () => {
639
+ resolve(false);
640
+ });
641
+ });
642
+ }
643
+ };
644
+ function createExecutor(config) {
645
+ return new ClaudeExecutor(config);
646
+ }
647
+
648
+ // src/listener.ts
649
+ import EventSource from "eventsource";
650
+ var DEFAULT_BASE_URL2 = "https://artyfacts.dev/api/v1";
651
+ var EVENT_TYPES = [
652
+ "connected",
653
+ "heartbeat",
654
+ "task_assigned",
655
+ "task_unblocked",
656
+ "blocker_resolved",
657
+ "notification",
658
+ "mcp_connect_request",
659
+ "connection_status"
660
+ ];
661
+ var ArtyfactsListener = class {
662
+ config;
663
+ eventSource = null;
664
+ callbacks = /* @__PURE__ */ new Map();
665
+ allCallbacks = /* @__PURE__ */ new Set();
666
+ state = "disconnected";
667
+ reconnectAttempts = 0;
668
+ maxReconnectAttempts = 10;
669
+ reconnectDelay = 1e3;
670
+ constructor(config) {
671
+ if (!config.apiKey) {
672
+ throw new Error("API key is required");
673
+ }
674
+ if (!config.agentId) {
675
+ throw new Error("Agent ID is required");
676
+ }
677
+ this.config = {
678
+ ...config,
679
+ baseUrl: config.baseUrl || DEFAULT_BASE_URL2
680
+ };
681
+ }
682
+ /**
683
+ * Get current connection state
684
+ */
685
+ get connectionState() {
686
+ return this.state;
687
+ }
688
+ /**
689
+ * Check if connected
690
+ */
691
+ get isConnected() {
692
+ return this.state === "connected";
693
+ }
694
+ /**
695
+ * Subscribe to all events
696
+ */
697
+ subscribe(callback) {
698
+ this.allCallbacks.add(callback);
699
+ return () => {
700
+ this.allCallbacks.delete(callback);
701
+ };
702
+ }
703
+ /**
704
+ * Subscribe to a specific event type
705
+ */
706
+ on(type, callback) {
707
+ if (!this.callbacks.has(type)) {
708
+ this.callbacks.set(type, /* @__PURE__ */ new Set());
709
+ }
710
+ this.callbacks.get(type).add(callback);
711
+ return () => {
712
+ const typeCallbacks = this.callbacks.get(type);
713
+ if (typeCallbacks) {
714
+ typeCallbacks.delete(callback);
715
+ if (typeCallbacks.size === 0) {
716
+ this.callbacks.delete(type);
717
+ }
718
+ }
719
+ };
720
+ }
721
+ /**
722
+ * Connect to the SSE stream
723
+ */
724
+ connect() {
725
+ if (this.eventSource) {
726
+ return;
727
+ }
728
+ this.setState("connecting");
729
+ const url = new URL(`${this.config.baseUrl}/events/stream`);
730
+ url.searchParams.set("apiKey", this.config.apiKey);
731
+ url.searchParams.set("agentId", this.config.agentId);
732
+ this.eventSource = new EventSource(url.toString(), {
733
+ headers: {
734
+ "Authorization": `Bearer ${this.config.apiKey}`
735
+ }
736
+ });
737
+ this.eventSource.onopen = () => {
738
+ this.reconnectAttempts = 0;
739
+ this.reconnectDelay = 1e3;
740
+ this.setState("connected");
741
+ };
742
+ this.eventSource.onmessage = (event) => {
743
+ this.handleMessage(event);
744
+ };
745
+ this.eventSource.onerror = (event) => {
746
+ this.handleError(event);
747
+ };
748
+ for (const eventType of EVENT_TYPES) {
749
+ this.eventSource.addEventListener(eventType, (event) => {
750
+ this.handleMessage(event, eventType);
751
+ });
752
+ }
753
+ }
754
+ /**
755
+ * Disconnect from the SSE stream
756
+ */
757
+ disconnect() {
758
+ if (this.eventSource) {
759
+ this.eventSource.close();
760
+ this.eventSource = null;
761
+ }
762
+ this.setState("disconnected");
763
+ }
764
+ /**
765
+ * Reconnect to the SSE stream
766
+ */
767
+ reconnect() {
768
+ this.disconnect();
769
+ this.connect();
770
+ }
771
+ /**
772
+ * Handle incoming SSE message
773
+ */
774
+ handleMessage(event, eventType) {
775
+ try {
776
+ const data = JSON.parse(event.data);
777
+ const rawData = data.data || data;
778
+ const normalizedData = rawData.id || rawData.task_id ? {
779
+ // v2 fields (primary)
780
+ id: rawData.id || rawData.task_id,
781
+ goalId: rawData.goal_id || rawData.artifact_id,
782
+ goalTitle: rawData.goal_title || rawData.artifact_title,
783
+ title: rawData.title || rawData.heading,
784
+ description: rawData.description || rawData.content,
785
+ priority: rawData.priority,
786
+ assignedTo: rawData.assigned_to,
787
+ assignedAt: rawData.assigned_at,
788
+ // Deprecated fields for backwards compatibility
789
+ taskId: rawData.id || rawData.task_id,
790
+ artifactId: rawData.goal_id || rawData.artifact_id,
791
+ artifactTitle: rawData.goal_title || rawData.artifact_title,
792
+ heading: rawData.title || rawData.heading,
793
+ content: rawData.description || rawData.content,
794
+ ...rawData
795
+ // Keep original fields too
796
+ } : rawData;
797
+ const artyfactsEvent = {
798
+ type: eventType || data.type || "unknown",
799
+ timestamp: data.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
800
+ data: normalizedData
801
+ };
802
+ const typeCallbacks = this.callbacks.get(artyfactsEvent.type);
803
+ if (typeCallbacks) {
804
+ for (const callback of typeCallbacks) {
805
+ this.safeCallCallback(callback, artyfactsEvent);
806
+ }
807
+ }
808
+ for (const callback of this.allCallbacks) {
809
+ this.safeCallCallback(callback, artyfactsEvent);
810
+ }
811
+ } catch (err) {
812
+ console.error("[Listener] Failed to parse SSE message:", event.data, err);
813
+ }
814
+ }
815
+ /**
816
+ * Safely call a callback, handling async and errors
817
+ */
818
+ async safeCallCallback(callback, event) {
819
+ try {
820
+ await callback(event);
821
+ } catch (err) {
822
+ console.error(`[Listener] Error in event callback for '${event.type}':`, err);
823
+ }
824
+ }
825
+ /**
826
+ * Handle SSE error
827
+ */
828
+ handleError(event) {
829
+ if (this.eventSource?.readyState === EventSource.CONNECTING) {
830
+ this.setState("reconnecting");
831
+ } else if (this.eventSource?.readyState === EventSource.CLOSED) {
832
+ this.setState("disconnected");
833
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
834
+ this.reconnectAttempts++;
835
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, 3e4);
836
+ console.log(
837
+ `[Listener] Connection lost, reconnecting in ${this.reconnectDelay / 1e3}s (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`
838
+ );
839
+ setTimeout(() => {
840
+ if (this.state === "disconnected") {
841
+ this.connect();
842
+ }
843
+ }, this.reconnectDelay);
844
+ } else {
845
+ const error = new Error("Max reconnection attempts reached");
846
+ this.config.onError?.(error);
847
+ }
848
+ }
849
+ }
850
+ /**
851
+ * Update connection state
852
+ */
853
+ setState(state) {
854
+ if (this.state !== state) {
855
+ this.state = state;
856
+ this.config.onStateChange?.(state);
857
+ }
858
+ }
859
+ };
860
+ function createListener(config) {
861
+ return new ArtyfactsListener(config);
862
+ }
863
+
864
+ // src/mcp.ts
865
+ import { spawnSync } from "child_process";
866
+ var DEFAULT_BASE_URL3 = "https://artyfacts.dev/api/v1";
867
+ var OAUTH_MCP_SERVERS = {
868
+ supabase: {
869
+ url: "https://mcp.supabase.com/mcp",
870
+ name: "supabase"
871
+ },
872
+ figma: {
873
+ url: "https://mcp.figma.com/mcp",
874
+ name: "figma"
875
+ }
876
+ };
877
+ var CREDENTIAL_MCP_CONFIGS = {
878
+ postgres: (config) => ({
879
+ command: "npx",
880
+ args: ["-y", "@modelcontextprotocol/server-postgres", config.connection_string]
881
+ }),
882
+ github: (config) => ({
883
+ command: "npx",
884
+ args: ["-y", "@modelcontextprotocol/server-github"],
885
+ env: {
886
+ GITHUB_PERSONAL_ACCESS_TOKEN: config.api_key
887
+ }
888
+ }),
889
+ filesystem: (config) => ({
890
+ command: "npx",
891
+ args: ["-y", "@modelcontextprotocol/server-filesystem", ...config.paths || []]
892
+ })
893
+ };
894
+ function addMcpServer(options) {
895
+ const { name, transport, url, command, args = [], env = {}, scope = "user" } = options;
896
+ const cliArgs = ["mcp", "add", "-s", scope, "-t", transport];
897
+ for (const [key, value] of Object.entries(env)) {
898
+ cliArgs.push("-e", `${key}=${value}`);
899
+ }
900
+ cliArgs.push(name);
901
+ if (transport === "http" && url) {
902
+ cliArgs.push(url);
903
+ } else if (transport === "stdio" && command) {
904
+ cliArgs.push("--", command, ...args);
905
+ } else {
906
+ return { success: false, error: "Invalid configuration: missing url or command" };
907
+ }
908
+ console.log(`[MCP] Running: claude ${cliArgs.join(" ")}`);
909
+ const result = spawnSync("claude", cliArgs, {
910
+ encoding: "utf-8",
911
+ stdio: ["pipe", "pipe", "pipe"]
912
+ });
913
+ if (result.status === 0) {
914
+ return { success: true };
915
+ } else {
916
+ const error = result.stderr || result.stdout || `Exit code ${result.status}`;
917
+ if (error.includes("already exists")) {
918
+ console.log(`[MCP] Server ${name} exists - removing old config...`);
919
+ spawnSync("claude", ["mcp", "remove", name], { encoding: "utf-8" });
920
+ const retryResult = spawnSync("claude", cliArgs, {
921
+ encoding: "utf-8",
922
+ stdio: ["pipe", "pipe", "pipe"]
923
+ });
924
+ if (retryResult.status === 0) {
925
+ return { success: true, replaced: true };
926
+ } else {
927
+ return { success: false, error: retryResult.stderr || retryResult.stdout || "Failed after removing old config" };
928
+ }
929
+ }
930
+ return { success: false, error };
931
+ }
932
+ }
933
+ var McpHandler = class {
934
+ config;
935
+ constructor(config) {
936
+ this.config = {
937
+ ...config,
938
+ baseUrl: config.baseUrl || DEFAULT_BASE_URL3
939
+ };
940
+ }
941
+ /**
942
+ * Handle an MCP connect request event
943
+ * Uses `claude mcp add` to configure the server dynamically
944
+ */
945
+ async handleConnectRequest(event) {
946
+ const { connection_id, platform, config: mcpConfig } = event.data;
947
+ try {
948
+ const serverName = mcpConfig?.server_name || platform;
949
+ const oauthServer = OAUTH_MCP_SERVERS[platform];
950
+ if (oauthServer) {
951
+ const result = addMcpServer({
952
+ name: serverName,
953
+ transport: "http",
954
+ url: oauthServer.url
955
+ });
956
+ if (!result.success) {
957
+ throw new Error(`Failed to add ${platform} MCP: ${result.error}`);
958
+ }
959
+ console.log(`[MCP] Added ${serverName} \u2192 ${oauthServer.url}`);
960
+ console.log(`[MCP] Starting ${platform} OAuth...`);
961
+ console.log(`[MCP] Claude will open - authenticate in browser, then type "done" to continue`);
962
+ console.log("");
963
+ const oauthProcess = spawnSync("claude", [
964
+ "--allowedTools",
965
+ `mcp__${serverName}__*`,
966
+ "--permission-mode",
967
+ "bypassPermissions",
968
+ `Authenticate with ${serverName} MCP now. Call the authenticate tool immediately.`
969
+ ], {
970
+ encoding: "utf-8",
971
+ stdio: "inherit",
972
+ // Interactive - user can see and respond
973
+ timeout: 3e5
974
+ // 5 minute timeout for OAuth
975
+ });
976
+ if (oauthProcess.status === 0) {
977
+ console.log(`[MCP] ${platform} OAuth completed!`);
978
+ } else {
979
+ console.log(`[MCP] OAuth session ended`);
980
+ }
981
+ } else {
982
+ const configBuilder = CREDENTIAL_MCP_CONFIGS[platform];
983
+ if (!configBuilder) {
984
+ throw new Error(`Unsupported MCP platform: ${platform}. Supported: ${[...Object.keys(OAUTH_MCP_SERVERS), ...Object.keys(CREDENTIAL_MCP_CONFIGS)].join(", ")}`);
985
+ }
986
+ if (!mcpConfig) {
987
+ throw new Error(`Platform ${platform} requires configuration (connection_string or api_key)`);
988
+ }
989
+ const serverConfig = configBuilder(mcpConfig);
990
+ const result = addMcpServer({
991
+ name: serverName,
992
+ transport: "stdio",
993
+ command: serverConfig.command,
994
+ args: serverConfig.args,
995
+ env: serverConfig.env
996
+ });
997
+ if (!result.success) {
998
+ throw new Error(`Failed to add ${platform} MCP: ${result.error}`);
999
+ }
1000
+ console.log(`[MCP] Added ${serverName} for ${platform}`);
1001
+ }
1002
+ await this.updateConnectionStatus(connection_id, {
1003
+ status: "active",
1004
+ mcp_configured: true
1005
+ });
1006
+ this.config.onConfigured?.(connection_id, platform);
1007
+ } catch (err) {
1008
+ console.error(`[MCP] Failed to configure ${platform}:`, err);
1009
+ await this.updateConnectionStatus(connection_id, {
1010
+ status: "error",
1011
+ mcp_configured: false,
1012
+ error_message: err.message
1013
+ });
1014
+ this.config.onError?.(err, connection_id);
1015
+ }
1016
+ }
1017
+ /**
1018
+ * Check if a platform supports OAuth (no credentials needed)
1019
+ */
1020
+ static supportsOAuth(platform) {
1021
+ return platform in OAUTH_MCP_SERVERS;
1022
+ }
1023
+ /**
1024
+ * Get list of platforms with OAuth support
1025
+ */
1026
+ static getOAuthPlatforms() {
1027
+ return Object.keys(OAUTH_MCP_SERVERS);
1028
+ }
1029
+ /**
1030
+ * Update connection status in Artyfacts
1031
+ */
1032
+ async updateConnectionStatus(connectionId, update) {
1033
+ const url = `${this.config.baseUrl}/connections/${connectionId}`;
1034
+ console.log(`[MCP] Updating connection ${connectionId} to status: ${update.status}`);
1035
+ try {
1036
+ const response = await fetch(url, {
1037
+ method: "PATCH",
1038
+ headers: {
1039
+ "Authorization": `Bearer ${this.config.apiKey}`,
1040
+ "Content-Type": "application/json"
1041
+ },
1042
+ body: JSON.stringify(update)
1043
+ });
1044
+ if (!response.ok) {
1045
+ const body = await response.text();
1046
+ console.error(`[MCP] Failed to update connection status: ${response.status} - ${body}`);
1047
+ } else {
1048
+ console.log(`[MCP] Connection ${connectionId} updated to ${update.status}`);
1049
+ }
1050
+ } catch (err) {
1051
+ console.error("[MCP] Failed to update connection status:", err);
1052
+ }
1053
+ }
1054
+ /**
1055
+ * List configured MCP servers using `claude mcp list`
1056
+ */
1057
+ listServers() {
1058
+ const result = spawnSync("claude", ["mcp", "list"], {
1059
+ encoding: "utf-8",
1060
+ stdio: ["pipe", "pipe", "pipe"]
1061
+ });
1062
+ if (result.status === 0 && result.stdout) {
1063
+ return result.stdout.trim().split("\n").filter(Boolean);
1064
+ }
1065
+ return [];
1066
+ }
1067
+ /**
1068
+ * Remove an MCP server using `claude mcp remove`
1069
+ */
1070
+ removeServer(serverName) {
1071
+ const result = spawnSync("claude", ["mcp", "remove", serverName], {
1072
+ encoding: "utf-8",
1073
+ stdio: ["pipe", "pipe", "pipe"]
1074
+ });
1075
+ if (result.status === 0) {
1076
+ console.log(`[MCP] Removed server: ${serverName}`);
1077
+ return true;
1078
+ } else {
1079
+ console.error(`[MCP] Failed to remove ${serverName}: ${result.stderr || result.stdout}`);
1080
+ return false;
1081
+ }
1082
+ }
1083
+ };
1084
+ function createMcpHandler(config) {
1085
+ return new McpHandler(config);
1086
+ }
1087
+
1088
+ export {
1089
+ loadCredentials,
1090
+ saveCredentials,
1091
+ clearCredentials,
1092
+ runDeviceAuth,
1093
+ promptForApiKey,
1094
+ getCredentials,
1095
+ ContextFetcher,
1096
+ buildPromptWithContext,
1097
+ createContextFetcher,
1098
+ ClaudeExecutor,
1099
+ createExecutor,
1100
+ ArtyfactsListener,
1101
+ createListener,
1102
+ McpHandler,
1103
+ createMcpHandler
1104
+ };
package/dist/cli.js CHANGED
@@ -258,8 +258,8 @@ You have access to Artyfacts MCP tools. USE THEM to complete your task:
258
258
  - **list_agents** - List all agents in the organization (call this FIRST when creating tasks)
259
259
  - **create_task** - Create a new task under a goal (requires goal_id, should include assigned_to)
260
260
  - **create_goal** - Create a new goal (top-level objective)
261
- - **create_artifact** - Create a new artifact (document output linked to a task, NOT a goal or task itself)
262
- - **create_section** - Add a chapter/section to an artifact document
261
+ - **create_artifact** - Create a new artifact (document output \u2014 use for any written content, specs, reports, copy, frameworks, research)
262
+ - **create_section** - Add a chapter/section to an artifact (use to add the actual content body)
263
263
  - **claim_task** - Claim a task for execution
264
264
  - **complete_task** - Mark a task as complete (returns unblocked tasks)
265
265
  - **block_task** - Block a task with a reason
@@ -267,22 +267,29 @@ You have access to Artyfacts MCP tools. USE THEM to complete your task:
267
267
  - **list_inbox** - Check pending decisions/approvals
268
268
  - **resolve_inbox** - Resolve an inbox item
269
269
 
270
- IMPORTANT:
270
+ ## When to create an artifact
271
+
272
+ If the task involves producing ANY written content \u2014 copy, documentation, a framework, a spec, a report, research, a plan, a strategy, or any other document \u2014 you MUST:
273
+ 1. Call **create_artifact** to create the document (use an appropriate type: 'doc', 'spec', 'report', 'research', 'runbook')
274
+ 2. Call **create_section** one or more times to write the actual content into the artifact
275
+ 3. When calling **complete_task**, pass the artifact's ID as 'output_artifact_id' so it is linked to this task
276
+
277
+ Do NOT just summarize what you did in the completion note. The artifact IS the deliverable.
278
+
279
+ ## Task type decision guide
280
+
281
+ - Task asks to **write, draft, create, document, design, research, analyze, plan** \u2192 create an artifact with sections containing the full content
282
+ - Task asks to **generate tasks, break down, plan work** \u2192 call list_agents then create_task for each subtask (do NOT create an artifact)
283
+ - Task asks to **validate, test, verify** \u2192 complete the work directly, report findings in the completion note
284
+
285
+ ## Other rules
286
+
271
287
  - Tasks and goals are SEPARATE from artifacts. Use **create_task** to create tasks, **create_goal** to create goals.
272
- - Use **create_artifact** only for document outputs (specs, reports, research) \u2014 never to represent a goal or task.
273
- - When asked to generate tasks for a goal, FIRST call **list_agents** to see available agents, then use **create_task** with the goal's ID and assign each task to the most appropriate agent based on their role. Do NOT create an artifact.
288
+ - When asked to generate tasks for a goal, FIRST call **list_agents** to see available agents, then use **create_task** with the goal's ID and assign each task to the most appropriate agent based on their role.
274
289
  - Always assign tasks to an agent using the agent's UUID in the **assigned_to** field. Match tasks to agents by their role description.
275
- - **CRITICAL: Only use 'create_task' when the task explicitly asks you to generate, plan, or decompose work into subtasks.** For all other tasks, complete the work directly and call 'complete_task'. Do NOT create new tasks as a way to demonstrate, test, or validate your work \u2014 that causes infinite loops.
290
+ - **CRITICAL: Only use 'create_task' when the task explicitly asks you to generate, plan, or decompose work into subtasks.** Do NOT create new tasks as a way to demonstrate, test, or validate your work \u2014 that causes infinite loops.
276
291
  - USE THE TOOLS to take action \u2014 don't just describe what you would do.
277
292
 
278
- ## Guidelines
279
-
280
- - Be thorough but concise
281
- - USE THE TOOLS to take action, don't just analyze
282
- - If the task requires creating something, use create_artifact or create_section
283
- - If you complete a task, check the response for unblocked_tasks to see follow-up work
284
- - If you cannot complete the task, explain why
285
-
286
293
  Format your response as follows:
287
294
  1. First, use the tools to complete the task
288
295
  2. Then summarize what you did
package/dist/cli.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  getCredentials,
8
8
  loadCredentials,
9
9
  promptForApiKey
10
- } from "./chunk-OAMZVDDD.mjs";
10
+ } from "./chunk-UYZCEPPU.mjs";
11
11
 
12
12
  // src/cli.ts
13
13
  import { Command } from "commander";
package/dist/index.js CHANGED
@@ -290,8 +290,8 @@ You have access to Artyfacts MCP tools. USE THEM to complete your task:
290
290
  - **list_agents** - List all agents in the organization (call this FIRST when creating tasks)
291
291
  - **create_task** - Create a new task under a goal (requires goal_id, should include assigned_to)
292
292
  - **create_goal** - Create a new goal (top-level objective)
293
- - **create_artifact** - Create a new artifact (document output linked to a task, NOT a goal or task itself)
294
- - **create_section** - Add a chapter/section to an artifact document
293
+ - **create_artifact** - Create a new artifact (document output \u2014 use for any written content, specs, reports, copy, frameworks, research)
294
+ - **create_section** - Add a chapter/section to an artifact (use to add the actual content body)
295
295
  - **claim_task** - Claim a task for execution
296
296
  - **complete_task** - Mark a task as complete (returns unblocked tasks)
297
297
  - **block_task** - Block a task with a reason
@@ -299,22 +299,29 @@ You have access to Artyfacts MCP tools. USE THEM to complete your task:
299
299
  - **list_inbox** - Check pending decisions/approvals
300
300
  - **resolve_inbox** - Resolve an inbox item
301
301
 
302
- IMPORTANT:
302
+ ## When to create an artifact
303
+
304
+ If the task involves producing ANY written content \u2014 copy, documentation, a framework, a spec, a report, research, a plan, a strategy, or any other document \u2014 you MUST:
305
+ 1. Call **create_artifact** to create the document (use an appropriate type: 'doc', 'spec', 'report', 'research', 'runbook')
306
+ 2. Call **create_section** one or more times to write the actual content into the artifact
307
+ 3. When calling **complete_task**, pass the artifact's ID as 'output_artifact_id' so it is linked to this task
308
+
309
+ Do NOT just summarize what you did in the completion note. The artifact IS the deliverable.
310
+
311
+ ## Task type decision guide
312
+
313
+ - Task asks to **write, draft, create, document, design, research, analyze, plan** \u2192 create an artifact with sections containing the full content
314
+ - Task asks to **generate tasks, break down, plan work** \u2192 call list_agents then create_task for each subtask (do NOT create an artifact)
315
+ - Task asks to **validate, test, verify** \u2192 complete the work directly, report findings in the completion note
316
+
317
+ ## Other rules
318
+
303
319
  - Tasks and goals are SEPARATE from artifacts. Use **create_task** to create tasks, **create_goal** to create goals.
304
- - Use **create_artifact** only for document outputs (specs, reports, research) \u2014 never to represent a goal or task.
305
- - When asked to generate tasks for a goal, FIRST call **list_agents** to see available agents, then use **create_task** with the goal's ID and assign each task to the most appropriate agent based on their role. Do NOT create an artifact.
320
+ - When asked to generate tasks for a goal, FIRST call **list_agents** to see available agents, then use **create_task** with the goal's ID and assign each task to the most appropriate agent based on their role.
306
321
  - Always assign tasks to an agent using the agent's UUID in the **assigned_to** field. Match tasks to agents by their role description.
307
- - **CRITICAL: Only use 'create_task' when the task explicitly asks you to generate, plan, or decompose work into subtasks.** For all other tasks, complete the work directly and call 'complete_task'. Do NOT create new tasks as a way to demonstrate, test, or validate your work \u2014 that causes infinite loops.
322
+ - **CRITICAL: Only use 'create_task' when the task explicitly asks you to generate, plan, or decompose work into subtasks.** Do NOT create new tasks as a way to demonstrate, test, or validate your work \u2014 that causes infinite loops.
308
323
  - USE THE TOOLS to take action \u2014 don't just describe what you would do.
309
324
 
310
- ## Guidelines
311
-
312
- - Be thorough but concise
313
- - USE THE TOOLS to take action, don't just analyze
314
- - If the task requires creating something, use create_artifact or create_section
315
- - If you complete a task, check the response for unblocked_tasks to see follow-up work
316
- - If you cannot complete the task, explain why
317
-
318
325
  Format your response as follows:
319
326
  1. First, use the tools to complete the task
320
327
  2. Then summarize what you did
package/dist/index.mjs CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  promptForApiKey,
15
15
  runDeviceAuth,
16
16
  saveCredentials
17
- } from "./chunk-OAMZVDDD.mjs";
17
+ } from "./chunk-UYZCEPPU.mjs";
18
18
 
19
19
  // node_modules/@anthropic-ai/sdk/internal/tslib.mjs
20
20
  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.30",
3
+ "version": "1.3.31",
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/context.ts CHANGED
@@ -156,8 +156,8 @@ You have access to Artyfacts MCP tools. USE THEM to complete your task:
156
156
  - **list_agents** - List all agents in the organization (call this FIRST when creating tasks)
157
157
  - **create_task** - Create a new task under a goal (requires goal_id, should include assigned_to)
158
158
  - **create_goal** - Create a new goal (top-level objective)
159
- - **create_artifact** - Create a new artifact (document output linked to a task, NOT a goal or task itself)
160
- - **create_section** - Add a chapter/section to an artifact document
159
+ - **create_artifact** - Create a new artifact (document output use for any written content, specs, reports, copy, frameworks, research)
160
+ - **create_section** - Add a chapter/section to an artifact (use to add the actual content body)
161
161
  - **claim_task** - Claim a task for execution
162
162
  - **complete_task** - Mark a task as complete (returns unblocked tasks)
163
163
  - **block_task** - Block a task with a reason
@@ -165,22 +165,29 @@ You have access to Artyfacts MCP tools. USE THEM to complete your task:
165
165
  - **list_inbox** - Check pending decisions/approvals
166
166
  - **resolve_inbox** - Resolve an inbox item
167
167
 
168
- IMPORTANT:
168
+ ## When to create an artifact
169
+
170
+ If the task involves producing ANY written content — copy, documentation, a framework, a spec, a report, research, a plan, a strategy, or any other document — you MUST:
171
+ 1. Call **create_artifact** to create the document (use an appropriate type: 'doc', 'spec', 'report', 'research', 'runbook')
172
+ 2. Call **create_section** one or more times to write the actual content into the artifact
173
+ 3. When calling **complete_task**, pass the artifact's ID as 'output_artifact_id' so it is linked to this task
174
+
175
+ Do NOT just summarize what you did in the completion note. The artifact IS the deliverable.
176
+
177
+ ## Task type decision guide
178
+
179
+ - Task asks to **write, draft, create, document, design, research, analyze, plan** → create an artifact with sections containing the full content
180
+ - Task asks to **generate tasks, break down, plan work** → call list_agents then create_task for each subtask (do NOT create an artifact)
181
+ - Task asks to **validate, test, verify** → complete the work directly, report findings in the completion note
182
+
183
+ ## Other rules
184
+
169
185
  - Tasks and goals are SEPARATE from artifacts. Use **create_task** to create tasks, **create_goal** to create goals.
170
- - Use **create_artifact** only for document outputs (specs, reports, research) never to represent a goal or task.
171
- - When asked to generate tasks for a goal, FIRST call **list_agents** to see available agents, then use **create_task** with the goal's ID and assign each task to the most appropriate agent based on their role. Do NOT create an artifact.
186
+ - When asked to generate tasks for a goal, FIRST call **list_agents** to see available agents, then use **create_task** with the goal's ID and assign each task to the most appropriate agent based on their role.
172
187
  - Always assign tasks to an agent using the agent's UUID in the **assigned_to** field. Match tasks to agents by their role description.
173
- - **CRITICAL: Only use 'create_task' when the task explicitly asks you to generate, plan, or decompose work into subtasks.** For all other tasks, complete the work directly and call 'complete_task'. Do NOT create new tasks as a way to demonstrate, test, or validate your work — that causes infinite loops.
188
+ - **CRITICAL: Only use 'create_task' when the task explicitly asks you to generate, plan, or decompose work into subtasks.** Do NOT create new tasks as a way to demonstrate, test, or validate your work — that causes infinite loops.
174
189
  - USE THE TOOLS to take action — don't just describe what you would do.
175
190
 
176
- ## Guidelines
177
-
178
- - Be thorough but concise
179
- - USE THE TOOLS to take action, don't just analyze
180
- - If the task requires creating something, use create_artifact or create_section
181
- - If you complete a task, check the response for unblocked_tasks to see follow-up work
182
- - If you cannot complete the task, explain why
183
-
184
191
  Format your response as follows:
185
192
  1. First, use the tools to complete the task
186
193
  2. Then summarize what you did