0xkobold 0.0.1

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 (258) hide show
  1. package/.agents/skills/nextjs-best-practices/SKILL.md +208 -0
  2. package/.agents/skills/sql-optimization-patterns/SKILL.md +509 -0
  3. package/HEARTBEAT.md +45 -0
  4. package/README.md +197 -0
  5. package/USAGE.md +191 -0
  6. package/dist/package.json +77 -0
  7. package/dist/src/agent/pi-adapter.js +307 -0
  8. package/dist/src/agent/pi-adapter.js.map +1 -0
  9. package/dist/src/agent/tool-adapter.js +86 -0
  10. package/dist/src/agent/tool-adapter.js.map +1 -0
  11. package/dist/src/approval/queue.js +114 -0
  12. package/dist/src/approval/queue.js.map +1 -0
  13. package/dist/src/ascii-kobold.js +76 -0
  14. package/dist/src/ascii-kobold.js.map +1 -0
  15. package/dist/src/cli/client.js +217 -0
  16. package/dist/src/cli/client.js.map +1 -0
  17. package/dist/src/cli/commands/agent.js +272 -0
  18. package/dist/src/cli/commands/agent.js.map +1 -0
  19. package/dist/src/cli/commands/chat.js +234 -0
  20. package/dist/src/cli/commands/chat.js.map +1 -0
  21. package/dist/src/cli/commands/config.js +202 -0
  22. package/dist/src/cli/commands/config.js.map +1 -0
  23. package/dist/src/cli/commands/daemon.js +203 -0
  24. package/dist/src/cli/commands/daemon.js.map +1 -0
  25. package/dist/src/cli/commands/gateway.js +184 -0
  26. package/dist/src/cli/commands/gateway.js.map +1 -0
  27. package/dist/src/cli/commands/init.js +175 -0
  28. package/dist/src/cli/commands/init.js.map +1 -0
  29. package/dist/src/cli/commands/kobold.js +21 -0
  30. package/dist/src/cli/commands/kobold.js.map +1 -0
  31. package/dist/src/cli/commands/logs.js +27 -0
  32. package/dist/src/cli/commands/logs.js.map +1 -0
  33. package/dist/src/cli/commands/mode.js +121 -0
  34. package/dist/src/cli/commands/mode.js.map +1 -0
  35. package/dist/src/cli/commands/persona.js +261 -0
  36. package/dist/src/cli/commands/persona.js.map +1 -0
  37. package/dist/src/cli/commands/start.js +66 -0
  38. package/dist/src/cli/commands/start.js.map +1 -0
  39. package/dist/src/cli/commands/status.js +117 -0
  40. package/dist/src/cli/commands/status.js.map +1 -0
  41. package/dist/src/cli/commands/stop.js +27 -0
  42. package/dist/src/cli/commands/stop.js.map +1 -0
  43. package/dist/src/cli/commands/system.js +128 -0
  44. package/dist/src/cli/commands/system.js.map +1 -0
  45. package/dist/src/cli/commands/tui.js +103 -0
  46. package/dist/src/cli/commands/tui.js.map +1 -0
  47. package/dist/src/cli/commands/update.js +133 -0
  48. package/dist/src/cli/commands/update.js.map +1 -0
  49. package/dist/src/cli/extensions/discord.js +113 -0
  50. package/dist/src/cli/extensions/discord.js.map +1 -0
  51. package/dist/src/cli/extensions/env.js +91 -0
  52. package/dist/src/cli/extensions/env.js.map +1 -0
  53. package/dist/src/cli/extensions/heartbeat.js +78 -0
  54. package/dist/src/cli/extensions/heartbeat.js.map +1 -0
  55. package/dist/src/cli/index.js +24 -0
  56. package/dist/src/cli/index.js.map +1 -0
  57. package/dist/src/cli/program.js +70 -0
  58. package/dist/src/cli/program.js.map +1 -0
  59. package/dist/src/cli/repl.js +184 -0
  60. package/dist/src/cli/repl.js.map +1 -0
  61. package/dist/src/cli/shared/discord-service.js +102 -0
  62. package/dist/src/cli/shared/discord-service.js.map +1 -0
  63. package/dist/src/config/index.js +10 -0
  64. package/dist/src/config/index.js.map +1 -0
  65. package/dist/src/config/loader.js +401 -0
  66. package/dist/src/config/loader.js.map +1 -0
  67. package/dist/src/config/paths.js +84 -0
  68. package/dist/src/config/paths.js.map +1 -0
  69. package/dist/src/config/types.js +8 -0
  70. package/dist/src/config/types.js.map +1 -0
  71. package/dist/src/context-detector.js +60 -0
  72. package/dist/src/context-detector.js.map +1 -0
  73. package/dist/src/discord/index.js +376 -0
  74. package/dist/src/discord/index.js.map +1 -0
  75. package/dist/src/event-bus/index.js +97 -0
  76. package/dist/src/event-bus/index.js.map +1 -0
  77. package/dist/src/extensions/command-args.js +68 -0
  78. package/dist/src/extensions/command-args.js.map +1 -0
  79. package/dist/src/extensions/core/agent-registry-extension.js +541 -0
  80. package/dist/src/extensions/core/agent-registry-extension.js.map +1 -0
  81. package/dist/src/extensions/core/agent-worker.js +148 -0
  82. package/dist/src/extensions/core/agent-worker.js.map +1 -0
  83. package/dist/src/extensions/core/compaction-safeguard.js +154 -0
  84. package/dist/src/extensions/core/compaction-safeguard.js.map +1 -0
  85. package/dist/src/extensions/core/confirm-destructive.js +43 -0
  86. package/dist/src/extensions/core/confirm-destructive.js.map +1 -0
  87. package/dist/src/extensions/core/context-aware-extension.js +124 -0
  88. package/dist/src/extensions/core/context-aware-extension.js.map +1 -0
  89. package/dist/src/extensions/core/context-pruning/extension.js +124 -0
  90. package/dist/src/extensions/core/context-pruning/extension.js.map +1 -0
  91. package/dist/src/extensions/core/context-pruning/pruner.js +312 -0
  92. package/dist/src/extensions/core/context-pruning/pruner.js.map +1 -0
  93. package/dist/src/extensions/core/context-pruning/runtime.js +48 -0
  94. package/dist/src/extensions/core/context-pruning/runtime.js.map +1 -0
  95. package/dist/src/extensions/core/context-pruning/settings.js +105 -0
  96. package/dist/src/extensions/core/context-pruning/settings.js.map +1 -0
  97. package/dist/src/extensions/core/dirty-repo-guard.js +42 -0
  98. package/dist/src/extensions/core/dirty-repo-guard.js.map +1 -0
  99. package/dist/src/extensions/core/discord-channel-extension.js +205 -0
  100. package/dist/src/extensions/core/discord-channel-extension.js.map +1 -0
  101. package/dist/src/extensions/core/discord-extension.js +142 -0
  102. package/dist/src/extensions/core/discord-extension.js.map +1 -0
  103. package/dist/src/extensions/core/env-loader-extension.js +157 -0
  104. package/dist/src/extensions/core/env-loader-extension.js.map +1 -0
  105. package/dist/src/extensions/core/fileops-extension.js +699 -0
  106. package/dist/src/extensions/core/fileops-extension.js.map +1 -0
  107. package/dist/src/extensions/core/gateway-extension.js +730 -0
  108. package/dist/src/extensions/core/gateway-extension.js.map +1 -0
  109. package/dist/src/extensions/core/git-checkpoint.js +46 -0
  110. package/dist/src/extensions/core/git-checkpoint.js.map +1 -0
  111. package/dist/src/extensions/core/handoff-extension.js +206 -0
  112. package/dist/src/extensions/core/handoff-extension.js.map +1 -0
  113. package/dist/src/extensions/core/heartbeat-extension.js +373 -0
  114. package/dist/src/extensions/core/heartbeat-extension.js.map +1 -0
  115. package/dist/src/extensions/core/mcp-extension.js +413 -0
  116. package/dist/src/extensions/core/mcp-extension.js.map +1 -0
  117. package/dist/src/extensions/core/mode-manager-extension.js +562 -0
  118. package/dist/src/extensions/core/mode-manager-extension.js.map +1 -0
  119. package/dist/src/extensions/core/multi-channel-extension.js +435 -0
  120. package/dist/src/extensions/core/multi-channel-extension.js.map +1 -0
  121. package/dist/src/extensions/core/ollama-provider-extension.js +66 -0
  122. package/dist/src/extensions/core/ollama-provider-extension.js.map +1 -0
  123. package/dist/src/extensions/core/onboarding-extension.js +122 -0
  124. package/dist/src/extensions/core/onboarding-extension.js.map +1 -0
  125. package/dist/src/extensions/core/persona-loader-extension.js +139 -0
  126. package/dist/src/extensions/core/persona-loader-extension.js.map +1 -0
  127. package/dist/src/extensions/core/pi-notify-extension.js +70 -0
  128. package/dist/src/extensions/core/pi-notify-extension.js.map +1 -0
  129. package/dist/src/extensions/core/protected-paths.js +24 -0
  130. package/dist/src/extensions/core/protected-paths.js.map +1 -0
  131. package/dist/src/extensions/core/questionnaire-extension.js +242 -0
  132. package/dist/src/extensions/core/questionnaire-extension.js.map +1 -0
  133. package/dist/src/extensions/core/self-update-extension.js +181 -0
  134. package/dist/src/extensions/core/self-update-extension.js.map +1 -0
  135. package/dist/src/extensions/core/session-bridge-extension.js +78 -0
  136. package/dist/src/extensions/core/session-bridge-extension.js.map +1 -0
  137. package/dist/src/extensions/core/session-manager-extension.js +319 -0
  138. package/dist/src/extensions/core/session-manager-extension.js.map +1 -0
  139. package/dist/src/extensions/core/session-name-extension.js +88 -0
  140. package/dist/src/extensions/core/session-name-extension.js.map +1 -0
  141. package/dist/src/extensions/core/session-pruning-extension.js +480 -0
  142. package/dist/src/extensions/core/session-pruning-extension.js.map +1 -0
  143. package/dist/src/extensions/core/task-manager-extension.js +661 -0
  144. package/dist/src/extensions/core/task-manager-extension.js.map +1 -0
  145. package/dist/src/extensions/core/update-extension.js +438 -0
  146. package/dist/src/extensions/core/update-extension.js.map +1 -0
  147. package/dist/src/extensions/core/websearch-extension.js +463 -0
  148. package/dist/src/extensions/core/websearch-extension.js.map +1 -0
  149. package/dist/src/extensions/index.js +5 -0
  150. package/dist/src/extensions/index.js.map +1 -0
  151. package/dist/src/extensions/loader.js +80 -0
  152. package/dist/src/extensions/loader.js.map +1 -0
  153. package/dist/src/gateway/index.js +353 -0
  154. package/dist/src/gateway/index.js.map +1 -0
  155. package/dist/src/index.js +150 -0
  156. package/dist/src/index.js.map +1 -0
  157. package/dist/src/llm/anthropic.js +86 -0
  158. package/dist/src/llm/anthropic.js.map +1 -0
  159. package/dist/src/llm/index.js +9 -0
  160. package/dist/src/llm/index.js.map +1 -0
  161. package/dist/src/llm/ollama.js +113 -0
  162. package/dist/src/llm/ollama.js.map +1 -0
  163. package/dist/src/llm/router.js +145 -0
  164. package/dist/src/llm/router.js.map +1 -0
  165. package/dist/src/llm/types.js +7 -0
  166. package/dist/src/llm/types.js.map +1 -0
  167. package/dist/src/memory/index.js +5 -0
  168. package/dist/src/memory/index.js.map +1 -0
  169. package/dist/src/memory/store.js +91 -0
  170. package/dist/src/memory/store.js.map +1 -0
  171. package/dist/src/pi-config.js +80 -0
  172. package/dist/src/pi-config.js.map +1 -0
  173. package/dist/src/skills/builtin/file.js +184 -0
  174. package/dist/src/skills/builtin/file.js.map +1 -0
  175. package/dist/src/skills/builtin/shell.js +100 -0
  176. package/dist/src/skills/builtin/shell.js.map +1 -0
  177. package/dist/src/skills/builtin/subagent.js +62 -0
  178. package/dist/src/skills/builtin/subagent.js.map +1 -0
  179. package/dist/src/skills/hello.js +42 -0
  180. package/dist/src/skills/hello.js.map +1 -0
  181. package/dist/src/skills/index.js +11 -0
  182. package/dist/src/skills/index.js.map +1 -0
  183. package/dist/src/skills/loader.js +382 -0
  184. package/dist/src/skills/loader.js.map +1 -0
  185. package/dist/src/skills/types.js +8 -0
  186. package/dist/src/skills/types.js.map +1 -0
  187. package/dist/src/utils/working-dir.js +71 -0
  188. package/dist/src/utils/working-dir.js.map +1 -0
  189. package/package.json +77 -0
  190. package/skills/1password/SKILL.md +70 -0
  191. package/skills/1password/references/cli-examples.md +29 -0
  192. package/skills/1password/references/get-started.md +17 -0
  193. package/skills/apple-notes/SKILL.md +77 -0
  194. package/skills/apple-reminders/SKILL.md +118 -0
  195. package/skills/bear-notes/SKILL.md +107 -0
  196. package/skills/blogwatcher/SKILL.md +69 -0
  197. package/skills/blucli/SKILL.md +47 -0
  198. package/skills/bluebubbles/SKILL.md +131 -0
  199. package/skills/camsnap/SKILL.md +45 -0
  200. package/skills/canvas/SKILL.md +198 -0
  201. package/skills/clawhub/SKILL.md +77 -0
  202. package/skills/coding-agent/SKILL.md +284 -0
  203. package/skills/discord/SKILL.md +197 -0
  204. package/skills/eightctl/SKILL.md +50 -0
  205. package/skills/food-order/SKILL.md +48 -0
  206. package/skills/gemini/SKILL.md +43 -0
  207. package/skills/gh-issues/SKILL.md +865 -0
  208. package/skills/gifgrep/SKILL.md +79 -0
  209. package/skills/github/SKILL.md +163 -0
  210. package/skills/gog/SKILL.md +116 -0
  211. package/skills/goplaces/SKILL.md +52 -0
  212. package/skills/healthcheck/SKILL.md +245 -0
  213. package/skills/himalaya/SKILL.md +257 -0
  214. package/skills/himalaya/references/configuration.md +184 -0
  215. package/skills/himalaya/references/message-composition.md +199 -0
  216. package/skills/imsg/SKILL.md +122 -0
  217. package/skills/mcporter/SKILL.md +61 -0
  218. package/skills/model-usage/SKILL.md +69 -0
  219. package/skills/model-usage/references/codexbar-cli.md +33 -0
  220. package/skills/model-usage/scripts/model_usage.py +310 -0
  221. package/skills/nano-banana-pro/SKILL.md +58 -0
  222. package/skills/nano-banana-pro/scripts/generate_image.py +184 -0
  223. package/skills/nano-pdf/SKILL.md +38 -0
  224. package/skills/notion/SKILL.md +172 -0
  225. package/skills/obsidian/SKILL.md +81 -0
  226. package/skills/openai-image-gen/SKILL.md +89 -0
  227. package/skills/openai-image-gen/scripts/gen.py +240 -0
  228. package/skills/openai-whisper/SKILL.md +38 -0
  229. package/skills/openai-whisper-api/SKILL.md +52 -0
  230. package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
  231. package/skills/openhue/SKILL.md +112 -0
  232. package/skills/oracle/SKILL.md +125 -0
  233. package/skills/ordercli/SKILL.md +78 -0
  234. package/skills/peekaboo/SKILL.md +190 -0
  235. package/skills/sag/SKILL.md +87 -0
  236. package/skills/session-logs/SKILL.md +115 -0
  237. package/skills/sherpa-onnx-tts/SKILL.md +103 -0
  238. package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
  239. package/skills/skill-creator/SKILL.md +370 -0
  240. package/skills/skill-creator/license.txt +202 -0
  241. package/skills/skill-creator/scripts/init_skill.py +378 -0
  242. package/skills/skill-creator/scripts/package_skill.py +111 -0
  243. package/skills/skill-creator/scripts/quick_validate.py +101 -0
  244. package/skills/slack/SKILL.md +144 -0
  245. package/skills/songsee/SKILL.md +49 -0
  246. package/skills/sonoscli/SKILL.md +46 -0
  247. package/skills/spotify-player/SKILL.md +64 -0
  248. package/skills/summarize/SKILL.md +87 -0
  249. package/skills/things-mac/SKILL.md +86 -0
  250. package/skills/tmux/SKILL.md +153 -0
  251. package/skills/tmux/scripts/find-sessions.sh +112 -0
  252. package/skills/tmux/scripts/wait-for-text.sh +83 -0
  253. package/skills/trello/SKILL.md +95 -0
  254. package/skills/video-frames/SKILL.md +46 -0
  255. package/skills/video-frames/scripts/frame.sh +81 -0
  256. package/skills/voice-call/SKILL.md +45 -0
  257. package/skills/wacli/SKILL.md +72 -0
  258. package/skills/weather/SKILL.md +112 -0
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Heartbeat Extension - Koclaw-style Periodic Check-ins
3
+ *
4
+ * Monitors agent health via periodic LLM check-ins using HEARTBEAT.md.
5
+ * This is a simplified implementation inspired by koclaw/openclaw.
6
+ *
7
+ * How it works:
8
+ * 1. Config from kobold.json (agents.defaults.heartbeat)
9
+ * 2. Agent reads HEARTBEAT.md and decides if action is needed
10
+ * 3. If response is HEARTBEAT_OK (with optional short text), it's suppressed
11
+ *
12
+ * Configuration in kobold.json:
13
+ * {
14
+ * "agents": {
15
+ * "defaults": {
16
+ * "heartbeat": {
17
+ * "enabled": true,
18
+ * "every": "30m",
19
+ * "prompt": "Read HEARTBEAT.md...",
20
+ * "ackMaxChars": 300,
21
+ * "activeHours": { "start": "09:00", "end": "22:00" }
22
+ * }
23
+ * }
24
+ * }
25
+ * }
26
+ *
27
+ * For users: Edit HEARTBEAT.md in your workspace to customize checks.
28
+ */
29
+ import * as fs from "node:fs/promises";
30
+ import * as path from "node:path";
31
+ import { loadConfig } from "../../config/loader.js";
32
+ // Constants matching koclaw conventions
33
+ const HEARTBEAT_TOKEN = "HEARTBEAT_OK";
34
+ const DEFAULT_EVERY = "30m";
35
+ const DEFAULT_ACK_MAX_CHARS = 300;
36
+ const HEARTBEAT_FILENAME = "HEARTBEAT.md";
37
+ function getDefaultConfig() {
38
+ return {
39
+ enabled: true,
40
+ every: DEFAULT_EVERY,
41
+ prompt: `Read HEARTBEAT.md if it exists. Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply ${HEARTBEAT_TOKEN}.`,
42
+ ackMaxChars: DEFAULT_ACK_MAX_CHARS,
43
+ activeHours: null,
44
+ };
45
+ }
46
+ // Parse duration string to milliseconds
47
+ function parseDurationMs(duration) {
48
+ const regex = /^(?:(\d+)h)?\s*(?:(\d+)m)?\s?(?:(\d+)s?)?$/i;
49
+ const match = duration.trim().match(regex);
50
+ if (!match) {
51
+ console.warn(`[Heartbeat] Invalid duration "${duration}", using default`);
52
+ return 30 * 60 * 1000;
53
+ }
54
+ const hours = parseInt(match[1] || "0", 10);
55
+ const minutes = parseInt(match[2] || "0", 10);
56
+ const seconds = parseInt(match[3] || "0", 10);
57
+ return ((hours * 60 + minutes) * 60 + seconds) * 1000;
58
+ }
59
+ // Read HEARTBEAT.md from workspace
60
+ async function readHeartbeatFile(workspacePath) {
61
+ try {
62
+ const filePath = path.join(workspacePath, HEARTBEAT_FILENAME);
63
+ const content = await fs.readFile(filePath, "utf-8");
64
+ return content;
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ // Check if content is effectively empty
71
+ function isEffectivelyEmpty(content) {
72
+ const lines = content.split("\n");
73
+ for (const line of lines) {
74
+ const trimmed = line.trim();
75
+ if (!trimmed)
76
+ continue;
77
+ if (/^#+\s/.test(trimmed))
78
+ continue;
79
+ if (/^[-*+]\s*(\[[\sXx]\]\s*)?$/.test(trimmed))
80
+ continue;
81
+ return false;
82
+ }
83
+ return true;
84
+ }
85
+ // Check if currently within active hours
86
+ function isWithinActiveHours(config) {
87
+ if (!config.activeHours)
88
+ return true;
89
+ const now = new Date();
90
+ const timeStr = now.toLocaleTimeString("en-US", {
91
+ hour12: false,
92
+ hour: "2-digit",
93
+ minute: "2-digit",
94
+ });
95
+ const [currentHour, currentMin] = timeStr.split(":").map(Number);
96
+ const [startHour, startMin] = config.activeHours.start.split(":").map(Number);
97
+ const [endHour, endMin] = config.activeHours.end.split(":").map(Number);
98
+ const currentMinutes = currentHour * 60 + currentMin;
99
+ const startMinutes = startHour * 60 + startMin;
100
+ const endMinutes = endHour * 60 + endMin;
101
+ if (startMinutes === endMinutes)
102
+ return true;
103
+ if (startMinutes > endMinutes) {
104
+ return currentMinutes >= startMinutes || currentMinutes < endMinutes;
105
+ }
106
+ return currentMinutes >= startMinutes && currentMinutes < endMinutes;
107
+ }
108
+ // Find workspace directory
109
+ async function findWorkspaceDir() {
110
+ try {
111
+ await fs.access(HEARTBEAT_FILENAME);
112
+ return process.cwd();
113
+ }
114
+ catch {
115
+ // not found
116
+ }
117
+ let current = process.cwd();
118
+ for (let i = 0; i < 5; i++) {
119
+ const parent = path.dirname(current);
120
+ if (parent === current)
121
+ break;
122
+ current = parent;
123
+ try {
124
+ await fs.access(path.join(current, HEARTBEAT_FILENAME));
125
+ return current;
126
+ }
127
+ catch {
128
+ continue;
129
+ }
130
+ }
131
+ return process.cwd();
132
+ }
133
+ async function fileExists(filepath) {
134
+ try {
135
+ await fs.access(filepath);
136
+ return true;
137
+ }
138
+ catch {
139
+ return false;
140
+ }
141
+ }
142
+ export default function heartbeatExtension(pi) {
143
+ const startTime = Date.now();
144
+ let checkInterval = null;
145
+ let lastHeartbeat = 0;
146
+ // Load config from kobold.json
147
+ let config = getDefaultConfig();
148
+ let configLoaded = false;
149
+ async function loadHeartbeatConfig() {
150
+ try {
151
+ const snapshot = await loadConfig();
152
+ const hbConfig = snapshot.config.agents?.defaults?.heartbeat;
153
+ if (hbConfig) {
154
+ config = {
155
+ enabled: hbConfig.enabled ?? config.enabled,
156
+ every: hbConfig.every ?? config.every,
157
+ prompt: hbConfig.prompt ?? config.prompt,
158
+ ackMaxChars: hbConfig.ackMaxChars ?? config.ackMaxChars,
159
+ activeHours: hbConfig.activeHours ?? config.activeHours,
160
+ };
161
+ }
162
+ configLoaded = true;
163
+ }
164
+ catch (err) {
165
+ console.warn('[Heartbeat] Failed to load config, using defaults:', err);
166
+ }
167
+ }
168
+ // Load config on startup
169
+ loadHeartbeatConfig().then(() => {
170
+ console.log(`[Heartbeat] Config loaded (enabled: ${config.enabled}, every: ${config.every})`);
171
+ });
172
+ // Reload config on environment changes
173
+ pi.on("session_start", async () => {
174
+ await loadHeartbeatConfig();
175
+ console.log(`[Heartbeat] Extension loaded (enabled: ${config.enabled}, every: ${config.every})`);
176
+ });
177
+ // ═══════════════════════════════════════════════════════════════
178
+ // REGISTER HEARTBEAT TOOL
179
+ // ═══════════════════════════════════════════════════════════════
180
+ pi.registerTool({
181
+ name: "heartbeat_check",
182
+ label: "Heartbeat Check",
183
+ description: "Perform a heartbeat check by reading HEARTBEAT.md. Returns the content for review. Reply with HEARTBEAT_OK if nothing needs attention, or describe the issue.",
184
+ // @ts-ignore TSchema mismatch
185
+ parameters: {
186
+ type: "object",
187
+ properties: {
188
+ force: {
189
+ type: "boolean",
190
+ description: "Force a check even if recently performed",
191
+ },
192
+ },
193
+ },
194
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
195
+ const { force } = params;
196
+ const now = Date.now();
197
+ const everyMs = parseDurationMs(config.every);
198
+ // Check active hours
199
+ if (!isWithinActiveHours(config)) {
200
+ return {
201
+ content: [{
202
+ type: "text",
203
+ text: "Heartbeat skipped - outside active hours. Reply HEARTBEAT_OK to acknowledge.",
204
+ }],
205
+ details: { skipped: true, reason: "outside_active_hours" },
206
+ };
207
+ }
208
+ if (!force && now - lastHeartbeat < everyMs) {
209
+ return {
210
+ content: [{
211
+ type: "text",
212
+ text: "Heartbeat skipped - too soon since last check. Reply HEARTBEAT_OK to acknowledge.",
213
+ }],
214
+ details: { skipped: true, lastHeartbeat },
215
+ };
216
+ }
217
+ const workspaceDir = await findWorkspaceDir();
218
+ const heartbeatContent = await readHeartbeatFile(workspaceDir);
219
+ if (heartbeatContent === null) {
220
+ return {
221
+ content: [{
222
+ type: "text",
223
+ text: `No HEARTBEAT.md found in workspace. Reply ${HEARTBEAT_TOKEN} to acknowledge, or create the file to enable heartbeat checks.`,
224
+ }],
225
+ details: { fileExists: false },
226
+ };
227
+ }
228
+ if (isEffectivelyEmpty(heartbeatContent)) {
229
+ return {
230
+ content: [{
231
+ type: "text",
232
+ text: "HEARTBEAT.md is effectively empty (no actionable items). Reply HEARTBEAT_OK to acknowledge.",
233
+ }],
234
+ details: { fileExists: true, isEmpty: true },
235
+ };
236
+ }
237
+ lastHeartbeat = now;
238
+ return {
239
+ content: [{
240
+ type: "text",
241
+ text: `HEARTBEAT.md content:\n\n${heartbeatContent}\n\nReview the checklist above. If nothing needs attention, reply with "${HEARTBEAT_TOKEN}". Otherwise, describe what needs attention.`,
242
+ }],
243
+ details: { fileExists: true, isEmpty: false },
244
+ };
245
+ },
246
+ });
247
+ // ═══════════════════════════════════════════════════════════════
248
+ // COMMANDS
249
+ // ═══════════════════════════════════════════════════════════════
250
+ pi.registerCommand("heartbeat", {
251
+ description: "Show heartbeat status or trigger manual check",
252
+ handler: async (args, ctx) => {
253
+ await loadHeartbeatConfig();
254
+ if (args.trim() === "now" || args.trim() === "check") {
255
+ if (!isWithinActiveHours(config)) {
256
+ ctx.ui.notify("❤️ Heartbeat skipped - outside active hours.", "info");
257
+ return;
258
+ }
259
+ ctx.ui.notify("Running heartbeat check...", "info");
260
+ const workspaceDir = await findWorkspaceDir();
261
+ const content = await readHeartbeatFile(workspaceDir);
262
+ if (content === null) {
263
+ ctx.ui.notify("❤️ No HEARTBEAT.md found. Run /heartbeat-init to create one.", "warning");
264
+ }
265
+ else if (isEffectivelyEmpty(content)) {
266
+ ctx.ui.notify("❤️ HEARTBEAT.md is empty - no actionable items.", "info");
267
+ }
268
+ else {
269
+ ctx.ui.notify(`❤️ HEARTBEAT.md found:\n${content.substring(0, 200)}...`, "info");
270
+ }
271
+ return;
272
+ }
273
+ const lines = [
274
+ "❤️ Heartbeat Configuration",
275
+ `Enabled: ${config.enabled ? "✅" : "❌"}`,
276
+ `Interval: ${config.every}`,
277
+ `Ack Max Chars: ${config.ackMaxChars}`,
278
+ ];
279
+ if (config.activeHours) {
280
+ lines.push(`Active Hours: ${config.activeHours.start} - ${config.activeHours.end}`);
281
+ }
282
+ const workspaceDir = await findWorkspaceDir();
283
+ const hbExists = await fileExists(path.join(workspaceDir, HEARTBEAT_FILENAME));
284
+ lines.push(`HEARTBEAT.md: ${hbExists ? "✅ Found" : "⚠️ Not found"}`);
285
+ lines.push("", "Usage:", " /heartbeat now - Run check immediately", " /heartbeat-init - Create HEARTBEAT.md");
286
+ ctx.ui.notify(lines.join("\n"), "info");
287
+ },
288
+ });
289
+ pi.registerCommand("heartbeat-init", {
290
+ description: "Create HEARTBEAT.md template in workspace",
291
+ handler: async (_args, ctx) => {
292
+ const workspaceDir = await findWorkspaceDir();
293
+ const filePath = path.join(workspaceDir, HEARTBEAT_FILENAME);
294
+ if (await fileExists(filePath)) {
295
+ ctx.ui.notify(`HEARTBEAT.md already exists at ${filePath}`, "warning");
296
+ return;
297
+ }
298
+ const template = `# Heartbeat Checklist
299
+
300
+ <!--
301
+ This file controls what the agent checks during periodic heartbeats.
302
+ Keep it short and actionable. If this file is empty or only contains headers,
303
+ heartbeats will be skipped to save tokens.
304
+
305
+ The agent reads this file every heartbeat interval (default: 30m).
306
+ If nothing needs attention, it replies with HEARTBEAT_OK.
307
+ If something needs attention, it describes the issue without the token.
308
+ -->
309
+
310
+ ## Regular Checks
311
+
312
+ - [ ] Review any pending tasks in your workspace
313
+ - [ ] Check for blocked items needing human input
314
+ - [ ] Verify system status (if monitoring anything)
315
+
316
+ ## Context-Aware
317
+
318
+ Only check these when relevant:
319
+ - [ ] Active sessions that haven't been updated recently
320
+ - [ ] Scheduled tasks or reminders
321
+ - [ ] Follow-ups from previous conversations
322
+
323
+ ## Response Protocol
324
+
325
+ If nothing needs attention → reply: \`HEARTBEAT_OK\`
326
+ If something needs attention → brief alert message (no HEARTBEAT_OK token)
327
+ `;
328
+ try {
329
+ await fs.writeFile(filePath, template, "utf-8");
330
+ ctx.ui.notify(`✅ Created ${filePath}`, "info");
331
+ }
332
+ catch (err) {
333
+ const errMsg = err instanceof Error ? err.message : String(err);
334
+ ctx.ui.notify(`❌ Failed to create file: ${errMsg}`, "error");
335
+ }
336
+ },
337
+ });
338
+ // ═══════════════════════════════════════════════════════════════
339
+ // AUTO-HEARTBEAT SCHEDULER
340
+ // ═══════════════════════════════════════════════════════════════
341
+ pi.on("session_start", async () => {
342
+ await loadHeartbeatConfig();
343
+ if (!config.enabled) {
344
+ console.log("[Heartbeat] Disabled in config, not starting scheduler");
345
+ return;
346
+ }
347
+ const everyMs = parseDurationMs(config.every);
348
+ // Schedule periodic checks
349
+ checkInterval = setInterval(async () => {
350
+ if (!isWithinActiveHours(config)) {
351
+ console.log("[Heartbeat] Skipped - outside active hours");
352
+ return;
353
+ }
354
+ const workspaceDir = await findWorkspaceDir();
355
+ const content = await readHeartbeatFile(workspaceDir);
356
+ // Skip if no file or empty file
357
+ if (content === null || isEffectivelyEmpty(content)) {
358
+ return;
359
+ }
360
+ // Use the tool to trigger heartbeat
361
+ pi.sendUserMessage("Perform a heartbeat check using the heartbeat_check tool.", { deliverAs: "followUp" });
362
+ }, everyMs);
363
+ console.log(`[Heartbeat] Scheduler started (every ${config.every})`);
364
+ });
365
+ pi.on("session_shutdown", async () => {
366
+ if (checkInterval) {
367
+ clearInterval(checkInterval);
368
+ console.log("[Heartbeat] Scheduler stopped");
369
+ }
370
+ });
371
+ console.log("[Heartbeat] Extension loaded");
372
+ }
373
+ //# sourceMappingURL=heartbeat-extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat-extension.js","sourceRoot":"","sources":["../../../../src/extensions/core/heartbeat-extension.ts"],"names":[],"mappings":"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"}