scar-cli 0.4.0__tar.gz → 0.6.0__tar.gz
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.
- scar_cli-0.6.0/.claude-plugin/marketplace.json +11 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.scars/0001-git-grep-ere-pitfalls.landmine.md +3 -5
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.scars/0002-agent-direct-hook-install.deadend.md +4 -4
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.scars/0005-history-rewrite-orphans-commit-evidence.landmine.md +13 -8
- scar_cli-0.6.0/.scars/0006-yaml-pattern-anchor-over-escaping.landmine.md +41 -0
- scar_cli-0.6.0/.scars/candidates/fp-log.txt +6 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/AGENTS.md +17 -0
- scar_cli-0.6.0/CHANGELOG.md +74 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/PKG-INFO +11 -1
- {scar_cli-0.4.0 → scar_cli-0.6.0}/README.md +10 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/ROADMAP.md +4 -4
- scar_cli-0.6.0/experiments/harvest/PROTOCOL.md +76 -0
- scar_cli-0.6.0/plugin/plugin.json +29 -0
- scar_cli-0.6.0/plugin/skills/scar-authoring/SKILL.md +93 -0
- scar_cli-0.6.0/plugin/skills/scar-authoring/assets/template.md +25 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/pyproject.toml +1 -1
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/agent.py +7 -0
- scar_cli-0.6.0/src/scar/cli.py +622 -0
- scar_cli-0.6.0/src/scar/evidence.py +86 -0
- scar_cli-0.6.0/src/scar/harvest.py +384 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/installer.py +43 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/match.py +20 -7
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/mcp.py +8 -1
- scar_cli-0.6.0/src/scar/orphan.py +233 -0
- scar_cli-0.6.0/src/scar/skills/scar-authoring/SKILL.md +93 -0
- scar_cli-0.6.0/src/scar/skills/scar-authoring/assets/template.md +25 -0
- scar_cli-0.6.0/tests/test_cli.py +659 -0
- scar_cli-0.6.0/tests/test_docs.py +14 -0
- scar_cli-0.6.0/tests/test_evidence.py +134 -0
- scar_cli-0.6.0/tests/test_harvest.py +338 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/tests/test_installer.py +33 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/tests/test_mcp.py +11 -0
- scar_cli-0.6.0/tests/test_orphan.py +398 -0
- scar_cli-0.6.0/tests/test_plugin.py +43 -0
- scar_cli-0.6.0/tests/test_skill.py +36 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/uv.lock +1 -1
- scar_cli-0.4.0/.scars/candidates/fp-log.txt +0 -1
- scar_cli-0.4.0/CHANGELOG.md +0 -44
- scar_cli-0.4.0/src/scar/cli.py +0 -288
- scar_cli-0.4.0/src/scar/harvest.py +0 -111
- scar_cli-0.4.0/tests/test_cli.py +0 -151
- scar_cli-0.4.0/tests/test_harvest.py +0 -63
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.github/workflows/ci.yml +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.github/workflows/pr-validation.yml +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.github/workflows/release.yml +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.gitignore +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.scars/0003-installer-binds-to-active-venv-scar.landmine.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.scars/0004-promote-roundtrip-drops-expires-evidence.landmine.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.scars/README.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/.scars/template.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/CONTRIBUTING.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/IDEA.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/LICENSE +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/SCAR-FORMAT.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/SPEC.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/STRESS-TEST.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/anchor-survival/PROTOCOL.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/anchor-survival/RESULTS.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/anchor-survival/long_replay.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/anchor-survival/replay.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/auto-authorship/FINDINGS.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/auto-authorship/PROTOCOL.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/.gitignore +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/PROTOCOL.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/RESULTS.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/fixture/.scars/0001-vendor-retry-window.fence.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/fixture/.scars/0002-evicting-session-store.deadend.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/fixture/.scars/0003-export-column-order.landmine.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/fixture/README.md +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/fixture/payments/retry.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/fixture/reports/export.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/fixture/services/sessions.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/fence-honor/grade.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/experiments/harvest/harvest.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/hook/scar-hooks.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/__init__.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/hooks.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/lint.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/model.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/render.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/src/scar/store.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/tests/test_hooks.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/tests/test_lifecycle.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/tests/test_lint.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/tests/test_match.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/tests/test_model.py +0 -0
- {scar_cli-0.4.0 → scar_cli-0.6.0}/tests/test_store.py +0 -0
|
@@ -8,12 +8,10 @@ created: 2026-06-09
|
|
|
8
8
|
authors: ["claude-code", kibukx]
|
|
9
9
|
anchors:
|
|
10
10
|
- path: experiments/anchor-survival/
|
|
11
|
-
- path:
|
|
12
|
-
- pattern: "git.{0,20}grep
|
|
11
|
+
- path: src/scar/harvest.py
|
|
12
|
+
- pattern: "git.{0,20}grep"
|
|
13
13
|
evidence:
|
|
14
|
-
-
|
|
15
|
-
- note: "produced a fake 0% anchor-survival run before diagnosis (gate 0.2)"
|
|
16
|
-
- note: "history rewritten at v0.1.0 public release; pre-release SHAs resolve on GitHub by URL but not in fresh clones"
|
|
14
|
+
- note: "orphaned receipt — pre-v0.1.0 commit 5c63b14 produced a fake 0% anchor-survival run before diagnosis (gate 0.2); resolves at github.com/Daily-Nerd/Scar/commit/5c63b14 until GC, not in fresh clones (rewritten at the v0.1.0 release)"
|
|
17
15
|
expires:
|
|
18
16
|
condition: "resolver layer gains integration tests over its git invocations"
|
|
19
17
|
review_after: 2027-06-09
|
|
@@ -8,11 +8,11 @@ created: 2026-06-09
|
|
|
8
8
|
authors: ["claude-code", kibukx]
|
|
9
9
|
anchors:
|
|
10
10
|
- path: hook/
|
|
11
|
-
-
|
|
11
|
+
- path: src/scar/installer.py
|
|
12
12
|
evidence:
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- note: "
|
|
13
|
+
- note: "orphaned receipt — pre-v0.1.0 installer commit faad8f6 at github.com/Daily-Nerd/Scar/commit/faad8f6 (unreachable in fresh clones)"
|
|
14
|
+
- note: "orphaned receipt — pre-v0.1.0 hooks commit bcd3864 at github.com/Daily-Nerd/Scar/commit/bcd3864 (unreachable in fresh clones)"
|
|
15
|
+
- note: "both SHAs orphaned by the fresh-start force-push at the v0.1.0 public release; resolve on GitHub by URL until GC, not in fresh clones"
|
|
16
16
|
status: active
|
|
17
17
|
---
|
|
18
18
|
|
{scar_cli-0.4.0 → scar_cli-0.6.0}/.scars/0005-history-rewrite-orphans-commit-evidence.landmine.md
RENAMED
|
@@ -8,7 +8,6 @@ created: 2026-06-11
|
|
|
8
8
|
authors: ["claude-code", "Kibukx"]
|
|
9
9
|
anchors:
|
|
10
10
|
- path: .scars/
|
|
11
|
-
- pattern: "push.{0,30}(--force|\\+[a-zA-Z]).{0,40}main|filter-repo|checkout --orphan"
|
|
12
11
|
evidence:
|
|
13
12
|
- note: v0.1.0 public release (2026-06-11): fresh-start force-push orphaned 3 commit SHAs cited by scars 0001 and 0002; SHAs still resolve on GitHub by URL but fail in any fresh clone, and GitHub may GC them eventually
|
|
14
13
|
expires:
|
|
@@ -19,7 +18,7 @@ status: active
|
|
|
19
18
|
|
|
20
19
|
Scars cite commit SHAs as evidence receipts. Those receipts implicitly assume
|
|
21
20
|
the SHA stays reachable in the repo's history forever. Any history rewrite —
|
|
22
|
-
fresh-start orphan branch, force-push, filter-repo scrub — silently breaks
|
|
21
|
+
fresh-start orphan branch, force-push, filter-repo scrub, or a routine squash-/rebase-merge — silently breaks
|
|
23
22
|
that assumption: the scar still lints clean and still fires (anchors are
|
|
24
23
|
paths/patterns, not commits), but `git show <sha>` fails in every fresh clone,
|
|
25
24
|
so the receipt is unverifiable exactly where strangers would check it.
|
|
@@ -29,9 +28,15 @@ Observed at the v0.1.0 public release: the fresh-start force-push orphaned
|
|
|
29
28
|
until after the push because nothing in the toolchain connects "history
|
|
30
29
|
operation" to "evidence integrity."
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
The everyday trigger is the merge strategy itself: this repo squash-merges, so a
|
|
32
|
+
feature-branch commit — exactly the SHA you cite while drafting a scar mid-PR —
|
|
33
|
+
is orphaned the moment that PR lands. Rebase-merge does the same; only a true
|
|
34
|
+
merge-commit preserves branch SHAs. So PREFER `pr:`/`issue:` evidence (it
|
|
35
|
+
resolves on GitHub regardless of merge strategy) or a SHA already on the default
|
|
36
|
+
branch, and avoid citing transient feature-branch SHAs at all.
|
|
37
|
+
|
|
38
|
+
Before any deliberate history rewrite: grep `.scars/` for `commit:` evidence and
|
|
39
|
+
either (a) amend with a note explaining the rewrite, (b) replace bare SHAs with
|
|
40
|
+
full GitHub commit URLs (at GC's mercy), or (c) inline the fact so the scar is
|
|
41
|
+
self-contained. `scar lint` now warns when a cited SHA is unreachable from HEAD
|
|
42
|
+
(#43) — but it fires after the fact; the durable fix is not citing branch SHAs.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 6
|
|
3
|
+
type: landmine
|
|
4
|
+
title: Pattern anchors over-escape through YAML double-quotes and silently only self-match
|
|
5
|
+
severity: medium
|
|
6
|
+
confidence: 0.9
|
|
7
|
+
created: 2026-06-13
|
|
8
|
+
authors: ["claude-code", "kibukx"]
|
|
9
|
+
anchors:
|
|
10
|
+
- path: src/scar/orphan.py
|
|
11
|
+
- path: src/scar/match.py
|
|
12
|
+
- path: .scars/
|
|
13
|
+
evidence:
|
|
14
|
+
- pr: 40
|
|
15
|
+
- note: scar 1 grep pattern matched only its own body, never experiments/anchor-survival/RESULTS.md
|
|
16
|
+
expires:
|
|
17
|
+
condition: "pattern anchors are authored through a validated path (e.g. scar draft) that escapes regex correctly, OR lint rejects a pattern whose only pre-exclusion match is the scar's own file"
|
|
18
|
+
review_after: 2027-06-13
|
|
19
|
+
status: active
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
A regex written in a scar's `pattern:` field passes through YAML double-quoted
|
|
23
|
+
string parsing before it ever reaches the matcher. Backslashes collapse: what
|
|
24
|
+
you type as a four-backslash word boundary in the file becomes a regex needing
|
|
25
|
+
*literal* backslashes, not a word boundary. The intended code almost never
|
|
26
|
+
contains literal backslashes, so the pattern matches nothing real.
|
|
27
|
+
|
|
28
|
+
The trap is that it still reads as LIVE. Pattern anchors are matched against ALL
|
|
29
|
+
tracked content, including the scar's own `.scars/` body, and the body quotes
|
|
30
|
+
the pattern verbatim, so the scar keeps itself alive by self-reference. Orphan
|
|
31
|
+
detection sees a live anchor and stays quiet. The protection is dead; the gauge
|
|
32
|
+
says green. On this repo, scars 1 and 5 were pure ghosts (own-body only) and
|
|
33
|
+
scar 2 matched zero files at all, none visible until self-referential exclusion
|
|
34
|
+
was added in PR #40 (`_pattern_anchor_live(..., exclude_path=self_path)`).
|
|
35
|
+
|
|
36
|
+
What a future editor must do: when adding a `pattern:` anchor, verify it matches
|
|
37
|
+
the REAL code with `scar lint` (it must NOT appear under partial-rot), not just
|
|
38
|
+
that the scar parses. Prefer a `path:` anchor when the target is a file or dir;
|
|
39
|
+
path anchors do not go through regex escaping and cannot self-match. If you must
|
|
40
|
+
use a regex with escapes, test it against tracked content excluding the scar's
|
|
41
|
+
own file before trusting it.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
2026-06-12 false trigger: meta-session — we tuned the revert-language detector itself, so assistant prose ('revert language', 'reverting' in test fixtures/PR text) matched REVERT_RE; nothing abandoned (tool_errors were expected CLI probes/rejections). First post-tune FP pattern: self-referential sessions about the drafter trip the drafter.
|
|
2
|
+
2026-06-13 false trigger: tool_errors were external API hiccups (pypistats rate-limit/404, bq schema field); no code approach tried-and-abandoned this session (design-only work)
|
|
3
|
+
2026-06-12 false trigger: orphan-detection impl — 'revert' is feature-domain ('revert case' reverse hint = anchors-live-again) + a planned AC#1 refactor swapping batch-1 copied anchor logic for a shared match.py primitive; replacement was design-mandated, not a deadend discovered by failure
|
|
4
|
+
2026-06-13 — false trigger: signals from iterative re-anchoring + a caught staging bug (fixed #51) + gh CLI flakiness; all genuine lessons already captured as scars #5/#6 and issues #50
|
|
5
|
+
2026-06-13 false trigger: harvest-labeling session — 'revert/reverted/abandoned' prose describes ANOTHER repo's history (homelab-apps reverts judged as scar candidates), not an approach abandoned here; tool_errors were a wc-on-wrong-path + benign exit-1 during label recording. Curation work trips the drafter like meta-sessions do (FP #1).
|
|
6
|
+
2026-06-24 false trigger: #54 scorer investigation — 'revert' is feature-domain (the revert heuristic) + 'user_corrections'/'wrong' is me correcting the ISSUE BODY's mechanism claim (flapping base vs uncapped osc bonus), not abandoning a tried code approach. Read-only mapping + scope question; no code written, nothing deadended. Same self-ref class as FP #1/#5: sessions reasoning about the scorer trip the scorer.
|
|
@@ -18,6 +18,23 @@ frontmatter.
|
|
|
18
18
|
- Do not silently ignore broken scar files. Run `scar lint` when changing scar
|
|
19
19
|
format, parsing, promotion, lifecycle, or candidate-writing behavior.
|
|
20
20
|
|
|
21
|
+
## Authoring scars
|
|
22
|
+
|
|
23
|
+
When you abandon an approach (deadend), keep intentional-looking weirdness
|
|
24
|
+
(fence), or discover non-obvious coupling (landmine), record it as a scar.
|
|
25
|
+
The full authoring contract — qualification criteria, the candidates-only write
|
|
26
|
+
path, mandatory YAML frontmatter, and the regex over-escaping trap — is packaged
|
|
27
|
+
as the `scar-authoring` skill.
|
|
28
|
+
|
|
29
|
+
- **Claude Code:** install the plugin (recommended) or `scar skill install` to
|
|
30
|
+
drop the skill into `~/.claude/skills/`. It auto-loads on trigger.
|
|
31
|
+
- **MCP agents (Cursor/Windsurf/opencode):** the `scar_draft` tool enforces the
|
|
32
|
+
candidates-only path and lints before writing; its description carries the
|
|
33
|
+
digest.
|
|
34
|
+
- **Any runtime / manual:** run `scar agent skill` to print the full skill body
|
|
35
|
+
and load it into context. The canonical file is
|
|
36
|
+
`src/scar/skills/scar-authoring/SKILL.md`.
|
|
37
|
+
|
|
21
38
|
## Agent Integrations
|
|
22
39
|
|
|
23
40
|
- MCP-capable agents can launch the local server with `scar mcp`.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.6.0](https://github.com/Daily-Nerd/Scar/compare/v0.5.0...v0.6.0) (2026-06-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **agent:** Packaged scar-authoring skill + Claude Code plugin ([#32](https://github.com/Daily-Nerd/Scar/issues/32)) ([#58](https://github.com/Daily-Nerd/Scar/issues/58)) ([b1eaca7](https://github.com/Daily-Nerd/Scar/commit/b1eaca7f702d86a4e5429c8c085c1537f5505d0d))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **harvest:** exclude .scars/ tree from candidates (self-ref noise) ([#56](https://github.com/Daily-Nerd/Scar/issues/56)) ([29c2d61](https://github.com/Daily-Nerd/Scar/commit/29c2d6116e23a748df8455170c311023c6578c5c)), closes [#55](https://github.com/Daily-Nerd/Scar/issues/55)
|
|
14
|
+
|
|
15
|
+
## [0.5.0](https://github.com/Daily-Nerd/Scar/compare/v0.4.0...v0.5.0) (2026-06-13)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **harvest:** precision@N reporting CLI — close the measurement loop ([#53](https://github.com/Daily-Nerd/Scar/issues/53)) ([100bd1d](https://github.com/Daily-Nerd/Scar/commit/100bd1d46bbac981a3629b74c237fc0584f5ce05))
|
|
21
|
+
* **harvest:** ranking layer — heuristic scorer + label-capture instrument ([#39](https://github.com/Daily-Nerd/Scar/issues/39)) ([7369f73](https://github.com/Daily-Nerd/Scar/commit/7369f738d3fe356a0290cbf05f0654a48587ee9f))
|
|
22
|
+
* **lifecycle:** lint warns on evidence commit SHAs unreachable from HEAD ([#44](https://github.com/Daily-Nerd/Scar/issues/44)) ([714357e](https://github.com/Daily-Nerd/Scar/commit/714357e9b6366ec67d71d086cf62d8dafbcae976))
|
|
23
|
+
* **lifecycle:** orphan detection — resolution failure, loud in CI ([#34](https://github.com/Daily-Nerd/Scar/issues/34)) ([421a12a](https://github.com/Daily-Nerd/Scar/commit/421a12aae25cc46f6aa40593a6274bb755d4b81b))
|
|
24
|
+
* **lifecycle:** partial-anchor rot — surface dead anchors on firing scars ([#40](https://github.com/Daily-Nerd/Scar/issues/40)) ([85fd57e](https://github.com/Daily-Nerd/Scar/commit/85fd57e397055576bd754c3d606417274d6a9d5c))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
* **scars:** drop [#6](https://github.com/Daily-Nerd/Scar/issues/6) orphaned receipt, broaden scar [#5](https://github.com/Daily-Nerd/Scar/issues/5) for squash-merge ([#51](https://github.com/Daily-Nerd/Scar/issues/51)) ([4c63ac5](https://github.com/Daily-Nerd/Scar/commit/4c63ac50c648d8ec47190c6045987a276c9fb9bf))
|
|
30
|
+
* **scars:** re-anchor 3 ghost pattern anchors to real code ([#42](https://github.com/Daily-Nerd/Scar/issues/42)) ([00a2fcb](https://github.com/Daily-Nerd/Scar/commit/00a2fcb5c41c165f260019ec95bc636b18d17491))
|
|
31
|
+
* **scars:** replace 3 orphaned bare commit-SHA receipts with self-contained notes ([#46](https://github.com/Daily-Nerd/Scar/issues/46)) ([a224619](https://github.com/Daily-Nerd/Scar/commit/a224619f47387cce401039bf9ddbb93cb3841641))
|
|
32
|
+
|
|
33
|
+
## [0.4.0](https://github.com/Daily-Nerd/Scar/compare/v0.3.0...v0.4.0) (2026-06-12)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* **format:** reserve optional receipt_id field ([#29](https://github.com/Daily-Nerd/Scar/issues/29)) ([47ce933](https://github.com/Daily-Nerd/Scar/commit/47ce933cde02fa1155d0474e98101804cb7b1a80))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Bug Fixes
|
|
42
|
+
|
|
43
|
+
* **hooks:** expose lifecycle commands ([#31](https://github.com/Daily-Nerd/Scar/issues/31)) ([dba2c0d](https://github.com/Daily-Nerd/Scar/commit/dba2c0d1c1bd8a0f73880bfab0ff17187eec2fb9)), closes [#30](https://github.com/Daily-Nerd/Scar/issues/30)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Documentation
|
|
47
|
+
|
|
48
|
+
* **roadmap:** truth pass — gates resolved, Phase 1 shipped, Phase 2 in progress ([#26](https://github.com/Daily-Nerd/Scar/issues/26)) ([7701a97](https://github.com/Daily-Nerd/Scar/commit/7701a97610f470e7726e7f5fc86932a5101eb255))
|
|
49
|
+
|
|
50
|
+
## [0.3.0](https://github.com/Daily-Nerd/Scar/compare/v0.2.0...v0.3.0) (2026-06-12)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Features
|
|
54
|
+
|
|
55
|
+
* **agents:** multi-agent scar integration — AGENTS.md, MCP server, agent helpers ([#21](https://github.com/Daily-Nerd/Scar/issues/21)) ([52c817f](https://github.com/Daily-Nerd/Scar/commit/52c817fc963f8f829b70de60b772c1097c6f0334))
|
|
56
|
+
|
|
57
|
+
## [0.2.0](https://github.com/Daily-Nerd/Scar/compare/v0.1.1...v0.2.0) (2026-06-12)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### Features
|
|
61
|
+
|
|
62
|
+
* **cli:** lifecycle v0 — challenge, archive, review_after surfacing ([#16](https://github.com/Daily-Nerd/Scar/issues/16)) ([0c6fb05](https://github.com/Daily-Nerd/Scar/commit/0c6fb05fbdbb57f8ac9b2a5b558e4cf121c3d5c0)), closes [#14](https://github.com/Daily-Nerd/Scar/issues/14)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### Documentation
|
|
66
|
+
|
|
67
|
+
* **readme:** scar challenge is planned, not shipped — point to lifecycle issue ([8c6b021](https://github.com/Daily-Nerd/Scar/commit/8c6b021c95299cf40bf6c2d978a0421bb9705cb6))
|
|
68
|
+
|
|
69
|
+
## [0.1.1](https://github.com/Daily-Nerd/Scar/compare/v0.1.0...v0.1.1) (2026-06-12)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
### Bug Fixes
|
|
73
|
+
|
|
74
|
+
* **hooks:** drafter triggers on revert language only ([#12](https://github.com/Daily-Nerd/Scar/issues/12)) ([547c4bb](https://github.com/Daily-Nerd/Scar/commit/547c4bb21e3521682b6a4046602d6703d88c2cf1)), closes [#11](https://github.com/Daily-Nerd/Scar/issues/11)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scar-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: SCAR — version control for negative knowledge (deadends, fences, landmines)
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -95,6 +95,16 @@ stop all automatic injection and drafting while keeping the repository's
|
|
|
95
95
|
scar hook uninstall
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
**Recommended (Claude Code): install the plugin** so the hooks *and* the
|
|
99
|
+
scar-authoring skill arrive together via the marketplace.
|
|
100
|
+
|
|
101
|
+
**Fallback / non-marketplace:** `scar hook install` registers the hooks, and
|
|
102
|
+
`scar skill install` drops the authoring skill into `~/.claude/skills/`. Both are
|
|
103
|
+
explicit — you run them, nothing is installed as a side effect.
|
|
104
|
+
|
|
105
|
+
Non-Claude agents: `scar agent skill` prints the authoring skill for any runtime;
|
|
106
|
+
MCP agents get the digest via the `scar_draft` tool description.
|
|
107
|
+
|
|
98
108
|
Wiring MCP-capable agents:
|
|
99
109
|
|
|
100
110
|
```bash
|
|
@@ -86,6 +86,16 @@ stop all automatic injection and drafting while keeping the repository's
|
|
|
86
86
|
scar hook uninstall
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
**Recommended (Claude Code): install the plugin** so the hooks *and* the
|
|
90
|
+
scar-authoring skill arrive together via the marketplace.
|
|
91
|
+
|
|
92
|
+
**Fallback / non-marketplace:** `scar hook install` registers the hooks, and
|
|
93
|
+
`scar skill install` drops the authoring skill into `~/.claude/skills/`. Both are
|
|
94
|
+
explicit — you run them, nothing is installed as a side effect.
|
|
95
|
+
|
|
96
|
+
Non-Claude agents: `scar agent skill` prints the authoring skill for any runtime;
|
|
97
|
+
MCP agents get the digest via the `scar_draft` tool description.
|
|
98
|
+
|
|
89
99
|
Wiring MCP-capable agents:
|
|
90
100
|
|
|
91
101
|
```bash
|
|
@@ -29,11 +29,11 @@ Public at [github.com/Daily-Nerd/Scar](https://github.com/Daily-Nerd/Scar), `sca
|
|
|
29
29
|
|
|
30
30
|
- ✅ **MCP server** (`scar_query`, `scar_why`, `scar_draft`) — shipped v0.3.0, dependency-free stdio, drafts gated to candidates/. First non-Claude agent (Codex) arrived and contributed the implementation — the deferral condition resolving itself.
|
|
31
31
|
- ✅ Multi-agent surface: committed `AGENTS.md`, `scar inject --diff`, `scar agent doctor/config` for Codex, Cursor, Windsurf, opencode (v0.3.0)
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- ⬜ Re-anchoring agent workflow: orphaned scar + orphaning diff → proposed new anchors as a PR
|
|
32
|
+
- ✅ CI surface: expiry warnings (`lint`/`status`, v0.2.0); orphan detection — all-anchors-dead firing scars → `orphaned`, loud in CI (#34); partial-rot advisory — firing scars with ≥1 dead anchor among live ones, named in `lint`/`status`/`orphan` (#35). Principle 3 now enforced by code for both total and partial rot.
|
|
33
|
+
- ✅ Harvest ranking layer — heuristic weighted scorer + label-capture instrument, zero-dep, deterministic (#39). Weights remain intuition until real-repo labels calibrate precision.
|
|
34
|
+
- ⬜ Re-anchoring agent workflow: orphaned/partially-rotted scar + orphaning diff → proposed new anchors as a PR
|
|
35
35
|
- ⬜ Editor surfaces (VS Code gutter marks, LSP code lens) — fences visible to humans, not only agents
|
|
36
|
-
-
|
|
36
|
+
- ✅ Lint warning on evidence commit SHAs unreachable from HEAD (#43) — scar #5's expiry condition, now enforced; advisory in `lint`, skipped on shallow clones
|
|
37
37
|
|
|
38
38
|
## Phase 3 — The org graph ⏸ parked by design
|
|
39
39
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Experiment: Harvest Ranking + Label Instrument (Issue #38)
|
|
2
|
+
|
|
3
|
+
**Question.** Can a cheap, explainable heuristic score RANK harvested candidates so the human curator reads the real scars first — without normalizing away the precision signal carried by candidate type?
|
|
4
|
+
|
|
5
|
+
**Why it matters.** Raw harvest precision sits at ~13% on real history. If a curator must read every candidate in arbitrary order, the tool costs more attention than it saves. Ranking earns its place only if the top-N is denser in real scars than the tail. This experiment builds the *instrument* (labels + precision@N) so that claim becomes measurable instead of asserted.
|
|
6
|
+
|
|
7
|
+
## What the ranker does
|
|
8
|
+
|
|
9
|
+
Each candidate gets a deterministic `score` (see `src/scar/harvest.py`). The score is a sum of calibration priors — base weight per signal type plus small bonuses (PR/issue ref on reverts, files-deleted threshold, oscillation count, comment specificity, recency). All weights are **priors, unvalidated until labels exist**; this experiment is how they get validated.
|
|
10
|
+
|
|
11
|
+
**Cross-section ranking uses RAW score, no normalization** (`scar harvest --top-k N`). The per-type base constants order `comment < flapping < deleted_component < revert`. That ordering is an intentional precision prior: signal *type* predicts precision, so a revert outranks a grep hit by design. Normalizing scores across types would erase exactly the signal we want to exploit. If the labels later show the ordering is wrong, fix the base constants — do not add normalization.
|
|
12
|
+
|
|
13
|
+
## Label JSONL format
|
|
14
|
+
|
|
15
|
+
Path: `experiments/harvest/labels.jsonl` (committed — instrument/data, like the anchor-survival replay). Written one line at a time by:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
scar harvest <repo> --label <id> keep|discard [--note "..."]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Each line is one JSON object:
|
|
22
|
+
|
|
23
|
+
| Field | Type | Meaning |
|
|
24
|
+
|---------|--------|---------|
|
|
25
|
+
| `id` | string | the candidate's stable id (see below) |
|
|
26
|
+
| `label` | string | **exactly** `"keep"` or `"discard"` — nothing else is accepted |
|
|
27
|
+
| `note` | string | free-text rationale (may be empty) |
|
|
28
|
+
| `date` | string | `YYYY-MM-DD`, from `time.strftime` (monkeypatchable in tests) |
|
|
29
|
+
| `repo` | string | the harvested repo's name (provenance) |
|
|
30
|
+
|
|
31
|
+
**Only `keep`/`discard` are valid.** The CLI rejects any other label value with a non-zero exit and writes nothing. This is load-bearing: `precision_at_n` reads `label == "keep"` and counts everything labeled as the denominator — a third value (`"maybe"`, `"skip"`) would silently corrupt precision by inflating the denominator without ever counting toward the numerator.
|
|
32
|
+
|
|
33
|
+
**Id validation.** `--label` runs `harvest(repo)` for the target repo, collects every candidate id, and **rejects an id not in that set** (mirrors `scar orphan --apply` rejecting an unknown `--id`). You cannot label a candidate that the current harvest does not produce.
|
|
34
|
+
|
|
35
|
+
## Candidate-id stability rule
|
|
36
|
+
|
|
37
|
+
`harvest.candidate_id(signal_type, candidate)` = first 10 hex of `sha1(signal_type + identifying-fields)`. The id is a hash of the **identifying fields only — NOT the score, NOT the id itself**, so the same candidate gets the same id across runs and a re-scored candidate keeps its label.
|
|
38
|
+
|
|
39
|
+
Identifying fields per type:
|
|
40
|
+
|
|
41
|
+
| Type | Hashed fields |
|
|
42
|
+
|---------------------|---------------|
|
|
43
|
+
| `revert` | `commit` |
|
|
44
|
+
| `deleted_component` | `component` |
|
|
45
|
+
| `flapping` | `file` + `key` |
|
|
46
|
+
| `comment` | `location` + `text[:40]` |
|
|
47
|
+
|
|
48
|
+
**Comment ids use `text[:40]`** — the first 40 characters of the comment text. Keep those 40 chars stable: editing the tail of a long comment preserves the id; editing the start changes it (and orphans any prior label). This deliberately tolerates the 120-char display truncation in `_comment_archaeology` without making the id depend on it.
|
|
49
|
+
|
|
50
|
+
## Precision@N
|
|
51
|
+
|
|
52
|
+
`harvest.precision_at_n(ranked, labels, n)`:
|
|
53
|
+
- `ranked` — candidates pre-sorted by score descending (caller's responsibility; `scar harvest --top-k` produces this order).
|
|
54
|
+
- `labels` — a `{id: "keep"|"discard"}` dict built from the JSONL (group by id; last write wins if a candidate was labeled twice).
|
|
55
|
+
- Take the first `n`. Among them, consider **only** candidates whose id is in `labels`. Return the fraction of that labeled subset where `label == "keep"`.
|
|
56
|
+
|
|
57
|
+
**Contract: unlabeled candidates in the top-N are excluded from BOTH numerator and denominator.** They neither help nor hurt the score — precision@N measures "of the ones we judged in the top-N, how many were real". If no candidate in the top-N is labeled, the result is `0.0` (not NaN, not an error).
|
|
58
|
+
|
|
59
|
+
## Method (to run once labels accrue)
|
|
60
|
+
|
|
61
|
+
1. Harvest a real repo; curate the top-N by hand, recording `keep`/`discard` via `--label`.
|
|
62
|
+
2. Build `{id: label}` from `labels.jsonl`.
|
|
63
|
+
3. Compute `precision_at_n` at several N (e.g. 5, 10, 20) and compare against the ~13% raw base rate.
|
|
64
|
+
4. Compare per-type precision to validate (or refute) the base-constant ordering.
|
|
65
|
+
|
|
66
|
+
## Pre-registered claim
|
|
67
|
+
|
|
68
|
+
- **Ranking earns its place** if precision@N for small N is materially above the ~13% raw base rate — i.e. the top of the ranked list is denser in real scars than the unranked pool.
|
|
69
|
+
- If precision@N ≈ base rate at every N, the score adds no signal and the constants need rework (or the heuristic is the wrong instrument).
|
|
70
|
+
|
|
71
|
+
## Limitations (declared)
|
|
72
|
+
|
|
73
|
+
1. Weights are hand-set priors, not fit to data — this instrument exists to replace the guess with a measurement, but until ~50 labels accrue the ranking is an assertion.
|
|
74
|
+
2. Single-curator labels carry that curator's bias; `keep`/`discard` is a coarse binary over what is really a confidence gradient.
|
|
75
|
+
3. `precision_at_n` ignores recall — a candidate the harvester never surfaced cannot be labeled, so a missed real scar is invisible here.
|
|
76
|
+
4. Recency scoring reads the wall clock at harvest time; the same candidate scored months apart can shift rank (id stays stable, so labels still attach correctly).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "scar",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "SCAR — version control for negative knowledge (deadends, fences, landmines)",
|
|
5
|
+
"hooks": {
|
|
6
|
+
"PreToolUse": [
|
|
7
|
+
{
|
|
8
|
+
"matcher": "Edit|Write|MultiEdit|NotebookEdit",
|
|
9
|
+
"hooks": [
|
|
10
|
+
{ "type": "command", "command": "scar hook precheck", "timeout": 10 }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"SessionStart": [
|
|
15
|
+
{
|
|
16
|
+
"hooks": [
|
|
17
|
+
{ "type": "command", "command": "scar hook session-notice", "timeout": 10 }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"Stop": [
|
|
22
|
+
{
|
|
23
|
+
"hooks": [
|
|
24
|
+
{ "type": "command", "command": "scar hook stop-drafter", "timeout": 15 }
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scar-authoring
|
|
3
|
+
description: >
|
|
4
|
+
Author negative-knowledge scars (deadend/fence/landmine) for a repo's .scars/
|
|
5
|
+
directory — qualification criteria, the candidates-only write path, and the
|
|
6
|
+
mandatory-frontmatter / regex-escaping traps that silently break scars.
|
|
7
|
+
Trigger: when you abandon an approach after trying it, when you keep code that
|
|
8
|
+
looks wrong on purpose, or when you discover that changing one thing breaks
|
|
9
|
+
another non-obviously — and you want to record it so the next agent does not
|
|
10
|
+
repeat the pain.
|
|
11
|
+
license: MIT
|
|
12
|
+
metadata:
|
|
13
|
+
author: Daily-Nerd
|
|
14
|
+
version: "1.0"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Authoring Scars
|
|
18
|
+
|
|
19
|
+
A scar records *negative knowledge*: a thing that was tried and failed, code
|
|
20
|
+
that looks wrong but is intentional, or a non-obvious coupling. Scars fire
|
|
21
|
+
automatically — the next editor sees the relevant scar injected before they
|
|
22
|
+
touch anchored code. Your job here is to write a good one.
|
|
23
|
+
|
|
24
|
+
## When to Use
|
|
25
|
+
|
|
26
|
+
- You **abandoned an approach** after trying it → `deadend`
|
|
27
|
+
- You **kept code that looks wrong on purpose** → `fence`
|
|
28
|
+
- You **found that changing A breaks B non-obviously** → `landmine`
|
|
29
|
+
|
|
30
|
+
Not a scar: routine debugging that eventually succeeded. If you tried something,
|
|
31
|
+
it worked, and you moved on — there is nothing to record.
|
|
32
|
+
|
|
33
|
+
## The Three Types
|
|
34
|
+
|
|
35
|
+
**`deadend` — tried and failed.** Protects against re-attempting a dead path.
|
|
36
|
+
Primary anchor is usually `pattern` (the shape of the failed approach).
|
|
37
|
+
*Example (scar #2):* an agent tried to install Claude Code hooks by writing
|
|
38
|
+
`~/.claude/settings.json` directly; the permission classifier denies it. Anchor
|
|
39
|
+
`path: src/scar/installer.py`; body says the *user* must run `scar hook install`.
|
|
40
|
+
|
|
41
|
+
**`fence` — looks wrong, is intentional.** Protects existing code from a
|
|
42
|
+
"cleanup" that would break it. Primary anchor is `path`.
|
|
43
|
+
*Example (scar #3):* the installer deliberately ignores an active virtualenv so
|
|
44
|
+
hooks bind to a stable `scar` on PATH, not a venv shim that disappears. Anchor
|
|
45
|
+
`path: src/scar/installer.py`; body says "do not 'simplify' this to plain
|
|
46
|
+
`shutil.which`."
|
|
47
|
+
|
|
48
|
+
**`landmine` — touching A breaks B.** Anchor the trigger site; the body names
|
|
49
|
+
the blast radius. *Example (scar #6):* a regex in a scar's `pattern:` field is
|
|
50
|
+
double-quoted YAML, so `\b` collapses and the anchor silently self-matches only
|
|
51
|
+
its own `.scars/` body — the protection is dead but the gauge reads green.
|
|
52
|
+
|
|
53
|
+
## The Write Contract (non-negotiable)
|
|
54
|
+
|
|
55
|
+
1. COPY `.scars/template.md` (or this skill's `assets/template.md`) — do not
|
|
56
|
+
edit the template itself.
|
|
57
|
+
2. Write to `.scars/candidates/<slug>.md` with `status: candidate`.
|
|
58
|
+
3. **Never** write into `.scars/*.md` directly. A human promotes via
|
|
59
|
+
`scar promote`.
|
|
60
|
+
4. If an MCP server is wired, prefer the `scar_draft` tool — it enforces the
|
|
61
|
+
path and runs lint before writing.
|
|
62
|
+
|
|
63
|
+
## Mandatory Frontmatter
|
|
64
|
+
|
|
65
|
+
A file without `---`-fenced YAML frontmatter is **not a scar at all** — it never
|
|
66
|
+
fires. Minimum valid block: `type`, `title`, `severity`, `confidence`,
|
|
67
|
+
`created`, `authors`, at least one `anchors` entry, and `status: candidate`.
|
|
68
|
+
|
|
69
|
+
## Anti-Over-Escape (the #1 silent failure)
|
|
70
|
+
|
|
71
|
+
Prefer a `path:` anchor — it cannot self-match and needs no escaping. If you
|
|
72
|
+
must use a `pattern:` regex: backslashes in double-quoted YAML collapse (`\b`
|
|
73
|
+
dies), and the pattern is matched against all tracked content **including the
|
|
74
|
+
scar's own body**, so a broken pattern keeps itself alive by self-reference.
|
|
75
|
+
Run `scar lint` and confirm the scar does NOT appear under partial-rot
|
|
76
|
+
(self-match only).
|
|
77
|
+
|
|
78
|
+
Wrong: `pattern: "\\bwiden\\b"` → Right: `path: src/widen/` (no escaping, no
|
|
79
|
+
self-match).
|
|
80
|
+
|
|
81
|
+
## Anchors, Severity, Size
|
|
82
|
+
|
|
83
|
+
- `path:` = repo-relative prefix (file or directory). `pattern:` =
|
|
84
|
+
case-insensitive regex over path + new content.
|
|
85
|
+
- Severity: `low | medium | high | critical`.
|
|
86
|
+
- Injection is capped at ~3 scars / ~700 chars each — write tight: 5–15 lines,
|
|
87
|
+
evidence cited inline.
|
|
88
|
+
|
|
89
|
+
## Verify Before Finishing
|
|
90
|
+
|
|
91
|
+
- `scar lint` must pass.
|
|
92
|
+
- At least one `evidence` receipt (commit / pr / incident / note) — without it
|
|
93
|
+
the scar is challengeable on sight.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
# COPY THIS FILE — do not edit the template itself.
|
|
3
|
+
# New scars: write to .scars/candidates/<slug>.md with status: candidate.
|
|
4
|
+
# A human reviewer promotes to .scars/NNNN-<slug>.<type>.md with status: active.
|
|
5
|
+
id: 0 # assigned at promotion (next free NNNN)
|
|
6
|
+
type: deadend # deadend = tried+failed | fence = looks wrong, intentional | landmine = touching A breaks B
|
|
7
|
+
title: One line, searchable, says the constraint
|
|
8
|
+
severity: medium # low | medium | high | critical
|
|
9
|
+
confidence: 0.7 # 0..1 — how sure are we this still holds
|
|
10
|
+
created: 1970-01-01
|
|
11
|
+
authors: ["claude-code"] # add the human reviewer at promotion
|
|
12
|
+
anchors:
|
|
13
|
+
- path: src/module/ # file or directory this protects
|
|
14
|
+
- pattern: "regex" # optional: fires when matching code appears in ANY new/edited file
|
|
15
|
+
evidence:
|
|
16
|
+
- commit: abc1234 # at least one receipt: commit, pr, incident, or note
|
|
17
|
+
expires:
|
|
18
|
+
condition: "what change would make this scar obsolete"
|
|
19
|
+
review_after: 1971-01-01 # force a freshness look even if condition never triggers
|
|
20
|
+
status: template # candidate | active | challenged | archived (template = never parsed)
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
Body: 5-15 lines of prose. What was tried/observed, why it failed or why the
|
|
24
|
+
weirdness is intentional, and what a future editor must do instead. Write it
|
|
25
|
+
for someone (human or agent) with zero context. Cite the evidence inline.
|
|
@@ -64,3 +64,10 @@ def config(target: str) -> str:
|
|
|
64
64
|
if target not in CONFIGS:
|
|
65
65
|
raise ValueError(f"unknown target '{target}' (expected: {', '.join(TARGETS)})")
|
|
66
66
|
return CONFIGS[target]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def skill() -> str:
|
|
70
|
+
"""Return the full scar-authoring SKILL.md body (packaged, runtime-neutral)."""
|
|
71
|
+
from importlib.resources import files
|
|
72
|
+
resource = files("scar").joinpath("skills/scar-authoring/SKILL.md")
|
|
73
|
+
return resource.read_text(encoding="utf-8")
|