@antoneeo/kb-agentic-skill 1.7.0 → 1.8.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  Every significant change to this skill is recorded here.
4
4
 
5
+ ## [1.8.0] - 2026-08-28
6
+
7
+ F-041 -- the mid-session drift field report, both items.
8
+
9
+ ### Added
10
+ - **`check` notes an unwired or dead session-orientation hook.** `init` wires the
11
+ hook at init time only; pre-F-036 and agent-bootstrapped projects never got it,
12
+ silently. `check` now prints, right after its summary line, a one-line `[note]` when no hook is detectable
13
+ in `.claude/settings.json` / `.claude/settings.local.json` / `.codex/hooks.json`,
14
+ and a distinct note when a hook is wired but its validator does not resolve
15
+ (any-resolves aggregation across entries; BOM-tolerant reads; fail-open on
16
+ malformed config). Informational only: never the exit code, never a `validate`
17
+ warning -- CI stays green because the wired hook legitimately lives in the
18
+ git-ignored local settings file.
19
+ - **`remind` -- an opt-in per-turn one-line reminder (UserPromptSubmit).** orient
20
+ fires once; mid-session drift had no mechanical guard. One constant, zero-read
21
+ line re-arms the kb minimum (triage levels, the topics/INDEX.md recall trigger,
22
+ the capture moment) at every prompt. Never wired by `init`, never a default:
23
+ ENFORCEMENT par.4 documents the snippet and the ~50 tokens/prompt cost.
24
+
5
25
  ## [1.7.0] - 2026-08-28
6
26
 
7
27
  F-040 — the capture moment (second-brain unit 2): the day's decisions reach the
package/README.md CHANGED
@@ -82,7 +82,11 @@ it: a repo that vendors it gets a repo-relative command in the shared `.claude/s
82
82
  (portable — commit it); otherwise the path is machine-specific and goes to the git-ignored
83
83
  `.claude/settings.local.json`, and each teammate runs `init` once. Re-running `init` never
84
84
  duplicates the hook, and a hook whose validator no longer resolves is reported as **broken**
85
- with the correction rather than counted as installed.
85
+ with the correction rather than counted as installed. Since F-041, `check` also
86
+ prints a one-line note when a project has no orientation hook wired (or a dead
87
+ one), and an opt-in per-turn reminder exists for long sessions: `sdlc_check.py
88
+ remind`, one constant line via UserPromptSubmit -- documented in `ENFORCEMENT.md`
89
+ par.4, never wired by default.
86
90
 
87
91
  ## The family: three lenses, one spine
88
92
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kb-agentic-skill",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Knowledge-Base & Document-First protocol with risk triage, Vision governance, signal distillation and optional devPNT integration.",
5
5
  "author": "Antonio Pinto (https://github.com/Antoneeo)"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antoneeo/kb-agentic-skill",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Knowledge-Base & Document-First protocol for Claude Code, Gemini CLI, Google Antigravity and Codex with risk triage, Vision governance, signal distillation and optional devPNT integration.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -86,6 +86,20 @@ that cannot be placed in a command safely, or a settings file it cannot write. `
86
86
  prints which case applied, per client — a silent skip is what let "documented default
87
87
  that nobody installs" survive in the first place.
88
88
 
89
+ **Older and agent-bootstrapped projects are silently unwired -- `check` now tells you (F-041).**
90
+ `init` wires the hook at init time only: a project initialized before F-036, or whose
91
+ docs root was bootstrapped by the agent (which runs `index`, never `init`), never
92
+ received it retroactively -- and until F-041 nothing said so. `check` now prints, right after its summary line, a
93
+ one-line `[note]` when no orientation hook is detectable (`.claude/settings.json`,
94
+ `.claude/settings.local.json`, `.codex/hooks.json`), and a DISTINCT note when a hook is
95
+ wired but the validator it names does not resolve (the wired-and-DEAD state below --
96
+ silence there would bless the worst of the three states). The notes are informational
97
+ only: never the exit code, never a `validate` warning -- CI (`validate --strict`) does
98
+ not see them, because the wired hook legitimately lives in the git-ignored
99
+ `settings.local.json`. Known residuals, accepted as honest: a client with no hook
100
+ mechanism (Gemini today) keeps the note -- read it as the manual-reads reminder; a
101
+ `.codex/hooks.json` wired in a shape other than the documented one keeps it too.
102
+
89
103
  **Which settings file, and why it is not always the shared one.** The command names a
90
104
  validator, and where that validator lives decides where the hook may be written:
91
105
 
@@ -133,6 +147,38 @@ Gemini CLI — wire the same command into its startup-hook mechanism if present;
133
147
  - **Hybrid/devPNT projects**: add `--hybrid` — the hook then appends a one-line pointer to run `devpnt_mcp_get_bootstrap` for the Master Plan / Knowledge Layer and does not replicate them; the filesystem orientation (router + handoff + README) still emits.
