harnery 0.8.0 → 0.10.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 (234) hide show
  1. package/README.md +2 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +8 -0
  4. package/dist/commands/agents.d.ts +24 -0
  5. package/dist/commands/agents.d.ts.map +1 -1
  6. package/dist/commands/agents.js +244 -13
  7. package/dist/commands/backup.d.ts +5 -4
  8. package/dist/commands/backup.d.ts.map +1 -1
  9. package/dist/commands/backup.js +15 -14
  10. package/dist/commands/browse.d.ts.map +1 -1
  11. package/dist/commands/browse.js +27 -1
  12. package/dist/commands/claude-desktop.d.ts +19 -0
  13. package/dist/commands/claude-desktop.d.ts.map +1 -0
  14. package/dist/commands/claude-desktop.js +168 -0
  15. package/dist/commands/context.d.ts.map +1 -1
  16. package/dist/commands/context.js +149 -1
  17. package/dist/commands/doctor.d.ts.map +1 -1
  18. package/dist/commands/doctor.js +37 -0
  19. package/dist/commands/eml.d.ts +32 -0
  20. package/dist/commands/eml.d.ts.map +1 -1
  21. package/dist/commands/eml.js +16 -3
  22. package/dist/commands/grep.d.ts +35 -2
  23. package/dist/commands/grep.d.ts.map +1 -1
  24. package/dist/commands/grep.js +427 -139
  25. package/dist/commands/harness.d.ts +11 -0
  26. package/dist/commands/harness.d.ts.map +1 -0
  27. package/dist/commands/harness.js +118 -0
  28. package/dist/commands/init.d.ts +15 -5
  29. package/dist/commands/init.d.ts.map +1 -1
  30. package/dist/commands/init.js +125 -14
  31. package/dist/commands/presence.d.ts +9 -4
  32. package/dist/commands/presence.d.ts.map +1 -1
  33. package/dist/commands/presence.js +88 -5
  34. package/dist/commands/relay.d.ts +9 -0
  35. package/dist/commands/relay.d.ts.map +1 -0
  36. package/dist/commands/relay.js +143 -0
  37. package/dist/commands/sync.d.ts.map +1 -1
  38. package/dist/commands/sync.js +5 -0
  39. package/dist/commands/workflow.d.ts +4 -0
  40. package/dist/commands/workflow.d.ts.map +1 -0
  41. package/dist/commands/workflow.js +82 -0
  42. package/dist/core/agents/canonical-emit.d.ts +15 -0
  43. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  44. package/dist/core/agents/canonical-emit.js +41 -3
  45. package/dist/core/agents/cli.js +14 -5
  46. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  47. package/dist/core/agents/render/prompt-context.js +44 -3
  48. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  49. package/dist/core/agents/render/session-context.js +26 -1
  50. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  51. package/dist/core/agents/rules/claim-conflict.js +26 -1
  52. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  53. package/dist/core/agents/rules/commit-conflict.js +4 -37
  54. package/dist/core/agents/rules/stop-hook.d.ts +8 -0
  55. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  56. package/dist/core/agents/rules/stop-hook.js +8 -0
  57. package/dist/core/agents/session-events.d.ts.map +1 -1
  58. package/dist/core/agents/session-events.js +16 -43
  59. package/dist/core/agents/state/heartbeat-projector.d.ts +2 -0
  60. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  61. package/dist/core/agents/state/heartbeat-projector.js +24 -4
  62. package/dist/core/agents/state/heartbeat-writer.d.ts +16 -2
  63. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  64. package/dist/core/agents/state/heartbeat-writer.js +24 -7
  65. package/dist/core/agents/state/names.d.ts +38 -3
  66. package/dist/core/agents/state/names.d.ts.map +1 -1
  67. package/dist/core/agents/state/names.js +46 -7
  68. package/dist/core/agents/state/pidmap.d.ts +5 -1
  69. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  70. package/dist/core/agents/state/pidmap.js +58 -2
  71. package/dist/core/agents/state/stale-sweep.d.ts +3 -2
  72. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  73. package/dist/core/agents/state/stale-sweep.js +6 -6
  74. package/dist/core/config.d.ts +77 -11
  75. package/dist/core/config.d.ts.map +1 -1
  76. package/dist/core/config.js +210 -25
  77. package/dist/core/context/index.d.ts +144 -0
  78. package/dist/core/context/index.d.ts.map +1 -0
  79. package/dist/core/context/index.js +380 -0
  80. package/dist/core/harnesses/bench.d.ts +29 -0
  81. package/dist/core/harnesses/bench.d.ts.map +1 -0
  82. package/dist/core/harnesses/bench.js +151 -0
  83. package/dist/core/harnesses/index.d.ts +9 -0
  84. package/dist/core/harnesses/index.d.ts.map +1 -0
  85. package/dist/core/harnesses/index.js +4 -0
  86. package/dist/core/harnesses/profiles.d.ts +62 -0
  87. package/dist/core/harnesses/profiles.d.ts.map +1 -0
  88. package/dist/core/harnesses/profiles.js +115 -0
  89. package/dist/core/harnesses/registry.d.ts +15 -0
  90. package/dist/core/harnesses/registry.d.ts.map +1 -0
  91. package/dist/core/harnesses/registry.js +113 -0
  92. package/dist/core/harnesses/types.d.ts +64 -0
  93. package/dist/core/harnesses/types.d.ts.map +1 -0
  94. package/dist/core/harnesses/types.js +20 -0
  95. package/dist/core/hooks/cli.js +309 -66
  96. package/dist/core/hooks/events/schema.d.ts +42 -1
  97. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  98. package/dist/core/hooks/harness/events.d.ts +7 -0
  99. package/dist/core/hooks/harness/events.d.ts.map +1 -1
  100. package/dist/core/hooks/harness/events.js +4 -0
  101. package/dist/core/hooks/harness/parse.d.ts +1 -1
  102. package/dist/core/hooks/harness/parse.d.ts.map +1 -1
  103. package/dist/core/hooks/harness/parse.js +4 -0
  104. package/dist/core/hooks/resolve/coord-root.d.ts +11 -0
  105. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -1
  106. package/dist/core/hooks/resolve/coord-root.js +28 -6
  107. package/dist/core/presence/blob.d.ts +40 -0
  108. package/dist/core/presence/blob.d.ts.map +1 -0
  109. package/dist/core/presence/blob.js +91 -0
  110. package/dist/core/presence/git.d.ts +68 -0
  111. package/dist/core/presence/git.d.ts.map +1 -0
  112. package/dist/core/presence/git.js +173 -0
  113. package/dist/core/presence/index.d.ts +74 -0
  114. package/dist/core/presence/index.d.ts.map +1 -0
  115. package/dist/core/presence/index.js +224 -0
  116. package/dist/core/presence/relay-client.d.ts +39 -0
  117. package/dist/core/presence/relay-client.d.ts.map +1 -0
  118. package/dist/core/presence/relay-client.js +295 -0
  119. package/dist/core/presence/relay-protocol.d.ts +94 -0
  120. package/dist/core/presence/relay-protocol.d.ts.map +1 -0
  121. package/dist/core/presence/relay-protocol.js +169 -0
  122. package/dist/core/workflow/billing.d.ts +48 -0
  123. package/dist/core/workflow/billing.d.ts.map +1 -0
  124. package/dist/core/workflow/billing.js +108 -0
  125. package/dist/core/workflow/child-env.d.ts +30 -0
  126. package/dist/core/workflow/child-env.d.ts.map +1 -0
  127. package/dist/core/workflow/child-env.js +43 -0
  128. package/dist/core/workflow/engine.d.ts +21 -0
  129. package/dist/core/workflow/engine.d.ts.map +1 -0
  130. package/dist/core/workflow/engine.js +349 -0
  131. package/dist/core/workflow/harnesses.d.ts +17 -0
  132. package/dist/core/workflow/harnesses.d.ts.map +1 -0
  133. package/dist/core/workflow/harnesses.js +21 -0
  134. package/dist/core/workflow/spawn-claude.d.ts +25 -0
  135. package/dist/core/workflow/spawn-claude.d.ts.map +1 -0
  136. package/dist/core/workflow/spawn-claude.js +103 -0
  137. package/dist/core/workflow/spawn-codex.d.ts +22 -0
  138. package/dist/core/workflow/spawn-codex.d.ts.map +1 -0
  139. package/dist/core/workflow/spawn-codex.js +88 -0
  140. package/dist/core/workflow/spawn-cursor.d.ts +29 -0
  141. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -0
  142. package/dist/core/workflow/spawn-cursor.js +91 -0
  143. package/dist/core/workflow/types.d.ts +152 -0
  144. package/dist/core/workflow/types.d.ts.map +1 -0
  145. package/dist/core/workflow/types.js +9 -0
  146. package/dist/core/workflow/validate.d.ts +15 -0
  147. package/dist/core/workflow/validate.d.ts.map +1 -0
  148. package/dist/core/workflow/validate.js +70 -0
  149. package/dist/lib/browser/client.d.ts +16 -0
  150. package/dist/lib/browser/client.d.ts.map +1 -1
  151. package/dist/lib/browser/client.js +22 -0
  152. package/dist/lib/browser/index.d.ts +1 -0
  153. package/dist/lib/browser/index.d.ts.map +1 -1
  154. package/dist/lib/browser/index.js +1 -0
  155. package/dist/lib/browser/launch-args.d.ts +21 -0
  156. package/dist/lib/browser/launch-args.d.ts.map +1 -0
  157. package/dist/lib/browser/launch-args.js +32 -0
  158. package/dist/lib/claude-desktop.d.ts +105 -0
  159. package/dist/lib/claude-desktop.d.ts.map +1 -0
  160. package/dist/lib/claude-desktop.js +217 -0
  161. package/dist/lib/docs-lint.d.ts.map +1 -1
  162. package/dist/lib/docs-lint.js +5 -2
  163. package/dist/lib/identities/assume.d.ts +51 -0
  164. package/dist/lib/identities/assume.d.ts.map +1 -0
  165. package/dist/lib/identities/assume.js +274 -0
  166. package/dist/lib/identities/index.d.ts +7 -7
  167. package/dist/lib/identities/index.d.ts.map +1 -1
  168. package/dist/lib/identities/index.js +24 -24
  169. package/dist/lib/instructions/templates.d.ts.map +1 -1
  170. package/dist/lib/instructions/templates.js +15 -2
  171. package/package.json +12 -1
  172. package/schemas/config.schema.json +88 -19
  173. package/src/commander.ts +8 -0
  174. package/src/commands/agents.ts +280 -14
  175. package/src/commands/backup.ts +17 -16
  176. package/src/commands/browse.ts +32 -0
  177. package/src/commands/claude-desktop.ts +215 -0
  178. package/src/commands/context.ts +174 -1
  179. package/src/commands/doctor.ts +44 -0
  180. package/src/commands/eml.ts +22 -4
  181. package/src/commands/grep.ts +535 -142
  182. package/src/commands/harness.ts +147 -0
  183. package/src/commands/init.ts +138 -17
  184. package/src/commands/presence.ts +111 -5
  185. package/src/commands/relay.ts +160 -0
  186. package/src/commands/sync.ts +4 -0
  187. package/src/commands/workflow.ts +123 -0
  188. package/src/core/agents/canonical-emit.ts +41 -3
  189. package/src/core/agents/cli.ts +15 -6
  190. package/src/core/agents/render/prompt-context.ts +48 -3
  191. package/src/core/agents/render/session-context.ts +27 -1
  192. package/src/core/agents/rules/claim-conflict.ts +26 -1
  193. package/src/core/agents/rules/commit-conflict.ts +4 -33
  194. package/src/core/agents/rules/stop-hook.ts +17 -0
  195. package/src/core/agents/session-events.ts +15 -39
  196. package/src/core/agents/state/heartbeat-projector.ts +23 -2
  197. package/src/core/agents/state/heartbeat-writer.ts +33 -7
  198. package/src/core/agents/state/names.ts +68 -9
  199. package/src/core/agents/state/pidmap.ts +59 -2
  200. package/src/core/agents/state/stale-sweep.ts +6 -11
  201. package/src/core/config.ts +271 -24
  202. package/src/core/context/index.ts +575 -0
  203. package/src/core/harnesses/bench.ts +214 -0
  204. package/src/core/harnesses/index.ts +31 -0
  205. package/src/core/harnesses/profiles.ts +141 -0
  206. package/src/core/harnesses/registry.ts +140 -0
  207. package/src/core/harnesses/types.ts +90 -0
  208. package/src/core/hooks/cli.ts +361 -65
  209. package/src/core/hooks/events/schema.ts +72 -0
  210. package/src/core/hooks/harness/events.ts +11 -0
  211. package/src/core/hooks/harness/parse.ts +7 -1
  212. package/src/core/hooks/resolve/coord-root.ts +28 -6
  213. package/src/core/presence/blob.ts +125 -0
  214. package/src/core/presence/git.ts +191 -0
  215. package/src/core/presence/index.ts +274 -0
  216. package/src/core/presence/relay-client.ts +322 -0
  217. package/src/core/presence/relay-protocol.ts +246 -0
  218. package/src/core/workflow/billing.ts +152 -0
  219. package/src/core/workflow/child-env.ts +47 -0
  220. package/src/core/workflow/engine.ts +403 -0
  221. package/src/core/workflow/harnesses.ts +33 -0
  222. package/src/core/workflow/spawn-claude.ts +118 -0
  223. package/src/core/workflow/spawn-codex.ts +93 -0
  224. package/src/core/workflow/spawn-cursor.ts +108 -0
  225. package/src/core/workflow/types.ts +160 -0
  226. package/src/core/workflow/validate.ts +75 -0
  227. package/src/lib/browser/client.ts +37 -0
  228. package/src/lib/browser/index.ts +1 -0
  229. package/src/lib/browser/launch-args.ts +33 -0
  230. package/src/lib/claude-desktop.ts +293 -0
  231. package/src/lib/docs-lint.ts +5 -2
  232. package/src/lib/identities/assume.ts +363 -0
  233. package/src/lib/identities/index.ts +28 -24
  234. package/src/lib/instructions/templates.ts +16 -2
