@bububuger/spanory 0.1.21 → 0.1.22
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 +139 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4681,6 +4681,139 @@ async function applyCodexWatchSetup({ homeRoot, dryRun }) {
|
|
|
4681
4681
|
scriptPresent: false
|
|
4682
4682
|
};
|
|
4683
4683
|
}
|
|
4684
|
+
function removeClaudeHooks(settings) {
|
|
4685
|
+
if (!settings.hooks || typeof settings.hooks !== "object")
|
|
4686
|
+
return;
|
|
4687
|
+
for (const eventName of Object.keys(settings.hooks)) {
|
|
4688
|
+
const groups = settings.hooks[eventName];
|
|
4689
|
+
if (!Array.isArray(groups))
|
|
4690
|
+
continue;
|
|
4691
|
+
for (const group of groups) {
|
|
4692
|
+
if (!group || !Array.isArray(group.hooks))
|
|
4693
|
+
continue;
|
|
4694
|
+
group.hooks = group.hooks.filter((hook) => !(hook && typeof hook === "object" && isSpanoryHookCommand(hook.command)));
|
|
4695
|
+
}
|
|
4696
|
+
settings.hooks[eventName] = groups.filter((group) => !group || !Array.isArray(group.hooks) || group.hooks.length > 0);
|
|
4697
|
+
if (settings.hooks[eventName].length === 0)
|
|
4698
|
+
delete settings.hooks[eventName];
|
|
4699
|
+
}
|
|
4700
|
+
if (Object.keys(settings.hooks).length === 0)
|
|
4701
|
+
delete settings.hooks;
|
|
4702
|
+
}
|
|
4703
|
+
async function teardownClaudeSetup({ homeRoot, dryRun }) {
|
|
4704
|
+
const settingsPath = path8.join(homeRoot, ".claude", "settings.json");
|
|
4705
|
+
let settings = {};
|
|
4706
|
+
try {
|
|
4707
|
+
settings = JSON.parse(await readFile8(settingsPath, "utf-8"));
|
|
4708
|
+
} catch (error) {
|
|
4709
|
+
if (error?.code !== "ENOENT")
|
|
4710
|
+
throw new Error(`failed to parse Claude settings: ${error.message ?? String(error)}`);
|
|
4711
|
+
return { runtime: "claude-code", ok: true, changed: false, dryRun, settingsPath, backup: null };
|
|
4712
|
+
}
|
|
4713
|
+
const before = JSON.stringify(settings);
|
|
4714
|
+
removeClaudeHooks(settings);
|
|
4715
|
+
const after = JSON.stringify(settings);
|
|
4716
|
+
const changed = before !== after;
|
|
4717
|
+
let backup = null;
|
|
4718
|
+
if (changed && !dryRun) {
|
|
4719
|
+
backup = await backupIfExists(settingsPath);
|
|
4720
|
+
await writeFile4(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
4721
|
+
}
|
|
4722
|
+
return { runtime: "claude-code", ok: true, changed, dryRun, settingsPath, backup };
|
|
4723
|
+
}
|
|
4724
|
+
async function teardownCodexSetup({ homeRoot, dryRun }) {
|
|
4725
|
+
const codexHome = path8.join(homeRoot, ".codex");
|
|
4726
|
+
const configPath = path8.join(codexHome, "config.toml");
|
|
4727
|
+
const scriptPath = path8.join(codexHome, "bin", "spanory-codex-notify.sh");
|
|
4728
|
+
let currentConfig = "";
|
|
4729
|
+
try {
|
|
4730
|
+
currentConfig = await readFile8(configPath, "utf-8");
|
|
4731
|
+
} catch (error) {
|
|
4732
|
+
if (error?.code !== "ENOENT")
|
|
4733
|
+
throw error;
|
|
4734
|
+
}
|
|
4735
|
+
const nextConfig = stripSpanoryCodexNotifyConfig(currentConfig);
|
|
4736
|
+
const configChanged = currentConfig !== nextConfig;
|
|
4737
|
+
let scriptPresent = false;
|
|
4738
|
+
try {
|
|
4739
|
+
await stat4(scriptPath);
|
|
4740
|
+
scriptPresent = true;
|
|
4741
|
+
} catch {
|
|
4742
|
+
}
|
|
4743
|
+
const changed = configChanged || scriptPresent;
|
|
4744
|
+
let configBackup = null;
|
|
4745
|
+
if (changed && !dryRun) {
|
|
4746
|
+
if (configChanged) {
|
|
4747
|
+
configBackup = await backupIfExists(configPath);
|
|
4748
|
+
await writeFile4(configPath, nextConfig, "utf-8");
|
|
4749
|
+
}
|
|
4750
|
+
if (scriptPresent)
|
|
4751
|
+
await rm(scriptPath, { force: true });
|
|
4752
|
+
}
|
|
4753
|
+
return { runtime: "codex", ok: true, changed, dryRun, configPath, scriptPath, configBackup, scriptRemoved: scriptPresent };
|
|
4754
|
+
}
|
|
4755
|
+
async function teardownOpenclawSetup({ homeRoot, openclawRuntimeHome, dryRun }) {
|
|
4756
|
+
if (!commandExists("openclaw")) {
|
|
4757
|
+
return { runtime: "openclaw", ok: true, skipped: true, detail: "openclaw command not found in PATH" };
|
|
4758
|
+
}
|
|
4759
|
+
const runtimeHome = openclawRuntimeHomeForSetup(homeRoot, openclawRuntimeHome);
|
|
4760
|
+
if (dryRun)
|
|
4761
|
+
return { runtime: "openclaw", ok: true, changed: true, dryRun };
|
|
4762
|
+
const result = runSystemCommand("openclaw", ["plugins", "uninstall", OPENCLAW_SPANORY_PLUGIN_ID], {
|
|
4763
|
+
env: { ...process.env, ...runtimeHome ? { OPENCLAW_STATE_DIR: resolveRuntimeHome3("openclaw", runtimeHome) } : {} }
|
|
4764
|
+
});
|
|
4765
|
+
if (result.code !== 0)
|
|
4766
|
+
throw new Error(result.stderr || result.error || "openclaw plugins uninstall failed");
|
|
4767
|
+
return { runtime: "openclaw", ok: true, changed: true, dryRun };
|
|
4768
|
+
}
|
|
4769
|
+
async function teardownOpencodeSetup({ homeRoot, opencodeRuntimeHome, dryRun }) {
|
|
4770
|
+
const runtimeHome = opencodeRuntimeHomeForSetup(homeRoot, opencodeRuntimeHome);
|
|
4771
|
+
const loaderFile = opencodePluginLoaderPath(runtimeHome);
|
|
4772
|
+
let present = false;
|
|
4773
|
+
try {
|
|
4774
|
+
await stat4(loaderFile);
|
|
4775
|
+
present = true;
|
|
4776
|
+
} catch {
|
|
4777
|
+
}
|
|
4778
|
+
if (present && !dryRun)
|
|
4779
|
+
await rm(loaderFile, { force: true });
|
|
4780
|
+
return { runtime: "opencode", ok: true, changed: present, dryRun, loaderFile };
|
|
4781
|
+
}
|
|
4782
|
+
async function runSetupTeardown(options) {
|
|
4783
|
+
const homeRoot = setupHomeRoot(options.home);
|
|
4784
|
+
const selected = parseSetupRuntimes(options.runtimes);
|
|
4785
|
+
const dryRun = Boolean(options.dryRun);
|
|
4786
|
+
const results = [];
|
|
4787
|
+
if (selected.includes("claude-code")) {
|
|
4788
|
+
try {
|
|
4789
|
+
results.push(await teardownClaudeSetup({ homeRoot, dryRun }));
|
|
4790
|
+
} catch (error) {
|
|
4791
|
+
results.push({ runtime: "claude-code", ok: false, error: String(error?.message ?? error) });
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
if (selected.includes("codex")) {
|
|
4795
|
+
try {
|
|
4796
|
+
results.push(await teardownCodexSetup({ homeRoot, dryRun }));
|
|
4797
|
+
} catch (error) {
|
|
4798
|
+
results.push({ runtime: "codex", ok: false, error: String(error?.message ?? error) });
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
if (selected.includes("openclaw")) {
|
|
4802
|
+
try {
|
|
4803
|
+
results.push(await teardownOpenclawSetup({ homeRoot, openclawRuntimeHome: options.openclawRuntimeHome, dryRun }));
|
|
4804
|
+
} catch (error) {
|
|
4805
|
+
results.push({ runtime: "openclaw", ok: false, error: String(error?.message ?? error) });
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4808
|
+
if (selected.includes("opencode")) {
|
|
4809
|
+
try {
|
|
4810
|
+
results.push(await teardownOpencodeSetup({ homeRoot, opencodeRuntimeHome: options.opencodeRuntimeHome, dryRun }));
|
|
4811
|
+
} catch (error) {
|
|
4812
|
+
results.push({ runtime: "opencode", ok: false, error: String(error?.message ?? error) });
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
return { ok: results.every((r) => r.ok), results };
|
|
4816
|
+
}
|
|
4684
4817
|
function commandExists(command) {
|
|
4685
4818
|
const result = runSystemCommand("which", [command], { env: process.env });
|
|
4686
4819
|
return result.code === 0;
|
|
@@ -5460,6 +5593,12 @@ setup.command("doctor").description("Run setup diagnostics for selected runtimes
|
|
|
5460
5593
|
if (!report2.ok)
|
|
5461
5594
|
process.exitCode = 2;
|
|
5462
5595
|
});
|
|
5596
|
+
setup.command("teardown").description("Remove all Spanory integration from local runtimes").option("--runtimes <csv>", `Comma-separated runtimes (default: ${DEFAULT_SETUP_RUNTIMES.join(",")})`, DEFAULT_SETUP_RUNTIMES.join(",")).option("--home <path>", "Home directory root override (default: $HOME)").option("--openclaw-runtime-home <path>", "Override OpenClaw runtime home (default: ~/.openclaw)").option("--opencode-runtime-home <path>", "Override OpenCode runtime home (default: ~/.config/opencode)").option("--dry-run", "Only print planned changes without writing files", false).action(async (options) => {
|
|
5597
|
+
const report2 = await runSetupTeardown(options);
|
|
5598
|
+
console.log(JSON.stringify(report2, null, 2));
|
|
5599
|
+
if (!report2.ok)
|
|
5600
|
+
process.exitCode = 2;
|
|
5601
|
+
});
|
|
5463
5602
|
program.command("upgrade").description("Upgrade spanory CLI from npm registry").option("--dry-run", "Print upgrade command without executing", false).option("--manager <name>", "Package manager override: npm|tnpm").option("--scope <scope>", "Install scope override: global|local").action(async (options) => {
|
|
5464
5603
|
const manager = options.manager === "tnpm" ? "tnpm" : detectUpgradePackageManager();
|
|
5465
5604
|
const scope = options.scope === "local" ? "local" : options.scope === "global" ? "global" : detectUpgradeScope();
|