@rudderhq/cli 0.3.4-canary.3 → 0.3.4-canary.30

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.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // ../packages/shared/dist/constants.js
13
- var ORGANIZATION_STATUSES, ORGANIZATION_INTELLIGENCE_PROFILE_PURPOSES, ORGANIZATION_INTELLIGENCE_PROFILE_STATUSES, DEPLOYMENT_MODES, DEPLOYMENT_EXPOSURES, AUTH_BASE_URL_MODES, AGENT_STATUSES, AGENT_RUNTIME_TYPES, AGENT_ROLES, AGENT_ICON_NAMES, AGENT_DICEBEAR_NOTIONISTS_ICON_PREFIX, AGENT_AVATAR_BACKGROUND_PRESET_IDS, ISSUE_STATUSES, ISSUE_PRIORITIES, CALENDAR_SOURCE_TYPES, CALENDAR_OWNER_TYPES, CALENDAR_VISIBILITIES, CALENDAR_SOURCE_STATUSES, CALENDAR_EVENT_KINDS, CALENDAR_EVENT_STATUSES, CALENDAR_SOURCE_MODES, CHAT_CONVERSATION_STATUSES, CHAT_ISSUE_CREATION_MODES, CHAT_MESSAGE_ROLES, CHAT_MESSAGE_KINDS, CHAT_MESSAGE_STATUSES, CHAT_CONTEXT_ENTITY_TYPES, GOAL_LEVELS, GOAL_STATUSES, PROJECT_STATUSES, ORGANIZATION_RESOURCE_KINDS, ORGANIZATION_RESOURCE_SOURCE_TYPES, PROJECT_RESOURCE_ATTACHMENT_ROLES, AUTOMATION_STATUSES, AUTOMATION_CONCURRENCY_POLICIES, AUTOMATION_CATCH_UP_POLICIES, AUTOMATION_OUTPUT_MODES, AUTOMATION_TRIGGER_SIGNING_MODES, PROJECT_COLORS, APPROVAL_TYPES, SECRET_PROVIDERS, STORAGE_PROVIDERS, BILLING_TYPES, FINANCE_EVENT_KINDS, FINANCE_DIRECTIONS, FINANCE_UNITS, BUDGET_SCOPE_TYPES, BUDGET_METRICS, BUDGET_WINDOW_KINDS, BUDGET_INCIDENT_RESOLUTION_ACTIONS, INVITE_JOIN_TYPES, JOIN_REQUEST_TYPES, JOIN_REQUEST_STATUSES, PERMISSION_KEYS, PLUGIN_STATUSES, PLUGIN_CATEGORIES, PLUGIN_CAPABILITIES, PLUGIN_UI_SLOT_TYPES, PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS, PLUGIN_LAUNCHER_PLACEMENT_ZONES, PLUGIN_LAUNCHER_ACTIONS, PLUGIN_LAUNCHER_BOUNDS, PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS, PLUGIN_UI_SLOT_ENTITY_TYPES, PLUGIN_STATE_SCOPE_KINDS;
13
+ var ORGANIZATION_STATUSES, ORGANIZATION_INTELLIGENCE_PROFILE_PURPOSES, ORGANIZATION_INTELLIGENCE_PROFILE_STATUSES, DEPLOYMENT_MODES, DEPLOYMENT_EXPOSURES, AUTH_BASE_URL_MODES, AGENT_STATUSES, AGENT_RUNTIME_TYPES, AGENT_ROLES, AGENT_ICON_NAMES, AGENT_DICEBEAR_NOTIONISTS_ICON_PREFIX, AGENT_AVATAR_BACKGROUND_PRESET_IDS, ISSUE_STATUSES, ISSUE_PRIORITIES, CALENDAR_SOURCE_TYPES, CALENDAR_OWNER_TYPES, CALENDAR_VISIBILITIES, CALENDAR_SOURCE_STATUSES, CALENDAR_EVENT_KINDS, CALENDAR_EVENT_STATUSES, CALENDAR_SOURCE_MODES, CHAT_CONVERSATION_STATUSES, CHAT_ISSUE_CREATION_MODES, CHAT_MESSAGE_ROLES, CHAT_MESSAGE_KINDS, CHAT_MESSAGE_STATUSES, CHAT_CONTEXT_ENTITY_TYPES, GOAL_LEVELS, GOAL_STATUSES, PROJECT_STATUSES, ORGANIZATION_RESOURCE_KINDS, ORGANIZATION_RESOURCE_SOURCE_TYPES, PROJECT_RESOURCE_ATTACHMENT_ROLES, AUTOMATION_STATUSES, AUTOMATION_CONCURRENCY_POLICIES, AUTOMATION_CATCH_UP_POLICIES, AUTOMATION_OUTPUT_MODES, AUTOMATION_TRIGGER_SIGNING_MODES, PROJECT_COLORS, PROJECT_ICONS, APPROVAL_TYPES, SECRET_PROVIDERS, STORAGE_PROVIDERS, BILLING_TYPES, FINANCE_EVENT_KINDS, FINANCE_DIRECTIONS, FINANCE_UNITS, BUDGET_SCOPE_TYPES, BUDGET_METRICS, BUDGET_WINDOW_KINDS, BUDGET_INCIDENT_RESOLUTION_ACTIONS, INVITE_JOIN_TYPES, JOIN_REQUEST_TYPES, JOIN_REQUEST_STATUSES, PERMISSION_KEYS, PLUGIN_STATUSES, PLUGIN_CATEGORIES, PLUGIN_CAPABILITIES, PLUGIN_UI_SLOT_TYPES, PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS, PLUGIN_LAUNCHER_PLACEMENT_ZONES, PLUGIN_LAUNCHER_ACTIONS, PLUGIN_LAUNCHER_BOUNDS, PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS, PLUGIN_UI_SLOT_ENTITY_TYPES, PLUGIN_STATE_SCOPE_KINDS;
14
14
  var init_constants = __esm({
15
15
  "../packages/shared/dist/constants.js"() {
16
16
  "use strict";
@@ -186,6 +186,24 @@ var init_constants = __esm({
186
186
  "linear-gradient(135deg, #475569 0%, #0f766e 100%)",
187
187
  "linear-gradient(135deg, #334155 0%, #7c3aed 100%)"
188
188
  ];
189
+ PROJECT_ICONS = [
190
+ "folder",
191
+ "book",
192
+ "plane",
193
+ "globe",
194
+ "code",
195
+ "rocket",
196
+ "target",
197
+ "lightbulb",
198
+ "wrench",
199
+ "shield",
200
+ "database",
201
+ "megaphone",
202
+ "palette",
203
+ "users",
204
+ "calendar",
205
+ "package"
206
+ ];
189
207
  APPROVAL_TYPES = [
190
208
  "hire_agent",
191
209
  "approve_ceo_strategy",
@@ -278,7 +296,6 @@ var init_constants = __esm({
278
296
  "project.workspaces.read",
279
297
  "issues.read",
280
298
  "issue.comments.read",
281
- "issue.documents.read",
282
299
  "agents.read",
283
300
  "goals.read",
284
301
  "goals.create",
@@ -289,7 +306,6 @@ var init_constants = __esm({
289
306
  "issues.create",
290
307
  "issues.update",
291
308
  "issue.comments.create",
292
- "issue.documents.write",
293
309
  "agents.pause",
294
310
  "agents.resume",
295
311
  "agents.invoke",
@@ -428,7 +444,7 @@ var init_workspace_backup = __esm({
428
444
 
429
445
  // ../packages/shared/dist/validators/instance.js
430
446
  import { z } from "zod";
431
- var instanceLocaleSchema, instanceGeneralSettingsSchema, patchInstanceGeneralSettingsSchema, instanceNotificationSettingsSchema, patchInstanceNotificationSettingsSchema, instanceLangfuseSettingsSchema, patchInstanceLangfuseSettingsSchema, OPERATOR_PROFILE_MORE_ABOUT_YOU_MAX_LENGTH, operatorProfileSettingsSchema, patchOperatorProfileSettingsSchema, instancePathPickerSelectionTypeSchema, instancePathPickerRequestSchema, instancePathPickerResultSchema;
447
+ var instanceLocaleSchema, instanceGeneralSettingsSchema, patchInstanceGeneralSettingsSchema, instanceNotificationSettingsSchema, patchInstanceNotificationSettingsSchema, instanceLangfuseSettingsSchema, patchInstanceLangfuseSettingsSchema, OPERATOR_PROFILE_MORE_ABOUT_YOU_MAX_LENGTH, operatorProfileSettingsSchema, patchOperatorProfileSettingsSchema, KEYBOARD_SHORTCUT_ACTION_IDS, keyboardShortcutActionIdSchema, keyboardShortcutBindingSchema, keyboardShortcutPreferenceSchema, keyboardShortcutSettingsSchema, instancePathPickerSelectionTypeSchema, instancePathPickerRequestSchema, instancePathPickerResultSchema;
432
448
  var init_instance = __esm({
433
449
  "../packages/shared/dist/validators/instance.js"() {
434
450
  "use strict";
@@ -468,6 +484,43 @@ var init_instance = __esm({
468
484
  moreAboutYou: z.string().max(OPERATOR_PROFILE_MORE_ABOUT_YOU_MAX_LENGTH).default("")
469
485
  }).strict();
470
486
  patchOperatorProfileSettingsSchema = operatorProfileSettingsSchema.partial();
487
+ KEYBOARD_SHORTCUT_ACTION_IDS = [
488
+ "commandPalette.open",
489
+ "settings.open",
490
+ "issue.create",
491
+ "sidebar.toggle",
492
+ "panel.toggle"
493
+ ];
494
+ keyboardShortcutActionIdSchema = z.enum(KEYBOARD_SHORTCUT_ACTION_IDS);
495
+ keyboardShortcutBindingSchema = z.object({
496
+ key: z.string().trim().min(1).max(64),
497
+ code: z.string().trim().min(1).max(80).optional(),
498
+ metaKey: z.boolean().optional(),
499
+ ctrlKey: z.boolean().optional(),
500
+ altKey: z.boolean().optional(),
501
+ shiftKey: z.boolean().optional()
502
+ }).strict();
503
+ keyboardShortcutPreferenceSchema = z.object({
504
+ actionId: keyboardShortcutActionIdSchema,
505
+ bindings: z.array(keyboardShortcutBindingSchema).max(4).optional(),
506
+ disabled: z.boolean().optional()
507
+ }).strict();
508
+ keyboardShortcutSettingsSchema = z.object({
509
+ shortcuts: z.array(keyboardShortcutPreferenceSchema).max(KEYBOARD_SHORTCUT_ACTION_IDS.length).default([])
510
+ }).strict().superRefine((value, ctx) => {
511
+ const seen = /* @__PURE__ */ new Set();
512
+ value.shortcuts.forEach((shortcut, index) => {
513
+ if (seen.has(shortcut.actionId)) {
514
+ ctx.addIssue({
515
+ code: z.ZodIssueCode.custom,
516
+ message: `Duplicate shortcut action id: ${shortcut.actionId}`,
517
+ path: ["shortcuts", index, "actionId"]
518
+ });
519
+ return;
520
+ }
521
+ seen.add(shortcut.actionId);
522
+ });
523
+ });
471
524
  instancePathPickerSelectionTypeSchema = z.enum(["file", "directory"]);
472
525
  instancePathPickerRequestSchema = z.object({
473
526
  selectionType: instancePathPickerSelectionTypeSchema
@@ -912,6 +965,7 @@ var init_chat = __esm({
912
965
  chatRichReferencesSchema = z8.array(chatRichReferenceSchema).max(5);
913
966
  chatAutomationCreateSchema = z8.object({
914
967
  title: z8.string().trim().min(1).max(200),
968
+ instructions: z8.string().trim().max(2e4).optional().nullable(),
915
969
  description: z8.string().trim().max(2e4).optional().nullable(),
916
970
  projectId: z8.string().uuid().optional().nullable(),
917
971
  goalId: z8.string().uuid().optional().nullable(),
@@ -927,7 +981,10 @@ var init_chat = __esm({
927
981
  label: z8.string().trim().max(120).optional().nullable(),
928
982
  enabled: z8.boolean().optional().default(true)
929
983
  })
930
- });
984
+ }).transform(({ description, instructions, ...value }) => ({
985
+ ...value,
986
+ instructions: instructions === void 0 ? description : instructions
987
+ }));
931
988
  createChatAttachmentMetadataSchema = z8.object({
932
989
  messageId: z8.string().uuid()
933
990
  });
@@ -1571,7 +1628,7 @@ function validateProjectWorkspace(value, ctx) {
1571
1628
  });
1572
1629
  }
1573
1630
  }
1574
- var executionWorkspaceStrategySchema, projectExecutionWorkspacePolicySchema, projectWorkspaceSourceTypeSchema, projectWorkspaceVisibilitySchema, projectWorkspaceFields, projectColorValues, legacyProjectColorSchema, projectColorSchema, createProjectWorkspaceSchema, updateProjectWorkspaceSchema, projectFields, createProjectSchema, updateProjectSchema;
1631
+ var executionWorkspaceStrategySchema, projectExecutionWorkspacePolicySchema, projectWorkspaceSourceTypeSchema, projectWorkspaceVisibilitySchema, projectWorkspaceFields, projectColorValues, legacyProjectColorSchema, projectColorSchema, projectIconSchema, createProjectWorkspaceSchema, updateProjectWorkspaceSchema, projectFields, createProjectSchema, updateProjectSchema;
1575
1632
  var init_project = __esm({
1576
1633
  "../packages/shared/dist/validators/project.js"() {
1577
1634
  "use strict";
@@ -1617,6 +1674,7 @@ var init_project = __esm({
1617
1674
  projectColorValues = new Set(PROJECT_COLORS);
1618
1675
  legacyProjectColorSchema = z13.string().regex(/^#[0-9a-fA-F]{6}$/);
1619
1676
  projectColorSchema = z13.string().refine((value) => legacyProjectColorSchema.safeParse(value).success || projectColorValues.has(value), "Color must be a 6-digit hex value or a supported project gradient");
1677
+ projectIconSchema = z13.enum(PROJECT_ICONS);
1620
1678
  createProjectWorkspaceSchema = z13.object({
1621
1679
  ...projectWorkspaceFields,
1622
1680
  isPrimary: z13.boolean().optional().default(false)
@@ -1635,6 +1693,7 @@ var init_project = __esm({
1635
1693
  leadAgentId: z13.string().uuid().optional().nullable(),
1636
1694
  targetDate: z13.string().optional().nullable(),
1637
1695
  color: projectColorSchema.optional().nullable(),
1696
+ icon: projectIconSchema.optional().nullable(),
1638
1697
  executionWorkspacePolicy: projectExecutionWorkspacePolicySchema.optional().nullable(),
1639
1698
  resourceAttachments: z13.array(projectResourceAttachmentInputSchema).optional(),
1640
1699
  newResources: z13.array(createProjectInlineResourceSchema).optional(),
@@ -1649,12 +1708,12 @@ var init_project = __esm({
1649
1708
 
1650
1709
  // ../packages/shared/dist/validators/issue.js
1651
1710
  import { z as z14 } from "zod";
1652
- var executionWorkspaceStrategySchema2, issueExecutionWorkspaceSettingsSchema, issueAssigneeAdapterOverridesSchema, createIssueSchema, createIssueLabelSchema, updateIssueLabelSchema, updateIssueSchema, reorderIssueSchema, checkoutIssueSchema, addIssueCommentSchema, updateIssueCommentSchema, reportIssueCommitSchema, linkIssueApprovalSchema, createIssueAttachmentMetadataSchema, createIssueWorkspaceAttachmentSchema, ISSUE_DOCUMENT_FORMATS, issueDocumentFormatSchema, issueDocumentKeySchema, upsertIssueDocumentSchema, createLibraryDocumentSchema, updateLibraryDocumentSchema, restoreLibraryDocumentRevisionSchema;
1711
+ var runWorkspaceStrategySchema, issueRunWorkspaceSettingsSchema, issueAssigneeAdapterOverridesSchema, createIssueSchema, createIssueLabelSchema, updateIssueLabelSchema, updateIssueSchema, reorderIssueSchema, checkoutIssueSchema, addIssueCommentSchema, updateIssueCommentSchema, reportIssueCommitSchema, linkIssueApprovalSchema, createIssueAttachmentMetadataSchema, createIssueWorkspaceAttachmentSchema, ISSUE_DOCUMENT_FORMATS, issueDocumentFormatSchema, createLibraryDocumentSchema, updateLibraryDocumentSchema, restoreLibraryDocumentRevisionSchema;
1653
1712
  var init_issue = __esm({
1654
1713
  "../packages/shared/dist/validators/issue.js"() {
1655
1714
  "use strict";
1656
1715
  init_constants();
1657
- executionWorkspaceStrategySchema2 = z14.object({
1716
+ runWorkspaceStrategySchema = z14.object({
1658
1717
  type: z14.enum(["project_primary", "git_worktree", "adapter_managed", "cloud_sandbox"]).optional(),
1659
1718
  baseRef: z14.string().optional().nullable(),
1660
1719
  branchTemplate: z14.string().optional().nullable(),
@@ -1662,9 +1721,9 @@ var init_issue = __esm({
1662
1721
  provisionCommand: z14.string().optional().nullable(),
1663
1722
  teardownCommand: z14.string().optional().nullable()
1664
1723
  }).strict();
1665
- issueExecutionWorkspaceSettingsSchema = z14.object({
1724
+ issueRunWorkspaceSettingsSchema = z14.object({
1666
1725
  mode: z14.enum(["inherit", "shared_workspace", "isolated_workspace", "operator_branch", "reuse_existing", "agent_default"]).optional(),
1667
- workspaceStrategy: executionWorkspaceStrategySchema2.optional().nullable(),
1726
+ workspaceStrategy: runWorkspaceStrategySchema.optional().nullable(),
1668
1727
  workspaceRuntime: z14.record(z14.unknown()).optional().nullable()
1669
1728
  }).strict();
1670
1729
  issueAssigneeAdapterOverridesSchema = z14.object({
@@ -1687,7 +1746,19 @@ var init_issue = __esm({
1687
1746
  requestDepth: z14.number().int().nonnegative().optional().default(0),
1688
1747
  billingCode: z14.string().optional().nullable(),
1689
1748
  assigneeAgentRuntimeOverrides: issueAssigneeAdapterOverridesSchema.optional().nullable(),
1749
+ runWorkspaceId: z14.string().uuid().optional().nullable(),
1750
+ runWorkspacePreference: z14.enum([
1751
+ "inherit",
1752
+ "shared_workspace",
1753
+ "isolated_workspace",
1754
+ "operator_branch",
1755
+ "reuse_existing",
1756
+ "agent_default"
1757
+ ]).optional().nullable(),
1758
+ runWorkspaceSettings: issueRunWorkspaceSettingsSchema.optional().nullable(),
1759
+ /** @deprecated Use runWorkspaceId. */
1690
1760
  executionWorkspaceId: z14.string().uuid().optional().nullable(),
1761
+ /** @deprecated Use runWorkspacePreference. */
1691
1762
  executionWorkspacePreference: z14.enum([
1692
1763
  "inherit",
1693
1764
  "shared_workspace",
@@ -1696,7 +1767,8 @@ var init_issue = __esm({
1696
1767
  "reuse_existing",
1697
1768
  "agent_default"
1698
1769
  ]).optional().nullable(),
1699
- executionWorkspaceSettings: issueExecutionWorkspaceSettingsSchema.optional().nullable(),
1770
+ /** @deprecated Use runWorkspaceSettings. */
1771
+ executionWorkspaceSettings: issueRunWorkspaceSettingsSchema.optional().nullable(),
1700
1772
  labelIds: z14.array(z14.string().uuid()).optional()
1701
1773
  });
1702
1774
  createIssueLabelSchema = z14.object({
@@ -1753,14 +1825,6 @@ var init_issue = __esm({
1753
1825
  });
1754
1826
  ISSUE_DOCUMENT_FORMATS = ["markdown"];
1755
1827
  issueDocumentFormatSchema = z14.enum(ISSUE_DOCUMENT_FORMATS);
1756
- issueDocumentKeySchema = z14.string().trim().min(1).max(64).regex(/^[a-z0-9][a-z0-9_-]*$/, "Document key must be lowercase letters, numbers, _ or -");
1757
- upsertIssueDocumentSchema = z14.object({
1758
- title: z14.string().trim().max(200).nullable().optional(),
1759
- format: issueDocumentFormatSchema,
1760
- body: z14.string().max(524288),
1761
- changeSummary: z14.string().trim().max(500).nullable().optional(),
1762
- baseRevisionId: z14.string().uuid().nullable().optional()
1763
- });
1764
1828
  createLibraryDocumentSchema = z14.object({
1765
1829
  title: z14.string().trim().max(200).nullable().optional(),
1766
1830
  format: issueDocumentFormatSchema.optional().default("markdown"),
@@ -1814,6 +1878,8 @@ var init_work_product = __esm({
1814
1878
  ]);
1815
1879
  createIssueWorkProductSchema = z15.object({
1816
1880
  projectId: z15.string().uuid().optional().nullable(),
1881
+ runWorkspaceId: z15.string().uuid().optional().nullable(),
1882
+ /** @deprecated Use runWorkspaceId. */
1817
1883
  executionWorkspaceId: z15.string().uuid().optional().nullable(),
1818
1884
  runtimeServiceId: z15.string().uuid().optional().nullable(),
1819
1885
  type: issueWorkProductTypeSchema,
@@ -1835,19 +1901,19 @@ var init_work_product = __esm({
1835
1901
 
1836
1902
  // ../packages/shared/dist/validators/execution-workspace.js
1837
1903
  import { z as z16 } from "zod";
1838
- var executionWorkspaceStatusSchema, updateExecutionWorkspaceSchema;
1904
+ var runWorkspaceStatusSchema, updateRunWorkspaceSchema;
1839
1905
  var init_execution_workspace = __esm({
1840
1906
  "../packages/shared/dist/validators/execution-workspace.js"() {
1841
1907
  "use strict";
1842
- executionWorkspaceStatusSchema = z16.enum([
1908
+ runWorkspaceStatusSchema = z16.enum([
1843
1909
  "active",
1844
1910
  "idle",
1845
1911
  "in_review",
1846
1912
  "archived",
1847
1913
  "cleanup_failed"
1848
1914
  ]);
1849
- updateExecutionWorkspaceSchema = z16.object({
1850
- status: executionWorkspaceStatusSchema.optional(),
1915
+ updateRunWorkspaceSchema = z16.object({
1916
+ status: runWorkspaceStatusSchema.optional(),
1851
1917
  cleanupEligibleAt: z16.string().datetime().optional().nullable(),
1852
1918
  cleanupReason: z16.string().optional().nullable(),
1853
1919
  metadata: z16.record(z16.unknown()).optional().nullable()
@@ -1923,22 +1989,31 @@ var init_approval = __esm({
1923
1989
 
1924
1990
  // ../packages/shared/dist/validators/automation.js
1925
1991
  import { z as z20 } from "zod";
1992
+ function normalizeAutomationInstructions(value) {
1993
+ const { instructions, ...rest } = value;
1994
+ return {
1995
+ ...rest,
1996
+ description: instructions === void 0 ? value.description : instructions
1997
+ };
1998
+ }
1926
1999
  function normalizeAutomationNotifications(value) {
1927
2000
  if (value.outputMode !== "chat_output" || !value.notifyOnIssueCreated)
1928
2001
  return value;
1929
2002
  return { ...value, notifyOnIssueCreated: false };
1930
2003
  }
1931
- var automationBodySchema, createAutomationSchema, updateAutomationSchema, baseTriggerSchema, createAutomationTriggerSchema, updateAutomationTriggerSchema, runAutomationSchema, rotateAutomationTriggerSecretSchema;
2004
+ var automationTextFieldSchema, automationBodyFieldsSchema, createAutomationSchema, updateAutomationSchema, baseTriggerSchema, createAutomationTriggerSchema, updateAutomationTriggerSchema, runAutomationSchema, rotateAutomationTriggerSecretSchema;
1932
2005
  var init_automation = __esm({
1933
2006
  "../packages/shared/dist/validators/automation.js"() {
1934
2007
  "use strict";
1935
2008
  init_constants();
1936
- automationBodySchema = z20.object({
2009
+ automationTextFieldSchema = z20.string().optional().nullable();
2010
+ automationBodyFieldsSchema = z20.object({
1937
2011
  projectId: z20.string().uuid().optional().nullable().default(null),
1938
2012
  goalId: z20.string().uuid().optional().nullable(),
1939
2013
  parentIssueId: z20.string().uuid().optional().nullable(),
1940
2014
  title: z20.string().trim().min(1).max(200),
1941
- description: z20.string().optional().nullable(),
2015
+ instructions: automationTextFieldSchema,
2016
+ description: automationTextFieldSchema,
1942
2017
  assigneeAgentId: z20.string().uuid(),
1943
2018
  priority: z20.enum(ISSUE_PRIORITIES).optional().default("medium"),
1944
2019
  status: z20.enum(AUTOMATION_STATUSES).optional().default("active"),
@@ -1948,7 +2023,7 @@ var init_automation = __esm({
1948
2023
  chatConversationId: z20.string().uuid().optional().nullable().default(null),
1949
2024
  notifyOnIssueCreated: z20.boolean().optional().default(false)
1950
2025
  });
1951
- createAutomationSchema = automationBodySchema.superRefine((value, ctx) => {
2026
+ createAutomationSchema = automationBodyFieldsSchema.superRefine((value, ctx) => {
1952
2027
  if (value.chatConversationId) {
1953
2028
  ctx.addIssue({
1954
2029
  code: z20.ZodIssueCode.custom,
@@ -1956,8 +2031,8 @@ var init_automation = __esm({
1956
2031
  message: "Chat output creates an automation-owned conversation; existing chats cannot be selected"
1957
2032
  });
1958
2033
  }
1959
- }).transform(normalizeAutomationNotifications);
1960
- updateAutomationSchema = automationBodySchema.partial().superRefine((value, ctx) => {
2034
+ }).transform(normalizeAutomationInstructions).transform(normalizeAutomationNotifications);
2035
+ updateAutomationSchema = automationBodyFieldsSchema.partial().superRefine((value, ctx) => {
1961
2036
  if (value.chatConversationId) {
1962
2037
  ctx.addIssue({
1963
2038
  code: z20.ZodIssueCode.custom,
@@ -1965,7 +2040,7 @@ var init_automation = __esm({
1965
2040
  message: "Chat output creates an automation-owned conversation; existing chats cannot be selected"
1966
2041
  });
1967
2042
  }
1968
- }).transform(normalizeAutomationNotifications);
2043
+ }).transform(normalizeAutomationInstructions).transform(normalizeAutomationNotifications);
1969
2044
  baseTriggerSchema = z20.object({
1970
2045
  label: z20.string().trim().max(120).optional().nullable(),
1971
2046
  enabled: z20.boolean().optional().default(true)
@@ -2713,7 +2788,7 @@ var init_organization_skill_reference = __esm({
2713
2788
  });
2714
2789
 
2715
2790
  // ../packages/shared/dist/project-mentions.js
2716
- var AGENT_AVATAR_UUID_RE, AGENT_ASSET_ICON_RE, AGENT_DICEBEAR_NOTIONISTS_ICON_RE, PROJECT_COLOR_VALUES, AGENT_AVATAR_BACKGROUND_VALUES;
2791
+ var AGENT_AVATAR_UUID_RE, AGENT_ASSET_ICON_RE, AGENT_DICEBEAR_NOTIONISTS_ICON_RE, PROJECT_COLOR_VALUES, PROJECT_ICON_VALUES, AGENT_AVATAR_BACKGROUND_VALUES;
2717
2792
  var init_project_mentions = __esm({
2718
2793
  "../packages/shared/dist/project-mentions.js"() {
2719
2794
  "use strict";
@@ -2722,12 +2797,13 @@ var init_project_mentions = __esm({
2722
2797
  AGENT_ASSET_ICON_RE = new RegExp(`^asset:${AGENT_AVATAR_UUID_RE}(?:\\?bg=([a-z0-9-]+))?$`, "i");
2723
2798
  AGENT_DICEBEAR_NOTIONISTS_ICON_RE = new RegExp(`^${AGENT_DICEBEAR_NOTIONISTS_ICON_PREFIX}${AGENT_AVATAR_UUID_RE}(?:\\?bg=([a-z0-9-]+))?$`, "i");
2724
2799
  PROJECT_COLOR_VALUES = new Set(PROJECT_COLORS);
2800
+ PROJECT_ICON_VALUES = new Set(PROJECT_ICONS);
2725
2801
  AGENT_AVATAR_BACKGROUND_VALUES = new Set(AGENT_AVATAR_BACKGROUND_PRESET_IDS);
2726
2802
  }
2727
2803
  });
2728
2804
 
2729
2805
  // ../packages/shared/dist/issue-activity.js
2730
- var ISSUE_UPDATE_ACTIVITY_METADATA_KEYS, LOW_SIGNAL_ISSUE_UPDATE_ACTIVITY_FIELDS, ISSUE_UPDATE_ACTIVITY_METADATA_KEY_SET, LOW_SIGNAL_ISSUE_UPDATE_ACTIVITY_FIELD_SET;
2806
+ var ISSUE_UPDATE_ACTIVITY_METADATA_KEYS, LOW_SIGNAL_ISSUE_UPDATE_ACTIVITY_FIELDS, INTERNAL_ISSUE_UPDATE_ACTIVITY_FIELDS, ISSUE_UPDATE_ACTIVITY_METADATA_KEY_SET, LOW_SIGNAL_ISSUE_UPDATE_ACTIVITY_FIELD_SET, INTERNAL_ISSUE_UPDATE_ACTIVITY_FIELD_SET;
2731
2807
  var init_issue_activity = __esm({
2732
2808
  "../packages/shared/dist/issue-activity.js"() {
2733
2809
  "use strict";
@@ -2743,8 +2819,19 @@ var init_issue_activity = __esm({
2743
2819
  "normalizedReason"
2744
2820
  ];
2745
2821
  LOW_SIGNAL_ISSUE_UPDATE_ACTIVITY_FIELDS = ["description", "title"];
2822
+ INTERNAL_ISSUE_UPDATE_ACTIVITY_FIELDS = [
2823
+ "executionWorkspaceId",
2824
+ "executionWorkspacePreference",
2825
+ "executionWorkspaceSettings",
2826
+ "currentExecutionWorkspace",
2827
+ "runWorkspaceId",
2828
+ "runWorkspacePreference",
2829
+ "runWorkspaceSettings",
2830
+ "currentRunWorkspace"
2831
+ ];
2746
2832
  ISSUE_UPDATE_ACTIVITY_METADATA_KEY_SET = new Set(ISSUE_UPDATE_ACTIVITY_METADATA_KEYS);
2747
2833
  LOW_SIGNAL_ISSUE_UPDATE_ACTIVITY_FIELD_SET = new Set(LOW_SIGNAL_ISSUE_UPDATE_ACTIVITY_FIELDS);
2834
+ INTERNAL_ISSUE_UPDATE_ACTIVITY_FIELD_SET = new Set(INTERNAL_ISSUE_UPDATE_ACTIVITY_FIELDS);
2748
2835
  }
2749
2836
  });
2750
2837
 
@@ -10665,54 +10752,6 @@ var AGENT_CLI_CAPABILITIES = [
10665
10752
  requiresRunId: false,
10666
10753
  attachesRunIdWhenAvailable: true
10667
10754
  },
10668
- {
10669
- id: "issue.documents.list",
10670
- command: "rudder issue documents list <issue>",
10671
- category: "issue",
10672
- description: "List issue documents.",
10673
- mutating: false,
10674
- contract: "agent-v1",
10675
- requiresOrgId: false,
10676
- requiresAgentId: false,
10677
- requiresRunId: false,
10678
- attachesRunIdWhenAvailable: false
10679
- },
10680
- {
10681
- id: "issue.documents.get",
10682
- command: "rudder issue documents get <issue> <key>",
10683
- category: "issue",
10684
- description: "Read one issue document by key.",
10685
- mutating: false,
10686
- contract: "agent-v1",
10687
- requiresOrgId: false,
10688
- requiresAgentId: false,
10689
- requiresRunId: false,
10690
- attachesRunIdWhenAvailable: false
10691
- },
10692
- {
10693
- id: "issue.documents.put",
10694
- command: "rudder issue documents put <issue> <key> --body-file <path>",
10695
- category: "issue",
10696
- description: "Legacy create or update of a DB-backed issue document; prefer `rudder library file put` for durable project files.",
10697
- mutating: true,
10698
- contract: "compat",
10699
- requiresOrgId: false,
10700
- requiresAgentId: false,
10701
- requiresRunId: false,
10702
- attachesRunIdWhenAvailable: true
10703
- },
10704
- {
10705
- id: "issue.documents.revisions",
10706
- command: "rudder issue documents revisions <issue> <key>",
10707
- category: "issue",
10708
- description: "List revisions for an issue document.",
10709
- mutating: false,
10710
- contract: "agent-v1",
10711
- requiresOrgId: false,
10712
- requiresAgentId: false,
10713
- requiresRunId: false,
10714
- attachesRunIdWhenAvailable: false
10715
- },
10716
10755
  {
10717
10756
  id: "project.list",
10718
10757
  command: "rudder project list --org-id <id>",
@@ -10765,7 +10804,7 @@ var AGENT_CLI_CAPABILITIES = [
10765
10804
  id: "library.file.list",
10766
10805
  command: "rudder library file list [directory]",
10767
10806
  category: "library",
10768
- description: "List path-based Library files and folders.",
10807
+ description: "List Library files and folders; file rows include `libraryEntryId` when a strong reference can be generated.",
10769
10808
  mutating: false,
10770
10809
  contract: "agent-v1",
10771
10810
  requiresOrgId: true,
@@ -10777,7 +10816,31 @@ var AGENT_CLI_CAPABILITIES = [
10777
10816
  id: "library.file.get",
10778
10817
  command: "rudder library file get <path>",
10779
10818
  category: "library",
10780
- description: "Read one path-based Library file.",
10819
+ description: "Fallback read when local filesystem access is unavailable; JSON includes `mentionHref` and `markdownLink`.",
10820
+ mutating: false,
10821
+ contract: "agent-v1",
10822
+ requiresOrgId: true,
10823
+ requiresAgentId: false,
10824
+ requiresRunId: false,
10825
+ attachesRunIdWhenAvailable: false
10826
+ },
10827
+ {
10828
+ id: "library.file.ref",
10829
+ command: "rudder library file ref <path>",
10830
+ category: "library",
10831
+ description: "Return the stable Markdown reference for one Library file without printing file content.",
10832
+ mutating: false,
10833
+ contract: "agent-v1",
10834
+ requiresOrgId: true,
10835
+ requiresAgentId: false,
10836
+ requiresRunId: false,
10837
+ attachesRunIdWhenAvailable: false
10838
+ },
10839
+ {
10840
+ id: "library.file.link",
10841
+ command: "rudder library file link <path>",
10842
+ category: "library",
10843
+ description: "Compatibility alias for `rudder library file ref <path>`.",
10781
10844
  mutating: false,
10782
10845
  contract: "agent-v1",
10783
10846
  requiresOrgId: true,
@@ -10789,7 +10852,7 @@ var AGENT_CLI_CAPABILITIES = [
10789
10852
  id: "library.file.put",
10790
10853
  command: "rudder library file put <path> --body-file <path>",
10791
10854
  category: "library",
10792
- description: "Create or update one path-based Library file.",
10855
+ description: "Fallback create/update when local filesystem access is unavailable; JSON includes `mentionHref` and `markdownLink`.",
10793
10856
  mutating: true,
10794
10857
  contract: "agent-v1",
10795
10858
  requiresOrgId: true,
@@ -10952,403 +11015,1493 @@ var AGENT_CLI_CAPABILITIES = [
10952
11015
  requiresAgentId: false,
10953
11016
  requiresRunId: false,
10954
11017
  attachesRunIdWhenAvailable: true
10955
- }
10956
- ];
10957
- var CATEGORY_TITLES = {
10958
- agent: "Agent",
10959
- issue: "Issue",
10960
- project: "Project",
10961
- approval: "Approval",
10962
- skill: "Skill",
10963
- library: "Library"
10964
- };
10965
- function getAgentCliCapabilities() {
10966
- return AGENT_CLI_CAPABILITIES.map((entry) => ({ ...entry }));
10967
- }
10968
- function getAgentCliCapabilityById(id) {
10969
- const entry = AGENT_CLI_CAPABILITIES.find((capability) => capability.id === id);
10970
- if (!entry) {
10971
- throw new Error(`Unknown agent CLI capability: ${id}`);
10972
- }
10973
- return entry;
10974
- }
10975
- function buildAgentCliCapabilitiesManifest(contract = "agent-v1") {
10976
- const capabilities = AGENT_CLI_CAPABILITIES.filter((entry) => contract === "all" || entry.contract === contract).map((entry) => ({
10977
- ...entry,
10978
- agentV1: entry.contract === "agent-v1"
10979
- }));
10980
- return {
10981
- schema: "rudder.agent-capabilities/v1",
10982
- contract,
10983
- defaults: {
10984
- orgIdEnvVar: "RUDDER_ORG_ID",
10985
- agentIdEnvVar: "RUDDER_AGENT_ID",
10986
- runIdEnvVar: "RUDDER_RUN_ID",
10987
- jsonErrors: "stderr-error-envelope"
10988
- },
10989
- capabilities
10990
- };
10991
- }
10992
- function formatAgentCliCapabilitiesHumanReadable(capabilities = getAgentCliCapabilities()) {
10993
- const lines = [];
10994
- for (const category of Object.keys(CATEGORY_TITLES)) {
10995
- const entries = capabilities.filter((capability) => capability.category === category);
10996
- if (entries.length === 0) continue;
10997
- lines.push(`${CATEGORY_TITLES[category]} commands:`);
10998
- for (const entry of entries) {
10999
- const tags = [
11000
- entry.contract,
11001
- entry.mutating ? "mutating" : "read-only",
11002
- entry.requiresOrgId ? "org" : null,
11003
- entry.requiresAgentId ? "agent" : null,
11004
- entry.attachesRunIdWhenAvailable ? "run-id" : null
11005
- ].filter(Boolean);
11006
- lines.push(`- ${entry.command} \u2014 ${entry.description} [${tags.join(", ")}]`);
11007
- }
11008
- lines.push("");
11009
- }
11010
- return lines.join("\n").trimEnd();
11011
- }
11012
-
11013
- // src/commands/client/issue.ts
11014
- function registerIssueCommands(program) {
11015
- const issue = program.command("issue").description("Issue operations");
11016
- addCommonClientOptions(
11017
- issue.command("list").description("List issues for an organization").option("-O, --org-id <id>", "Organization ID").option("--status <csv>", "Comma-separated statuses").option("--assignee-agent-id <id>", "Filter by assignee agent ID").option("--project-id <id>", "Filter by project ID").option("--query <text>", "Server-side search on identifier/title/description/comments").option("--match <text>", "Local text match on identifier/title/description").action(async (opts) => {
11018
- try {
11019
- const ctx = resolveCommandContext(opts, { requireCompany: true });
11020
- const rows = await ctx.api.get(buildIssueListPath(ctx.orgId, opts, opts.query)) ?? [];
11021
- const filtered = filterIssueRows(rows, opts.match);
11022
- printIssueRows(filtered, ctx.json);
11023
- } catch (err) {
11024
- handleCommandError(err);
11025
- }
11026
- }),
11027
- { includeCompany: false }
11028
- );
11029
- addCommonClientOptions(
11030
- issue.command("search").description(getAgentCliCapabilityById("issue.search").description).argument("<query>", "Server-side issue search query").option("-O, --org-id <id>", "Organization ID").option("--status <csv>", "Comma-separated statuses").option("--assignee-agent-id <id>", "Filter by assignee agent ID").option("--project-id <id>", "Filter by project ID").action(async (query, opts) => {
11031
- try {
11032
- const ctx = resolveCommandContext(opts, { requireCompany: true });
11033
- const rows = await ctx.api.get(buildIssueListPath(ctx.orgId, opts, query)) ?? [];
11034
- printIssueRows(rows, ctx.json);
11035
- } catch (err) {
11036
- handleCommandError(err);
11037
- }
11038
- }),
11039
- { includeCompany: false }
11040
- );
11041
- addCommonClientOptions(
11042
- issue.command("get").description(getAgentCliCapabilityById("issue.get").description).argument("<idOrIdentifier>", "Issue ID or identifier").action(async (idOrIdentifier, opts) => {
11043
- try {
11044
- const ctx = resolveCommandContext(opts);
11045
- const row = await ctx.api.get(`/api/issues/${idOrIdentifier}`);
11046
- printOutput(row, { json: ctx.json });
11047
- } catch (err) {
11048
- handleCommandError(err);
11049
- }
11050
- })
11051
- );
11052
- addCommonClientOptions(
11053
- issue.command("context").description(getAgentCliCapabilityById("issue.context").description).argument("<issueId>", "Issue ID or identifier").option("--wake-comment-id <id>", "Fetch one wake comment in the heartbeat context response").action(async (issueId, opts) => {
11054
- try {
11055
- const ctx = resolveCommandContext(opts);
11056
- const params = new URLSearchParams();
11057
- if (opts.wakeCommentId) params.set("wakeCommentId", opts.wakeCommentId);
11058
- const query = params.toString();
11059
- const row = await ctx.api.get(
11060
- `/api/issues/${issueId}/heartbeat-context${query ? `?${query}` : ""}`
11061
- );
11062
- printOutput(row, { json: ctx.json });
11063
- } catch (err) {
11064
- handleCommandError(err);
11065
- }
11066
- })
11067
- );
11068
- addCommonClientOptions(
11069
- issue.command("labels").description("Issue label operations").command("list").description("List issue labels for an organization").option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
11070
- try {
11071
- const ctx = resolveCommandContext(opts, { requireCompany: true });
11072
- const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/labels`) ?? [];
11073
- printOutput(rows, { json: ctx.json });
11074
- } catch (err) {
11075
- handleCommandError(err);
11076
- }
11077
- }),
11078
- { includeCompany: false }
11079
- );
11080
- addCommonClientOptions(
11081
- issue.command("create").description(getAgentCliCapabilityById("issue.create").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--title <title>", "Issue title").option("--description <text>", "Issue description").option("--status <status>", "Issue status").option("--priority <priority>", "Issue priority").option("--assignee-agent-id <id>", "Assignee agent ID").option("--project-id <id>", "Project ID").option("--goal-id <id>", "Goal ID").option("--parent-id <id>", "Parent issue ID").option("--request-depth <n>", "Request depth integer").option("--billing-code <code>", "Billing code").option("--label-id <id>", "Issue label ID; may be repeated", collectNonEmptyOption("--label-id"), []).option("--label <name>", "Issue label name to resolve exactly; may be repeated", collectNonEmptyOption("--label"), []).action(async (opts) => {
11082
- try {
11083
- const ctx = resolveCommandContext(opts, { requireCompany: true });
11084
- const labelIds = await resolveIssueLabelIds(ctx, opts);
11085
- const payload = createIssueSchema.parse({
11086
- title: opts.title,
11087
- description: opts.description,
11088
- status: opts.status,
11089
- priority: opts.priority,
11090
- assigneeAgentId: opts.assigneeAgentId,
11091
- projectId: opts.projectId,
11092
- goalId: opts.goalId,
11093
- parentId: opts.parentId,
11094
- requestDepth: parseOptionalInt(opts.requestDepth),
11095
- billingCode: opts.billingCode,
11096
- labelIds: labelIds.length > 0 ? labelIds : void 0
11097
- });
11098
- const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/issues`, payload);
11099
- printOutput(created, { json: ctx.json });
11100
- } catch (err) {
11101
- handleCommandError(err);
11102
- }
11103
- }),
11104
- { includeCompany: false }
11105
- );
11106
- addCommonClientOptions(
11107
- issue.command("update").description(getAgentCliCapabilityById("issue.update").description).argument("<issueId>", "Issue ID").option("--title <title>", "Issue title").option("--description <text>", "Issue description").option("--status <status>", "Issue status").option("--priority <priority>", "Issue priority").option("--assignee-agent-id <id>", "Assignee agent ID").option("--project-id <id>", "Project ID").option("--goal-id <id>", "Goal ID").option("--parent-id <id>", "Parent issue ID").option("--request-depth <n>", "Request depth integer").option("--billing-code <code>", "Billing code").option("--comment-file <path>", "Read optional update comment from a file, or '-' for stdin").option("--image <path>", "Image file to upload and append to the update comment; may be repeated", collectImagePath, []).option("--hidden-at <iso8601|null>", "Set hiddenAt timestamp or literal 'null'").action(async (issueId, opts) => {
11108
- try {
11109
- const ctx = resolveCommandContext(opts);
11110
- const commentText = await resolveFileTextInput({
11111
- file: opts.commentFile,
11112
- fileOption: "--comment-file",
11113
- removedTextOption: "--comment",
11114
- required: false
11115
- });
11116
- const comment = await appendUploadedIssueImages(ctx, issueId, commentText, opts.image);
11117
- const payload = updateIssueSchema.parse({
11118
- title: opts.title,
11119
- description: opts.description,
11018
+ },
11019
+ {
11020
+ id: "automation.list",
11021
+ command: "rudder automation list --org-id <id>",
11022
+ category: "automation",
11023
+ description: "List automations for an organization with compact local filters.",
11024
+ mutating: false,
11025
+ contract: "agent-v1",
11026
+ requiresOrgId: true,
11027
+ requiresAgentId: false,
11028
+ requiresRunId: false,
11029
+ attachesRunIdWhenAvailable: false
11030
+ },
11031
+ {
11032
+ id: "automation.get",
11033
+ command: "rudder automation get <automation-id>",
11034
+ category: "automation",
11035
+ description: "Read one automation detail including triggers and recent runs.",
11036
+ mutating: false,
11037
+ contract: "agent-v1",
11038
+ requiresOrgId: false,
11039
+ requiresAgentId: false,
11040
+ requiresRunId: false,
11041
+ attachesRunIdWhenAvailable: false
11042
+ },
11043
+ {
11044
+ id: "automation.runs",
11045
+ command: "rudder automation runs <automation-id>",
11046
+ category: "automation",
11047
+ description: "List recent runs for one automation.",
11048
+ mutating: false,
11049
+ contract: "agent-v1",
11050
+ requiresOrgId: false,
11051
+ requiresAgentId: false,
11052
+ requiresRunId: false,
11053
+ attachesRunIdWhenAvailable: false
11054
+ },
11055
+ {
11056
+ id: "automation.triggers.list",
11057
+ command: "rudder automation triggers list <automation-id>",
11058
+ category: "automation",
11059
+ description: "List triggers configured for one automation.",
11060
+ mutating: false,
11061
+ contract: "agent-v1",
11062
+ requiresOrgId: false,
11063
+ requiresAgentId: false,
11064
+ requiresRunId: false,
11065
+ attachesRunIdWhenAvailable: false
11066
+ },
11067
+ {
11068
+ id: "automation.create",
11069
+ command: "rudder automation create --org-id <id> --title <title> --assignee-agent-id <id>",
11070
+ category: "automation",
11071
+ description: "Create an automation through the governed automation API.",
11072
+ mutating: true,
11073
+ contract: "agent-v1",
11074
+ requiresOrgId: true,
11075
+ requiresAgentId: false,
11076
+ requiresRunId: false,
11077
+ attachesRunIdWhenAvailable: true
11078
+ },
11079
+ {
11080
+ id: "automation.update",
11081
+ command: "rudder automation update <automation-id>",
11082
+ category: "automation",
11083
+ description: "Update automation fields through the governed automation API.",
11084
+ mutating: true,
11085
+ contract: "agent-v1",
11086
+ requiresOrgId: false,
11087
+ requiresAgentId: false,
11088
+ requiresRunId: false,
11089
+ attachesRunIdWhenAvailable: true
11090
+ },
11091
+ {
11092
+ id: "automation.enable",
11093
+ command: "rudder automation enable <automation-id>",
11094
+ category: "automation",
11095
+ description: "Enable an automation by setting status to active.",
11096
+ mutating: true,
11097
+ contract: "agent-v1",
11098
+ requiresOrgId: false,
11099
+ requiresAgentId: false,
11100
+ requiresRunId: false,
11101
+ attachesRunIdWhenAvailable: true
11102
+ },
11103
+ {
11104
+ id: "automation.disable",
11105
+ command: "rudder automation disable <automation-id>",
11106
+ category: "automation",
11107
+ description: "Disable an automation by setting status to paused.",
11108
+ mutating: true,
11109
+ contract: "agent-v1",
11110
+ requiresOrgId: false,
11111
+ requiresAgentId: false,
11112
+ requiresRunId: false,
11113
+ attachesRunIdWhenAvailable: true
11114
+ },
11115
+ {
11116
+ id: "automation.run",
11117
+ command: "rudder automation run <automation-id>",
11118
+ category: "automation",
11119
+ description: "Trigger a manual automation run.",
11120
+ mutating: true,
11121
+ contract: "agent-v1",
11122
+ requiresOrgId: false,
11123
+ requiresAgentId: false,
11124
+ requiresRunId: false,
11125
+ attachesRunIdWhenAvailable: true
11126
+ },
11127
+ {
11128
+ id: "chat.list",
11129
+ command: "rudder chat list --org-id <id>",
11130
+ category: "chat",
11131
+ description: "List chat conversations without dumping full message history.",
11132
+ mutating: false,
11133
+ contract: "agent-v1",
11134
+ requiresOrgId: true,
11135
+ requiresAgentId: false,
11136
+ requiresRunId: false,
11137
+ attachesRunIdWhenAvailable: false
11138
+ },
11139
+ {
11140
+ id: "chat.search",
11141
+ command: "rudder chat search <query> --org-id <id>",
11142
+ category: "chat",
11143
+ description: "Search chats with bounded snippets and optional scope filtering.",
11144
+ mutating: false,
11145
+ contract: "agent-v1",
11146
+ requiresOrgId: true,
11147
+ requiresAgentId: false,
11148
+ requiresRunId: false,
11149
+ attachesRunIdWhenAvailable: false
11150
+ },
11151
+ {
11152
+ id: "chat.get",
11153
+ command: "rudder chat get <chat-id>",
11154
+ category: "chat",
11155
+ description: "Read one chat conversation record.",
11156
+ mutating: false,
11157
+ contract: "agent-v1",
11158
+ requiresOrgId: false,
11159
+ requiresAgentId: false,
11160
+ requiresRunId: false,
11161
+ attachesRunIdWhenAvailable: false
11162
+ },
11163
+ {
11164
+ id: "chat.messages",
11165
+ command: "rudder chat messages <chat-id> [--limit <n>] [--cursor <cursor>] [--include-transcript]",
11166
+ category: "chat",
11167
+ description: "Read bounded chat messages with page cursors; transcript output is omitted unless requested.",
11168
+ mutating: false,
11169
+ contract: "agent-v1",
11170
+ requiresOrgId: false,
11171
+ requiresAgentId: false,
11172
+ requiresRunId: false,
11173
+ attachesRunIdWhenAvailable: false
11174
+ },
11175
+ {
11176
+ id: "chat.transcript",
11177
+ command: "rudder chat transcript <chat-id> [--limit <n>] [--cursor <cursor>] [--max-output-chars <n>]",
11178
+ category: "chat",
11179
+ description: "Read paginated chat messages with assistant transcript entries clipped in human output.",
11180
+ mutating: false,
11181
+ contract: "agent-v1",
11182
+ requiresOrgId: false,
11183
+ requiresAgentId: false,
11184
+ requiresRunId: false,
11185
+ attachesRunIdWhenAvailable: false
11186
+ },
11187
+ {
11188
+ id: "chat.read",
11189
+ command: "rudder chat read <chat-id> [--turn-limit <n>] [--cursor <cursor>] [--include-output]",
11190
+ category: "chat",
11191
+ description: "Read a bounded recent-message snapshot for one chat with page cursors.",
11192
+ mutating: false,
11193
+ contract: "agent-v1",
11194
+ requiresOrgId: false,
11195
+ requiresAgentId: false,
11196
+ requiresRunId: false,
11197
+ attachesRunIdWhenAvailable: false
11198
+ },
11199
+ {
11200
+ id: "chat.create",
11201
+ command: "rudder chat create --org-id <id>",
11202
+ category: "chat",
11203
+ description: "Create a chat conversation.",
11204
+ mutating: true,
11205
+ contract: "agent-v1",
11206
+ requiresOrgId: true,
11207
+ requiresAgentId: false,
11208
+ requiresRunId: false,
11209
+ attachesRunIdWhenAvailable: true
11210
+ },
11211
+ {
11212
+ id: "chat.send",
11213
+ command: "rudder chat send <chat-id> --body <text>",
11214
+ category: "chat",
11215
+ description: "Send a chat message and persist the assistant reply through the server.",
11216
+ mutating: true,
11217
+ contract: "agent-v1",
11218
+ requiresOrgId: false,
11219
+ requiresAgentId: false,
11220
+ requiresRunId: false,
11221
+ attachesRunIdWhenAvailable: true
11222
+ },
11223
+ {
11224
+ id: "chat.archive",
11225
+ command: "rudder chat archive <chat-id>",
11226
+ category: "chat",
11227
+ description: "Archive a chat conversation without deleting it.",
11228
+ mutating: true,
11229
+ contract: "agent-v1",
11230
+ requiresOrgId: false,
11231
+ requiresAgentId: false,
11232
+ requiresRunId: false,
11233
+ attachesRunIdWhenAvailable: true
11234
+ },
11235
+ {
11236
+ id: "runs.list",
11237
+ command: "rudder runs list --org-id <id>",
11238
+ category: "runs",
11239
+ description: "List observed agent runs with filters for status, agent, issue, runtime, and time.",
11240
+ mutating: false,
11241
+ contract: "agent-v1",
11242
+ requiresOrgId: true,
11243
+ requiresAgentId: false,
11244
+ requiresRunId: false,
11245
+ attachesRunIdWhenAvailable: false
11246
+ },
11247
+ {
11248
+ id: "runs.get",
11249
+ command: "rudder runs get <run-id>",
11250
+ category: "runs",
11251
+ description: "Read one observed run detail.",
11252
+ mutating: false,
11253
+ contract: "agent-v1",
11254
+ requiresOrgId: false,
11255
+ requiresAgentId: false,
11256
+ requiresRunId: false,
11257
+ attachesRunIdWhenAvailable: false
11258
+ },
11259
+ {
11260
+ id: "runs.events",
11261
+ command: "rudder runs events <run-id>",
11262
+ category: "runs",
11263
+ description: "List persisted run events.",
11264
+ mutating: false,
11265
+ contract: "agent-v1",
11266
+ requiresOrgId: false,
11267
+ requiresAgentId: false,
11268
+ requiresRunId: false,
11269
+ attachesRunIdWhenAvailable: false
11270
+ },
11271
+ {
11272
+ id: "runs.log",
11273
+ command: "rudder runs log <run-id>",
11274
+ category: "runs",
11275
+ description: "Read stored run log content with clipped human output.",
11276
+ mutating: false,
11277
+ contract: "agent-v1",
11278
+ requiresOrgId: false,
11279
+ requiresAgentId: false,
11280
+ requiresRunId: false,
11281
+ attachesRunIdWhenAvailable: false
11282
+ },
11283
+ {
11284
+ id: "runs.transcript",
11285
+ command: "rudder runs transcript <run-id> [--turn-limit <n>] [--cursor <cursor>] [--include-output]",
11286
+ category: "runs",
11287
+ description: "Read the server-normalized run transcript; human output is compact and JSON includes full entries.",
11288
+ mutating: false,
11289
+ contract: "agent-v1",
11290
+ requiresOrgId: false,
11291
+ requiresAgentId: false,
11292
+ requiresRunId: false,
11293
+ attachesRunIdWhenAvailable: false
11294
+ },
11295
+ {
11296
+ id: "runs.errors",
11297
+ command: "rudder runs errors <run-id>",
11298
+ category: "runs",
11299
+ description: "List failed tool calls, stderr, runtime failures, and jump-to-context commands.",
11300
+ mutating: false,
11301
+ contract: "agent-v1",
11302
+ requiresOrgId: false,
11303
+ requiresAgentId: false,
11304
+ requiresRunId: false,
11305
+ attachesRunIdWhenAvailable: false
11306
+ },
11307
+ {
11308
+ id: "runs.cancel",
11309
+ command: "rudder runs cancel <run-id>",
11310
+ category: "runs",
11311
+ description: "Cancel a heartbeat run through the governed server route.",
11312
+ mutating: true,
11313
+ contract: "agent-v1",
11314
+ requiresOrgId: false,
11315
+ requiresAgentId: false,
11316
+ requiresRunId: false,
11317
+ attachesRunIdWhenAvailable: true
11318
+ },
11319
+ {
11320
+ id: "runs.retry",
11321
+ command: "rudder runs retry <run-id>",
11322
+ category: "runs",
11323
+ description: "Retry a failed, timed out, or cancelled run through the governed server route.",
11324
+ mutating: true,
11325
+ contract: "agent-v1",
11326
+ requiresOrgId: false,
11327
+ requiresAgentId: false,
11328
+ requiresRunId: false,
11329
+ attachesRunIdWhenAvailable: true
11330
+ }
11331
+ ];
11332
+ var CATEGORY_TITLES = {
11333
+ agent: "Agent",
11334
+ issue: "Issue",
11335
+ project: "Project",
11336
+ automation: "Automation",
11337
+ chat: "Chat",
11338
+ runs: "Runs",
11339
+ approval: "Approval",
11340
+ skill: "Skill",
11341
+ library: "Library"
11342
+ };
11343
+ function getAgentCliCapabilities() {
11344
+ return AGENT_CLI_CAPABILITIES.map((entry) => ({ ...entry }));
11345
+ }
11346
+ function getAgentCliCapabilityById(id) {
11347
+ const entry = AGENT_CLI_CAPABILITIES.find((capability) => capability.id === id);
11348
+ if (!entry) {
11349
+ throw new Error(`Unknown agent CLI capability: ${id}`);
11350
+ }
11351
+ return entry;
11352
+ }
11353
+ function buildAgentCliCapabilitiesManifest(contract = "agent-v1") {
11354
+ const capabilities = AGENT_CLI_CAPABILITIES.filter((entry) => contract === "all" || entry.contract === contract).map((entry) => ({
11355
+ ...entry,
11356
+ agentV1: entry.contract === "agent-v1"
11357
+ }));
11358
+ return {
11359
+ schema: "rudder.agent-capabilities/v1",
11360
+ contract,
11361
+ defaults: {
11362
+ orgIdEnvVar: "RUDDER_ORG_ID",
11363
+ agentIdEnvVar: "RUDDER_AGENT_ID",
11364
+ runIdEnvVar: "RUDDER_RUN_ID",
11365
+ jsonErrors: "stderr-error-envelope"
11366
+ },
11367
+ capabilities
11368
+ };
11369
+ }
11370
+ function formatAgentCliCapabilitiesHumanReadable(capabilities = getAgentCliCapabilities()) {
11371
+ const lines = [];
11372
+ for (const category of Object.keys(CATEGORY_TITLES)) {
11373
+ const entries = capabilities.filter((capability) => capability.category === category);
11374
+ if (entries.length === 0) continue;
11375
+ lines.push(`${CATEGORY_TITLES[category]} commands:`);
11376
+ for (const entry of entries) {
11377
+ const tags = [
11378
+ entry.contract,
11379
+ entry.mutating ? "mutating" : "read-only",
11380
+ entry.requiresOrgId ? "org" : null,
11381
+ entry.requiresAgentId ? "agent" : null,
11382
+ entry.attachesRunIdWhenAvailable ? "run-id" : null
11383
+ ].filter(Boolean);
11384
+ lines.push(`- ${entry.command} \u2014 ${entry.description} [${tags.join(", ")}]`);
11385
+ }
11386
+ lines.push("");
11387
+ }
11388
+ return lines.join("\n").trimEnd();
11389
+ }
11390
+
11391
+ // src/commands/client/issue.ts
11392
+ function registerIssueCommands(program) {
11393
+ const issue = program.command("issue").description("Issue operations");
11394
+ addCommonClientOptions(
11395
+ issue.command("list").description("List issues for an organization").option("-O, --org-id <id>", "Organization ID").option("--status <csv>", "Comma-separated statuses").option("--assignee-agent-id <id>", "Filter by assignee agent ID").option("--project-id <id>", "Filter by project ID").option("--query <text>", "Server-side search on identifier/title/description/comments").option("--match <text>", "Local text match on identifier/title/description").action(async (opts) => {
11396
+ try {
11397
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
11398
+ const rows = await ctx.api.get(buildIssueListPath(ctx.orgId, opts, opts.query)) ?? [];
11399
+ const filtered = filterIssueRows(rows, opts.match);
11400
+ printIssueRows(filtered, ctx.json);
11401
+ } catch (err) {
11402
+ handleCommandError(err);
11403
+ }
11404
+ }),
11405
+ { includeCompany: false }
11406
+ );
11407
+ addCommonClientOptions(
11408
+ issue.command("search").description(getAgentCliCapabilityById("issue.search").description).argument("<query>", "Server-side issue search query").option("-O, --org-id <id>", "Organization ID").option("--status <csv>", "Comma-separated statuses").option("--assignee-agent-id <id>", "Filter by assignee agent ID").option("--project-id <id>", "Filter by project ID").action(async (query, opts) => {
11409
+ try {
11410
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
11411
+ const rows = await ctx.api.get(buildIssueListPath(ctx.orgId, opts, query)) ?? [];
11412
+ printIssueRows(rows, ctx.json);
11413
+ } catch (err) {
11414
+ handleCommandError(err);
11415
+ }
11416
+ }),
11417
+ { includeCompany: false }
11418
+ );
11419
+ addCommonClientOptions(
11420
+ issue.command("get").description(getAgentCliCapabilityById("issue.get").description).argument("<idOrIdentifier>", "Issue ID or identifier").action(async (idOrIdentifier, opts) => {
11421
+ try {
11422
+ const ctx = resolveCommandContext(opts);
11423
+ const row = await ctx.api.get(`/api/issues/${idOrIdentifier}`);
11424
+ printOutput(row, { json: ctx.json });
11425
+ } catch (err) {
11426
+ handleCommandError(err);
11427
+ }
11428
+ })
11429
+ );
11430
+ addCommonClientOptions(
11431
+ issue.command("context").description(getAgentCliCapabilityById("issue.context").description).argument("<issueId>", "Issue ID or identifier").option("--wake-comment-id <id>", "Fetch one wake comment in the heartbeat context response").action(async (issueId, opts) => {
11432
+ try {
11433
+ const ctx = resolveCommandContext(opts);
11434
+ const params = new URLSearchParams();
11435
+ if (opts.wakeCommentId) params.set("wakeCommentId", opts.wakeCommentId);
11436
+ const query = params.toString();
11437
+ const row = await ctx.api.get(
11438
+ `/api/issues/${issueId}/heartbeat-context${query ? `?${query}` : ""}`
11439
+ );
11440
+ printOutput(row, { json: ctx.json });
11441
+ } catch (err) {
11442
+ handleCommandError(err);
11443
+ }
11444
+ })
11445
+ );
11446
+ addCommonClientOptions(
11447
+ issue.command("labels").description("Issue label operations").command("list").description("List issue labels for an organization").option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
11448
+ try {
11449
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
11450
+ const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/labels`) ?? [];
11451
+ printOutput(rows, { json: ctx.json });
11452
+ } catch (err) {
11453
+ handleCommandError(err);
11454
+ }
11455
+ }),
11456
+ { includeCompany: false }
11457
+ );
11458
+ addCommonClientOptions(
11459
+ issue.command("create").description(getAgentCliCapabilityById("issue.create").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--title <title>", "Issue title").option("--description <text>", "Issue description").option("--status <status>", "Issue status").option("--priority <priority>", "Issue priority").option("--assignee-agent-id <id>", "Assignee agent ID").option("--project-id <id>", "Project ID").option("--goal-id <id>", "Goal ID").option("--parent-id <id>", "Parent issue ID").option("--request-depth <n>", "Request depth integer").option("--billing-code <code>", "Billing code").option("--label-id <id>", "Issue label ID; may be repeated", collectNonEmptyOption("--label-id"), []).option("--label <name>", "Issue label name to resolve exactly; may be repeated", collectNonEmptyOption("--label"), []).action(async (opts) => {
11460
+ try {
11461
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
11462
+ const labelIds = await resolveIssueLabelIds(ctx, opts);
11463
+ const payload = createIssueSchema.parse({
11464
+ title: opts.title,
11465
+ description: opts.description,
11466
+ status: opts.status,
11467
+ priority: opts.priority,
11468
+ assigneeAgentId: opts.assigneeAgentId,
11469
+ projectId: opts.projectId,
11470
+ goalId: opts.goalId,
11471
+ parentId: opts.parentId,
11472
+ requestDepth: parseOptionalInt(opts.requestDepth),
11473
+ billingCode: opts.billingCode,
11474
+ labelIds: labelIds.length > 0 ? labelIds : void 0
11475
+ });
11476
+ const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/issues`, payload);
11477
+ printOutput(created, { json: ctx.json });
11478
+ } catch (err) {
11479
+ handleCommandError(err);
11480
+ }
11481
+ }),
11482
+ { includeCompany: false }
11483
+ );
11484
+ addCommonClientOptions(
11485
+ issue.command("update").description(getAgentCliCapabilityById("issue.update").description).argument("<issueId>", "Issue ID").option("--title <title>", "Issue title").option("--description <text>", "Issue description").option("--status <status>", "Issue status").option("--priority <priority>", "Issue priority").option("--assignee-agent-id <id>", "Assignee agent ID").option("--project-id <id>", "Project ID").option("--goal-id <id>", "Goal ID").option("--parent-id <id>", "Parent issue ID").option("--request-depth <n>", "Request depth integer").option("--billing-code <code>", "Billing code").option("--comment-file <path>", "Read optional update comment from a file, or '-' for stdin").option("--image <path>", "Image file to upload and append to the update comment; may be repeated", collectImagePath, []).option("--hidden-at <iso8601|null>", "Set hiddenAt timestamp or literal 'null'").action(async (issueId, opts) => {
11486
+ try {
11487
+ const ctx = resolveCommandContext(opts);
11488
+ const commentText = await resolveFileTextInput({
11489
+ file: opts.commentFile,
11490
+ fileOption: "--comment-file",
11491
+ removedTextOption: "--comment",
11492
+ required: false
11493
+ });
11494
+ const comment = await appendUploadedIssueImages(ctx, issueId, commentText, opts.image);
11495
+ const payload = updateIssueSchema.parse({
11496
+ title: opts.title,
11497
+ description: opts.description,
11498
+ status: opts.status,
11499
+ priority: opts.priority,
11500
+ assigneeAgentId: opts.assigneeAgentId,
11501
+ projectId: opts.projectId,
11502
+ goalId: opts.goalId,
11503
+ parentId: opts.parentId,
11504
+ requestDepth: parseOptionalInt(opts.requestDepth),
11505
+ billingCode: opts.billingCode,
11506
+ comment,
11507
+ hiddenAt: parseHiddenAt(opts.hiddenAt)
11508
+ });
11509
+ const updated = await ctx.api.patch(`/api/issues/${issueId}`, payload);
11510
+ printOutput(updated, { json: ctx.json });
11511
+ } catch (err) {
11512
+ handleCommandError(err);
11513
+ }
11514
+ })
11515
+ );
11516
+ addCommonClientOptions(
11517
+ issue.command("comment").description(getAgentCliCapabilityById("issue.comment").description).argument("<issueId>", "Issue ID").option("--body-file <path>", "Read comment body from a file, or '-' for stdin").option("--image <path>", "Image file to upload and append to the comment; may be repeated", collectImagePath, []).option("--reopen", "Reopen if issue is done/cancelled").action(async (issueId, opts) => {
11518
+ try {
11519
+ const ctx = resolveCommandContext(opts);
11520
+ const bodyText = await resolveFileTextInput({
11521
+ file: opts.bodyFile,
11522
+ fileOption: "--body-file",
11523
+ removedTextOption: "--body",
11524
+ required: true
11525
+ });
11526
+ const body = await appendUploadedIssueImages(ctx, issueId, bodyText, opts.image);
11527
+ const payload = addIssueCommentSchema.parse({
11528
+ body,
11529
+ reopen: opts.reopen
11530
+ });
11531
+ const comment = await ctx.api.post(`/api/issues/${issueId}/comments`, payload);
11532
+ if (!isIssueCommentResponse(comment)) {
11533
+ throw new Error("Issue comment request completed without a persisted comment response");
11534
+ }
11535
+ printOutput(comment, { json: ctx.json });
11536
+ } catch (err) {
11537
+ handleCommandError(err);
11538
+ }
11539
+ })
11540
+ );
11541
+ addCommonClientOptions(
11542
+ issue.command("review").description(getAgentCliCapabilityById("issue.review").description).argument("<issueId>", "Issue ID").requiredOption(
11543
+ "--decision <decision>",
11544
+ "Review decision: approve, request_changes, needs_followup, or blocked"
11545
+ ).option("--comment-file <path>", "Read required review comment from a file, or '-' for stdin").action(async (issueId, opts) => {
11546
+ try {
11547
+ const ctx = resolveCommandContext(opts);
11548
+ const decision = parseReviewDecision(opts.decision);
11549
+ const comment = await resolveFileTextInput({
11550
+ file: opts.commentFile,
11551
+ fileOption: "--comment-file",
11552
+ removedTextOption: "--comment",
11553
+ required: true
11554
+ });
11555
+ const updated = await ctx.api.patch(`/api/issues/${issueId}`, {
11556
+ reviewDecision: decision,
11557
+ comment
11558
+ });
11559
+ printOutput(updated, { json: ctx.json });
11560
+ } catch (err) {
11561
+ handleCommandError(err);
11562
+ }
11563
+ })
11564
+ );
11565
+ addCommonClientOptions(
11566
+ issue.command("commit").description(getAgentCliCapabilityById("issue.commit").description).argument("<issueId>", "Issue ID").requiredOption("--sha <sha>", "Commit SHA").requiredOption("--message <subject>", "Commit subject or message").option("--branch <name>", "Branch name").option("--repo-path <path>", "Repository path").option("--workspace-path <path>", "Workspace path").option("--count <n>", "Number of commits represented by this report").action(async (issueId, opts) => {
11567
+ try {
11568
+ const ctx = resolveCommandContext(opts);
11569
+ const payload = reportIssueCommitSchema.parse({
11570
+ sha: opts.sha,
11571
+ message: opts.message,
11572
+ branch: opts.branch,
11573
+ repoPath: opts.repoPath,
11574
+ workspacePath: opts.workspacePath,
11575
+ commitCount: parseOptionalInt(opts.count)
11576
+ });
11577
+ const reported = await ctx.api.post(`/api/issues/${issueId}/commit`, payload);
11578
+ printOutput(reported, { json: ctx.json });
11579
+ } catch (err) {
11580
+ handleCommandError(err);
11581
+ }
11582
+ })
11583
+ );
11584
+ addCommonClientOptions(
11585
+ issue.command("done").description(getAgentCliCapabilityById("issue.done").description).argument("<issueId>", "Issue ID").option("--comment-file <path>", "Read required completion comment from a file, or '-' for stdin").option("--image <path>", "Image file to upload and append to the completion comment; may be repeated", collectImagePath, []).action(async (issueId, opts) => {
11586
+ try {
11587
+ const ctx = resolveCommandContext(opts);
11588
+ const commentText = await resolveFileTextInput({
11589
+ file: opts.commentFile,
11590
+ fileOption: "--comment-file",
11591
+ removedTextOption: "--comment",
11592
+ required: true
11593
+ });
11594
+ const comment = await appendUploadedIssueImages(ctx, issueId, commentText, opts.image);
11595
+ const updated = await ctx.api.patch(`/api/issues/${issueId}`, {
11596
+ status: "done",
11597
+ comment
11598
+ });
11599
+ printOutput(updated, { json: ctx.json });
11600
+ } catch (err) {
11601
+ handleCommandError(err);
11602
+ }
11603
+ })
11604
+ );
11605
+ addCommonClientOptions(
11606
+ issue.command("block").description(getAgentCliCapabilityById("issue.block").description).argument("<issueId>", "Issue ID").option("--comment-file <path>", "Read required blocker comment from a file, or '-' for stdin").option("--image <path>", "Image file to upload and append to the blocker comment; may be repeated", collectImagePath, []).action(async (issueId, opts) => {
11607
+ try {
11608
+ const ctx = resolveCommandContext(opts);
11609
+ const commentText = await resolveFileTextInput({
11610
+ file: opts.commentFile,
11611
+ fileOption: "--comment-file",
11612
+ removedTextOption: "--comment",
11613
+ required: true
11614
+ });
11615
+ const comment = await appendUploadedIssueImages(ctx, issueId, commentText, opts.image);
11616
+ const updated = await ctx.api.patch(`/api/issues/${issueId}`, {
11617
+ status: "blocked",
11618
+ comment
11619
+ });
11620
+ printOutput(updated, { json: ctx.json });
11621
+ } catch (err) {
11622
+ handleCommandError(err);
11623
+ }
11624
+ })
11625
+ );
11626
+ const comments = issue.command("comments").description("Issue comment operations");
11627
+ addCommonClientOptions(
11628
+ comments.command("list").description(getAgentCliCapabilityById("issue.comments.list").description).argument("<issueId>", "Issue ID").option("--after <commentId>", "Only return comments after this comment ID").option("--order <order>", "Comment ordering (asc or desc)", "desc").action(async (issueId, opts) => {
11629
+ try {
11630
+ const ctx = resolveCommandContext(opts);
11631
+ const params = new URLSearchParams();
11632
+ if (opts.after) params.set("after", opts.after);
11633
+ if (opts.order) params.set("order", opts.order);
11634
+ const query = params.toString();
11635
+ const rows = await ctx.api.get(
11636
+ `/api/issues/${issueId}/comments${query ? `?${query}` : ""}`
11637
+ ) ?? [];
11638
+ printOutput(rows, { json: ctx.json });
11639
+ } catch (err) {
11640
+ handleCommandError(err);
11641
+ }
11642
+ })
11643
+ );
11644
+ addCommonClientOptions(
11645
+ comments.command("get").description(getAgentCliCapabilityById("issue.comments.get").description).argument("<issueId>", "Issue ID").argument("<commentId>", "Comment ID").action(async (issueId, commentId, opts) => {
11646
+ try {
11647
+ const ctx = resolveCommandContext(opts);
11648
+ const row = await ctx.api.get(`/api/issues/${issueId}/comments/${commentId}`);
11649
+ printOutput(row, { json: ctx.json });
11650
+ } catch (err) {
11651
+ handleCommandError(err);
11652
+ }
11653
+ })
11654
+ );
11655
+ addCommonClientOptions(
11656
+ issue.command("checkout").description(getAgentCliCapabilityById("issue.checkout").description).argument("<issueId>", "Issue ID").option("--agent-id <id>", "Agent ID (defaults to RUDDER_AGENT_ID)").option(
11657
+ "--expected-statuses <csv>",
11658
+ "Expected current statuses",
11659
+ "todo,backlog,blocked"
11660
+ ).action(async (issueId, opts) => {
11661
+ try {
11662
+ const ctx = resolveCommandContext(opts);
11663
+ const agentId = opts.agentId?.trim() || ctx.agentId;
11664
+ if (!agentId) {
11665
+ throw new Error("Agent ID is required. Pass --agent-id or set RUDDER_AGENT_ID.");
11666
+ }
11667
+ const payload = checkoutIssueSchema.parse({
11668
+ agentId,
11669
+ expectedStatuses: parseCsv(opts.expectedStatuses)
11670
+ });
11671
+ const updated = await ctx.api.post(`/api/issues/${issueId}/checkout`, payload);
11672
+ printOutput(updated, { json: ctx.json });
11673
+ } catch (err) {
11674
+ handleCommandError(err);
11675
+ }
11676
+ })
11677
+ );
11678
+ addCommonClientOptions(
11679
+ issue.command("release").description(getAgentCliCapabilityById("issue.release").description).argument("<issueId>", "Issue ID").action(async (issueId, opts) => {
11680
+ try {
11681
+ const ctx = resolveCommandContext(opts);
11682
+ const updated = await ctx.api.post(`/api/issues/${issueId}/release`, {});
11683
+ printOutput(updated, { json: ctx.json });
11684
+ } catch (err) {
11685
+ handleCommandError(err);
11686
+ }
11687
+ })
11688
+ );
11689
+ }
11690
+ function collectImagePath(value, previous) {
11691
+ const trimmed = value.trim();
11692
+ if (!trimmed) {
11693
+ throw new Error("--image path cannot be empty");
11694
+ }
11695
+ return [...previous, trimmed];
11696
+ }
11697
+ function collectNonEmptyOption(optionName) {
11698
+ return (value, previous) => {
11699
+ const trimmed = value.trim();
11700
+ if (!trimmed) {
11701
+ throw new Error(`${optionName} cannot be empty`);
11702
+ }
11703
+ return [...previous, trimmed];
11704
+ };
11705
+ }
11706
+ async function resolveFileTextInput(opts) {
11707
+ if (opts.removedTextOption && process.argv.includes(opts.removedTextOption)) {
11708
+ throw new Error(`${opts.removedTextOption} was removed; write the body to a file and use ${opts.fileOption} <path> or ${opts.fileOption} - for stdin`);
11709
+ }
11710
+ const hasFile = opts.file !== void 0;
11711
+ if (hasFile) return readTextInputFile(opts.file, opts.fileOption);
11712
+ if (opts.required) {
11713
+ throw new Error(`Provide ${opts.fileOption} <path>; use ${opts.fileOption} - for stdin`);
11714
+ }
11715
+ return void 0;
11716
+ }
11717
+ async function readTextInputFile(inputPath, optionName) {
11718
+ if (inputPath === "-") {
11719
+ return readStdinText();
11720
+ }
11721
+ const resolvedPath = path16.resolve(process.cwd(), inputPath);
11722
+ return readFile4(resolvedPath, "utf8").catch((err) => {
11723
+ throw new Error(`Unable to read ${optionName} ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
11724
+ });
11725
+ }
11726
+ async function readStdinText() {
11727
+ const chunks = [];
11728
+ for await (const chunk of process.stdin) {
11729
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
11730
+ }
11731
+ return Buffer.concat(chunks).toString("utf8");
11732
+ }
11733
+ async function resolveIssueLabelIds(ctx, opts) {
11734
+ const explicitIds = opts.labelId ?? [];
11735
+ const names = opts.label ?? [];
11736
+ if (names.length === 0 && explicitIds.length > 0) return [...new Set(explicitIds)];
11737
+ if (names.length === 0 && opts.parentId) return [];
11738
+ const labels = await ctx.api.get(`/api/orgs/${ctx.orgId}/labels`) ?? [];
11739
+ if (names.length === 0) {
11740
+ if (ctx.agentId && labels.length >= 5) {
11741
+ throw new Error(
11742
+ `Organization has ${labels.length} issue labels. Choose at least one with --label-id <id> or --label <name>. Available labels: ${formatAvailableLabelNames(labels)}`
11743
+ );
11744
+ }
11745
+ return [];
11746
+ }
11747
+ const resolvedIds = names.map((name) => {
11748
+ const exact = labels.find((label) => label.name === name) ?? labels.find((label) => label.name.toLowerCase() === name.toLowerCase());
11749
+ if (!exact) {
11750
+ throw new Error(`Unknown issue label "${name}". Available labels: ${formatAvailableLabelNames(labels)}`);
11751
+ }
11752
+ return exact.id;
11753
+ });
11754
+ return [.../* @__PURE__ */ new Set([...explicitIds, ...resolvedIds])];
11755
+ }
11756
+ function formatAvailableLabelNames(labels) {
11757
+ return labels.map((label) => label.name).join(", ") || "(none)";
11758
+ }
11759
+ async function appendUploadedIssueImages(ctx, issueId, body, imagePaths) {
11760
+ const paths = imagePaths ?? [];
11761
+ if (paths.length === 0) return body;
11762
+ const issue = await ctx.api.get(`/api/issues/${issueId}`);
11763
+ if (!issue) {
11764
+ throw new Error("Issue not found");
11765
+ }
11766
+ const links = [];
11767
+ for (const imagePath of paths) {
11768
+ const attachment = await uploadIssueCommentImage(ctx, issue, imagePath);
11769
+ links.push(formatAttachmentMarkdown(attachment));
11770
+ }
11771
+ const base = body?.trimEnd() ?? "";
11772
+ const imageBlock = links.join("\n");
11773
+ return base ? `${base}
11774
+
11775
+ ${imageBlock}` : imageBlock;
11776
+ }
11777
+ async function uploadIssueCommentImage(ctx, issue, imagePath) {
11778
+ const resolvedPath = path16.resolve(process.cwd(), imagePath);
11779
+ const stats = await stat4(resolvedPath).catch((err) => {
11780
+ throw new Error(`Unable to read image ${imagePath}: ${err instanceof Error ? err.message : String(err)}`);
11781
+ });
11782
+ if (!stats.isFile()) {
11783
+ throw new Error(`Image path must be a file: ${imagePath}`);
11784
+ }
11785
+ const filename = path16.basename(resolvedPath);
11786
+ const contentType = inferCommentImageContentType(filename);
11787
+ const buffer = await readFile4(resolvedPath);
11788
+ if (buffer.length <= 0) {
11789
+ throw new Error(`Image is empty: ${imagePath}`);
11790
+ }
11791
+ const form = new FormData();
11792
+ form.set("usage", "comment_inline");
11793
+ form.set("file", new Blob([buffer], { type: contentType }), filename);
11794
+ const attachment = await ctx.api.postForm(
11795
+ `/api/orgs/${issue.orgId}/issues/${issue.id}/attachments`,
11796
+ form
11797
+ );
11798
+ if (!attachment) {
11799
+ throw new Error(`Image upload returned no attachment: ${imagePath}`);
11800
+ }
11801
+ return attachment;
11802
+ }
11803
+ function inferCommentImageContentType(filename) {
11804
+ const ext = path16.extname(filename).toLowerCase();
11805
+ switch (ext) {
11806
+ case ".png":
11807
+ return "image/png";
11808
+ case ".jpg":
11809
+ case ".jpeg":
11810
+ return "image/jpeg";
11811
+ case ".webp":
11812
+ return "image/webp";
11813
+ case ".gif":
11814
+ return "image/gif";
11815
+ default:
11816
+ throw new Error(`Unsupported comment image type: ${filename}. Use PNG, JPEG, WebP, or GIF.`);
11817
+ }
11818
+ }
11819
+ function formatAttachmentMarkdown(attachment) {
11820
+ const alt = escapeMarkdownAltText(attachment.originalFilename ?? "image");
11821
+ return `![${alt}](${attachment.contentPath})`;
11822
+ }
11823
+ function escapeMarkdownAltText(value) {
11824
+ return value.replaceAll("\\", "\\\\").replaceAll("]", "\\]");
11825
+ }
11826
+ function parseCsv(value) {
11827
+ if (!value) return [];
11828
+ return value.split(",").map((v) => v.trim()).filter(Boolean);
11829
+ }
11830
+ function parseOptionalInt(value) {
11831
+ if (value === void 0) return void 0;
11832
+ const parsed = Number.parseInt(value, 10);
11833
+ if (!Number.isFinite(parsed)) {
11834
+ throw new Error(`Invalid integer value: ${value}`);
11835
+ }
11836
+ return parsed;
11837
+ }
11838
+ function parseHiddenAt(value) {
11839
+ if (value === void 0) return void 0;
11840
+ if (value.trim().toLowerCase() === "null") return null;
11841
+ return value;
11842
+ }
11843
+ function parseReviewDecision(value) {
11844
+ const normalized = value.trim();
11845
+ if (normalized === "approve" || normalized === "request_changes" || normalized === "needs_followup" || normalized === "blocked") {
11846
+ return normalized;
11847
+ }
11848
+ throw new Error("Invalid review decision. Use approve, request_changes, needs_followup, or blocked.");
11849
+ }
11850
+ function isIssueCommentResponse(value) {
11851
+ return Boolean(
11852
+ value && typeof value.id === "string" && value.id.trim().length > 0 && typeof value.issueId === "string" && value.issueId.trim().length > 0 && typeof value.body === "string"
11853
+ );
11854
+ }
11855
+ function filterIssueRows(rows, match) {
11856
+ if (!match?.trim()) return rows;
11857
+ const needle = match.trim().toLowerCase();
11858
+ return rows.filter((row) => {
11859
+ const text6 = [row.identifier, row.title, row.description].filter((part) => Boolean(part)).join("\n").toLowerCase();
11860
+ return text6.includes(needle);
11861
+ });
11862
+ }
11863
+ function buildIssueListPath(orgId, opts, searchQuery) {
11864
+ const params = new URLSearchParams();
11865
+ if (opts.status) params.set("status", opts.status);
11866
+ if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
11867
+ if (opts.projectId) params.set("projectId", opts.projectId);
11868
+ if (searchQuery?.trim()) {
11869
+ params.set("q", searchQuery.trim());
11870
+ params.set("searchFields", "title,description,comment");
11871
+ }
11872
+ const query = params.toString();
11873
+ return `/api/orgs/${orgId}/issues${query ? `?${query}` : ""}`;
11874
+ }
11875
+ function printIssueRows(rows, json) {
11876
+ if (json) {
11877
+ printOutput(rows, { json: true });
11878
+ return;
11879
+ }
11880
+ if (rows.length === 0) {
11881
+ printOutput([], { json: false });
11882
+ return;
11883
+ }
11884
+ for (const item of rows) {
11885
+ console.log(
11886
+ formatInlineRecord({
11887
+ identifier: item.identifier,
11888
+ id: item.id,
11889
+ status: item.status,
11890
+ priority: item.priority,
11891
+ assigneeAgentId: item.assigneeAgentId,
11892
+ assigneeUserId: item.assigneeUserId,
11893
+ title: item.title,
11894
+ projectId: item.projectId,
11895
+ updatedAt: item.updatedAt,
11896
+ ...item.searchMatch ? { match: formatIssueSearchMatch(item.searchMatch) } : {}
11897
+ })
11898
+ );
11899
+ }
11900
+ }
11901
+ function formatIssueSearchMatch(match) {
11902
+ const commentSuffix = match.commentId ? `#${match.commentId}` : "";
11903
+ return `${match.field}${commentSuffix}: ${match.snippet}`;
11904
+ }
11905
+
11906
+ // src/commands/client/project.ts
11907
+ init_dist();
11908
+ function registerProjectCommands(program) {
11909
+ const project = program.command("project").description("Project operations");
11910
+ addCommonClientOptions(
11911
+ project.command("list").description(getAgentCliCapabilityById("project.list").description).option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
11912
+ try {
11913
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
11914
+ const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/projects`) ?? [];
11915
+ printOutput(rows, { json: ctx.json });
11916
+ } catch (err) {
11917
+ handleCommandError(err);
11918
+ }
11919
+ }),
11920
+ { includeCompany: false }
11921
+ );
11922
+ addCommonClientOptions(
11923
+ project.command("get").description(getAgentCliCapabilityById("project.get").description).argument("<projectIdOrShortname>", "Project ID or shortname").option("-O, --org-id <id>", "Organization ID for shortname resolution").action(async (projectRef, opts) => {
11924
+ try {
11925
+ const ctx = resolveCommandContext(opts);
11926
+ const row = await ctx.api.get(projectPath(projectRef, ctx.orgId));
11927
+ printOutput(row, { json: ctx.json });
11928
+ } catch (err) {
11929
+ handleCommandError(err);
11930
+ }
11931
+ }),
11932
+ { includeCompany: false }
11933
+ );
11934
+ addCommonClientOptions(
11935
+ project.command("create").description(getAgentCliCapabilityById("project.create").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--name <name>", "Project name").option("--description <text>", "Project description").option("--status <status>", "Project status").option("--goal-id <id>", "Primary goal ID").option("--goal-ids <csv>", "Comma-separated goal IDs").option("--lead-agent-id <id>", "Lead agent ID").option("--target-date <date>", "Target date").option("--color <value>", "Project color or supported gradient token").action(async (opts) => {
11936
+ try {
11937
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
11938
+ const payload = createProjectSchema.parse({
11939
+ name: opts.name,
11940
+ description: opts.description,
11120
11941
  status: opts.status,
11121
- priority: opts.priority,
11122
- assigneeAgentId: opts.assigneeAgentId,
11123
- projectId: opts.projectId,
11124
11942
  goalId: opts.goalId,
11125
- parentId: opts.parentId,
11126
- requestDepth: parseOptionalInt(opts.requestDepth),
11127
- billingCode: opts.billingCode,
11128
- comment,
11129
- hiddenAt: parseHiddenAt(opts.hiddenAt)
11943
+ goalIds: parseCsv2(opts.goalIds),
11944
+ leadAgentId: opts.leadAgentId,
11945
+ targetDate: opts.targetDate,
11946
+ color: opts.color
11130
11947
  });
11131
- const updated = await ctx.api.patch(`/api/issues/${issueId}`, payload);
11948
+ const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/projects`, payload);
11949
+ printOutput(created, { json: ctx.json });
11950
+ } catch (err) {
11951
+ handleCommandError(err);
11952
+ }
11953
+ }),
11954
+ { includeCompany: false }
11955
+ );
11956
+ addCommonClientOptions(
11957
+ project.command("update").description(getAgentCliCapabilityById("project.update").description).argument("<projectIdOrShortname>", "Project ID or shortname").option("-O, --org-id <id>", "Organization ID for shortname resolution").option("--name <name>", "Project name").option("--description <text>", "Project description").option("--status <status>", "Project status").option("--goal-id <id>", "Primary goal ID").option("--goal-ids <csv>", "Comma-separated goal IDs").option("--lead-agent-id <id>", "Lead agent ID").option("--target-date <date>", "Target date").option("--color <value>", "Project color or supported gradient token").option("--archived-at <iso8601|null>", "Set archivedAt timestamp or literal 'null'").action(async (projectRef, opts) => {
11958
+ try {
11959
+ const ctx = resolveCommandContext(opts);
11960
+ const payload = updateProjectSchema.parse({
11961
+ name: opts.name,
11962
+ description: opts.description,
11963
+ status: opts.status,
11964
+ goalId: opts.goalId,
11965
+ goalIds: parseCsv2(opts.goalIds),
11966
+ leadAgentId: opts.leadAgentId,
11967
+ targetDate: opts.targetDate,
11968
+ color: opts.color,
11969
+ archivedAt: parseNullableOption(opts.archivedAt)
11970
+ });
11971
+ const updated = await ctx.api.patch(projectPath(projectRef, ctx.orgId), payload);
11132
11972
  printOutput(updated, { json: ctx.json });
11133
11973
  } catch (err) {
11134
11974
  handleCommandError(err);
11135
11975
  }
11976
+ }),
11977
+ { includeCompany: false }
11978
+ );
11979
+ }
11980
+ function parseCsv2(value) {
11981
+ if (!value) return void 0;
11982
+ const items = value.split(",").map((item) => item.trim()).filter(Boolean);
11983
+ return items.length > 0 ? items : void 0;
11984
+ }
11985
+ function parseNullableOption(value) {
11986
+ if (value === void 0) return void 0;
11987
+ return value === "null" ? null : value;
11988
+ }
11989
+ function projectPath(projectRef, orgId) {
11990
+ const params = new URLSearchParams();
11991
+ if (orgId) params.set("orgId", orgId);
11992
+ const query = params.toString();
11993
+ return `/api/projects/${encodeURIComponent(projectRef)}${query ? `?${query}` : ""}`;
11994
+ }
11995
+
11996
+ // src/commands/client/agent.ts
11997
+ init_dist();
11998
+
11999
+ // ../packages/agent-runtime-utils/dist/server-utils.process.js
12000
+ var MAX_CAPTURE_BYTES = 4 * 1024 * 1024;
12001
+ var MAX_EXCERPT_BYTES = 32 * 1024;
12002
+ var RUDDER_SKILL_ROOT_RELATIVE_CANDIDATES = [
12003
+ "../../server/resources/bundled-skills",
12004
+ "../../skills",
12005
+ "../../../../../server/resources/bundled-skills"
12006
+ ];
12007
+ function normalizePathSlashes(value) {
12008
+ return value.replaceAll("\\", "/");
12009
+ }
12010
+ function isMaintainerOnlySkillTarget(candidate) {
12011
+ const normalized = normalizePathSlashes(candidate);
12012
+ return normalized.includes("/server/resources/bundled-skills/") || normalized.includes("/.agents/skills/");
12013
+ }
12014
+
12015
+ // ../packages/agent-runtime-utils/dist/server-utils.prompts.js
12016
+ var RUDDER_AGENT_OPERATING_CONTRACT = [
12017
+ "# Rudder Agent Operating Contract",
12018
+ "",
12019
+ "Your home directory is `$AGENT_HOME`. Everything personal to you -- life, memory, knowledge -- lives there. Other agents may have their own folders and you may update them when necessary.",
12020
+ "",
12021
+ "Use these paths consistently:",
12022
+ "",
12023
+ "- Personal instructions live under `$AGENT_HOME/instructions`.",
12024
+ "- Personal memory lives under `$AGENT_HOME/memory`.",
12025
+ "- Tacit memory instruction lives at `$AGENT_HOME/instructions/MEMORY.md` and is automatically loaded when present.",
12026
+ "- Personal skills live under `$AGENT_HOME/skills`.",
12027
+ "- Shared organization workspace root lives under `$RUDDER_ORG_WORKSPACE_ROOT`.",
12028
+ "- Shared organization skills live under `$RUDDER_ORG_SKILLS_DIR`.",
12029
+ "- Project Library root lives under `$RUDDER_PROJECT_LIBRARY_ROOT` when the run has project context.",
12030
+ "- Project Library locator lives in `$RUDDER_PROJECT_LIBRARY_PATH` when the run has project context, for example `projects/<project-key>`.",
12031
+ '- Library-backed project resources use `sourceType: "library"`; their `locator` points into `library:projects/<project-key>/`.',
12032
+ "- Project Context is explicit operator-curated context, not the whole knowledge boundary. When it is insufficient, inspect broader Library and org workspace know-how before concluding context is missing.",
12033
+ "- In local trusted runs, durable generated project work files should be written directly under `$RUDDER_PROJECT_LIBRARY_ROOT` with normal filesystem tools.",
12034
+ "- `library:projects/<project-key>/...` is the Rudder product locator for those files, not the Markdown link syntax and not a reason to route ordinary local edits through the CLI.",
12035
+ '- When you create or update a durable Library file, always include a user-visible Markdown link to that file in your final chat reply or issue comment. Use `rudder library file ref "$RUDDER_PROJECT_LIBRARY_PATH/<relative-file>" --json` and paste the returned `markdownLink`; do not pass the absolute `$RUDDER_PROJECT_LIBRARY_ROOT/...` path to `ref`, and do not hand-write `library-entry://...` or `library-file://...` links.',
12036
+ '- If `$RUDDER_PROJECT_LIBRARY_ROOT` is unset or inaccessible, use `rudder library file get/put "$RUDDER_PROJECT_LIBRARY_PATH/<relative-file>"` as the remote or restricted runtime fallback.',
12037
+ "- Use `/tmp` only for transient scratch files and temporary verification files; do not put durable work product there.",
12038
+ "- Local trusted runtimes may expose the host operator home as `$RUDDER_OPERATOR_HOME`; use it only when a local skill or script intentionally needs operator-owned desktop app or CLI state. Do not replace `$HOME` with it.",
12039
+ "",
12040
+ "When you create or copy a skill under `$AGENT_HOME/skills/<slug>/`, check the agent's Skills snapshot before claiming it will load in future runs. If it is installed but not enabled, say exactly that future runs will not load it until enabled, and offer to enable it with `rudder agent skills enable <agent-id> <selection-ref>` when you have permission.",
12041
+ "",
12042
+ "When you write issue comments or chat replies, match the language of the user's or board's most recent substantive message unless they explicitly ask for a different language.",
12043
+ "When you mention a web page, issue URL, external dashboard, or other user-openable target in an issue comment or chat reply, write it as a clickable Markdown link with a descriptive label, for example `[NameSilo transfer page](https://www.namesilo.com/account_domain_manage_transfer.php)`. Do not put action URLs in backticks or code blocks unless you are showing literal code or a command.",
12044
+ "",
12045
+ "## Rudder Renderable Links",
12046
+ "",
12047
+ "When you mention Rudder entities in any user-visible Markdown output, prefer Rudder's renderable Markdown link syntax over plain IDs, bare URLs, or backticked references so the UI can render chips and navigate correctly.",
12048
+ "",
12049
+ "- Issues: use `[<identifier-or-title>](issue://<issue-id>?r=<identifier>)`; include `&c=<comment-id>` when linking to a specific comment.",
12050
+ "- Agents: use `[<agent-name>](agent://<agent-id>)` for reference-only links. Use `[<agent-name>](agent://<agent-id>?intent=wake)` only when you deliberately want an issue-composer mention to wake that agent.",
12051
+ "- Projects: use `[<project-name>](project://<project-id>)` when citing a Rudder project.",
12052
+ "- Chat threads: use `[<chat-title>](chat://<conversation-id>?t=<url-encoded-title>)` when citing a Rudder chat conversation.",
12053
+ '- Library files: use the `markdownLink` returned by `rudder library file ref "$RUDDER_PROJECT_LIBRARY_PATH/<relative-file>" --json`; do not hand-write `library-entry://...` links, and treat `library-file://...` as legacy path syntax only.',
12054
+ "",
12055
+ "Write these as normal Markdown links, not inside code spans or code blocks, unless you are literally documenting the syntax.",
12056
+ "",
12057
+ "Mention-triggered comment wakes arrive with `RUDDER_WAKE_COMMENT_ID`; read that wake comment before acting. Board/operator issue comments can wake an agent with a plain `@AgentName` token or an issue-composer agent mention serialized as `agent://agent-id?intent=wake`. Plain structured links such as `agent://agent-id` are reference-only links for rendering and navigation. Agent-authored issue comments do not fan out peer wakeups by default; use reviewer, assignment, or explicit handoff workflow instead. Mentioning an agent requests attention or collaboration; it does not transfer issue ownership unless the comment also makes an explicit handoff and normal checkout rules allow it.",
12058
+ "",
12059
+ "When an issue comment, done comment, or blocker comment cites visual evidence from a local screenshot/image path, attach the image with the Rudder CLI `--image <path>` option instead of leaving only the filesystem path in the text.",
12060
+ "",
12061
+ "## Memory and Shared Work Notes",
12062
+ "",
12063
+ "You MUST use the `para-memory-files` skill for all memory operations: storing facts, writing daily notes, creating entities, running weekly synthesis, recalling past context, and managing shared work notes. The skill defines your three-layer memory system (knowledge graph, daily notes, tacit knowledge), the PARA folder structure, atomic fact schemas, memory decay rules, and recall conventions.",
12064
+ "",
12065
+ "Keep stable preferences and operating lessons in `$AGENT_HOME/instructions/MEMORY.md`. Use `$AGENT_HOME/memory/YYYY-MM-DD.md` for daily notes and `$AGENT_HOME/life/` for structured long-term memory; those files are not auto-loaded.",
12066
+ "",
12067
+ "Invoke it whenever you need to remember, retrieve, or organize anything.",
12068
+ "",
12069
+ "## Safety Considerations",
12070
+ "",
12071
+ "- Never exfiltrate secrets or private data.",
12072
+ "- Do not perform any destructive commands unless explicitly requested by the board."
12073
+ ].join("\n");
12074
+
12075
+ // ../packages/agent-runtime-utils/dist/server-utils.cli.js
12076
+ import { promises as fs13 } from "node:fs";
12077
+ import path17 from "node:path";
12078
+ async function resolveRudderSkillsDir(moduleDir, additionalCandidates = []) {
12079
+ const candidates = [
12080
+ ...RUDDER_SKILL_ROOT_RELATIVE_CANDIDATES.map((relativePath) => path17.resolve(moduleDir, relativePath)),
12081
+ ...additionalCandidates.map((candidate) => path17.resolve(candidate))
12082
+ ];
12083
+ const seenRoots = /* @__PURE__ */ new Set();
12084
+ for (const root of candidates) {
12085
+ if (seenRoots.has(root))
12086
+ continue;
12087
+ seenRoots.add(root);
12088
+ const isDirectory = await fs13.stat(root).then((stats) => stats.isDirectory()).catch(() => false);
12089
+ if (isDirectory)
12090
+ return root;
12091
+ }
12092
+ return null;
12093
+ }
12094
+ async function removeMaintainerOnlySkillSymlinks(skillsHome, allowedSkillNames) {
12095
+ const allowed = new Set(Array.from(allowedSkillNames));
12096
+ try {
12097
+ const entries = await fs13.readdir(skillsHome, { withFileTypes: true });
12098
+ const removed = [];
12099
+ for (const entry of entries) {
12100
+ if (allowed.has(entry.name))
12101
+ continue;
12102
+ const target = path17.join(skillsHome, entry.name);
12103
+ const existing = await fs13.lstat(target).catch(() => null);
12104
+ if (!existing?.isSymbolicLink())
12105
+ continue;
12106
+ const linkedPath = await fs13.readlink(target).catch(() => null);
12107
+ if (!linkedPath)
12108
+ continue;
12109
+ const resolvedLinkedPath = path17.isAbsolute(linkedPath) ? linkedPath : path17.resolve(path17.dirname(target), linkedPath);
12110
+ if (!isMaintainerOnlySkillTarget(linkedPath) && !isMaintainerOnlySkillTarget(resolvedLinkedPath)) {
12111
+ continue;
12112
+ }
12113
+ await fs13.unlink(target);
12114
+ removed.push(entry.name);
12115
+ }
12116
+ return removed;
12117
+ } catch {
12118
+ return [];
12119
+ }
12120
+ }
12121
+
12122
+ // src/commands/client/agent.ts
12123
+ import fs14 from "node:fs/promises";
12124
+ import os2 from "node:os";
12125
+ import path18 from "node:path";
12126
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
12127
+ var __moduleDir = path18.dirname(fileURLToPath3(import.meta.url));
12128
+ function codexSkillsHome() {
12129
+ const fromEnv = process.env.CODEX_HOME?.trim();
12130
+ const base = fromEnv && fromEnv.length > 0 ? fromEnv : path18.join(os2.homedir(), ".codex");
12131
+ return path18.join(base, "skills");
12132
+ }
12133
+ function claudeSkillsHome() {
12134
+ const fromEnv = process.env.CLAUDE_HOME?.trim();
12135
+ const base = fromEnv && fromEnv.length > 0 ? fromEnv : path18.join(os2.homedir(), ".claude");
12136
+ return path18.join(base, "skills");
12137
+ }
12138
+ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
12139
+ const summary = {
12140
+ tool,
12141
+ target: targetSkillsDir,
12142
+ linked: [],
12143
+ removed: [],
12144
+ skipped: [],
12145
+ failed: []
12146
+ };
12147
+ await fs14.mkdir(targetSkillsDir, { recursive: true });
12148
+ const entries = await fs14.readdir(sourceSkillsDir, { withFileTypes: true });
12149
+ summary.removed = await removeMaintainerOnlySkillSymlinks(
12150
+ targetSkillsDir,
12151
+ entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name)
12152
+ );
12153
+ for (const entry of entries) {
12154
+ if (!entry.isDirectory()) continue;
12155
+ const source = path18.join(sourceSkillsDir, entry.name);
12156
+ const target = path18.join(targetSkillsDir, entry.name);
12157
+ const existing = await fs14.lstat(target).catch(() => null);
12158
+ if (existing) {
12159
+ if (existing.isSymbolicLink()) {
12160
+ let linkedPath = null;
12161
+ try {
12162
+ linkedPath = await fs14.readlink(target);
12163
+ } catch (err) {
12164
+ await fs14.unlink(target);
12165
+ try {
12166
+ await fs14.symlink(source, target);
12167
+ summary.linked.push(entry.name);
12168
+ continue;
12169
+ } catch (linkErr) {
12170
+ summary.failed.push({
12171
+ name: entry.name,
12172
+ error: err instanceof Error && linkErr instanceof Error ? `${err.message}; then ${linkErr.message}` : err instanceof Error ? err.message : `Failed to recover broken symlink: ${String(err)}`
12173
+ });
12174
+ continue;
12175
+ }
12176
+ }
12177
+ const resolvedLinkedPath = path18.isAbsolute(linkedPath) ? linkedPath : path18.resolve(path18.dirname(target), linkedPath);
12178
+ const linkedTargetExists = await fs14.stat(resolvedLinkedPath).then(() => true).catch(() => false);
12179
+ if (!linkedTargetExists) {
12180
+ await fs14.unlink(target);
12181
+ } else {
12182
+ summary.skipped.push(entry.name);
12183
+ continue;
12184
+ }
12185
+ } else {
12186
+ summary.skipped.push(entry.name);
12187
+ continue;
12188
+ }
12189
+ }
12190
+ try {
12191
+ await fs14.symlink(source, target);
12192
+ summary.linked.push(entry.name);
12193
+ } catch (err) {
12194
+ summary.failed.push({
12195
+ name: entry.name,
12196
+ error: err instanceof Error ? err.message : String(err)
12197
+ });
12198
+ }
12199
+ }
12200
+ return summary;
12201
+ }
12202
+ function buildAgentEnvExports(input) {
12203
+ const escaped = (value) => value.replace(/'/g, `'"'"'`);
12204
+ return [
12205
+ `export RUDDER_API_URL='${escaped(input.apiBase)}'`,
12206
+ `export RUDDER_ORG_ID='${escaped(input.orgId)}'`,
12207
+ `export RUDDER_AGENT_ID='${escaped(input.agentId)}'`,
12208
+ `export RUDDER_API_KEY='${escaped(input.apiKey)}'`
12209
+ ].join("\n");
12210
+ }
12211
+ function registerAgentCommands(program) {
12212
+ const agent = program.command("agent").description("Agent operations");
12213
+ const config = agent.command("config").description("Agent configuration discovery and redacted snapshots");
12214
+ addCommonClientOptions(
12215
+ agent.command("me").description(getAgentCliCapabilityById("agent.me").description).action(async (opts) => {
12216
+ try {
12217
+ const ctx = resolveCommandContext(opts);
12218
+ const row = await ctx.api.get("/api/agents/me");
12219
+ printOutput(row, { json: ctx.json });
12220
+ } catch (err) {
12221
+ handleCommandError(err);
12222
+ }
11136
12223
  })
11137
12224
  );
11138
12225
  addCommonClientOptions(
11139
- issue.command("comment").description(getAgentCliCapabilityById("issue.comment").description).argument("<issueId>", "Issue ID").option("--body-file <path>", "Read comment body from a file, or '-' for stdin").option("--image <path>", "Image file to upload and append to the comment; may be repeated", collectImagePath, []).option("--reopen", "Reopen if issue is done/cancelled").action(async (issueId, opts) => {
12226
+ agent.command("inbox").description(getAgentCliCapabilityById("agent.inbox").description).action(async (opts) => {
11140
12227
  try {
11141
12228
  const ctx = resolveCommandContext(opts);
11142
- const bodyText = await resolveFileTextInput({
11143
- file: opts.bodyFile,
11144
- fileOption: "--body-file",
11145
- removedTextOption: "--body",
11146
- required: true
11147
- });
11148
- const body = await appendUploadedIssueImages(ctx, issueId, bodyText, opts.image);
11149
- const payload = addIssueCommentSchema.parse({
11150
- body,
11151
- reopen: opts.reopen
11152
- });
11153
- const comment = await ctx.api.post(`/api/issues/${issueId}/comments`, payload);
11154
- if (!isIssueCommentResponse(comment)) {
11155
- throw new Error("Issue comment request completed without a persisted comment response");
12229
+ const rows = await ctx.api.get("/api/agents/me/inbox-lite") ?? [];
12230
+ if (ctx.json) {
12231
+ printOutput(rows, { json: true });
12232
+ return;
12233
+ }
12234
+ if (rows.length === 0) {
12235
+ printOutput([], { json: false });
12236
+ return;
12237
+ }
12238
+ for (const row of rows) {
12239
+ console.log(
12240
+ formatInlineRecord({
12241
+ identifier: row.identifier,
12242
+ id: row.id,
12243
+ relationship: row.relationship ?? "assignee",
12244
+ status: row.status,
12245
+ priority: row.priority,
12246
+ title: row.title,
12247
+ projectId: row.projectId,
12248
+ goalId: row.goalId,
12249
+ parentId: row.parentId,
12250
+ updatedAt: row.updatedAt
12251
+ })
12252
+ );
11156
12253
  }
11157
- printOutput(comment, { json: ctx.json });
11158
12254
  } catch (err) {
11159
12255
  handleCommandError(err);
11160
12256
  }
11161
12257
  })
11162
12258
  );
11163
12259
  addCommonClientOptions(
11164
- issue.command("review").description(getAgentCliCapabilityById("issue.review").description).argument("<issueId>", "Issue ID").requiredOption(
11165
- "--decision <decision>",
11166
- "Review decision: approve, request_changes, needs_followup, or blocked"
11167
- ).option("--comment-file <path>", "Read required review comment from a file, or '-' for stdin").action(async (issueId, opts) => {
12260
+ agent.command("capabilities").description(getAgentCliCapabilityById("agent.capabilities").description).option("--contract <name>", "Capability subset to show (agent-v1 or all)", "agent-v1").action(async (opts) => {
11168
12261
  try {
11169
- const ctx = resolveCommandContext(opts);
11170
- const decision = parseReviewDecision(opts.decision);
11171
- const comment = await resolveFileTextInput({
11172
- file: opts.commentFile,
11173
- fileOption: "--comment-file",
11174
- removedTextOption: "--comment",
11175
- required: true
11176
- });
11177
- const updated = await ctx.api.patch(`/api/issues/${issueId}`, {
11178
- reviewDecision: decision,
11179
- comment
11180
- });
11181
- printOutput(updated, { json: ctx.json });
12262
+ const contract = opts.contract === "all" ? "all" : "agent-v1";
12263
+ const capabilities = getAgentCliCapabilities().filter((entry) => contract === "all" ? true : entry.contract === contract);
12264
+ if (opts.json) {
12265
+ printOutput(buildAgentCliCapabilitiesManifest(contract), { json: true });
12266
+ return;
12267
+ }
12268
+ console.log(formatAgentCliCapabilitiesHumanReadable(capabilities));
11182
12269
  } catch (err) {
11183
12270
  handleCommandError(err);
11184
12271
  }
11185
12272
  })
11186
12273
  );
11187
12274
  addCommonClientOptions(
11188
- issue.command("commit").description(getAgentCliCapabilityById("issue.commit").description).argument("<issueId>", "Issue ID").requiredOption("--sha <sha>", "Commit SHA").requiredOption("--message <subject>", "Commit subject or message").option("--branch <name>", "Branch name").option("--repo-path <path>", "Repository path").option("--workspace-path <path>", "Workspace path").option("--count <n>", "Number of commits represented by this report").action(async (issueId, opts) => {
12275
+ agent.command("list").description(getAgentCliCapabilityById("agent.list").description).option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
11189
12276
  try {
11190
- const ctx = resolveCommandContext(opts);
11191
- const payload = reportIssueCommitSchema.parse({
11192
- sha: opts.sha,
11193
- message: opts.message,
11194
- branch: opts.branch,
11195
- repoPath: opts.repoPath,
11196
- workspacePath: opts.workspacePath,
11197
- commitCount: parseOptionalInt(opts.count)
11198
- });
11199
- const reported = await ctx.api.post(`/api/issues/${issueId}/commit`, payload);
11200
- printOutput(reported, { json: ctx.json });
12277
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12278
+ const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/agents`) ?? [];
12279
+ if (ctx.json) {
12280
+ printOutput(rows, { json: true });
12281
+ return;
12282
+ }
12283
+ if (rows.length === 0) {
12284
+ printOutput([], { json: false });
12285
+ return;
12286
+ }
12287
+ for (const row of rows) {
12288
+ console.log(
12289
+ formatInlineRecord({
12290
+ id: row.id,
12291
+ name: row.name,
12292
+ role: row.role,
12293
+ status: row.status,
12294
+ reportsTo: row.reportsTo,
12295
+ budgetMonthlyCents: row.budgetMonthlyCents,
12296
+ spentMonthlyCents: row.spentMonthlyCents
12297
+ })
12298
+ );
12299
+ }
11201
12300
  } catch (err) {
11202
12301
  handleCommandError(err);
11203
12302
  }
11204
- })
12303
+ }),
12304
+ { includeCompany: false }
11205
12305
  );
11206
12306
  addCommonClientOptions(
11207
- issue.command("done").description(getAgentCliCapabilityById("issue.done").description).argument("<issueId>", "Issue ID").option("--comment-file <path>", "Read required completion comment from a file, or '-' for stdin").option("--image <path>", "Image file to upload and append to the completion comment; may be repeated", collectImagePath, []).action(async (issueId, opts) => {
12307
+ config.command("index").description(getAgentCliCapabilityById("agent.config.index").description).action(async (opts) => {
11208
12308
  try {
11209
12309
  const ctx = resolveCommandContext(opts);
11210
- const commentText = await resolveFileTextInput({
11211
- file: opts.commentFile,
11212
- fileOption: "--comment-file",
11213
- removedTextOption: "--comment",
11214
- required: true
11215
- });
11216
- const comment = await appendUploadedIssueImages(ctx, issueId, commentText, opts.image);
11217
- const updated = await ctx.api.patch(`/api/issues/${issueId}`, {
11218
- status: "done",
11219
- comment
11220
- });
11221
- printOutput(updated, { json: ctx.json });
12310
+ const doc = await ctx.api.get("/llms/agent-configuration.txt");
12311
+ printOutput(doc ?? "", { json: ctx.json });
11222
12312
  } catch (err) {
11223
12313
  handleCommandError(err);
11224
12314
  }
11225
12315
  })
11226
12316
  );
11227
12317
  addCommonClientOptions(
11228
- issue.command("block").description(getAgentCliCapabilityById("issue.block").description).argument("<issueId>", "Issue ID").option("--comment-file <path>", "Read required blocker comment from a file, or '-' for stdin").option("--image <path>", "Image file to upload and append to the blocker comment; may be repeated", collectImagePath, []).action(async (issueId, opts) => {
12318
+ config.command("doc").description(getAgentCliCapabilityById("agent.config.doc").description).argument("<agentRuntimeType>", "Agent runtime type").action(async (agentRuntimeType, opts) => {
11229
12319
  try {
11230
12320
  const ctx = resolveCommandContext(opts);
11231
- const commentText = await resolveFileTextInput({
11232
- file: opts.commentFile,
11233
- fileOption: "--comment-file",
11234
- removedTextOption: "--comment",
11235
- required: true
11236
- });
11237
- const comment = await appendUploadedIssueImages(ctx, issueId, commentText, opts.image);
11238
- const updated = await ctx.api.patch(`/api/issues/${issueId}`, {
11239
- status: "blocked",
11240
- comment
11241
- });
11242
- printOutput(updated, { json: ctx.json });
12321
+ const doc = await ctx.api.get(
12322
+ `/llms/agent-configuration/${encodeURIComponent(agentRuntimeType)}.txt`
12323
+ );
12324
+ printOutput(doc ?? "", { json: ctx.json });
11243
12325
  } catch (err) {
11244
12326
  handleCommandError(err);
11245
12327
  }
11246
12328
  })
11247
12329
  );
11248
- const comments = issue.command("comments").description("Issue comment operations");
11249
12330
  addCommonClientOptions(
11250
- comments.command("list").description(getAgentCliCapabilityById("issue.comments.list").description).argument("<issueId>", "Issue ID").option("--after <commentId>", "Only return comments after this comment ID").option("--order <order>", "Comment ordering (asc or desc)", "desc").action(async (issueId, opts) => {
12331
+ config.command("list").description(getAgentCliCapabilityById("agent.config.list").description).option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
12332
+ try {
12333
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12334
+ const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/agent-configurations`) ?? [];
12335
+ if (ctx.json) {
12336
+ printOutput(rows, { json: true });
12337
+ return;
12338
+ }
12339
+ if (rows.length === 0) {
12340
+ printOutput([], { json: false });
12341
+ return;
12342
+ }
12343
+ for (const row of rows) {
12344
+ console.log(
12345
+ formatInlineRecord({
12346
+ id: row.id,
12347
+ name: row.name,
12348
+ role: row.role,
12349
+ status: row.status,
12350
+ agentRuntimeType: row.agentRuntimeType,
12351
+ reportsTo: row.reportsTo,
12352
+ updatedAt: row.updatedAt
12353
+ })
12354
+ );
12355
+ }
12356
+ } catch (err) {
12357
+ handleCommandError(err);
12358
+ }
12359
+ }),
12360
+ { includeCompany: false }
12361
+ );
12362
+ addCommonClientOptions(
12363
+ config.command("get").description(getAgentCliCapabilityById("agent.config.get").description).argument("<agentId>", "Agent ID or shortname/url-key").option("-O, --org-id <id>", "Organization ID").action(async (agentId, opts) => {
11251
12364
  try {
11252
12365
  const ctx = resolveCommandContext(opts);
11253
- const params = new URLSearchParams();
11254
- if (opts.after) params.set("after", opts.after);
11255
- if (opts.order) params.set("order", opts.order);
11256
- const query = params.toString();
11257
- const rows = await ctx.api.get(
11258
- `/api/issues/${issueId}/comments${query ? `?${query}` : ""}`
11259
- ) ?? [];
11260
- printOutput(rows, { json: ctx.json });
12366
+ const query = new URLSearchParams();
12367
+ if (ctx.orgId) query.set("orgId", ctx.orgId);
12368
+ const row = await ctx.api.get(
12369
+ `/api/agents/${encodeURIComponent(agentId)}/configuration${query.size > 0 ? `?${query.toString()}` : ""}`
12370
+ );
12371
+ printOutput(row, { json: ctx.json });
11261
12372
  } catch (err) {
11262
12373
  handleCommandError(err);
11263
12374
  }
11264
12375
  })
11265
12376
  );
11266
12377
  addCommonClientOptions(
11267
- comments.command("get").description(getAgentCliCapabilityById("issue.comments.get").description).argument("<issueId>", "Issue ID").argument("<commentId>", "Comment ID").action(async (issueId, commentId, opts) => {
12378
+ agent.command("icons").description(getAgentCliCapabilityById("agent.icons").description).action(async (opts) => {
11268
12379
  try {
11269
12380
  const ctx = resolveCommandContext(opts);
11270
- const row = await ctx.api.get(`/api/issues/${issueId}/comments/${commentId}`);
11271
- printOutput(row, { json: ctx.json });
12381
+ const doc = await ctx.api.get("/llms/agent-icons.txt");
12382
+ printOutput(doc ?? "", { json: ctx.json });
11272
12383
  } catch (err) {
11273
12384
  handleCommandError(err);
11274
12385
  }
11275
12386
  })
11276
12387
  );
11277
- const documents = issue.command("documents").description("Issue document operations");
12388
+ const skills = agent.command("skills").description("Agent skill selection operations");
11278
12389
  addCommonClientOptions(
11279
- documents.command("list").description(getAgentCliCapabilityById("issue.documents.list").description).argument("<issueId>", "Issue ID").action(async (issueId, opts) => {
12390
+ skills.command("create").description(getAgentCliCapabilityById("agent.skills.create").description).argument("[agent-id]", "Agent ID; defaults to RUDDER_AGENT_ID").requiredOption("--name <name>", "Skill display name").option("--slug <slug>", "Skill short name").option("--description <text>", "Skill description").option("--markdown <markdown>", "Full SKILL.md content").option("--markdown-file <path>", "Read full SKILL.md content from a local file").option("--enable", "Enable the created private skill for future runs").action(async (agentIdArg, opts) => {
11280
12391
  try {
11281
12392
  const ctx = resolveCommandContext(opts);
11282
- const rows = await ctx.api.get(`/api/issues/${issueId}/documents`) ?? [];
11283
- printOutput(rows, { json: ctx.json });
12393
+ const agentId = agentIdArg?.trim() || ctx.agentId;
12394
+ if (!agentId) {
12395
+ throw new Error("Agent ID is required. Pass [agent-id] or set RUDDER_AGENT_ID.");
12396
+ }
12397
+ if (opts.markdown && opts.markdownFile) {
12398
+ throw new Error("Pass only one of --markdown or --markdown-file.");
12399
+ }
12400
+ const markdown = opts.markdownFile ? await fs14.readFile(path18.resolve(opts.markdownFile), "utf8") : opts.markdown;
12401
+ const payload = organizationSkillCreateSchema.parse({
12402
+ name: opts.name,
12403
+ slug: opts.slug?.trim() || null,
12404
+ description: opts.description?.trim() || null,
12405
+ markdown: markdown ?? null
12406
+ });
12407
+ const created = await ctx.api.post(
12408
+ `/api/agents/${encodeURIComponent(agentId)}/skills/private`,
12409
+ payload
12410
+ );
12411
+ if (!created) {
12412
+ throw new Error("Failed to create agent skill");
12413
+ }
12414
+ let enabledSnapshot = null;
12415
+ if (opts.enable) {
12416
+ enabledSnapshot = await ctx.api.post(
12417
+ `/api/agents/${encodeURIComponent(agentId)}/skills/enable`,
12418
+ { skills: [created.selectionKey] }
12419
+ );
12420
+ }
12421
+ const result = { created, enabledSnapshot };
12422
+ if (ctx.json) {
12423
+ printOutput(result, { json: true });
12424
+ return;
12425
+ }
12426
+ console.log(
12427
+ formatInlineRecord({
12428
+ key: created.key,
12429
+ selectionKey: created.selectionKey,
12430
+ sourceClass: created.sourceClass,
12431
+ sourcePath: created.sourcePath,
12432
+ enabled: Boolean(enabledSnapshot?.desiredSkills.includes(created.selectionKey)),
12433
+ detail: opts.enable ? "Created and enabled. Future runs will load this skill." : "Created, not enabled. Future runs will not load it until enabled."
12434
+ })
12435
+ );
11284
12436
  } catch (err) {
11285
12437
  handleCommandError(err);
11286
12438
  }
11287
12439
  })
11288
12440
  );
11289
12441
  addCommonClientOptions(
11290
- documents.command("get").description(getAgentCliCapabilityById("issue.documents.get").description).argument("<issueId>", "Issue ID").argument("<key>", "Document key").action(async (issueId, key, opts) => {
12442
+ skills.command("enable").description(getAgentCliCapabilityById("agent.skills.enable").description).argument("<agentId>", "Agent ID").argument("<selectionRefs...>", "Skill selection refs to enable").action(async (agentId, selectionRefs, opts) => {
11291
12443
  try {
11292
12444
  const ctx = resolveCommandContext(opts);
11293
- const row = await ctx.api.get(`/api/issues/${issueId}/documents/${key}`);
11294
- printOutput(row, { json: ctx.json });
12445
+ const snapshot = await ctx.api.post(`/api/agents/${agentId}/skills/enable`, {
12446
+ skills: selectionRefs
12447
+ });
12448
+ printOutput(snapshot, { json: ctx.json });
11295
12449
  } catch (err) {
11296
12450
  handleCommandError(err);
11297
12451
  }
11298
12452
  })
11299
12453
  );
11300
12454
  addCommonClientOptions(
11301
- documents.command("put").description(getAgentCliCapabilityById("issue.documents.put").description).argument("<issueId>", "Issue ID").argument("<key>", "Document key").option("--body-file <path>", "Read document body from a file, or '-' for stdin").option("--title <text>", "Document title").option("--format <format>", "Document format", "markdown").option("--change-summary <text>", "Optional change summary").option("--base-revision-id <id>", "Latest revision id for optimistic concurrency").action(async (issueId, key, opts) => {
12455
+ skills.command("sync").description(getAgentCliCapabilityById("agent.skills.sync").description).argument("<agentId>", "Agent ID").requiredOption(
12456
+ "--desired-skills <csv>",
12457
+ "Comma-separated desired skill refs (for example rudder/rudder)"
12458
+ ).action(async (agentId, opts) => {
11302
12459
  try {
11303
12460
  const ctx = resolveCommandContext(opts);
11304
- const body = await resolveFileTextInput({
11305
- file: opts.bodyFile,
11306
- fileOption: "--body-file",
11307
- removedTextOption: "--body",
11308
- required: true
11309
- });
11310
- const payload = upsertIssueDocumentSchema.parse({
11311
- title: opts.title,
11312
- format: opts.format,
11313
- body,
11314
- changeSummary: opts.changeSummary,
11315
- baseRevisionId: opts.baseRevisionId
12461
+ const snapshot = await ctx.api.post(`/api/agents/${agentId}/skills/sync`, {
12462
+ desiredSkills: parseCsv3(opts.desiredSkills)
11316
12463
  });
11317
- const row = await ctx.api.put(`/api/issues/${issueId}/documents/${key}`, payload);
11318
- printOutput(row, { json: ctx.json });
12464
+ printOutput(snapshot, { json: ctx.json });
11319
12465
  } catch (err) {
11320
12466
  handleCommandError(err);
11321
12467
  }
11322
12468
  })
11323
12469
  );
11324
12470
  addCommonClientOptions(
11325
- documents.command("revisions").description(getAgentCliCapabilityById("issue.documents.revisions").description).argument("<issueId>", "Issue ID").argument("<key>", "Document key").action(async (issueId, key, opts) => {
12471
+ agent.command("get").description(getAgentCliCapabilityById("agent.get").description).argument("<agentId>", "Agent ID or shortname/url-key").option("-O, --org-id <id>", "Organization ID").action(async (agentId, opts) => {
11326
12472
  try {
11327
12473
  const ctx = resolveCommandContext(opts);
11328
- const rows = await ctx.api.get(`/api/issues/${issueId}/documents/${key}/revisions`) ?? [];
11329
- printOutput(rows, { json: ctx.json });
12474
+ const query = new URLSearchParams();
12475
+ if (ctx.orgId) query.set("orgId", ctx.orgId);
12476
+ const row = await ctx.api.get(
12477
+ `/api/agents/${encodeURIComponent(agentId)}${query.size > 0 ? `?${query.toString()}` : ""}`
12478
+ );
12479
+ printOutput(row, { json: ctx.json });
11330
12480
  } catch (err) {
11331
12481
  handleCommandError(err);
11332
12482
  }
11333
12483
  })
11334
12484
  );
11335
12485
  addCommonClientOptions(
11336
- issue.command("checkout").description(getAgentCliCapabilityById("issue.checkout").description).argument("<issueId>", "Issue ID").option("--agent-id <id>", "Agent ID (defaults to RUDDER_AGENT_ID)").option(
11337
- "--expected-statuses <csv>",
11338
- "Expected current statuses",
11339
- "todo,backlog,blocked"
11340
- ).action(async (issueId, opts) => {
12486
+ agent.command("update").description(getAgentCliCapabilityById("agent.update").description).argument("[agentId]", "Agent ID or shortname/url-key; defaults to RUDDER_AGENT_ID").option("-O, --org-id <id>", "Organization ID").option("--name <name>", "Agent display name").option("--role <role>", "Agent organization role").option("--title <title>", "Agent title shown in mentions and agent labels").option("--clear-title", "Clear the agent title").option("--capabilities <text>", "Agent capabilities/description text").option("--capabilities-file <path>", "Read agent capabilities/description from a local file").option("--description <text>", "Alias for --capabilities").option("--description-file <path>", "Alias for --capabilities-file").option("--clear-capabilities", "Clear the agent capabilities/description").option("--clear-description", "Alias for --clear-capabilities").option("--reports-to <agent-id>", "Manager agent ID").option("--clear-reports-to", "Clear the manager relationship").action(async (agentIdArg, opts) => {
11341
12487
  try {
11342
12488
  const ctx = resolveCommandContext(opts);
11343
- const agentId = opts.agentId?.trim() || ctx.agentId;
12489
+ const agentId = agentIdArg?.trim() || ctx.agentId;
11344
12490
  if (!agentId) {
11345
- throw new Error("Agent ID is required. Pass --agent-id or set RUDDER_AGENT_ID.");
12491
+ throw new Error("Agent ID is required. Pass [agent-id] or set RUDDER_AGENT_ID.");
11346
12492
  }
11347
- const payload = checkoutIssueSchema.parse({
11348
- agentId,
11349
- expectedStatuses: parseCsv(opts.expectedStatuses)
11350
- });
11351
- const updated = await ctx.api.post(`/api/issues/${issueId}/checkout`, payload);
12493
+ const patch = await buildAgentUpdatePatch(opts);
12494
+ if (Object.keys(patch).length === 0) {
12495
+ throw new Error(
12496
+ "No agent fields to update. Pass --name, --role, --title, --capabilities, --description, or --reports-to."
12497
+ );
12498
+ }
12499
+ const query = new URLSearchParams();
12500
+ if (ctx.orgId) query.set("orgId", ctx.orgId);
12501
+ const updated = await ctx.api.patch(
12502
+ `/api/agents/${encodeURIComponent(agentId)}${query.size > 0 ? `?${query.toString()}` : ""}`,
12503
+ patch
12504
+ );
11352
12505
  printOutput(updated, { json: ctx.json });
11353
12506
  } catch (err) {
11354
12507
  handleCommandError(err);
@@ -11356,243 +12509,193 @@ function registerIssueCommands(program) {
11356
12509
  })
11357
12510
  );
11358
12511
  addCommonClientOptions(
11359
- issue.command("release").description(getAgentCliCapabilityById("issue.release").description).argument("<issueId>", "Issue ID").action(async (issueId, opts) => {
12512
+ agent.command("hire").description(getAgentCliCapabilityById("agent.hire").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--payload <json>", "Hire payload as JSON object").action(async (opts) => {
11360
12513
  try {
11361
- const ctx = resolveCommandContext(opts);
11362
- const updated = await ctx.api.post(`/api/issues/${issueId}/release`, {});
11363
- printOutput(updated, { json: ctx.json });
12514
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12515
+ const payloadJson = parseJsonObject(opts.payload, "payload");
12516
+ const payload = createAgentHireSchema.parse(payloadJson);
12517
+ const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/agent-hires`, payload);
12518
+ printOutput(created, { json: ctx.json });
11364
12519
  } catch (err) {
11365
12520
  handleCommandError(err);
11366
12521
  }
11367
- })
12522
+ }),
12523
+ { includeCompany: false }
12524
+ );
12525
+ addCommonClientOptions(
12526
+ agent.command("local-cli").description(
12527
+ "Create an agent API key, optionally install local Rudder skills for direct Codex/Claude CLI use, and print shell exports"
12528
+ ).argument("<agentRef>", "Agent ID or shortname/url-key").option("-O, --org-id <id>", "Organization ID").option("--key-name <name>", "API key label", "local-cli").option(
12529
+ "--no-install-skills",
12530
+ "Skip the optional local Codex/Claude skill installation step"
12531
+ ).action(async (agentRef, opts) => {
12532
+ try {
12533
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12534
+ const query = new URLSearchParams({ orgId: ctx.orgId ?? "" });
12535
+ const agentRow = await ctx.api.get(
12536
+ `/api/agents/${encodeURIComponent(agentRef)}?${query.toString()}`
12537
+ );
12538
+ if (!agentRow) {
12539
+ throw new Error(`Agent not found: ${agentRef}`);
12540
+ }
12541
+ const now = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
12542
+ const keyName = opts.keyName?.trim() ? opts.keyName.trim() : `local-cli-${now}`;
12543
+ const key = await ctx.api.post(`/api/agents/${agentRow.id}/keys`, { name: keyName });
12544
+ if (!key) {
12545
+ throw new Error("Failed to create API key");
12546
+ }
12547
+ const installSummaries = [];
12548
+ if (opts.installSkills !== false) {
12549
+ const skillsDir = await resolveRudderSkillsDir(__moduleDir, [path18.resolve(process.cwd(), "skills")]);
12550
+ if (!skillsDir) {
12551
+ throw new Error(
12552
+ "Could not locate local Rudder skills directory. Expected ./skills in the repo checkout."
12553
+ );
12554
+ }
12555
+ installSummaries.push(
12556
+ await installSkillsForTarget(skillsDir, codexSkillsHome(), "codex"),
12557
+ await installSkillsForTarget(skillsDir, claudeSkillsHome(), "claude")
12558
+ );
12559
+ }
12560
+ const exportsText = buildAgentEnvExports({
12561
+ apiBase: ctx.api.apiBase,
12562
+ orgId: agentRow.orgId,
12563
+ agentId: agentRow.id,
12564
+ apiKey: key.token
12565
+ });
12566
+ if (ctx.json) {
12567
+ printOutput(
12568
+ {
12569
+ agent: {
12570
+ id: agentRow.id,
12571
+ name: agentRow.name,
12572
+ urlKey: agentRow.urlKey,
12573
+ orgId: agentRow.orgId
12574
+ },
12575
+ key: {
12576
+ id: key.id,
12577
+ name: key.name,
12578
+ createdAt: key.createdAt,
12579
+ token: key.token
12580
+ },
12581
+ skills: installSummaries,
12582
+ exports: exportsText
12583
+ },
12584
+ { json: true }
12585
+ );
12586
+ return;
12587
+ }
12588
+ console.log(`Agent: ${agentRow.name} (${agentRow.id})`);
12589
+ console.log(`API key created: ${key.name} (${key.id})`);
12590
+ if (installSummaries.length > 0) {
12591
+ for (const summary of installSummaries) {
12592
+ console.log(
12593
+ `${summary.tool}: linked=${summary.linked.length} removed=${summary.removed.length} skipped=${summary.skipped.length} failed=${summary.failed.length} target=${summary.target}`
12594
+ );
12595
+ for (const failed of summary.failed) {
12596
+ console.log(` failed ${failed.name}: ${failed.error}`);
12597
+ }
12598
+ }
12599
+ }
12600
+ console.log("");
12601
+ console.log("# Run this in your shell before launching codex/claude:");
12602
+ console.log(exportsText);
12603
+ } catch (err) {
12604
+ handleCommandError(err);
12605
+ }
12606
+ }),
12607
+ { includeCompany: false }
11368
12608
  );
11369
12609
  }
11370
- function collectImagePath(value, previous) {
11371
- const trimmed = value.trim();
11372
- if (!trimmed) {
11373
- throw new Error("--image path cannot be empty");
11374
- }
11375
- return [...previous, trimmed];
11376
- }
11377
- function collectNonEmptyOption(optionName) {
11378
- return (value, previous) => {
11379
- const trimmed = value.trim();
11380
- if (!trimmed) {
11381
- throw new Error(`${optionName} cannot be empty`);
11382
- }
11383
- return [...previous, trimmed];
11384
- };
11385
- }
11386
- async function resolveFileTextInput(opts) {
11387
- if (opts.removedTextOption && process.argv.includes(opts.removedTextOption)) {
11388
- throw new Error(`${opts.removedTextOption} was removed; write the body to a file and use ${opts.fileOption} <path> or ${opts.fileOption} - for stdin`);
11389
- }
11390
- const hasFile = opts.file !== void 0;
11391
- if (hasFile) return readTextInputFile(opts.file, opts.fileOption);
11392
- if (opts.required) {
11393
- throw new Error(`Provide ${opts.fileOption} <path>; use ${opts.fileOption} - for stdin`);
11394
- }
11395
- return void 0;
11396
- }
11397
- async function readTextInputFile(inputPath, optionName) {
11398
- if (inputPath === "-") {
11399
- return readStdinText();
11400
- }
11401
- const resolvedPath = path16.resolve(process.cwd(), inputPath);
11402
- return readFile4(resolvedPath, "utf8").catch((err) => {
11403
- throw new Error(`Unable to read ${optionName} ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
11404
- });
11405
- }
11406
- async function readStdinText() {
11407
- const chunks = [];
11408
- for await (const chunk of process.stdin) {
11409
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
11410
- }
11411
- return Buffer.concat(chunks).toString("utf8");
11412
- }
11413
- async function resolveIssueLabelIds(ctx, opts) {
11414
- const explicitIds = opts.labelId ?? [];
11415
- const names = opts.label ?? [];
11416
- if (names.length === 0 && explicitIds.length > 0) return [...new Set(explicitIds)];
11417
- if (names.length === 0 && opts.parentId) return [];
11418
- const labels = await ctx.api.get(`/api/orgs/${ctx.orgId}/labels`) ?? [];
11419
- if (names.length === 0) {
11420
- if (ctx.agentId && labels.length >= 5) {
11421
- throw new Error(
11422
- `Organization has ${labels.length} issue labels. Choose at least one with --label-id <id> or --label <name>. Available labels: ${formatAvailableLabelNames(labels)}`
11423
- );
11424
- }
11425
- return [];
11426
- }
11427
- const resolvedIds = names.map((name) => {
11428
- const exact = labels.find((label) => label.name === name) ?? labels.find((label) => label.name.toLowerCase() === name.toLowerCase());
11429
- if (!exact) {
11430
- throw new Error(`Unknown issue label "${name}". Available labels: ${formatAvailableLabelNames(labels)}`);
11431
- }
11432
- return exact.id;
11433
- });
11434
- return [.../* @__PURE__ */ new Set([...explicitIds, ...resolvedIds])];
11435
- }
11436
- function formatAvailableLabelNames(labels) {
11437
- return labels.map((label) => label.name).join(", ") || "(none)";
11438
- }
11439
- async function appendUploadedIssueImages(ctx, issueId, body, imagePaths) {
11440
- const paths = imagePaths ?? [];
11441
- if (paths.length === 0) return body;
11442
- const issue = await ctx.api.get(`/api/issues/${issueId}`);
11443
- if (!issue) {
11444
- throw new Error("Issue not found");
12610
+ async function buildAgentUpdatePatch(opts) {
12611
+ const rawPatch = {};
12612
+ if (opts.name !== void 0) rawPatch.name = opts.name;
12613
+ if (opts.role !== void 0) rawPatch.role = opts.role;
12614
+ if (opts.title !== void 0 && opts.clearTitle) {
12615
+ throw new Error("Pass only one of --title or --clear-title.");
11445
12616
  }
11446
- const links = [];
11447
- for (const imagePath of paths) {
11448
- const attachment = await uploadIssueCommentImage(ctx, issue, imagePath);
11449
- links.push(formatAttachmentMarkdown(attachment));
12617
+ if (opts.title !== void 0) rawPatch.title = opts.title;
12618
+ if (opts.clearTitle) rawPatch.title = null;
12619
+ if (opts.reportsTo !== void 0 && opts.clearReportsTo) {
12620
+ throw new Error("Pass only one of --reports-to or --clear-reports-to.");
11450
12621
  }
11451
- const base = body?.trimEnd() ?? "";
11452
- const imageBlock = links.join("\n");
11453
- return base ? `${base}
11454
-
11455
- ${imageBlock}` : imageBlock;
11456
- }
11457
- async function uploadIssueCommentImage(ctx, issue, imagePath) {
11458
- const resolvedPath = path16.resolve(process.cwd(), imagePath);
11459
- const stats = await stat4(resolvedPath).catch((err) => {
11460
- throw new Error(`Unable to read image ${imagePath}: ${err instanceof Error ? err.message : String(err)}`);
11461
- });
11462
- if (!stats.isFile()) {
11463
- throw new Error(`Image path must be a file: ${imagePath}`);
12622
+ if (opts.reportsTo !== void 0) rawPatch.reportsTo = opts.reportsTo;
12623
+ if (opts.clearReportsTo) rawPatch.reportsTo = null;
12624
+ const capabilitiesInputs = [
12625
+ opts.capabilities !== void 0 ? "--capabilities" : null,
12626
+ opts.capabilitiesFile !== void 0 ? "--capabilities-file" : null,
12627
+ opts.description !== void 0 ? "--description" : null,
12628
+ opts.descriptionFile !== void 0 ? "--description-file" : null
12629
+ ].filter(Boolean);
12630
+ const clearCapabilities = opts.clearCapabilities || opts.clearDescription;
12631
+ if (capabilitiesInputs.length > 1) {
12632
+ throw new Error(
12633
+ "Pass only one of --capabilities, --capabilities-file, --description, or --description-file."
12634
+ );
11464
12635
  }
11465
- const filename = path16.basename(resolvedPath);
11466
- const contentType = inferCommentImageContentType(filename);
11467
- const buffer = await readFile4(resolvedPath);
11468
- if (buffer.length <= 0) {
11469
- throw new Error(`Image is empty: ${imagePath}`);
12636
+ if (capabilitiesInputs.length > 0 && clearCapabilities) {
12637
+ throw new Error("Pass either capabilities/description input or a clear flag, not both.");
11470
12638
  }
11471
- const form = new FormData();
11472
- form.set("usage", "comment_inline");
11473
- form.set("file", new Blob([buffer], { type: contentType }), filename);
11474
- const attachment = await ctx.api.postForm(
11475
- `/api/orgs/${issue.orgId}/issues/${issue.id}/attachments`,
11476
- form
11477
- );
11478
- if (!attachment) {
11479
- throw new Error(`Image upload returned no attachment: ${imagePath}`);
12639
+ if (opts.capabilities !== void 0) rawPatch.capabilities = opts.capabilities;
12640
+ if (opts.description !== void 0) rawPatch.capabilities = opts.description;
12641
+ if (opts.capabilitiesFile !== void 0) {
12642
+ rawPatch.capabilities = await fs14.readFile(path18.resolve(opts.capabilitiesFile), "utf8");
11480
12643
  }
11481
- return attachment;
11482
- }
11483
- function inferCommentImageContentType(filename) {
11484
- const ext = path16.extname(filename).toLowerCase();
11485
- switch (ext) {
11486
- case ".png":
11487
- return "image/png";
11488
- case ".jpg":
11489
- case ".jpeg":
11490
- return "image/jpeg";
11491
- case ".webp":
11492
- return "image/webp";
11493
- case ".gif":
11494
- return "image/gif";
11495
- default:
11496
- throw new Error(`Unsupported comment image type: ${filename}. Use PNG, JPEG, WebP, or GIF.`);
12644
+ if (opts.descriptionFile !== void 0) {
12645
+ rawPatch.capabilities = await fs14.readFile(path18.resolve(opts.descriptionFile), "utf8");
11497
12646
  }
12647
+ if (clearCapabilities) rawPatch.capabilities = null;
12648
+ return updateAgentSchema.parse(rawPatch);
11498
12649
  }
11499
- function formatAttachmentMarkdown(attachment) {
11500
- const alt = escapeMarkdownAltText(attachment.originalFilename ?? "image");
11501
- return `![${alt}](${attachment.contentPath})`;
11502
- }
11503
- function escapeMarkdownAltText(value) {
11504
- return value.replaceAll("\\", "\\\\").replaceAll("]", "\\]");
11505
- }
11506
- function parseCsv(value) {
12650
+ function parseCsv3(value) {
11507
12651
  if (!value) return [];
11508
- return value.split(",").map((v) => v.trim()).filter(Boolean);
11509
- }
11510
- function parseOptionalInt(value) {
11511
- if (value === void 0) return void 0;
11512
- const parsed = Number.parseInt(value, 10);
11513
- if (!Number.isFinite(parsed)) {
11514
- throw new Error(`Invalid integer value: ${value}`);
11515
- }
11516
- return parsed;
11517
- }
11518
- function parseHiddenAt(value) {
11519
- if (value === void 0) return void 0;
11520
- if (value.trim().toLowerCase() === "null") return null;
11521
- return value;
11522
- }
11523
- function parseReviewDecision(value) {
11524
- const normalized = value.trim();
11525
- if (normalized === "approve" || normalized === "request_changes" || normalized === "needs_followup" || normalized === "blocked") {
11526
- return normalized;
11527
- }
11528
- throw new Error("Invalid review decision. Use approve, request_changes, needs_followup, or blocked.");
11529
- }
11530
- function isIssueCommentResponse(value) {
11531
- return Boolean(
11532
- value && typeof value.id === "string" && value.id.trim().length > 0 && typeof value.issueId === "string" && value.issueId.trim().length > 0 && typeof value.body === "string"
11533
- );
11534
- }
11535
- function filterIssueRows(rows, match) {
11536
- if (!match?.trim()) return rows;
11537
- const needle = match.trim().toLowerCase();
11538
- return rows.filter((row) => {
11539
- const text6 = [row.identifier, row.title, row.description].filter((part) => Boolean(part)).join("\n").toLowerCase();
11540
- return text6.includes(needle);
11541
- });
11542
- }
11543
- function buildIssueListPath(orgId, opts, searchQuery) {
11544
- const params = new URLSearchParams();
11545
- if (opts.status) params.set("status", opts.status);
11546
- if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
11547
- if (opts.projectId) params.set("projectId", opts.projectId);
11548
- if (searchQuery?.trim()) {
11549
- params.set("q", searchQuery.trim());
11550
- params.set("searchFields", "title,description,comment");
11551
- }
11552
- const query = params.toString();
11553
- return `/api/orgs/${orgId}/issues${query ? `?${query}` : ""}`;
12652
+ return value.split(",").map((entry) => entry.trim()).filter(Boolean);
11554
12653
  }
11555
- function printIssueRows(rows, json) {
11556
- if (json) {
11557
- printOutput(rows, { json: true });
11558
- return;
11559
- }
11560
- if (rows.length === 0) {
11561
- printOutput([], { json: false });
11562
- return;
11563
- }
11564
- for (const item of rows) {
11565
- console.log(
11566
- formatInlineRecord({
11567
- identifier: item.identifier,
11568
- id: item.id,
11569
- status: item.status,
11570
- priority: item.priority,
11571
- assigneeAgentId: item.assigneeAgentId,
11572
- assigneeUserId: item.assigneeUserId,
11573
- title: item.title,
11574
- projectId: item.projectId,
11575
- updatedAt: item.updatedAt,
11576
- ...item.searchMatch ? { match: formatIssueSearchMatch(item.searchMatch) } : {}
11577
- })
11578
- );
12654
+ function parseJsonObject(value, name) {
12655
+ try {
12656
+ const parsed = JSON.parse(value);
12657
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
12658
+ throw new Error(`${name} must be a JSON object`);
12659
+ }
12660
+ return parsed;
12661
+ } catch (err) {
12662
+ throw new Error(`Invalid ${name} JSON: ${err instanceof Error ? err.message : String(err)}`);
11579
12663
  }
11580
12664
  }
11581
- function formatIssueSearchMatch(match) {
11582
- const commentSuffix = match.commentId ? `#${match.commentId}` : "";
11583
- return `${match.field}${commentSuffix}: ${match.snippet}`;
11584
- }
11585
12665
 
11586
- // src/commands/client/project.ts
12666
+ // src/commands/client/approval.ts
11587
12667
  init_dist();
11588
- function registerProjectCommands(program) {
11589
- const project = program.command("project").description("Project operations");
12668
+ import { readFile as readFile5 } from "node:fs/promises";
12669
+ import path19 from "node:path";
12670
+ function registerApprovalCommands(program) {
12671
+ const approval = program.command("approval").description("Approval operations");
11590
12672
  addCommonClientOptions(
11591
- project.command("list").description(getAgentCliCapabilityById("project.list").description).option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
12673
+ approval.command("list").description("List approvals for an organization").option("-O, --org-id <id>", "Organization ID").option("--status <status>", "Status filter").action(async (opts) => {
11592
12674
  try {
11593
12675
  const ctx = resolveCommandContext(opts, { requireCompany: true });
11594
- const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/projects`) ?? [];
11595
- printOutput(rows, { json: ctx.json });
12676
+ const params = new URLSearchParams();
12677
+ if (opts.status) params.set("status", opts.status);
12678
+ const query = params.toString();
12679
+ const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/approvals${query ? `?${query}` : ""}`) ?? [];
12680
+ if (ctx.json) {
12681
+ printOutput(rows, { json: true });
12682
+ return;
12683
+ }
12684
+ if (rows.length === 0) {
12685
+ printOutput([], { json: false });
12686
+ return;
12687
+ }
12688
+ for (const row of rows) {
12689
+ console.log(
12690
+ formatInlineRecord({
12691
+ id: row.id,
12692
+ type: row.type,
12693
+ status: row.status,
12694
+ requestedByAgentId: row.requestedByAgentId,
12695
+ requestedByUserId: row.requestedByUserId
12696
+ })
12697
+ );
12698
+ }
11596
12699
  } catch (err) {
11597
12700
  handleCommandError(err);
11598
12701
  }
@@ -11600,32 +12703,39 @@ function registerProjectCommands(program) {
11600
12703
  { includeCompany: false }
11601
12704
  );
11602
12705
  addCommonClientOptions(
11603
- project.command("get").description(getAgentCliCapabilityById("project.get").description).argument("<projectIdOrShortname>", "Project ID or shortname").option("-O, --org-id <id>", "Organization ID for shortname resolution").action(async (projectRef, opts) => {
12706
+ approval.command("get").description(getAgentCliCapabilityById("approval.get").description).argument("<approvalId>", "Approval ID").action(async (approvalId, opts) => {
11604
12707
  try {
11605
12708
  const ctx = resolveCommandContext(opts);
11606
- const row = await ctx.api.get(projectPath(projectRef, ctx.orgId));
12709
+ const row = await ctx.api.get(`/api/approvals/${approvalId}`);
11607
12710
  printOutput(row, { json: ctx.json });
11608
12711
  } catch (err) {
11609
12712
  handleCommandError(err);
11610
12713
  }
11611
- }),
11612
- { includeCompany: false }
12714
+ })
11613
12715
  );
11614
12716
  addCommonClientOptions(
11615
- project.command("create").description(getAgentCliCapabilityById("project.create").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--name <name>", "Project name").option("--description <text>", "Project description").option("--status <status>", "Project status").option("--goal-id <id>", "Primary goal ID").option("--goal-ids <csv>", "Comma-separated goal IDs").option("--lead-agent-id <id>", "Lead agent ID").option("--target-date <date>", "Target date").option("--color <value>", "Project color or supported gradient token").action(async (opts) => {
12717
+ approval.command("issues").description(getAgentCliCapabilityById("approval.issues").description).argument("<approvalId>", "Approval ID").action(async (approvalId, opts) => {
12718
+ try {
12719
+ const ctx = resolveCommandContext(opts);
12720
+ const rows = await ctx.api.get(`/api/approvals/${approvalId}/issues`) ?? [];
12721
+ printOutput(rows, { json: ctx.json });
12722
+ } catch (err) {
12723
+ handleCommandError(err);
12724
+ }
12725
+ })
12726
+ );
12727
+ addCommonClientOptions(
12728
+ approval.command("create").description(getAgentCliCapabilityById("approval.create").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--type <type>", "Approval type (hire_agent|approve_ceo_strategy)").requiredOption("--payload <json>", "Approval payload as JSON object").option("--requested-by-agent-id <id>", "Requesting agent ID").option("--issue-ids <csv>", "Comma-separated linked issue IDs").action(async (opts) => {
11616
12729
  try {
11617
12730
  const ctx = resolveCommandContext(opts, { requireCompany: true });
11618
- const payload = createProjectSchema.parse({
11619
- name: opts.name,
11620
- description: opts.description,
11621
- status: opts.status,
11622
- goalId: opts.goalId,
11623
- goalIds: parseCsv2(opts.goalIds),
11624
- leadAgentId: opts.leadAgentId,
11625
- targetDate: opts.targetDate,
11626
- color: opts.color
12731
+ const payloadJson = parseJsonObject2(opts.payload, "payload");
12732
+ const payload = createApprovalSchema.parse({
12733
+ type: opts.type,
12734
+ payload: payloadJson,
12735
+ requestedByAgentId: opts.requestedByAgentId,
12736
+ issueIds: parseCsv4(opts.issueIds)
11627
12737
  });
11628
- const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/projects`, payload);
12738
+ const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/approvals`, payload);
11629
12739
  printOutput(created, { json: ctx.json });
11630
12740
  } catch (err) {
11631
12741
  handleCommandError(err);
@@ -11634,312 +12744,132 @@ function registerProjectCommands(program) {
11634
12744
  { includeCompany: false }
11635
12745
  );
11636
12746
  addCommonClientOptions(
11637
- project.command("update").description(getAgentCliCapabilityById("project.update").description).argument("<projectIdOrShortname>", "Project ID or shortname").option("-O, --org-id <id>", "Organization ID for shortname resolution").option("--name <name>", "Project name").option("--description <text>", "Project description").option("--status <status>", "Project status").option("--goal-id <id>", "Primary goal ID").option("--goal-ids <csv>", "Comma-separated goal IDs").option("--lead-agent-id <id>", "Lead agent ID").option("--target-date <date>", "Target date").option("--color <value>", "Project color or supported gradient token").option("--archived-at <iso8601|null>", "Set archivedAt timestamp or literal 'null'").action(async (projectRef, opts) => {
12747
+ approval.command("approve").description("Approve an approval request").argument("<approvalId>", "Approval ID").option("--decision-note <text>", "Decision note").option("--decided-by-user-id <id>", "Decision actor user ID").action(async (approvalId, opts) => {
11638
12748
  try {
11639
12749
  const ctx = resolveCommandContext(opts);
11640
- const payload = updateProjectSchema.parse({
11641
- name: opts.name,
11642
- description: opts.description,
11643
- status: opts.status,
11644
- goalId: opts.goalId,
11645
- goalIds: parseCsv2(opts.goalIds),
11646
- leadAgentId: opts.leadAgentId,
11647
- targetDate: opts.targetDate,
11648
- color: opts.color,
11649
- archivedAt: parseNullableOption(opts.archivedAt)
12750
+ const payload = resolveApprovalSchema.parse({
12751
+ decisionNote: opts.decisionNote,
12752
+ decidedByUserId: opts.decidedByUserId
11650
12753
  });
11651
- const updated = await ctx.api.patch(projectPath(projectRef, ctx.orgId), payload);
12754
+ const updated = await ctx.api.post(`/api/approvals/${approvalId}/approve`, payload);
11652
12755
  printOutput(updated, { json: ctx.json });
11653
12756
  } catch (err) {
11654
12757
  handleCommandError(err);
11655
12758
  }
11656
- }),
11657
- { includeCompany: false }
11658
- );
11659
- }
11660
- function parseCsv2(value) {
11661
- if (!value) return void 0;
11662
- const items = value.split(",").map((item) => item.trim()).filter(Boolean);
11663
- return items.length > 0 ? items : void 0;
11664
- }
11665
- function parseNullableOption(value) {
11666
- if (value === void 0) return void 0;
11667
- return value === "null" ? null : value;
11668
- }
11669
- function projectPath(projectRef, orgId) {
11670
- const params = new URLSearchParams();
11671
- if (orgId) params.set("orgId", orgId);
11672
- const query = params.toString();
11673
- return `/api/projects/${encodeURIComponent(projectRef)}${query ? `?${query}` : ""}`;
11674
- }
11675
-
11676
- // src/commands/client/agent.ts
11677
- init_dist();
11678
-
11679
- // ../packages/agent-runtime-utils/dist/server-utils.process.js
11680
- var MAX_CAPTURE_BYTES = 4 * 1024 * 1024;
11681
- var MAX_EXCERPT_BYTES = 32 * 1024;
11682
- var RUDDER_SKILL_ROOT_RELATIVE_CANDIDATES = [
11683
- "../../server/resources/bundled-skills",
11684
- "../../skills",
11685
- "../../../../../server/resources/bundled-skills"
11686
- ];
11687
- function normalizePathSlashes(value) {
11688
- return value.replaceAll("\\", "/");
11689
- }
11690
- function isMaintainerOnlySkillTarget(candidate) {
11691
- const normalized = normalizePathSlashes(candidate);
11692
- return normalized.includes("/server/resources/bundled-skills/") || normalized.includes("/.agents/skills/");
11693
- }
11694
-
11695
- // ../packages/agent-runtime-utils/dist/server-utils.prompts.js
11696
- var RUDDER_AGENT_OPERATING_CONTRACT = [
11697
- "# Rudder Agent Operating Contract",
11698
- "",
11699
- "Your home directory is `$AGENT_HOME`. Everything personal to you -- life, memory, knowledge -- lives there. Other agents may have their own folders and you may update them when necessary.",
11700
- "",
11701
- "Use these paths consistently:",
11702
- "",
11703
- "- Personal instructions live under `$AGENT_HOME/instructions`.",
11704
- "- Personal memory lives under `$AGENT_HOME/memory`.",
11705
- "- Tacit memory instruction lives at `$AGENT_HOME/instructions/MEMORY.md` and is automatically loaded when present.",
11706
- "- Personal skills live under `$AGENT_HOME/skills`.",
11707
- "- Shared organization workspace root lives under `$RUDDER_ORG_WORKSPACE_ROOT`.",
11708
- "- Shared organization skills live under `$RUDDER_ORG_SKILLS_DIR`.",
11709
- '- Library-backed project resources use `sourceType: "library"`; their `locator` points into `library:projects/<project-name>/`.',
11710
- "- Project Context is explicit operator-curated context, not the whole knowledge boundary. When it is insufficient, inspect broader Library and org workspace know-how before concluding context is missing.",
11711
- "- Durable generated project work files should be written under `library:projects/<project-name>/`.",
11712
- "- When you create or update a durable Library file, always include a user-visible Markdown link to that file in your final chat reply or issue comment. Use `library-file://file?p=<url-encoded-relative-path>&t=<url-encoded-title>` for Library file links.",
11713
- "- Use `/tmp` only for transient scratch files and temporary verification files; do not put durable work product there.",
11714
- "- Local trusted runtimes may expose the host operator home as `$RUDDER_OPERATOR_HOME`; use it only when a local skill or script intentionally needs operator-owned desktop app or CLI state. Do not replace `$HOME` with it.",
11715
- "",
11716
- "When you create or copy a skill under `$AGENT_HOME/skills/<slug>/`, check the agent's Skills snapshot before claiming it will load in future runs. If it is installed but not enabled, say exactly that future runs will not load it until enabled, and offer to enable it with `rudder agent skills enable <agent-id> <selection-ref>` when you have permission.",
11717
- "",
11718
- "When you write issue comments or chat replies, match the language of the user's or board's most recent substantive message unless they explicitly ask for a different language.",
11719
- "When you mention a web page, issue URL, external dashboard, or other user-openable target in an issue comment or chat reply, write it as a clickable Markdown link with a descriptive label, for example `[NameSilo transfer page](https://www.namesilo.com/account_domain_manage_transfer.php)`. Do not put action URLs in backticks or code blocks unless you are showing literal code or a command.",
11720
- "",
11721
- "Mention-triggered comment wakes arrive with `RUDDER_WAKE_COMMENT_ID`; read that wake comment before acting. Board/operator issue comments can wake an agent with a plain `@AgentName` token or an issue-composer agent mention serialized as `agent://agent-id?intent=wake`. Plain structured links such as `agent://agent-id` are reference-only links for rendering and navigation. Agent-authored issue comments do not fan out peer wakeups by default; use reviewer, assignment, or explicit handoff workflow instead. Mentioning an agent requests attention or collaboration; it does not transfer issue ownership unless the comment also makes an explicit handoff and normal checkout rules allow it.",
11722
- "",
11723
- "When an issue comment, done comment, or blocker comment cites visual evidence from a local screenshot/image path, attach the image with the Rudder CLI `--image <path>` option instead of leaving only the filesystem path in the text.",
11724
- "",
11725
- "## Memory and Shared Work Notes",
11726
- "",
11727
- "You MUST use the `para-memory-files` skill for all memory operations: storing facts, writing daily notes, creating entities, running weekly synthesis, recalling past context, and managing shared work notes. The skill defines your three-layer memory system (knowledge graph, daily notes, tacit knowledge), the PARA folder structure, atomic fact schemas, memory decay rules, and recall conventions.",
11728
- "",
11729
- "Keep stable preferences and operating lessons in `$AGENT_HOME/instructions/MEMORY.md`. Use `$AGENT_HOME/memory/YYYY-MM-DD.md` for daily notes and `$AGENT_HOME/life/` for structured long-term memory; those files are not auto-loaded.",
11730
- "",
11731
- "Invoke it whenever you need to remember, retrieve, or organize anything.",
11732
- "",
11733
- "## Safety Considerations",
11734
- "",
11735
- "- Never exfiltrate secrets or private data.",
11736
- "- Do not perform any destructive commands unless explicitly requested by the board."
11737
- ].join("\n");
11738
-
11739
- // ../packages/agent-runtime-utils/dist/server-utils.cli.js
11740
- import { promises as fs13 } from "node:fs";
11741
- import path17 from "node:path";
11742
- async function resolveRudderSkillsDir(moduleDir, additionalCandidates = []) {
11743
- const candidates = [
11744
- ...RUDDER_SKILL_ROOT_RELATIVE_CANDIDATES.map((relativePath) => path17.resolve(moduleDir, relativePath)),
11745
- ...additionalCandidates.map((candidate) => path17.resolve(candidate))
11746
- ];
11747
- const seenRoots = /* @__PURE__ */ new Set();
11748
- for (const root of candidates) {
11749
- if (seenRoots.has(root))
11750
- continue;
11751
- seenRoots.add(root);
11752
- const isDirectory = await fs13.stat(root).then((stats) => stats.isDirectory()).catch(() => false);
11753
- if (isDirectory)
11754
- return root;
11755
- }
11756
- return null;
11757
- }
11758
- async function removeMaintainerOnlySkillSymlinks(skillsHome, allowedSkillNames) {
11759
- const allowed = new Set(Array.from(allowedSkillNames));
11760
- try {
11761
- const entries = await fs13.readdir(skillsHome, { withFileTypes: true });
11762
- const removed = [];
11763
- for (const entry of entries) {
11764
- if (allowed.has(entry.name))
11765
- continue;
11766
- const target = path17.join(skillsHome, entry.name);
11767
- const existing = await fs13.lstat(target).catch(() => null);
11768
- if (!existing?.isSymbolicLink())
11769
- continue;
11770
- const linkedPath = await fs13.readlink(target).catch(() => null);
11771
- if (!linkedPath)
11772
- continue;
11773
- const resolvedLinkedPath = path17.isAbsolute(linkedPath) ? linkedPath : path17.resolve(path17.dirname(target), linkedPath);
11774
- if (!isMaintainerOnlySkillTarget(linkedPath) && !isMaintainerOnlySkillTarget(resolvedLinkedPath)) {
11775
- continue;
11776
- }
11777
- await fs13.unlink(target);
11778
- removed.push(entry.name);
11779
- }
11780
- return removed;
11781
- } catch {
11782
- return [];
11783
- }
11784
- }
11785
-
11786
- // src/commands/client/agent.ts
11787
- import fs14 from "node:fs/promises";
11788
- import os2 from "node:os";
11789
- import path18 from "node:path";
11790
- import { fileURLToPath as fileURLToPath3 } from "node:url";
11791
- var __moduleDir = path18.dirname(fileURLToPath3(import.meta.url));
11792
- function codexSkillsHome() {
11793
- const fromEnv = process.env.CODEX_HOME?.trim();
11794
- const base = fromEnv && fromEnv.length > 0 ? fromEnv : path18.join(os2.homedir(), ".codex");
11795
- return path18.join(base, "skills");
11796
- }
11797
- function claudeSkillsHome() {
11798
- const fromEnv = process.env.CLAUDE_HOME?.trim();
11799
- const base = fromEnv && fromEnv.length > 0 ? fromEnv : path18.join(os2.homedir(), ".claude");
11800
- return path18.join(base, "skills");
11801
- }
11802
- async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
11803
- const summary = {
11804
- tool,
11805
- target: targetSkillsDir,
11806
- linked: [],
11807
- removed: [],
11808
- skipped: [],
11809
- failed: []
11810
- };
11811
- await fs14.mkdir(targetSkillsDir, { recursive: true });
11812
- const entries = await fs14.readdir(sourceSkillsDir, { withFileTypes: true });
11813
- summary.removed = await removeMaintainerOnlySkillSymlinks(
11814
- targetSkillsDir,
11815
- entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name)
12759
+ })
12760
+ );
12761
+ addCommonClientOptions(
12762
+ approval.command("reject").description("Reject an approval request").argument("<approvalId>", "Approval ID").option("--decision-note <text>", "Decision note").option("--decided-by-user-id <id>", "Decision actor user ID").action(async (approvalId, opts) => {
12763
+ try {
12764
+ const ctx = resolveCommandContext(opts);
12765
+ const payload = resolveApprovalSchema.parse({
12766
+ decisionNote: opts.decisionNote,
12767
+ decidedByUserId: opts.decidedByUserId
12768
+ });
12769
+ const updated = await ctx.api.post(`/api/approvals/${approvalId}/reject`, payload);
12770
+ printOutput(updated, { json: ctx.json });
12771
+ } catch (err) {
12772
+ handleCommandError(err);
12773
+ }
12774
+ })
11816
12775
  );
11817
- for (const entry of entries) {
11818
- if (!entry.isDirectory()) continue;
11819
- const source = path18.join(sourceSkillsDir, entry.name);
11820
- const target = path18.join(targetSkillsDir, entry.name);
11821
- const existing = await fs14.lstat(target).catch(() => null);
11822
- if (existing) {
11823
- if (existing.isSymbolicLink()) {
11824
- let linkedPath = null;
11825
- try {
11826
- linkedPath = await fs14.readlink(target);
11827
- } catch (err) {
11828
- await fs14.unlink(target);
11829
- try {
11830
- await fs14.symlink(source, target);
11831
- summary.linked.push(entry.name);
11832
- continue;
11833
- } catch (linkErr) {
11834
- summary.failed.push({
11835
- name: entry.name,
11836
- error: err instanceof Error && linkErr instanceof Error ? `${err.message}; then ${linkErr.message}` : err instanceof Error ? err.message : `Failed to recover broken symlink: ${String(err)}`
11837
- });
11838
- continue;
11839
- }
11840
- }
11841
- const resolvedLinkedPath = path18.isAbsolute(linkedPath) ? linkedPath : path18.resolve(path18.dirname(target), linkedPath);
11842
- const linkedTargetExists = await fs14.stat(resolvedLinkedPath).then(() => true).catch(() => false);
11843
- if (!linkedTargetExists) {
11844
- await fs14.unlink(target);
11845
- } else {
11846
- summary.skipped.push(entry.name);
11847
- continue;
11848
- }
11849
- } else {
11850
- summary.skipped.push(entry.name);
11851
- continue;
11852
- }
11853
- }
11854
- try {
11855
- await fs14.symlink(source, target);
11856
- summary.linked.push(entry.name);
11857
- } catch (err) {
11858
- summary.failed.push({
11859
- name: entry.name,
11860
- error: err instanceof Error ? err.message : String(err)
11861
- });
11862
- }
11863
- }
11864
- return summary;
11865
- }
11866
- function buildAgentEnvExports(input) {
11867
- const escaped = (value) => value.replace(/'/g, `'"'"'`);
11868
- return [
11869
- `export RUDDER_API_URL='${escaped(input.apiBase)}'`,
11870
- `export RUDDER_ORG_ID='${escaped(input.orgId)}'`,
11871
- `export RUDDER_AGENT_ID='${escaped(input.agentId)}'`,
11872
- `export RUDDER_API_KEY='${escaped(input.apiKey)}'`
11873
- ].join("\n");
11874
- }
11875
- function registerAgentCommands(program) {
11876
- const agent = program.command("agent").description("Agent operations");
11877
- const config = agent.command("config").description("Agent configuration discovery and redacted snapshots");
11878
12776
  addCommonClientOptions(
11879
- agent.command("me").description(getAgentCliCapabilityById("agent.me").description).action(async (opts) => {
12777
+ approval.command("request-revision").description("Request revision for an approval").argument("<approvalId>", "Approval ID").option("--decision-note <text>", "Decision note").option("--decided-by-user-id <id>", "Decision actor user ID").action(async (approvalId, opts) => {
11880
12778
  try {
11881
12779
  const ctx = resolveCommandContext(opts);
11882
- const row = await ctx.api.get("/api/agents/me");
11883
- printOutput(row, { json: ctx.json });
12780
+ const payload = requestApprovalRevisionSchema.parse({
12781
+ decisionNote: opts.decisionNote,
12782
+ decidedByUserId: opts.decidedByUserId
12783
+ });
12784
+ const updated = await ctx.api.post(`/api/approvals/${approvalId}/request-revision`, payload);
12785
+ printOutput(updated, { json: ctx.json });
11884
12786
  } catch (err) {
11885
12787
  handleCommandError(err);
11886
12788
  }
11887
12789
  })
11888
12790
  );
11889
12791
  addCommonClientOptions(
11890
- agent.command("inbox").description(getAgentCliCapabilityById("agent.inbox").description).action(async (opts) => {
12792
+ approval.command("resubmit").description(getAgentCliCapabilityById("approval.resubmit").description).argument("<approvalId>", "Approval ID").option("--payload <json>", "Payload JSON object").action(async (approvalId, opts) => {
11891
12793
  try {
11892
12794
  const ctx = resolveCommandContext(opts);
11893
- const rows = await ctx.api.get("/api/agents/me/inbox-lite") ?? [];
11894
- if (ctx.json) {
11895
- printOutput(rows, { json: true });
11896
- return;
11897
- }
11898
- if (rows.length === 0) {
11899
- printOutput([], { json: false });
11900
- return;
11901
- }
11902
- for (const row of rows) {
11903
- console.log(
11904
- formatInlineRecord({
11905
- identifier: row.identifier,
11906
- id: row.id,
11907
- relationship: row.relationship ?? "assignee",
11908
- status: row.status,
11909
- priority: row.priority,
11910
- title: row.title,
11911
- projectId: row.projectId,
11912
- goalId: row.goalId,
11913
- parentId: row.parentId,
11914
- updatedAt: row.updatedAt
11915
- })
11916
- );
11917
- }
12795
+ const payload = resubmitApprovalSchema.parse({
12796
+ payload: opts.payload ? parseJsonObject2(opts.payload, "payload") : void 0
12797
+ });
12798
+ const updated = await ctx.api.post(`/api/approvals/${approvalId}/resubmit`, payload);
12799
+ printOutput(updated, { json: ctx.json });
11918
12800
  } catch (err) {
11919
12801
  handleCommandError(err);
11920
12802
  }
11921
12803
  })
11922
12804
  );
11923
12805
  addCommonClientOptions(
11924
- agent.command("capabilities").description(getAgentCliCapabilityById("agent.capabilities").description).option("--contract <name>", "Capability subset to show (agent-v1 or all)", "agent-v1").action(async (opts) => {
12806
+ approval.command("comment").description(getAgentCliCapabilityById("approval.comment").description).argument("<approvalId>", "Approval ID").option("--body-file <path>", "Read comment body from a file, or '-' for stdin").action(async (approvalId, opts) => {
11925
12807
  try {
11926
- const contract = opts.contract === "all" ? "all" : "agent-v1";
11927
- const capabilities = getAgentCliCapabilities().filter((entry) => contract === "all" ? true : entry.contract === contract);
11928
- if (opts.json) {
11929
- printOutput(buildAgentCliCapabilitiesManifest(contract), { json: true });
11930
- return;
11931
- }
11932
- console.log(formatAgentCliCapabilitiesHumanReadable(capabilities));
12808
+ const ctx = resolveCommandContext(opts);
12809
+ const body = await resolveBodyFile(opts.bodyFile);
12810
+ const created = await ctx.api.post(`/api/approvals/${approvalId}/comments`, {
12811
+ body
12812
+ });
12813
+ printOutput(created, { json: ctx.json });
11933
12814
  } catch (err) {
11934
12815
  handleCommandError(err);
11935
12816
  }
11936
12817
  })
11937
12818
  );
12819
+ }
12820
+ async function resolveBodyFile(inputPath) {
12821
+ if (!inputPath) {
12822
+ throw new Error("Provide --body-file <path>; use --body-file - for stdin");
12823
+ }
12824
+ return readTextInputFile2(inputPath, "--body-file");
12825
+ }
12826
+ async function readTextInputFile2(inputPath, optionName) {
12827
+ if (inputPath === "-") {
12828
+ return readStdinText2();
12829
+ }
12830
+ const resolvedPath = path19.resolve(process.cwd(), inputPath);
12831
+ return readFile5(resolvedPath, "utf8").catch((err) => {
12832
+ throw new Error(`Unable to read ${optionName} ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
12833
+ });
12834
+ }
12835
+ async function readStdinText2() {
12836
+ const chunks = [];
12837
+ for await (const chunk of process.stdin) {
12838
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
12839
+ }
12840
+ return Buffer.concat(chunks).toString("utf8");
12841
+ }
12842
+ function parseCsv4(value) {
12843
+ if (!value) return void 0;
12844
+ const rows = value.split(",").map((v) => v.trim()).filter(Boolean);
12845
+ return rows.length > 0 ? rows : void 0;
12846
+ }
12847
+ function parseJsonObject2(value, name) {
12848
+ try {
12849
+ const parsed = JSON.parse(value);
12850
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
12851
+ throw new Error(`${name} must be a JSON object`);
12852
+ }
12853
+ return parsed;
12854
+ } catch (err) {
12855
+ throw new Error(`Invalid ${name} JSON: ${err instanceof Error ? err.message : String(err)}`);
12856
+ }
12857
+ }
12858
+
12859
+ // src/commands/client/activity.ts
12860
+ function registerActivityCommands(program) {
12861
+ const activity = program.command("activity").description("Activity log operations");
11938
12862
  addCommonClientOptions(
11939
- agent.command("list").description(getAgentCliCapabilityById("agent.list").description).option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
12863
+ activity.command("list").description("List organization activity log entries").option("-O, --org-id <id>", "Organization ID").option("--agent-id <id>", "Filter by agent ID").option("--entity-type <type>", "Filter by entity type").option("--entity-id <id>", "Filter by entity ID").action(async (opts) => {
11940
12864
  try {
11941
12865
  const ctx = resolveCommandContext(opts, { requireCompany: true });
11942
- const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/agents`) ?? [];
12866
+ const params = new URLSearchParams();
12867
+ if (opts.agentId) params.set("agentId", opts.agentId);
12868
+ if (opts.entityType) params.set("entityType", opts.entityType);
12869
+ if (opts.entityId) params.set("entityId", opts.entityId);
12870
+ const query = params.toString();
12871
+ const path23 = `/api/orgs/${ctx.orgId}/activity${query ? `?${query}` : ""}`;
12872
+ const rows = await ctx.api.get(path23) ?? [];
11943
12873
  if (ctx.json) {
11944
12874
  printOutput(rows, { json: true });
11945
12875
  return;
@@ -11952,12 +12882,12 @@ function registerAgentCommands(program) {
11952
12882
  console.log(
11953
12883
  formatInlineRecord({
11954
12884
  id: row.id,
11955
- name: row.name,
11956
- role: row.role,
11957
- status: row.status,
11958
- reportsTo: row.reportsTo,
11959
- budgetMonthlyCents: row.budgetMonthlyCents,
11960
- spentMonthlyCents: row.spentMonthlyCents
12885
+ action: row.action,
12886
+ actorType: row.actorType,
12887
+ actorId: row.actorId,
12888
+ entityType: row.entityType,
12889
+ entityId: row.entityId,
12890
+ createdAt: String(row.createdAt)
11961
12891
  })
11962
12892
  );
11963
12893
  }
@@ -11967,35 +12897,34 @@ function registerAgentCommands(program) {
11967
12897
  }),
11968
12898
  { includeCompany: false }
11969
12899
  );
12900
+ }
12901
+
12902
+ // src/commands/client/dashboard.ts
12903
+ function registerDashboardCommands(program) {
12904
+ const dashboard = program.command("dashboard").description("Dashboard summary operations");
11970
12905
  addCommonClientOptions(
11971
- config.command("index").description(getAgentCliCapabilityById("agent.config.index").description).action(async (opts) => {
11972
- try {
11973
- const ctx = resolveCommandContext(opts);
11974
- const doc = await ctx.api.get("/llms/agent-configuration.txt");
11975
- printOutput(doc ?? "", { json: ctx.json });
11976
- } catch (err) {
11977
- handleCommandError(err);
11978
- }
11979
- })
11980
- );
11981
- addCommonClientOptions(
11982
- config.command("doc").description(getAgentCliCapabilityById("agent.config.doc").description).argument("<agentRuntimeType>", "Agent runtime type").action(async (agentRuntimeType, opts) => {
12906
+ dashboard.command("get").description("Get dashboard summary for an organization").option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
11983
12907
  try {
11984
- const ctx = resolveCommandContext(opts);
11985
- const doc = await ctx.api.get(
11986
- `/llms/agent-configuration/${encodeURIComponent(agentRuntimeType)}.txt`
11987
- );
11988
- printOutput(doc ?? "", { json: ctx.json });
12908
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12909
+ const row = await ctx.api.get(`/api/orgs/${ctx.orgId}/dashboard`);
12910
+ printOutput(row, { json: ctx.json });
11989
12911
  } catch (err) {
11990
12912
  handleCommandError(err);
11991
12913
  }
11992
- })
12914
+ }),
12915
+ { includeCompany: false }
11993
12916
  );
12917
+ }
12918
+
12919
+ // src/commands/client/skill.ts
12920
+ init_dist();
12921
+ function registerSkillCommands(program) {
12922
+ const skill = program.command("skill").description("Organization skill library operations");
11994
12923
  addCommonClientOptions(
11995
- config.command("list").description(getAgentCliCapabilityById("agent.config.list").description).option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
12924
+ skill.command("list").description(getAgentCliCapabilityById("skill.list").description).option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
11996
12925
  try {
11997
12926
  const ctx = resolveCommandContext(opts, { requireCompany: true });
11998
- const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/agent-configurations`) ?? [];
12927
+ const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/skills`) ?? [];
11999
12928
  if (ctx.json) {
12000
12929
  printOutput(rows, { json: true });
12001
12930
  return;
@@ -12008,12 +12937,12 @@ function registerAgentCommands(program) {
12008
12937
  console.log(
12009
12938
  formatInlineRecord({
12010
12939
  id: row.id,
12940
+ key: row.key,
12941
+ slug: row.slug,
12011
12942
  name: row.name,
12012
- role: row.role,
12013
- status: row.status,
12014
- agentRuntimeType: row.agentRuntimeType,
12015
- reportsTo: row.reportsTo,
12016
- updatedAt: row.updatedAt
12943
+ sourceBadge: row.sourceBadge,
12944
+ compatibility: row.compatibility,
12945
+ attachedAgentCount: row.attachedAgentCount
12017
12946
  })
12018
12947
  );
12019
12948
  }
@@ -12024,342 +12953,232 @@ function registerAgentCommands(program) {
12024
12953
  { includeCompany: false }
12025
12954
  );
12026
12955
  addCommonClientOptions(
12027
- config.command("get").description(getAgentCliCapabilityById("agent.config.get").description).argument("<agentId>", "Agent ID or shortname/url-key").option("-O, --org-id <id>", "Organization ID").action(async (agentId, opts) => {
12956
+ skill.command("get").description(getAgentCliCapabilityById("skill.get").description).argument("<skillId>", "Skill ID").option("-O, --org-id <id>", "Organization ID").action(async (skillId, opts) => {
12028
12957
  try {
12029
- const ctx = resolveCommandContext(opts);
12030
- const query = new URLSearchParams();
12031
- if (ctx.orgId) query.set("orgId", ctx.orgId);
12032
- const row = await ctx.api.get(
12033
- `/api/agents/${encodeURIComponent(agentId)}/configuration${query.size > 0 ? `?${query.toString()}` : ""}`
12034
- );
12958
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12959
+ const row = await ctx.api.get(`/api/orgs/${ctx.orgId}/skills/${skillId}`);
12035
12960
  printOutput(row, { json: ctx.json });
12036
12961
  } catch (err) {
12037
12962
  handleCommandError(err);
12038
12963
  }
12039
- })
12040
- );
12041
- addCommonClientOptions(
12042
- agent.command("icons").description(getAgentCliCapabilityById("agent.icons").description).action(async (opts) => {
12043
- try {
12044
- const ctx = resolveCommandContext(opts);
12045
- const doc = await ctx.api.get("/llms/agent-icons.txt");
12046
- printOutput(doc ?? "", { json: ctx.json });
12047
- } catch (err) {
12048
- handleCommandError(err);
12049
- }
12050
- })
12964
+ }),
12965
+ { includeCompany: false }
12051
12966
  );
12052
- const skills = agent.command("skills").description("Agent skill selection operations");
12053
12967
  addCommonClientOptions(
12054
- skills.command("create").description(getAgentCliCapabilityById("agent.skills.create").description).argument("[agent-id]", "Agent ID; defaults to RUDDER_AGENT_ID").requiredOption("--name <name>", "Skill display name").option("--slug <slug>", "Skill short name").option("--description <text>", "Skill description").option("--markdown <markdown>", "Full SKILL.md content").option("--markdown-file <path>", "Read full SKILL.md content from a local file").option("--enable", "Enable the created private skill for future runs").action(async (agentIdArg, opts) => {
12968
+ skill.command("file").description(getAgentCliCapabilityById("skill.file").description).argument("<skillId>", "Skill ID").option("-O, --org-id <id>", "Organization ID").option("--path <path>", "Skill package file path", "SKILL.md").action(async (skillId, opts) => {
12055
12969
  try {
12056
- const ctx = resolveCommandContext(opts);
12057
- const agentId = agentIdArg?.trim() || ctx.agentId;
12058
- if (!agentId) {
12059
- throw new Error("Agent ID is required. Pass [agent-id] or set RUDDER_AGENT_ID.");
12060
- }
12061
- if (opts.markdown && opts.markdownFile) {
12062
- throw new Error("Pass only one of --markdown or --markdown-file.");
12063
- }
12064
- const markdown = opts.markdownFile ? await fs14.readFile(path18.resolve(opts.markdownFile), "utf8") : opts.markdown;
12065
- const payload = organizationSkillCreateSchema.parse({
12066
- name: opts.name,
12067
- slug: opts.slug?.trim() || null,
12068
- description: opts.description?.trim() || null,
12069
- markdown: markdown ?? null
12070
- });
12071
- const created = await ctx.api.post(
12072
- `/api/agents/${encodeURIComponent(agentId)}/skills/private`,
12073
- payload
12074
- );
12075
- if (!created) {
12076
- throw new Error("Failed to create agent skill");
12077
- }
12078
- let enabledSnapshot = null;
12079
- if (opts.enable) {
12080
- enabledSnapshot = await ctx.api.post(
12081
- `/api/agents/${encodeURIComponent(agentId)}/skills/enable`,
12082
- { skills: [created.selectionKey] }
12083
- );
12084
- }
12085
- const result = { created, enabledSnapshot };
12086
- if (ctx.json) {
12087
- printOutput(result, { json: true });
12088
- return;
12089
- }
12090
- console.log(
12091
- formatInlineRecord({
12092
- key: created.key,
12093
- selectionKey: created.selectionKey,
12094
- sourceClass: created.sourceClass,
12095
- sourcePath: created.sourcePath,
12096
- enabled: Boolean(enabledSnapshot?.desiredSkills.includes(created.selectionKey)),
12097
- detail: opts.enable ? "Created and enabled. Future runs will load this skill." : "Created, not enabled. Future runs will not load it until enabled."
12098
- })
12970
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12971
+ const query = new URLSearchParams({ path: opts.path ?? "SKILL.md" });
12972
+ const row = await ctx.api.get(
12973
+ `/api/orgs/${ctx.orgId}/skills/${skillId}/files?${query.toString()}`
12099
12974
  );
12975
+ printOutput(row, { json: ctx.json });
12100
12976
  } catch (err) {
12101
12977
  handleCommandError(err);
12102
12978
  }
12103
- })
12979
+ }),
12980
+ { includeCompany: false }
12104
12981
  );
12105
12982
  addCommonClientOptions(
12106
- skills.command("enable").description(getAgentCliCapabilityById("agent.skills.enable").description).argument("<agentId>", "Agent ID").argument("<selectionRefs...>", "Skill selection refs to enable").action(async (agentId, selectionRefs, opts) => {
12983
+ skill.command("import").description(getAgentCliCapabilityById("skill.import").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--source <source>", "Skill source (local path, URL, or repo ref)").action(async (opts) => {
12107
12984
  try {
12108
- const ctx = resolveCommandContext(opts);
12109
- const snapshot = await ctx.api.post(`/api/agents/${agentId}/skills/enable`, {
12110
- skills: selectionRefs
12111
- });
12112
- printOutput(snapshot, { json: ctx.json });
12985
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12986
+ const payload = organizationSkillImportSchema.parse({ source: opts.source });
12987
+ const result = await ctx.api.post(`/api/orgs/${ctx.orgId}/skills/import`, payload);
12988
+ printOutput(result, { json: ctx.json });
12113
12989
  } catch (err) {
12114
12990
  handleCommandError(err);
12115
12991
  }
12116
- })
12992
+ }),
12993
+ { includeCompany: false }
12117
12994
  );
12118
12995
  addCommonClientOptions(
12119
- skills.command("sync").description(getAgentCliCapabilityById("agent.skills.sync").description).argument("<agentId>", "Agent ID").requiredOption(
12120
- "--desired-skills <csv>",
12121
- "Comma-separated desired skill refs (for example rudder/rudder)"
12122
- ).action(async (agentId, opts) => {
12996
+ skill.command("scan-local").description(getAgentCliCapabilityById("skill.scan-local").description).option("-O, --org-id <id>", "Organization ID").option("--roots <csv>", "Comma-separated local roots to scan").action(async (opts) => {
12123
12997
  try {
12124
- const ctx = resolveCommandContext(opts);
12125
- const snapshot = await ctx.api.post(`/api/agents/${agentId}/skills/sync`, {
12126
- desiredSkills: parseCsv3(opts.desiredSkills)
12998
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
12999
+ const payload = organizationSkillLocalScanRequestSchema.parse({
13000
+ roots: parseCsv5(opts.roots)
12127
13001
  });
12128
- printOutput(snapshot, { json: ctx.json });
13002
+ const result = await ctx.api.post(
13003
+ `/api/orgs/${ctx.orgId}/skills/scan-local`,
13004
+ payload
13005
+ );
13006
+ printOutput(result, { json: ctx.json });
12129
13007
  } catch (err) {
12130
13008
  handleCommandError(err);
12131
13009
  }
12132
- })
13010
+ }),
13011
+ { includeCompany: false }
12133
13012
  );
12134
13013
  addCommonClientOptions(
12135
- agent.command("get").description(getAgentCliCapabilityById("agent.get").description).argument("<agentId>", "Agent ID or shortname/url-key").option("-O, --org-id <id>", "Organization ID").action(async (agentId, opts) => {
13014
+ skill.command("scan-projects").description(getAgentCliCapabilityById("skill.scan-projects").description).option("-O, --org-id <id>", "Organization ID").option("--project-ids <csv>", "Comma-separated project IDs").option("--workspace-ids <csv>", "Comma-separated workspace IDs").action(async (opts) => {
12136
13015
  try {
12137
- const ctx = resolveCommandContext(opts);
12138
- const query = new URLSearchParams();
12139
- if (ctx.orgId) query.set("orgId", ctx.orgId);
12140
- const row = await ctx.api.get(
12141
- `/api/agents/${encodeURIComponent(agentId)}${query.size > 0 ? `?${query.toString()}` : ""}`
13016
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
13017
+ const payload = organizationSkillProjectScanRequestSchema.parse({
13018
+ projectIds: parseCsv5(opts.projectIds),
13019
+ workspaceIds: parseCsv5(opts.workspaceIds)
13020
+ });
13021
+ const result = await ctx.api.post(
13022
+ `/api/orgs/${ctx.orgId}/skills/scan-projects`,
13023
+ payload
12142
13024
  );
12143
- printOutput(row, { json: ctx.json });
13025
+ printOutput(result, { json: ctx.json });
12144
13026
  } catch (err) {
12145
13027
  handleCommandError(err);
12146
13028
  }
12147
- })
13029
+ }),
13030
+ { includeCompany: false }
13031
+ );
13032
+ }
13033
+ function parseCsv5(value) {
13034
+ if (!value) return void 0;
13035
+ const rows = value.split(",").map((entry) => entry.trim()).filter(Boolean);
13036
+ return rows.length > 0 ? rows : void 0;
13037
+ }
13038
+
13039
+ // src/commands/client/library.ts
13040
+ import { readFile as readFile6 } from "node:fs/promises";
13041
+ import path20 from "node:path";
13042
+ function toLibraryFileLinkResult(detail) {
13043
+ return {
13044
+ filePath: detail.filePath,
13045
+ libraryEntryId: detail.libraryEntryId,
13046
+ mentionHref: detail.mentionHref,
13047
+ markdownLink: detail.markdownLink
13048
+ };
13049
+ }
13050
+ async function printLibraryFileReference(filePath, opts) {
13051
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
13052
+ const search = new URLSearchParams();
13053
+ search.set("path", filePath.trim());
13054
+ const result = await ctx.api.get(
13055
+ `/api/orgs/${ctx.orgId}/workspace/file?${search.toString()}`
12148
13056
  );
13057
+ if (!result) throw new Error("Library file not found");
13058
+ printOutput(toLibraryFileLinkResult(result), { json: ctx.json });
13059
+ }
13060
+ function registerLibraryCommands(program) {
13061
+ const library = program.command("library").description("Library file operations");
13062
+ const file = library.command("file").description("Library file operations");
12149
13063
  addCommonClientOptions(
12150
- agent.command("update").description(getAgentCliCapabilityById("agent.update").description).argument("[agentId]", "Agent ID or shortname/url-key; defaults to RUDDER_AGENT_ID").option("-O, --org-id <id>", "Organization ID").option("--name <name>", "Agent display name").option("--role <role>", "Agent organization role").option("--title <title>", "Agent title shown in mentions and agent labels").option("--clear-title", "Clear the agent title").option("--capabilities <text>", "Agent capabilities/description text").option("--capabilities-file <path>", "Read agent capabilities/description from a local file").option("--description <text>", "Alias for --capabilities").option("--description-file <path>", "Alias for --capabilities-file").option("--clear-capabilities", "Clear the agent capabilities/description").option("--clear-description", "Alias for --clear-capabilities").option("--reports-to <agent-id>", "Manager agent ID").option("--clear-reports-to", "Clear the manager relationship").action(async (agentIdArg, opts) => {
13064
+ file.command("list").description(getAgentCliCapabilityById("library.file.list").description).argument("[directoryPath]", "Library directory path", "projects").action(async (directoryPath, opts) => {
12151
13065
  try {
12152
- const ctx = resolveCommandContext(opts);
12153
- const agentId = agentIdArg?.trim() || ctx.agentId;
12154
- if (!agentId) {
12155
- throw new Error("Agent ID is required. Pass [agent-id] or set RUDDER_AGENT_ID.");
12156
- }
12157
- const patch = await buildAgentUpdatePatch(opts);
12158
- if (Object.keys(patch).length === 0) {
12159
- throw new Error(
12160
- "No agent fields to update. Pass --name, --role, --title, --capabilities, --description, or --reports-to."
12161
- );
12162
- }
12163
- const query = new URLSearchParams();
12164
- if (ctx.orgId) query.set("orgId", ctx.orgId);
12165
- const updated = await ctx.api.patch(
12166
- `/api/agents/${encodeURIComponent(agentId)}${query.size > 0 ? `?${query.toString()}` : ""}`,
12167
- patch
13066
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
13067
+ const search = new URLSearchParams();
13068
+ const normalizedPath = directoryPath.trim();
13069
+ if (normalizedPath) search.set("path", normalizedPath);
13070
+ const query = search.toString();
13071
+ const result = await ctx.api.get(
13072
+ `/api/orgs/${ctx.orgId}/workspace/files${query ? `?${query}` : ""}`
12168
13073
  );
12169
- printOutput(updated, { json: ctx.json });
13074
+ printOutput(result, { json: ctx.json });
12170
13075
  } catch (err) {
12171
13076
  handleCommandError(err);
12172
13077
  }
12173
- })
13078
+ }),
13079
+ { includeCompany: true }
12174
13080
  );
12175
13081
  addCommonClientOptions(
12176
- agent.command("hire").description(getAgentCliCapabilityById("agent.hire").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--payload <json>", "Hire payload as JSON object").action(async (opts) => {
13082
+ file.command("get").description(getAgentCliCapabilityById("library.file.get").description).argument("<filePath>", "Library file path").action(async (filePath, opts) => {
12177
13083
  try {
12178
13084
  const ctx = resolveCommandContext(opts, { requireCompany: true });
12179
- const payloadJson = parseJsonObject(opts.payload, "payload");
12180
- const payload = createAgentHireSchema.parse(payloadJson);
12181
- const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/agent-hires`, payload);
12182
- printOutput(created, { json: ctx.json });
13085
+ const search = new URLSearchParams();
13086
+ search.set("path", filePath.trim());
13087
+ const result = await ctx.api.get(
13088
+ `/api/orgs/${ctx.orgId}/workspace/file?${search.toString()}`
13089
+ );
13090
+ printOutput(result, { json: ctx.json });
12183
13091
  } catch (err) {
12184
13092
  handleCommandError(err);
12185
13093
  }
12186
13094
  }),
12187
- { includeCompany: false }
13095
+ { includeCompany: true }
12188
13096
  );
12189
13097
  addCommonClientOptions(
12190
- agent.command("local-cli").description(
12191
- "Create an agent API key, optionally install local Rudder skills for direct Codex/Claude CLI use, and print shell exports"
12192
- ).argument("<agentRef>", "Agent ID or shortname/url-key").option("-O, --org-id <id>", "Organization ID").option("--key-name <name>", "API key label", "local-cli").option(
12193
- "--no-install-skills",
12194
- "Skip the optional local Codex/Claude skill installation step"
12195
- ).action(async (agentRef, opts) => {
13098
+ file.command("link").description(getAgentCliCapabilityById("library.file.link").description).argument("<filePath>", "Library file path").action(async (filePath, opts) => {
13099
+ try {
13100
+ await printLibraryFileReference(filePath, opts);
13101
+ } catch (err) {
13102
+ handleCommandError(err);
13103
+ }
13104
+ }),
13105
+ { includeCompany: true }
13106
+ );
13107
+ addCommonClientOptions(
13108
+ file.command("ref").description(getAgentCliCapabilityById("library.file.ref").description).argument("<filePath>", "Library file path").action(async (filePath, opts) => {
13109
+ try {
13110
+ await printLibraryFileReference(filePath, opts);
13111
+ } catch (err) {
13112
+ handleCommandError(err);
13113
+ }
13114
+ }),
13115
+ { includeCompany: true }
13116
+ );
13117
+ addCommonClientOptions(
13118
+ file.command("put").description(getAgentCliCapabilityById("library.file.put").description).argument("<filePath>", "Library file path").option("--body-file <path>", "Read file content from a file, or '-' for stdin").action(async (filePath, opts) => {
12196
13119
  try {
12197
13120
  const ctx = resolveCommandContext(opts, { requireCompany: true });
12198
- const query = new URLSearchParams({ orgId: ctx.orgId ?? "" });
12199
- const agentRow = await ctx.api.get(
12200
- `/api/agents/${encodeURIComponent(agentRef)}?${query.toString()}`
13121
+ const body = await resolveBodyFileInput(opts.bodyFile);
13122
+ const search = new URLSearchParams();
13123
+ search.set("path", filePath.trim());
13124
+ const updatePath = `/api/orgs/${ctx.orgId}/workspace/file?${search.toString()}`;
13125
+ const updated = await ctx.api.patch(
13126
+ updatePath,
13127
+ { content: body },
13128
+ { ignoreNotFound: true }
12201
13129
  );
12202
- if (!agentRow) {
12203
- throw new Error(`Agent not found: ${agentRef}`);
12204
- }
12205
- const now = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
12206
- const keyName = opts.keyName?.trim() ? opts.keyName.trim() : `local-cli-${now}`;
12207
- const key = await ctx.api.post(`/api/agents/${agentRow.id}/keys`, { name: keyName });
12208
- if (!key) {
12209
- throw new Error("Failed to create API key");
12210
- }
12211
- const installSummaries = [];
12212
- if (opts.installSkills !== false) {
12213
- const skillsDir = await resolveRudderSkillsDir(__moduleDir, [path18.resolve(process.cwd(), "skills")]);
12214
- if (!skillsDir) {
12215
- throw new Error(
12216
- "Could not locate local Rudder skills directory. Expected ./skills in the repo checkout."
12217
- );
12218
- }
12219
- installSummaries.push(
12220
- await installSkillsForTarget(skillsDir, codexSkillsHome(), "codex"),
12221
- await installSkillsForTarget(skillsDir, claudeSkillsHome(), "claude")
12222
- );
12223
- }
12224
- const exportsText = buildAgentEnvExports({
12225
- apiBase: ctx.api.apiBase,
12226
- orgId: agentRow.orgId,
12227
- agentId: agentRow.id,
12228
- apiKey: key.token
12229
- });
12230
- if (ctx.json) {
12231
- printOutput(
12232
- {
12233
- agent: {
12234
- id: agentRow.id,
12235
- name: agentRow.name,
12236
- urlKey: agentRow.urlKey,
12237
- orgId: agentRow.orgId
12238
- },
12239
- key: {
12240
- id: key.id,
12241
- name: key.name,
12242
- createdAt: key.createdAt,
12243
- token: key.token
12244
- },
12245
- skills: installSummaries,
12246
- exports: exportsText
12247
- },
12248
- { json: true }
12249
- );
13130
+ if (updated) {
13131
+ printOutput(updated, { json: ctx.json });
12250
13132
  return;
12251
13133
  }
12252
- console.log(`Agent: ${agentRow.name} (${agentRow.id})`);
12253
- console.log(`API key created: ${key.name} (${key.id})`);
12254
- if (installSummaries.length > 0) {
12255
- for (const summary of installSummaries) {
12256
- console.log(
12257
- `${summary.tool}: linked=${summary.linked.length} removed=${summary.removed.length} skipped=${summary.skipped.length} failed=${summary.failed.length} target=${summary.target}`
12258
- );
12259
- for (const failed of summary.failed) {
12260
- console.log(` failed ${failed.name}: ${failed.error}`);
12261
- }
12262
- }
12263
- }
12264
- console.log("");
12265
- console.log("# Run this in your shell before launching codex/claude:");
12266
- console.log(exportsText);
13134
+ const created = await ctx.api.post(
13135
+ `/api/orgs/${ctx.orgId}/workspace/file`,
13136
+ { filePath: filePath.trim(), content: body }
13137
+ );
13138
+ printOutput(created, { json: ctx.json });
12267
13139
  } catch (err) {
12268
13140
  handleCommandError(err);
12269
13141
  }
12270
13142
  }),
12271
- { includeCompany: false }
12272
- );
12273
- }
12274
- async function buildAgentUpdatePatch(opts) {
12275
- const rawPatch = {};
12276
- if (opts.name !== void 0) rawPatch.name = opts.name;
12277
- if (opts.role !== void 0) rawPatch.role = opts.role;
12278
- if (opts.title !== void 0 && opts.clearTitle) {
12279
- throw new Error("Pass only one of --title or --clear-title.");
12280
- }
12281
- if (opts.title !== void 0) rawPatch.title = opts.title;
12282
- if (opts.clearTitle) rawPatch.title = null;
12283
- if (opts.reportsTo !== void 0 && opts.clearReportsTo) {
12284
- throw new Error("Pass only one of --reports-to or --clear-reports-to.");
12285
- }
12286
- if (opts.reportsTo !== void 0) rawPatch.reportsTo = opts.reportsTo;
12287
- if (opts.clearReportsTo) rawPatch.reportsTo = null;
12288
- const capabilitiesInputs = [
12289
- opts.capabilities !== void 0 ? "--capabilities" : null,
12290
- opts.capabilitiesFile !== void 0 ? "--capabilities-file" : null,
12291
- opts.description !== void 0 ? "--description" : null,
12292
- opts.descriptionFile !== void 0 ? "--description-file" : null
12293
- ].filter(Boolean);
12294
- const clearCapabilities = opts.clearCapabilities || opts.clearDescription;
12295
- if (capabilitiesInputs.length > 1) {
12296
- throw new Error(
12297
- "Pass only one of --capabilities, --capabilities-file, --description, or --description-file."
12298
- );
12299
- }
12300
- if (capabilitiesInputs.length > 0 && clearCapabilities) {
12301
- throw new Error("Pass either capabilities/description input or a clear flag, not both.");
13143
+ { includeCompany: true }
13144
+ );
13145
+ }
13146
+ async function resolveBodyFileInput(inputPath) {
13147
+ if (process.argv.includes("--body")) {
13148
+ throw new Error("--body was removed; write the body to a file and use --body-file <path> or --body-file - for stdin");
12302
13149
  }
12303
- if (opts.capabilities !== void 0) rawPatch.capabilities = opts.capabilities;
12304
- if (opts.description !== void 0) rawPatch.capabilities = opts.description;
12305
- if (opts.capabilitiesFile !== void 0) {
12306
- rawPatch.capabilities = await fs14.readFile(path18.resolve(opts.capabilitiesFile), "utf8");
13150
+ if (inputPath === void 0) {
13151
+ throw new Error("Provide --body-file <path>; use --body-file - for stdin");
12307
13152
  }
12308
- if (opts.descriptionFile !== void 0) {
12309
- rawPatch.capabilities = await fs14.readFile(path18.resolve(opts.descriptionFile), "utf8");
13153
+ if (inputPath === "-") {
13154
+ return readStdinText3();
12310
13155
  }
12311
- if (clearCapabilities) rawPatch.capabilities = null;
12312
- return updateAgentSchema.parse(rawPatch);
12313
- }
12314
- function parseCsv3(value) {
12315
- if (!value) return [];
12316
- return value.split(",").map((entry) => entry.trim()).filter(Boolean);
13156
+ const resolvedPath = path20.resolve(process.cwd(), inputPath);
13157
+ return readFile6(resolvedPath, "utf8").catch((err) => {
13158
+ throw new Error(`Unable to read --body-file ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
13159
+ });
12317
13160
  }
12318
- function parseJsonObject(value, name) {
12319
- try {
12320
- const parsed = JSON.parse(value);
12321
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
12322
- throw new Error(`${name} must be a JSON object`);
12323
- }
12324
- return parsed;
12325
- } catch (err) {
12326
- throw new Error(`Invalid ${name} JSON: ${err instanceof Error ? err.message : String(err)}`);
13161
+ async function readStdinText3() {
13162
+ const chunks = [];
13163
+ for await (const chunk of process.stdin) {
13164
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
12327
13165
  }
13166
+ return Buffer.concat(chunks).toString("utf8");
12328
13167
  }
12329
13168
 
12330
- // src/commands/client/approval.ts
13169
+ // src/commands/client/automation.ts
12331
13170
  init_dist();
12332
- import { readFile as readFile5 } from "node:fs/promises";
12333
- import path19 from "node:path";
12334
- function registerApprovalCommands(program) {
12335
- const approval = program.command("approval").description("Approval operations");
13171
+ function registerAutomationCommands(program) {
13172
+ const automation = program.command("automation").description("Automation operations");
12336
13173
  addCommonClientOptions(
12337
- approval.command("list").description("List approvals for an organization").option("-O, --org-id <id>", "Organization ID").option("--status <status>", "Status filter").action(async (opts) => {
13174
+ automation.command("list").description(getAgentCliCapabilityById("automation.list").description).option("-O, --org-id <id>", "Organization ID").option("--status <status>", "Filter by automation status").option("--assignee-agent-id <id>", "Filter by assignee agent ID").option("--project-id <id>", "Filter by project ID").option("--output-mode <mode>", "Filter by output mode").action(async (opts) => {
12338
13175
  try {
12339
13176
  const ctx = resolveCommandContext(opts, { requireCompany: true });
12340
- const params = new URLSearchParams();
12341
- if (opts.status) params.set("status", opts.status);
12342
- const query = params.toString();
12343
- const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/approvals${query ? `?${query}` : ""}`) ?? [];
12344
- if (ctx.json) {
12345
- printOutput(rows, { json: true });
12346
- return;
12347
- }
12348
- if (rows.length === 0) {
12349
- printOutput([], { json: false });
12350
- return;
12351
- }
12352
- for (const row of rows) {
12353
- console.log(
12354
- formatInlineRecord({
12355
- id: row.id,
12356
- type: row.type,
12357
- status: row.status,
12358
- requestedByAgentId: row.requestedByAgentId,
12359
- requestedByUserId: row.requestedByUserId
12360
- })
12361
- );
12362
- }
13177
+ const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/automations`) ?? [];
13178
+ const filtered = rows.filter(
13179
+ (row) => (!opts.status || row.status === opts.status) && (!opts.assigneeAgentId || row.assigneeAgentId === opts.assigneeAgentId) && (!opts.projectId || row.projectId === opts.projectId) && (!opts.outputMode || row.outputMode === opts.outputMode)
13180
+ );
13181
+ printOutput(ctx.json ? filtered : filtered.map(formatAutomationListItem), { json: ctx.json });
12363
13182
  } catch (err) {
12364
13183
  handleCommandError(err);
12365
13184
  }
@@ -12367,10 +13186,10 @@ function registerApprovalCommands(program) {
12367
13186
  { includeCompany: false }
12368
13187
  );
12369
13188
  addCommonClientOptions(
12370
- approval.command("get").description(getAgentCliCapabilityById("approval.get").description).argument("<approvalId>", "Approval ID").action(async (approvalId, opts) => {
13189
+ automation.command("get").description(getAgentCliCapabilityById("automation.get").description).argument("<automationId>", "Automation ID").action(async (automationId, opts) => {
12371
13190
  try {
12372
13191
  const ctx = resolveCommandContext(opts);
12373
- const row = await ctx.api.get(`/api/approvals/${approvalId}`);
13192
+ const row = await ctx.api.get(`/api/automations/${encodeURIComponent(automationId)}`);
12374
13193
  printOutput(row, { json: ctx.json });
12375
13194
  } catch (err) {
12376
13195
  handleCommandError(err);
@@ -12378,59 +13197,51 @@ function registerApprovalCommands(program) {
12378
13197
  })
12379
13198
  );
12380
13199
  addCommonClientOptions(
12381
- approval.command("issues").description(getAgentCliCapabilityById("approval.issues").description).argument("<approvalId>", "Approval ID").action(async (approvalId, opts) => {
13200
+ automation.command("runs").description(getAgentCliCapabilityById("automation.runs").description).argument("<automationId>", "Automation ID").option("--limit <n>", "Maximum runs to return", "50").action(async (automationId, opts) => {
12382
13201
  try {
12383
13202
  const ctx = resolveCommandContext(opts);
12384
- const rows = await ctx.api.get(`/api/approvals/${approvalId}/issues`) ?? [];
12385
- printOutput(rows, { json: ctx.json });
13203
+ const params = new URLSearchParams();
13204
+ if (opts.limit) params.set("limit", opts.limit);
13205
+ const rows = await ctx.api.get(
13206
+ `/api/automations/${encodeURIComponent(automationId)}/runs?${params.toString()}`
13207
+ ) ?? [];
13208
+ printOutput(ctx.json ? rows : rows.map(formatAutomationRun), { json: ctx.json });
12386
13209
  } catch (err) {
12387
13210
  handleCommandError(err);
12388
13211
  }
12389
13212
  })
12390
13213
  );
12391
13214
  addCommonClientOptions(
12392
- approval.command("create").description(getAgentCliCapabilityById("approval.create").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--type <type>", "Approval type (hire_agent|approve_ceo_strategy)").requiredOption("--payload <json>", "Approval payload as JSON object").option("--requested-by-agent-id <id>", "Requesting agent ID").option("--issue-ids <csv>", "Comma-separated linked issue IDs").action(async (opts) => {
13215
+ automation.command("triggers").description("Automation trigger operations").command("list").description(getAgentCliCapabilityById("automation.triggers.list").description).argument("<automationId>", "Automation ID").action(async (automationId, opts) => {
12393
13216
  try {
12394
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12395
- const payloadJson = parseJsonObject2(opts.payload, "payload");
12396
- const payload = createApprovalSchema.parse({
12397
- type: opts.type,
12398
- payload: payloadJson,
12399
- requestedByAgentId: opts.requestedByAgentId,
12400
- issueIds: parseCsv4(opts.issueIds)
12401
- });
12402
- const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/approvals`, payload);
12403
- printOutput(created, { json: ctx.json });
13217
+ const ctx = resolveCommandContext(opts);
13218
+ const row = await ctx.api.get(`/api/automations/${encodeURIComponent(automationId)}`);
13219
+ const triggers = row?.triggers ?? [];
13220
+ printOutput(triggers, { json: ctx.json });
12404
13221
  } catch (err) {
12405
13222
  handleCommandError(err);
12406
13223
  }
12407
- }),
12408
- { includeCompany: false }
13224
+ })
12409
13225
  );
12410
13226
  addCommonClientOptions(
12411
- approval.command("approve").description("Approve an approval request").argument("<approvalId>", "Approval ID").option("--decision-note <text>", "Decision note").option("--decided-by-user-id <id>", "Decision actor user ID").action(async (approvalId, opts) => {
13227
+ automation.command("create").description(getAgentCliCapabilityById("automation.create").description).option("-O, --org-id <id>", "Organization ID").option("--payload <json>", "Raw automation create payload JSON").option("--title <title>", "Automation title").option("--instructions <text>", "Automation run instructions").option("--description <text>", "Deprecated alias for --instructions").option("--assignee-agent-id <id>", "Assignee agent ID").option("--project-id <id>", "Project ID").option("--goal-id <id>", "Goal ID").option("--parent-issue-id <id>", "Parent issue ID").option("--priority <priority>", "Issue priority for tracked output").option("--status <status>", "Automation status").option("--output-mode <mode>", "Automation output mode").option("--concurrency-policy <policy>", "Automation concurrency policy").option("--catch-up-policy <policy>", "Automation catch-up policy").option("--notify-on-issue-created", "Notify when tracked issue output is created").action(async (opts) => {
12412
13228
  try {
12413
- const ctx = resolveCommandContext(opts);
12414
- const payload = resolveApprovalSchema.parse({
12415
- decisionNote: opts.decisionNote,
12416
- decidedByUserId: opts.decidedByUserId
12417
- });
12418
- const updated = await ctx.api.post(`/api/approvals/${approvalId}/approve`, payload);
12419
- printOutput(updated, { json: ctx.json });
13229
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
13230
+ const payload = createAutomationSchema.parse(buildAutomationPayload(opts));
13231
+ const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/automations`, payload);
13232
+ printOutput(created, { json: ctx.json });
12420
13233
  } catch (err) {
12421
13234
  handleCommandError(err);
12422
13235
  }
12423
- })
13236
+ }),
13237
+ { includeCompany: false }
12424
13238
  );
12425
13239
  addCommonClientOptions(
12426
- approval.command("reject").description("Reject an approval request").argument("<approvalId>", "Approval ID").option("--decision-note <text>", "Decision note").option("--decided-by-user-id <id>", "Decision actor user ID").action(async (approvalId, opts) => {
13240
+ automation.command("update").description(getAgentCliCapabilityById("automation.update").description).argument("<automationId>", "Automation ID").option("--payload <json>", "Raw automation update payload JSON").option("--title <title>", "Automation title").option("--instructions <text>", "Automation run instructions").option("--description <text>", "Deprecated alias for --instructions").option("--assignee-agent-id <id>", "Assignee agent ID").option("--project-id <id>", "Project ID").option("--goal-id <id>", "Goal ID").option("--parent-issue-id <id>", "Parent issue ID").option("--priority <priority>", "Issue priority for tracked output").option("--status <status>", "Automation status").option("--output-mode <mode>", "Automation output mode").option("--concurrency-policy <policy>", "Automation concurrency policy").option("--catch-up-policy <policy>", "Automation catch-up policy").option("--notify-on-issue-created", "Notify when tracked issue output is created").action(async (automationId, opts) => {
12427
13241
  try {
12428
13242
  const ctx = resolveCommandContext(opts);
12429
- const payload = resolveApprovalSchema.parse({
12430
- decisionNote: opts.decisionNote,
12431
- decidedByUserId: opts.decidedByUserId
12432
- });
12433
- const updated = await ctx.api.post(`/api/approvals/${approvalId}/reject`, payload);
13243
+ const payload = updateAutomationSchema.parse(buildAutomationPayload(opts));
13244
+ const updated = await ctx.api.patch(`/api/automations/${encodeURIComponent(automationId)}`, payload);
12434
13245
  printOutput(updated, { json: ctx.json });
12435
13246
  } catch (err) {
12436
13247
  handleCommandError(err);
@@ -12438,14 +13249,12 @@ function registerApprovalCommands(program) {
12438
13249
  })
12439
13250
  );
12440
13251
  addCommonClientOptions(
12441
- approval.command("request-revision").description("Request revision for an approval").argument("<approvalId>", "Approval ID").option("--decision-note <text>", "Decision note").option("--decided-by-user-id <id>", "Decision actor user ID").action(async (approvalId, opts) => {
13252
+ automation.command("enable").description(getAgentCliCapabilityById("automation.enable").description).argument("<automationId>", "Automation ID").action(async (automationId, opts) => {
12442
13253
  try {
12443
13254
  const ctx = resolveCommandContext(opts);
12444
- const payload = requestApprovalRevisionSchema.parse({
12445
- decisionNote: opts.decisionNote,
12446
- decidedByUserId: opts.decidedByUserId
13255
+ const updated = await ctx.api.patch(`/api/automations/${encodeURIComponent(automationId)}`, {
13256
+ status: "active"
12447
13257
  });
12448
- const updated = await ctx.api.post(`/api/approvals/${approvalId}/request-revision`, payload);
12449
13258
  printOutput(updated, { json: ctx.json });
12450
13259
  } catch (err) {
12451
13260
  handleCommandError(err);
@@ -12453,13 +13262,12 @@ function registerApprovalCommands(program) {
12453
13262
  })
12454
13263
  );
12455
13264
  addCommonClientOptions(
12456
- approval.command("resubmit").description(getAgentCliCapabilityById("approval.resubmit").description).argument("<approvalId>", "Approval ID").option("--payload <json>", "Payload JSON object").action(async (approvalId, opts) => {
13265
+ automation.command("disable").description(getAgentCliCapabilityById("automation.disable").description).argument("<automationId>", "Automation ID").action(async (automationId, opts) => {
12457
13266
  try {
12458
13267
  const ctx = resolveCommandContext(opts);
12459
- const payload = resubmitApprovalSchema.parse({
12460
- payload: opts.payload ? parseJsonObject2(opts.payload, "payload") : void 0
13268
+ const updated = await ctx.api.patch(`/api/automations/${encodeURIComponent(automationId)}`, {
13269
+ status: "paused"
12461
13270
  });
12462
- const updated = await ctx.api.post(`/api/approvals/${approvalId}/resubmit`, payload);
12463
13271
  printOutput(updated, { json: ctx.json });
12464
13272
  } catch (err) {
12465
13273
  handleCommandError(err);
@@ -12467,161 +13275,374 @@ function registerApprovalCommands(program) {
12467
13275
  })
12468
13276
  );
12469
13277
  addCommonClientOptions(
12470
- approval.command("comment").description(getAgentCliCapabilityById("approval.comment").description).argument("<approvalId>", "Approval ID").option("--body-file <path>", "Read comment body from a file, or '-' for stdin").action(async (approvalId, opts) => {
13278
+ automation.command("run").description(getAgentCliCapabilityById("automation.run").description).argument("<automationId>", "Automation ID").option("--trigger-id <id>", "Trigger ID").option("--payload <json>", "Manual run payload JSON").option("--idempotency-key <key>", "Idempotency key").option("--source <source>", "Run source", "manual").action(async (automationId, opts) => {
12471
13279
  try {
12472
13280
  const ctx = resolveCommandContext(opts);
12473
- const body = await resolveBodyFile(opts.bodyFile);
12474
- const created = await ctx.api.post(`/api/approvals/${approvalId}/comments`, {
12475
- body
13281
+ const payload = runAutomationSchema.parse({
13282
+ triggerId: opts.triggerId,
13283
+ payload: opts.payload ? parseJsonOption(opts.payload, "--payload") : void 0,
13284
+ idempotencyKey: opts.idempotencyKey,
13285
+ source: opts.source
12476
13286
  });
12477
- printOutput(created, { json: ctx.json });
13287
+ const run = await ctx.api.post(`/api/automations/${encodeURIComponent(automationId)}/run`, payload);
13288
+ printOutput(run, { json: ctx.json });
12478
13289
  } catch (err) {
12479
13290
  handleCommandError(err);
12480
13291
  }
12481
13292
  })
12482
13293
  );
12483
13294
  }
12484
- async function resolveBodyFile(inputPath) {
12485
- if (!inputPath) {
12486
- throw new Error("Provide --body-file <path>; use --body-file - for stdin");
12487
- }
12488
- return readTextInputFile2(inputPath, "--body-file");
13295
+ function buildAutomationPayload(opts) {
13296
+ return {
13297
+ ...parseJsonObjectOption(opts.payload, "--payload"),
13298
+ ...definedRecord({
13299
+ title: opts.title,
13300
+ instructions: opts.instructions ?? opts.description,
13301
+ assigneeAgentId: opts.assigneeAgentId,
13302
+ projectId: opts.projectId,
13303
+ goalId: opts.goalId,
13304
+ parentIssueId: opts.parentIssueId,
13305
+ priority: opts.priority,
13306
+ status: opts.status,
13307
+ outputMode: opts.outputMode,
13308
+ concurrencyPolicy: opts.concurrencyPolicy,
13309
+ catchUpPolicy: opts.catchUpPolicy,
13310
+ notifyOnIssueCreated: opts.notifyOnIssueCreated
13311
+ })
13312
+ };
12489
13313
  }
12490
- async function readTextInputFile2(inputPath, optionName) {
12491
- if (inputPath === "-") {
12492
- return readStdinText2();
13314
+ function definedRecord(record) {
13315
+ return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== void 0));
13316
+ }
13317
+ function parseJsonOption(value, label) {
13318
+ try {
13319
+ return JSON.parse(value);
13320
+ } catch (error) {
13321
+ throw new Error(`${label} must be valid JSON`);
12493
13322
  }
12494
- const resolvedPath = path19.resolve(process.cwd(), inputPath);
12495
- return readFile5(resolvedPath, "utf8").catch((err) => {
12496
- throw new Error(`Unable to read ${optionName} ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
12497
- });
12498
13323
  }
12499
- async function readStdinText2() {
12500
- const chunks = [];
12501
- for await (const chunk of process.stdin) {
12502
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
13324
+ function parseJsonObjectOption(value, label) {
13325
+ if (!value) return {};
13326
+ const parsed = parseJsonOption(value, label);
13327
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
13328
+ throw new Error(`${label} must be a JSON object`);
12503
13329
  }
12504
- return Buffer.concat(chunks).toString("utf8");
13330
+ return parsed;
12505
13331
  }
12506
- function parseCsv4(value) {
12507
- if (!value) return void 0;
12508
- const rows = value.split(",").map((v) => v.trim()).filter(Boolean);
12509
- return rows.length > 0 ? rows : void 0;
13332
+ function formatAutomationListItem(row) {
13333
+ const nextRunAt = row.triggers.map((trigger) => trigger.nextRunAt).filter(Boolean).sort()[0] ?? null;
13334
+ return {
13335
+ id: row.id,
13336
+ title: row.title,
13337
+ status: row.status,
13338
+ assigneeAgentId: row.assigneeAgentId,
13339
+ outputMode: row.outputMode,
13340
+ lastRun: row.lastRun?.status ?? "-",
13341
+ nextRunAt
13342
+ };
12510
13343
  }
12511
- function parseJsonObject2(value, name) {
12512
- try {
12513
- const parsed = JSON.parse(value);
12514
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
12515
- throw new Error(`${name} must be a JSON object`);
12516
- }
12517
- return parsed;
12518
- } catch (err) {
12519
- throw new Error(`Invalid ${name} JSON: ${err instanceof Error ? err.message : String(err)}`);
12520
- }
13344
+ function formatAutomationRun(row) {
13345
+ return {
13346
+ id: row.id,
13347
+ status: row.status,
13348
+ source: row.source,
13349
+ triggeredAt: row.triggeredAt,
13350
+ linkedIssue: row.linkedIssue?.identifier ?? row.linkedIssueId ?? "-",
13351
+ linkedChatConversationId: row.linkedChatConversationId ?? "-",
13352
+ failureReason: row.failureReason ?? "-"
13353
+ };
12521
13354
  }
12522
13355
 
12523
- // src/commands/client/activity.ts
12524
- function registerActivityCommands(program) {
12525
- const activity = program.command("activity").description("Activity log operations");
13356
+ // src/commands/client/chat.ts
13357
+ init_dist();
13358
+ function registerChatCommands(program) {
13359
+ const chat = program.command("chat").description("Chat operations");
12526
13360
  addCommonClientOptions(
12527
- activity.command("list").description("List organization activity log entries").option("-O, --org-id <id>", "Organization ID").option("--agent-id <id>", "Filter by agent ID").option("--entity-type <type>", "Filter by entity type").option("--entity-id <id>", "Filter by entity ID").action(async (opts) => {
13361
+ chat.command("list").description(getAgentCliCapabilityById("chat.list").description).option("-O, --org-id <id>", "Organization ID").option("--status <status>", "active, resolved, archived, or all", "active").option("--query <text>", "Server-side chat search query").option("--limit <n>", "Maximum rows to print").action(async (opts) => {
12528
13362
  try {
12529
13363
  const ctx = resolveCommandContext(opts, { requireCompany: true });
12530
- const params = new URLSearchParams();
12531
- if (opts.agentId) params.set("agentId", opts.agentId);
12532
- if (opts.entityType) params.set("entityType", opts.entityType);
12533
- if (opts.entityId) params.set("entityId", opts.entityId);
12534
- const query = params.toString();
12535
- const path23 = `/api/orgs/${ctx.orgId}/activity${query ? `?${query}` : ""}`;
12536
- const rows = await ctx.api.get(path23) ?? [];
12537
- if (ctx.json) {
12538
- printOutput(rows, { json: true });
12539
- return;
12540
- }
12541
- if (rows.length === 0) {
12542
- printOutput([], { json: false });
12543
- return;
12544
- }
12545
- for (const row of rows) {
12546
- console.log(
12547
- formatInlineRecord({
12548
- id: row.id,
12549
- action: row.action,
12550
- actorType: row.actorType,
12551
- actorId: row.actorId,
12552
- entityType: row.entityType,
12553
- entityId: row.entityId,
12554
- createdAt: String(row.createdAt)
12555
- })
12556
- );
12557
- }
13364
+ const rows = await listChats(ctx, opts);
13365
+ printOutput(ctx.json ? rows : rows.map(formatChatConversation), { json: ctx.json });
13366
+ } catch (err) {
13367
+ handleCommandError(err);
13368
+ }
13369
+ }),
13370
+ { includeCompany: false }
13371
+ );
13372
+ addCommonClientOptions(
13373
+ chat.command("search").description(getAgentCliCapabilityById("chat.search").description).argument("<query>", "Search query").option("-O, --org-id <id>", "Organization ID").option("--status <status>", "active, resolved, archived, or all", "all").option("--scope <scope>", "Search scope: all, title, summary, messages", "all").option("--limit <n>", "Maximum rows to print", "20").option("--snippet-chars <n>", "Maximum snippet characters", "220").action(async (query, opts) => {
13374
+ try {
13375
+ const ctx = resolveCommandContext(opts, { requireCompany: true });
13376
+ const rows = await listChats(ctx, { ...opts, query });
13377
+ const filtered = filterChatSearchRows(rows, query, opts.scope ?? "all").slice(0, parseLimit(opts.limit, rows.length));
13378
+ const snippetChars = parseLimit(opts.snippetChars, 220);
13379
+ printOutput(
13380
+ ctx.json ? filtered : filtered.map((row) => formatChatSearchResult(row, snippetChars)),
13381
+ { json: ctx.json }
13382
+ );
13383
+ } catch (err) {
13384
+ handleCommandError(err);
13385
+ }
13386
+ }),
13387
+ { includeCompany: false }
13388
+ );
13389
+ addCommonClientOptions(
13390
+ chat.command("get").description(getAgentCliCapabilityById("chat.get").description).argument("<chatId>", "Chat conversation ID").action(async (chatId, opts) => {
13391
+ try {
13392
+ const ctx = resolveCommandContext(opts);
13393
+ const row = await ctx.api.get(`/api/chats/${encodeURIComponent(chatId)}`);
13394
+ printOutput(row, { json: ctx.json });
13395
+ } catch (err) {
13396
+ handleCommandError(err);
13397
+ }
13398
+ })
13399
+ );
13400
+ addCommonClientOptions(
13401
+ chat.command("messages").description(getAgentCliCapabilityById("chat.messages").description).argument("<chatId>", "Chat conversation ID").option("--include-transcript", "Include assistant transcript entries").option("--include-output", "Alias for --include-transcript").option("--include-outputs", "Alias for --include-transcript").option("--limit <n>", "Maximum messages to print").option("--cursor <cursor>", "Stable message cursor returned in page.nextCursor").option("--max-output-chars <n>", "Maximum transcript output chars for human output", "1200").action(async (chatId, opts) => {
13402
+ try {
13403
+ const ctx = resolveCommandContext(opts);
13404
+ const page = await getChatMessagesPage(ctx, chatId, {
13405
+ includeTranscript: includesChatTranscript(opts),
13406
+ limit: opts.limit,
13407
+ cursor: opts.cursor
13408
+ });
13409
+ printOutput(
13410
+ ctx.json ? page : page.messages.map((message) => formatChatMessage(message, parseLimit(opts.maxOutputChars, 1200))),
13411
+ { json: ctx.json }
13412
+ );
13413
+ } catch (err) {
13414
+ handleCommandError(err);
13415
+ }
13416
+ })
13417
+ );
13418
+ addCommonClientOptions(
13419
+ chat.command("transcript").description(getAgentCliCapabilityById("chat.transcript").description).argument("<chatId>", "Chat conversation ID").option("--limit <n>", "Maximum messages to print").option("--cursor <cursor>", "Stable message cursor returned in page.nextCursor").option("--max-chars <n>", "Maximum transcript chars per message", "1200").option("--max-output-chars <n>", "Alias for --max-chars").action(async (chatId, opts) => {
13420
+ try {
13421
+ const ctx = resolveCommandContext(opts);
13422
+ const page = await getChatMessagesPage(ctx, chatId, {
13423
+ includeTranscript: true,
13424
+ limit: opts.limit,
13425
+ cursor: opts.cursor
13426
+ });
13427
+ const maxChars = parseLimit(opts.maxOutputChars ?? opts.maxChars, 1200);
13428
+ printOutput(
13429
+ ctx.json ? page : page.messages.flatMap((message) => formatChatTranscriptMessage(message, maxChars)),
13430
+ { json: ctx.json }
13431
+ );
13432
+ } catch (err) {
13433
+ handleCommandError(err);
13434
+ }
13435
+ })
13436
+ );
13437
+ addCommonClientOptions(
13438
+ chat.command("read").description(getAgentCliCapabilityById("chat.read").description).argument("<chatId>", "Chat conversation ID").option("--include-transcript", "Include assistant transcript entries").option("--include-output", "Alias for --include-transcript").option("--include-outputs", "Alias for --include-transcript").option("--limit <n>", "Maximum recent messages", "20").option("--turn-limit <n>", "Alias for --limit for chat turn snapshots").option("--cursor <cursor>", "Stable message cursor returned in page.nextCursor").option("--max-output-chars <n>", "Maximum transcript output chars for human output", "1200").action(async (chatId, opts) => {
13439
+ try {
13440
+ const ctx = resolveCommandContext(opts);
13441
+ const [conversation, page] = await Promise.all([
13442
+ ctx.api.get(`/api/chats/${encodeURIComponent(chatId)}`),
13443
+ getChatMessagesPage(ctx, chatId, {
13444
+ includeTranscript: includesChatTranscript(opts),
13445
+ limit: opts.turnLimit ?? opts.limit,
13446
+ cursor: opts.cursor
13447
+ })
13448
+ ]);
13449
+ const payload = {
13450
+ conversation,
13451
+ messages: page.messages,
13452
+ page: page.page
13453
+ };
13454
+ printOutput(
13455
+ ctx.json ? payload : page.messages.map((message) => formatChatMessage(message, parseLimit(opts.maxOutputChars, 1200))),
13456
+ { json: ctx.json }
13457
+ );
12558
13458
  } catch (err) {
12559
13459
  handleCommandError(err);
12560
13460
  }
12561
- }),
12562
- { includeCompany: false }
13461
+ })
12563
13462
  );
12564
- }
12565
-
12566
- // src/commands/client/dashboard.ts
12567
- function registerDashboardCommands(program) {
12568
- const dashboard = program.command("dashboard").description("Dashboard summary operations");
12569
13463
  addCommonClientOptions(
12570
- dashboard.command("get").description("Get dashboard summary for an organization").option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
13464
+ chat.command("create").description(getAgentCliCapabilityById("chat.create").description).option("-O, --org-id <id>", "Organization ID").option("--payload <json>", "Raw chat create payload JSON").option("--title <title>", "Chat title").option("--summary <text>", "Chat summary").option("--preferred-agent-id <id>", "Preferred agent ID").option("--issue-creation-mode <mode>", "Issue creation mode").option("--plan-mode", "Create a plan-mode chat").action(async (opts) => {
12571
13465
  try {
12572
13466
  const ctx = resolveCommandContext(opts, { requireCompany: true });
12573
- const row = await ctx.api.get(`/api/orgs/${ctx.orgId}/dashboard`);
12574
- printOutput(row, { json: ctx.json });
13467
+ const payload = createChatConversationSchema.parse({
13468
+ ...parseJsonObjectOption2(opts.payload, "--payload"),
13469
+ ...definedRecord2({
13470
+ title: opts.title,
13471
+ summary: opts.summary,
13472
+ preferredAgentId: opts.preferredAgentId,
13473
+ issueCreationMode: opts.issueCreationMode,
13474
+ planMode: opts.planMode
13475
+ })
13476
+ });
13477
+ const created = await ctx.api.post(`/api/orgs/${ctx.orgId}/chats`, payload);
13478
+ printOutput(created, { json: ctx.json });
12575
13479
  } catch (err) {
12576
13480
  handleCommandError(err);
12577
13481
  }
12578
13482
  }),
12579
13483
  { includeCompany: false }
12580
13484
  );
12581
- }
12582
-
12583
- // src/commands/client/skill.ts
12584
- init_dist();
12585
- function registerSkillCommands(program) {
12586
- const skill = program.command("skill").description("Organization skill library operations");
12587
13485
  addCommonClientOptions(
12588
- skill.command("list").description(getAgentCliCapabilityById("skill.list").description).option("-O, --org-id <id>", "Organization ID").action(async (opts) => {
13486
+ chat.command("send").description(getAgentCliCapabilityById("chat.send").description).argument("<chatId>", "Chat conversation ID").option("--body <text>", "Message body").option("--edit-user-message-id <id>", "Regenerate/edit from a prior user message").action(async (chatId, opts) => {
12589
13487
  try {
12590
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12591
- const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/skills`) ?? [];
12592
- if (ctx.json) {
12593
- printOutput(rows, { json: true });
12594
- return;
12595
- }
12596
- if (rows.length === 0) {
12597
- printOutput([], { json: false });
12598
- return;
12599
- }
12600
- for (const row of rows) {
12601
- console.log(
12602
- formatInlineRecord({
12603
- id: row.id,
12604
- key: row.key,
12605
- slug: row.slug,
12606
- name: row.name,
12607
- sourceBadge: row.sourceBadge,
12608
- compatibility: row.compatibility,
12609
- attachedAgentCount: row.attachedAgentCount
12610
- })
12611
- );
12612
- }
13488
+ const ctx = resolveCommandContext(opts);
13489
+ const body = opts.body ?? await readStdin();
13490
+ const payload = addChatMessageSchema.parse({
13491
+ body,
13492
+ editUserMessageId: opts.editUserMessageId
13493
+ });
13494
+ const result = await ctx.api.post(`/api/chats/${encodeURIComponent(chatId)}/messages`, payload);
13495
+ printOutput(result, { json: ctx.json });
12613
13496
  } catch (err) {
12614
13497
  handleCommandError(err);
12615
13498
  }
12616
- }),
12617
- { includeCompany: false }
13499
+ })
12618
13500
  );
12619
13501
  addCommonClientOptions(
12620
- skill.command("get").description(getAgentCliCapabilityById("skill.get").description).argument("<skillId>", "Skill ID").option("-O, --org-id <id>", "Organization ID").action(async (skillId, opts) => {
13502
+ chat.command("archive").description(getAgentCliCapabilityById("chat.archive").description).argument("<chatId>", "Chat conversation ID").action(async (chatId, opts) => {
13503
+ try {
13504
+ const ctx = resolveCommandContext(opts);
13505
+ const payload = updateChatConversationSchema.parse({ status: "archived" });
13506
+ const updated = await ctx.api.patch(`/api/chats/${encodeURIComponent(chatId)}`, payload);
13507
+ printOutput(updated, { json: ctx.json });
13508
+ } catch (err) {
13509
+ handleCommandError(err);
13510
+ }
13511
+ })
13512
+ );
13513
+ }
13514
+ async function listChats(ctx, opts) {
13515
+ const params = new URLSearchParams();
13516
+ if (opts.status) params.set("status", opts.status);
13517
+ if (opts.query) params.set("q", opts.query);
13518
+ const rows = await ctx.api.get(`/api/orgs/${ctx.orgId}/chats?${params.toString()}`) ?? [];
13519
+ return rows.slice(0, parseLimit(opts.limit, rows.length));
13520
+ }
13521
+ async function getChatMessagesPage(ctx, chatId, opts) {
13522
+ const params = new URLSearchParams();
13523
+ params.set("envelope", "true");
13524
+ params.set("order", "newest");
13525
+ params.set("limit", String(parseLimit(opts.limit, 50)));
13526
+ if (opts.cursor) params.set("cursor", opts.cursor);
13527
+ if (opts.includeTranscript) params.set("includeTranscript", "true");
13528
+ const page = await ctx.api.get(`/api/chats/${encodeURIComponent(chatId)}/messages?${params.toString()}`);
13529
+ if (!page) {
13530
+ throw new Error("Chat messages response was empty");
13531
+ }
13532
+ return page;
13533
+ }
13534
+ function filterChatSearchRows(rows, query, scope) {
13535
+ const normalized = query.toLowerCase();
13536
+ if (scope === "all") return rows;
13537
+ return rows.filter((row) => {
13538
+ if (scope === "title") return row.title.toLowerCase().includes(normalized);
13539
+ if (scope === "summary") return Boolean(row.summary?.toLowerCase().includes(normalized));
13540
+ if (scope === "messages") return Boolean(row.searchPreview?.toLowerCase().includes(normalized));
13541
+ return true;
13542
+ });
13543
+ }
13544
+ function formatChatConversation(row) {
13545
+ return {
13546
+ id: row.id,
13547
+ title: row.title,
13548
+ status: row.status,
13549
+ preferredAgentId: row.preferredAgentId ?? "-",
13550
+ unread: row.unreadCount,
13551
+ lastMessageAt: row.lastMessageAt ?? "-",
13552
+ preview: row.latestReplyPreview ?? row.latestUserMessagePreview ?? "-"
13553
+ };
13554
+ }
13555
+ function formatChatSearchResult(row, maxChars) {
13556
+ return {
13557
+ id: row.id,
13558
+ title: row.title,
13559
+ status: row.status,
13560
+ lastMessageAt: row.lastMessageAt ?? "-",
13561
+ snippet: clip(row.searchPreview ?? row.latestReplyPreview ?? row.latestUserMessagePreview ?? row.summary ?? "", maxChars)
13562
+ };
13563
+ }
13564
+ function formatChatMessage(row, maxOutputChars = 1200) {
13565
+ return {
13566
+ id: row.id,
13567
+ role: row.role,
13568
+ kind: row.kind,
13569
+ status: row.status,
13570
+ createdAt: row.createdAt,
13571
+ body: clip(row.body, 220),
13572
+ transcriptEntries: row.transcriptSummary?.entryCount ?? row.transcript?.length ?? 0,
13573
+ ...row.transcript?.length ? { transcriptPreview: clip(row.transcript.map((entry) => formatTranscriptEntry(entry, maxOutputChars)).join(" "), maxOutputChars) } : {}
13574
+ };
13575
+ }
13576
+ function formatChatTranscriptMessage(row, maxChars) {
13577
+ const header = {
13578
+ id: row.id,
13579
+ role: row.role,
13580
+ kind: row.kind,
13581
+ status: row.status,
13582
+ createdAt: row.createdAt,
13583
+ body: clip(row.body, 220)
13584
+ };
13585
+ const transcriptRows = (row.transcript ?? []).map((entry, index) => ({
13586
+ id: `${row.id}:entry-${index + 1}`,
13587
+ messageId: row.id,
13588
+ role: row.role,
13589
+ entry: formatTranscriptEntry(entry, maxChars)
13590
+ }));
13591
+ return transcriptRows.length > 0 ? [header, ...transcriptRows] : [header];
13592
+ }
13593
+ function formatTranscriptEntry(entry, maxChars) {
13594
+ if (entry.kind === "tool_call") return `${entry.ts} tool_call ${entry.name} ${clip(JSON.stringify(entry.input), maxChars)}`;
13595
+ if (entry.kind === "tool_result") return `${entry.ts} tool_result ${entry.toolName ?? entry.toolUseId} ${entry.isError ? "ERROR " : ""}${clip(entry.content, maxChars)}`;
13596
+ if (entry.kind === "result") return `${entry.ts} result ${entry.isError ? "ERROR " : ""}${clip(entry.text || entry.errors.join("; "), maxChars)}`;
13597
+ if ("text" in entry) return `${entry.ts} ${entry.kind} ${clip(entry.text, maxChars)}`;
13598
+ return `${entry.ts} ${entry.kind}`;
13599
+ }
13600
+ function parseLimit(value, fallback) {
13601
+ const parsed = Number(value ?? fallback);
13602
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
13603
+ return Math.floor(parsed);
13604
+ }
13605
+ function includesChatTranscript(opts) {
13606
+ return Boolean(opts.includeTranscript || opts.includeOutput || opts.includeOutputs);
13607
+ }
13608
+ function clip(value, maxChars) {
13609
+ const text6 = value.replace(/\s+/g, " ").trim();
13610
+ if (text6.length <= maxChars) return text6;
13611
+ return `${text6.slice(0, Math.max(0, maxChars - 1))}\u2026`;
13612
+ }
13613
+ function parseJsonObjectOption2(value, label) {
13614
+ if (!value) return {};
13615
+ try {
13616
+ const parsed = JSON.parse(value);
13617
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
13618
+ throw new Error(`${label} must be a JSON object`);
13619
+ }
13620
+ return parsed;
13621
+ } catch (error) {
13622
+ if (error instanceof Error && error.message.includes("JSON object")) throw error;
13623
+ throw new Error(`${label} must be valid JSON`);
13624
+ }
13625
+ }
13626
+ function definedRecord2(record) {
13627
+ return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== void 0));
13628
+ }
13629
+ async function readStdin() {
13630
+ const chunks = [];
13631
+ for await (const chunk of process.stdin) {
13632
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
13633
+ }
13634
+ return Buffer.concat(chunks).toString("utf8");
13635
+ }
13636
+
13637
+ // src/commands/client/runs.ts
13638
+ function registerRunsCommands(program) {
13639
+ const runs = program.command("runs").description("Run debugging operations");
13640
+ addCommonClientOptions(
13641
+ runs.command("list").description(getAgentCliCapabilityById("runs.list").description).option("-O, --org-id <id>", "Organization ID").option("--updated-after <iso>", "Only runs updated after this timestamp").option("--run-id-prefix <prefix>", "Filter by run ID prefix").option("--agent-id <id>", "Filter by agent ID").option("--status <status>", "Filter by run status").option("--runtime <type>", "Filter by runtime type").option("--issue-id <id>", "Filter by linked issue ID").option("--created-before <iso>", "Only runs created before this timestamp").option("--limit <n>", "Maximum rows", "200").action(async (opts) => {
12621
13642
  try {
12622
13643
  const ctx = resolveCommandContext(opts, { requireCompany: true });
12623
- const row = await ctx.api.get(`/api/orgs/${ctx.orgId}/skills/${skillId}`);
12624
- printOutput(row, { json: ctx.json });
13644
+ const rows = await ctx.api.get(`/api/run-intelligence/orgs/${ctx.orgId}/runs?${buildRunsListQuery(opts)}`) ?? [];
13645
+ printOutput(ctx.json ? rows : rows.map(formatRunListRow), { json: ctx.json });
12625
13646
  } catch (err) {
12626
13647
  handleCommandError(err);
12627
13648
  }
@@ -12629,167 +13650,178 @@ function registerSkillCommands(program) {
12629
13650
  { includeCompany: false }
12630
13651
  );
12631
13652
  addCommonClientOptions(
12632
- skill.command("file").description(getAgentCliCapabilityById("skill.file").description).argument("<skillId>", "Skill ID").option("-O, --org-id <id>", "Organization ID").option("--path <path>", "Skill package file path", "SKILL.md").action(async (skillId, opts) => {
13653
+ runs.command("get").description(getAgentCliCapabilityById("runs.get").description).argument("<runId>", "Run ID").action(async (runId, opts) => {
12633
13654
  try {
12634
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12635
- const query = new URLSearchParams({ path: opts.path ?? "SKILL.md" });
12636
- const row = await ctx.api.get(
12637
- `/api/orgs/${ctx.orgId}/skills/${skillId}/files?${query.toString()}`
12638
- );
13655
+ const ctx = resolveCommandContext(opts);
13656
+ const row = await ctx.api.get(`/api/run-intelligence/runs/${encodeURIComponent(runId)}`);
12639
13657
  printOutput(row, { json: ctx.json });
12640
13658
  } catch (err) {
12641
13659
  handleCommandError(err);
12642
13660
  }
12643
- }),
12644
- { includeCompany: false }
13661
+ })
12645
13662
  );
12646
13663
  addCommonClientOptions(
12647
- skill.command("import").description(getAgentCliCapabilityById("skill.import").description).option("-O, --org-id <id>", "Organization ID").requiredOption("--source <source>", "Skill source (local path, URL, or repo ref)").action(async (opts) => {
13664
+ runs.command("events").description(getAgentCliCapabilityById("runs.events").description).argument("<runId>", "Run ID").action(async (runId, opts) => {
12648
13665
  try {
12649
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12650
- const payload = organizationSkillImportSchema.parse({ source: opts.source });
12651
- const result = await ctx.api.post(`/api/orgs/${ctx.orgId}/skills/import`, payload);
12652
- printOutput(result, { json: ctx.json });
13666
+ const ctx = resolveCommandContext(opts);
13667
+ const rows = await ctx.api.get(`/api/run-intelligence/runs/${encodeURIComponent(runId)}/events`) ?? [];
13668
+ printOutput(ctx.json ? rows : rows.map(formatRunEvent), { json: ctx.json });
12653
13669
  } catch (err) {
12654
13670
  handleCommandError(err);
12655
13671
  }
12656
- }),
12657
- { includeCompany: false }
13672
+ })
12658
13673
  );
12659
13674
  addCommonClientOptions(
12660
- skill.command("scan-local").description(getAgentCliCapabilityById("skill.scan-local").description).option("-O, --org-id <id>", "Organization ID").option("--roots <csv>", "Comma-separated local roots to scan").action(async (opts) => {
13675
+ runs.command("log").description(getAgentCliCapabilityById("runs.log").description).argument("<runId>", "Run ID").option("--max-chars <n>", "Maximum log characters for human output", "12000").action(async (runId, opts) => {
12661
13676
  try {
12662
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12663
- const payload = organizationSkillLocalScanRequestSchema.parse({
12664
- roots: parseCsv5(opts.roots)
12665
- });
12666
- const result = await ctx.api.post(
12667
- `/api/orgs/${ctx.orgId}/skills/scan-local`,
12668
- payload
12669
- );
12670
- printOutput(result, { json: ctx.json });
13677
+ const ctx = resolveCommandContext(opts);
13678
+ const row = await ctx.api.get(`/api/run-intelligence/runs/${encodeURIComponent(runId)}/log`);
13679
+ if (ctx.json) {
13680
+ printOutput(row, { json: true });
13681
+ } else {
13682
+ process.stdout.write(clip2(row?.content ?? "", parseLimit2(opts.maxChars, 12e3)) + "\n");
13683
+ }
12671
13684
  } catch (err) {
12672
13685
  handleCommandError(err);
12673
13686
  }
12674
- }),
12675
- { includeCompany: false }
13687
+ })
12676
13688
  );
12677
13689
  addCommonClientOptions(
12678
- skill.command("scan-projects").description(getAgentCliCapabilityById("skill.scan-projects").description).option("-O, --org-id <id>", "Organization ID").option("--project-ids <csv>", "Comma-separated project IDs").option("--workspace-ids <csv>", "Comma-separated workspace IDs").action(async (opts) => {
13690
+ runs.command("transcript").description(getAgentCliCapabilityById("runs.transcript").description).argument("<runId>", "Run ID").option("--errors-only", "Show only error transcript rows").option("--around-error <id>", "Show context around a run error id such as step-12").option("--context-turns <n>", "Turns around --around-error", "1").option("--cursor <cursor>", "Stable transcript cursor returned in page.nextCursor").option("--turn-limit <n>", "Maximum turns to return", "20").option("--chronological", "Show oldest-first instead of default newest-first").option("--narrative", "Use a narrative human layout").option("--max-chars <n>", "Maximum output characters per row", "1200").option("--max-output-chars <n>", "Alias for --max-chars").option("--include-output", "Include row output in compact human transcript rows").option("--include-outputs", "Alias for --include-output").action(async (runId, opts) => {
12679
13691
  try {
12680
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12681
- const payload = organizationSkillProjectScanRequestSchema.parse({
12682
- projectIds: parseCsv5(opts.projectIds),
12683
- workspaceIds: parseCsv5(opts.workspaceIds)
12684
- });
12685
- const result = await ctx.api.post(
12686
- `/api/orgs/${ctx.orgId}/skills/scan-projects`,
12687
- payload
13692
+ const ctx = resolveCommandContext(opts);
13693
+ const payload = await ctx.api.get(
13694
+ `/api/run-intelligence/runs/${encodeURIComponent(runId)}/transcript?${buildTranscriptQuery(opts, { json: ctx.json })}`
12688
13695
  );
12689
- printOutput(result, { json: ctx.json });
13696
+ if (ctx.json) {
13697
+ printOutput(payload, { json: true });
13698
+ } else if (opts.narrative) {
13699
+ printOutput((payload?.rows ?? []).map(formatRunTranscriptNarrative), { json: false });
13700
+ } else {
13701
+ printOutput((payload?.rows ?? []).map(formatRunTranscriptRow), { json: false });
13702
+ }
12690
13703
  } catch (err) {
12691
13704
  handleCommandError(err);
12692
13705
  }
12693
- }),
12694
- { includeCompany: false }
13706
+ })
12695
13707
  );
12696
- }
12697
- function parseCsv5(value) {
12698
- if (!value) return void 0;
12699
- const rows = value.split(",").map((entry) => entry.trim()).filter(Boolean);
12700
- return rows.length > 0 ? rows : void 0;
12701
- }
12702
-
12703
- // src/commands/client/library.ts
12704
- import { readFile as readFile6 } from "node:fs/promises";
12705
- import path20 from "node:path";
12706
- function registerLibraryCommands(program) {
12707
- const library = program.command("library").description("Library file operations");
12708
- const file = library.command("file").description("Path-based Library file operations");
12709
13708
  addCommonClientOptions(
12710
- file.command("list").description(getAgentCliCapabilityById("library.file.list").description).argument("[directoryPath]", "Library directory path", "projects").action(async (directoryPath, opts) => {
13709
+ runs.command("errors").description(getAgentCliCapabilityById("runs.errors").description).argument("<runId>", "Run ID").option("--max-chars <n>", "Maximum output characters per error", "1200").action(async (runId, opts) => {
12711
13710
  try {
12712
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12713
- const search = new URLSearchParams();
12714
- const normalizedPath = directoryPath.trim();
12715
- if (normalizedPath) search.set("path", normalizedPath);
12716
- const query = search.toString();
12717
- const result = await ctx.api.get(
12718
- `/api/orgs/${ctx.orgId}/workspace/files${query ? `?${query}` : ""}`
13711
+ const ctx = resolveCommandContext(opts);
13712
+ const params = new URLSearchParams();
13713
+ params.set("maxChars", String(parseLimit2(opts.maxChars, 1200)));
13714
+ const payload = await ctx.api.get(
13715
+ `/api/run-intelligence/runs/${encodeURIComponent(runId)}/errors?${params.toString()}`
12719
13716
  );
12720
- printOutput(result, { json: ctx.json });
13717
+ printOutput(ctx.json ? payload : (payload?.errors ?? []).map(formatRunError), { json: ctx.json });
12721
13718
  } catch (err) {
12722
13719
  handleCommandError(err);
12723
13720
  }
12724
- }),
12725
- { includeCompany: true }
13721
+ })
12726
13722
  );
12727
13723
  addCommonClientOptions(
12728
- file.command("get").description(getAgentCliCapabilityById("library.file.get").description).argument("<filePath>", "Library file path").action(async (filePath, opts) => {
13724
+ runs.command("cancel").description(getAgentCliCapabilityById("runs.cancel").description).argument("<runId>", "Run ID").action(async (runId, opts) => {
12729
13725
  try {
12730
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12731
- const search = new URLSearchParams();
12732
- search.set("path", filePath.trim());
12733
- const result = await ctx.api.get(
12734
- `/api/orgs/${ctx.orgId}/workspace/file?${search.toString()}`
12735
- );
12736
- printOutput(result, { json: ctx.json });
13726
+ const ctx = resolveCommandContext(opts);
13727
+ const row = await ctx.api.post(`/api/heartbeat-runs/${encodeURIComponent(runId)}/cancel`, {});
13728
+ printOutput(row, { json: ctx.json });
12737
13729
  } catch (err) {
12738
13730
  handleCommandError(err);
12739
13731
  }
12740
- }),
12741
- { includeCompany: true }
13732
+ })
12742
13733
  );
12743
13734
  addCommonClientOptions(
12744
- file.command("put").description(getAgentCliCapabilityById("library.file.put").description).argument("<filePath>", "Library file path").option("--body-file <path>", "Read file content from a file, or '-' for stdin").action(async (filePath, opts) => {
13735
+ runs.command("retry").description(getAgentCliCapabilityById("runs.retry").description).argument("<runId>", "Run ID").action(async (runId, opts) => {
12745
13736
  try {
12746
- const ctx = resolveCommandContext(opts, { requireCompany: true });
12747
- const body = await resolveBodyFileInput(opts.bodyFile);
12748
- const search = new URLSearchParams();
12749
- search.set("path", filePath.trim());
12750
- const updatePath = `/api/orgs/${ctx.orgId}/workspace/file?${search.toString()}`;
12751
- const updated = await ctx.api.patch(
12752
- updatePath,
12753
- { content: body },
12754
- { ignoreNotFound: true }
12755
- );
12756
- if (updated) {
12757
- printOutput(updated, { json: ctx.json });
12758
- return;
12759
- }
12760
- const created = await ctx.api.post(
12761
- `/api/orgs/${ctx.orgId}/workspace/file`,
12762
- { filePath: filePath.trim(), content: body }
12763
- );
12764
- printOutput(created, { json: ctx.json });
13737
+ const ctx = resolveCommandContext(opts);
13738
+ const row = await ctx.api.post(`/api/heartbeat-runs/${encodeURIComponent(runId)}/retry`, {});
13739
+ printOutput(row, { json: ctx.json });
12765
13740
  } catch (err) {
12766
13741
  handleCommandError(err);
12767
13742
  }
12768
- }),
12769
- { includeCompany: true }
13743
+ })
12770
13744
  );
12771
13745
  }
12772
- async function resolveBodyFileInput(inputPath) {
12773
- if (process.argv.includes("--body")) {
12774
- throw new Error("--body was removed; write the body to a file and use --body-file <path> or --body-file - for stdin");
12775
- }
12776
- if (inputPath === void 0) {
12777
- throw new Error("Provide --body-file <path>; use --body-file - for stdin");
12778
- }
12779
- if (inputPath === "-") {
12780
- return readStdinText3();
12781
- }
12782
- const resolvedPath = path20.resolve(process.cwd(), inputPath);
12783
- return readFile6(resolvedPath, "utf8").catch((err) => {
12784
- throw new Error(`Unable to read --body-file ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
12785
- });
13746
+ function buildRunsListQuery(opts) {
13747
+ const params = new URLSearchParams();
13748
+ if (opts.updatedAfter) params.set("updatedAfter", opts.updatedAfter);
13749
+ if (opts.runIdPrefix) params.set("runIdPrefix", opts.runIdPrefix);
13750
+ if (opts.agentId) params.set("agentId", opts.agentId);
13751
+ if (opts.status) params.set("status", opts.status);
13752
+ if (opts.runtime) params.set("runtime", opts.runtime);
13753
+ if (opts.issueId) params.set("issueId", opts.issueId);
13754
+ if (opts.createdBefore) params.set("createdBefore", opts.createdBefore);
13755
+ if (opts.limit) params.set("limit", opts.limit);
13756
+ return params.toString();
12786
13757
  }
12787
- async function readStdinText3() {
12788
- const chunks = [];
12789
- for await (const chunk of process.stdin) {
12790
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
12791
- }
12792
- return Buffer.concat(chunks).toString("utf8");
13758
+ function buildTranscriptQuery(opts, output) {
13759
+ const params = new URLSearchParams();
13760
+ if (opts.errorsOnly) params.set("errorsOnly", "true");
13761
+ if (opts.aroundError) params.set("aroundError", opts.aroundError);
13762
+ if (opts.cursor) params.set("cursor", opts.cursor);
13763
+ if (opts.turnLimit) params.set("turnLimit", String(parseLimit2(opts.turnLimit, 20)));
13764
+ params.set("contextTurns", String(parseLimit2(opts.contextTurns, 1)));
13765
+ params.set("order", opts.chronological || opts.narrative ? "oldest" : "newest");
13766
+ params.set("output", output.json ? "full" : "compact");
13767
+ const includeOutputs = output.json || Boolean(opts.includeOutput || opts.includeOutputs || opts.narrative);
13768
+ params.set("includeOutputs", includeOutputs ? "true" : "false");
13769
+ params.set("maxChars", String(parseLimit2(opts.maxOutputChars ?? opts.maxChars, 1200)));
13770
+ return params.toString();
13771
+ }
13772
+ function formatRunListRow(row) {
13773
+ return {
13774
+ id: row.run.id,
13775
+ status: row.run.status,
13776
+ agent: row.agentName ?? row.run.agentId,
13777
+ runtime: row.bundle.agentRuntimeType,
13778
+ issue: row.issue?.identifier ?? row.issue?.id ?? "-",
13779
+ updatedAt: row.run.updatedAt
13780
+ };
13781
+ }
13782
+ function formatRunEvent(row) {
13783
+ return {
13784
+ seq: row.seq,
13785
+ level: row.level,
13786
+ stream: row.stream,
13787
+ message: row.message,
13788
+ createdAt: row.createdAt
13789
+ };
13790
+ }
13791
+ function formatRunTranscriptRow(row) {
13792
+ return {
13793
+ id: row.id,
13794
+ turn: row.turnIndex ?? "-",
13795
+ kind: row.kind,
13796
+ ts: row.ts,
13797
+ error: row.isError ? "yes" : "no",
13798
+ preview: row.preview || row.detailPreview,
13799
+ ...row.output ? { output: row.output.text } : {}
13800
+ };
13801
+ }
13802
+ function formatRunTranscriptNarrative(row) {
13803
+ const marker = row.isError ? "ERROR " : "";
13804
+ return `${row.id} ${row.ts} ${marker}${row.kind}: ${row.preview || row.detailPreview}${row.output ? `
13805
+ ${row.output.text}` : ""}`;
13806
+ }
13807
+ function formatRunError(row) {
13808
+ return {
13809
+ id: row.id,
13810
+ type: row.type,
13811
+ turn: row.turnIndex ?? "-",
13812
+ summary: row.summary,
13813
+ output: row.output.text,
13814
+ context: row.transcriptContext?.command ?? "-"
13815
+ };
13816
+ }
13817
+ function parseLimit2(value, fallback) {
13818
+ const parsed = Number(value ?? fallback);
13819
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
13820
+ return Math.floor(parsed);
13821
+ }
13822
+ function clip2(value, maxChars) {
13823
+ if (value.length <= maxChars) return value;
13824
+ return `${value.slice(0, Math.max(0, maxChars - 1))}\u2026`;
12793
13825
  }
12794
13826
 
12795
13827
  // src/config/data-dir.ts
@@ -13172,6 +14204,9 @@ function createProgram() {
13172
14204
  registerDashboardCommands(program);
13173
14205
  registerSkillCommands(program);
13174
14206
  registerLibraryCommands(program);
14207
+ registerAutomationCommands(program);
14208
+ registerChatCommands(program);
14209
+ registerRunsCommands(program);
13175
14210
  registerLazyWorktreeCommands(program);
13176
14211
  registerPluginCommands(program);
13177
14212
  registerLazyBenchmarkCommands(program);