ccpoke 1.7.1 → 1.7.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/ccpoke-lock.json +102 -0
- package/dist/agent/claude-code/claude-code-installer.d.ts +7 -21
- package/dist/agent/claude-code/claude-code-installer.js +67 -317
- package/dist/agent/claude-code/claude-code-installer.js.map +1 -1
- package/dist/agent/claude-code/claude-code-provider.d.ts +1 -1
- package/dist/agent/claude-code/claude-code-provider.js +6 -6
- package/dist/agent/claude-code/claude-code-provider.js.map +1 -1
- package/dist/agent/codex/codex-installer.d.ts +7 -12
- package/dist/agent/codex/codex-installer.js +50 -105
- package/dist/agent/codex/codex-installer.js.map +1 -1
- package/dist/agent/codex/codex-provider.d.ts +1 -1
- package/dist/agent/codex/codex-provider.js +6 -6
- package/dist/agent/codex/codex-provider.js.map +1 -1
- package/dist/agent/cursor/cursor-installer.d.ts +7 -13
- package/dist/agent/cursor/cursor-installer.js +53 -109
- package/dist/agent/cursor/cursor-installer.js.map +1 -1
- package/dist/agent/cursor/cursor-provider.d.ts +1 -1
- package/dist/agent/cursor/cursor-provider.js +6 -6
- package/dist/agent/cursor/cursor-provider.js.map +1 -1
- package/dist/agent/gemini-cli/gemini-cli-installer.d.ts +7 -12
- package/dist/agent/gemini-cli/gemini-cli-installer.js +64 -91
- package/dist/agent/gemini-cli/gemini-cli-installer.js.map +1 -1
- package/dist/agent/gemini-cli/gemini-cli-provider.d.ts +1 -1
- package/dist/agent/gemini-cli/gemini-cli-provider.js +6 -6
- package/dist/agent/gemini-cli/gemini-cli-provider.js.map +1 -1
- package/dist/agent/gemini-cli/gemini-cli-settings.d.ts +1 -3
- package/dist/agent/gemini-cli/gemini-cli-settings.js +6 -33
- package/dist/agent/gemini-cli/gemini-cli-settings.js.map +1 -1
- package/dist/agent/opencode/opencode-installer.d.ts +7 -9
- package/dist/agent/opencode/opencode-installer.js +18 -130
- package/dist/agent/opencode/opencode-installer.js.map +1 -1
- package/dist/agent/opencode/opencode-provider.d.ts +1 -1
- package/dist/agent/opencode/opencode-provider.js +6 -6
- package/dist/agent/opencode/opencode-provider.js.map +1 -1
- package/dist/agent/types.d.ts +12 -5
- package/dist/channel/discord/discord-session-command-handler.js +1 -0
- package/dist/channel/discord/discord-session-command-handler.js.map +1 -1
- package/dist/channel/telegram/telegram-channel.d.ts +1 -0
- package/dist/channel/telegram/telegram-channel.js +30 -28
- package/dist/channel/telegram/telegram-channel.js.map +1 -1
- package/dist/commands/setup.js +96 -38
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/update.js +3 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/hooks/hook-env-writer.d.ts +4 -0
- package/dist/hooks/hook-env-writer.js +33 -0
- package/dist/hooks/hook-env-writer.js.map +1 -0
- package/dist/hooks/hook-lock.d.ts +11 -0
- package/dist/hooks/hook-lock.js +40 -0
- package/dist/hooks/hook-lock.js.map +1 -0
- package/dist/hooks/hook-script-copier.d.ts +6 -0
- package/dist/hooks/hook-script-copier.js +49 -0
- package/dist/hooks/hook-script-copier.js.map +1 -0
- package/dist/i18n/locales/en.js +4 -3
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/vi.js +4 -3
- package/dist/i18n/locales/vi.js.map +1 -1
- package/dist/i18n/locales/zh.js +4 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/types.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/tmux/tmux-scanner.js +45 -17
- package/dist/tmux/tmux-scanner.js.map +1 -1
- package/dist/tmux/tmux-session-resolver.js +8 -8
- package/dist/tmux/tmux-session-resolver.js.map +1 -1
- package/dist/utils/atomic-file.d.ts +3 -0
- package/dist/utils/atomic-file.js +26 -0
- package/dist/utils/atomic-file.js.map +1 -0
- package/dist/utils/constants.d.ts +1 -0
- package/dist/utils/constants.js +15 -2
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/paths.d.ts +5 -0
- package/dist/utils/paths.js +5 -0
- package/dist/utils/paths.js.map +1 -1
- package/hooks/claude-code-notification.cmd +13 -0
- package/hooks/claude-code-notification.sh +10 -0
- package/hooks/claude-code-permission-request.cmd +13 -0
- package/hooks/claude-code-permission-request.sh +13 -0
- package/hooks/claude-code-pretooluse.cmd +15 -0
- package/hooks/claude-code-pretooluse.sh +13 -0
- package/hooks/claude-code-session-start.cmd +13 -0
- package/hooks/claude-code-session-start.sh +17 -0
- package/hooks/claude-code-stop.cmd +11 -0
- package/hooks/claude-code-stop.sh +8 -0
- package/hooks/codex-notify.cmd +12 -0
- package/hooks/codex-notify.sh +8 -0
- package/hooks/cursor-stop.cmd +12 -0
- package/hooks/cursor-stop.sh +8 -0
- package/hooks/gemini-notification.cmd +12 -0
- package/hooks/gemini-notification.sh +10 -0
- package/hooks/gemini-session-start.cmd +12 -0
- package/hooks/gemini-session-start.sh +10 -0
- package/hooks/gemini-stop.cmd +12 -0
- package/hooks/gemini-stop.sh +10 -0
- package/hooks/lib/common.cmd +12 -0
- package/hooks/lib/common.sh +39 -0
- package/hooks/lib/json-merge.cjs +11 -0
- package/hooks/lib/json-read.cjs +9 -0
- package/hooks/opencode-notify.js +119 -0
- package/package.json +6 -2
- package/dist/utils/windows-hook-script-builder.d.ts +0 -2
- package/dist/utils/windows-hook-script-builder.js +0 -24
- package/dist/utils/windows-hook-script-builder.js.map +0 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ccpoke shared hook library
|
|
3
|
+
|
|
4
|
+
CCPOKE_ENV_FILE="$HOME/.ccpoke/hooks/.env"
|
|
5
|
+
[ -f "$CCPOKE_ENV_FILE" ] || exit 0
|
|
6
|
+
. "$CCPOKE_ENV_FILE"
|
|
7
|
+
|
|
8
|
+
CCPOKE_HOST="${CCPOKE_HOST:-localhost}"
|
|
9
|
+
|
|
10
|
+
ccpoke_detect_tmux() {
|
|
11
|
+
CCPOKE_TMUX_TARGET=""
|
|
12
|
+
[ -n "$TMUX_PANE" ] || return 0
|
|
13
|
+
CCPOKE_TMUX_TARGET=$(tmux display-message -t "$TMUX_PANE" \
|
|
14
|
+
-p '#{session_name}:#{window_index}.#{pane_index}' 2>/dev/null || echo "")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
ccpoke_inject_tmux() {
|
|
18
|
+
local json="$1"
|
|
19
|
+
if [ -n "$CCPOKE_TMUX_TARGET" ] && \
|
|
20
|
+
echo "$CCPOKE_TMUX_TARGET" | grep -qE '^[a-zA-Z0-9_.:/@ -]+$'; then
|
|
21
|
+
echo "$json" | sed 's/}$/,"tmux_target":"'"$CCPOKE_TMUX_TARGET"'"}/'
|
|
22
|
+
else
|
|
23
|
+
echo "$json"
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
ccpoke_post() {
|
|
28
|
+
local route="$1"
|
|
29
|
+
local payload="$2"
|
|
30
|
+
local max_time="${3:-5}"
|
|
31
|
+
echo "$payload" | curl -s -X POST "http://$CCPOKE_HOST:$CCPOKE_PORT$route" \
|
|
32
|
+
-H "Content-Type: application/json" \
|
|
33
|
+
-H "X-CCPoke-Secret: $CCPOKE_SECRET" \
|
|
34
|
+
--data-binary @- --max-time "$max_time" > /dev/null 2>&1 || true
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ccpoke_json_escape() {
|
|
38
|
+
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g' | tr -d '\n\r'
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const file = process.argv[2];
|
|
3
|
+
const key = process.argv[3];
|
|
4
|
+
const value = process.argv[4];
|
|
5
|
+
try {
|
|
6
|
+
const data = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
7
|
+
data[key] = value;
|
|
8
|
+
fs.writeFileSync(file, JSON.stringify(data));
|
|
9
|
+
} catch {
|
|
10
|
+
/* best-effort */
|
|
11
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
function loadCcpokeConfig() {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(readFileSync(join(homedir(), ".ccpoke", "config.json"), "utf-8"));
|
|
8
|
+
} catch {
|
|
9
|
+
return { hook_port: 9377, hook_secret: "" };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default async function ({ $, client, directory, worktree }) {
|
|
14
|
+
const cfg = loadCcpokeConfig();
|
|
15
|
+
const ccpokeHost = process.env.CCPOKE_HOST || "localhost";
|
|
16
|
+
const port = cfg.hook_port;
|
|
17
|
+
const secret = cfg.hook_secret;
|
|
18
|
+
const agentParam = "?agent=opencode";
|
|
19
|
+
|
|
20
|
+
let cachedTmuxTarget = null;
|
|
21
|
+
|
|
22
|
+
async function detectTmuxTarget() {
|
|
23
|
+
if (cachedTmuxTarget !== null) return cachedTmuxTarget;
|
|
24
|
+
try {
|
|
25
|
+
const result = await $`tmux display-message -p '#{session_name}:#{window_index}.#{pane_index}'`
|
|
26
|
+
.nothrow()
|
|
27
|
+
.quiet();
|
|
28
|
+
cachedTmuxTarget = result.stdout?.toString().trim() || "";
|
|
29
|
+
} catch {
|
|
30
|
+
cachedTmuxTarget = "";
|
|
31
|
+
}
|
|
32
|
+
return cachedTmuxTarget;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function fetchSessionContext(sessionID) {
|
|
36
|
+
if (!sessionID || !client) return { summary: "", model: "" };
|
|
37
|
+
try {
|
|
38
|
+
const res = await client.session.messages({
|
|
39
|
+
path: { id: sessionID },
|
|
40
|
+
query: { limit: 2 },
|
|
41
|
+
});
|
|
42
|
+
const messages = res.data ?? [];
|
|
43
|
+
let summary = "";
|
|
44
|
+
let model = "";
|
|
45
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
46
|
+
const msg = messages[i];
|
|
47
|
+
if (msg.info?.role !== "assistant") continue;
|
|
48
|
+
model = msg.info.modelID ? msg.info.providerID + "/" + msg.info.modelID : "";
|
|
49
|
+
const texts = (msg.parts ?? []).filter((p) => p.type === "text").map((p) => p.text ?? "");
|
|
50
|
+
summary = texts.join("\\n").slice(0, 500);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
return { summary, model };
|
|
54
|
+
} catch {
|
|
55
|
+
return { summary: "", model: "" };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
event: async ({ event }) => {
|
|
61
|
+
if (event.type === "question.asked") {
|
|
62
|
+
const props = event.properties || {};
|
|
63
|
+
const tmuxTarget = await detectTmuxTarget();
|
|
64
|
+
const payload = JSON.stringify({
|
|
65
|
+
session_id: props.sessionID || "",
|
|
66
|
+
tool_input: { questions: props.questions || [] },
|
|
67
|
+
cwd: worktree || directory,
|
|
68
|
+
tmux_target: tmuxTarget,
|
|
69
|
+
});
|
|
70
|
+
try {
|
|
71
|
+
await $`echo ${payload} | curl -s -X POST "http://${ccpokeHost}:${port}/hook/ask-user-question${agentParam}" -H "Content-Type: application/json" -H "X-CCPoke-Secret: ${secret}" --data-binary @- --max-time 5`
|
|
72
|
+
.nothrow()
|
|
73
|
+
.quiet();
|
|
74
|
+
} catch {}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (event.type === "permission.asked") {
|
|
79
|
+
const props = event.properties || {};
|
|
80
|
+
const tmuxTarget = await detectTmuxTarget();
|
|
81
|
+
const payload = JSON.stringify({
|
|
82
|
+
session_id: props.sessionID || "",
|
|
83
|
+
tool_name: props.permission || "unknown",
|
|
84
|
+
tool_input: {
|
|
85
|
+
patterns: props.patterns || [],
|
|
86
|
+
metadata: props.metadata || {},
|
|
87
|
+
},
|
|
88
|
+
cwd: worktree || directory,
|
|
89
|
+
tmux_target: tmuxTarget,
|
|
90
|
+
});
|
|
91
|
+
try {
|
|
92
|
+
await $`echo ${payload} | curl -s -X POST "http://${ccpokeHost}:${port}/hook/permission-request${agentParam}" -H "Content-Type: application/json" -H "X-CCPoke-Secret: ${secret}" --data-binary @- --max-time 5`
|
|
93
|
+
.nothrow()
|
|
94
|
+
.quiet();
|
|
95
|
+
} catch {}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (event.type !== "session.idle" && event.type !== "session.error") return;
|
|
100
|
+
const sessionID = event.properties?.sessionID || "";
|
|
101
|
+
const cwd = worktree || directory;
|
|
102
|
+
const ctx = await fetchSessionContext(sessionID);
|
|
103
|
+
const tmuxTarget = await detectTmuxTarget();
|
|
104
|
+
const payload = JSON.stringify({
|
|
105
|
+
session_id: sessionID,
|
|
106
|
+
prompt_response: ctx.summary,
|
|
107
|
+
cwd,
|
|
108
|
+
model: ctx.model,
|
|
109
|
+
event_type: event.type,
|
|
110
|
+
tmux_target: tmuxTarget,
|
|
111
|
+
});
|
|
112
|
+
try {
|
|
113
|
+
await $`echo ${payload} | curl -s -X POST "http://${ccpokeHost}:${port}/hook/stop${agentParam}" -H "Content-Type: application/json" -H "X-CCPoke-Secret: ${secret}" --data-binary @- --max-time 5`
|
|
114
|
+
.nothrow()
|
|
115
|
+
.quiet();
|
|
116
|
+
} catch {}
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccpoke",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "Your AI agent pokes you when it's done — zero-config, one command: npx -y ccpoke",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"dist",
|
|
38
|
+
"hooks",
|
|
39
|
+
"ccpoke-lock.json",
|
|
38
40
|
"LICENSE",
|
|
39
41
|
"README.md",
|
|
40
42
|
"README.en.md"
|
|
@@ -89,6 +91,8 @@
|
|
|
89
91
|
"lint": "eslint src/",
|
|
90
92
|
"lint:fix": "eslint src/ --fix",
|
|
91
93
|
"format": "prettier --write 'src/**/*.ts'",
|
|
92
|
-
"format:check": "prettier --check 'src/**/*.ts'"
|
|
94
|
+
"format:check": "prettier --check 'src/**/*.ts'",
|
|
95
|
+
"hash:update": "tsx scripts/update-hook-hashes.ts",
|
|
96
|
+
"hash:check": "tsx scripts/check-hook-hashes.ts"
|
|
93
97
|
}
|
|
94
98
|
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const DEBUG_LOG = `%TEMP%\\ccpoke-hook-debug.log`;
|
|
2
|
-
function buildDebugLine(message) {
|
|
3
|
-
return `if defined CCPOKE_DEBUG echo [%DATE% %TIME%] ${message} >> ${DEBUG_LOG}`;
|
|
4
|
-
}
|
|
5
|
-
export function buildWindowsHookScript(version, port, route, secret, inputMode = "stdin") {
|
|
6
|
-
const lines = [
|
|
7
|
-
`@REM ccpoke-version: ${version}`,
|
|
8
|
-
`@echo off`,
|
|
9
|
-
`setlocal`,
|
|
10
|
-
`if not defined CCPOKE_HOST set CCPOKE_HOST=localhost`,
|
|
11
|
-
`set TMPFILE=%TEMP%\\ccpoke-%RANDOM%%RANDOM%.json`,
|
|
12
|
-
buildDebugLine(`route=${route} inputMode=${inputMode}`),
|
|
13
|
-
];
|
|
14
|
-
if (inputMode === "stdin") {
|
|
15
|
-
lines.push(`findstr "^" > %TMPFILE%`);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
lines.push(`node -e "require('fs').writeFileSync(process.env.TMPFILE,process.argv[1]||'{}')" %*`);
|
|
19
|
-
lines.push(buildDebugLine(`arg=written-via-node`));
|
|
20
|
-
}
|
|
21
|
-
lines.push(buildDebugLine(`tmpfile=%TMPFILE%`), `set TMUX_TARGET=`, `if defined TMUX_PANE (`, ` for /f "tokens=*" %%a in ('psmux display-message -t "%TMUX_PANE%" -p "#{session_name}:#{window_index}.#{pane_index}" 2^>nul') do set TMUX_TARGET=%%a`, `)`, `if defined TMUX_TARGET (`, ` powershell -NoProfile -Command "$j=Get-Content '%TMPFILE%' -Raw|ConvertFrom-Json; $j|Add-Member -NotePropertyName tmux_target -NotePropertyValue '%TMUX_TARGET%' -Force; $j|ConvertTo-Json -Compress|Set-Content '%TMPFILE%'"`, `)`, `curl.exe -s -X POST http://%CCPOKE_HOST%:${port}${route} -H "Content-Type: application/json" -H "X-CCPoke-Secret: ${secret}" -d @%TMPFILE% > nul 2>&1`, buildDebugLine(`curl_exit=%ERRORLEVEL%`), `del %TMPFILE% > nul 2>&1`, `endlocal`, ``);
|
|
22
|
-
return lines.join("\r\n");
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=windows-hook-script-builder.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"windows-hook-script-builder.js","sourceRoot":"","sources":["../../src/utils/windows-hook-script-builder.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,+BAA+B,CAAC;AAElD,SAAS,cAAc,CAAC,OAAe;IACrC,OAAO,gDAAgD,OAAO,OAAO,SAAS,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,OAAe,EACf,IAAY,EACZ,KAAa,EACb,MAAc,EACd,YAA2B,OAAO;IAElC,MAAM,KAAK,GAAG;QACZ,wBAAwB,OAAO,EAAE;QACjC,WAAW;QACX,UAAU;QACV,sDAAsD;QACtD,kDAAkD;QAClD,cAAc,CAAC,SAAS,KAAK,cAAc,SAAS,EAAE,CAAC;KACxD,CAAC;IAEF,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CACR,qFAAqF,CACtF,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,IAAI,CACR,cAAc,CAAC,mBAAmB,CAAC,EACnC,kBAAkB,EAClB,wBAAwB,EACxB,wJAAwJ,EACxJ,GAAG,EACH,0BAA0B,EAC1B,iOAAiO,EACjO,GAAG,EACH,4CAA4C,IAAI,GAAG,KAAK,6DAA6D,MAAM,4BAA4B,EACvJ,cAAc,CAAC,wBAAwB,CAAC,EACxC,0BAA0B,EAC1B,UAAU,EACV,EAAE,CACH,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
|