va-claw 0.1.3 → 0.2.0

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 (56) hide show
  1. package/README.md +130 -0
  2. package/README.zh-CN.md +478 -0
  3. package/dist/va-claw-bundle.mjs +584 -64
  4. package/docs/releases/v0.1.3.md +20 -0
  5. package/package.json +1 -1
  6. package/packages/cli/dist/.tsbuildinfo +1 -1
  7. package/packages/cli/dist/claw-store.d.ts +38 -0
  8. package/packages/cli/dist/claw-store.d.ts.map +1 -0
  9. package/packages/cli/dist/claw-store.js +139 -0
  10. package/packages/cli/dist/claw-store.js.map +1 -0
  11. package/packages/cli/dist/deps.d.ts.map +1 -1
  12. package/packages/cli/dist/deps.js +2 -1
  13. package/packages/cli/dist/deps.js.map +1 -1
  14. package/packages/cli/dist/handlers.d.ts +20 -0
  15. package/packages/cli/dist/handlers.d.ts.map +1 -1
  16. package/packages/cli/dist/handlers.js +159 -1
  17. package/packages/cli/dist/handlers.js.map +1 -1
  18. package/packages/cli/dist/index.d.ts +2 -2
  19. package/packages/cli/dist/index.d.ts.map +1 -1
  20. package/packages/cli/dist/index.js +2 -2
  21. package/packages/cli/dist/index.js.map +1 -1
  22. package/packages/cli/dist/install-files.d.ts +1 -0
  23. package/packages/cli/dist/install-files.d.ts.map +1 -1
  24. package/packages/cli/dist/install-files.js +3 -0
  25. package/packages/cli/dist/install-files.js.map +1 -1
  26. package/packages/cli/dist/output.d.ts +2 -0
  27. package/packages/cli/dist/output.d.ts.map +1 -1
  28. package/packages/cli/dist/output.js +21 -0
  29. package/packages/cli/dist/output.js.map +1 -1
  30. package/packages/cli/dist/program.d.ts.map +1 -1
  31. package/packages/cli/dist/program.js +40 -1
  32. package/packages/cli/dist/program.js.map +1 -1
  33. package/packages/cli/dist/test-helpers.d.ts.map +1 -1
  34. package/packages/cli/dist/test-helpers.js +1 -0
  35. package/packages/cli/dist/test-helpers.js.map +1 -1
  36. package/packages/cli/dist/types.d.ts +1 -0
  37. package/packages/cli/dist/types.d.ts.map +1 -1
  38. package/packages/daemon/dist/.tsbuildinfo +1 -1
  39. package/packages/daemon/dist/wake-cycle.d.ts +19 -2
  40. package/packages/daemon/dist/wake-cycle.d.ts.map +1 -1
  41. package/packages/daemon/dist/wake-cycle.js +209 -30
  42. package/packages/daemon/dist/wake-cycle.js.map +1 -1
  43. package/packages/identity/dist/.tsbuildinfo +1 -1
  44. package/packages/identity/dist/defaults.d.ts +1 -0
  45. package/packages/identity/dist/defaults.d.ts.map +1 -1
  46. package/packages/identity/dist/defaults.js +6 -0
  47. package/packages/identity/dist/defaults.js.map +1 -1
  48. package/packages/identity/dist/render.d.ts.map +1 -1
  49. package/packages/identity/dist/render.js +27 -0
  50. package/packages/identity/dist/render.js.map +1 -1
  51. package/packages/identity/dist/types.d.ts +1 -0
  52. package/packages/identity/dist/types.d.ts.map +1 -1
  53. package/skills/claw-fleet-protocol.md +58 -0
  54. package/skills/install-va-claw.md +4 -0
  55. package/skills/memory-protocol.md +48 -0
  56. package/skills/migrate-to-va-claw.md +141 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // packages/cli/dist/deps.js
4
- import { spawnSync as spawnSync4 } from "node:child_process";
4
+ import { spawnSync as spawnSync3 } from "node:child_process";
5
5
  import { readFile as readFile4 } from "node:fs/promises";
6
6
  import { createInterface as createInterface2 } from "node:readline/promises";
7
7
 
@@ -28,11 +28,13 @@ import { dirname } from "node:path";
28
28
 
29
29
  // packages/identity/dist/defaults.js
30
30
  var DEFAULT_LOOP_INTERVAL = "0 * * * *";
