@kairyou/agent-tools 0.7.1 → 0.7.4

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/README.md CHANGED
@@ -75,8 +75,8 @@ npx -y @kairyou/agent-tools@latest <capability> -a <agent...>
75
75
 
76
76
  `--dry-run` previews, `--uninstall` unwires the integration from the agent, and
77
77
  re-running the install command updates. The installer only touches config
78
- entries it wrote itself; shared files under `~/.agent-tools` are kept on
79
- uninstall.
78
+ entries it wrote itself, and `config.jsonc` updates only add missing default
79
+ keys without touching your edits or comments.
80
80
 
81
81
  | Capability | Claude Code | Codex | OpenCode |
82
82
  | --- | --- | --- | --- |
@@ -90,19 +90,19 @@ uninstall.
90
90
  npx -y @kairyou/agent-tools@latest statusline -a claude
91
91
  ```
92
92
 
93
- The installer writes `statusLine` to `~/.claude/settings.json`. The default
94
- output is:
93
+ The installer writes `statusLine` to `~/.claude/settings.json`. Example output:
95
94
 
96
95
  ```text
96
+ # Pick and order the fields via statusline.fields in ~/.agent-tools/config.jsonc:
97
97
  ⎇ main | Opus 4.8 | 5h 7% ⟳2h54m | w 41% ⟳3d1h
98
- ```
99
98
 
100
- Here `5h` and `w` are Claude's rolling usage windows; `⟳` is the reset countdown.
101
- When a compatible API relay is active, the statusline also appends provider usage.
99
+ # With a compatible API relay, its quota info is shown instead, e.g.:
100
+ main | Opus 4.8 | API | balance $362 | today $61.7 | 30d $566
101
+ ```
102
102
 
