harnery 0.5.0 → 0.7.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.
- package/README.md +16 -6
- package/dist/commander.d.ts +29 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +94 -33
- package/dist/commands/browse-ai.js +1 -1
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +41 -9
- package/dist/commands/cookies.js +1 -1
- package/dist/commands/decision.d.ts +4 -0
- package/dist/commands/decision.d.ts.map +1 -0
- package/dist/commands/decision.js +354 -0
- 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 +74 -2
- 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/fetch.js +1 -1
- 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/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +32 -8
- package/dist/core/agents/events/consume.d.ts +25 -2
- package/dist/core/agents/events/consume.d.ts.map +1 -1
- package/dist/core/agents/events/consume.js +55 -7
- package/dist/core/agents/events/emit.d.ts +2 -1
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +6 -1
- 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/scratch.d.ts +1 -1
- package/dist/core/agents/state/scratch.js +2 -2
- 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 -17
- 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/agent-browser/client.js +1 -1
- package/dist/lib/browser/client.d.ts +14 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +20 -0
- package/dist/lib/browser/index.d.ts +1 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/runts.d.ts +44 -0
- package/dist/lib/browser/runts.d.ts.map +1 -0
- package/dist/lib/browser/runts.js +193 -0
- package/dist/lib/completion/walk.js +1 -1
- package/dist/lib/cookies/client.d.ts +1 -1
- package/dist/lib/cookies/client.d.ts.map +1 -1
- package/dist/lib/cookies/client.js +1 -1
- package/dist/lib/decision/index.d.ts +212 -0
- package/dist/lib/decision/index.d.ts.map +1 -0
- package/dist/lib/decision/index.js +523 -0
- 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 +3 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +67 -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 +15 -10
- 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 +9 -6
- package/src/commander.ts +35 -0
- package/src/commands/agents.ts +97 -29
- package/src/commands/browse-ai.ts +1 -1
- package/src/commands/browse.ts +63 -8
- package/src/commands/cookies.ts +1 -1
- package/src/commands/decision.ts +438 -0
- package/src/commands/deinit.ts +5 -0
- package/src/commands/devtools.ts +284 -0
- package/src/commands/docs.ts +86 -2
- package/src/commands/doctor.ts +13 -4
- package/src/commands/env.ts +11 -77
- package/src/commands/fetch.ts +1 -1
- 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/coord-client.ts +34 -7
- package/src/core/agents/events/consume.ts +65 -7
- package/src/core/agents/events/emit.ts +7 -1
- package/src/core/agents/rules/claim-conflict.ts +17 -6
- package/src/core/agents/state/scratch.ts +2 -2
- package/src/core/config.ts +15 -1
- package/src/core/hooks/cli.ts +3 -3
- package/src/core/hooks/effects/index.ts +23 -16
- 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/agent-browser/client.ts +1 -1
- package/src/lib/browser/client.ts +28 -0
- package/src/lib/browser/index.ts +4 -0
- package/src/lib/browser/runts.ts +218 -0
- package/src/lib/completion/walk.ts +1 -1
- package/src/lib/cookies/client.ts +2 -2
- package/src/lib/decision/index.ts +685 -0
- 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 +61 -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 +15 -10
- 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") {
|
|
@@ -97,5 +101,6 @@ const server = Bun.serve({
|
|
|
97
101
|
// This worker runs detached via `bun run gate.ts`, outside the CLI command
|
|
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
|
-
console.log(`
|
|
101
|
-
console.log(`
|
|
104
|
+
console.log(`harn-tunnel-gate :${server.port} -> ${UPSTREAM_HTTP} (Host: ${VHOST})`); // lint-ok-emission: detached worker, see file note above
|
|
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.0",
|
|
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",
|
|
@@ -128,6 +128,9 @@
|
|
|
128
128
|
"prepublishOnly": "npm run clean && npm run build",
|
|
129
129
|
"typecheck": "bun x tsc --noEmit",
|
|
130
130
|
"lint": "bun x @biomejs/biome check src",
|
|
131
|
+
"check:portability": "bun run scripts/check-portability.ts",
|
|
132
|
+
"check:layering": "bun run scripts/check-layering.ts",
|
|
133
|
+
"lint:fix": "bun x @biomejs/biome check --write src",
|
|
131
134
|
"format": "bun x @biomejs/biome format --write src",
|
|
132
135
|
"test": "bun test src tests",
|
|
133
136
|
"test:web": "cd web && bun install && bun test",
|
package/src/commander.ts
CHANGED
|
@@ -27,7 +27,9 @@ import { registerCompletionCommand } from "./commands/completion.ts";
|
|
|
27
27
|
import { registerConfigGetCommand } from "./commands/config-get.ts";
|
|
28
28
|
import { registerContextCommand } from "./commands/context.ts";
|
|
29
29
|
import { registerCookiesCommand } from "./commands/cookies.ts";
|
|
30
|
+
import { registerDecisionCommand } from "./commands/decision.ts";
|
|
30
31
|
import { registerDeinitCommand } from "./commands/deinit.ts";
|
|
32
|
+
import { registerDevtoolsCommand } from "./commands/devtools.ts";
|
|
31
33
|
import { registerDocsCommand } from "./commands/docs.ts";
|
|
32
34
|
import { registerDoctorCommand } from "./commands/doctor.ts";
|
|
33
35
|
import { registerEditBatchCommand } from "./commands/edit-batch.ts";
|
|
@@ -80,6 +82,20 @@ export interface HarneryContextOpts {
|
|
|
80
82
|
skipCommands?: readonly string[];
|
|
81
83
|
}
|
|
82
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
|
+
|
|
83
99
|
export interface HarneryProgramContext {
|
|
84
100
|
/** Project name (e.g., "my-monorepo"). Used in user-facing log lines + telemetry tags. */
|
|
85
101
|
projectName?: string;
|
|
@@ -97,6 +113,13 @@ export interface HarneryProgramContext {
|
|
|
97
113
|
* submodule row.
|
|
98
114
|
*/
|
|
99
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>;
|
|
100
123
|
/**
|
|
101
124
|
* Optional callback that returns extra HTTP headers to attach to outbound
|
|
102
125
|
* `fetch` calls based on the target URL. Useful for consumers that need
|
|
@@ -129,6 +152,16 @@ export interface HarneryProgramContext {
|
|
|
129
152
|
* exclusions (`.claude/`, `.harnery/`, `.codex/`, `.cursor/`).
|
|
130
153
|
*/
|
|
131
154
|
extraDocsExcludedPrefixes?: readonly string[];
|
|
155
|
+
/**
|
|
156
|
+
* Filenames permitted at the host project's `docs/` root (parent repo only).
|
|
157
|
+
* When set, `harn docs lint` flags any other `.md`/`.json` file sitting
|
|
158
|
+
* loose at `docs/` root (rule `docs-root-file`) — topic docs belong in
|
|
159
|
+
* `docs/<topic>/` subdirs. Names are matched exactly (basename). When
|
|
160
|
+
* omitted or empty, the rule is a no-op, so standalone `harn` and consumers
|
|
161
|
+
* that don't opt in are unaffected. Submodule `docs/` roots are never
|
|
162
|
+
* checked (their entry tiers differ from the parent's).
|
|
163
|
+
*/
|
|
164
|
+
docsRootAllowlist?: readonly string[];
|
|
132
165
|
/**
|
|
133
166
|
* Default Host header for `tunnel up` when `--vhost` is omitted: a literal
|
|
134
167
|
* host, or a resolver evaluated at start time (e.g. read a dev stack's
|
|
@@ -225,6 +258,8 @@ export function createHarneryProgram(opts: HarneryContextOpts = {}): Command {
|
|
|
225
258
|
registerCompletionCommand(program, emit, opts.context);
|
|
226
259
|
registerContextCommand(program, emit, opts.context);
|
|
227
260
|
registerScratchCommand(program, emit);
|
|
261
|
+
registerDecisionCommand(program, emit);
|
|
262
|
+
registerDevtoolsCommand(program, emit);
|
|
228
263
|
registerTunnelCommand(program, emit, opts.context);
|
|
229
264
|
registerDocsCommand(program, emit, opts.context);
|
|
230
265
|
registerAgentsCommand(program, emit);
|