@tryarcanist/cli 0.1.199 → 0.1.201

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/index.js +103 -23
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -419,12 +419,15 @@ Creates a scheduled automation for a GitHub repo. Create/delete require a write-
419
419
  ```bash
420
420
  arcanist automations create your-org/your-repo "summarize new regressions" --cron "*/15 * * * *"
421
421
  arcanist automations create your-org/your-repo "audit N+1s" --cron "0 13 * * 5" --model gpt-5.5
422
+ arcanist automations create your-org/your-repo "/audit-prod-docs https://docs.tryarcanist.com/" --cron "0 8 * * *" --slack-team-id T0123ABC --slack-channel-id C0456DEF
422
423
  printf "summarize new regressions" | arcanist automations create your-org/your-repo --prompt-stdin --cron "*/15 * * * *" --json
423
424
  arcanist automations create https://github.com/your-org/your-repo - --cron "0 14 * * 1-5" --name "Weekday summary"
424
425
  ```
425
426
 
426
427
  Use `--model <model>` to pin the model for sessions started by the automation. Backend is derived from the model; non-codex backends such as Claude or opencode are limited to Arcanist team businesses. When `--model` is omitted the codex backend default is used.
427
428
 
429
+ Use `--slack-team-id <team-id>` together with `--slack-channel-id <channel-id>` to deliver each completed automation digest into a Slack channel. Both flags are required together; the target workspace must already be connected to Arcanist and the bot must already be in the channel.
430
+
428
431
  JSON mode prints the created rule. Human-readable mode prints the rule ID, repo, normalized cron, and next fire time.
429
432
 
430
433
  ### `arcanist automations list`
package/dist/index.js CHANGED
@@ -17,31 +17,46 @@ function normalizeBaseUrl(url) {
17
17
  return url.replace(/\/+$/, "");
18
18
  }
19
19
 
20
+ // ../../shared/utils/api-error-envelope.ts
21
+ function parseApiErrorEnvelope(body) {
22
+ if (!body || typeof body !== "object") return null;
23
+ const record = body;
24
+ const topLevelCode = typeof record.code === "string" && record.code ? record.code : null;
25
+ const topLevelMessage = typeof record.message === "string" && record.message ? record.message : null;
26
+ if (typeof record.error === "string" && record.error) {
27
+ return {
28
+ code: topLevelCode ?? record.error,
29
+ message: topLevelMessage ?? record.error,
30
+ raw: record.error
31
+ };
32
+ }
33
+ if (record.error && typeof record.error === "object") {
34
+ const nested = record.error;
35
+ const code = typeof nested.code === "string" && nested.code ? nested.code : topLevelCode;
36
+ const message = typeof nested.message === "string" && nested.message ? nested.message : topLevelMessage;
37
+ return code || message ? { code, message, raw: record.error } : null;
38
+ }
39
+ return topLevelCode || topLevelMessage ? { code: topLevelCode, message: topLevelMessage, raw: body } : null;
40
+ }
41
+
20
42
  // src/utils/api-error.ts
