aisnitch 0.2.5 → 0.2.13
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 +1 -1
- package/dist/cli/index.cjs +152 -26
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +152 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/cli/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { Command, InvalidArgumentError } from "commander";
|
|
|
8
8
|
|
|
9
9
|
// src/package-info.ts
|
|
10
10
|
var AISNITCH_PACKAGE_NAME = "aisnitch";
|
|
11
|
-
var AISNITCH_VERSION = "0.2.
|
|
11
|
+
var AISNITCH_VERSION = "0.2.12";
|
|
12
12
|
var AISNITCH_DESCRIPTION = "Universal bridge for AI coding tool activity \u2014 capture, normalize, stream.";
|
|
13
13
|
|
|
14
14
|
// src/core/events/schema.ts
|
|
@@ -723,7 +723,6 @@ var ANSI_RESET = "\x1B[0m";
|
|
|
723
723
|
var ANSI_RED = "\x1B[31m";
|
|
724
724
|
var ANSI_GREEN = "\x1B[32m";
|
|
725
725
|
var ANSI_CYAN = "\x1B[36m";
|
|
726
|
-
var CLAUDE_FILE_CHANGED_MATCHER = ".*";
|
|
727
726
|
var CLAUDE_CODE_HOOK_EVENTS = [
|
|
728
727
|
"SessionStart",
|
|
729
728
|
"UserPromptSubmit",
|
|
@@ -734,15 +733,12 @@ var CLAUDE_CODE_HOOK_EVENTS = [
|
|
|
734
733
|
"Notification",
|
|
735
734
|
"SubagentStart",
|
|
736
735
|
"SubagentStop",
|
|
737
|
-
"TaskCreated",
|
|
738
736
|
"TaskCompleted",
|
|
739
737
|
"Stop",
|
|
740
738
|
"StopFailure",
|
|
741
739
|
"TeammateIdle",
|
|
742
740
|
"InstructionsLoaded",
|
|
743
741
|
"ConfigChange",
|
|
744
|
-
"CwdChanged",
|
|
745
|
-
"FileChanged",
|
|
746
742
|
"WorktreeCreate",
|
|
747
743
|
"WorktreeRemove",
|
|
748
744
|
"PreCompact",
|
|
@@ -843,9 +839,15 @@ var FileToolSetupBase = class {
|
|
|
843
839
|
var ClaudeCodeSetup = class extends FileToolSetupBase {
|
|
844
840
|
settingsPath;
|
|
845
841
|
hookUrl;
|
|
842
|
+
scriptPath;
|
|
846
843
|
constructor(httpPort, dependencies = {}) {
|
|
847
844
|
super("claude-code", dependencies.binaryExists);
|
|
848
845
|
this.settingsPath = dependencies.claudeSettingsPath ?? join2(dependencies.homeDirectory ?? homedir2(), ".claude", "settings.json");
|
|
846
|
+
this.scriptPath = join2(
|
|
847
|
+
dependencies.homeDirectory ?? homedir2(),
|
|
848
|
+
".claude",
|
|
849
|
+
"aisnitch-forward.mjs"
|
|
850
|
+
);
|
|
849
851
|
this.hookUrl = `http://localhost:${httpPort}/hooks/claude-code`;
|
|
850
852
|
}
|
|
851
853
|
async detect() {
|
|
@@ -854,7 +856,49 @@ var ClaudeCodeSetup = class extends FileToolSetupBase {
|
|
|
854
856
|
getConfigPath() {
|
|
855
857
|
return this.settingsPath;
|
|
856
858
|
}
|
|
859
|
+
async computeDiff() {
|
|
860
|
+
const currentSettingsContent = await readOptionalFile(this.settingsPath);
|
|
861
|
+
const currentScriptContent = await readOptionalFile(this.scriptPath);
|
|
862
|
+
const nextSettingsContent = this.buildNextSettingsContent(currentSettingsContent);
|
|
863
|
+
const nextScriptContent = buildClaudeForwardScriptSource();
|
|
864
|
+
return [
|
|
865
|
+
renderColoredDiff(
|
|
866
|
+
this.settingsPath,
|
|
867
|
+
currentSettingsContent,
|
|
868
|
+
nextSettingsContent
|
|
869
|
+
),
|
|
870
|
+
"",
|
|
871
|
+
renderColoredDiff(
|
|
872
|
+
this.scriptPath,
|
|
873
|
+
currentScriptContent,
|
|
874
|
+
nextScriptContent
|
|
875
|
+
)
|
|
876
|
+
].join("\n");
|
|
877
|
+
}
|
|
878
|
+
async apply() {
|
|
879
|
+
const currentSettingsContent = await readOptionalFile(this.settingsPath);
|
|
880
|
+
const currentScriptContent = await readOptionalFile(this.scriptPath);
|
|
881
|
+
const nextSettingsContent = this.buildNextSettingsContent(currentSettingsContent);
|
|
882
|
+
const nextScriptContent = buildClaudeForwardScriptSource();
|
|
883
|
+
await mkdir2(dirname2(this.settingsPath), { recursive: true });
|
|
884
|
+
await mkdir2(dirname2(this.scriptPath), { recursive: true });
|
|
885
|
+
if (currentSettingsContent !== null) {
|
|
886
|
+
await copyFile(this.settingsPath, this.getFileBackupPath(this.settingsPath));
|
|
887
|
+
}
|
|
888
|
+
if (currentScriptContent !== null) {
|
|
889
|
+
await copyFile(this.scriptPath, this.getFileBackupPath(this.scriptPath));
|
|
890
|
+
}
|
|
891
|
+
await writeFile2(this.settingsPath, nextSettingsContent, "utf8");
|
|
892
|
+
await writeFile2(this.scriptPath, nextScriptContent, "utf8");
|
|
893
|
+
}
|
|
894
|
+
async revert() {
|
|
895
|
+
await restoreBackupOrRemove(this.settingsPath);
|
|
896
|
+
await restoreBackupOrRemove(this.scriptPath);
|
|
897
|
+
}
|
|
857
898
|
buildNextContent(currentContent) {
|
|
899
|
+
return Promise.resolve(this.buildNextSettingsContent(currentContent));
|
|
900
|
+
}
|
|
901
|
+
buildNextSettingsContent(currentContent) {
|
|
858
902
|
const parsedSettings = parseClaudeSettings(currentContent);
|
|
859
903
|
const currentHooks = parsedSettings.hooks ?? {};
|
|
860
904
|
const nextHooks = {
|
|
@@ -864,15 +908,19 @@ var ClaudeCodeSetup = class extends FileToolSetupBase {
|
|
|
864
908
|
nextHooks[hookEventName] = ensureClaudeAISnitchHook(
|
|
865
909
|
currentHooks[hookEventName] ?? [],
|
|
866
910
|
hookEventName,
|
|
867
|
-
this.hookUrl
|
|
911
|
+
this.hookUrl,
|
|
912
|
+
this.scriptPath
|
|
868
913
|
);
|
|
869
914
|
}
|
|
870
915
|
const nextSettings = {
|
|
871
916
|
...parsedSettings,
|
|
872
917
|
hooks: nextHooks
|
|
873
918
|
};
|
|
874
|
-
return
|
|
875
|
-
|
|
919
|
+
return `${JSON.stringify(nextSettings, null, 2)}
|
|
920
|
+
`;
|
|
921
|
+
}
|
|
922
|
+
getFileBackupPath(path) {
|
|
923
|
+
return `${path}.bak`;
|
|
876
924
|
}
|
|
877
925
|
};
|
|
878
926
|
var OpenCodeSetup = class extends FileToolSetupBase {
|
|
@@ -1676,43 +1724,116 @@ async function defaultConfirm(_diff, prompt) {
|
|
|
1676
1724
|
readlineInterface.close();
|
|
1677
1725
|
}
|
|
1678
1726
|
}
|
|
1679
|
-
function ensureClaudeAISnitchHook(groups, hookEventName, hookUrl) {
|
|
1727
|
+
function ensureClaudeAISnitchHook(groups, hookEventName, hookUrl, scriptPath) {
|
|
1680
1728
|
const clonedGroups = groups.map((group) => ({
|
|
1681
1729
|
...group,
|
|
1682
1730
|
hooks: group.hooks.map((handler) => ({ ...handler }))
|
|
1683
1731
|
}));
|
|
1684
|
-
const
|
|
1685
|
-
const matchingGroup = clonedGroups.find((group) => group.matcher === matcher);
|
|
1732
|
+
const matchingGroup = clonedGroups.find((group) => group.matcher === void 0);
|
|
1686
1733
|
if (matchingGroup) {
|
|
1687
|
-
|
|
1734
|
+
matchingGroup.hooks = matchingGroup.hooks.filter(
|
|
1735
|
+
(handler) => !isLegacyClaudeAISnitchHttpHook(handler, hookUrl)
|
|
1736
|
+
);
|
|
1737
|
+
const existingHook = matchingGroup.hooks.find(
|
|
1738
|
+
(handler) => isClaudeAISnitchHook(handler, hookEventName, hookUrl, scriptPath)
|
|
1739
|
+
);
|
|
1688
1740
|
if (existingHook) {
|
|
1689
1741
|
if (existingHook.async !== true) {
|
|
1690
1742
|
existingHook.async = true;
|
|
1691
1743
|
}
|
|
1692
1744
|
return clonedGroups;
|
|
1693
1745
|
}
|
|
1694
|
-
matchingGroup.hooks.push(
|
|
1746
|
+
matchingGroup.hooks.push(
|
|
1747
|
+
createClaudeAISnitchHook(hookEventName, hookUrl, scriptPath)
|
|
1748
|
+
);
|
|
1695
1749
|
return clonedGroups;
|
|
1696
1750
|
}
|
|
1697
|
-
|
|
1698
|
-
hooks: [createClaudeAISnitchHook(hookUrl)]
|
|
1699
|
-
}
|
|
1700
|
-
matcher,
|
|
1701
|
-
hooks: [createClaudeAISnitchHook(hookUrl)]
|
|
1702
|
-
};
|
|
1703
|
-
clonedGroups.push(nextGroup);
|
|
1751
|
+
clonedGroups.push({
|
|
1752
|
+
hooks: [createClaudeAISnitchHook(hookEventName, hookUrl, scriptPath)]
|
|
1753
|
+
});
|
|
1704
1754
|
return clonedGroups;
|
|
1705
1755
|
}
|
|
1706
|
-
function createClaudeAISnitchHook(hookUrl) {
|
|
1756
|
+
function createClaudeAISnitchHook(hookEventName, hookUrl, scriptPath) {
|
|
1707
1757
|
return {
|
|
1708
1758
|
async: true,
|
|
1709
|
-
|
|
1710
|
-
|
|
1759
|
+
command: buildClaudeForwardCommand(hookEventName, hookUrl, scriptPath),
|
|
1760
|
+
type: "command"
|
|
1711
1761
|
};
|
|
1712
1762
|
}
|
|
1713
|
-
function isClaudeAISnitchHook(handler, hookUrl) {
|
|
1763
|
+
function isClaudeAISnitchHook(handler, hookEventName, hookUrl, scriptPath) {
|
|
1764
|
+
return handler.type === "command" && typeof handler.command === "string" && handler.command === buildClaudeForwardCommand(hookEventName, hookUrl, scriptPath);
|
|
1765
|
+
}
|
|
1766
|
+
function isLegacyClaudeAISnitchHttpHook(handler, hookUrl) {
|
|
1714
1767
|
return handler.type === "http" && handler.url === hookUrl;
|
|
1715
1768
|
}
|
|
1769
|
+
function buildClaudeForwardCommand(hookEventName, hookUrl, scriptPath) {
|
|
1770
|
+
return `node ${shellEscapeSingle(scriptPath)} ${hookEventName} ${shellEscapeSingle(hookUrl)}`;
|
|
1771
|
+
}
|
|
1772
|
+
function buildClaudeForwardScriptSource() {
|
|
1773
|
+
return `#!/usr/bin/env node
|
|
1774
|
+
/**
|
|
1775
|
+
* AISnitch Claude Code hook bridge
|
|
1776
|
+
*
|
|
1777
|
+
* \u{1F4D6} Claude Code currently exposes command hooks fed through stdin JSON.
|
|
1778
|
+
* This bridge keeps the Claude config valid while still forwarding every
|
|
1779
|
+
* selected hook event into the local AISnitch HTTP receiver.
|
|
1780
|
+
*/
|
|
1781
|
+
|
|
1782
|
+
function isRecord(value) {
|
|
1783
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
async function readInput() {
|
|
1787
|
+
const chunks = [];
|
|
1788
|
+
|
|
1789
|
+
for await (const chunk of process.stdin) {
|
|
1790
|
+
chunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
return chunks.join("");
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
async function main() {
|
|
1797
|
+
const hookEventName = process.argv[2] ?? "unknown";
|
|
1798
|
+
const endpoint = process.argv[3] ?? "http://localhost:4821/hooks/claude-code";
|
|
1799
|
+
const rawInput = await readInput();
|
|
1800
|
+
let payload = {};
|
|
1801
|
+
|
|
1802
|
+
if (rawInput.trim().length > 0) {
|
|
1803
|
+
try {
|
|
1804
|
+
const parsedPayload = JSON.parse(rawInput);
|
|
1805
|
+
|
|
1806
|
+
if (isRecord(parsedPayload)) {
|
|
1807
|
+
payload = parsedPayload;
|
|
1808
|
+
}
|
|
1809
|
+
} catch {
|
|
1810
|
+
payload = {
|
|
1811
|
+
raw: rawInput
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
try {
|
|
1817
|
+
await fetch(endpoint, {
|
|
1818
|
+
method: "POST",
|
|
1819
|
+
headers: {
|
|
1820
|
+
"content-type": "application/json"
|
|
1821
|
+
},
|
|
1822
|
+
body: JSON.stringify({
|
|
1823
|
+
...payload,
|
|
1824
|
+
hook_event_name: hookEventName
|
|
1825
|
+
})
|
|
1826
|
+
});
|
|
1827
|
+
} catch {
|
|
1828
|
+
// Claude hooks must stay fire-and-forget for observability only.
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
void main().catch(() => {
|
|
1833
|
+
// Never bubble hook bridge failures back into Claude Code itself.
|
|
1834
|
+
});
|
|
1835
|
+
`;
|
|
1836
|
+
}
|
|
1716
1837
|
function ensureGeminiAISnitchHook(groups, hookUrl) {
|
|
1717
1838
|
const clonedGroups = groups.map((group) => ({
|
|
1718
1839
|
...group,
|
|
@@ -2227,6 +2348,9 @@ async function readOptionalFile(path) {
|
|
|
2227
2348
|
throw error;
|
|
2228
2349
|
}
|
|
2229
2350
|
}
|
|
2351
|
+
function shellEscapeSingle(value) {
|
|
2352
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
2353
|
+
}
|
|
2230
2354
|
async function restoreBackupOrRemove(path) {
|
|
2231
2355
|
const backupPath = `${path}.bak`;
|
|
2232
2356
|
if (await fileExists(backupPath)) {
|
|
@@ -9513,6 +9637,7 @@ var Pipeline = class {
|
|
|
9513
9637
|
|
|
9514
9638
|
// src/tui/index.tsx
|
|
9515
9639
|
import { render } from "ink";
|
|
9640
|
+
import { withFullScreen } from "fullscreen-ink";
|
|
9516
9641
|
import WebSocket4 from "ws";
|
|
9517
9642
|
|
|
9518
9643
|
// src/tui/App.tsx
|
|
@@ -11421,7 +11546,7 @@ function attachEventBusMonitor(eventBus, output) {
|
|
|
11421
11546
|
// src/tui/index.tsx
|
|
11422
11547
|
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
11423
11548
|
async function renderForegroundTui(options) {
|
|
11424
|
-
const
|
|
11549
|
+
const ink = withFullScreen(
|
|
11425
11550
|
/* @__PURE__ */ jsx13(
|
|
11426
11551
|
App,
|
|
11427
11552
|
{
|
|
@@ -11443,7 +11568,8 @@ async function renderForegroundTui(options) {
|
|
|
11443
11568
|
}
|
|
11444
11569
|
)
|
|
11445
11570
|
);
|
|
11446
|
-
await
|
|
11571
|
+
await ink.start();
|
|
11572
|
+
await ink.waitUntilExit;
|
|
11447
11573
|
}
|
|
11448
11574
|
async function renderManagedTui(options) {
|
|
11449
11575
|
const app = render(
|