@integrity-labs/agt-cli 0.15.4 → 0.15.7

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.
@@ -22,7 +22,7 @@ import {
22
22
  resolveChannels,
23
23
  resolveDmTarget,
24
24
  wrapScheduledTaskPrompt
25
- } from "../chunk-WDF7NJ2F.js";
25
+ } from "../chunk-WIW5FIRY.js";
26
26
  import {
27
27
  findTaskByTemplate,
28
28
  getProjectDir,
@@ -42,7 +42,7 @@ import {
42
42
  startPersistentSession,
43
43
  stopAllSessionsAndWait,
44
44
  stopPersistentSession
45
- } from "../chunk-IEVDKEIT.js";
45
+ } from "../chunk-S3SFU5IM.js";
46
46
 
47
47
  // src/lib/manager-worker.ts
48
48
  import { createHash } from "crypto";
@@ -1069,8 +1069,8 @@ function startRealtimeKanban(config2) {
1069
1069
  filter: filterStr
1070
1070
  }, (payload) => {
1071
1071
  const item = payload.new;
1072
- if (item.status === "today") {
1073
- log2(`[realtime] New kanban item in 'today': "${item.title}" for agent ${item.agent_id}`);
1072
+ if (item.status === "todo") {
1073
+ log2(`[realtime] New kanban item in 'todo': item_id=${item.id} agent=${item.agent_id}`);
1074
1074
  onTodayItem(item);
1075
1075
  }
1076
1076
  }).on("postgres_changes", {
@@ -1081,8 +1081,8 @@ function startRealtimeKanban(config2) {
1081
1081
  }, (payload) => {
1082
1082
  const item = payload.new;
1083
1083
  const old = payload.old;
1084
- if (item.status === "today" && old.status !== "today") {
1085
- log2(`[realtime] Kanban item moved to 'today': "${item.title}" for agent ${item.agent_id}`);
1084
+ if (item.status === "todo" && old.status !== "todo") {
1085
+ log2(`[realtime] Kanban item moved to 'todo': item_id=${item.id} agent=${item.agent_id}`);
1086
1086
  onTodayItem(item);
1087
1087
  }
1088
1088
  if (onCompletion && (item.status === "done" || item.status === "failed") && old.status !== item.status) {
@@ -1753,19 +1753,19 @@ The following skills are installed in \`.claude/skills/\`. Claude Code auto-acti
1753
1753
 
1754
1754
  ${body}
1755
1755
 
1756
- ## Updating Plugins (ENG-4341)
1756
+ ## Updating Integrations (ENG-4341)
1757
1757
 
1758
- Plugin skills under \`.claude/skills/plugin-*/SKILL.md\` are **read-only** and managed by the platform. They are derived from the plugin's database row plus any per-agent context overrides, and are re-rendered every time the manager polls or a context change is broadcast over Supabase Realtime.
1758
+ Integration skills under \`.claude/skills/integration-*/SKILL.md\` are **read-only** and managed by the platform. They are derived from the integration's database row plus any per-agent context overrides, and are re-rendered every time the manager polls or a context change is broadcast over Supabase Realtime.
1759
1759
 
1760
- **Never edit \`.claude/skills/plugin-*/SKILL.md\` files directly.** If you do, your edit will be silently overwritten on the next manager refresh, AND it won't propagate to other agents using the same plugin.
1760
+ **Never edit \`.claude/skills/integration-*/SKILL.md\` files directly.** If you do, your edit will be silently overwritten on the next manager refresh, AND it won't propagate to other agents using the same integration.
1761
1761
 
1762
- To change a plugin's behavior (add a rule, update a default, tell the plugin not to do something), call the **\`plugin.improve\`** MCP tool with the user's request. The tool calls the platform API, which uses an LLM to translate the request into a structured update of the plugin's typed context fields and/or freeform overrides text. You'll get a diff back to show the user; on confirmation, call the tool again with \`auto_apply: true\` to apply.
1762
+ To change an integration's behavior (add a rule, update a default, tell the integration not to do something), call the **\`plugin.improve\`** MCP tool with the user's request. The tool calls the platform API, which uses an LLM to translate the request into a structured update of the integration's typed context fields and/or freeform overrides text. You'll get a diff back to show the user; on confirmation, call the tool again with \`auto_apply: true\` to apply.
1763
1763
 
1764
1764
  Examples of when to use \`plugin.improve\`:
1765
- - *"Update the Coding plugin so we always use trunk instead of main"*
1766
- - *"Add a rule to the Coding plugin: never merge directly to main, always open a PR"*
1767
- - *"Tell the Knowledge Base plugin not to return results below 70% relevance"*
1768
- - *"The Slack plugin should post incidents to #oncall, not #general"*
1765
+ - *"Update the Coding integration so we always use trunk instead of main"*
1766
+ - *"Add a rule to the Coding integration: never merge directly to main, always open a PR"*
1767
+ - *"Tell the Knowledge Base integration not to return results below 70% relevance"*
1768
+ - *"The Slack integration should post incidents to #oncall, not #general"*
1769
1769
  ${SKILLS_INDEX_END}`;
1770
1770
  const current = rfs(claudeMdPath, "utf-8");
1771
1771
  let next;
@@ -2035,7 +2035,7 @@ async function pollCycle() {
2035
2035
  }
2036
2036
  try {
2037
2037
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
2038
- const { collectDiagnostics } = await import("../persistent-session-ALP5DGFI.js");
2038
+ const { collectDiagnostics } = await import("../persistent-session-Q4X2KRS6.js");
2039
2039
  const diagCodeNames = [...persistentSessionAgents];
2040
2040
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
2041
2041
  let tailscaleHostname;
@@ -2897,7 +2897,7 @@ async function processAgent(agent, agentStates) {
2897
2897
  );
2898
2898
  for (const [pluginSlug, scopes] of pluginGroups) {
2899
2899
  try {
2900
- const pluginSkillId = `plugin-${pluginSlug}`.replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2900
+ const pluginSkillId = `integration-${pluginSlug}`.replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2901
2901
  currentPluginSkillIds.add(pluginSkillId);
2902
2902
  const ctx = contextBySlug.get(pluginSlug);
2903
2903
  const renderedScopes = scopes.map((s) => ({
@@ -2943,7 +2943,9 @@ async function processAgent(agent, agentStates) {
2943
2943
  for (const dir of existingDirs) {
2944
2944
  try {
2945
2945
  for (const entry of readdirSync2(dir)) {
2946
- if (entry.startsWith("plugin-")) discoveredEntries.add(entry);
2946
+ if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
2947
+ discoveredEntries.add(entry);
2948
+ }
2947
2949
  }
2948
2950
  } catch {
2949
2951
  }
@@ -2959,7 +2961,7 @@ async function processAgent(agent, agentStates) {
2959
2961
  };
2960
2962
  for (const entry of discoveredEntries) {
2961
2963
  if (!currentPluginSkillIds.has(entry)) {
2962
- removeSkillFolder(entry, "orphaned plugin skill");
2964
+ removeSkillFolder(entry, "orphaned skill folder");
2963
2965
  }
2964
2966
  }
2965
2967
  } catch (err) {
@@ -3139,7 +3141,7 @@ async function processAgent(agent, agentStates) {
3139
3141
  } else if (agentFw === "claude-code") {
3140
3142
  if (sessionMode === "persistent") {
3141
3143
  if (isSessionHealthy(agent.code_name)) {
3142
- const todayItem = boardItems.find((b) => b.status === "today");
3144
+ const todayItem = boardItems.find((b) => b.status === "todo");
3143
3145
  const taskHint = todayItem ? ` Top item: "${todayItem.title}" (priority ${todayItem.priority}).` : "";
3144
3146
  injectMessage(agent.code_name, "task", `New work triggered. Check your kanban board with kanban_list and pick up the next item.${taskHint}`, {
3145
3147
  task_name: "kanban-work-trigger"
@@ -3465,7 +3467,7 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
3465
3467
  prompt = boardPrefix + prompt;
3466
3468
  }
3467
3469
  if (KANBAN_WORK_TEMPLATES.has(task.templateId)) {
3468
- const todayItem = boardItems.find((b) => b.status === "today");
3470
+ const todayItem = boardItems.find((b) => b.status === "todo");
3469
3471
  if (todayItem) {
3470
3472
  try {
3471
3473
  await api.post("/host/kanban", {
@@ -3487,9 +3489,42 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
3487
3489
  });
3488
3490
  }
3489
3491
  }
3492
+ async function startRun(opts) {
3493
+ try {
3494
+ const res = await api.post(
3495
+ "/host/runs/start",
3496
+ opts
3497
+ );
3498
+ return { run_id: res.run_id ?? null, kanban_item_id: res.kanban_item_id ?? null };
3499
+ } catch (err) {
3500
+ const errText = err instanceof Error ? err.message : String(err);
3501
+ const errId = createHash("sha256").update(errText).digest("hex").slice(0, 12);
3502
+ log(`[runs] start failed for agent_id=${opts.agent_id} source_type=${opts.source_type} error_id=${errId}`);
3503
+ return { run_id: null, kanban_item_id: null };
3504
+ }
3505
+ }
3506
+ async function finishRun(runId, outcome, options = {}) {
3507
+ try {
3508
+ await api.post("/host/runs/finish", {
3509
+ run_id: runId,
3510
+ outcome,
3511
+ outcome_message: options.outcomeMessage,
3512
+ metadata: options.metadata,
3513
+ complete_kanban_item_id: options.completeKanbanItemId ?? void 0,
3514
+ result: options.result
3515
+ });
3516
+ } catch (err) {
3517
+ const errText = err instanceof Error ? err.message : String(err);
3518
+ const errId = createHash("sha256").update(errText).digest("hex").slice(0, 12);
3519
+ log(`[runs] finish failed for run_id=${runId} outcome=${outcome} error_id=${errId}`);
3520
+ }
3521
+ }
3490
3522
  async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
3491
3523
  const projectDir = getProjectDir(codeName);
3492
3524
  const mcpConfigPath = join2(projectDir, ".mcp.json");
3525
+ let runId = null;
3526
+ let kanbanItemId = null;
3527
+ let taskResult;
3493
3528
  sanitizeMcpJson(mcpConfigPath, requireHost());
3494
3529
  prompt = wrapScheduledTaskPrompt(prompt);
3495
3530
  try {
@@ -3536,6 +3571,16 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
3536
3571
  log(`[claude-scheduler] Skipping task '${task.name}' for '${codeName}' \u2014 auth resolve failed: ${err.message}`);
3537
3572
  return;
3538
3573
  }
3574
+ const startResult = await startRun({
3575
+ agent_id: agentId,
3576
+ source_type: "scheduled_task",
3577
+ source_ref: task.taskId,
3578
+ metadata: { template_id: task.templateId, name: task.name },
3579
+ materialize_kanban: { title: task.name, priority: 2 }
3580
+ });
3581
+ runId = startResult.run_id;
3582
+ kanbanItemId = startResult.kanban_item_id;
3583
+ if (runId) childEnv["AGT_RUN_ID"] = runId;
3539
3584
  const { stdout, stderr } = await execFilePromiseLong(resolveClaudeBinary(), claudeArgs, {
3540
3585
  cwd: projectDir,
3541
3586
  timeout: 3e5,
@@ -3546,6 +3591,7 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
3546
3591
  log(`[claude-scheduler] Task '${task.name}' stderr for '${codeName}': ${stderr.slice(0, 500)}`);
3547
3592
  }
3548
3593
  const output = stdout.trim();
3594
+ taskResult = output.slice(0, 4e3) || void 0;
3549
3595
  log(`[claude-scheduler] Task '${task.name}' completed for '${codeName}' (${output.length} chars): ${output.slice(0, 300)}`);
3550
3596
  await processClaudeTaskResult(codeName, agentId, task.templateId, output, {
3551
3597
  mode: task.deliveryMode,
@@ -3553,6 +3599,13 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
3553
3599
  to: task.deliveryTo,
3554
3600
  taskId: task.taskId
3555
3601
  });
3602
+ if (runId) {
3603
+ await finishRun(runId, "completed", {
3604
+ metadata: { output_length: output.length },
3605
+ completeKanbanItemId: kanbanItemId,
3606
+ result: taskResult
3607
+ });
3608
+ }
3556
3609
  const updated = markTaskFired(codeName, task.taskId, "ok");
3557
3610
  claudeSchedulerStates.set(codeName, updated);
3558
3611
  if (task.scheduleKind === "at") {
@@ -3565,13 +3618,25 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
3565
3618
  const errMsg = err instanceof Error ? err.message : String(err);
3566
3619
  log(`[claude-scheduler] Task '${task.name}' failed for '${codeName}': ${errMsg}`);
3567
3620
  if (err instanceof ChildProcessError) {
3568
- if (err.stdout.trim()) {
3569
- log(`[claude-scheduler] Task '${task.name}' stdout for '${codeName}': ${err.stdout.trim().slice(0, 1e3)}`);
3621
+ const errStdout = err.stdout.trim();
3622
+ if (errStdout) {
3623
+ taskResult = errStdout.slice(0, 4e3) || taskResult;
3624
+ log(`[claude-scheduler] Task '${task.name}' stdout for '${codeName}': ${errStdout.slice(0, 1e3)}`);
3570
3625
  }
3571
3626
  if (err.stderr.trim()) {
3572
3627
  log(`[claude-scheduler] Task '${task.name}' stderr for '${codeName}': ${err.stderr.trim().slice(0, 1e3)}`);
3573
3628
  }
3574
3629
  }
3630
+ if (runId) {
3631
+ try {
3632
+ await finishRun(runId, "failed", {
3633
+ outcomeMessage: errMsg,
3634
+ completeKanbanItemId: kanbanItemId,
3635
+ result: taskResult
3636
+ });
3637
+ } catch {
3638
+ }
3639
+ }
3575
3640
  const updated = markTaskFired(codeName, task.taskId, "error");
3576
3641
  claudeSchedulerStates.set(codeName, updated);
3577
3642
  }
@@ -3819,7 +3884,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
3819
3884
  prompt = boardPrefix + prompt;
3820
3885
  }
3821
3886
  if (KANBAN_WORK_TEMPLATES.has(task.templateId)) {
3822
- const todayItem = boardItems.find((b) => b.status === "today");
3887
+ const todayItem = boardItems.find((b) => b.status === "todo");
3823
3888
  if (todayItem) {
3824
3889
  try {
3825
3890
  await api.post("/host/kanban", {
@@ -4214,7 +4279,7 @@ var TASK_UPDATE_TEMPLATES = /* @__PURE__ */ new Set(["hourly-status", "task-upda
4214
4279
  var PLAN_TEMPLATES = /* @__PURE__ */ new Set(["morning-plan"]);
4215
4280
  var KANBAN_WORK_TEMPLATES = /* @__PURE__ */ new Set(["kanban-work"]);
4216
4281
  var BOARD_INJECT_TEMPLATES = /* @__PURE__ */ new Set(["morning-plan", "task-update", "hourly-status", "end-of-day-summary", "kanban-work"]);
4217
- var ACTIONABLE_STATUSES = /* @__PURE__ */ new Set(["backlog", "today", "in_progress"]);
4282
+ var ACTIONABLE_STATUSES = /* @__PURE__ */ new Set(["backlog", "todo", "in_progress"]);
4218
4283
  function hasActionableItems(items) {
4219
4284
  return items.some((item) => ACTIONABLE_STATUSES.has(item.status));
4220
4285
  }
@@ -4347,8 +4412,8 @@ function parseStandupSummary(summary) {
4347
4412
  if (lower.includes("yesterday") || lower.includes("accomplished")) {
4348
4413
  currentSection = "yesterday";
4349
4414
  continue;
4350
- } else if (lower.includes("today") || lower.includes("working on")) {
4351
- currentSection = "today";
4415
+ } else if (lower.includes("todo") || lower.includes("working on")) {
4416
+ currentSection = "todo";
4352
4417
  continue;
4353
4418
  } else if (lower.includes("blocker")) {
4354
4419
  currentSection = "blockers";
@@ -4360,7 +4425,7 @@ function parseStandupSummary(summary) {
4360
4425
  case "yesterday":
4361
4426
  yesterday += (yesterday ? "\n" : "") + trimmed;
4362
4427
  break;
4363
- case "today":
4428
+ case "todo":
4364
4429
  today += (today ? "\n" : "") + trimmed;
4365
4430
  break;
4366
4431
  case "blockers":
@@ -4403,7 +4468,7 @@ function parsePlanItems(summary) {
4403
4468
  title,
4404
4469
  priority,
4405
4470
  estimated_minutes: estimatedMinutes,
4406
- status: "today"
4471
+ status: "todo"
4407
4472
  };
4408
4473
  } else if (currentItem && trimmed && !trimmed.match(/^(?:PLAN|---)/i)) {
4409
4474
  const descLine = sanitizeKanbanString(trimmed, MAX_KANBAN_NOTES_LENGTH);
@@ -4413,7 +4478,7 @@ function parsePlanItems(summary) {
4413
4478
  if (currentItem) items.push(currentItem);
4414
4479
  return items;
4415
4480
  }
4416
- var VALID_KANBAN_STATUSES = /* @__PURE__ */ new Set(["backlog", "today", "in_progress", "done"]);
4481
+ var VALID_KANBAN_STATUSES = /* @__PURE__ */ new Set(["backlog", "todo", "in_progress", "done"]);
4417
4482
  var MAX_KANBAN_TITLE_LENGTH = 500;
4418
4483
  var MAX_KANBAN_NOTES_LENGTH = 2e3;
4419
4484
  function sanitizeKanbanString(value, maxLen) {
@@ -4459,9 +4524,10 @@ function parseKanbanUpdates(summary) {
4459
4524
  const lines = kanbanSection.split("\n");
4460
4525
  for (const line of lines) {
4461
4526
  const trimmed = line.trim();
4462
- const match = trimmed.match(/^[-*•]\s*"([^"]+)":\s*(backlog|today|in_progress|done)(?:\s*\((.+)\))?/i);
4527
+ const match = trimmed.match(/^[-*•]\s*"([^"]+)":\s*(backlog|todo|today|in_progress|done)(?:\s*\((.+)\))?/i);
4463
4528
  if (match) {
4464
- const status = match[2].toLowerCase();
4529
+ const rawStatus = match[2].toLowerCase();
4530
+ const status = rawStatus === "today" ? "todo" : rawStatus;
4465
4531
  if (!VALID_KANBAN_STATUSES.has(status)) continue;
4466
4532
  const title = sanitizeKanbanString(match[1], MAX_KANBAN_TITLE_LENGTH);
4467
4533
  if (!title) continue;
@@ -4503,7 +4569,7 @@ function formatBoardForPrompt(items, template) {
4503
4569
  const lines = [];
4504
4570
  if (template === "morning-plan") {
4505
4571
  lines.push("=== CURRENT BOARD ===");
4506
- for (const [status, label] of [["backlog", "BACKLOG (carry-over)"], ["today", "TODAY"], ["in_progress", "IN PROGRESS"]]) {
4572
+ for (const [status, label] of [["backlog", "BACKLOG (carry-over)"], ["todo", "TO DO"], ["in_progress", "IN PROGRESS"]]) {
4507
4573
  const statusItems = grouped[status];
4508
4574
  if (statusItems && statusItems.length > 0) {
4509
4575
  lines.push(`${label}:`);
@@ -4515,13 +4581,13 @@ function formatBoardForPrompt(items, template) {
4515
4581
  lines.push("=====================");
4516
4582
  lines.push("");
4517
4583
  lines.push("Create today's plan. You may:");
4518
- lines.push('- Move backlog items to "today"');
4584
+ lines.push('- Move backlog items to "todo"');
4519
4585
  lines.push("- Add new items you've identified");
4520
4586
  lines.push("- Reprioritise existing items");
4521
4587
  lines.push("");
4522
4588
  } else {
4523
4589
  lines.push("=== YOUR KANBAN BOARD ===");
4524
- for (const [status, label] of [["today", "TODAY"], ["in_progress", "IN PROGRESS"], ["backlog", "BACKLOG"]]) {
4590
+ for (const [status, label] of [["todo", "TO DO"], ["in_progress", "IN PROGRESS"], ["backlog", "BACKLOG"]]) {
4525
4591
  const statusItems = grouped[status];
4526
4592
  if (statusItems && statusItems.length > 0) {
4527
4593
  lines.push(`${label}:`);