@holdyourvoice/hyv 3.3.5 → 3.4.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 +66 -116
- package/dist/agents/catalog.js +118 -0
- package/dist/agents/catalog.test.js +60 -0
- package/dist/agents/emit.js +42 -0
- package/dist/agents/emit.test.js +64 -0
- package/dist/agents/index.js +4 -0
- package/dist/agents/load.js +184 -0
- package/dist/agents/load.test.js +149 -0
- package/dist/agents/types.js +18 -0
- package/dist/cli.js +464 -317
- package/dist/cli.test.js +76 -0
- package/dist/fact-linter.js +107 -98
- package/dist/mcp.js +43 -192
- package/dist/rebuild-task.test.js +1 -1
- package/dist/release-audit.test.js +5 -1
- package/dist/stage1-evaluation.js +74 -48
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/skills/hyv-analyze/SKILL.md +24 -0
- package/skills/hyv-analyze/agent.json +49 -0
- package/skills/hyv-analyze/agents/openai.yaml +4 -0
- package/skills/hyv-apply-hidden-text-policy/SKILL.md +24 -0
- package/skills/hyv-apply-hidden-text-policy/agent.json +49 -0
- package/skills/hyv-apply-hidden-text-policy/agents/openai.yaml +4 -0
- package/skills/hyv-apply-rebuild/SKILL.md +24 -0
- package/skills/hyv-apply-rebuild/agent.json +49 -0
- package/skills/hyv-apply-rebuild/agents/openai.yaml +4 -0
- package/skills/hyv-apply-rewrite/SKILL.md +24 -0
- package/skills/hyv-apply-rewrite/agent.json +48 -0
- package/skills/hyv-apply-rewrite/agents/openai.yaml +4 -0
- package/skills/hyv-batch-analyze/SKILL.md +24 -0
- package/skills/hyv-batch-analyze/agent.json +45 -0
- package/skills/hyv-batch-analyze/agents/openai.yaml +4 -0
- package/skills/hyv-fact-lint/SKILL.md +24 -0
- package/skills/hyv-fact-lint/agent.json +50 -0
- package/skills/hyv-fact-lint/agents/openai.yaml +4 -0
- package/skills/hyv-final-check/SKILL.md +24 -0
- package/skills/hyv-final-check/agent.json +43 -0
- package/skills/hyv-final-check/agents/openai.yaml +4 -0
- package/skills/hyv-hygiene/SKILL.md +24 -0
- package/skills/hyv-hygiene/agent.json +50 -0
- package/skills/hyv-hygiene/agents/openai.yaml +4 -0
- package/skills/hyv-inspect-hidden-text/SKILL.md +24 -0
- package/skills/hyv-inspect-hidden-text/agent.json +48 -0
- package/skills/hyv-inspect-hidden-text/agents/openai.yaml +4 -0
- package/skills/hyv-learning/SKILL.md +24 -0
- package/skills/hyv-learning/agent.json +48 -0
- package/skills/hyv-learning/agents/openai.yaml +4 -0
- package/skills/hyv-lifecycle/SKILL.md +24 -0
- package/skills/hyv-lifecycle/agent.json +48 -0
- package/skills/hyv-lifecycle/agents/openai.yaml +4 -0
- package/skills/hyv-logic-lint/SKILL.md +24 -0
- package/skills/hyv-logic-lint/agent.json +47 -0
- package/skills/hyv-logic-lint/agents/openai.yaml +4 -0
- package/skills/hyv-mcp/SKILL.md +24 -0
- package/skills/hyv-mcp/agent.json +43 -0
- package/skills/hyv-mcp/agents/openai.yaml +4 -0
- package/skills/hyv-patterns/SKILL.md +24 -0
- package/skills/hyv-patterns/agent.json +43 -0
- package/skills/hyv-patterns/agents/openai.yaml +4 -0
- package/skills/hyv-prepare-judgment/SKILL.md +24 -0
- package/skills/hyv-prepare-judgment/agent.json +52 -0
- package/skills/hyv-prepare-judgment/agents/openai.yaml +4 -0
- package/skills/hyv-prepare-rebuild/SKILL.md +24 -0
- package/skills/hyv-prepare-rebuild/agent.json +54 -0
- package/skills/hyv-prepare-rebuild/agents/openai.yaml +4 -0
- package/skills/hyv-prepare-rewrite/SKILL.md +24 -0
- package/skills/hyv-prepare-rewrite/agent.json +51 -0
- package/skills/hyv-prepare-rewrite/agents/openai.yaml +4 -0
- package/skills/hyv-profile/SKILL.md +24 -0
- package/skills/hyv-profile/agent.json +50 -0
- package/skills/hyv-profile/agents/openai.yaml +4 -0
- package/skills/hyv-rebuild-writer-request/SKILL.md +24 -0
- package/skills/hyv-rebuild-writer-request/agent.json +47 -0
- package/skills/hyv-rebuild-writer-request/agents/openai.yaml +4 -0
- package/skills/hyv-reduce-judgment/SKILL.md +24 -0
- package/skills/hyv-reduce-judgment/agent.json +45 -0
- package/skills/hyv-reduce-judgment/agents/openai.yaml +4 -0
- package/skills/hyv-rewrite-prompt/SKILL.md +24 -0
- package/skills/hyv-rewrite-prompt/agent.json +48 -0
- package/skills/hyv-rewrite-prompt/agents/openai.yaml +4 -0
- package/skills/hyv-verify/SKILL.md +24 -0
- package/skills/hyv-verify/agent.json +49 -0
- package/skills/hyv-verify/agents/openai.yaml +4 -0
- package/skills/hyv-verify-spec/SKILL.md +24 -0
- package/skills/hyv-verify-spec/agent.json +50 -0
- package/skills/hyv-verify-spec/agents/openai.yaml +4 -0
package/Readme.md
CHANGED
|
@@ -2,168 +2,118 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@holdyourvoice/hyv)
|
|
4
4
|
|
|
5
|
-
Hold Your Voice is a local
|
|
5
|
+
Hold Your Voice (`hyv`) is a local writing checker. It helps you edit AI-assisted writing without losing your own writing patterns.
|
|
6
6
|
|
|
7
|
-
It runs two checks
|
|
7
|
+
It runs two independent checks:
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
9
|
+
- VoiceDNA compares a draft with a profile built from your writing samples.
|
|
10
|
+
- AI Editor finds deterministic patterns that often make writing generic or formulaic.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
The package also checks hidden Unicode, source-backed facts, document logic, and protected claims. All checks run locally, without model calls, automatic draft changes, or runtime network requests.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## install
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
**Requirements:** Node.js 20+, npm, and at least two writing samples you have the right to use.
|
|
19
|
-
|
|
20
|
-
### Install
|
|
16
|
+
You need Node.js 20 or newer and at least two writing samples you have the right to use.
|
|
21
17
|
|
|
22
18
|
```bash
|
|
23
19
|
npm install --global @holdyourvoice/hyv
|
|
24
|
-
hyv patterns
|
|
25
20
|
```
|
|
26
21
|
|
|
27
|
-
|
|
22
|
+
For a one-off command, replace `hyv` with `npx @holdyourvoice/hyv`.
|
|
23
|
+
|
|
24
|
+
## basic workflow
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
1. Build a local profile from your samples.
|
|
30
27
|
|
|
31
28
|
```bash
|
|
32
|
-
hyv profile profile.json samples/one.md samples/two.md
|
|
29
|
+
hyv profile profile.json samples/one.md samples/two.md
|
|
33
30
|
```
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
Add `--avoid=phrase` for wording that must block a draft. Repeat the option for more phrases.
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
2. Check a draft against that profile.
|
|
38
35
|
|
|
39
36
|
```bash
|
|
40
37
|
hyv analyze draft.md profile.json
|
|
41
38
|
```
|
|
42
39
|
|
|
43
|
-
The result
|
|
44
|
-
|
|
45
|
-
```json
|
|
46
|
-
{
|
|
47
|
-
"voiceDna": { "score": 93, "passed": true, "findings": [] },
|
|
48
|
-
"aiEditor": { "score": 88, "passed": true, "findings": [] },
|
|
49
|
-
"hygiene": { "suspiciousCount": 0, "fixableCount": 0, "hits": [] },
|
|
50
|
-
"passed": true
|
|
51
|
-
}
|
|
52
|
-
```
|
|
40
|
+
The result contains separate VoiceDNA and AI Editor reports. The top-level `passed` value is true only when every required check passes.
|
|
53
41
|
|
|
54
|
-
|
|
42
|
+
3. Create an editing brief, edit the draft, and verify the candidate.
|
|
55
43
|
|
|
56
44
|
```bash
|
|
57
45
|
hyv rewrite-prompt draft.md profile.json > rewrite-brief.md
|
|
58
46
|
hyv verify draft.md candidate.md profile.json
|
|
59
47
|
```
|
|
60
48
|
|
|
61
|
-
|
|
49
|
+
Send the brief to a human editor or a model you choose. Delivery stays under your control.
|
|
62
50
|
|
|
63
|
-
|
|
51
|
+
4. Check the exact text before delivery.
|
|
64
52
|
|
|
65
53
|
```bash
|
|
54
|
+
hyv final-check candidate.md
|
|
66
55
|
producer | hyv final-check -
|
|
67
|
-
hyv final-check final-response.md
|
|
68
56
|
```
|
|
69
57
|
|
|
70
|
-
`final-check`
|
|
58
|
+
`final-check` writes accepted text to stdout. It withholds output and exits with code `2` when unresolved hidden Unicode remains.
|
|
71
59
|
|
|
72
|
-
|
|
60
|
+
## commands
|
|
73
61
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
hyv
|
|
77
|
-
|
|
62
|
+
| Command | Purpose |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `hyv profile <profile.json> <sample...>` | Build a local profile from two or more samples. |
|
|
65
|
+
| `hyv analyze <draft> <profile.json>` | Run VoiceDNA, AI Editor, and hygiene checks. |
|
|
66
|
+
| `hyv hygiene <draft> [--fix]` | Inspect hidden Unicode or write a conservative cleaned copy. |
|
|
67
|
+
| `hyv inspect-hidden-text <draft> [policy.json]` | Inspect hidden text with an optional policy. |
|
|
68
|
+
| `hyv apply-hidden-text-policy <draft> <policy.json> <output>` | Apply approved hidden-text removals. |
|
|
69
|
+
| `hyv final-check <path\|->` | Gate the exact text before delivery. |
|
|
70
|
+
| `hyv fact-lint <draft\|-> --source=id:path` | Check claims against local source files. |
|
|
71
|
+
| `hyv logic-lint <draft\|-> [brief.json]` | Check deterministic document logic. |
|
|
72
|
+
| `hyv batch-analyze <draft...>` | Find repeated openings and endings across drafts. |
|
|
73
|
+
| `hyv rewrite-prompt <draft> <profile.json>` | Create a constrained editing brief. |
|
|
74
|
+
| `hyv prepare-rewrite ...` | Create a fingerprint-bound edit task. |
|
|
75
|
+
| `hyv apply-rewrite ...` | Apply and verify a response to an edit task. |
|
|
76
|
+
| `hyv prepare-judgment ...` | Create a pre-edit or post-candidate judgment task. |
|
|
77
|
+
| `hyv reduce-judgment <envelope...>` | Reduce judgments to SHIP, EDIT, REBUILD, CLEAR, or ESCALATE. |
|
|
78
|
+
| `hyv prepare-rebuild ...` | Create an authorized whole-document rebuild task. |
|
|
79
|
+
| `hyv rebuild-writer-request ...` | Create the writer-only part of a rebuild task. |
|
|
80
|
+
| `hyv apply-rebuild ...` | Apply and verify an authorized rebuild response. |
|
|
81
|
+
| `hyv verify <original> <candidate> <profile.json>` | Verify a candidate without changing learning state. |
|
|
82
|
+
| `hyv verify-spec ...` | Verify a candidate and a CopySpec. |
|
|
83
|
+
| `hyv lifecycle ...` | Run semantic review and final approval steps. |
|
|
84
|
+
| `hyv learning ...` | Inspect or change local profile learning. |
|
|
85
|
+
| `hyv patterns` | Print the active AI Editor rule catalog. |
|
|
86
|
+
| `hyv agent list\|validate\|describe\|emit` | Inspect or emit portable agent contracts. |
|
|
87
|
+
| `hyv mcp` | Start the local MCP server on standard input/output. |
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
## The editing loop
|
|
82
|
-
|
|
83
|
-
```mermaid
|
|
84
|
-
flowchart LR
|
|
85
|
-
S[Your samples] --> P[Build profile]
|
|
86
|
-
P --> D[VoiceDNA profile]
|
|
87
|
-
T[Draft] --> A[Analyze]
|
|
88
|
-
D --> A
|
|
89
|
-
A --> V[VoiceDNA report]
|
|
90
|
-
A --> E[AI Editor report]
|
|
91
|
-
V --> B[Tiered brief]
|
|
92
|
-
E --> B
|
|
93
|
-
B --> C[Human or chosen model]
|
|
94
|
-
T --> G[Verify candidate]
|
|
95
|
-
C --> G
|
|
96
|
-
D --> G
|
|
97
|
-
G --> R[Errors block; findings stay visible]
|
|
98
|
-
R --> H[final-check before output]
|
|
99
|
-
```
|
|
89
|
+
Most commands return JSON. Exit code `0` means the command completed, `2` means a content or policy gate failed, and `1` means the command or input was invalid.
|
|
100
90
|
|
|
101
|
-
|
|
91
|
+
Run `hyv <command>` without enough arguments to see its exact usage. Read the [CLI reference](docs/wiki/CLI-Reference.md) for every option.
|
|
102
92
|
|
|
103
|
-
##
|
|
93
|
+
## portable agents and MCP
|
|
104
94
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
| `hyv prepare-rewrite` / `apply-rewrite` | Fingerprint-bound sentence or range edits. |
|
|
111
|
-
| `hyv prepare-rebuild` / `apply-rebuild` | Whole-document rebuild after an authorized recommendation. |
|
|
112
|
-
| `hyv prepare-judgment` / `reduce-judgment` | Reduce findings to SHIP, EDIT, or REBUILD. |
|
|
113
|
-
| `hyv lifecycle` | Semantic and human-review lifecycle steps. |
|
|
114
|
-
| `hyv learning show` | Inspect local voice-memory preferences. |
|
|
115
|
-
|
|
116
|
-
## Commands
|
|
117
|
-
|
|
118
|
-
| Command | Input | Output |
|
|
119
|
-
| --- | --- | --- |
|
|
120
|
-
| `hyv profile <profile.json> <sample...>` | Two or more text files | Profile JSON |
|
|
121
|
-
| `hyv analyze <draft> <profile.json>` | Draft and profile | Analysis JSON |
|
|
122
|
-
| `hyv hygiene <draft> [--fix] [--output=path]` | Draft | Hygiene report or cleaned copy plus receipt |
|
|
123
|
-
| `hyv final-check <path\|->` | Any final text | Accepted text on stdout or a withheld-output report |
|
|
124
|
-
| `hyv rewrite-prompt <draft> <profile.json>` | Draft and profile | Markdown editing brief |
|
|
125
|
-
| `hyv prepare-rewrite <draft> <profile.json> <task.json>` | Draft and profile | Versioned task file |
|
|
126
|
-
| `hyv apply-rewrite <task.json> <response.json> <profile.json>` | Task, response, profile | Candidate evaluation JSON |
|
|
127
|
-
| `hyv prepare-judgment <pre-edit\|post-candidate> <kind> <draft> <profile.json> <task.json> [candidate.md]` | Draft, profile, optional candidate | Versioned judgment task |
|
|
128
|
-
| `hyv reduce-judgment <envelope.json> ...` | Signed judgment envelopes | Recommendation JSON |
|
|
129
|
-
| `hyv prepare-rebuild <draft> <profile.json> <reduction.json> <copy-spec.json> <task.json> [--recomposition-policy policy.json]` | Draft, recommendation, CopySpec, capability, optional policy | Versioned rebuild task |
|
|
130
|
-
| `hyv apply-rebuild <task.json> <response.json> <profile.json>` | Task, response, profile, capability | Candidate evaluation JSON |
|
|
131
|
-
| `hyv verify <original> <candidate> <profile.json>` | Original, candidate, profile | Verification JSON and exit code |
|
|
132
|
-
| `hyv verify-spec <original> <candidate> <profile.json> <copy-spec.json>` | Original, candidate, profile, CopySpec | Verification JSON with hard claim gate |
|
|
133
|
-
| `hyv learning <show\|inspect\|add\|record\|ratify\|supersede\|migrate\|clear> ...` | Profile, operation, bounded metadata | Preferences or a text-free receipt |
|
|
134
|
-
| `hyv lifecycle <prepare-semantic\|submit-verdict\|inspect\|validate-final-approval\|finalize> ...` | Versioned lifecycle artifacts | Lifecycle artifact or metadata |
|
|
135
|
-
| `hyv patterns` | None | Ruleset JSON |
|
|
136
|
-
|
|
137
|
-
Every file argument can be `-` when the command accepts input on standard input. Use `npx @holdyourvoice/hyv <command>` if you have not installed the CLI globally.
|
|
138
|
-
|
|
139
|
-
## Privacy
|
|
140
|
-
|
|
141
|
-
Your samples, drafts, profiles, and candidates stay on your machine. Verification is read-only. Learning commands can write text-free local events under `~/.hyv/learning/` — profile fingerprint, finding IDs, counts, and an opaque digest. No writing text is uploaded, and the package makes no runtime network requests.
|
|
142
|
-
|
|
143
|
-
Keep writing samples, edit histories, and client text out of public commits unless you hold the rights and a provenance record. A profile is aggregated JSON and can still reveal vocabulary, so store private profiles outside public repositories.
|
|
144
|
-
|
|
145
|
-
## Documentation
|
|
146
|
-
|
|
147
|
-
| Read this | When you need |
|
|
148
|
-
| --- | --- |
|
|
149
|
-
| [The Wiki](https://github.com/shashank-sn/holdyourvoice/wiki) | Product and contributor docs. |
|
|
150
|
-
| [Architecture](docs/ARCHITECTURE.md) | Source boundaries and extension rules. |
|
|
151
|
-
| [Prompt contract](docs/PROMPT-CONTRACT.md) | The tier order and editing constraints. |
|
|
152
|
-
| [VoiceDNA](docs/VOICE-DNA.md) | The 13 profile elements. |
|
|
153
|
-
| [Fact linter](docs/wiki/Fact-Linter.md) | The source-consistency checker. |
|
|
154
|
-
| [Support](SUPPORT.md) | Funding without a feature gate. |
|
|
95
|
+
The `skills/hyv-*` directories package the CLI workflows as portable agent contracts. Use `hyv agent validate` to check them and `hyv agent emit` to create a host-specific prompt or JSON contract.
|
|
96
|
+
|
|
97
|
+
The MCP server exposes the same local engine for compatible hosts. Read [Portable Agents](docs/wiki/Portable-Agents.md), [Claude Desktop setup](docs/CLAUDE-DESKTOP.md), or [Claude Code setup](docs/CLAUDE-CODE.md).
|
|
98
|
+
|
|
99
|
+
## privacy and safety
|
|
155
100
|
|
|
156
|
-
|
|
101
|
+
Drafts, samples, profiles, candidates, and source files stay on your machine. The package has no accounts, telemetry, hosted analysis, or runtime network requests.
|
|
102
|
+
|
|
103
|
+
Learning commands can write text-free events under `~/.hyv/learning/`. A manually added learning instruction is stored as entered. Keep private writing, profiles, and client data out of public repositories.
|
|
104
|
+
|
|
105
|
+
VoiceDNA fit, AI-pattern findings, fact consistency, and human approval are separate results. A clean report only states that its configured checks passed. Authorship, factual truth, and publication quality still need separate evidence or review.
|
|
106
|
+
|
|
107
|
+
## development
|
|
157
108
|
|
|
158
109
|
```bash
|
|
110
|
+
npm ci
|
|
159
111
|
npm test
|
|
160
112
|
npm run check:release
|
|
161
113
|
```
|
|
162
114
|
|
|
163
|
-
Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
|
|
164
|
-
|
|
165
|
-
`@holdyourvoice/hyv` publishes automatically when a change reaches `main`. Bump the version in `package.json` in the same pull request as a release-worthy change; the workflow publishes only if that version is not already on npm.
|
|
115
|
+
Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request. The main design boundaries are in [Architecture](docs/ARCHITECTURE.md) and the full user guides are in the [wiki](https://github.com/shashank-sn/holdyourvoice/wiki).
|
|
166
116
|
|
|
167
|
-
##
|
|
117
|
+
## license
|
|
168
118
|
|
|
169
|
-
[MIT](LICENSE). Third-party writing and data
|
|
119
|
+
[MIT](LICENSE). Third-party writing and data keep their own rights.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const CATALOG = {
|
|
2
|
+
generic: {
|
|
3
|
+
id: 'generic', display_name: 'Generic coding environment', native_skills: false, subagents: false,
|
|
4
|
+
hooks: false, blocking_approvals: false, file_edits: false, command_execution: false, browser_automation: false,
|
|
5
|
+
background_tasks: false, cli: true, repository_read: false,
|
|
6
|
+
context_capacity: 'host-defined', filesystem_mode: 'host-defined', network_policy: 'host-defined',
|
|
7
|
+
subagent_isolation: false, session_reset: false, structured_output: false,
|
|
8
|
+
integration: 'portable Markdown instructions and standalone CLI',
|
|
9
|
+
},
|
|
10
|
+
codex: {
|
|
11
|
+
id: 'codex', display_name: 'Codex', native_skills: true, subagents: true,
|
|
12
|
+
hooks: false, blocking_approvals: true, file_edits: true, command_execution: true, browser_automation: false,
|
|
13
|
+
background_tasks: false, cli: true, repository_read: true,
|
|
14
|
+
context_capacity: 'host-defined', filesystem_mode: 'sandboxed', network_policy: 'approval-gated',
|
|
15
|
+
subagent_isolation: true, session_reset: true, structured_output: true,
|
|
16
|
+
integration: 'native skills with AGENTS.md fallback',
|
|
17
|
+
},
|
|
18
|
+
'claude-code': {
|
|
19
|
+
id: 'claude-code', display_name: 'Claude Code', native_skills: true, subagents: true,
|
|
20
|
+
hooks: true, blocking_approvals: true, file_edits: true, command_execution: true, browser_automation: false,
|
|
21
|
+
background_tasks: false, cli: true, repository_read: true,
|
|
22
|
+
context_capacity: 'host-defined', filesystem_mode: 'host-defined', network_policy: 'approval-gated',
|
|
23
|
+
subagent_isolation: true, session_reset: false, structured_output: true,
|
|
24
|
+
integration: 'native skills and agent instructions',
|
|
25
|
+
},
|
|
26
|
+
cursor: {
|
|
27
|
+
id: 'cursor', display_name: 'Cursor', native_skills: false, subagents: false,
|
|
28
|
+
hooks: false, blocking_approvals: false, file_edits: true, command_execution: true, browser_automation: false,
|
|
29
|
+
background_tasks: false, cli: true, repository_read: true,
|
|
30
|
+
context_capacity: 'host-defined', filesystem_mode: 'workspace', network_policy: 'host-defined',
|
|
31
|
+
subagent_isolation: false, session_reset: false, structured_output: false,
|
|
32
|
+
integration: 'generated rules and portable instructions',
|
|
33
|
+
},
|
|
34
|
+
copilot: {
|
|
35
|
+
id: 'copilot', display_name: 'GitHub Copilot', native_skills: false, subagents: false,
|
|
36
|
+
hooks: false, blocking_approvals: false, file_edits: true, command_execution: false, browser_automation: false,
|
|
37
|
+
background_tasks: false, cli: true, repository_read: true,
|
|
38
|
+
context_capacity: 'host-defined', filesystem_mode: 'workspace', network_policy: 'host-defined',
|
|
39
|
+
subagent_isolation: false, session_reset: false, structured_output: false,
|
|
40
|
+
integration: 'repository instructions and custom agent definitions',
|
|
41
|
+
},
|
|
42
|
+
'gemini-cli': {
|
|
43
|
+
id: 'gemini-cli', display_name: 'Gemini CLI', native_skills: false, subagents: false,
|
|
44
|
+
hooks: false, blocking_approvals: true, file_edits: true, command_execution: true, browser_automation: false,
|
|
45
|
+
background_tasks: true, cli: true, repository_read: true,
|
|
46
|
+
context_capacity: 'host-defined', filesystem_mode: 'workspace', network_policy: 'approval-gated',
|
|
47
|
+
subagent_isolation: false, session_reset: false, structured_output: true,
|
|
48
|
+
integration: 'terminal-agent instructions and standalone CLI',
|
|
49
|
+
},
|
|
50
|
+
'ide-agent': {
|
|
51
|
+
id: 'ide-agent', display_name: 'IDE coding agent', native_skills: false, subagents: false,
|
|
52
|
+
hooks: false, blocking_approvals: false, file_edits: true, command_execution: false, browser_automation: false,
|
|
53
|
+
background_tasks: false, cli: true, repository_read: true,
|
|
54
|
+
context_capacity: 'host-defined', filesystem_mode: 'workspace', network_policy: 'host-defined',
|
|
55
|
+
subagent_isolation: false, session_reset: false, structured_output: false,
|
|
56
|
+
integration: 'generated rules or portable instructions',
|
|
57
|
+
},
|
|
58
|
+
windsurf: {
|
|
59
|
+
id: 'windsurf', display_name: 'Windsurf', native_skills: false, subagents: false,
|
|
60
|
+
hooks: false, blocking_approvals: false, file_edits: true, command_execution: true, browser_automation: false,
|
|
61
|
+
background_tasks: false, cli: true, repository_read: true,
|
|
62
|
+
context_capacity: 'host-defined', filesystem_mode: 'workspace', network_policy: 'host-defined',
|
|
63
|
+
subagent_isolation: false, session_reset: false, structured_output: false,
|
|
64
|
+
integration: 'generated workspace rules and standalone CLI',
|
|
65
|
+
},
|
|
66
|
+
cline: {
|
|
67
|
+
id: 'cline', display_name: 'Cline', native_skills: false, subagents: false,
|
|
68
|
+
hooks: false, blocking_approvals: false, file_edits: true, command_execution: true, browser_automation: false,
|
|
69
|
+
background_tasks: false, cli: true, repository_read: true,
|
|
70
|
+
context_capacity: 'host-defined', filesystem_mode: 'workspace', network_policy: 'host-defined',
|
|
71
|
+
subagent_isolation: false, session_reset: false, structured_output: false,
|
|
72
|
+
integration: 'generated workspace rules and standalone CLI',
|
|
73
|
+
},
|
|
74
|
+
'roo-code': {
|
|
75
|
+
id: 'roo-code', display_name: 'Roo Code', native_skills: false, subagents: false,
|
|
76
|
+
hooks: false, blocking_approvals: false, file_edits: true, command_execution: true, browser_automation: false,
|
|
77
|
+
background_tasks: false, cli: true, repository_read: true,
|
|
78
|
+
context_capacity: 'host-defined', filesystem_mode: 'workspace', network_policy: 'host-defined',
|
|
79
|
+
subagent_isolation: false, session_reset: false, structured_output: false,
|
|
80
|
+
integration: 'generated workspace rules and standalone CLI',
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
export function resolveHost(id) {
|
|
84
|
+
return CATALOG[id] ?? CATALOG.generic;
|
|
85
|
+
}
|
|
86
|
+
export function listHosts() {
|
|
87
|
+
return Object.values(CATALOG).sort((a, b) => a.id.localeCompare(b.id));
|
|
88
|
+
}
|
|
89
|
+
function hostSupports(host, permission) {
|
|
90
|
+
switch (permission) {
|
|
91
|
+
case 'read_repository': return host.repository_read;
|
|
92
|
+
case 'write_repository': return host.file_edits;
|
|
93
|
+
case 'execute_commands': return host.command_execution;
|
|
94
|
+
case 'network': return false;
|
|
95
|
+
case 'browser_automation': return host.browser_automation;
|
|
96
|
+
case 'git_write':
|
|
97
|
+
case 'pull_request_write': return host.command_execution;
|
|
98
|
+
case 'subagents': return host.subagents;
|
|
99
|
+
default: return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function runtime(agent, hostID) {
|
|
103
|
+
const host = resolveHost(hostID);
|
|
104
|
+
const available = [];
|
|
105
|
+
const unavailable = [];
|
|
106
|
+
for (const permission of agent.permissions) {
|
|
107
|
+
if (hostSupports(host, permission))
|
|
108
|
+
available.push(permission);
|
|
109
|
+
else
|
|
110
|
+
unavailable.push({ capability: permission, status: 'NOT_AVAILABLE' });
|
|
111
|
+
}
|
|
112
|
+
let mode = 'procedural';
|
|
113
|
+
if (unavailable.length > 0)
|
|
114
|
+
mode = 'prompt-only';
|
|
115
|
+
if (host.native_skills && unavailable.length === 0)
|
|
116
|
+
mode = 'native';
|
|
117
|
+
return { agent, host, available_capabilities: available, unavailable_capabilities: unavailable, execution_mode: mode };
|
|
118
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { resolveHost, listHosts, runtime } from './catalog.js';
|
|
4
|
+
import { AGENT_SCHEMA_VERSION } from './types.js';
|
|
5
|
+
function minimalDescriptor(id, permissions) {
|
|
6
|
+
return {
|
|
7
|
+
schema_version: AGENT_SCHEMA_VERSION,
|
|
8
|
+
id,
|
|
9
|
+
title: id,
|
|
10
|
+
description: 'test',
|
|
11
|
+
instruction_file: 'SKILL.md',
|
|
12
|
+
role: 'Tester',
|
|
13
|
+
workflow_phase: 'test',
|
|
14
|
+
input: { required: ['draft'], optional: [] },
|
|
15
|
+
output: { required: ['report'], optional: [] },
|
|
16
|
+
evidence_requirements: ['evidence'],
|
|
17
|
+
permissions,
|
|
18
|
+
stop_conditions: ['stop'],
|
|
19
|
+
tool_free_mode: { available: true, behavior: 'report', unavailable_statuses: ['NOT_AVAILABLE'] },
|
|
20
|
+
handoff_to: [],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
test('resolves known hosts and falls back to generic for unknown ids', () => {
|
|
24
|
+
assert.equal(resolveHost('codex').id, 'codex');
|
|
25
|
+
assert.equal(resolveHost('future-ide').id, 'generic');
|
|
26
|
+
assert.equal(resolveHost('future-ide').native_skills, false);
|
|
27
|
+
assert.equal(resolveHost('future-ide').cli, true);
|
|
28
|
+
});
|
|
29
|
+
test('catalog has unique host ids and includes generic', () => {
|
|
30
|
+
const hosts = listHosts();
|
|
31
|
+
const ids = hosts.map((host) => host.id);
|
|
32
|
+
assert.ok(ids.includes('generic'));
|
|
33
|
+
assert.equal(new Set(ids).size, ids.length);
|
|
34
|
+
});
|
|
35
|
+
test('runtime marks unsupported permissions unavailable and switches to prompt-only', () => {
|
|
36
|
+
const descriptor = minimalDescriptor('hyv-verify', ['read_repository', 'network', 'subagents']);
|
|
37
|
+
const resolved = runtime(descriptor, 'generic');
|
|
38
|
+
assert.deepEqual(resolved.available_capabilities, []);
|
|
39
|
+
assert.deepEqual(resolved.unavailable_capabilities, [
|
|
40
|
+
{ capability: 'read_repository', status: 'NOT_AVAILABLE' },
|
|
41
|
+
{ capability: 'network', status: 'NOT_AVAILABLE' },
|
|
42
|
+
{ capability: 'subagents', status: 'NOT_AVAILABLE' },
|
|
43
|
+
]);
|
|
44
|
+
assert.equal(resolved.execution_mode, 'prompt-only');
|
|
45
|
+
});
|
|
46
|
+
test('runtime uses native mode when the host supports every permission', () => {
|
|
47
|
+
const descriptor = minimalDescriptor('hyv-verify', ['read_repository', 'execute_commands']);
|
|
48
|
+
const resolved = runtime(descriptor, 'codex');
|
|
49
|
+
assert.deepEqual(resolved.available_capabilities, ['read_repository', 'execute_commands']);
|
|
50
|
+
assert.deepEqual(resolved.unavailable_capabilities, []);
|
|
51
|
+
assert.equal(resolved.execution_mode, 'native');
|
|
52
|
+
});
|
|
53
|
+
test('network is never available on any host', () => {
|
|
54
|
+
for (const host of listHosts()) {
|
|
55
|
+
const resolved = runtime(minimalDescriptor('hyv-verify', ['network']), host.id);
|
|
56
|
+
assert.deepEqual(resolved.available_capabilities, []);
|
|
57
|
+
assert.deepEqual(resolved.unavailable_capabilities, [{ capability: 'network', status: 'NOT_AVAILABLE' }]);
|
|
58
|
+
assert.equal(resolved.execution_mode, 'prompt-only');
|
|
59
|
+
}
|
|
60
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { runtime } from './catalog.js';
|
|
2
|
+
export function describe(pkg, hostID) {
|
|
3
|
+
return runtime(pkg.descriptor, hostID);
|
|
4
|
+
}
|
|
5
|
+
export function emitJson(pkg, hostID) {
|
|
6
|
+
return JSON.stringify(describe(pkg, hostID), null, 2);
|
|
7
|
+
}
|
|
8
|
+
function printable(values) {
|
|
9
|
+
return values.length === 0 ? 'none' : values.join(', ');
|
|
10
|
+
}
|
|
11
|
+
function printableUnavailable(values) {
|
|
12
|
+
return values.length === 0 ? 'none' : values.map(({ capability, status }) => `${capability} (${status})`).join(', ');
|
|
13
|
+
}
|
|
14
|
+
function runtimeSummary(descriptor) {
|
|
15
|
+
const host = descriptor.host;
|
|
16
|
+
return `Context capacity: ${host.context_capacity}; filesystem mode: ${host.filesystem_mode}; network policy: ${host.network_policy}; browser/UI: ${host.browser_automation}; subagent isolation: ${host.subagent_isolation}; session reset: ${host.session_reset}; structured output: ${host.structured_output}.`;
|
|
17
|
+
}
|
|
18
|
+
export function emitPrompt(pkg, hostID) {
|
|
19
|
+
const descriptor = pkg.descriptor;
|
|
20
|
+
const resolved = describe(pkg, hostID);
|
|
21
|
+
const lines = [];
|
|
22
|
+
lines.push(`# ${descriptor.title}`, '');
|
|
23
|
+
lines.push(`Role: ${descriptor.role}`);
|
|
24
|
+
lines.push(`Phase: ${descriptor.workflow_phase}`);
|
|
25
|
+
lines.push(`Execution mode: ${resolved.execution_mode}`, '');
|
|
26
|
+
lines.push('## Contract', '');
|
|
27
|
+
lines.push(`Required input: ${printable(descriptor.input.required)}`);
|
|
28
|
+
lines.push(`Required output: ${printable(descriptor.output.required)}`);
|
|
29
|
+
lines.push(`Evidence: ${printable(descriptor.evidence_requirements)}`);
|
|
30
|
+
lines.push(`Stop conditions: ${printable(descriptor.stop_conditions)}`, '');
|
|
31
|
+
lines.push('## Runtime descriptor', '');
|
|
32
|
+
lines.push(runtimeSummary(resolved), '');
|
|
33
|
+
lines.push('## Capability boundary', '');
|
|
34
|
+
lines.push(`Available: ${printable(resolved.available_capabilities)}`);
|
|
35
|
+
lines.push(`Unavailable: ${printableUnavailable(resolved.unavailable_capabilities)}`, '');
|
|
36
|
+
lines.push(`When a capability is unavailable, ${descriptor.tool_free_mode.behavior} Status must be one of: ${printable(descriptor.tool_free_mode.unavailable_statuses)}.`, '');
|
|
37
|
+
lines.push('## Handoff', '');
|
|
38
|
+
lines.push(`Next agents: ${printable(descriptor.handoff_to)}`, '');
|
|
39
|
+
lines.push('## Instructions', '');
|
|
40
|
+
lines.push(pkg.instructions.trim());
|
|
41
|
+
return `${lines.join('\n')}\n`;
|
|
42
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { describe, emitJson, emitPrompt } from './emit.js';
|
|
4
|
+
import { AGENT_SCHEMA_VERSION } from './types.js';
|
|
5
|
+
function makePackage(id) {
|
|
6
|
+
return {
|
|
7
|
+
directory: '/pkg',
|
|
8
|
+
instructions: 'Usage: hyv verify original.md candidate.md profile.json',
|
|
9
|
+
descriptor: {
|
|
10
|
+
schema_version: AGENT_SCHEMA_VERSION,
|
|
11
|
+
id,
|
|
12
|
+
title: 'hyv-verify',
|
|
13
|
+
description: 'verify a candidate',
|
|
14
|
+
instruction_file: 'SKILL.md',
|
|
15
|
+
role: 'Verifier',
|
|
16
|
+
workflow_phase: 'verify',
|
|
17
|
+
input: { required: ['original', 'candidate'], optional: ['writing brief'] },
|
|
18
|
+
output: { required: ['verification report'], optional: [] },
|
|
19
|
+
evidence_requirements: ['preserve revision evidence'],
|
|
20
|
+
permissions: ['read_repository', 'network'],
|
|
21
|
+
stop_conditions: ['stop when evidence is stale'],
|
|
22
|
+
tool_free_mode: {
|
|
23
|
+
available: true,
|
|
24
|
+
behavior: 'report the decision and exact unavailable capability',
|
|
25
|
+
unavailable_statuses: ['NOT_AVAILABLE', 'NOT_CONFIGURED', 'NOT_RUN', 'STALE', 'ERROR'],
|
|
26
|
+
},
|
|
27
|
+
handoff_to: ['hyv-lifecycle'],
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
test('describe resolves a host and returns a runtime descriptor', () => {
|
|
32
|
+
const resolved = describe(makePackage('hyv-verify'), 'codex');
|
|
33
|
+
assert.equal(resolved.agent.id, 'hyv-verify');
|
|
34
|
+
assert.equal(resolved.host.id, 'codex');
|
|
35
|
+
assert.ok(resolved.available_capabilities.includes('read_repository'));
|
|
36
|
+
assert.deepEqual(resolved.unavailable_capabilities, [{ capability: 'network', status: 'NOT_AVAILABLE' }]);
|
|
37
|
+
assert.equal(resolved.execution_mode, 'prompt-only');
|
|
38
|
+
});
|
|
39
|
+
test('emitJson renders a valid JSON runtime descriptor', () => {
|
|
40
|
+
const body = emitJson(makePackage('hyv-verify'), 'generic');
|
|
41
|
+
const parsed = JSON.parse(body);
|
|
42
|
+
assert.equal(parsed.agent.id, 'hyv-verify');
|
|
43
|
+
assert.equal(parsed.host.id, 'generic');
|
|
44
|
+
assert.ok(Array.isArray(parsed.unavailable_capabilities));
|
|
45
|
+
assert.deepEqual(parsed.unavailable_capabilities, [
|
|
46
|
+
{ capability: 'read_repository', status: 'NOT_AVAILABLE' },
|
|
47
|
+
{ capability: 'network', status: 'NOT_AVAILABLE' },
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
test('emitPrompt includes instructions, capability boundary, and required statuses', () => {
|
|
51
|
+
const prompt = emitPrompt(makePackage('hyv-verify'), 'codex');
|
|
52
|
+
assert.match(prompt, /# hyv-verify/);
|
|
53
|
+
assert.match(prompt, /Role: Verifier/);
|
|
54
|
+
assert.match(prompt, /Usage: hyv verify original\.md candidate\.md profile\.json/);
|
|
55
|
+
assert.match(prompt, /Available: read_repository/);
|
|
56
|
+
assert.match(prompt, /Unavailable: network \(NOT_AVAILABLE\)/);
|
|
57
|
+
assert.match(prompt, /NOT_AVAILABLE, NOT_CONFIGURED, NOT_RUN, STALE, ERROR/);
|
|
58
|
+
assert.match(prompt, /Next agents: hyv-lifecycle/);
|
|
59
|
+
});
|
|
60
|
+
test('emitPrompt reports no available capabilities for a fully unavailable host', () => {
|
|
61
|
+
const prompt = emitPrompt(makePackage('hyv-verify'), 'generic');
|
|
62
|
+
assert.match(prompt, /Available: none/);
|
|
63
|
+
assert.match(prompt, /Execution mode: prompt-only/);
|
|
64
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { AGENT_SCHEMA_VERSION, VALID_PERMISSIONS, VALID_UNAVAILABLE_STATUSES, } from './types.js';
|
|
2
|
+
export { resolveHost, listHosts, runtime } from './catalog.js';
|
|
3
|
+
export { loadAll, loadAllFrom, sortedIds, validateAll, validateId } from './load.js';
|
|
4
|
+
export { describe, emitJson, emitPrompt } from './emit.js';
|