@@ -24,22 +24,22 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFile
24
24
  import { resolve } from "node:path";
25
25
  import { monorepoRoot } from "../../core/agents/index.js";
26
26
  export const IDENTITY_SCHEMA_VERSION = 1;
27
- function identitiesDir() {
28
- const root = monorepoRoot();
27
+ function identitiesDir(coordRoot) {
28
+ const root = coordRoot ?? monorepoRoot();
29
29
  if (!root)
30
30
  return null;
31
31
  return resolve(root, ".harnery", "identities");
32
32
  }
33
- function ensureDir() {
34
- const dir = identitiesDir();
33
+ function ensureDir(coordRoot) {
34
+ const dir = identitiesDir(coordRoot);
35
35
  if (!dir)
36
36
  throw new Error("not in an agent session; no monorepo root");
37
37
  if (!existsSync(dir))
38
38
  mkdirSync(dir, { recursive: true });
39
39
  return dir;
40
40
  }
41
- function identityPath(agentId) {
42
- return resolve(ensureDir(), `${agentId}.json`);
41
+ function identityPath(agentId, coordRoot) {
42
+ return resolve(ensureDir(coordRoot), `${agentId}.json`);
43
43
  }
44
44
  function nowIso() {
45
45
  return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -66,8 +66,8 @@ export function displayName(name) {
66
66
  return name.startsWith("agent-") ? name : `agent-${name}`;
67
67
  }
68
68
  /** Read one identity by id. Null if missing. */
69
- export function lookupById(agentId) {
70
- const dir = identitiesDir();
69
+ export function lookupById(agentId, coordRoot) {
70
+ const dir = identitiesDir(coordRoot);
71
71
  if (!dir)
72
72
  return null;
73
73
  const fp = resolve(dir, `${agentId}.json`);
@@ -77,8 +77,8 @@ export function lookupById(agentId) {
77
77
  }
78
78
  /** Read one identity by display name (case-insensitive on bare name).
79
79
  * Scans the directory; O(N) on identity count. */
80
- export function lookupByName(name) {
81
- const dir = identitiesDir();
80
+ export function lookupByName(name, coordRoot) {
81
+ const dir = identitiesDir(coordRoot);
82
82
  if (!dir || !existsSync(dir))
83
83
  return null;
84
84
  const wanted = bareName(name).toLowerCase();
@@ -98,8 +98,8 @@ export function lookupByName(name) {
98
98
  return null;
99
99
  }
100
100
  /** All known identities, sorted by created_at ascending. */
101
- export function listIdentities() {
102
- const dir = identitiesDir();
101
+ export function listIdentities(coordRoot) {
102
+ const dir = identitiesDir(coordRoot);
103
103
  if (!dir || !existsSync(dir))
104
104
  return [];
105
105
  const out = [];
@@ -123,8 +123,8 @@ export function listIdentities() {
123
123
  * surface whichever happened to be read first; the second is orphaned.
124
124
  * For agent personas (added at human cadence) this race is theoretical.
125
125
  */
126
- export function ensureIdentity(name) {
127
- const existing = lookupByName(name);
126
+ export function ensureIdentity(name, coordRoot) {
127
+ const existing = lookupByName(name, coordRoot);
128
128
  if (existing)
129
129
  return existing;
130
130
  const id = {
@@ -134,13 +134,13 @@ export function ensureIdentity(name) {
134
134
  aliases: [],
135
135
  created_at: nowIso(),
136
136
  };
137
- writeIdentity(id);
137
+ writeIdentity(id, coordRoot);
138
138
  return id;
139
139
  }
140
140
  /** Persist an identity (tmp + rename). Creates the dir if missing. */
141
- export function writeIdentity(id) {
142
- ensureDir();
143
- const fp = identityPath(id.agent_id);
141
+ export function writeIdentity(id, coordRoot) {
142
+ ensureDir(coordRoot);
143
+ const fp = identityPath(id.agent_id, coordRoot);
144
144
  const tmp = `${fp}.tmp.${process.pid}`;
145
145
  writeFileSync(tmp, `${JSON.stringify(id, null, 2)}\n`, "utf8");
146
146
  renameSync(tmp, fp);
@@ -150,8 +150,8 @@ export function writeIdentity(id) {
150
150
  * lookup still resolves correctly. The agent_id is stable across renames,
151
151
  * which is the whole point of the registry.
152
152
  */
153
- export function renameIdentity(agentId, newName) {
154
- const existing = lookupById(agentId);
153
+ export function renameIdentity(agentId, newName, coordRoot) {
154
+ const existing = lookupById(agentId, coordRoot);
155
155
  if (!existing) {
156
156
  throw new Error(`renameIdentity: no identity matching '${agentId}'`);
157
157
  }
@@ -163,7 +163,7 @@ export function renameIdentity(agentId, newName) {
163
163
  name: bare,
164
164
  aliases: [...existing.aliases, { name: existing.name, retired_at: nowIso() }],
165
165
  };
166
- writeIdentity(next);
166
+ writeIdentity(next, coordRoot);
167
167
  return next;
168
168
  }
169
169
  /**
@@ -175,16 +175,16 @@ export function renameIdentity(agentId, newName) {
175
175
  * Does NOT mint. Callers that want mint-on-miss should call ensureIdentity()
176
176
  * with a name, then use the returned agent_id.
177
177
  */
178
- export function resolveAgentId(input) {
178
+ export function resolveAgentId(input, coordRoot) {
179
179
  const trimmed = input.trim();
180
180
  if (!trimmed)
181
181
  return null;
182
182
  // UUID shape check, relaxed; we only need the lookupById to confirm.
183
183
  if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmed)) {
184
- const byId = lookupById(trimmed);
184
+ const byId = lookupById(trimmed, coordRoot);
185
185
  if (byId)
186
186
  return byId.agent_id;
187
187
  }
188
- const byName = lookupByName(trimmed);
188
+ const byName = lookupByName(trimmed, coordRoot);
189
189
  return byName ? byName.agent_id : null;
190
190
  }
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/lib/instructions/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,sEAAsE;AACtE,eAAO,MAAM,aAAa,WAAW,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,WAA6C,GACpD,MAAM,CA+CR;AAID,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAsKD,eAAO,MAAM,MAAM,EAAE,aAAa,EA4BjC,CAAC"}
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/lib/instructions/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,sEAAsE;AACtE,eAAO,MAAM,aAAa,WAAW,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,WAA6C,GACpD,MAAM,CA4DR;AAID,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAsKD,eAAO,MAAM,MAAM,EAAE,aAAa,EA4BjC,CAAC"}
@@ -10,6 +10,7 @@
10
10
  * files), so they compile into `dist/` and resolve identically under Bun and
11
11
  * Node — no `files`-field copy or package-path guesswork.
12
12
  */
13
+ import { SCRATCH_CATEGORIES } from "../../core/scratch/index.js";
13
14
  import { buildOwnedSkill } from "./splice.js";
14
15
  /** Managed-region name for the AGENTS.md orientation block. */
15
16
  export const INSTRUCTIONS_REGION = "instructions";
@@ -39,6 +40,12 @@ export function renderInstructionsBlock(binName, skills = { decide: true, counci
39
40
  const councilPointer = skills.council
40
41
  ? "The `harn-council` skill has the steward and member flow."
41
42
  : `See \`${b} council --help\` for the steward and member flow.`;
43
+ // Render the scratch categories from the canonical enum so this prose can
44
+ // never drift from what `scratch add` actually accepts (the "note, plan…" list
45
+ // silently lagged the tool by two categories before this).
46
+ const scratchCats = SCRATCH_CATEGORIES.length > 1
47
+ ? `${SCRATCH_CATEGORIES.slice(0, -1).join(", ")}, or ${SCRATCH_CATEGORIES.at(-1)}`
48
+ : SCRATCH_CATEGORIES[0];
42
49
  return `## harnery coordination
43
50
 
44
51
  This project runs [harnery](https://harnery.com) for multi-agent coordination.
@@ -52,13 +59,19 @@ active peers and the files they've claimed; \`${b} agents set-task "<focus>"\`
52
59
  declares your current focus so peers can see it. Check for peers before editing
53
60
  widely-shared files.
54
61
 
62
+ **Durable role handoff.** When you are replacing a prior session in the same
63
+ named role, run \`${b} agents identity assume <name>\` before declaring your task.
64
+ It reclaims an abandoned namesake (no live process) and refuses only when another
65
+ live process still holds the name; never hand-edit Harnery's history, heartbeat,
66
+ or derived identity cache.
67
+
55
68
  **Declare intent on shell commands.** Every command you run is captured to the
56
69
  coordination ledger. Lead a shell command with a \`# intent: <why>\` comment (or set
57
70
  the tool's description) so the recorded event carries a reason instead of
58
71
  \`(no intent)\`.
59
72
 
60
- **Scratch journal.** \`${b} scratch add <category> "<text>"\` (category = note, plan,
61
- decision, blocker, or handoff) leaves breadcrumbs that survive context compaction;
73
+ **Scratch journal.** \`${b} scratch add <category> "<text>"\` (category = ${scratchCats})
74
+ leaves breadcrumbs that survive context compaction;
62
75
  \`${b} scratch read\` reads yours, \`${b} scratch read --name <peer>\` reads a peer's.
63
76
  Use it for anything future-you or a peer will need to pick up your thread.
64
77
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harnery",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Multi-agent coordination for AI coding agents - Claude Code, Cursor, and Codex.",
5
5
  "license": "MIT",
6
6
  "author": "Ryan Kelly",
@@ -52,6 +52,16 @@
52
52
  "types": "./dist/core/hooks/index.d.ts",
53
53
  "import": "./dist/core/hooks/index.js"
54
54
  },
55
+ "./core/harnesses": {
56
+ "bun": "./src/core/harnesses/index.ts",
57
+ "types": "./dist/core/harnesses/index.d.ts",
58
+ "import": "./dist/core/harnesses/index.js"
59
+ },
60
+ "./core/context": {
61
+ "bun": "./src/core/context/index.ts",
62
+ "types": "./dist/core/context/index.d.ts",
63
+ "import": "./dist/core/context/index.js"
64
+ },
55
65
  "./lib/agent-browser": {
56
66
  "bun": "./src/lib/agent-browser/index.ts",
57
67
  "types": "./dist/lib/agent-browser/index.d.ts",
@@ -129,6 +139,7 @@
129
139
  "typecheck": "bun x tsc --noEmit",
130
140
  "lint": "bun x @biomejs/biome check src",
131
141
  "check:portability": "bun run scripts/check-portability.ts",
142
+ "check:public-surface": "bun run scripts/check-public-surface.ts",
132
143
  "check:layering": "bun run scripts/check-layering.ts",
133
144
  "lint:fix": "bun x @biomejs/biome check --write src",
134
145
  "format": "bun x @biomejs/biome format --write src",
@@ -31,12 +31,7 @@
31
31
  "type": "integer",
32
32
  "minimum": 30,
33
33
  "default": 600,
34
- "description": "How stale a peer heartbeat can get before the sweeper prunes it."
35
- },
36
- "name_pool": {
37
- "type": "string",
38
- "default": "default",
39
- "description": "Which agent-name pool to draw from. 'default' / 'hurricane' / a custom pool id."
34
+ "description": "How stale a peer heartbeat can get before the sweeper prunes it. Env override: HARNERY_AGENT_COORD_FRESHNESS."
40
35
  }
41
36
  }
42
37
  },
@@ -48,7 +43,7 @@
48
43
  "type": "integer",
49
44
  "minimum": 1024,
50
45
  "maximum": 65535,
51
- "default": 7777,
46
+ "default": 9000,
52
47
  "description": "Port that `harn web up` binds to."
53
48
  },
54
49
  "bind": {
@@ -77,35 +72,109 @@
77
72
  }
78
73
  }
79
74
  },
75
+ "tools": {
76
+ "type": "object",
77
+ "additionalProperties": false,
78
+ "description": "Managed-tool provisioning consent.",
79
+ "properties": {
80
+ "ripgrep": {
81
+ "type": "object",
82
+ "additionalProperties": false,
83
+ "properties": {
84
+ "autoInstall": {
85
+ "type": "boolean",
86
+ "default": false,
87
+ "description": "Allow `grep` to download the pinned, checksum-verified ripgrep into the harnery tools dir on first miss. Env override: HARNERY_TOOLS_AUTOINSTALL=1|0."
88
+ }
89
+ }
90
+ }
91
+ }
92
+ },
93
+ "workflow": {
94
+ "type": "object",
95
+ "additionalProperties": false,
96
+ "properties": {
97
+ "subscriptionOnly": {
98
+ "type": "boolean",
99
+ "default": false,
100
+ "description": "Pin `workflow run` to subscription billing: API-key vars are scrubbed from every child env. Stamped true by `harn init` when the key is absent. Env override: HARNERY_WORKFLOW_SUBSCRIPTION_ONLY=1|0."
101
+ }
102
+ }
103
+ },
104
+ "skills": {
105
+ "type": "object",
106
+ "additionalProperties": false,
107
+ "properties": {
108
+ "exclude": {
109
+ "type": "array",
110
+ "items": { "type": "string" },
111
+ "default": [],
112
+ "description": "Shipped harnery skills this repo suppresses (e.g. because it maintains richer host-specific versions)."
113
+ }
114
+ }
115
+ },
116
+ "presence": {
117
+ "type": "object",
118
+ "additionalProperties": false,
119
+ "description": "Cross-machine session presence (decision record 0016).",
120
+ "properties": {
121
+ "enabled": {
122
+ "type": "boolean",
123
+ "default": true,
124
+ "description": "Presence over git refs (refs/harnery/presence/<machine> on origin). On by default when an origin remote exists; fail-silent everywhere. Env override: HARNERY_PRESENCE=1|0."
125
+ },
126
+ "relay": {
127
+ "type": "string",
128
+ "description": "Optional live-socket upgrade: a wss:// relay URL (public reference: wss://relay.harnery.com; self-host via `harn relay serve` or relay/worker/). Hooks keep a per-machine daemon connected; git refs stay on as the floor. Env override: HARNERY_PRESENCE_RELAY=<url|0>."
129
+ }
130
+ }
131
+ },
80
132
  "backup": {
81
133
  "type": "object",
82
134
  "additionalProperties": false,
135
+ "description": "`harn backup` (restic) defaults. Env overrides: HARNERY_RESTIC_REPO, HARNERY_RESTIC_PASSWORD_FILE. `harn backup` is manual (init/snapshot/prune/...); there is no auto-snapshot scheduler.",
83
136
  "properties": {
84
137
  "repo": {
85
138
  "type": "string",
86
- "description": "restic repository URL. Common: 'rclone:gdrive:harnery-backup/<project>' for Google Drive."
139
+ "description": "restic repository path or URL. Default: ~/.cache/harnery/restic-repo. Common remote form: 'rclone:gdrive:harnery-backup/<project>'."
87
140
  },
88
- "schedule": {
141
+ "password_file": {
89
142
  "type": "string",
90
- "enum": ["off", "daily", "hourly"],
91
- "default": "off",
92
- "description": "Auto-snapshot cadence. 'off' = manual only."
143
+ "description": "File holding the restic repo password. Default: ~/.config/harnery/restic-password (autogenerated on `harn backup init`)."
144
+ },
145
+ "keep_daily": {
146
+ "type": "integer",
147
+ "minimum": 0,
148
+ "default": 7,
149
+ "description": "Daily snapshots to keep on `harn backup prune` (overridable per run with --keep-daily)."
150
+ },
151
+ "keep_weekly": {
152
+ "type": "integer",
153
+ "minimum": 0,
154
+ "default": 4,
155
+ "description": "Weekly snapshots to keep on `harn backup prune` (overridable with --keep-weekly)."
156
+ },
157
+ "keep_monthly": {
158
+ "type": "integer",
159
+ "minimum": 0,
160
+ "default": 6,
161
+ "description": "Monthly snapshots to keep on `harn backup prune` (overridable with --keep-monthly)."
93
162
  }
94
163
  }
95
164
  },
96
165
  "sync": {
97
166
  "type": "object",
98
167
  "additionalProperties": false,
168
+ "description": "`harn sync` (rclone) defaults for the curated cross-machine subset (identities/, scratch/archived/, councils/). Env overrides: HARNERY_SYNC_REMOTE, HARNERY_SYNC_PREFIX. `harn sync init` also records these in ~/.config/harnery/sync.json (a lower-precedence fallback than this section).",
99
169
  "properties": {
100
- "enabled": {
101
- "type": "boolean",
102
- "default": false,
103
- "description": "Enable cross-machine sync via Google Drive."
170
+ "remote": {
171
+ "type": "string",
172
+ "description": "rclone remote name (e.g. 'gdrive'). Set up the remote with `rclone config`; harnery doesn't wrap the OAuth flow."
104
173
  },
105
- "drive_folder": {
174
+ "prefix": {
106
175
  "type": "string",
107
- "default": "harnery-sync",
108
- "description": "Folder name in Google Drive that holds the synced subset."
176
+ "default": "harnery",
177
+ "description": "Subpath under the rclone remote root that holds the synced subset."
109
178
  }
110
179
  }
111
180
  }
package/src/commander.ts CHANGED
@@ -23,6 +23,7 @@ import { registerBackupCommand } from "./commands/backup.ts";
23
23
  import { registerBrowseCommand } from "./commands/browse.ts";
24
24
  import { registerBrowseAiCommand } from "./commands/browse-ai.ts";
25
25
  import { registerCallersCommand } from "./commands/callers.ts";
26
+ import { registerClaudeDesktopCommand } from "./commands/claude-desktop.ts";
26
27
  import { registerCompletionCommand } from "./commands/completion.ts";
27
28
  import { registerConfigGetCommand } from "./commands/config-get.ts";
28
29
  import { registerContextCommand } from "./commands/context.ts";
@@ -38,10 +39,12 @@ import { registerEnvCommand } from "./commands/env.ts";
38
39
  import { registerFetchCommand } from "./commands/fetch.ts";
39
40
  import { registerFileHistoryCommand } from "./commands/file-history.ts";
40
41
  import { registerGrepCommand } from "./commands/grep.ts";
42
+ import { registerHarnessCommand } from "./commands/harness.ts";
41
43
  import { registerInitCommand } from "./commands/init.ts";
42
44
  import { registerOutlineCommand } from "./commands/outline.ts";
43
45
  import { registerPresenceCommand } from "./commands/presence.ts";
44
46
  import { registerReadCommand } from "./commands/read.ts";
47
+ import { registerRelayCommand } from "./commands/relay.ts";
45
48
  import { registerScratchCommand } from "./commands/scratch.ts";
46
49
  import { registerSessionCommand } from "./commands/session.ts";
47
50
  import { registerSyncCommand } from "./commands/sync.ts";
@@ -49,6 +52,7 @@ import { registerSectionCommand, registerTocCommand } from "./commands/toc.ts";
49
52
  import { registerTokensCommand } from "./commands/tokens.ts";
50
53
  import { registerTunnelCommand } from "./commands/tunnel.ts";
51
54
  import { registerWebCommand } from "./commands/web.ts";
55
+ import { registerWorkflowCommand } from "./commands/workflow.ts";
52
56
 
53
57
  export interface HarneryContextOpts {
54
58
  /**
@@ -250,6 +254,7 @@ export function createHarneryProgram(opts: HarneryContextOpts = {}): Command {
250
254
  registerEmlCommand(program, emit);
251
255
  registerEnvCommand(program, emit, opts.context);
252
256
  registerPresenceCommand(program, emit);
257
+ registerRelayCommand(program, emit);
253
258
  registerConfigGetCommand(program, emit);
254
259
  registerFileHistoryCommand(program, emit);
255
260
  registerOutlineCommand(program, emit);
@@ -258,6 +263,7 @@ export function createHarneryProgram(opts: HarneryContextOpts = {}): Command {
258
263
  registerCallersCommand(program, emit, opts.context);
259
264
  registerEditBatchCommand(program, emit);
260
265
  registerGrepCommand(program, emit, opts.context);
266
+ registerHarnessCommand(program, emit);
261
267
  registerCookiesCommand(program, emit);
262
268
  registerFetchCommand(program, emit, opts.context);
263
269
  registerReadCommand(program, emit);
@@ -276,7 +282,9 @@ export function createHarneryProgram(opts: HarneryContextOpts = {}): Command {
276
282
  registerInitCommand(program, emit, opts.binName);
277
283
  registerDeinitCommand(program, emit, opts.binName);
278
284
  registerBackupCommand(program, emit);
285
+ registerClaudeDesktopCommand(program, emit);
279
286
  registerSyncCommand(program, emit);
287
+ registerWorkflowCommand(program, emit);
280
288
  if (include("web")) registerWebCommand(program, emit);
281
289
 
282
290
  return program;