103
- To choose what appears, edit `statusline.fields` in
104
- `~/.agent-tools/config.jsonc`. Installer updates only add missing default keys
105
- and never touch your edits or comments.
103
+ Here `5h` and `w` are Claude's rolling usage windows and `⟳` is the reset
104
+ countdown; see [Provider usage](#provider-usage) below for relay quota
105
+ compatibility and configuration.
106
106
 
107
107
  ### Provider usage
108
108
 
@@ -149,6 +149,19 @@ Keep `preset` set to `auto` for automatic detection. Select a specific protocol
149
149
  only when you know which usage endpoint the gateway exposes; a configured
150
150
  custom route id is also accepted.
151
151
 
152
+ Output examples:
153
+
154
+ ```text
155
+ # Relay plan quota.
156
+ API | D $0.0/$100 | W $0.0/$300 | Exp 07-08
157
+
158
+ # Wallet balance.
159
+ API | balance $362 | today $61.7 | 30d $566
160
+ ```
161
+
162
+ Fields: `D/W/M` are daily/weekly/monthly spend against plan limits; `Exp` is
163
+ the plan expiry; `balance` is wallet credit; `today` and `30d` are API spend.
164
+
152
165
  #### Custom gateway routes
153
166
 
154
167
  For gateways the built-in probes cannot reach (e.g. cookie-authenticated
@@ -198,19 +211,6 @@ export async function run(context, { requestJson, agentConfig }) {
198
211
  through to the next route (with `providerUsage.debug` enabled, failures are
199
212
  logged to `~/.agent-tools/logs/usage-debug.log`).
200
213
 
201
- Output examples:
202
-
203
- ```text
204
- # Relay plan quota.
205
- API | D $0.0/$100 | W $0.0/$300 | Exp 07-08
206
-
207
- # Wallet balance.
208
- API | balance $362 | today $61.7 | 30d $566
209
- ```
210
-
211
- Fields: `D/W/M` are daily/weekly/monthly spend against plan limits; `Exp` is
212
- the plan expiry; `balance` is wallet credit; `today` and `30d` are API spend.
213
-
214
214
  ### Vision (cross-model image understanding)
215
215
 
216
216
  Lets a main model that cannot see images ask a multimodal model specific questions about an image (local path or http(s) URL) and reason on from the answers. Typical uses: reading error screenshots, implementing UI from design mockups, locating the glitch in a bug-report screenshot. One installer capability bundling three parts: the `inspect_image` MCP stdio server, the `at-vision` policy skill, and a human diagnostic CLI.
package/README.zh-CN.md CHANGED
@@ -72,7 +72,8 @@ npx -y @kairyou/agent-tools@latest <capability> -a <agent...>
72
72
  ```
73
73
 
74
74
  `--dry-run` 预览, `--uninstall` 解除对应集成, 重新执行安装命令即为更新.
75
- 安装器只改动自己写入的配置项; 卸载会保留 `~/.agent-tools` 下的共享文件.
75
+ 安装器只改动自己写入的配置项, 更新 `config.jsonc` 时只补充缺失的默认键,
76
+ 不会动你的修改和注释.
76
77
 
77
78
  | Capability | Claude Code | Codex | OpenCode |
78
79
  | --- | --- | --- | --- |
@@ -86,17 +87,18 @@ npx -y @kairyou/agent-tools@latest <capability> -a <agent...>
86
87
  npx -y @kairyou/agent-tools@latest statusline -a claude
87
88
  ```
88
89
 
89
- 安装器会把 `statusLine` 写入 `~/.claude/settings.json`. 默认显示:
90
+ 安装器会把 `statusLine` 写入 `~/.claude/settings.json`. 输出示例:
90
91
 
91
92
  ```text
93
+ # 显示项与顺序由 ~/.agent-tools/config.jsonc 的 statusline.fields 控制:
92
94
  ⎇ main | Opus 4.8 | 5h 7% ⟳2h54m | w 41% ⟳3d1h
93
- ```
94
95
 
95
- 其中 `5h` / `w` Claude 的滚动用量窗口, `⟳` 后面是重置倒计时.
96
- 使用兼容的 API 中转时, statusline 还会追加 provider usage.
96
+ # 使用兼容的 API 中转时, 显示中转的额度信息, 比如:
97
+ main | Opus 4.8 | API | balance $362 | today $61.7 | 30d $566
98
+ ```
97
99
 
98
- 如需控制显示项, 修改 `~/.agent-tools/config.jsonc` 里的
99
- `statusline.fields`. 安装器更新时只会补充缺失的默认键, 不会动你的修改和注释.
100
+ 其中 `5h` / `w` 是 Claude 的滚动用量窗口, `⟳` 后面是重置倒计时;
101
+ 中转额度信息的兼容性与配置见下方 [Provider usage](#provider-usage).
100
102
 
101
103
  ### Provider usage
102
104
 
@@ -138,6 +140,19 @@ provider 的 `base_url` 和密钥; Claude Code: 读取 `ANTHROPIC_BASE_URL` 与
138
140
  保持 `preset: "auto"` 即可自动探测. 只有明确知道网关开放的是哪种用量协议时,
139
141
  才指定相应的内置 preset 或已配置的自定义 route id.
140
142
 
143
+ 显示效果示例:
144
+
145
+ ```text
146
+ # 中转套餐额度.
147
+ API | D $0.0/$100 | W $0.0/$300 | Exp 07-08
148
+
149
+ # 钱包余额.
150
+ API | balance $362 | today $61.7 | 30d $566
151
+ ```
152
+
153
+ 字段含义: `D/W/M` 是日/周/月套餐消耗与上限, `Exp` 是套餐到期日,
154
+ `balance` 是钱包余额, `today` / `30d` 是今日与近 30 天 API 消耗.
155
+
141
156
  #### 自定义网关路由
142
157
 
143
158
  内置探测覆盖不到的网关(比如 cookie 认证的中转), 可以自己写路由模块并在
@@ -184,19 +199,6 @@ export async function run(context, { requestJson, agentConfig }) {
184
199
  `text` 是自由字符串; 成功返回 `{ text }`, 抛错则回落到下一条路由
185
200
  (开启 `providerUsage.debug` 后, 失败会记录到 `~/.agent-tools/logs/usage-debug.log`).
186
201
 
187
- 显示效果示例:
188
-
189
- ```text
190
- # 中转套餐额度.
191
- API | D $0.0/$100 | W $0.0/$300 | Exp 07-08
192
-
193
- # 钱包余额.
194
- API | balance $362 | today $61.7 | 30d $566
195
- ```
196
-
197
- 字段含义: `D/W/M` 是日/周/月套餐消耗与上限, `Exp` 是套餐到期日,
198
- `balance` 是钱包余额, `today` / `30d` 是今日与近 30 天 API 消耗.
199
-
200
202
  ### Vision(跨模型识图)
201
203
 
202
204
  让不支持图片的主模型借助多模态模型识图: 针对图片(本地路径或 http(s) URL)提出具体问题, 拿到答案后继续自己的推理. 常见场景: 读取报错截图, 按设计稿还原 UI, 定位测试反馈截图里的界面问题. 一个安装器 capability 包含三部分: `inspect_image` MCP stdio server, `at-vision` 策略 skill, 以及人工诊断 CLI.
File without changes
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@kairyou/agent-tools",
3
- "version": "0.7.1",
3
+ "version": "0.7.4",
4
4
  "description": "Reusable Agent Skills and installable integrations (statusline, provider usage, vision) for Codex, Claude Code, and opencode.",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/kairyou/agent-tools.git"
9
+ },
6
10
  "engines": {
7
11
  "node": ">=22"
8
12
  },
@@ -45,6 +45,7 @@
45
45
  // -h, --help Show this help.
46
46
 
47
47
  import { spawnSync } from "node:child_process";
48
+ import { createHash } from "node:crypto";
48
49
  import fs from "node:fs";
49
50
  import os from "node:os";
50
51
  import path from "node:path";
@@ -93,6 +94,7 @@ const USAGE_SKILL_NAME = "at-usage";
93
94
  const USAGE_SKILL_SRC = path.join(REPO_ROOT, "integrations", "usage", "skills", USAGE_SKILL_NAME);
94
95
  const VISION_BUNDLED_MCP_SERVER = path.join(REPO_ROOT, "dist", "vision", "mcp-server.mjs");
95
96
  const VISION_BUNDLED_CLI = path.join(REPO_ROOT, "dist", "vision", "cli.mjs");
97
+ const INSTALL_STATE_PATH = path.join(INSTALL_ROOT, "install-state.json");
96
98
 
97
99
  function fwd(p) {
98
100
  return p.replace(/\\/g, "/");
@@ -242,7 +244,9 @@ function installRuntimeAssets(opts) {
242
244
  }
243
245
  console.log(`runtime: ${INSTALL_ROOT}`);
244
246
  for (const [src, dest, options] of files) copyRuntimeFile(src, dest, opts.dryRun, options);
245
- if (wants(opts, "usage")) syncUsageRoutesDir(opts.dryRun);
247
+ // The statusline refreshes via the usage engine, so it needs packaged
248
+ // routes too.
249
+ if (wants(opts, "usage") || wants(opts, "statusline")) syncUsageRoutesDir(opts.dryRun);
246
250
  }
247
251
 
248
252
  // Repo-shipped usage routes are replaced wholesale so routes removed from the
@@ -443,13 +447,12 @@ const VISION_RATE_LIMIT_STATE = path.join(INSTALL_ROOT, "cache", "vision-rate-li
443
447
  const VISION_DIST_DIR = path.join(REPO_ROOT, "dist", "vision");
444
448
  const VISION_RUNTIME_SERVER = path.join(VISION_RUNTIME_DIR, "mcp-server.mjs");
445
449
  const VISION_RUNTIME_CLI = path.join(VISION_RUNTIME_DIR, "cli.mjs");
446
- const SKILL_MARKER = ".agent-tools-managed.json";
447
- const VISION_SKILL_MARKER_DATA = Object.freeze({
450
+ const VISION_SKILL_IDENTITY = Object.freeze({
448
451
  owner: "@kairyou/agent-tools",
449
452
  capability: "vision",
450
453
  artifact: "skill",
451
454
  });
452
- const USAGE_SKILL_MARKER_DATA = Object.freeze({
455
+ const USAGE_SKILL_IDENTITY = Object.freeze({
453
456
  owner: "@kairyou/agent-tools",
454
457
  capability: "usage",
455
458
  artifact: "skill",
@@ -556,26 +559,110 @@ function installVisionRuntime(opts) {
556
559
  }
557
560
  }
558
561
 
559
- function isManagedSkillDir(dest, markerData) {
560
- const marker = path.join(dest, SKILL_MARKER);
561
- if (!fs.existsSync(marker)) return false;
562
+ function skillManifestKey(dest) {
563
+ const resolved = fwd(path.resolve(dest));
564
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
565
+ }
566
+
567
+ function skillHash(skillFile) {
568
+ if (!fs.existsSync(skillFile)) return "";
569
+ return createHash("sha256").update(fs.readFileSync(skillFile)).digest("hex");
570
+ }
571
+
572
+ function matchesIdentity(value, identity) {
573
+ return Object.entries(identity).every(([key, expected]) => value?.[key] === expected);
574
+ }
575
+
576
+ function readInstallState() {
577
+ const value = readJson(INSTALL_STATE_PATH);
578
+ if (Object.keys(value).length === 0) return { version: 1, artifacts: {} };
579
+ if (
580
+ value.version !== 1 ||
581
+ !value.artifacts ||
582
+ typeof value.artifacts !== "object" ||
583
+ Array.isArray(value.artifacts)
584
+ ) {
585
+ throw new Error(`Cannot parse ${INSTALL_STATE_PATH}: unsupported install state schema`);
586
+ }
587
+ return value;
588
+ }
589
+
590
+ function writeInstallState(state) {
591
+ if (Object.keys(state.artifacts).length === 0) {
592
+ fs.rmSync(INSTALL_STATE_PATH, { force: true });
593
+ return;
594
+ }
595
+ const text = JSON.stringify(state, null, 2) + "\n";
596
+ const suffix = `${process.pid}-${Date.now()}`;
597
+ const stage = `${INSTALL_STATE_PATH}.stage-${suffix}`;
598
+ const backup = `${INSTALL_STATE_PATH}.backup-${suffix}`;
599
+ let movedCurrent = false;
600
+ fs.mkdirSync(path.dirname(INSTALL_STATE_PATH), { recursive: true });
562
601
  try {
563
- const value = JSON.parse(fs.readFileSync(marker, "utf8"));
564
- return Object.entries(markerData).every(([key, expected]) => value?.[key] === expected);
565
- } catch {
566
- return false;
602
+ fs.writeFileSync(stage, text);
603
+ if (fs.existsSync(INSTALL_STATE_PATH)) {
604
+ fs.renameSync(INSTALL_STATE_PATH, backup);
605
+ movedCurrent = true;
606
+ }
607
+ fs.renameSync(stage, INSTALL_STATE_PATH);
608
+ fs.rmSync(backup, { force: true });
609
+ } catch (error) {
610
+ fs.rmSync(stage, { force: true });
611
+ if (movedCurrent && !fs.existsSync(INSTALL_STATE_PATH) && fs.existsSync(backup)) {
612
+ fs.renameSync(backup, INSTALL_STATE_PATH);
613
+ }
614
+ throw error;
615
+ } finally {
616
+ fs.rmSync(stage, { force: true });
617
+ if (fs.existsSync(INSTALL_STATE_PATH)) fs.rmSync(backup, { force: true });
567
618
  }
619
+ console.log(` wrote ${INSTALL_STATE_PATH}`);
620
+ }
621
+
622
+ function managedSkillStatus(dest, identity) {
623
+ const entry = readInstallState().artifacts[skillManifestKey(dest)];
624
+ if (entry) {
625
+ if (!matchesIdentity(entry, identity)) return "other";
626
+ const currentHash = skillHash(path.join(dest, "SKILL.md"));
627
+ return currentHash && currentHash === entry.sha256 ? "managed" : "modified";
628
+ }
629
+ return "unmanaged";
630
+ }
631
+
632
+ function recordManagedSkill(dest, identity) {
633
+ const state = readInstallState();
634
+ state.artifacts[skillManifestKey(dest)] = {
635
+ path: fwd(path.resolve(dest)),
636
+ ...identity,
637
+ sha256: skillHash(path.join(dest, "SKILL.md")),
638
+ };
639
+ writeInstallState(state);
640
+ }
641
+
642
+ function forgetManagedSkill(dest) {
643
+ const state = readInstallState();
644
+ if (!state.artifacts[skillManifestKey(dest)]) return;
645
+ delete state.artifacts[skillManifestKey(dest)];
646
+ writeInstallState(state);
568
647
  }
569
648
 
570
649
  function installManagedSkillDir(
571
650
  skillsRoot,
572
- { name, source, markerData, replacements, remove, dryRun }
651
+ { name, source, identity, replacements, remove, dryRun }
573
652
  ) {
574
653
  const dest = path.join(skillsRoot, name);
575
- const isManaged = () => isManagedSkillDir(dest, markerData);
654
+ const status = fs.existsSync(dest) ? managedSkillStatus(dest, identity) : "missing";
576
655
  if (remove) {
577
- if (!fs.existsSync(dest)) return;
578
- if (!isManaged()) {
656
+ if (!fs.existsSync(dest)) {
657
+ if (!dryRun) forgetManagedSkill(dest);
658
+ return;
659
+ }
660
+ if (status === "modified") {
661
+ if (!dryRun) forgetManagedSkill(dest);
662
+ console.log(` kept modified skill and released ownership ${dest}`);
663
+ return;
664
+ }
665
+ if (status !== "managed") {
579
666
  console.log(` kept unmanaged ${dest}`);
580
667
  return;
581
668
  }
@@ -584,10 +671,18 @@ function installManagedSkillDir(
584
671
  return;
585
672
  }
586
673
  fs.rmSync(dest, { recursive: true, force: true });
674
+ forgetManagedSkill(dest);
587
675
  console.log(` removed ${dest}`);
588
676
  return;
589
677
  }
590
- if (fs.existsSync(dest) && !isManaged()) {
678
+ if (status === "modified") {
679
+ console.error(
680
+ ` Refusing to overwrite modified managed skill directory ${dest}. ` +
681
+ `Move or restore it, then re-run the install.`
682
+ );
683
+ process.exit(1);
684
+ }
685
+ if (fs.existsSync(dest) && status !== "managed") {
591
686
  console.error(
592
687
  ` Refusing to overwrite existing unowned skill directory ${dest}. ` +
593
688
  `Move or remove it, then re-run the install.`
@@ -610,10 +705,7 @@ function installManagedSkillDir(
610
705
  skill = skill.replaceAll(token, value);
611
706
  }
612
707
  fs.writeFileSync(skillFile, skill);
613
- fs.writeFileSync(
614
- path.join(dest, SKILL_MARKER),
615
- JSON.stringify(markerData, null, 2) + "\n"
616
- );
708
+ recordManagedSkill(dest, identity);
617
709
  console.log(` wrote ${dest}`);
618
710
  }
619
711
 
@@ -621,7 +713,7 @@ function installVisionSkillDir(skillsRoot, { remove, dryRun }) {
621
713
  installManagedSkillDir(skillsRoot, {
622
714
  name: VISION_SKILL_NAME,
623
715
  source: VISION_SKILL_SRC,
624
- markerData: VISION_SKILL_MARKER_DATA,
716
+ identity: VISION_SKILL_IDENTITY,
625
717
  replacements: { "{{VISION_CLI_PATH}}": fwd(VISION_RUNTIME_CLI) },
626
718
  remove,
627
719
  dryRun,
@@ -632,7 +724,7 @@ function installUsageSkillDir(skillsRoot, agent, { remove, dryRun }) {
632
724
  installManagedSkillDir(skillsRoot, {
633
725
  name: USAGE_SKILL_NAME,
634
726
  source: USAGE_SKILL_SRC,
635
- markerData: USAGE_SKILL_MARKER_DATA,
727
+ identity: USAGE_SKILL_IDENTITY,
636
728
  replacements: {
637
729
  "{{USAGE_CLI_PATH}}": fwd(RUNTIME.usageCli),
638
730
  "{{USAGE_AGENT}}": agent,
@@ -3,52 +3,20 @@
3
3
  //
4
4
  // Usage:
5
5
  // npm run release [-- minor|major] # default: patch
6
- // npm run release -- --dry-run # npm test + preview tarball, no bump/publish
6
+ // npm run release -- --dry-run # npm test + preview tarball, no bump/push
7
7
  //
8
- // Steps: clean-tree check -> npm auth check -> npm test -> npm version -> npm publish -> git push
8
+ // Local steps: clean-tree check -> npm test -> npm version -> git push --follow-tags.
9
+ // Publishing runs on GitHub Actions via npm trusted publishing (OIDC, no
10
+ // tokens, no npm login): .github/workflows/release.yml publishes when the
11
+ // version tag lands.
9
12
  //
10
13
  // Release when scripts/ integrations/ config.default.jsonc or
11
14
  // package.json deps change — npx users only get these from the published package.
12
15
  // (skills/ ships from GitHub via `npx skills add`; no release needed.)
13
16
 
14
17
  import { spawnSync } from "node:child_process";
15
- import { readFileSync } from "node:fs";
16
18
 
17
19
  const BUMPS = ["patch", "minor", "major"];
18
- const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
19
- const PACKAGE_NAME = packageJson.name;
20
-
21
- if (typeof PACKAGE_NAME !== "string" || PACKAGE_NAME.trim() === "") {
22
- console.error("package.json must define a non-empty name before releasing.");
23
- process.exit(2);
24
- }
25
-
26
- function readNpmConfig(key) {
27
- const npmExecPath = process.env.npm_execpath;
28
- const result = npmExecPath
29
- ? spawnSync(process.execPath, [npmExecPath, "config", "get", key], { encoding: "utf8" })
30
- : spawnSync("npm", ["config", "get", key], { encoding: "utf8", shell: true });
31
-
32
- if (result.status !== 0) {
33
- const detail = result.stderr?.trim() || result.error?.message || "npm config exited unexpectedly";
34
- console.error(`Unable to read npm config ${key}:\n${detail}`);
35
- process.exit(result.status ?? 2);
36
- }
37
-
38
- const value = result.stdout?.trim();
39
- return value && value !== "undefined" && value !== "null" ? value : undefined;
40
- }
41
-
42
- const packageScope = PACKAGE_NAME.match(/^(@[^/]+)\//)?.[1];
43
- const REGISTRY = (packageScope && readNpmConfig(`${packageScope}:registry`)) || readNpmConfig("registry");
44
-
45
- try {
46
- const registryUrl = new URL(REGISTRY);
47
- if (!["http:", "https:"].includes(registryUrl.protocol)) throw new Error("unsupported protocol");
48
- } catch {
49
- console.error("npm registry config must be a valid HTTP(S) URL before releasing.");
50
- process.exit(2);
51
- }
52
20
 
53
21
  let bump = "patch";
54
22
  let dryRun = false;
@@ -61,66 +29,33 @@ for (const arg of process.argv.slice(2)) {
61
29
  }
62
30
  }
63
31
 
64
- function run(command, opts = {}) {
65
- const { onFailure, ...spawnOptions } = opts;
32
+ function run(command) {
66
33
  console.log(`\n> ${command}`);
67
- const result = spawnSync(command, { stdio: "inherit", shell: true, ...spawnOptions });
34
+ const result = spawnSync(command, { stdio: "inherit", shell: true });
68
35
  if (result.status !== 0) {
69
36
  console.error(`\nAborted: \`${command}\` exited with ${result.status}.`);
70
- onFailure?.();
71
37
  process.exit(result.status ?? 1);
72
38
  }
73
39
  }
