harnery 0.6.0 → 0.7.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.
- package/README.md +16 -6
- package/dist/commander.d.ts +19 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +2 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +51 -4
- package/dist/commands/deinit.d.ts.map +1 -1
- package/dist/commands/deinit.js +4 -0
- package/dist/commands/devtools.d.ts +4 -0
- package/dist/commands/devtools.d.ts.map +1 -0
- package/dist/commands/devtools.js +239 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +69 -1
- package/dist/commands/doctor.js +12 -4
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +3 -63
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +54 -14
- package/dist/commands/scratch.js +1 -1
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +273 -62
- package/dist/commands/web-fetch.js +1 -1
- package/dist/core/agents/cli.js +48 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +32 -8
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +4 -0
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +16 -5
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +10 -3
- package/dist/core/config.d.ts +10 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +13 -0
- package/dist/core/hooks/cli.js +3 -3
- package/dist/core/hooks/effects/index.d.ts +11 -7
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +15 -18
- package/dist/core/hooks/events/emit.d.ts.map +1 -1
- package/dist/core/hooks/events/emit.js +4 -0
- package/dist/core/hooks/events/rotate.d.ts +43 -0
- package/dist/core/hooks/events/rotate.d.ts.map +1 -0
- package/dist/core/hooks/events/rotate.js +142 -0
- package/dist/core/hooks/harness/events.d.ts +11 -1
- package/dist/core/hooks/harness/events.d.ts.map +1 -1
- package/dist/core/hooks/harness/events.js +22 -3
- package/dist/core/hooks/harness/wiring.d.ts +8 -0
- package/dist/core/hooks/harness/wiring.d.ts.map +1 -1
- package/dist/core/hooks/harness/wiring.js +34 -5
- package/dist/core/scratch/index.d.ts.map +1 -0
- package/dist/{lib → core}/scratch/index.js +2 -2
- package/dist/lib/devtools.d.ts +178 -0
- package/dist/lib/devtools.d.ts.map +1 -0
- package/dist/lib/devtools.js +1328 -0
- package/dist/lib/docs-frontmatter-migrate.d.ts +33 -0
- package/dist/lib/docs-frontmatter-migrate.d.ts.map +1 -0
- package/dist/lib/docs-frontmatter-migrate.js +364 -0
- package/dist/lib/docs-frontmatter.d.ts +33 -0
- package/dist/lib/docs-frontmatter.d.ts.map +1 -0
- package/dist/lib/docs-frontmatter.js +130 -0
- package/dist/lib/docs-index.d.ts +1 -0
- package/dist/lib/docs-index.d.ts.map +1 -1
- package/dist/lib/docs-index.js +4 -5
- package/dist/lib/docs-lint.d.ts +2 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +18 -12
- package/dist/lib/docs-meta.d.ts +14 -0
- package/dist/lib/docs-meta.d.ts.map +1 -0
- package/dist/lib/docs-meta.js +34 -0
- package/dist/lib/docs-sweep.d.ts +12 -0
- package/dist/lib/docs-sweep.d.ts.map +1 -1
- package/dist/lib/docs-sweep.js +98 -103
- package/dist/lib/format.js +2 -2
- package/dist/lib/http/index.d.ts +1 -0
- package/dist/lib/http/index.d.ts.map +1 -1
- package/dist/lib/http/index.js +1 -0
- package/dist/lib/http/request.d.ts +77 -0
- package/dist/lib/http/request.d.ts.map +1 -0
- package/dist/lib/http/request.js +105 -0
- package/dist/lib/instructions/apply.d.ts +63 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -0
- package/dist/lib/instructions/apply.js +255 -0
- package/dist/lib/instructions/splice.d.ts +73 -0
- package/dist/lib/instructions/splice.d.ts.map +1 -0
- package/dist/lib/instructions/splice.js +118 -0
- package/dist/lib/instructions/templates.d.ts +45 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -0
- package/dist/lib/instructions/templates.js +258 -0
- package/dist/lib/tunnel/gate.d.ts +1 -0
- package/dist/lib/tunnel/gate.d.ts.map +1 -1
- package/dist/lib/tunnel/gate.js +14 -9
- package/dist/lib/tunnel/state.d.ts +11 -1
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +8 -3
- package/package.json +7 -6
- package/src/commander.ts +23 -0
- package/src/commands/agents.ts +50 -3
- package/src/commands/deinit.ts +5 -0
- package/src/commands/devtools.ts +284 -0
- package/src/commands/docs.ts +81 -1
- package/src/commands/doctor.ts +13 -4
- package/src/commands/env.ts +11 -77
- package/src/commands/init.ts +66 -15
- package/src/commands/scratch.ts +1 -1
- package/src/commands/tunnel.ts +316 -65
- package/src/commands/web-fetch.ts +1 -1
- package/src/core/agents/cli.ts +55 -0
- package/src/core/agents/coord-client.ts +34 -7
- package/src/core/agents/events/emit.ts +5 -0
- package/src/core/agents/rules/claim-conflict.ts +17 -6
- package/src/core/agents/state/heartbeat-projector.ts +11 -3
- package/src/core/config.ts +14 -0
- package/src/core/hooks/cli.ts +3 -3
- package/src/core/hooks/effects/index.ts +23 -17
- package/src/core/hooks/events/emit.ts +5 -0
- package/src/core/hooks/events/rotate.ts +151 -0
- package/src/core/hooks/harness/events.ts +30 -3
- package/src/core/hooks/harness/wiring.ts +46 -5
- package/src/{lib → core}/scratch/index.ts +2 -2
- package/src/lib/devtools.ts +1653 -0
- package/src/lib/docs-frontmatter-migrate.ts +427 -0
- package/src/lib/docs-frontmatter.ts +151 -0
- package/src/lib/docs-index.ts +4 -5
- package/src/lib/docs-lint.ts +17 -11
- package/src/lib/docs-meta.ts +44 -0
- package/src/lib/docs-sweep.ts +104 -102
- package/src/lib/format.ts +2 -2
- package/src/lib/http/index.ts +1 -0
- package/src/lib/http/request.ts +154 -0
- package/src/lib/instructions/apply.ts +318 -0
- package/src/lib/instructions/splice.ts +148 -0
- package/src/lib/instructions/templates.ts +295 -0
- package/src/lib/tunnel/gate.ts +14 -9
- package/src/lib/tunnel/state.ts +19 -4
- package/dist/lib/scratch/index.d.ts.map +0 -1
- /package/dist/{lib → core}/scratch/index.d.ts +0 -0
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent-facing content harnery ships into a consumer: one orientation block
|
|
3
|
+
* for `AGENTS.md` and the generic skills. Everything here is engine mechanics
|
|
4
|
+
* only — no triage rubric, no escalation targets, no host doc-layout policy
|
|
5
|
+
* (those stay host-authored, per ADR 0007's portability split). Every command
|
|
6
|
+
* string renders through `binName`; a template that only reads for `harn` is a
|
|
7
|
+
* bug the portability guard exists to catch.
|
|
8
|
+
*
|
|
9
|
+
* Content, not paths: templates are TS string builders (not shipped `.md`
|
|
10
|
+
* files), so they compile into `dist/` and resolve identically under Bun and
|
|
11
|
+
* Node — no `files`-field copy or package-path guesswork.
|
|
12
|
+
*/
|
|
13
|
+
import { buildOwnedSkill } from "./splice.js";
|
|
14
|
+
/** Managed-region name for the AGENTS.md orientation block. */
|
|
15
|
+
export const INSTRUCTIONS_REGION = "instructions";
|
|
16
|
+
/** Managed-region name for the CLAUDE.md `@AGENTS.md` import shim. */
|
|
17
|
+
export const IMPORT_REGION = "import";
|
|
18
|
+
/**
|
|
19
|
+
* The always-on orientation spliced into `AGENTS.md`. Target ≤ 80 rendered
|
|
20
|
+
* lines: it costs every agent context on every turn, so it states that each
|
|
21
|
+
* surface *exists* and gives one line of *when* — the *how* lives in the skills
|
|
22
|
+
* and each command's `--help`. Skill names are fixed (`harn-decide`,
|
|
23
|
+
* `harn-council`) even for a renamed bin; only command strings track `binName`.
|
|
24
|
+
*
|
|
25
|
+
* The block only points at a skill that actually exists here: a host that
|
|
26
|
+
* excludes one via `skills.exclude`, or a harness with no skill primitive
|
|
27
|
+
* (cursor/codex get the block but no skill files), gets a `--help` pointer
|
|
28
|
+
* instead of a dangling reference to a skill it doesn't have.
|
|
29
|
+
*/
|
|
30
|
+
export function renderInstructionsBlock(binName, skills = { decide: true, council: true }) {
|
|
31
|
+
const b = binName;
|
|
32
|
+
const named = [skills.decide && "`harn-decide`", skills.council && "`harn-council`"].filter(Boolean);
|
|
33
|
+
const deeper = named.length > 0
|
|
34
|
+
? `Procedures for the deeper flows live in the ${named.join(" and ")} skill${named.length > 1 ? "s" : ""}.`
|
|
35
|
+
: `See \`${b} decision --help\` and \`${b} council --help\` for the deeper procedures.`;
|
|
36
|
+
const decidePointer = skills.decide
|
|
37
|
+
? "The `harn-decide` skill has the file / claim / resolve-with-evidence procedure."
|
|
38
|
+
: `See \`${b} decision --help\` for the file / claim / resolve-with-evidence procedure.`;
|
|
39
|
+
const councilPointer = skills.council
|
|
40
|
+
? "The `harn-council` skill has the steward and member flow."
|
|
41
|
+
: `See \`${b} council --help\` for the steward and member flow.`;
|
|
42
|
+
return `## harnery coordination
|
|
43
|
+
|
|
44
|
+
This project runs [harnery](https://harnery.com) for multi-agent coordination.
|
|
45
|
+
You share this checkout with other agents; the surfaces below keep you oriented
|
|
46
|
+
and out of each other's way. Run \`${b} <command> --help\` for any command's full
|
|
47
|
+
surface. ${deeper}
|
|
48
|
+
|
|
49
|
+
**Identity + peers.** You are one of several agents in this repo.
|
|
50
|
+
\`${b} agents whoami\` is you; \`${b} agents status\` shows your session plus the
|
|
51
|
+
active peers and the files they've claimed; \`${b} agents set-task "<focus>"\`
|
|
52
|
+
declares your current focus so peers can see it. Check for peers before editing
|
|
53
|
+
widely-shared files.
|
|
54
|
+
|
|
55
|
+
**Declare intent on shell commands.** Every command you run is captured to the
|
|
56
|
+
coordination ledger. Lead a shell command with a \`# intent: <why>\` comment (or set
|
|
57
|
+
the tool's description) so the recorded event carries a reason instead of
|
|
58
|
+
\`(no intent)\`.
|
|
59
|
+
|
|
60
|
+
**Scratch journal.** \`${b} scratch add <category> "<text>"\` (category = note, plan,
|
|
61
|
+
decision, blocker, or handoff) leaves breadcrumbs that survive context compaction;
|
|
62
|
+
\`${b} scratch read\` reads yours, \`${b} scratch read --name <peer>\` reads a peer's.
|
|
63
|
+
Use it for anything future-you or a peer will need to pick up your thread.
|
|
64
|
+
|
|
65
|
+
**Decision docket.** When you would otherwise stop to ask a human a decision you
|
|
66
|
+
can't resolve from the repo, file it instead. \`${b} decision file "<question>"\`
|
|
67
|
+
records it and lets you proceed on a stated default; \`${b} decision search "<terms>"\`
|
|
68
|
+
surfaces prior decisions, so check for precedent before re-deciding. ${decidePointer}
|
|
69
|
+
|
|
70
|
+
**Councils.** For a hard or contested decision, convene a council of agents.
|
|
71
|
+
\`${b} council create "<objective>"\` runs structured rounds toward a decision. ${councilPointer}`;
|
|
72
|
+
}
|
|
73
|
+
function decideBody(b) {
|
|
74
|
+
return `The decision docket is a persistent queue for decisions you would otherwise
|
|
75
|
+
route to a human. It's built on the \`${b} decision\` engine. This skill is the
|
|
76
|
+
mechanics — file, find precedent, claim, and resolve with evidence. *When* a
|
|
77
|
+
decision needs a human at all (versus one you settle yourself) is host policy;
|
|
78
|
+
if this project defines that rubric, follow it.
|
|
79
|
+
|
|
80
|
+
## Modes
|
|
81
|
+
|
|
82
|
+
- **A decision you're facing (default)** → capture: record it and proceed.
|
|
83
|
+
- **\`resolve <id>\`** → pick up an open decision, research it, resolve it.
|
|
84
|
+
- **\`review\`** → surface resolved-but-unreviewed decisions for a human to skim.
|
|
85
|
+
|
|
86
|
+
## Capture (default)
|
|
87
|
+
|
|
88
|
+
1. **Check precedent first.** \`${b} decision search "<key terms>"\`. If a resolved
|
|
89
|
+
decision already answers this, cite it — don't re-litigate.
|
|
90
|
+
2. **File it** when the choice has a second consumer (a human will want to see it,
|
|
91
|
+
or a future agent will face it again) or reversal is expensive. Skip the
|
|
92
|
+
docket for pure local mechanics (a variable name, one of two equivalent idioms).
|
|
93
|
+
|
|
94
|
+
\`\`\`bash
|
|
95
|
+
${b} decision file "<the decision as a clear question>" \\
|
|
96
|
+
--context "why it matters / what's blocked / the options you see" \\
|
|
97
|
+
--default-taken "<what you're proceeding with>"
|
|
98
|
+
\`\`\`
|
|
99
|
+
|
|
100
|
+
For a decision with real substance, write a brief to a file and pass
|
|
101
|
+
\`--brief <path>\` so the reviewer sees options + evidence, not a cold prompt.
|
|
102
|
+
3. **Proceed on your default.** Filing does not mean blocking — note the id in your
|
|
103
|
+
reply and keep working.
|
|
104
|
+
|
|
105
|
+
## Resolve (\`resolve <id>\`)
|
|
106
|
+
|
|
107
|
+
\`\`\`bash
|
|
108
|
+
${b} decision show <id> # read the question + context + any brief
|
|
109
|
+
${b} decision claim <id> # mark it deliberating (claimed by you)
|
|
110
|
+
\`\`\`
|
|
111
|
+
|
|
112
|
+
Research it for real — run the queries, read the files, compute the costs. Then
|
|
113
|
+
resolve with **cited evidence** (the engine rejects an evidence-free resolution):
|
|
114
|
+
|
|
115
|
+
\`\`\`bash
|
|
116
|
+
${b} decision resolve <id> \\
|
|
117
|
+
--recommendation "<the call>" \\
|
|
118
|
+
--evidence "<a fact you established: a query run, a file read, a cost computed>" \\
|
|
119
|
+
--evidence "<another>" \\
|
|
120
|
+
--reversal-cost "<cost to undo if wrong>" \\
|
|
121
|
+
--wrong-if "<what would make this wrong>" \\
|
|
122
|
+
--revisit-when "<trigger to revisit>"
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
If the decision is genuinely hard or contested, escalate to a council
|
|
126
|
+
(\`${b} council create "<objective>"\`) and link it rather than forcing a thin
|
|
127
|
+
single-agent resolution.
|
|
128
|
+
|
|
129
|
+
## Review (\`review\`)
|
|
130
|
+
|
|
131
|
+
\`\`\`bash
|
|
132
|
+
${b} decision list --status resolved
|
|
133
|
+
\`\`\`
|
|
134
|
+
|
|
135
|
+
For each one a human reacts to, record the verdict so triage self-corrects:
|
|
136
|
+
|
|
137
|
+
\`\`\`bash
|
|
138
|
+
${b} decision review <id> --verdict ratified # agreed; no action
|
|
139
|
+
${b} decision review <id> --verdict overridden --note "…" # disagreed
|
|
140
|
+
${b} decision review <id> --verdict wrong-tier-low # "didn't need to see this"
|
|
141
|
+
${b} decision review <id> --verdict wrong-tier-high # "should have seen it sooner"
|
|
142
|
+
\`\`\``;
|
|
143
|
+
}
|
|
144
|
+
function councilBody(b) {
|
|
145
|
+
return `A council convenes several agents to deliberate a hard or contested decision
|
|
146
|
+
over structured rounds. This skill wraps the \`${b} council\` surface with the
|
|
147
|
+
guardrails a router needs when passing prompts between agents. It's asymmetric on
|
|
148
|
+
purpose: it does the most for **contributors** (refusing a misrouted prompt) and
|
|
149
|
+
the least for **stewards**.
|
|
150
|
+
|
|
151
|
+
Every mode starts by running \`${b} agents whoami --json\` and, when an id is given,
|
|
152
|
+
\`${b} council show <id> --json\`, so the logic works against typed data, not text.
|
|
153
|
+
|
|
154
|
+
## Modes
|
|
155
|
+
|
|
156
|
+
- **No argument** → list councils you're a member of; surface what waits on you.
|
|
157
|
+
- **\`create <objective>\`** → open the web member-picker with the objective filled.
|
|
158
|
+
- **\`contribute <id>\`** → the guarded contribution flow (below).
|
|
159
|
+
- **\`prompts <id>\`** → steward: draft each pending member's routing prompt.
|
|
160
|
+
- **\`show <id>\`** → render council state verbatim.
|
|
161
|
+
|
|
162
|
+
## List (no arg)
|
|
163
|
+
|
|
164
|
+
\`\`\`bash
|
|
165
|
+
${b} agents whoami --json
|
|
166
|
+
${b} council list --mine --json
|
|
167
|
+
\`\`\`
|
|
168
|
+
|
|
169
|
+
One section per council you're a member of: id + objective, round N (open /
|
|
170
|
+
collected) with N/M contributors, and your status (awaiting prompt / prompt ready
|
|
171
|
+
/ already contributed) with the next command to run. Stop after listing — don't
|
|
172
|
+
auto-route into contribute.
|
|
173
|
+
|
|
174
|
+
## Create (\`create <objective>\`)
|
|
175
|
+
|
|
176
|
+
The web UI is the member + steward picker; don't create from the CLI (that skips
|
|
177
|
+
the steward choice). Emit the link with the objective URL-encoded:
|
|
178
|
+
|
|
179
|
+
\`\`\`
|
|
180
|
+
http://localhost:9000/councils/new?objective=<encoded>
|
|
181
|
+
\`\`\`
|
|
182
|
+
|
|
183
|
+
If the dev server isn't up, start it with \`${b} web up\`.
|
|
184
|
+
|
|
185
|
+
## Contribute (\`contribute <id>\`) — the guarded flow
|
|
186
|
+
|
|
187
|
+
Run these checks in order; refuse with a specific reason if any fails.
|
|
188
|
+
|
|
189
|
+
1. **Membership.** If your \`whoami\` name isn't in \`manifest.members\`, refuse — the
|
|
190
|
+
router likely meant a different agent's session.
|
|
191
|
+
2. **Already contributed.** If you're in \`current_round_contributors\`, refuse —
|
|
192
|
+
wait for the steward to advance the round.
|
|
193
|
+
3. **Prompt routing.** Find your entry in \`current_round_prompts\`. If none is
|
|
194
|
+
drafted for you, refuse (the steward must write one first). If the routed body
|
|
195
|
+
carries a \`<!-- council-route … member: <name> -->\` header naming a *different*
|
|
196
|
+
agent, refuse — the wrong prompt was pasted into your session.
|
|
197
|
+
4. **Compose** per your prompt (read \`manifest.target_doc\` in full if set; strip
|
|
198
|
+
the route header before treating the body as instructions).
|
|
199
|
+
5. **Submit:**
|
|
200
|
+
|
|
201
|
+
\`\`\`bash
|
|
202
|
+
${b} council contribute <id> --message "<status-line>" --file <path>
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
Use \`--file\` when you edited a target doc (captures the post-edit state);
|
|
206
|
+
\`--message\` for a prose-only contribution. If the prompt asks for a
|
|
207
|
+
substantive/trivial classification, end with the literal \`<substantive>\` or
|
|
208
|
+
\`<trivial>\` angle-bracket tag on the final line (the exit-criterion parser keys
|
|
209
|
+
on it). When in doubt, lean \`<trivial>\`.
|
|
210
|
+
|
|
211
|
+
## Prompts (\`prompts <id>\`) — steward
|
|
212
|
+
|
|
213
|
+
1. **Authority.** If your \`whoami\` name ≠ the council's \`steward\`, stop.
|
|
214
|
+
2. **Plan the round** from the target doc + prior rounds. Round 1 must include a
|
|
215
|
+
completeness critic — assign one member the explicit charge: "What important
|
|
216
|
+
thing is NOT in this document at all — a missing dimension, not a flaw in what's
|
|
217
|
+
written?" Lens-scoped reviewers reliably miss whole absent dimensions.
|
|
218
|
+
3. **Draft + write** one prompt per member missing from \`current_round_prompts\`:
|
|
219
|
+
|
|
220
|
+
\`\`\`bash
|
|
221
|
+
${b} council prompt <id> agent-<Name> --message "..." # or --file <path>
|
|
222
|
+
\`\`\`
|
|
223
|
+
|
|
224
|
+
The CLI auto-prepends the \`<!-- council-route … -->\` header — never write it by
|
|
225
|
+
hand. Every prompt must instruct the member to end with the literal
|
|
226
|
+
\`<substantive>\` / \`<trivial>\` tag.
|
|
227
|
+
|
|
228
|
+
## Refusal style
|
|
229
|
+
|
|
230
|
+
Lead with what you are versus what the council expects, cite the structural fact
|
|
231
|
+
that triggered the refusal (member list, contributors, prompt absence, route
|
|
232
|
+
mismatch), and offer the right next step. Don't propose a workaround that bypasses
|
|
233
|
+
the guard.`;
|
|
234
|
+
}
|
|
235
|
+
export const SKILLS = [
|
|
236
|
+
{
|
|
237
|
+
id: "harn-decide",
|
|
238
|
+
relPath: "harn-decide/SKILL.md",
|
|
239
|
+
render: (binName) => buildOwnedSkill({
|
|
240
|
+
name: "harn-decide",
|
|
241
|
+
description: "File a decision into the docket instead of blocking on a human — search precedent, file it, and proceed on a reversible default; or pick up and resolve an open decision with cited evidence. Use whenever you're about to ask a human a decision-shaped question you could resolve yourself.",
|
|
242
|
+
argumentHint: "[<the decision / question you're facing> | resolve <id> | review]",
|
|
243
|
+
binName,
|
|
244
|
+
body: decideBody(binName),
|
|
245
|
+
}),
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: "harn-council",
|
|
249
|
+
relPath: "harn-council/SKILL.md",
|
|
250
|
+
render: (binName) => buildOwnedSkill({
|
|
251
|
+
name: "harn-council",
|
|
252
|
+
description: "Interact with the multi-agent council system: list / create / show / prompts (steward) / contribute (member). Guards against misrouting — refuses to contribute when you aren't a member, have already contributed, or weren't routed a prompt.",
|
|
253
|
+
argumentHint: "[<id-or-fragment> | create <objective> | contribute <id> | prompts <id> | show <id>]",
|
|
254
|
+
binName,
|
|
255
|
+
body: councilBody(binName),
|
|
256
|
+
}),
|
|
257
|
+
},
|
|
258
|
+
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../../src/lib/tunnel/gate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../../src/lib/tunnel/gate.ts"],"names":[],"mappings":"AAiBA,iBAAS,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGlD;AAED,QAAA,MAAM,KAAK,aAKV,CAAC;AACF,QAAA,MAAM,MAAM,QAAwD,CAAC;AACrE,QAAA,MAAM,KAAK,QAAkD,CAAC;AAC9D,QAAA,MAAM,IAAI,QAA0E,CAAC;AACrF,QAAA,MAAM,MAAM,QAA8D,CAAC;AAE3E,QAAA,MAAM,aAAa,QAAqB,CAAC;AACzC,QAAA,MAAM,WAAW,QAAmB,CAAC;AAErC,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,QAAA,MAAM,MAAM,oBAsEV,CAAC"}
|
package/dist/lib/tunnel/gate.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Reverse-proxy worker spawned by `tunnel up`. Listens on 127.0.0.1:<port
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
2
|
+
// Reverse-proxy worker spawned by `tunnel up`. Listens on 127.0.0.1:<port>
|
|
3
|
+
// and forwards HTTP + WebSocket requests to an upstream with a Host header
|
|
4
|
+
// rewrite and Content-Encoding stripped (Bun's fetch auto-decompresses the
|
|
5
|
+
// body but retains the encoding header, which breaks browser decoding
|
|
6
|
+
// downstream). Cloudflare provider mode checks CF-Connecting-IP against an
|
|
7
|
+
// allowlist; trusted local proxy mode lets the local exposer own access.
|
|
7
8
|
//
|
|
8
9
|
// Runs detached, outside the CLI command framework; no command context is
|
|
9
10
|
// available; stdout/stderr is captured into .cache/tunnel/gate.log by the
|
|
@@ -25,6 +26,7 @@ const ALLOW = new Set((process.env.HARNERY_TUNNEL_ALLOW ?? "")
|
|
|
25
26
|
const TARGET = process.env.HARNERY_TUNNEL_TARGET ?? "127.0.0.1:8001";
|
|
26
27
|
const VHOST = process.env.HARNERY_TUNNEL_VHOST ?? "localhost";
|
|
27
28
|
const PORT = Number(process.env.HARNERY_TUNNEL_PORT ?? argvFlag("--port") ?? "9001");
|
|
29
|
+
const ACCESS = process.env.HARNERY_TUNNEL_ACCESS ?? "cloudflare-allowlist";
|
|
28
30
|
const UPSTREAM_HTTP = `http://${TARGET}`;
|
|
29
31
|
const UPSTREAM_WS = `ws://${TARGET}`;
|
|
30
32
|
const server = Bun.serve({
|
|
@@ -36,9 +38,11 @@ const server = Bun.serve({
|
|
|
36
38
|
// Bun's max, and beyond any realistic single-turn delay.
|
|
37
39
|
idleTimeout: 255,
|
|
38
40
|
async fetch(req, server) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
if (ACCESS === "cloudflare-allowlist") {
|
|
42
|
+
const ip = req.headers.get("cf-connecting-ip") ?? "";
|
|
43
|
+
if (!ALLOW.has(ip)) {
|
|
44
|
+
return new Response("403 Forbidden\n", { status: 403 });
|
|
45
|
+
}
|
|
42
46
|
}
|
|
43
47
|
const url = new URL(req.url);
|
|
44
48
|
if (req.headers.get("upgrade")?.toLowerCase() === "websocket") {
|
|
@@ -98,4 +102,5 @@ const server = Bun.serve({
|
|
|
98
102
|
// framework, so no AsyncLocalStorage context is available. stdout/stderr is
|
|
99
103
|
// captured into .cache/tunnel/gate.log by the spawning command.
|
|
100
104
|
console.log(`harn-tunnel-gate :${server.port} -> ${UPSTREAM_HTTP} (Host: ${VHOST})`); // lint-ok-emission: detached worker, see file note above
|
|
101
|
-
console.log(`
|
|
105
|
+
console.log(`access: ${ACCESS}`); // lint-ok-emission: detached worker, see file note above
|
|
106
|
+
console.log(`allow: ${[...ALLOW].join(", ") || "(empty, not used outside allowlist mode)"}`); // lint-ok-emission: detached worker, see file note above
|
|
@@ -2,18 +2,28 @@
|
|
|
2
2
|
export declare const DEFAULT_INSTANCE = "default";
|
|
3
3
|
export declare function gateLogFile(name: string): string;
|
|
4
4
|
export declare function cfdLogFile(name: string): string;
|
|
5
|
+
export declare function providerLogFile(name: string, provider: TunnelProvider): string;
|
|
5
6
|
export interface TunnelConfig {
|
|
6
7
|
allowed_ips: string[];
|
|
7
8
|
}
|
|
9
|
+
export type TunnelProvider = "cloudflare" | "tailscale";
|
|
10
|
+
export type TailscaleMode = "serve" | "funnel";
|
|
8
11
|
export interface TunnelState {
|
|
9
12
|
name: string;
|
|
13
|
+
provider: TunnelProvider;
|
|
10
14
|
url: string;
|
|
11
15
|
gate_pid: number;
|
|
12
|
-
|
|
16
|
+
/** Present for Cloudflare quick tunnels; absent for Tailscale Serve/Funnel. */
|
|
17
|
+
cloudflared_pid?: number;
|
|
18
|
+
/** Optional provider-side process when a provider owns one. */
|
|
19
|
+
provider_pid?: number;
|
|
13
20
|
started_at: string;
|
|
14
21
|
target: string;
|
|
15
22
|
vhost: string;
|
|
16
23
|
gate_port: number;
|
|
24
|
+
tailscale_mode?: TailscaleMode;
|
|
25
|
+
tailscale_path?: string;
|
|
26
|
+
tailscale_https_port?: number;
|
|
17
27
|
}
|
|
18
28
|
export declare function readConfig(): TunnelConfig;
|
|
19
29
|
export declare function writeConfig(cfg: TunnelConfig): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/lib/tunnel/state.ts"],"names":[],"mappings":"AAuBA,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAW1C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAgBD,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/lib/tunnel/state.ts"],"names":[],"mappings":"AAuBA,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAW1C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAG9E;AAgBD,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,WAAW,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAOD,wBAAgB,UAAU,IAAI,YAAY,CAWzC;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAEnD;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,MAAyB,GAAG,WAAW,GAAG,IAAI,CAQ7E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEnD;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAAyB,GAAG,IAAI,CAGhE;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,WAAW,EAAE,CAW1C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAuB1C"}
|
package/dist/lib/tunnel/state.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Tunnel config + state persistence +
|
|
2
|
-
//
|
|
1
|
+
// Tunnel config + state persistence + provider helpers. Lives under
|
|
2
|
+
// <cwd>/.cache/tunnel/; gitignored, so the allowlist is per-machine.
|
|
3
3
|
import { execSync } from "node:child_process";
|
|
4
4
|
import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
5
5
|
import { resolve } from "node:path";
|
|
@@ -27,6 +27,11 @@ export function gateLogFile(name) {
|
|
|
27
27
|
export function cfdLogFile(name) {
|
|
28
28
|
return name === DEFAULT_INSTANCE ? "cloudflared.log" : `cloudflared-${name}.log`;
|
|
29
29
|
}
|
|
30
|
+
export function providerLogFile(name, provider) {
|
|
31
|
+
if (provider === "cloudflare")
|
|
32
|
+
return cfdLogFile(name);
|
|
33
|
+
return name === DEFAULT_INSTANCE ? "tailscale.log" : `tailscale-${name}.log`;
|
|
34
|
+
}
|
|
30
35
|
/** Map a state filename back to its instance name (inverse of stateFile). */
|
|
31
36
|
function nameFromStateFile(file) {
|
|
32
37
|
if (file === "state.json")
|
|
@@ -42,7 +47,7 @@ const DEFAULT_CONFIG = {
|
|
|
42
47
|
};
|
|
43
48
|
/** Normalize a parsed state blob; supply `name` for pre-multi-instance files. */
|
|
44
49
|
function normalizeState(raw, fallbackName) {
|
|
45
|
-
return { ...raw, name: raw.name ?? fallbackName };
|
|
50
|
+
return { ...raw, name: raw.name ?? fallbackName, provider: raw.provider ?? "cloudflare" };
|
|
46
51
|
}
|
|
47
52
|
export function readConfig() {
|
|
48
53
|
const p = cachePath("tunnel", CONFIG_FILE);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harnery",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Multi-agent coordination
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"description": "Multi-agent coordination for AI coding agents - Claude Code, Cursor, and Codex.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryan Kelly",
|
|
7
7
|
"homepage": "https://harnery.com",
|
|
@@ -87,10 +87,10 @@
|
|
|
87
87
|
"types": "./dist/lib/readability/index.d.ts",
|
|
88
88
|
"import": "./dist/lib/readability/index.js"
|
|
89
89
|
},
|
|
90
|
-
"./
|
|
91
|
-
"bun": "./src/
|
|
92
|
-
"types": "./dist/
|
|
93
|
-
"import": "./dist/
|
|
90
|
+
"./core/scratch": {
|
|
91
|
+
"bun": "./src/core/scratch/index.ts",
|
|
92
|
+
"types": "./dist/core/scratch/index.d.ts",
|
|
93
|
+
"import": "./dist/core/scratch/index.js"
|
|
94
94
|
},
|
|
95
95
|
"./lib/tunnel/state": {
|
|
96
96
|
"bun": "./src/lib/tunnel/state.ts",
|
|
@@ -129,6 +129,7 @@
|
|
|
129
129
|
"typecheck": "bun x tsc --noEmit",
|
|
130
130
|
"lint": "bun x @biomejs/biome check src",
|
|
131
131
|
"check:portability": "bun run scripts/check-portability.ts",
|
|
132
|
+
"check:layering": "bun run scripts/check-layering.ts",
|
|
132
133
|
"lint:fix": "bun x @biomejs/biome check --write src",
|
|
133
134
|
"format": "bun x @biomejs/biome format --write src",
|
|
134
135
|
"test": "bun test src tests",
|
package/src/commander.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { registerContextCommand } from "./commands/context.ts";
|
|
|
29
29
|
import { registerCookiesCommand } from "./commands/cookies.ts";
|
|
30
30
|
import { registerDecisionCommand } from "./commands/decision.ts";
|
|
31
31
|
import { registerDeinitCommand } from "./commands/deinit.ts";
|
|
32
|
+
import { registerDevtoolsCommand } from "./commands/devtools.ts";
|
|
32
33
|
import { registerDocsCommand } from "./commands/docs.ts";
|
|
33
34
|
import { registerDoctorCommand } from "./commands/doctor.ts";
|
|
34
35
|
import { registerEditBatchCommand } from "./commands/edit-batch.ts";
|
|
@@ -81,6 +82,20 @@ export interface HarneryContextOpts {
|
|
|
81
82
|
skipCommands?: readonly string[];
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
/** One row in a `harn env` section report. */
|
|
86
|
+
export interface EnvCheck {
|
|
87
|
+
label: string;
|
|
88
|
+
value: string;
|
|
89
|
+
status?: "ok" | "missing" | "warn" | "info";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A `harn env` section: an async probe returning its rows. Hosts register extra
|
|
94
|
+
* sections (e.g. cloud-provider connectivity) via `context.envSections`; harnery
|
|
95
|
+
* core ships only the generic sections (runtimes, docker, git).
|
|
96
|
+
*/
|
|
97
|
+
export type EnvSection = () => Promise<EnvCheck[]>;
|
|
98
|
+
|
|
84
99
|
export interface HarneryProgramContext {
|
|
85
100
|
/** Project name (e.g., "my-monorepo"). Used in user-facing log lines + telemetry tags. */
|
|
86
101
|
projectName?: string;
|
|
@@ -98,6 +113,13 @@ export interface HarneryProgramContext {
|
|
|
98
113
|
* submodule row.
|
|
99
114
|
*/
|
|
100
115
|
submodules?: readonly string[];
|
|
116
|
+
/**
|
|
117
|
+
* Extra `harn env` sections keyed by name (e.g. `{ gcp, bq }`). Merged into
|
|
118
|
+
* the built-in generic sections (runtimes, docker, git), so a host can expose
|
|
119
|
+
* `harn env <name>` for its own environment probes without harnery core
|
|
120
|
+
* carrying provider-specific checks. harn standalone ships none.
|
|
121
|
+
*/
|
|
122
|
+
envSections?: Record<string, EnvSection>;
|
|
101
123
|
/**
|
|
102
124
|
* Optional callback that returns extra HTTP headers to attach to outbound
|
|
103
125
|
* `fetch` calls based on the target URL. Useful for consumers that need
|
|
@@ -237,6 +259,7 @@ export function createHarneryProgram(opts: HarneryContextOpts = {}): Command {
|
|
|
237
259
|
registerContextCommand(program, emit, opts.context);
|
|
238
260
|
registerScratchCommand(program, emit);
|
|
239
261
|
registerDecisionCommand(program, emit);
|
|
262
|
+
registerDevtoolsCommand(program, emit);
|
|
240
263
|
registerTunnelCommand(program, emit, opts.context);
|
|
241
264
|
registerDocsCommand(program, emit, opts.context);
|
|
242
265
|
registerAgentsCommand(program, emit);
|
package/src/commands/agents.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
normalizeHarness,
|
|
37
37
|
readHeartbeat,
|
|
38
38
|
resolveOwner,
|
|
39
|
+
resolveOwnerBySessionEnv,
|
|
39
40
|
resolveOwnerWithSource,
|
|
40
41
|
} from "../core/agents/index.ts";
|
|
41
42
|
import { resolveBinName } from "../core/config.ts";
|
|
@@ -46,6 +47,7 @@ import { resolveBinName } from "../core/config.ts";
|
|
|
46
47
|
const STREAM_SCAN_CAP_BYTES = 128 * 1024 * 1024; // 128 MiB
|
|
47
48
|
|
|
48
49
|
import { parsePsChainLine } from "../core/hooks/resolve/anchor.ts";
|
|
50
|
+
import { appendEntry, resolveOwnerByName } from "../core/scratch/index.ts";
|
|
49
51
|
import {
|
|
50
52
|
buildCouncilId,
|
|
51
53
|
buildInviteMarkdown,
|
|
@@ -80,7 +82,6 @@ import {
|
|
|
80
82
|
lookupById as lookupIdentityById,
|
|
81
83
|
lookupByName as lookupIdentityByName,
|
|
82
84
|
} from "../lib/identities/index.ts";
|
|
83
|
-
import { appendEntry, resolveOwnerByName } from "../lib/scratch/index.ts";
|
|
84
85
|
|
|
85
86
|
const FRESHNESS_SECS = 600; // 10-minute heartbeat-freshness window.
|
|
86
87
|
|
|
@@ -166,8 +167,7 @@ export function registerAgentsCommand(program: Command, emitParam: EmitContext):
|
|
|
166
167
|
cmd
|
|
167
168
|
.command("show <name>")
|
|
168
169
|
.description(
|
|
169
|
-
"Deep-dive on one peer agent: registry state (files held, last tool, task) " +
|
|
170
|
-
"plus claude-sessions history (latest title, recent prompts, recent tools, tool-usage tallies). " +
|
|
170
|
+
"Deep-dive on one peer agent: registry state (files held, last tool, task, turn summary). " +
|
|
171
171
|
"Disambiguates name → instance_id via prefix match.",
|
|
172
172
|
)
|
|
173
173
|
.option("--json", "JSON envelope output")
|
|
@@ -694,6 +694,7 @@ function runWhoami(opts: { json?: boolean }): void {
|
|
|
694
694
|
process.exit(1);
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
+
ensureCursorSession(root);
|
|
697
698
|
const resolved = resolveOwnerWithSource();
|
|
698
699
|
const myOwner = resolved.owner;
|
|
699
700
|
if (!myOwner) {
|
|
@@ -1172,6 +1173,50 @@ function formatLocalShort(iso: string): string {
|
|
|
1172
1173
|
}).format(d);
|
|
1173
1174
|
}
|
|
1174
1175
|
|
|
1176
|
+
function cursorEnvSessionId(): string | null {
|
|
1177
|
+
const raw = process.env.CURSOR_SESSION_ID?.trim() || process.env.CURSOR_CONVERSATION_ID?.trim();
|
|
1178
|
+
if (!raw) return null;
|
|
1179
|
+
return raw.startsWith("bc-") && raw.length > 3 ? raw.slice(3) : raw;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function shouldBootstrapCursorSession(): boolean {
|
|
1183
|
+
return process.env.CURSOR_AGENT === "1" && cursorEnvSessionId() !== null;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function ensureCursorSession(root: string): void {
|
|
1187
|
+
if (!shouldBootstrapCursorSession()) return;
|
|
1188
|
+
if (resolveOwnerBySessionEnv(root)) return;
|
|
1189
|
+
|
|
1190
|
+
const sessionId = cursorEnvSessionId();
|
|
1191
|
+
if (!sessionId) return;
|
|
1192
|
+
const agentHook = resolve(root, "harnery", "bin", "agent-hook");
|
|
1193
|
+
if (!existsSync(agentHook)) return;
|
|
1194
|
+
|
|
1195
|
+
const payload = JSON.stringify({
|
|
1196
|
+
conversation_id: sessionId,
|
|
1197
|
+
session_id: sessionId,
|
|
1198
|
+
hook_event_name: "sessionStart",
|
|
1199
|
+
workspace_roots: [root],
|
|
1200
|
+
cwd: root,
|
|
1201
|
+
composer_mode: "agent",
|
|
1202
|
+
is_background_agent: false,
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
spawnSync("bash", [agentHook, "session-start", "--harness", "cursor"], {
|
|
1206
|
+
input: payload,
|
|
1207
|
+
cwd: root,
|
|
1208
|
+
encoding: "utf8",
|
|
1209
|
+
timeout: 3000,
|
|
1210
|
+
env: {
|
|
1211
|
+
...process.env,
|
|
1212
|
+
HARNERY_AGENT_COORD_PLATFORM: "cursor",
|
|
1213
|
+
HARNERY_COORD_ROOT_OVERRIDE: root,
|
|
1214
|
+
CURSOR_SESSION_ID: sessionId,
|
|
1215
|
+
CURSOR_CONVERSATION_ID: sessionId,
|
|
1216
|
+
},
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1175
1220
|
function runReleaseClaim(path: string): void {
|
|
1176
1221
|
const root = monorepoRoot();
|
|
1177
1222
|
if (!root) {
|
|
@@ -1220,6 +1265,7 @@ function runSetTask(task: string, opts?: { sessionId?: string }): void {
|
|
|
1220
1265
|
// Identity: prefer explicit --session-id (the ppid-walk-free escape hatch,
|
|
1221
1266
|
// mirrors `status`), fall back to the ppid walk. Cursor shell tool calls
|
|
1222
1267
|
// don't descend from a pid-map-registered anchor, so the walk can miss there.
|
|
1268
|
+
if (!opts?.sessionId) ensureCursorSession(root);
|
|
1223
1269
|
const myOwner = opts?.sessionId ?? resolveOwner();
|
|
1224
1270
|
if (!myOwner) {
|
|
1225
1271
|
emit.error({
|
|
@@ -1272,6 +1318,7 @@ function runStatus(opts: { json?: boolean; sessionId?: string }): void {
|
|
|
1272
1318
|
|
|
1273
1319
|
// Identity resolution: prefer explicit --session-id (hook-friendly), fall
|
|
1274
1320
|
// back to ppid walk for interactive shell usage.
|
|
1321
|
+
if (!opts.sessionId) ensureCursorSession(root);
|
|
1275
1322
|
const myOwner = opts.sessionId ?? resolveOwner();
|
|
1276
1323
|
if (!myOwner) {
|
|
1277
1324
|
emit.error({
|
package/src/commands/deinit.ts
CHANGED
|
@@ -34,6 +34,7 @@ import type { Command } from "commander";
|
|
|
34
34
|
import type { EmitContext } from "../commander.ts";
|
|
35
35
|
import { DEFAULT_BIN_NAME } from "../core/config.ts";
|
|
36
36
|
import { HARNESS_SPECS, type HarnessId } from "../core/hooks/harness/events.ts";
|
|
37
|
+
import { removeInstructions } from "../lib/instructions/apply.ts";
|
|
37
38
|
import { type SettingsFile, unwireHooks } from "./init.ts";
|
|
38
39
|
|
|
39
40
|
interface DeinitOpts {
|
|
@@ -133,6 +134,10 @@ export function registerDeinitCommand(program: Command, emit: EmitContext, binNa
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
// ── 1b. agent-facing instructions block + skills ───────────────────────
|
|
138
|
+
const removed = removeInstructions(projectRoot, { harness, dryRun });
|
|
139
|
+
actions.push(...removed.actions, ...removed.warnings.map((w) => `! ${w}`));
|
|
140
|
+
|
|
136
141
|
// ── 2. coord root (opt-in; destructive) ────────────────────────────────
|
|
137
142
|
if (purgeState) {
|
|
138
143
|
if (!coordExists) {
|