134
148
  - Like the CI gate (§2), if you copied `sdlc_check.py` into the repo, the hook references that copy — keep it current when you update the skill.
135
149
 
150
+ ### Per-turn reminder: `remind` (UserPromptSubmit -- opt-in, NEVER a default)
151
+
152
+ `orient` fires once, at session start; a long session drifts after it (a compaction,
153
+ ten turns of other work -- the F-041 field report). `sdlc_check.py remind` prints ONE
154
+ constant line -- the kb minimum: the triage levels, the topic-index recall trigger, the
155
+ capture moment -- for the client to inject at every prompt. Cost: ~50 tokens per
156
+ prompt, which is exactly why `init` never wires it and never will by default: the
157
+ per-turn cost is the project's choice. The line is constant and zero-read, so nothing
158
+ repo-controlled can ride into the agent's context through it, and the vision's
159
+ no-per-turn-graph-queries non-goal stays honored -- the line re-arms the
160
+ entry-into-topic trigger; it never runs the reading itself.
161
+
162
+ Claude Code -- in the project's `.claude/settings.json` (or `settings.local.json` when
163
+ the validator path is machine-specific, same rule as the orientation hook above):
164
+
165
+ ```json
166
+ {
167
+ "hooks": {
168
+ "UserPromptSubmit": [
169
+ {
170
+ "hooks": [
171
+ {
172
+ "type": "command",
173
+ "command": "python \"C:\\Users\\<user>\\.claude\\skills\\kb-agentic\\scripts\\sdlc_check.py\" remind"
174
+ }
175
+ ]
176
+ }
177
+ ]
178
+ }
179
+ }
180
+ ```
181
+
136
182
  ## 5. Skill eval battery (release gate)
137
183
 
138
184
  **Skill development only.** `test_*.py` and `evals/` are deliberately absent from the npm `files` allowlist — they never reach an installed consumer, so this section applies to whoever builds the skill, not to a project that uses it. (Consumers get `sdlc_check.py`; §1–§4 are theirs.)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: kb-agentic
3
- version: 1.7.0
3
+ version: 1.8.0
4
4
  description: Knowledge-Base & Document-First protocol with risk-proportional triage, Vision as a guide, Signal Distillation, a complete Standalone mode and optional symbiosis with devPNT. Use for user documentation, knowledge extraction, SOPs, research notes, decision logs, knowledge management — and for answering project questions from the claim ledger and topic graph.
5
5
  author: Antonio Pinto (https://github.com/Antoneeo)
6
6
  copyright: (c) 2026 Antonio Pinto
@@ -23,7 +23,7 @@ Support files in the skill directory:
23
23
  - `review.md`: the review discipline — when a review is due, how to request one, how to receive findings, how to review.
24
24
  - `dispatch.md`: opt-in subagent execution of an approved plan.
25
25
  - `routing.md`: which lens owns this unit of work. Read ONLY when a sibling lens skill is installed alongside this one; a single-lens install never reads it.
26
- - `scripts/sdlc_check.py` + `scripts/sdlc_core.py`: the mechanical validator for the docs root (`check`, `validate`, `index`, `stale`, `mark`, `gate`, `plan`, `orient`, `migrate`, and the knowledge overlay: `graph`, `corpus`, `claim-id`, `anchor`, `export`, `import`). Two files: the core is the family's shared spine; the entry point IS the knowledge overlay — the claim ledger and topic-graph checks live inside it, so the core alone runs none of them. Copy both, or neither.
26
+ - `scripts/sdlc_check.py` + `scripts/sdlc_core.py`: the mechanical validator for the docs root (`check`, `validate`, `index`, `stale`, `mark`, `gate`, `plan`, `orient`, `migrate`, and the knowledge overlay: `graph`, `corpus`, `claim-id`, `anchor`, `export`, `import`, plus `remind` — the opt-in per-turn one-line reminder, ENFORCEMENT §4). Two files: the core is the family's shared spine; the entry point IS the knowledge overlay — the claim ledger and topic-graph checks live inside it, so the core alone runs none of them. Copy both, or neither.
27
27
  - `ENFORCEMENT.md`: optional setup for CI and hooks.
28
28
 
29
29
  Read these files only when needed. `SKILL.md` is the operating contract; the support files are progressive resources.
@@ -1598,6 +1598,26 @@ def kb_cmd_orient(argv):
1598
1598
  return rc
1599
1599
 
1600
1600
 
1601
+ def kb_cmd_remind(_argv):
1602
+ """F-041 item B: the per-turn one-line reminder (UserPromptSubmit, opt-in).
1603
+
1604
+ A CONSTANT, zero-read line -- the kb minimum an agent needs re-armed every
1605
+ prompt: triage, the recall trigger, the capture moment. Constant by
1606
+ contract: nothing repo-controlled may ride a line injected into the agent's
1607
+ context every turn, and zero reads is what keeps the per-turn cost flat.
1608
+ The vision's non-goal bans per-turn GRAPH queries; this line only re-arms
1609
+ the entry-into-topic criterion, it never runs the descent. Never wired by
1610
+ init and never a default: ENFORCEMENT par.4 documents the manual snippet
1611
+ and the ~50 tokens/prompt cost -- the project chooses. Fail-open like every
1612
+ hook surface: argv is ignored whole, the exit code is always 0."""
1613
+ print("kb-agentic: triage before acting -- L1 quick fact, L2 propagation of "
1614
+ "settled knowledge, L3 new knowledge unit, Spike exploration; declare "
1615
+ "the level. Answering about this project's domain? Read topics/INDEX.md "
1616
+ "under the docs root first and cite claim ids, or state no coverage. "
1617
+ "Decisions made today get recorded in the KB at session close.")
1618
+ return 0
1619
+
1620
+
1601
1621
  def kb_cmd_help():
1602
1622
  """The spine's usage, then the overlay's own commands.
1603
1623
 
@@ -1619,6 +1639,8 @@ knowledge overlay (kb-agentic) -- also available:
1619
1639
  anchor <path> <phrase> resolve a phrase to a verified locator span
1620
1640
  export --out <dir> bundle a subgraph WITH the bytes its claims cite
1621
1641
  import <dir> merge a bundle in additively (never overwrites)
1642
+ remind one-line per-turn reminder (UserPromptSubmit hook,
1643
+ opt-in -- ENFORCEMENT par.4; constant, zero-read)
1622
1644
 
1623
1645
  index / validate / check the spine's behaviour PLUS the claim ledger and
1624
1646
  the topic graph""")
