Skip to content

Commit 4c0574b

Browse files
committed
Merge develop into main: v0.8.3 - Copilot install + ~/.human-voice single-dir
2 parents c6b4eae + 2804f56 commit 4c0574b

25 files changed

Lines changed: 1492 additions & 109 deletions

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "human-voice",
3-
"version": "0.5.0",
3+
"version": "0.8.3",
44
"description": "Detect AI-generated writing patterns and build authentic voice profiles through adaptive interviews and computational stylistics",
55
"author": {
66
"name": "zircote",

CHANGELOG.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,95 @@ All notable changes to the Human Voice plugin will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.8.3] - 2026-04-22
9+
10+
### Changed
11+
12+
- `/human-voice:voice-copilot-install` slash command simplified: single
13+
invocation step that honours the user's flags as passed. When `--dry-run`
14+
is supplied the CLI prints intended writes and stops; Claude no longer
15+
chases a real install afterwards unless explicitly asked.
16+
17+
## [0.8.2] - 2026-04-22
18+
19+
### Fixed
20+
21+
- `/human-voice:voice-copilot-install` slash command invoked
22+
`bin/voice-copilot-install` via a relative path, which failed when Claude's
23+
cwd was the user's target project rather than the plugin root. Updated the
24+
command to invoke `"${CLAUDE_PLUGIN_ROOT}/bin/voice-copilot-install"`, which
25+
keeps the caller's cwd intact so `--target=.` still resolves to the project.
26+
27+
## [0.8.1] - 2026-04-22
28+
29+
### Fixed
30+
31+
- `voice-copilot-install` defaulted `--target=.` to the plugin root because the
32+
bash wrapper did `cd "$PLUGIN_ROOT"` before exec. Removed the chdir; PYTHONPATH
33+
already lets Python import `lib.*` from anywhere, and `.` now correctly
34+
resolves to the user's cwd.
35+
36+
## [0.8.0] - 2026-04-22
37+
38+
### Added
39+
40+
- **`/human-voice:voice-copilot-install` command** (CLI: `bin/voice-copilot-install`)
41+
— installs one or more voice profiles into a target project's GitHub Copilot
42+
configuration. Writes the full surface so Copilot (code review, coding agent,
43+
Chat) applies the voice automatically:
44+
- `.github/copilot-instructions.md` (repo-wide, marker-idempotent)
45+
- `AGENTS.md` at repo root (coding-agent focus, marker-idempotent)
46+
- `.github/instructions/human-voice-<slug>.instructions.md` with `applyTo`
47+
globs (one per profile; path-scoped routing)
48+
- `.github/prompts/voice-{review,fix,draft}.prompt.md` (Copilot Chat slash
49+
commands)
50+
- `.github/agents/human-voice-<slug>.agent.md` (Copilot custom agents)
51+
- `.github/human-voice/<slug>/profile.json` (redacted) +
52+
`voice-prompt.txt`
53+
- `.github/human-voice/scripts/*.js` (bundled character-restriction
54+
validators)
55+
- `.github/workflows/voice-review.yml` (PR workflow that runs the validator
56+
and posts findings as a PR comment; triggers on `docs/**`, `README*`,
57+
`CHANGELOG*`, `CONTRIBUTING*`, `**/*.{md,mdx}`)
58+
- Multi-profile install with `--profiles a,b --route 'GLOB=SLUG;...'` routing.
59+
- Idempotent merge semantics via `<!-- human-voice:start -->` /
60+
`<!-- human-voice:end -->` markers — re-running never clobbers user content.
61+
- Profile redaction by default (drops `metadata`, `known_gaps`, trims
62+
`calibration` to summary) so public repos don't leak session notes. Pass
63+
`--no-redact` to opt out.
64+
- Overwrite policies: `merge` (default), `force`, `error`.
65+
- `--dry-run` mode that prints intended writes without touching the filesystem.
66+
- 21 new tests under `tests/test_copilot_install.py` covering redaction,
67+
routing, single/multi-profile install, overwrite policies, idempotency, and
68+
dry-run.
69+
70+
### Changed
71+
72+
- Expanded `docs/guides/copilot-integration.md` to document the new installer
73+
alongside the existing minimal-export flow.
74+
75+
## [0.7.0] - 2026-04-22
76+
77+
### Changed
78+
79+
- **Single canonical data directory.** All plugin data (profiles, sessions,
80+
config, voice-prompt.txt, observer-protocol.md) now lives in
81+
`~/.human-voice/` unconditionally. `CLAUDE_PLUGIN_DATA` and any other env
82+
var are ignored by the resolver. Rationale: users with multiple Claude
83+
accounts and differing `~/.claude*` directories want exactly one place for
84+
voice data. Skills, commands, agents, hooks, and setup script all point at
85+
`~/.human-voice/` directly.
86+
- `lib.config.migrate_legacy_data` is now a no-op shim retained only for
87+
backward-compatible callers.
88+
89+
### Removed
90+
91+
- Env-var based data-directory resolution. The short-lived `HUMAN_VOICE_DATA_DIR`
92+
override (introduced in 0.6.0 but never released) is also removed — there is
93+
no escape hatch by design.
94+
- `.human-voice-plugin` marker / stamping (no longer needed with a single
95+
fixed location).
96+
897
## [0.5.0] - 2026-04-15
998

1099
### Fixed
@@ -97,6 +186,11 @@ Pattern detection based on:
97186
- [The Field Guide to AI Slop](https://www.ignorance.ai/p/the-field-guide-to-ai-slop)
98187
- [Common AI Words - Grammarly](https://www.grammarly.com/blog/ai/common-ai-words/)
99188

189+
[0.8.3]: https://github.com/zircote/human-voice/compare/v0.8.2...v0.8.3
190+
[0.8.2]: https://github.com/zircote/human-voice/compare/v0.8.1...v0.8.2
191+
[0.8.1]: https://github.com/zircote/human-voice/compare/v0.8.0...v0.8.1
192+
[0.8.0]: https://github.com/zircote/human-voice/compare/v0.7.0...v0.8.0
193+
[0.7.0]: https://github.com/zircote/human-voice/compare/v0.5.0...v0.7.0
100194
[0.5.0]: https://github.com/zircote/human-voice/compare/v0.4.0...v0.5.0
101195
[0.4.0]: https://github.com/zircote/human-voice/compare/v0.3.0...v0.4.0
102196
[0.3.0]: https://github.com/zircote/human-voice/compare/v0.2.0...v0.3.0

agents/interview-conductor.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ The loop works as follows — repeat until `action` is `interview_complete` or t
6767
## Session Management
6868

6969
- When starting a NEW session: the command has already created the session directory and provided you the session_id and session_dir path. Load state.json and begin the loop.
70-
- **Data directory**: All sessions and profiles are stored under `$CLAUDE_PLUGIN_DATA` when set, otherwise under `~/.human-voice/`. When looking for existing sessions or profiles, always check `~/.human-voice/` as the canonical fallback location.
70+
- **Data directory**: All sessions and profiles are stored under `~/.human-voice/`. This is the single canonical location — no env vars redirect it. Always look for existing sessions and profiles there.
7171
- When RESUMING: load state.json, recap progress conversationally ("Welcome back — you're in Section X of Y, about Z minutes remaining"), then enter the loop at step 1.
7272
- After every user response, save session state atomically to `state.json` within the session directory.
7373
- The state file tracks: current module, current question index, all collected responses, branching decisions, format streak count, quality flags, and timing data.

agents/profile-synthesizer.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ print(f'Profile published as {slug} -> {path}')
9393
"
9494
```
9595

96-
This stores the profile under `$CLAUDE_PLUGIN_DATA/profiles/{slug}/` and activates it, which copies to the top-level well-known locations:
97-
- `$CLAUDE_PLUGIN_DATA/profile.json` — full voice profile (read by hooks and agents)
98-
- `$CLAUDE_PLUGIN_DATA/voice-prompt.txt` — compact injection text for LLM system prompts
96+
This stores the profile under `~/.human-voice/profiles/{slug}/` and activates it, which copies to the top-level well-known locations:
97+
- `~/.human-voice/profile.json` — full voice profile (read by hooks and agents)
98+
- `~/.human-voice/voice-prompt.txt` — compact injection text for LLM system prompts
9999

100-
**IMPORTANT**: When `$CLAUDE_PLUGIN_DATA` is not set, the data directory defaults to `~/.human-voice`. All profiles, sessions, and config live under `~/.human-voice/` in standalone/development mode. When looking for existing profiles, always check `~/.human-voice/profiles/` and `~/.human-voice/profile.json` as the canonical fallback locations.
100+
**IMPORTANT**: All profiles, sessions, and config live under `~/.human-voice/`. This is the single canonical location — no env vars redirect it. Always look for existing profiles at `~/.human-voice/profiles/` and the active profile at `~/.human-voice/profile.json`.
101101

102102
The session is also marked as `complete` automatically.
103103

bin/voice-copilot-install

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
# Install voice profiles into a project's GitHub Copilot configuration.
3+
#
4+
# Runs from the caller's cwd — --target=. (the default) resolves to where
5+
# the user invoked the command, not the plugin root. PYTHONPATH is set so
6+
# that `import lib.copilot_install` succeeds without a chdir.
7+
set -euo pipefail
8+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
10+
export PYTHONPATH="${PLUGIN_ROOT}:${PLUGIN_ROOT}/scoring/src:${PLUGIN_ROOT}/nlp/src:${PYTHONPATH:-}"
11+
exec python3 -m lib.copilot_install "$@"

commands/voice-copilot-install.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
description: Install voice profile(s) into a project's GitHub Copilot configuration
3+
---
4+
5+
# /human-voice:voice-copilot-install
6+
7+
Install one or more human-voice profiles into a target project as GitHub Copilot
8+
custom instructions, prompts, agents, AGENTS.md, and a PR review workflow.
9+
10+
## Arguments
11+
12+
`$ARGUMENTS` — optional CLI args forwarded to `bin/voice-copilot-install`.
13+
Examples:
14+
15+
- `--target /path/to/repo` (default: current working directory)
16+
- `--profile robert-allen` (single profile; default: active profile)
17+
- `--profiles robert-allen,tech-authority --route 'docs/**=tech-authority;**/*.md=robert-allen'`
18+
- `--default robert-allen` (which installed slug is the repo default)
19+
- `--overwrite error|merge|force` (default: `merge` — replaces marker blocks, preserves surrounding content)
20+
- `--dry-run` (print intended writes, touch nothing)
21+
- `--no-workflow`, `--no-prompts`, `--no-agent`, `--no-agents-md`, `--no-redact`
22+
23+
If `$ARGUMENTS` is empty, run with defaults (active profile, install into cwd, merge mode).
24+
25+
## Steps
26+
27+
**Important**: always invoke the CLI via its absolute path at `${CLAUDE_PLUGIN_ROOT}/bin/voice-copilot-install`. The default `--target=.` resolves against the user's current working directory (the project they're standing in), which is what you want. Do NOT `cd` into the plugin root first — that would make `--target=.` resolve to the plugin itself.
28+
29+
1. **Run**: `"${CLAUDE_PLUGIN_ROOT}/bin/voice-copilot-install" $ARGUMENTS`. Surface the list of written/skipped files. If the user passed `--dry-run`, the CLI will print intended writes and touch nothing — do not follow up with a real install unless they ask.
30+
31+
2. **Report**: Summarise what was written and point the user at `.github/copilot-instructions.md` as the entry point. If a workflow was written, remind them to commit and push so Copilot code review picks it up on the next PR.
32+
33+
## What gets written
34+
35+
- `.github/copilot-instructions.md` — repo-wide, loaded by Copilot automatically on GitHub.
36+
- `AGENTS.md` at repo root — coding-agent instructions.
37+
- `.github/instructions/human-voice-<slug>.instructions.md` — path-scoped (one per profile; `applyTo` glob drives routing).
38+
- `.github/prompts/voice-{review,fix,draft}.prompt.md` — reusable Copilot Chat slash commands.
39+
- `.github/agents/human-voice-<slug>.agent.md` — Copilot custom agents (one per profile).
40+
- `.github/human-voice/<slug>/profile.json` + `voice-prompt.txt` — redacted profile artefacts.
41+
- `.github/human-voice/scripts/*.js` — bundled validator scripts.
42+
- `.github/workflows/voice-review.yml` — PR workflow that runs validators on changed prose and comments findings.
43+
44+
Repo-wide instructions and AGENTS.md are written inside `<!-- human-voice:start -->` / `<!-- human-voice:end -->` markers, so re-runs are idempotent and never clobber user-added content outside the block.
45+
46+
## Notes
47+
48+
- Profile data is redacted by default before embedding (drops `metadata` and `known_gaps`, trims `calibration` to its summary). Pass `--no-redact` only for private repos.
49+
- The workflow triggers only on pull requests touching `docs/**`, `README*`, `CHANGELOG*`, `CONTRIBUTING*`, and `**/*.{md,mdx}`.
50+
- For multi-profile installs, provide a `--route` spec mapping globs to slugs. The default profile applies to anything not matched by an explicit glob.

commands/voice-drift.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Generate a drift report comparing accumulated voice observations against the cur
1919
recall_memories(query="voice drift observed pattern writing", namespace="voice-observations", mode="hybrid")
2020
```
2121

22-
2. **Load profile**: Read `${CLAUDE_PLUGIN_DATA}/profile.json` for current dimension scores (`${CLAUDE_PLUGIN_DATA}` defaults to `~/.human-voice` when not set). Extract the score for each dimension from the `dimensions` object. Do not use hardcoded values; every user's profile is different.
22+
2. **Load profile**: Read `~/.human-voice/profile.json` for current dimension scores. Extract the score for each dimension from the `dimensions` object. Do not use hardcoded values; every user's profile is different.
2323

2424
3. **Compare**: For each observation, compare against the dimension scores loaded from the profile in step 2. Cover all gold standard dimensions (formality, emotional_tone, personality, complexity, audience_awareness, authority, narrativity, humor) and all gap dimensions that have scores. Also compare mechanics observations (contractions, Oxford comma, punctuation style) against the `mechanics` section of the profile.
2525

commands/voice-interview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Start a new voice elicitation interview session.
1111

1212
## Procedure
1313

14-
1. **Create session**: Run `bin/voice-session create` to generate a new session. This creates `${CLAUDE_PLUGIN_DATA}/sessions/{session_id}/` with `state.json` and `responses.jsonl`. Capture the session_id and session_dir path from the output.
14+
1. **Create session**: Run `bin/voice-session create` to generate a new session. This creates `~/.human-voice/sessions/{session_id}/` with `state.json` and `responses.jsonl`. Capture the session_id and session_dir path from the output.
1515

1616
2. **Load question bank**: Verify question bank modules exist in `question-bank/modules/`.
1717

commands/voice-setup.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ allowed-tools: Read, Write, Bash(python3:*), Bash(mkdir:*), Glob, AskUserQuestio
77

88
# Human Voice Setup
99

10-
Interactive configuration wizard. Creates `${CLAUDE_PLUGIN_DATA}/config.json` — the unified config for both AI pattern detection and voice elicitation.
10+
Interactive configuration wizard. Creates `~/.human-voice/config.json` — the unified config for both AI pattern detection and voice elicitation.
1111

1212
## Process
1313

14-
1. **Check for existing config**: Read `${CLAUDE_PLUGIN_DATA}/config.json` if it exists. Ask: "Configuration exists. Update or replace?"
14+
1. **Check for existing config**: Read `~/.human-voice/config.json` if it exists. Ask: "Configuration exists. Update or replace?"
1515

1616
2. **Detect project structure**: Scan for content directories (`docs/`, `_posts/`, `content/`, `_docs/`), markdown files, and static site generators.
1717

@@ -25,7 +25,7 @@ Interactive configuration wizard. Creates `${CLAUDE_PLUGIN_DATA}/config.json`
2525

2626
**Interview defaults**: Override estimated questions, format streak limit, quality thresholds?
2727

28-
4. **Write config**: Run `python3 -c "from lib.config import save_config; import json; save_config(json.loads('{...}'))"` to write `${CLAUDE_PLUGIN_DATA}/config.json` atomically.
28+
4. **Write config**: Run `python3 -c "from lib.config import save_config; import json; save_config(json.loads('{...}'))"` to write `~/.human-voice/config.json` atomically.
2929

3030
5. **Verify**: Run `python3 -m lib.config show` to display the saved config.
3131

@@ -49,7 +49,7 @@ The config file is JSON, schema-validated against `question-bank/schemas/config.
4949
"output": { "verbosity": "normal", ... }
5050
},
5151
"interview": {
52-
"session_storage": "${CLAUDE_PLUGIN_DATA}/sessions",
52+
"session_storage": "~/.human-voice/sessions",
5353
"estimated_questions": 70,
5454
"quality": { ... },
5555
"scoring": { ... },

docs/guides/copilot-integration.md

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,86 @@ diataxis_goal: Export voice profiles to a repository for use by GitHub Copilot a
66

77
# Integrating with GitHub Copilot
88

9-
This guide covers how to export voice profiles to a repository so GitHub Copilot applies them automatically during content generation and review.
9+
This guide covers how to install voice profiles into a project so GitHub Copilot (code review, coding agent, chat) applies them automatically.
10+
11+
Two commands are available:
12+
13+
- **`voice-copilot-install`** (recommended) — writes the full Copilot surface: `copilot-instructions.md`, path-scoped `instructions/*.instructions.md`, reusable `prompts/*.prompt.md`, custom `agents/*.agent.md`, root `AGENTS.md`, redacted profile artefacts under `.github/human-voice/`, and a PR review workflow. Idempotent.
14+
- **`voice-profiles install`** (minimal) — writes only a single `.github/copilot-instructions.md`. Retained for users who want the smallest footprint.
1015

1116
## Prerequisites
1217

1318
- At least one voice profile stored in the registry (from an interview, design, or template)
1419
- The human-voice plugin installed
1520
- GitHub Copilot enabled on the target repository
1621

17-
## Install profiles to a repository
22+
## Full Copilot install (recommended)
23+
24+
Run the slash command from the target project root, or invoke the CLI directly:
25+
26+
```
27+
/human-voice:voice-copilot-install
28+
```
29+
30+
```bash
31+
bin/voice-copilot-install --target /path/to/repo
32+
```
33+
34+
With no flags, this installs the currently-active profile into the cwd using merge mode. The command is idempotent — re-running replaces only the content between `<!-- human-voice:start -->` / `<!-- human-voice:end -->` markers; anything you added outside those markers is preserved.
35+
36+
### What it writes
37+
38+
| Path | Role |
39+
|---|---|
40+
| `.github/copilot-instructions.md` | Repo-wide instructions loaded automatically by Copilot. |
41+
| `AGENTS.md` | Root-level instructions for Copilot coding agent. |
42+
| `.github/instructions/human-voice-<slug>.instructions.md` | Path-scoped rules; the `applyTo` frontmatter glob drives routing. |
43+
| `.github/prompts/voice-review.prompt.md` | `/voice-review` slash command in Copilot Chat. |
44+
| `.github/prompts/voice-fix.prompt.md` | `/voice-fix` — rewrite a selection in-voice. |
45+
| `.github/prompts/voice-draft.prompt.md` | `/voice-draft` — new content in-voice. |
46+
| `.github/agents/human-voice-<slug>.agent.md` | Copilot custom agents (one per profile). |
47+
| `.github/human-voice/<slug>/profile.json` | Redacted profile (metadata and raw calibration stripped). |
48+
| `.github/human-voice/<slug>/voice-prompt.txt` | Compact voice rules. |
49+
| `.github/human-voice/scripts/*.js` | Bundled character-restriction validators. |
50+
| `.github/workflows/voice-review.yml` | PR workflow that runs the validator and posts findings. |
51+
52+
### Multi-profile install with routing
53+
54+
Install multiple profiles and map globs to slugs via `--route`:
55+
56+
```bash
57+
bin/voice-copilot-install \
58+
--profiles robert-allen,tech-authority \
59+
--default robert-allen \
60+
--route 'docs/**=tech-authority;**/*.md=robert-allen' \
61+
--target /path/to/repo
62+
```
63+
64+
Each profile gets its own `instructions/*.instructions.md` with a distinct `applyTo` glob. The default profile covers anything not matched by an explicit glob.
65+
66+
### Privacy
67+
68+
By default, the installer redacts the embedded `profile.json`: it drops `metadata` (session ID, notes), drops `known_gaps`, and trims `calibration` to its summary. The retained fields (`dimensions`, `mechanics`, `distinctive_features`, `voice_aspirations`, `identity_summary`, `calibration.summary`) are the signals Copilot needs to emulate voice. Pass `--no-redact` only for private repos where the full profile is acceptable.
69+
70+
### Dry run
71+
72+
```bash
73+
bin/voice-copilot-install --dry-run
74+
```
75+
76+
Prints the list of files that would be written, with byte sizes. Touches nothing.
77+
78+
### Overwrite policies
79+
80+
- `--overwrite merge` (default) — replace content between markers; preserve surrounding text.
81+
- `--overwrite force` — overwrite the entire target file.
82+
- `--overwrite error` — fail if any target exists.
83+
84+
Non-markered files (instructions, prompts, agents, workflow, embedded artefacts) are overwritten in `force` mode and skipped in `error` mode.
85+
86+
## Minimal install (legacy)
1887

19-
Use the `voice-profiles install` command to export one or more profiles into a repository. This generates a single `.github/copilot-instructions.md` file containing all specified profiles with routing logic.
88+
Use the original `voice-profiles install` command to export one or more profiles into a repository. This generates a single `.github/copilot-instructions.md` file containing all specified profiles with routing logic.
2089

2190
```bash
2291
bin/voice-profiles install robert-allen zircote-brand --to-repo ../my-repo

0 commit comments

Comments
 (0)