74
40
 
75
- function printCommands(message, commands) {
76
- console.error(`\n${message}\n`);
77
- for (const command of commands) console.error(` ${command}`);
78
- }
79
-
80
41
  const status = spawnSync("git status --porcelain", { shell: true, encoding: "utf8" });
81
42
  if (!dryRun && status.stdout.trim() !== "") {
82
43
  console.error("Working tree is not clean — commit or stash first:\n" + status.stdout);
83
44
  process.exit(1);
84
45
  }
85
46
 
86
- if (!dryRun) {
87
- // Fail before npm version creates a commit and tag when the saved token is stale.
88
- run(`npm whoami --registry=${REGISTRY}`, {
89
- onFailure() {
90
- printCommands("npm authentication is missing or expired. Log in, verify the account, then retry:", [
91
- `npm login --registry=${REGISTRY}`,
92
- `npm whoami --registry=${REGISTRY}`,
93
- `npm run release -- ${bump}`,
94
- ]);
95
- },
96
- });
97
- }
98
-
99
47
  run("npm test");
100
48
 
101
49
  if (dryRun) {
102
50
  // Shows exactly which files would ship; verify nothing is missing/extra.
103
51
  run("npm pack --dry-run");
104
- console.log("\nDry run complete. No version bump, nothing published.");
52
+ console.log("\nDry run complete. No version bump, nothing pushed.");
105
53
  process.exit(0);
106
54
  }
107
55
 
108
56
  run(`npm version ${bump}`);
109
- run("npm publish", {
110
- onFailure() {
111
- printCommands(
112
- "npm publish failed after the version commit and tag were created. Do not run the release command again. Fix authentication/permissions, then resume:",
113
- [
114
- `npm login --registry=${REGISTRY}`,
115
- `npm whoami --registry=${REGISTRY}`,
116
- `npm owner ls ${PACKAGE_NAME}`,
117
- "npm publish",
118
- `npm view ${PACKAGE_NAME} version`,
119
- "git push --follow-tags",
120
- ],
121
- );
122
- },
123
- });
124
57
  run("git push --follow-tags");
125
58
 
126
- console.log("\nRelease complete.");
59
+ console.log(
60
+ "\nTag pushed. GitHub Actions publishes it: https://github.com/kairyou/agent-tools/actions"
61
+ );