@@ -1636,6 +1658,10 @@ def main(argv=None):
1636
1658
  # spine runs it on the raw argv, the overlay only appends the topic router.
1637
1659
  if argv and argv[0] == "orient":
1638
1660
  return kb_cmd_orient(argv)
1661
+ # `remind` too: it is a hook surface (UserPromptSubmit), so a stray flag
1662
+ # must print the line, not an argparse usage error into someone's prompt.
1663
+ if argv and argv[0] == "remind":
1664
+ return kb_cmd_remind(argv)
1639
1665
  # Forward-by-default: anything not intercepted goes to the spine untouched.
1640
1666
  # Never a hand-copied command tuple - that is how a spine command gets
1641
1667
  # silently dropped (mkt_check.py ships that exact defect with `migrate`).
@@ -1646,6 +1646,109 @@ def cmd_mark(root, paths):
1646
1646
  return 0
1647
1647
 
1648
1648
 
1649
+ # ------------------------------------------------- orientation-hook wiring
1650
+ # F-041: read-only mirror of the installer-side detection in lib.js
1651
+ # (findOrientHook + orientHookValidator). The JS owns WRITING hooks, with its
1652
+ # injection guards; this side only DETECTS, at `check`, so a project whose
1653
+ # sessions start unoriented hears about it in-project (ENFORCEMENT par.4: the
1654
+ # hook is wired at init only -- pre-F-036 and agent-bootstrapped projects never
1655
+ # got it, and a wired-but-dead hook is "the worst of the three states").
1656
+ # The file list is a SUPERSET of the JS one: .codex/hooks.json is documented in
1657
+ # ENFORCEMENT par.4 but never written by init. Entry-point names are pinned
1658
+ # equal on both sides.
1659
+ ORIENT_HOOK_ENTRY_POINTS = ("sdlc_check.py", "mkt_check.py")
1660
+ ORIENT_HOOK_CONFIG_FILES = (".claude/settings.json",
1661
+ ".claude/settings.local.json",
1662
+ ".codex/hooks.json")
1663
+ ORIENT_HOOK_CONFIG_CAP = 1024 * 1024 # settings are tiny; cap pathological files
1664
+ _ORIENT_HOOK_CMD_RE = re.compile(r" orient(\s|$)")
1665
+ _ORIENT_HOOK_TOKEN_RE = re.compile(r'"([^"]*)"|(\S+)')
1666
+
1667
+
1668
+ def _orient_hook_commands(root):
1669
+ """Yield the command strings of SessionStart entries invoking an orient
1670
+ entry point, across every known client config under root. Fail-open: an
1671
+ unreadable, oversized, malformed or directory-shaped file contributes
1672
+ nothing. Reads are utf-8-sig -- a BOM'd settings.json (PowerShell/Notepad)
1673
+ must not read as unwired (declared divergence from Node's JSON.parse)."""
1674
+ for rel in ORIENT_HOOK_CONFIG_FILES:
1675
+ p = Path(root) / rel
1676
+ try:
1677
+ if not p.is_file() or p.stat().st_size > ORIENT_HOOK_CONFIG_CAP:
1678
+ continue
1679
+ data = json.loads(p.read_text(encoding="utf-8-sig"))
1680
+ except Exception:
1681
+ continue
1682
+ if not isinstance(data, dict):
1683
+ continue
1684
+ hooks = data.get("hooks")
1685
+ groups = hooks.get("SessionStart") if isinstance(hooks, dict) else None
1686
+ if groups is None:
1687
+ groups = data.get("SessionStart") # the .codex/hooks.json shape
1688
+ if not isinstance(groups, list):
1689
+ continue
1690
+ for group in groups:
1691
+ if not isinstance(group, dict):
1692
+ continue
1693
+ inner = group.get("hooks")
1694
+ for h in inner if isinstance(inner, list) else [group]:
1695
+ cmd = h.get("command") if isinstance(h, dict) else None
1696
+ if (isinstance(cmd, str)
1697
+ and any(n in cmd for n in ORIENT_HOOK_ENTRY_POINTS)
1698
+ and _ORIENT_HOOK_CMD_RE.search(cmd)):
1699
+ yield cmd
1700
+
1701
+
1702
+ def orient_hook_state(root):
1703
+ """'wired' | 'dead' | 'unwired'.
1704
+
1705
+ A matching entry whose tokens name no validator is "cannot tell" and counts
1706
+ as wired (the lib.js contract: never report broken on cannot-tell). 'dead'
1707
+ requires every matching entry to name a validator that does not resolve:
1708
+ the client runs ALL SessionStart hooks, so one live entry beside a dead one
1709
+ still orients the session (any-resolves aggregation). Relative tokens
1710
+ resolve against the project root -- the vendored-validator convention."""
1711
+ matched = False
1712
+ for cmd in _orient_hook_commands(root):
1713
+ matched = True
1714
+ token = None
1715
+ for m in _ORIENT_HOOK_TOKEN_RE.finditer(cmd):
1716
+ tok = m.group(1) if m.group(1) is not None else m.group(2)
1717
+ if any(tok.endswith(n) for n in ORIENT_HOOK_ENTRY_POINTS):
1718
+ token = tok
1719
+ break
1720
+ if token is None:
1721
+ return "wired"
1722
+ tp = Path(token)
1723
+ if not tp.is_absolute():
1724
+ tp = Path(root) / tp
1725
+ if tp.exists():
1726
+ return "wired"
1727
+ return "dead" if matched else "unwired"
1728
+
1729
+
1730
+ def print_orient_hook_note(root):
1731
+ """Print the FS-A wiring note (unwired / dead), or nothing when wired.
1732
+
1733
+ Informational only, by contract: never an exit code, never a validate
1734
+ warning (--strict CI must not fail on a hook that legitimately lives in
1735
+ git-ignored settings.local.json). Called by the spine's cmd_check AND by
1736
+ an overlay that replaces check with its own pipeline (the marketing lens
1737
+ does): the note is check-layer behaviour, whoever owns the check."""
1738
+ try:
1739
+ state = orient_hook_state(root)
1740
+ if state == "unwired":
1741
+ print("[note] no session-orientation hook wired (.claude/settings.json, "
1742
+ ".claude/settings.local.json, .codex/hooks.json): sessions start "
1743
+ "unoriented -- run the installer's init or wire it per ENFORCEMENT.md par.4")
1744
+ elif state == "dead":
1745
+ print("[note] a session-orientation hook is wired but the validator it names "
1746
+ "does not resolve: sessions start unoriented -- re-run init or fix the "
1747
+ "command (ENFORCEMENT.md par.4)")
1748
+ except Exception:
1749
+ pass # detection must never break check
1750
+
1751
+
1649
1752
  def cmd_check(root, strict=False, hybrid=False):
1650
1753
  print("===== validate =====")
1651
1754
  rc_v = cmd_validate(root, strict=strict, hybrid=hybrid)
@@ -1653,6 +1756,7 @@ def cmd_check(root, strict=False, hybrid=False):
1653
1756
  rc_s = cmd_stale(root, hybrid=hybrid)
1654
1757
  print(f"\ncheck: {'CLEAN' if not (rc_v or rc_s) else 'NOT CLEAN'} "
1655
1758
  f"(validate rc={rc_v}, stale rc={rc_s})")
1759
+ print_orient_hook_note(root)
1656
1760
  return 1 if (rc_v or rc_s) else 0
1657
1761
 
1658
1762