21
43
  function parseApiErrorBody(body) {
22
44
  try {
23
45
  const parsed = JSON.parse(body);
24
46
  if (!parsed || typeof parsed !== "object") return null;
25
- const record = parsed;
26
- const topLevelMessage = typeof record.message === "string" && record.message ? record.message : void 0;
27
- if (typeof record.error === "string" && record.error) {
47
+ const envelope = parseApiErrorEnvelope(parsed);
48
+ if (!envelope) return null;
49
+ if (typeof envelope.raw === "string" && envelope.raw) {
28
50
  return {
29
- serverCode: typeof record.code === "string" && record.code ? record.code : record.error,
30
- rawError: record.error,
31
- message: topLevelMessage ?? record.error
51
+ ...envelope.code ? { serverCode: envelope.code } : {},
52
+ rawError: envelope.raw,
53
+ ...envelope.message ? { message: envelope.message } : {}
32
54
  };
33
55
  }
34
- if (record.error && typeof record.error === "object") {
35
- const error = record.error;
36
- const message = typeof error.message === "string" && error.message ? error.message : topLevelMessage;
37
- const serverCode = typeof error.code === "string" && error.code ? error.code : void 0;
38
- return message || serverCode ? {
39
- ...message ? { message } : {},
40
- ...serverCode ? { serverCode } : {},
41
- ...serverCode ? { rawError: serverCode } : {}
42
- } : null;
43
- }
44
- return topLevelMessage ? { message: topLevelMessage } : null;
56
+ return envelope.message || envelope.code ? {
57
+ ...envelope.message ? { message: envelope.message } : {},
58
+ ...envelope.code ? { serverCode: envelope.code, rawError: envelope.code } : {}
59
+ } : null;
45
60
  } catch {
46
61
  return null;
47
62
  }
@@ -705,9 +720,20 @@ var MODEL_REGISTRY = [
705
720
  capabilities: { codexToolSearch: true },
706
721
  contextWindow: 105e4,
707
722
  // Verified 2026-07-09 against OpenAI's GPT-5.6 migration guide and model
708
- // catalog: the alias routes to gpt-5.6-sol and supports max effort.
723
+ // catalog: the alias routes to gpt-5.6-sol and supports max effort. Pricing
724
+ // mirrors gpt-5.6-sol since the alias resolves to it.
709
725
  reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "medium" },
710
- costTracked: false,
726
+ pricing: {
727
+ inputPerMillion: 5,
728
+ outputPerMillion: 30,
729
+ cacheReadPerMillion: 0.5,
730
+ longContext: {
731
+ thresholdTokens: 272e3,
732
+ inputPerMillion: 10,
733
+ outputPerMillion: 45,
734
+ cacheReadPerMillion: 1
735
+ }
736
+ },
711
737
  sessionStart: { eligible: true }
712
738
  },
713
739
  {
@@ -718,7 +744,17 @@ var MODEL_REGISTRY = [
718
744
  capabilities: { codexToolSearch: true },
719
745
  contextWindow: 105e4,
720
746
  reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "medium" },
721
- costTracked: false,
747
+ pricing: {
748
+ inputPerMillion: 5,
749
+ outputPerMillion: 30,
750
+ cacheReadPerMillion: 0.5,
751
+ longContext: {
752
+ thresholdTokens: 272e3,
753
+ inputPerMillion: 10,
754
+ outputPerMillion: 45,
755
+ cacheReadPerMillion: 1
756
+ }
757
+ },
722
758
  sessionStart: { eligible: true }
723
759
  },
724
760
  {
@@ -729,7 +765,17 @@ var MODEL_REGISTRY = [
729
765
  capabilities: { codexToolSearch: true },
730
766
  contextWindow: 105e4,
731
767
  reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "medium" },
732
- costTracked: false,
768
+ pricing: {
769
+ inputPerMillion: 2.5,
770
+ outputPerMillion: 15,
771
+ cacheReadPerMillion: 0.25,
772
+ longContext: {
773
+ thresholdTokens: 272e3,
774
+ inputPerMillion: 5,
775
+ outputPerMillion: 22.5,
776
+ cacheReadPerMillion: 0.5
777
+ }
778
+ },
733
779
  sessionStart: { eligible: true }
734
780
  },
735
781
  {
@@ -740,7 +786,17 @@ var MODEL_REGISTRY = [
740
786
  capabilities: { codexToolSearch: true },
741
787
  contextWindow: 105e4,
742
788
  reasoning: { efforts: ["none", "low", "medium", "high", "xhigh", "max"], default: "medium" },
743
- costTracked: false,
789
+ pricing: {
790
+ inputPerMillion: 1,
791
+ outputPerMillion: 6,
792
+ cacheReadPerMillion: 0.1,
793
+ longContext: {
794
+ thresholdTokens: 272e3,
795
+ inputPerMillion: 2,
796
+ outputPerMillion: 9,
797
+ cacheReadPerMillion: 0.2
798
+ }
799
+ },
744
800
  sessionStart: { eligible: true }
745
801
  },
746
802
  {
@@ -1115,6 +1171,10 @@ var AUTOMATION_ERROR_HINTS = {
1115
1171
  repo_not_available: "Check that the token owner has GitHub access and Arcanist is installed for that repo.",
1116
1172
  duplicate_rule: "An enabled automation already exists for this repo, cron, and prompt.",
1117
1173
  invalid_skill: "Use a leading slash skill that exists in the selected repo, for example: /audit-prod-docs",
1174
+ invalid_slack_target: "Pass both --slack-team-id and --slack-channel-id to enable Slack delivery.",
1175
+ slack_workspace_not_connected: "Connect that Slack workspace to Arcanist before creating the automation.",
1176
+ slack_channel_unavailable: "Check the Slack channel ID and confirm the bot can see the channel.",
1177
+ slack_bot_not_in_channel: "Invite the Arcanist bot to the channel, then retry.",
1118
1178
  rule_cap_reached: "Delete or disable an existing automation before creating another one. The cap is 20 enabled rules.",
1119
1179
  installation_unresolved: "Reconnect or reinstall the GitHub integration for that repository, then retry.",
1120
1180
  model_not_available: "That model's backend (Claude/opencode) is limited to Arcanist team businesses; use the codex default or a codex model.",
@@ -1125,6 +1185,15 @@ var MAX_ALL_PAGES = 1e3;
1125
1185
  async function createAutomationCommand(repoUrl, promptArg, options, command) {
1126
1186
  const repo = parseAutomationRepo(repoUrl);
1127
1187
  const prompt = await resolvePromptInput(promptArg, options);
1188
+ const slackTeamId = options.slackTeamId?.trim();
1189
+ const slackChannelId = options.slackChannelId?.trim();
1190
+ const slackDeliveryRequested = options.slackTeamId !== void 0 || options.slackChannelId !== void 0;
1191
+ if (slackDeliveryRequested && (!slackTeamId || !slackChannelId)) {
1192
+ throw new CliError(
1193
+ "user",
1194
+ "Slack delivery requires both --slack-team-id and --slack-channel-id with non-empty values."
1195
+ );
1196
+ }
1128
1197
  const runtime = getRuntimeOptions(command, options);
1129
1198
  const config = requireConfig(runtime);
1130
1199
  let normalizedModel;
@@ -1144,6 +1213,10 @@ async function createAutomationCommand(repoUrl, promptArg, options, command) {
1144
1213
  };
1145
1214
  if (options.name !== void 0) body.name = options.name;
1146
1215
  if (options.model !== void 0) body.modelId = normalizedModel;
1216
+ if (slackDeliveryRequested) {
1217
+ body.slackTeamId = slackTeamId;
1218
+ body.slackChannelId = slackChannelId;
1219
+ }
1147
1220
  const payload = await automationApiFetch(config, "/api/automation/schedules", {
1148
1221
  method: "POST",
1149
1222
  body: JSON.stringify(body)
@@ -1258,6 +1331,9 @@ function printAutomationRule(rule) {
1258
1331
  console.log(`Repo: ${rule.repoOwner}/${rule.repoName}`);
1259
1332
  console.log(`Cron: ${rule.normalizedCron}`);
1260
1333
  if (rule.modelId) console.log(`Model: ${rule.modelId}`);
1334
+ if (rule.slackTeamId && rule.slackChannelId) {
1335
+ console.log(`Slack delivery: ${rule.slackTeamId}/${rule.slackChannelId}`);
1336
+ }
1261
1337
  console.log(`Next fire: ${formatTime(rule.nextFireAt)}`);
1262
1338
  }
1263
1339
  function formatTime(value) {
@@ -5683,12 +5759,16 @@ var automations = program.command("automations").description("Automation command
5683
5759
  automations.command("create").description("Create a scheduled automation").argument("<repo-url>", "Repository URL").argument("[prompt]", "Prompt to run, or '-' to read stdin").requiredOption("--cron <expr>", "Cron expression").option("--name <name>", "Automation display name").option(
5684
5760
  "--model <model>",
5685
5761
  "Model to pin for this automation's sessions (e.g. gpt-5.5, claude-opus-4-8). Defaults to the codex backend default."
5686
- ).option("--prompt-stdin", "Read prompt from stdin").addHelpText(
5762
+ ).option(
5763
+ "--slack-team-id <team-id>",
5764
+ "Slack workspace/team ID for final digest delivery (requires --slack-channel-id)"
5765
+ ).option("--slack-channel-id <channel-id>", "Slack channel ID for final digest delivery (requires --slack-team-id)").option("--prompt-stdin", "Read prompt from stdin").addHelpText(
5687
5766
  "after",
5688
5767
  `
5689
5768
  Examples:
5690
5769
  arcanist automations create tryarcanist/arcanist "summarize regressions" --cron "*/15 * * * *"
5691
5770
  arcanist automations create tryarcanist/arcanist "audit N+1s" --cron "0 13 * * 5" --model gpt-5.5
5771
+ arcanist automations create tryarcanist/arcanist "/audit-prod-docs https://docs.tryarcanist.com/" --cron "0 8 * * *" --slack-team-id T0123ABC --slack-channel-id C0456DEF
5692
5772
  printf "summarize regressions" | arcanist automations create tryarcanist/arcanist --prompt-stdin --cron "*/15 * * * *" --json
5693
5773
  `
5694
5774
  ).action((repoUrl, prompt, options, command) => createAutomationCommand(repoUrl, prompt, options, command));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.199",
3
+ "version": "0.1.201",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {