* fix: disable Claude co-author attribution by default
* fix: harden default co-author opt-out and correct the docs
Follow-up on the co-author default in this PR.
- Remove the existsSync/writeFileSync race in the installer settings write
(CodeQL js/file-system-race, high). A single guarded read now covers the
fresh-install case, and unreadable or non-object settings are left untouched.
- Respect `attribution` as an explicit user choice. It supersedes
`includeCoAuthoredBy` in Claude Code 2.1.x, so a user who configured it would
otherwise have had a dead key written into their settings.
- Share one opt-out rule via scripts/lib/claude-commit-attribution.js instead of
duplicating it across the installer and plugin setup.
- Update the git-workflow rule and its nine mirrors and translations, which
still told users ECC does not ship this setting.
We keep writing the deprecated `includeCoAuthoredBy` key rather than
`attribution`: unknown keys fail Claude Code settings validation, so writing
`attribution` would break users on older versions.
* fix(install): rewrite relative skill links for injected ecc namespace
Skill and rule markdown is byte-copied during a claude install, but the
home/project adapters inject an `ecc/` namespace segment
(skills/<id> -> skills/ecc/<id>, rules/<x> -> rules/ecc/<x>). Source-relative
links such as `../../rules/react/hooks.md` therefore broke after install: the
extra level changed what `../..` resolved to, and the link target itself moved
under rules/ecc/.
Rewrite relative links in namespaced markdown so they resolve to the file's
actual installed location, derived from the plan's own file placements (no
hardcoded namespace literal, so the links cannot drift from where files land).
Non-namespacing adapters and links to non-installed targets are left untouched;
URLs, anchors, absolute paths, and fenced code blocks are never rewritten.
Fixes#2340
* fix(install): keep non-namespaced markdown on the byte-for-byte copy path
Address review feedback: the markdown branch in applyInstallPlan diverted every
copy-file markdown operation through read+rewrite+write, so identity-mapped
markdown (source path == install path, no namespace injected) lost byte-for-byte
content and source mode bits even though no link rewrite was needed.
Gate the rewrite on isNamespacedSource() so only files whose install path
actually changed (e.g. skills/x -> skills/ecc/x) leave the copyFileSync path;
everything else is copied verbatim as before.
* test(install): emit failure stack in the link-rewrite test runner
Address review feedback: the local test() harness logged only error.message,
so a failing assertion lost its source line and diff. Print error.stack on
stderr on failure so broken rewrite cases stay diagnosable.
Completes the install-target matrix for Claude Code. Until now, ECC's
Claude support was home-scope only (~/.claude/) via the `claude` target.
This adds a project-scope counterpart (./.claude/) via a new
`claude-project` target so teams can install ECC per-repo without
contaminating ~/.claude/ — matching the existing project-scope adapters
for Cursor, Antigravity, Gemini, CodeBuddy, Joycode, and Zed.
Symmetric with `claude`:
- Same namespace under rules/ecc and skills/ecc
- Same docs/<locale> handling for --locale
- Same hooks placeholder substitution for hooks.json
- Reuses claude-home's destination-mapping logic 1:1
Use cases:
- Monorepos with multiple Flow-managed projects
- Teams that want ECC scoped per-project without touching ~/.claude/
- Per-project skill/rule isolation when global install isn't desirable
No breaking change: existing --target claude continues to route to
claude-home (user-scope) unchanged. New target is opt-in.
Tests
-----
- 4 new tests in tests/lib/install-targets.test.js
(root resolution, lookup-by-id, plan parity with claude, foreign-path filtering)
- All install-target regression guards (schema enum / SUPPORTED_INSTALL_TARGETS)
still pass
- End-to-end smoke: `--target claude-project --profile minimal --dry-run`
emits 359 ops with destinations rooted at <projectRoot>/.claude/ (parity
with --target claude which emits 359 ops rooted at ~/.claude/)