31
+ var DEFAULT_WAKE_TIMEOUT_MS = 3e5;
31
32
  var DEFAULT_CONFIG = {
32
33
  name: "va-claw",
33
34
  persona: "A pragmatic CLI identity that values clarity, rigor, and continuity.",
34
35
  systemPrompt: "You are va-claw. Be direct, honest about uncertainty, and keep actions aligned with the saved identity.",
35
36
  wakePrompt: "Wake up, load the saved identity, and continue from the most recent remembered state.",
37
+ wakeTimeoutMs: DEFAULT_WAKE_TIMEOUT_MS,
36
38
  loopInterval: DEFAULT_LOOP_INTERVAL,
37
39
  channels: {
38
40
  discord: {
@@ -58,6 +60,9 @@ function pickString(value, fallback) {
58
60
  function pickBoolean(value, fallback) {
59
61
  return typeof value === "boolean" ? value : fallback;
60
62
  }
63
+ function pickNumber(value, fallback) {
64
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
65
+ }
61
66
  function getDefaultIdentity() {
62
67
  return { ...DEFAULT_CONFIG };
63
68
  }
@@ -73,6 +78,7 @@ function normalizeConfig(input) {
73
78
  persona: pickString(Reflect.get(data, "persona"), base.persona),
74
79
  systemPrompt: pickString(Reflect.get(data, "systemPrompt"), base.systemPrompt),
75
80
  wakePrompt: pickString(Reflect.get(data, "wakePrompt"), base.wakePrompt),
81
+ wakeTimeoutMs: pickNumber(Reflect.get(data, "wakeTimeoutMs"), base.wakeTimeoutMs ?? DEFAULT_WAKE_TIMEOUT_MS),
76
82
  loopInterval: pickString(Reflect.get(data, "loopInterval"), base.loopInterval),
77
83
  channels: {
78
84
  discord: {
@@ -136,6 +142,19 @@ function toClaudeMdSnippet(config) {
136
142
  normalized.persona,
137
143
  "",
138
144
  textBlock("System Prompt:", normalized.systemPrompt),
145
+ "",
146
+ "Operational protocol:",
147
+ "- Check long-running claw status with `va-claw protocol`.",
148
+ "- For readable text use `va-claw protocol --text`.",
149
+ "- Manage claws with `va-claw claw list | add | set | remove | heartbeat`.",
150
+ '- If asked "va/claw \u5728\u5E72\u4EC0\u4E48" / "my claws are doing what" / similar fleet questions, call `va-claw protocol --text` and answer from it.',
151
+ '- If the user asks a non-technical phrasing like "\u73B0\u5728\u90FD\u5728\u5E72\u561B", interpret it as a fleet status request and run `va-claw protocol --text`.',
152
+ "",
153
+ "Memory protocol:",
154
+ '- At session start, run `va-claw memory recall "<current topic or task>"` before answering.',
155
+ '- After important work, run `va-claw memory memorize "<key>" "<essence>" --tags <tags> --importance <0.0-1.0>`.',
156
+ "- Use importance `0.8-1.0` for decisions or hard-won knowledge; use `0.5` for routine outputs.",
157
+ "- Do not memorize every message; store only things worth remembering across sessions.",
139
158
  "<!-- va-claw:identity:end -->"
140
159
  ].join("\n");
141
160
  }
@@ -151,7 +170,21 @@ function toCodexSystemPrompt(config) {
151
170
  "Wake prompt:",
152
171
  normalized.wakePrompt,
153
172
  "",
154
- `Loop interval: ${normalized.loopInterval}`
173
+ `Wake timeout (ms): ${normalized.wakeTimeoutMs ?? 3e5}`,
174
+ "",
175
+ `Loop interval: ${normalized.loopInterval}`,
176
+ "",
177
+ "Operational protocol:",
178
+ "Use `va-claw protocol` in terminal to get long-running claw state.",
179
+ 'Use `va-claw protocol --text` when a user asks in plain language, including Chinese phrases like "va/claw \u5728\u5E72\u4EC0\u4E48".',
180
+ "Use `va-claw claw list` and `va-claw claw set <name> --status <status>` to manage claws.",
181
+ "If the user asks what claws are doing, run `va-claw protocol --text` and summarize only from that output.",
182
+ "",
183
+ "Memory protocol:",
184
+ 'At session start, run `va-claw memory recall "<current topic or task>"` before answering.',
185
+ 'After important work, run `va-claw memory memorize "<key>" "<essence>" --tags <tags> --importance <0.0-1.0>`.',
186
+ "Use importance `0.8-1.0` for decisions or hard-won knowledge; use `0.5` for routine outputs.",
187
+ "Do not memorize every message; store only things worth remembering across sessions."
155
188
  ].join("\n");
156
189
  }
157
190
 
@@ -924,7 +957,10 @@ var E = class {
924
957
  };
925
958
 
926
959
  // packages/daemon/dist/wake-cycle.js
927
- import { spawnSync as spawnSync2 } from "node:child_process";
960
+ import { spawn } from "node:child_process";
961
+ import { appendFile, mkdir as mkdir3 } from "node:fs/promises";
962
+ import { homedir as homedir4 } from "node:os";
963
+ import { dirname as dirname3, resolve as resolve3 } from "node:path";
928
964
 
929
965
  // packages/memory/dist/default-store.js
930
966
  import { homedir as homedir2 } from "node:os";
@@ -1861,46 +1897,211 @@ function looksLikePath(value) {
1861
1897
 
1862
1898
  // packages/daemon/dist/wake-cycle.js
1863
1899
  var DEFAULT_WARN2 = (message) => console.warn(message);
1900
+ var DEFAULT_WAKE_TIMEOUT_MS2 = 3e5;
1901
+ var OUTPUT_TAIL_LIMIT = 2e3;
1902
+ var OUTPUT_BUFFER_LIMIT = 10 * 1024 * 1024;
1903
+ var wakeRunning = false;
1864
1904
  async function runWakeCycle(config, deps = {}) {
1865
- const adapter = await (deps.detect ?? detectCliAdapter)({ warn: deps.warn });
1866
- if (!adapter) {
1905
+ if (wakeRunning) {
1906
+ (deps.warn ?? DEFAULT_WARN2)("[va-claw/daemon] skipping wake: previous wake still running");
1867
1907
  return null;
1868
1908
  }
1869
- const prompt = await resolveWakePrompt(config, deps);
1870
- const spawnOptions = {
1871
- cwd: process.cwd(),
1872
- encoding: "utf8",
1873
- env: {
1874
- ...readProcessEnv(),
1875
- CLAUDECODE: void 0,
1876
- CLAUDE_CODE_SESSION: void 0
1877
- },
1878
- maxBuffer: 10 * 1024 * 1024
1879
- };
1880
- const result = (deps.spawn ?? spawnSync2)(adapter.command, [...adapter.args, prompt], spawnOptions);
1881
- if (result.status !== 0) {
1882
- (deps.warn ?? DEFAULT_WARN2)(`[va-claw/daemon] ${adapter.name} wake failed: ${readStderr(result.stderr)}`);
1909
+ wakeRunning = true;
1910
+ try {
1911
+ return await _runWakeCycleInner(config, deps);
1912
+ } finally {
1913
+ wakeRunning = false;
1914
+ }
1915
+ }
1916
+ async function _runWakeCycleInner(config, deps = {}) {
1917
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
1918
+ const startedAt = now();
1919
+ let combinedOutput = "";
1920
+ try {
1921
+ const adapter = await (deps.detect ?? detectCliAdapter)({ warn: deps.warn });
1922
+ if (!adapter) {
1923
+ return null;
1924
+ }
1925
+ const prompt = await resolveWakePrompt(config, deps);
1926
+ const timeoutMs = resolveWakeTimeoutMs(config);
1927
+ const wakeArgs = [...adapter.args, prompt];
1928
+ const wakeOptions = {
1929
+ cwd: process.cwd(),
1930
+ env: {
1931
+ ...readProcessEnv(),
1932
+ CLAUDECODE: void 0,
1933
+ CLAUDE_CODE_SESSION: void 0
1934
+ }
1935
+ };
1936
+ const result = await (deps.executeWake ?? executeWakeProcess)(adapter.command, wakeArgs, wakeOptions, timeoutMs);
1937
+ combinedOutput = result.combinedOutput;
1938
+ const finishedAt = now();
1939
+ const durationMs = finishedAt.getTime() - startedAt.getTime();
1940
+ if (result.exitCode !== 0) {
1941
+ await writeWakeLogSafe({
1942
+ ts: startedAt.toISOString(),
1943
+ duration_ms: Math.max(0, durationMs),
1944
+ exit_code: result.exitCode,
1945
+ output_tail: tailOutput(result.combinedOutput)
1946
+ }, deps);
1947
+ (deps.warn ?? DEFAULT_WARN2)(result.exitCode === "timeout" ? `[va-claw/daemon] ${adapter.name} wake timed out after ${timeoutMs}ms.` : `[va-claw/daemon] ${adapter.name} wake failed: ${readFailureOutput(result.stderr, result.combinedOutput)}`);
1948
+ return null;
1949
+ }
1950
+ await (deps.storeMemory ?? store)(result.stdout, {
1951
+ source: "va-claw-daemon",
1952
+ kind: "wake",
1953
+ cli: adapter.name,
1954
+ identity: config.name,
1955
+ wokeAt: finishedAt.toISOString()
1956
+ });
1957
+ await writeWakeLogSafe({
1958
+ ts: startedAt.toISOString(),
1959
+ duration_ms: Math.max(0, durationMs),
1960
+ exit_code: 0,
1961
+ output_tail: tailOutput(result.combinedOutput)
1962
+ }, deps);
1963
+ return finishedAt;
1964
+ } catch (error) {
1965
+ const failedAt = now();
1966
+ await writeWakeLogSafe({
1967
+ ts: startedAt.toISOString(),
1968
+ duration_ms: Math.max(0, failedAt.getTime() - startedAt.getTime()),
1969
+ exit_code: "crash",
1970
+ output_tail: tailOutput(combinedOutput)
1971
+ }, deps);
1972
+ (deps.warn ?? DEFAULT_WARN2)(`[va-claw/daemon] wake crashed: ${String(error)}`);
1883
1973
  return null;
1884
1974
  }
1885
- const wokeAt = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
1886
- await (deps.storeMemory ?? store)(result.stdout ?? "", {
1887
- source: "va-claw-daemon",
1888
- kind: "wake",
1889
- cli: adapter.name,
1890
- identity: config.name,
1891
- wokeAt: wokeAt.toISOString()
1892
- });
1893
- return wokeAt;
1894
1975
  }
1895
- function readStderr(stderr) {
1896
- if (typeof stderr === "string" && stderr.trim() !== "") {
1897
- return stderr.trim();
1976
+ async function executeWakeProcess(command, args, options, timeoutMs) {
1977
+ let child;
1978
+ try {
1979
+ child = spawn(command, args, {
1980
+ cwd: options.cwd,
1981
+ env: options.env,
1982
+ stdio: ["ignore", "pipe", "pipe"]
1983
+ });
1984
+ } catch (error) {
1985
+ return {
1986
+ combinedOutput: String(error),
1987
+ exitCode: "spawn_error",
1988
+ stderr: String(error),
1989
+ stdout: ""
1990
+ };
1898
1991
  }
1899
- return "unknown error";
1992
+ let stdout2 = "";
1993
+ let stderr = "";
1994
+ let combinedOutput = "";
1995
+ let settled = false;
1996
+ let combinedSize = 0;
1997
+ let stdoutSize = 0;
1998
+ let stderrSize = 0;
1999
+ child.stdout.on("data", (chunk) => {
2000
+ const text2 = String(chunk);
2001
+ if (stdoutSize < OUTPUT_BUFFER_LIMIT) {
2002
+ stdout2 += text2;
2003
+ stdoutSize += text2.length;
2004
+ }
2005
+ if (combinedSize < OUTPUT_BUFFER_LIMIT) {
2006
+ combinedOutput += text2;
2007
+ combinedSize += text2.length;
2008
+ }
2009
+ });
2010
+ child.stderr.on("data", (chunk) => {
2011
+ const text2 = String(chunk);
2012
+ if (stderrSize < OUTPUT_BUFFER_LIMIT) {
2013
+ stderr += text2;
2014
+ stderrSize += text2.length;
2015
+ }
2016
+ if (combinedSize < OUTPUT_BUFFER_LIMIT) {
2017
+ combinedOutput += text2;
2018
+ combinedSize += text2.length;
2019
+ }
2020
+ });
2021
+ return await new Promise((resolve5) => {
2022
+ const timer = setTimeout(() => {
2023
+ if (settled) {
2024
+ return;
2025
+ }
2026
+ settled = true;
2027
+ clearTimeout(timer);
2028
+ child.kill("SIGTERM");
2029
+ const killTimer = setTimeout(() => {
2030
+ try {
2031
+ child.kill("SIGKILL");
2032
+ } catch {
2033
+ }
2034
+ }, 5e3);
2035
+ const timeoutResult = {
2036
+ combinedOutput,
2037
+ exitCode: "timeout",
2038
+ stderr,
2039
+ stdout: stdout2
2040
+ };
2041
+ child.once("close", () => {
2042
+ clearTimeout(killTimer);
2043
+ resolve5(timeoutResult);
2044
+ });
2045
+ }, timeoutMs);
2046
+ child.on("error", (error) => {
2047
+ if (settled) {
2048
+ return;
2049
+ }
2050
+ settled = true;
2051
+ clearTimeout(timer);
2052
+ const message = String(error);
2053
+ stderr = stderr === "" ? message : `${stderr}
2054
+ ${message}`;
2055
+ combinedOutput += message;
2056
+ resolve5({
2057
+ combinedOutput,
2058
+ exitCode: "spawn_error",
2059
+ stderr,
2060
+ stdout: stdout2
2061
+ });
2062
+ });
2063
+ child.on("close", (code) => {
2064
+ if (settled) {
2065
+ return;
2066
+ }
2067
+ settled = true;
2068
+ clearTimeout(timer);
2069
+ resolve5({
2070
+ combinedOutput,
2071
+ exitCode: typeof code === "number" ? code : 1,
2072
+ stderr,
2073
+ stdout: stdout2
2074
+ });
2075
+ });
2076
+ });
1900
2077
  }
1901
2078
  function readProcessEnv() {
1902
2079
  return process.env ?? {};
1903
2080
  }
2081
+ function readFailureOutput(stderr, combinedOutput) {
2082
+ const candidate = stderr.trim() !== "" ? stderr : combinedOutput;
2083
+ const text2 = candidate.trim();
2084
+ return text2 === "" ? "unknown error" : tailOutput(text2, 200);
2085
+ }
2086
+ function resolveWakeTimeoutMs(config) {
2087
+ return typeof config.wakeTimeoutMs === "number" && Number.isFinite(config.wakeTimeoutMs) && config.wakeTimeoutMs > 0 ? config.wakeTimeoutMs : DEFAULT_WAKE_TIMEOUT_MS2;
2088
+ }
2089
+ function tailOutput(text2, limit = OUTPUT_TAIL_LIMIT) {
2090
+ return text2.length <= limit ? text2 : text2.slice(-limit);
2091
+ }
2092
+ async function writeWakeLogSafe(entry, deps) {
2093
+ try {
2094
+ await (deps.writeWakeLog ?? writeWakeLog)(entry);
2095
+ } catch (error) {
2096
+ (deps.warn ?? DEFAULT_WARN2)(`[va-claw/daemon] failed to write wake.log: ${String(error)}`);
2097
+ }
2098
+ }
2099
+ async function writeWakeLog(entry) {
2100
+ const logPath = resolve3(homedir4(), ".va-claw", "wake.log");
2101
+ await mkdir3(dirname3(logPath), { recursive: true });
2102
+ await appendFile(logPath, `${JSON.stringify(entry)}
2103
+ `, "utf8");
2104
+ }
1904
2105
  async function resolveWakePrompt(config, deps) {
1905
2106
  try {
1906
2107
  const skills = await (deps.listSkills ?? listSkills)();
@@ -1970,29 +2171,29 @@ async function getDaemonStatus() {
1970
2171
  }
1971
2172
 
1972
2173
  // packages/daemon/dist/service.js
1973
- import { mkdir as mkdir3, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
1974
- import { dirname as dirname4 } from "node:path";
1975
- import { spawnSync as spawnSync3 } from "node:child_process";
2174
+ import { mkdir as mkdir4, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
2175
+ import { dirname as dirname5 } from "node:path";
2176
+ import { spawnSync as spawnSync2 } from "node:child_process";
1976
2177
 
1977
2178
  // packages/daemon/dist/service-files.js
1978
- import { homedir as homedir4 } from "node:os";
1979
- import { dirname as dirname3, join as join4, resolve as resolve3 } from "node:path";
2179
+ import { homedir as homedir5 } from "node:os";
2180
+ import { dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
1980
2181
  import { fileURLToPath } from "node:url";
1981
2182
  function createServiceDefinition(type) {
1982
- const packageRoot = resolve3(dirname3(fileURLToPath(import.meta.url)), "..");
1983
- const repoRoot = resolve3(packageRoot, "../..");
2183
+ const packageRoot = resolve4(dirname4(fileURLToPath(import.meta.url)), "..");
2184
+ const repoRoot = resolve4(packageRoot, "../..");
1984
2185
  const runnerPath = join4(packageRoot, "dist", "runner.js");
1985
2186
  const nodePath = process.execPath;
1986
2187
  if (type === "launchd") {
1987
2188
  return {
1988
- path: join4(homedir4(), "Library", "LaunchAgents", "com.va-claw.daemon.plist"),
2189
+ path: join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist"),
1989
2190
  content: renderLaunchdPlist(repoRoot, [nodePath, runnerPath]),
1990
2191
  command: "launchctl",
1991
- args: ["load", join4(homedir4(), "Library", "LaunchAgents", "com.va-claw.daemon.plist")]
2192
+ args: ["load", join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist")]
1992
2193
  };
1993
2194
  }
1994
2195
  return {
1995
- path: join4(homedir4(), ".config", "systemd", "user", "va-claw.service"),
2196
+ path: join4(homedir5(), ".config", "systemd", "user", "va-claw.service"),
1996
2197
  content: renderSystemdUnit(repoRoot, [nodePath, runnerPath]),
1997
2198
  command: "systemctl",
1998
2199
  args: ["--user", "enable", "--now", "va-claw.service"]
@@ -2002,7 +2203,7 @@ function createUninstallCommand(type) {
2002
2203
  if (type === "launchd") {
2003
2204
  return {
2004
2205
  command: "launchctl",
2005
- args: ["unload", join4(homedir4(), "Library", "LaunchAgents", "com.va-claw.daemon.plist")]
2206
+ args: ["unload", join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist")]
2006
2207
  };
2007
2208
  }
2008
2209
  return {
@@ -2057,7 +2258,7 @@ function quoteSystemdArg(value) {
2057
2258
  // packages/daemon/dist/service.js
2058
2259
  async function installDaemonService(type) {
2059
2260
  const definition = createServiceDefinition(type);
2060
- await mkdir3(dirname4(definition.path), { recursive: true });
2261
+ await mkdir4(dirname5(definition.path), { recursive: true });
2061
2262
  await writeFile3(definition.path, definition.content, "utf8");
2062
2263
  if (type === "systemd") {
2063
2264
  runCommand("systemctl", ["--user", "daemon-reload"]);
@@ -2074,7 +2275,7 @@ async function uninstallDaemonService(type) {
2074
2275
  }
2075
2276
  }
2076
2277
  function runCommand(command, args, allowFailure = false) {
2077
- const result = spawnSync3(command, args, { encoding: "utf8" });
2278
+ const result = spawnSync2(command, args, { encoding: "utf8" });
2078
2279
  if (allowFailure || result.status === 0) {
2079
2280
  return;
2080
2281
  }
@@ -2083,9 +2284,9 @@ function runCommand(command, args, allowFailure = false) {
2083
2284
  }
2084
2285
 
2085
2286
  // packages/cli/dist/install-files.js
2086
- import { access as access2, mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
2087
- import { homedir as homedir5 } from "node:os";
2088
- import { dirname as dirname5, join as join5 } from "node:path";
2287
+ import { access as access2, mkdir as mkdir5, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
2288
+ import { homedir as homedir6 } from "node:os";
2289
+ import { dirname as dirname6, join as join5 } from "node:path";
2089
2290
  var CLAUDE_MARKERS = {
2090
2291
  start: "<!-- va-claw:identity:start -->",
2091
2292
  end: "<!-- va-claw:identity:end -->"
@@ -2094,15 +2295,18 @@ var CODEX_MARKERS = {
2094
2295
  start: "<!-- va-claw:codex:start -->",
2095
2296
  end: "<!-- va-claw:codex:end -->"
2096
2297
  };
2097
- function resolveClaudeMdPath(home = homedir5()) {
2298
+ function resolveClaudeMdPath(home = homedir6()) {
2098
2299
  return join5(home, ".claude", "CLAUDE.md");
2099
2300
  }
2100
- function resolveCodexInstructionsPath(home = homedir5()) {
2301
+ function resolveCodexInstructionsPath(home = homedir6()) {
2101
2302
  return join5(home, ".codex", "instructions.md");
2102
2303
  }
2103
- function resolveMemoryDbPath(home = homedir5()) {
2304
+ function resolveMemoryDbPath(home = homedir6()) {
2104
2305
  return join5(home, ".va-claw", "memory.db");
2105
2306
  }
2307
+ function resolveClawRegistryPath(home = homedir6()) {
2308
+ return join5(home, ".va-claw", "claws.json");
2309
+ }
2106
2310
  async function fileExists(path) {
2107
2311
  try {
2108
2312
  await access2(path);
@@ -2114,7 +2318,7 @@ async function fileExists(path) {
2114
2318
  async function upsertManagedBlock(path, block, markers) {
2115
2319
  const current = (await readOptionalFile(path)).trim();
2116
2320
  const next = appendManagedBlock(current, block, markers);
2117
- await mkdir4(dirname5(path), { recursive: true });
2321
+ await mkdir5(dirname6(path), { recursive: true });
2118
2322
  await writeFile4(path, next, "utf8");
2119
2323
  }
2120
2324
  async function removeManagedBlock(path, markers) {
@@ -2123,7 +2327,7 @@ async function removeManagedBlock(path, markers) {
2123
2327
  return;
2124
2328
  }
2125
2329
  const next = stripManagedBlock(current, markers).trim();
2126
- await mkdir4(dirname5(path), { recursive: true });
2330
+ await mkdir5(dirname6(path), { recursive: true });
2127
2331
  await writeFile4(path, next === "" ? "" : `${next}
2128
2332
  `, "utf8");
2129
2333
  }
@@ -2165,8 +2369,9 @@ function createDefaultCliDeps() {
2165
2369
  codexPath: resolveCodexInstructionsPath(),
2166
2370
  configPath: DEFAULT_CONFIG_PATH,
2167
2371
  memoryDbPath: resolveMemoryDbPath(),
2372
+ clawRegistryPath: resolveClawRegistryPath(),
2168
2373
  platform: process.platform,
2169
- spawnSync: spawnSync4,
2374
+ spawnSync: spawnSync3,
2170
2375
  stdout: process.stdout,
2171
2376
  stderr: process.stderr,
2172
2377
  fileExists,
@@ -2269,6 +2474,25 @@ function formatSkills(skills) {
2269
2474
  ` path: ${skill.path}`
2270
2475
  ].join("\n")).join("\n\n");
2271
2476
  }
2477
+ function formatClawDefinitions(claws) {
2478
+ if (claws.length === 0) {
2479
+ return "No claws registered.";
2480
+ }
2481
+ return claws.map((claw) => {
2482
+ const lines = [
2483
+ `${claw.name} [${claw.status}]`,
2484
+ ` goal: ${claw.goal || "(no goal)"}`,
2485
+ ` cli: ${claw.cliCommand || "va-claw"}`,
2486
+ ` tags: ${claw.tags.join(", ") || "(none)"}`,
2487
+ ` note: ${claw.note || "(none)"}`,
2488
+ ` updatedAt: ${claw.updatedAt}`
2489
+ ];
2490
+ if (claw.lastSeenAt) {
2491
+ lines.push(` lastSeenAt: ${claw.lastSeenAt}`);
2492
+ }
2493
+ return lines.join("\n");
2494
+ }).join("\n\n");
2495
+ }
2272
2496
  function writeLine(stream, message) {
2273
2497
  stream.write(`${message}
2274
2498
  `);
@@ -2276,7 +2500,7 @@ function writeLine(stream, message) {
2276
2500
 
2277
2501
  // packages/cli/dist/wait.js
2278
2502
  async function waitForStopSignal(stop) {
2279
- await new Promise((resolve4, reject) => {
2503
+ await new Promise((resolve5, reject) => {
2280
2504
  let stopping = false;
2281
2505
  const cleanup = () => {
2282
2506
  process.off("SIGINT", onSignal);
@@ -2289,7 +2513,7 @@ async function waitForStopSignal(stop) {
2289
2513
  stopping = true;
2290
2514
  void stop().then(() => {
2291
2515
  cleanup();
2292
- resolve4();
2516
+ resolve5();
2293
2517
  }, (error) => {
2294
2518
  cleanup();
2295
2519
  reject(error);
@@ -2420,7 +2644,7 @@ function parseMetadata2(raw) {
2420
2644
  }
2421
2645
 
2422
2646
  // packages/cli/dist/platform.js
2423
- import { homedir as homedir6 } from "node:os";
2647
+ import { homedir as homedir7 } from "node:os";
2424
2648
  import { join as join6 } from "node:path";
2425
2649
  function detectServiceType(platform) {
2426
2650
  if (platform === "darwin") {
@@ -2431,27 +2655,163 @@ function detectServiceType(platform) {
2431
2655
  }
2432
2656
  throw new Error(`Unsupported platform for va-claw daemon service: ${platform}`);
2433
2657
  }
2434
- function probeServiceRunning(type, spawnSync5) {
2658
+ function probeServiceRunning(type, spawnSync4) {
2435
2659
  if (type === "launchd") {
2436
- return spawnSync5("launchctl", ["list", "com.va-claw.daemon"], { encoding: "utf8" }).status === 0;
2660
+ return spawnSync4("launchctl", ["list", "com.va-claw.daemon"], { encoding: "utf8" }).status === 0;
2437
2661
  }
2438
- const result = spawnSync5("systemctl", ["--user", "is-active", "va-claw.service"], {
2662
+ const result = spawnSync4("systemctl", ["--user", "is-active", "va-claw.service"], {
2439
2663
  encoding: "utf8"
2440
2664
  });
2441
2665
  return result.status === 0 && result.stdout.trim() === "active";
2442
2666
  }
2443
- function stopInstalledService(type, spawnSync5) {
2667
+ function stopInstalledService(type, spawnSync4) {
2444
2668
  if (type === "launchd") {
2445
- spawnSync5("launchctl", ["unload", resolveLaunchdPath()], { encoding: "utf8" });
2669
+ spawnSync4("launchctl", ["unload", resolveLaunchdPath()], { encoding: "utf8" });
2446
2670
  return;
2447
2671
  }
2448
- spawnSync5("systemctl", ["--user", "stop", "va-claw.service"], { encoding: "utf8" });
2672
+ spawnSync4("systemctl", ["--user", "stop", "va-claw.service"], { encoding: "utf8" });
2449
2673
  }
2450
2674
  function resolveLaunchdPath() {
2451
- return join6(homedir6(), "Library", "LaunchAgents", "com.va-claw.daemon.plist");
2675
+ return join6(homedir7(), "Library", "LaunchAgents", "com.va-claw.daemon.plist");
2676
+ }
2677
+
2678
+ // packages/cli/dist/claw-store.js
2679
+ import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
2680
+ import { dirname as dirname7 } from "node:path";
2681
+ var DEFAULT_CLAW_STATUS = "idle";
2682
+ var KNOWN_STATUSES = /* @__PURE__ */ new Set(["running", "working", "idle", "waiting", "error", "offline", "stopped"]);
2683
+ var REGISTRY_VERSION = 1;
2684
+ var DEFAULT_NOW = () => /* @__PURE__ */ new Date();
2685
+ function isClawStatus(value) {
2686
+ return KNOWN_STATUSES.has(value ?? "");
2687
+ }
2688
+ function pickString2(value, fallback = "") {
2689
+ return typeof value === "string" ? value.trim() : fallback;
2690
+ }
2691
+ function pickTags(value) {
2692
+ if (!Array.isArray(value)) {
2693
+ return [];
2694
+ }
2695
+ return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0);
2696
+ }
2697
+ function normalizeStatus(value) {
2698
+ return isClawStatus(typeof value === "string" ? value : void 0) ? value : DEFAULT_CLAW_STATUS;
2699
+ }
2700
+ function validateClawStatus(value) {
2701
+ if (typeof value !== "string") {
2702
+ return null;
2703
+ }
2704
+ const normalized = value.trim().toLowerCase();
2705
+ return isClawStatus(normalized) ? normalized : null;
2706
+ }
2707
+ function normalizeClawDefinition(raw) {
2708
+ if (!raw || typeof raw !== "object") {
2709
+ return null;
2710
+ }
2711
+ const name = pickString2(Reflect.get(raw, "name"));
2712
+ if (name === "") {
2713
+ return null;
2714
+ }
2715
+ const createdAt = pickString2(Reflect.get(raw, "createdAt"), (/* @__PURE__ */ new Date(0)).toISOString());
2716
+ const updatedAt = pickString2(Reflect.get(raw, "updatedAt"), createdAt);
2717
+ return {
2718
+ name,
2719
+ goal: pickString2(Reflect.get(raw, "goal")),
2720
+ status: normalizeStatus(Reflect.get(raw, "status")),
2721
+ cliCommand: pickString2(Reflect.get(raw, "cliCommand"), "va-claw"),
2722
+ note: pickString2(Reflect.get(raw, "note")),
2723
+ tags: pickTags(Reflect.get(raw, "tags")),
2724
+ lastSeenAt: pickString2(Reflect.get(raw, "lastSeenAt"), "").trim() || void 0,
2725
+ createdAt,
2726
+ updatedAt
2727
+ };
2728
+ }
2729
+ function normalizeClawRegistry(raw) {
2730
+ if (!raw || typeof raw !== "object") {
2731
+ return { version: REGISTRY_VERSION, claws: [] };
2732
+ }
2733
+ const version = typeof Reflect.get(raw, "version") === "number" ? Reflect.get(raw, "version") : REGISTRY_VERSION;
2734
+ const rawClaws = Reflect.get(raw, "claws");
2735
+ const claws = Array.isArray(rawClaws) ? rawClaws.map(normalizeClawDefinition).filter(Boolean) : [];
2736
+ return { version: Number(version) > 0 ? Number(version) : REGISTRY_VERSION, claws };
2737
+ }
2738
+ async function listClaws(registryPath) {
2739
+ const registry = await loadClawRegistry(registryPath);
2740
+ return [...registry.claws];
2741
+ }
2742
+ async function registerClaw(registryPath, input, now = DEFAULT_NOW) {
2743
+ const registry = await loadClawRegistry(registryPath);
2744
+ const exists2 = registry.claws.some((entry) => entry.name === input.name);
2745
+ if (exists2) {
2746
+ throw new Error(`Claw already exists: ${input.name}`);
2747
+ }
2748
+ const timestamp = now().toISOString();
2749
+ const claw = {
2750
+ name: input.name,
2751
+ goal: pickString2(input.goal),
2752
+ status: input.status ?? DEFAULT_CLAW_STATUS,
2753
+ cliCommand: pickString2(input.cliCommand, "va-claw"),
2754
+ note: pickString2(input.note),
2755
+ tags: Array.isArray(input.tags) ? input.tags.filter((tag) => tag.trim() !== "") : [],
2756
+ createdAt: timestamp,
2757
+ updatedAt: timestamp
2758
+ };
2759
+ registry.claws = [claw, ...registry.claws];
2760
+ await saveClawRegistry(registryPath, registry);
2761
+ return claw;
2762
+ }
2763
+ async function updateClaw(registryPath, name, updates, now = DEFAULT_NOW) {
2764
+ const registry = await loadClawRegistry(registryPath);
2765
+ const index = registry.claws.findIndex((entry) => entry.name === name);
2766
+ if (index < 0) {
2767
+ return null;
2768
+ }
2769
+ const nowIso = now().toISOString();
2770
+ const current = registry.claws[index];
2771
+ const next = {
2772
+ ...current,
2773
+ goal: updates.goal ?? current.goal,
2774
+ status: updates.status ?? current.status,
2775
+ cliCommand: updates.cliCommand ?? current.cliCommand,
2776
+ note: updates.note ?? current.note,
2777
+ tags: Array.isArray(updates.tags) ? updates.tags.filter((tag) => tag.trim() !== "") : current.tags,
2778
+ lastSeenAt: updates.seen ? nowIso : current.lastSeenAt,
2779
+ updatedAt: nowIso
2780
+ };
2781
+ registry.claws[index] = next;
2782
+ await saveClawRegistry(registryPath, registry);
2783
+ return next;
2784
+ }
2785
+ async function removeClaw(registryPath, name) {
2786
+ const registry = await loadClawRegistry(registryPath);
2787
+ const before = registry.claws.length;
2788
+ registry.claws = registry.claws.filter((entry) => entry.name !== name);
2789
+ if (registry.claws.length === before) {
2790
+ return false;
2791
+ }
2792
+ await saveClawRegistry(registryPath, registry);
2793
+ return true;
2794
+ }
2795
+ async function loadClawRegistry(registryPath) {
2796
+ try {
2797
+ const rawText = await readFile5(registryPath, "utf8");
2798
+ return normalizeClawRegistry(JSON.parse(rawText));
2799
+ } catch (error) {
2800
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
2801
+ return { version: REGISTRY_VERSION, claws: [] };
2802
+ }
2803
+ throw error;
2804
+ }
2805
+ }
2806
+ async function saveClawRegistry(registryPath, registry) {
2807
+ await mkdir6(dirname7(registryPath), { recursive: true });
2808
+ await writeFile5(registryPath, `${JSON.stringify({ ...registry, version: REGISTRY_VERSION }, null, 2)}
2809
+ `, "utf8");
2452
2810
  }
2453
2811
 
2454
2812
  // packages/cli/dist/handlers.js
2813
+ var CLAW_FLEET_PROTOCOL_SKILL_URL = "https://raw.githubusercontent.com/Vadaski/va-claw/main/skills/claw-fleet-protocol.md";
2814
+ var MEMORY_SKILL_URL = "https://raw.githubusercontent.com/Vadaski/va-claw/main/skills/memory-protocol.md";
2455
2815
  async function runInstall(target, deps) {
2456
2816
  const installTarget = normalizeInstallTarget(target);
2457
2817
  const config = await deps.fileExists(deps.configPath) ? await deps.loadIdentity() : await deps.runInstallWizard();
@@ -2468,6 +2828,14 @@ async function runInstall(target, deps) {
2468
2828
  const serviceType = detectServiceType(deps.platform);
2469
2829
  await deps.installDaemonService(serviceType);
2470
2830
  summary.push(`Daemon service: ${serviceType}`);
2831
+ const fleetSkillName = await installFleetProtocolSkill(deps);
2832
+ if (fleetSkillName) {
2833
+ summary.push(`Claw fleet protocol skill: ${fleetSkillName}`);
2834
+ }
2835
+ const memorySkillName = await installMemoryProtocolSkill(deps);
2836
+ if (memorySkillName) {
2837
+ summary.push(`Memory protocol skill: ${memorySkillName}`);
2838
+ }
2471
2839
  for (const line of summary) {
2472
2840
  writeLine(deps.stdout, line);
2473
2841
  }
@@ -2500,6 +2868,150 @@ async function runStatus(deps) {
2500
2868
  writeLine(deps.stdout, `Last wake: ${lastWakeAt ?? "never"}`);
2501
2869
  writeLine(deps.stdout, `Memory entries: ${memoryCount}`);
2502
2870
  }
2871
+ async function runClawStatus(deps) {
2872
+ const clawStatus = await buildProtocolReport(deps);
2873
+ writeLine(deps.stdout, formatClawDefinitions(clawStatus.claws));
2874
+ writeLine(deps.stdout, `Daemon running: ${clawStatus.runtime.running ? "yes" : "no"} | service: ${clawStatus.runtime.serviceRunning ? "running" : "stopped"} | wakeCount: ${clawStatus.runtime.wakeCount}`);
2875
+ }
2876
+ async function runClawList(deps) {
2877
+ const claws = await listClaws(deps.clawRegistryPath);
2878
+ writeLine(deps.stdout, formatClawDefinitions(claws));
2879
+ }
2880
+ async function runClawAdd(name, options, deps) {
2881
+ const normalizedName = name.trim();
2882
+ if (normalizedName === "") {
2883
+ throw new Error("Claw name cannot be empty.");
2884
+ }
2885
+ const status = options.status ? validateClawStatus(options.status) : null;
2886
+ if (options.status && status === null) {
2887
+ throw new Error(`Invalid claw status: ${options.status}`);
2888
+ }
2889
+ const claw = await registerClaw(deps.clawRegistryPath, {
2890
+ name: normalizedName,
2891
+ goal: options.goal,
2892
+ status: status ?? void 0,
2893
+ cliCommand: options.cliCommand,
2894
+ note: options.note,
2895
+ tags: options.tags ? splitCommaList(options.tags) : []
2896
+ });
2897
+ writeLine(deps.stdout, formatClawDefinitions([claw]));
2898
+ }
2899
+ async function runClawUpdate(name, options, deps) {
2900
+ const status = options.status ? validateClawStatus(options.status) : null;
2901
+ const normalizedName = name.trim();
2902
+ if (normalizedName === "") {
2903
+ throw new Error("Claw name cannot be empty.");
2904
+ }
2905
+ if (options.status && status === null) {
2906
+ throw new Error(`Invalid claw status: ${options.status}`);
2907
+ }
2908
+ const patch = {
2909
+ goal: options.goal,
2910
+ status: status ?? void 0,
2911
+ cliCommand: options.cliCommand,
2912
+ note: options.note,
2913
+ tags: options.tags ? splitCommaList(options.tags) : void 0,
2914
+ seen: options.seen === "1" || options.seen === "true" || options.seen === "yes"
2915
+ };
2916
+ const claw = await updateClaw(deps.clawRegistryPath, normalizedName, patch);
2917
+ if (!claw) {
2918
+ writeLine(deps.stdout, `Claw not found: ${name}`);
2919
+ return;
2920
+ }
2921
+ writeLine(deps.stdout, formatClawDefinitions([claw]));
2922
+ }
2923
+ async function runClawRemove(name, deps) {
2924
+ const normalizedName = name.trim();
2925
+ if (normalizedName === "") {
2926
+ throw new Error("Claw name cannot be empty.");
2927
+ }
2928
+ const removed = await removeClaw(deps.clawRegistryPath, normalizedName);
2929
+ writeLine(deps.stdout, removed ? `Removed claw: ${normalizedName}` : `Claw not found: ${normalizedName}`);
2930
+ }
2931
+ async function runClawHeartbeat(name, deps) {
2932
+ const normalizedName = name.trim();
2933
+ if (normalizedName === "") {
2934
+ throw new Error("Claw name cannot be empty.");
2935
+ }
2936
+ const claw = await updateClaw(deps.clawRegistryPath, normalizedName, { seen: true, status: "running" });
2937
+ if (!claw) {
2938
+ writeLine(deps.stdout, `Claw not found: ${name}`);
2939
+ return;
2940
+ }
2941
+ writeLine(deps.stdout, formatClawDefinitions([claw]));
2942
+ }
2943
+ async function runProtocol(deps, textMode = false) {
2944
+ const report = await buildProtocolReport(deps);
2945
+ if (textMode) {
2946
+ writeLine(deps.stdout, `protocol: ${report.protocol}`);
2947
+ writeLine(deps.stdout, `timestamp: ${report.timestamp}`);
2948
+ writeLine(deps.stdout, `daemon.running: ${report.runtime.running}`);
2949
+ writeLine(deps.stdout, `daemon.serviceRunning: ${report.runtime.serviceRunning}`);
2950
+ writeLine(deps.stdout, `daemon.discord: ${report.runtime.discord}`);
2951
+ writeLine(deps.stdout, `daemon.wakeCount: ${report.runtime.wakeCount}`);
2952
+ writeLine(deps.stdout, `daemon.lastWakeAt: ${report.runtime.lastWakeAt ?? "never"}`);
2953
+ writeLine(deps.stdout, `memory.entries: ${report.memory.entries}`);
2954
+ writeLine(deps.stdout, `memory.lastWakeAt: ${report.memory.lastWakeAt ?? "never"}`);
2955
+ writeLine(deps.stdout, "");
2956
+ writeLine(deps.stdout, "Claws:");
2957
+ writeLine(deps.stdout, formatClawDefinitions(report.claws));
2958
+ return;
2959
+ }
2960
+ writeLine(deps.stdout, JSON.stringify(report, null, 2));
2961
+ }
2962
+ async function buildProtocolReport(deps) {
2963
+ const runtime = await deps.getDaemonStatus();
2964
+ const serviceType = safeDetectServiceType(deps.platform);
2965
+ const serviceRunning = serviceType ? probeServiceRunning(serviceType, deps.spawnSync) : false;
2966
+ const runtimeLastWakeAt = runtime.lastWakeAt?.toISOString() ?? null;
2967
+ const fallbackLastWakeAt = await findLastWakeAt(deps.memoryDbPath, deps.fileExists);
2968
+ const memoryCount = await countMemoryEntries(deps.memoryDbPath, deps.fileExists);
2969
+ const claws = await listClaws(deps.clawRegistryPath);
2970
+ return {
2971
+ protocol: "va-claw-claw-protocol-1",
2972
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2973
+ runtime: {
2974
+ running: runtime.running,
2975
+ serviceRunning,
2976
+ discord: runtime.discord,
2977
+ wakeCount: runtime.wakeCount,
2978
+ lastWakeAt: runtimeLastWakeAt ?? fallbackLastWakeAt
2979
+ },
2980
+ memory: {
2981
+ entries: memoryCount,
2982
+ lastWakeAt: runtimeLastWakeAt ?? fallbackLastWakeAt
2983
+ },
2984
+ claws: claws.map((claw) => ({
2985
+ name: claw.name,
2986
+ goal: claw.goal,
2987
+ status: claw.status,
2988
+ cliCommand: claw.cliCommand,
2989
+ note: claw.note,
2990
+ tags: claw.tags,
2991
+ lastSeenAt: claw.lastSeenAt,
2992
+ createdAt: claw.createdAt,
2993
+ updatedAt: claw.updatedAt
2994
+ }))
2995
+ };
2996
+ }
2997
+ async function installFleetProtocolSkill(deps) {
2998
+ return installRemoteSkill(deps, CLAW_FLEET_PROTOCOL_SKILL_URL, "claw-fleet-protocol");
2999
+ }
3000
+ async function installMemoryProtocolSkill(deps) {
3001
+ return installRemoteSkill(deps, MEMORY_SKILL_URL, "memory-protocol");
3002
+ }
3003
+ async function installRemoteSkill(deps, url, name) {
3004
+ try {
3005
+ const response = await fetch(url);
3006
+ if (!response.ok) {
3007
+ return null;
3008
+ }
3009
+ const content = await response.text();
3010
+ return await deps.skillInstall(content, name);
3011
+ } catch {
3012
+ return null;
3013
+ }
3014
+ }
2503
3015
  async function runMemorySearch(query, deps) {
2504
3016
  writeLine(deps.stdout, formatMemoryEntries(await deps.memorySearch(query, 10)));
2505
3017
  }
@@ -2769,6 +3281,7 @@ function createCliProgram(deps = createDefaultCliDeps()) {
2769
3281
  program.command("stop").description("Stop the daemon.").action(async () => runStop(deps));
2770
3282
  program.command("status").description("Show daemon and memory status.").action(async () => runStatus(deps));
2771
3283
  program.command("uninstall").description("Remove daemon service and injected prompts.").action(async () => runUninstall(deps));
3284
+ program.command("protocol").description("Emit a machine-readable protocol summary for agent-facing integrations.").option("--text", "Print a human-readable protocol summary instead of JSON.").action(async (options) => runProtocol(deps, Boolean(options.text)));
2772
3285
  const memory = program.command("memory").description("Memory operations.");
2773
3286
  memory.command("search").description("Search memory.").argument("<query>").action(async (query) => runMemorySearch(query, deps));
2774
3287
  memory.command("memorize").description("Store or update a memory entry by key.").argument("<key>").argument("<essence>").option("--tags <tags>", "Comma-separated tags.").option("--details <details>", "Details text.").option("--importance <importance>", "Importance from 0 to 1.").action((key, essence, options) => runMemoryMemorize(key, essence, options, deps));
@@ -2798,6 +3311,13 @@ function createCliProgram(deps = createDefaultCliDeps()) {
2798
3311
  slack.command("setup").description("Configure Slack bot credentials.").option("--bot-token <token>", "Slack bot token").option("--app-token <token>", "Slack app token").option("--cli-command <command>", "CLI command to invoke for each message").action(async (options) => runSlackChannelSetup(options.botToken, options.appToken, options.cliCommand, deps));
2799
3312
  slack.command("start").description("Start the Slack channel in the foreground.").action(async () => runSlackChannelStart(deps));
2800
3313
  slack.command("status").description("Show Slack channel status.").action(async () => runSlackChannelStatus(deps));
3314
+ const claw = program.command("claw").description("Long-running claw operations.");
3315
+ claw.command("status").description("Show claw status and daemon summary.").action(async () => runClawStatus(deps));
3316
+ claw.command("list").description("List all registered claws.").action(async () => runClawList(deps));
3317
+ claw.command("add").description("Register a long-running claw.").argument("<name>").option("--goal <goal>", "Describe what this claw is responsible for.").option("--status <status>", "running | working | idle | waiting | error | offline | stopped.").option("--cli-command <command>", "Command to execute this claw's actions (default: va-claw).").option("--note <note>", "Single-line note for this claw.").option("--tags <tags>", "Comma-separated tags.").action((name, options) => runClawAdd(name, options, deps));
3318
+ claw.command("set").description("Update a claw's state.").argument("<name>").option("--goal <goal>", "New goal for this claw.").option("--status <status>", "running | working | idle | waiting | error | offline | stopped.").option("--cli-command <command>", "Update command used for this claw.").option("--note <note>", "Update note.").option("--tags <tags>", "Replace tags, comma-separated.").option("--seen", "Mark claw as alive now.").action((name, options) => runClawUpdate(name, { ...options, seen: options.seen ? "true" : void 0 }, deps));
3319
+ claw.command("heartbeat").description("Mark a claw as active and update lastSeenAt.").argument("<name>").action((name) => runClawHeartbeat(name, deps));
3320
+ claw.command("remove").description("Unregister a claw.").argument("<name>").action((name) => runClawRemove(name, deps));
2801
3321
  return program;
2802
3322
  }
2803
3323
  async function runCli(argv = process.argv, deps) {