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.
Files changed (104) hide show
  1. package/ccpoke-lock.json +102 -0
  2. package/dist/agent/claude-code/claude-code-installer.d.ts +7 -21
  3. package/dist/agent/claude-code/claude-code-installer.js +67 -317
  4. package/dist/agent/claude-code/claude-code-installer.js.map +1 -1
  5. package/dist/agent/claude-code/claude-code-provider.d.ts +1 -1
  6. package/dist/agent/claude-code/claude-code-provider.js +6 -6
  7. package/dist/agent/claude-code/claude-code-provider.js.map +1 -1
  8. package/dist/agent/codex/codex-installer.d.ts +7 -12
  9. package/dist/agent/codex/codex-installer.js +50 -105
  10. package/dist/agent/codex/codex-installer.js.map +1 -1
  11. package/dist/agent/codex/codex-provider.d.ts +1 -1
  12. package/dist/agent/codex/codex-provider.js +6 -6
  13. package/dist/agent/codex/codex-provider.js.map +1 -1
  14. package/dist/agent/cursor/cursor-installer.d.ts +7 -13
  15. package/dist/agent/cursor/cursor-installer.js +53 -109
  16. package/dist/agent/cursor/cursor-installer.js.map +1 -1
  17. package/dist/agent/cursor/cursor-provider.d.ts +1 -1
  18. package/dist/agent/cursor/cursor-provider.js +6 -6
  19. package/dist/agent/cursor/cursor-provider.js.map +1 -1
  20. package/dist/agent/gemini-cli/gemini-cli-installer.d.ts +7 -12
  21. package/dist/agent/gemini-cli/gemini-cli-installer.js +64 -91
  22. package/dist/agent/gemini-cli/gemini-cli-installer.js.map +1 -1
  23. package/dist/agent/gemini-cli/gemini-cli-provider.d.ts +1 -1
  24. package/dist/agent/gemini-cli/gemini-cli-provider.js +6 -6
  25. package/dist/agent/gemini-cli/gemini-cli-provider.js.map +1 -1
  26. package/dist/agent/gemini-cli/gemini-cli-settings.d.ts +1 -3
  27. package/dist/agent/gemini-cli/gemini-cli-settings.js +6 -33
  28. package/dist/agent/gemini-cli/gemini-cli-settings.js.map +1 -1
  29. package/dist/agent/opencode/opencode-installer.d.ts +7 -9
  30. package/dist/agent/opencode/opencode-installer.js +18 -130
  31. package/dist/agent/opencode/opencode-installer.js.map +1 -1
  32. package/dist/agent/opencode/opencode-provider.d.ts +1 -1
  33. package/dist/agent/opencode/opencode-provider.js +6 -6
  34. package/dist/agent/opencode/opencode-provider.js.map +1 -1
  35. package/dist/agent/types.d.ts +12 -5
  36. package/dist/channel/discord/discord-session-command-handler.js +1 -0
  37. package/dist/channel/discord/discord-session-command-handler.js.map +1 -1
  38. package/dist/channel/telegram/telegram-channel.d.ts +1 -0
  39. package/dist/channel/telegram/telegram-channel.js +30 -28
  40. package/dist/channel/telegram/telegram-channel.js.map +1 -1
  41. package/dist/commands/setup.js +96 -38
  42. package/dist/commands/setup.js.map +1 -1
  43. package/dist/commands/update.js +3 -1
  44. package/dist/commands/update.js.map +1 -1
  45. package/dist/hooks/hook-env-writer.d.ts +4 -0
  46. package/dist/hooks/hook-env-writer.js +33 -0
  47. package/dist/hooks/hook-env-writer.js.map +1 -0
  48. package/dist/hooks/hook-lock.d.ts +11 -0
  49. package/dist/hooks/hook-lock.js +40 -0
  50. package/dist/hooks/hook-lock.js.map +1 -0
  51. package/dist/hooks/hook-script-copier.d.ts +6 -0
  52. package/dist/hooks/hook-script-copier.js +49 -0
  53. package/dist/hooks/hook-script-copier.js.map +1 -0
  54. package/dist/i18n/locales/en.js +4 -3
  55. package/dist/i18n/locales/en.js.map +1 -1
  56. package/dist/i18n/locales/vi.js +4 -3
  57. package/dist/i18n/locales/vi.js.map +1 -1
  58. package/dist/i18n/locales/zh.js +4 -3
  59. package/dist/i18n/locales/zh.js.map +1 -1
  60. package/dist/i18n/types.d.ts +3 -2
  61. package/dist/index.js +3 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/tmux/tmux-scanner.js +45 -17
  64. package/dist/tmux/tmux-scanner.js.map +1 -1
  65. package/dist/tmux/tmux-session-resolver.js +8 -8
  66. package/dist/tmux/tmux-session-resolver.js.map +1 -1
  67. package/dist/utils/atomic-file.d.ts +3 -0
  68. package/dist/utils/atomic-file.js +26 -0
  69. package/dist/utils/atomic-file.js.map +1 -0
  70. package/dist/utils/constants.d.ts +1 -0
  71. package/dist/utils/constants.js +15 -2
  72. package/dist/utils/constants.js.map +1 -1
  73. package/dist/utils/paths.d.ts +5 -0
  74. package/dist/utils/paths.js +5 -0
  75. package/dist/utils/paths.js.map +1 -1
  76. package/hooks/claude-code-notification.cmd +13 -0
  77. package/hooks/claude-code-notification.sh +10 -0
  78. package/hooks/claude-code-permission-request.cmd +13 -0
  79. package/hooks/claude-code-permission-request.sh +13 -0
  80. package/hooks/claude-code-pretooluse.cmd +15 -0
  81. package/hooks/claude-code-pretooluse.sh +13 -0
  82. package/hooks/claude-code-session-start.cmd +13 -0
  83. package/hooks/claude-code-session-start.sh +17 -0
  84. package/hooks/claude-code-stop.cmd +11 -0
  85. package/hooks/claude-code-stop.sh +8 -0
  86. package/hooks/codex-notify.cmd +12 -0
  87. package/hooks/codex-notify.sh +8 -0
  88. package/hooks/cursor-stop.cmd +12 -0
  89. package/hooks/cursor-stop.sh +8 -0
  90. package/hooks/gemini-notification.cmd +12 -0
  91. package/hooks/gemini-notification.sh +10 -0
  92. package/hooks/gemini-session-start.cmd +12 -0
  93. package/hooks/gemini-session-start.sh +10 -0
  94. package/hooks/gemini-stop.cmd +12 -0
  95. package/hooks/gemini-stop.sh +10 -0
  96. package/hooks/lib/common.cmd +12 -0
  97. package/hooks/lib/common.sh +39 -0
  98. package/hooks/lib/json-merge.cjs +11 -0
  99. package/hooks/lib/json-read.cjs +9 -0
  100. package/hooks/opencode-notify.js +119 -0
  101. package/package.json +6 -2
  102. package/dist/utils/windows-hook-script-builder.d.ts +0 -2
  103. package/dist/utils/windows-hook-script-builder.js +0 -24
  104. 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,9 @@
1
+ const fs = require("fs");
2
+ const file = process.argv[2];
3
+ const field = process.argv[3];
4
+ try {
5
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
6
+ process.stdout.write(String(data[field] || ""));
7
+ } catch {
8
+ process.stdout.write("");
9
+ }
@@ -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.1",
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,2 +0,0 @@
1
- export type HookInputMode = "stdin" | "argument";
2
- export declare function buildWindowsHookScript(version: string, port: number, route: string, secret: string, inputMode?: HookInputMode): string;
@@ -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"}