@misaka-net/misakanet-setup 0.4.1 → 0.4.2

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
@@ -1,7 +1,7 @@
1
1
  # @misaka-net/misakanet-setup
2
2
 
3
- One command to teach your **Claude Code**, **Codex**, **Hermes** or **OpenClaw** to check
4
- MisakaNet's failure lessons before repeating a mistake — and to distil the session's reusable
3
+ One command to teach your **Claude Code**, **Codex**, **Hermes**, **OpenClaw** or **codewhale** to
4
+ check MisakaNet's failure lessons before repeating a mistake — and to distil the session's reusable
5
5
  lessons at a checkpoint.
6
6
 
7
7
  ```bash
@@ -69,7 +69,7 @@ const valueOf = (flag, dflt) => {
69
69
  const only = valueOf('--only', '').split(',').map((s) => s.trim()).filter(Boolean);
70
70
  const DRY = has('--dry-run');
71
71
  const HOME = resolve(valueOf('--home', homedir()));
72
- const AGENTS = ['claude', 'codex', 'hermes', 'openclaw'];
72
+ const AGENTS = ['claude', 'codex', 'hermes', 'openclaw', 'codewhale'];
73
73
 
74
74
  const done = [];
75
75
  const manual = [];
@@ -103,7 +103,7 @@ function readText(path) {
103
103
  * meant to prevent is now caught in CI instead, by a test that binds this literal to the
104
104
  * manifest — a failing test is a better place for that than a request header.
105
105
  */
106
- const VERSION = '0.4.1';
106
+ const VERSION = '0.4.2';
107
107
 
108
108
  function backup(path) {
109
109
  if (DRY || !readText(path)) return;
@@ -352,6 +352,7 @@ function detect(agent) {
352
352
  codex: ['.codex'],
353
353
  hermes: ['.hermes'],
354
354
  openclaw: ['.openclaw'],
355
+ codewhale: ['.codewhale'],
355
356
  }[agent] || [];
356
357
  return paths.some((p) => existsSync(join(HOME, p)));
357
358
  }
@@ -673,6 +674,75 @@ async function installHermes(hookPath, bearer) {
673
674
  * The manual `openclaw mcp add …` line is still printed for the user to run themselves — as
674
675
  * text, never executed.
675
676
  */
677
+ /**
678
+ * codewhale keeps MCP servers in its own JSON file, and reads workspace rules from a
679
+ * plain `AGENTS.md` — but only in a *trusted* project (`config.toml`'s
680
+ * `[projects."<dir>"] trust_level = "trusted"`). So the block goes into the trusted
681
+ * project directories, which are also where the user actually works, rather than into a
682
+ * guessed user-level path (checked on 0.9.7: no user-level AGENTS.md is read).
683
+ *
684
+ * The token is the one thing codewhale will not take inline: `bearer_token_env_var` names
685
+ * an environment variable, so "install once" for this agent ends with the user exporting
686
+ * `MISAKANET_TOKEN` once. That is stated rather than silently half-installed.
687
+ */
688
+ function codewhaleProjects() {
689
+ const text = readText(join(HOME, '.codewhale', 'config.toml'));
690
+ if (!text) return [];
691
+ const out = [];
692
+ let current = null;
693
+ for (const line of text.split('\n')) {
694
+ const header = line.match(/^\s*\[projects\.(.+?)\]\s*$/);
695
+ if (header) { current = header[1].trim().replace(/^"|"$/g, ''); continue; }
696
+ if (/^\s*\[/.test(line)) { current = null; continue; }
697
+ if (current && /^\s*trust_level\s*=\s*"trusted"/.test(line)) out.push(current);
698
+ }
699
+ return out;
700
+ }
701
+
702
+ async function installCodewhale(bearer) {
703
+ const cfg = join(HOME, '.codewhale', 'mcp.json');
704
+ const data = readJson(cfg, null) || {
705
+ timeouts: { connect_timeout: 10, execute_timeout: 60, read_timeout: 120 },
706
+ servers: {},
707
+ };
708
+ const servers = { ...(data.servers || {}) };
709
+ const entry = {
710
+ command: null,
711
+ args: [],
712
+ env: {},
713
+ url: ENDPOINT,
714
+ connect_timeout: null,
715
+ execute_timeout: null,
716
+ read_timeout: null,
717
+ disabled: false,
718
+ enabled: true,
719
+ required: false,
720
+ enabled_tools: [],
721
+ disabled_tools: [],
722
+ bearer_token_env_var: 'MISAKANET_TOKEN',
723
+ };
724
+ if (JSON.stringify(servers.misakanet || null) !== JSON.stringify(entry)) {
725
+ servers.misakanet = entry;
726
+ backup(cfg);
727
+ writeText(cfg, `${JSON.stringify({ ...data, servers }, null, 2)}\n`);
728
+ ok(`codewhale:注册 MCP(streamable-http)→ ${cfg}`);
729
+ } else {
730
+ ok('codewhale:MCP 已注册(无改动)');
731
+ }
732
+
733
+ const projects = codewhaleProjects();
734
+ for (const project of projects) {
735
+ if (!existsSync(project)) continue;
736
+ ok(`codewhale:规则块 ${injectBlock(join(project, 'AGENTS.md'), PROMPT_BLOCK)} → ${join(project, 'AGENTS.md')}`);
737
+ }
738
+ if (!projects.length) {
739
+ need('codewhale:没有受信任的项目目录(config.toml 里没有 trust_level = "trusted")→ '
740
+ + '先在 codewhale 里打开一次你的项目并信任它,再运行本命令,规则块才会写进去');
741
+ }
742
+ need('codewhale:token 只能通过环境变量给 → 在你的 shell 配置里加 `export MISAKANET_TOKEN=<你的 token>`'
743
+ + '(本机状态:npx @misaka-net/misakanet-setup --verify 会告诉你 token 在哪)');
744
+ }
745
+
676
746
  async function installOpenclaw(bearer) {
677
747
  const workspace = openclawWorkspaces()[0];
678
748
  const rules = join(workspace, 'AGENTS.md');
@@ -850,6 +920,20 @@ function uninstall() {
850
920
  const rules = join(workspace, 'AGENTS.md');
851
921
  if (stripBlock(rules)) ok(`移除规则块 → ${rules}`);
852
922
  }
923
+
924
+ // codewhale: same two surfaces as the install (its own mcp.json + trusted projects).
925
+ for (const project of codewhaleProjects()) {
926
+ const rules = join(project, 'AGENTS.md');
927
+ if (stripBlock(rules)) ok(`移除规则块 → ${rules}`);
928
+ }
929
+ const whaleCfg = join(HOME, '.codewhale', 'mcp.json');
930
+ const whale = readJson(whaleCfg, null);
931
+ if (whale?.servers?.misakanet) {
932
+ delete whale.servers.misakanet;
933
+ backup(whaleCfg);
934
+ writeText(whaleCfg, `${JSON.stringify(whale, null, 2)}\n`);
935
+ ok(`移除 MCP 注册 → ${whaleCfg}`);
936
+ }
853
937
  const cfg = join(HOME, '.claude.json');
854
938
  const data = readJson(cfg, null);
855
939
  if (data && data.mcpServers?.misakanet) {
@@ -974,6 +1058,7 @@ if (!targets.length) {
974
1058
  else if (agent === 'codex') await installCodex(hookPath, bearer);
975
1059
  else if (agent === 'hermes') await installHermes(hookPath, bearer);
976
1060
  else if (agent === 'openclaw') await installOpenclaw(bearer);
1061
+ else if (agent === 'codewhale') await installCodewhale(bearer);
977
1062
  }
978
1063
  }
979
1064
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@misaka-net/misakanet-setup",
3
- "version": "0.4.1",
4
- "description": "One-command setup: teach your Claude Code / Codex / Hermes / OpenClaw to search MisakaNet failure lessons before repeating a mistake, and to distil the session at a checkpoint.",
3
+ "version": "0.4.2",
4
+ "description": "One-command setup: teach your Claude Code / Codex / Hermes / OpenClaw / codewhale to search MisakaNet failure lessons before repeating a mistake, and to distil the session at a checkpoint.",
5
5
  "bin": {
6
6
  "misakanet-setup": "bin/misakanet-setup.mjs"
7
7
  },