From 85c3f254776837c7bf6854b478d0c5c63989428b Mon Sep 17 00:00:00 2001 From: "Charles C. Figueiredo" Date: Mon, 14 Sep 2026 13:34:58 -0400 Subject: [PATCH] Force LF on every clone; Desktop install notes; phase-2 acceptance record; 0.3.1 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01KTKYvLEVY4mStJ3iPaB1Mh --- .claude-plugin/marketplace.json | 4 +-- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- .gitattributes | 14 ++++++++++ CHANGELOG.md | 18 ++++++++++++- CLAUDE.md | 2 ++ README.md | 11 +++++--- docs/acceptance/v0.3/README.md | 19 +++++++++++++ .../design/2026-09-14-humanize-v0.3-design.md | 6 +++++ plugin.json | 2 +- pyproject.toml | 2 +- tests/test_portability.py | 27 +++++++++++++++++++ uv.lock | 2 +- 13 files changed, 99 insertions(+), 12 deletions(-) create mode 100644 .gitattributes diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index edc9637..e889971 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -7,14 +7,14 @@ }, "metadata": { "description": "Measures prose against 13 studies of how AI and human writing differ, from word choice to sentence rhythm to story shape, then rewrites the tells out.", - "version": "0.3.0" + "version": "0.3.1" }, "plugins": [ { "name": "humanize", "description": "Audits a draft layer by layer (vocabulary, punctuation, sentence shape, openers, closers, grammar, narrative, per-model habits) against 13 studies, then rewrites what it flags in the author's voice.", "source": "./", - "version": "0.3.0", + "version": "0.3.1", "author": { "name": "ccf" }, "license": "MIT", "keywords": ["humanize", "writing", "prose", "editing", "style", "ai-writing", "rewriting", "linguistics"], diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 8670ef9..d9d29c9 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "humanize", - "version": "0.3.0", + "version": "0.3.1", "description": "Audits a draft layer by layer (vocabulary, punctuation, sentence shape, openers, closers, grammar, narrative, per-model habits) against 13 studies, then rewrites what it flags in the author's voice.", "author": { "name": "ccf" }, "license": "MIT", diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 3607618..d75d2bf 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "humanize", - "version": "0.3.0", + "version": "0.3.1", "description": "Audits a draft layer by layer (vocabulary, punctuation, sentence shape, openers, closers, grammar, narrative, per-model habits) against 13 studies, then rewrites what it flags in the author's voice.", "author": { "name": "ccf" }, "homepage": "https://github.com/ccf/humanize", diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0da17bb --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# Every clone gets LF, whatever the host's core.autocrlf. The Claude Desktop skill +# loader rejects SKILL.md frontmatter that arrives as CRLF (anthropics/claude-code +# #36902); tests/test_portability.py asserts no tracked text file carries a CR. +* text=auto eol=lf + +# Binary files (none tracked today; listed so a future asset is never normalized) +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.zip binary +*.parquet binary +*.pdf binary +*.docx binary diff --git a/CHANGELOG.md b/CHANGELOG.md index 38203d3..eac5a24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ All notable changes to this project are documented here. The format follows ## [Unreleased] +## [0.3.1] - 2026-09-14 + +### Added +- `.gitattributes` forcing LF on every clone, and a test that no tracked text file carries a + CR: the Claude Desktop skill loader rejects CRLF frontmatter, and "Add from repository" is a + Git clone subject to the host's `core.autocrlf`. + +### Changed +- README: the Claude Desktop row now leads with the repository route (macOS and Windows) and + says to restart the app after installing or updating a plugin — the `/` menu can list + `humanize:humanize` before it is invocable. The skill zip is the alternative for claude.ai + without the Desktop app. +- Acceptance record: phase-2 outcomes (Codex and Hermes installs from `main`, the 0.2.0 → 0.3.0 + plugin upgrade, Desktop on both platforms) recorded in `docs/acceptance/v0.3/README.md`. + ## [0.3.0] - 2026-09-14 ### Added @@ -115,7 +130,8 @@ Initial release (#1). CI (pre-commit, pytest on Python 3.9 and 3.13, `claude plugin validate --strict`), and a Bugbot review guide. -[Unreleased]: https://github.com/ccf/humanize/compare/v0.3.0...HEAD +[Unreleased]: https://github.com/ccf/humanize/compare/v0.3.1...HEAD +[0.3.1]: https://github.com/ccf/humanize/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/ccf/humanize/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/ccf/humanize/compare/v0.1.2...v0.2.0 [0.1.2]: https://github.com/ccf/humanize/compare/v0.1.1...v0.1.2 diff --git a/CLAUDE.md b/CLAUDE.md index f247fe0..86cd6ed 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,6 +51,8 @@ Never run bare `ruff format .` — ruff 0.16 formats Python fences inside `.codex-plugin/plugin.json`, both `marketplace.json` fields, `pyproject.toml`, and the newest CHANGELOG heading — `tests/test_portability.py` fails on drift. - Nothing under `tests/`, `skills/**/scripts/`, or `tools/package_skill_zip.py` touches the network. +- Tracked text files are LF (`.gitattributes`: `* text=auto eol=lf`) — the Claude Desktop skill + loader rejects CRLF frontmatter; `tests/test_portability.py` fails on any CR. - `skills/` is harness-agnostic: no harness variables (`${CLAUDE_…}`, `HERMES_SKILL_DIR`, `CURSOR_…`, `CODEX_…`) and no install commands inside it; frontmatter is exactly the Agent Skills fields (`argument-hint` is a hard error outside Claude Code). `tests/test_portability.py` diff --git a/README.md b/README.md index 6e730cc..0e19a41 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ recorded in `docs/acceptance/`. | Cursor | `cp -R skills/humanize ~/.cursor/skills/` (every project) or `.cursor/skills/` (this project) | the skill appears in the `/` menu | `/humanize …` or automatically | | Hermes Agent | `hermes skills install ccf/humanize/humanize --category writing`; in a running session, `/reload-skills` | `hermes skills list` | `/humanize …` or automatically | | Any Agent-Skills harness | `npx skills add ccf/humanize` or `cp -R skills/humanize ~/.agents/skills/` | harness-specific | harness-specific | -| Claude Desktop — Cowork | Customize → Plugins → Add from repository `ccf/humanize` → install `humanize` | listed under Customize → Plugins | `/` or `+` picker, or automatically | -| Claude Desktop — chat (claude.ai) | Customize → Skills → upload `humanize-skill-.zip` from the [latest release](https://github.com/ccf/humanize/releases); "Code execution and file creation" must be on | listed under Customize → Skills | automatically, or the sidebar `/` menu | +| Claude Desktop (macOS, Windows) | Customize → Plugins → Add from repository `ccf/humanize` → install `humanize` → restart the app | listed under Customize → Plugins; `/humanize:humanize` in the picker | `/` or `+` picker, or automatically, in chats and Cowork | +| claude.ai on the web or mobile, or without a linked repo | Customize → Skills → upload `humanize-skill-.zip` from the [latest release](https://github.com/ccf/humanize/releases); "Code execution and file creation" must be on | listed under Customize → Skills | automatically, or the sidebar `/` menu | Notes: @@ -44,8 +44,11 @@ Notes: skipped and the audit proceeds from reading alone. `hermes plugins install` also works on recent builds but installs the package disabled and read-only; prefer `hermes skills install`. Installing from a raw `SKILL.md` URL fetches one file and is not supported. -- **Claude Desktop** picks up a Cowork plugin update only when `version` changes. In claude.ai - chat the skill triggers by description; picking it from the `/` menu passes no arguments. +- **Claude Desktop** picks up a plugin update only when `version` changes, and it needs a + restart after installing or updating a plugin: until then the `/` menu can list a skill the + app cannot yet invoke ("Unknown command: /humanize:humanize"). The zip is only for claude.ai + without the Desktop app. In claude.ai chat the skill triggers by description; picking it from + the `/` menu passes no arguments. - **Word, PDF, PowerPoint inputs.** humanize delegates extraction to the harness's document skills. In Claude Code that is Anthropic's `document-skills` plugin (`/plugin marketplace add anthropics/skills`, `/plugin install document-skills@anthropic-agent-skills`; its `docx` read diff --git a/docs/acceptance/v0.3/README.md b/docs/acceptance/v0.3/README.md index 7bbedfc..7ea7c37 100644 --- a/docs/acceptance/v0.3/README.md +++ b/docs/acceptance/v0.3/README.md @@ -189,3 +189,22 @@ Claude Code: PASS. Codex: PASS. Cursor: SKIP (not authenticated on this machine) Hermes: SKIP (not authenticated on this machine). Both SKIPs move to phase 2 (post-merge, pre-tag) per the design doc's Post-review amendments, and are re-run after login, with transcripts committed to this directory. + +## Phase 2 record (post-merge, 2026-09-14) + +- Acceptance 3 from `main`: `codex plugin marketplace add ccf/humanize` and + `codex plugin add humanize@humanize` installed 0.3.0; `codex debug prompt-input` + lists `humanize:humanize`. Left installed. +- Acceptance 4: `hermes skills install ccf/humanize/humanize --category writing` + fetched SKILL.md, all six references, and the script (resolved through the + skills.sh registry); `hermes skills list` shows `writing/humanize` enabled. +- Acceptance 6: the installed Claude Code plugin upgraded 0.2.0 → 0.3.0 through the + root-level marketplace (`source: "./"` proven). +- Acceptance 5a: Claude Desktop → Customize → Plugins → Add from repository + `ccf/humanize` installed the plugin and the skill runs in chats on macOS and on + Windows. Windows first answered `Unknown command: /humanize:humanize` from the + picker until the app was restarted — the slash menu lists the skill before the + invocable registry has it (cf. anthropics/claude-code #62485). The README now says + to restart. Acceptance 5b (zip upload) is not needed for the Desktop app; the zip + stays on the release for claude.ai without it. +- Cursor and Hermes smoke transcripts: still pending login on this machine. diff --git a/docs/design/2026-09-14-humanize-v0.3-design.md b/docs/design/2026-09-14-humanize-v0.3-design.md index 391dee0..a14b0ea 100644 --- a/docs/design/2026-09-14-humanize-v0.3-design.md +++ b/docs/design/2026-09-14-humanize-v0.3-design.md @@ -457,3 +457,9 @@ is the specific source of a number or a file; the tool is described as grounded side effect on interrupt; the raw Claude stream is written to a temp directory. - §2B — if a harness does not show the skill's path, the model searches for `scripts/surface_scan.py` and, failing that, skips the scan and says so. +- §6/§8 (0.3.1) — acceptance 5a passed: the Desktop app's "Add from repository" route installs the + plugin on macOS and Windows and the skill runs in chats; Windows needed an app restart before the + picker's `humanize:humanize` entry became invocable (registry lag, cf. anthropics/claude-code + #62485). The zip (5b) is the alternative for claude.ai without the Desktop app, not a requirement. + A `.gitattributes` now forces LF on every clone because the Desktop loader rejects CRLF frontmatter + (#36902) and "Add from repository" is a `git clone` subject to the host's `core.autocrlf`. diff --git a/plugin.json b/plugin.json index b04fef9..7dca723 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "humanize", - "version": "0.3.0", + "version": "0.3.1", "description": "Audits a draft layer by layer (vocabulary, punctuation, sentence shape, openers, closers, grammar, narrative, per-model habits) against 13 studies, then rewrites what it flags in the author's voice.", "author": { "name": "ccf" }, "license": "MIT", diff --git a/pyproject.toml b/pyproject.toml index 33962b2..e039e8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "humanize-plugin" -version = "0.3.0" +version = "0.3.1" description = "Agent skill that audits prose for AI tells and rewrites it in the author's voice, on evidence from 13 studies." requires-python = ">=3.9" license = "MIT" diff --git a/tests/test_portability.py b/tests/test_portability.py index 4621389..eac83c2 100644 --- a/tests/test_portability.py +++ b/tests/test_portability.py @@ -237,3 +237,30 @@ def test_study_count_matches_sources(): for token in found: value = int(token) if token.isdigit() else NUMBER_WORDS[token.lower()] assert value == n, f"{rel} says {token}, SOURCES.md has {n}" + + +TEXT_SUFFIXES = {".md", ".py", ".json", ".toml", ".yml", ".yaml", ".sh", ".txt", ".csv", ".cfg"} +SKIP_DIRS = { + ".git", + ".venv", + "node_modules", + "dist", + "__pycache__", + ".superpowers", + ".pytest_cache", +} + + +def test_tracked_text_files_use_lf(): + """The Claude Desktop skill loader rejects CRLF frontmatter; .gitattributes forces LF.""" + offenders = [] + for path in ROOT.rglob("*"): + if any(part in SKIP_DIRS for part in path.parts): + continue + is_text = path.suffix in TEXT_SUFFIXES or path.name == ".gitattributes" + if not path.is_file() or not is_text: + continue + if b"\r" in path.read_bytes(): + offenders.append(str(path.relative_to(ROOT))) + assert not offenders, offenders + assert "eol=lf" in (ROOT / ".gitattributes").read_text(encoding="utf-8") diff --git a/uv.lock b/uv.lock index 42e39f8..f7910d8 100644 --- a/uv.lock +++ b/uv.lock @@ -86,7 +86,7 @@ wheels = [ [[package]] name = "humanize-plugin" -version = "0.3.0" +version = "0.3.1" source = { virtual = "." } [package.dev-dependencies]