@theglitchking/semantic-memory 1.6.1 → 1.7.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +61 -0
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +19 -2
- package/THIRD_PARTY_NOTICES.md +17 -0
- package/commands/jev-mode.md +18 -0
- package/commands/skill-loop.md +23 -0
- package/dist/{chunk-TMMTOFHI.js → chunk-34VN47IW.js} +2 -2
- package/dist/chunk-3FPUBAEW.js +113 -0
- package/dist/chunk-3FPUBAEW.js.map +1 -0
- package/dist/{chunk-ZLIHQZ4C.js → chunk-6TIKWOY4.js} +5 -5
- package/dist/{chunk-KSH2QO6V.js → chunk-7UAVDOI6.js} +5 -5
- package/dist/{chunk-PGVIBUL3.js → chunk-AIXQFTSB.js} +5 -5
- package/dist/chunk-BEACL25M.js +224 -0
- package/dist/chunk-BEACL25M.js.map +1 -0
- package/dist/chunk-CATPGINC.js +186 -0
- package/dist/chunk-CATPGINC.js.map +1 -0
- package/dist/chunk-DBFD3MTT.js +130 -0
- package/dist/chunk-DBFD3MTT.js.map +1 -0
- package/dist/{chunk-HZ7B5KHY.js → chunk-EXAUVIGW.js} +4 -4
- package/dist/chunk-GYR7U6KA.js +80 -0
- package/dist/chunk-GYR7U6KA.js.map +1 -0
- package/dist/chunk-IEZVFKUT.js +157 -0
- package/dist/chunk-IEZVFKUT.js.map +1 -0
- package/dist/chunk-MQS7JEBD.js +137 -0
- package/dist/chunk-MQS7JEBD.js.map +1 -0
- package/dist/chunk-NCGM36NX.js +118 -0
- package/dist/chunk-NCGM36NX.js.map +1 -0
- package/dist/chunk-NIZLBNH4.js +165 -0
- package/dist/chunk-NIZLBNH4.js.map +1 -0
- package/dist/chunk-OT3MV7XF.js +123 -0
- package/dist/chunk-OT3MV7XF.js.map +1 -0
- package/dist/chunk-P3GLY42T.js +291 -0
- package/dist/chunk-P3GLY42T.js.map +1 -0
- package/dist/chunk-PJXYCCI2.js +177 -0
- package/dist/chunk-PJXYCCI2.js.map +1 -0
- package/dist/chunk-R6GNP7DS.js +87 -0
- package/dist/chunk-R6GNP7DS.js.map +1 -0
- package/dist/chunk-TKHWVISV.js +71 -0
- package/dist/chunk-TKHWVISV.js.map +1 -0
- package/dist/{chunk-GYBUBJYM.js → chunk-VBLCUWT4.js} +17 -8
- package/dist/chunk-VBLCUWT4.js.map +1 -0
- package/dist/chunk-VQCNWRTL.js +121 -0
- package/dist/chunk-VQCNWRTL.js.map +1 -0
- package/dist/{chunk-HUG7E25D.js → chunk-Y7H76Q7F.js} +1 -1
- package/dist/chunk-Y7H76Q7F.js.map +1 -0
- package/dist/chunk-ZSWI3Y4Y.js +232 -0
- package/dist/chunk-ZSWI3Y4Y.js.map +1 -0
- package/dist/cli/index.js +381 -52
- package/dist/cli/index.js.map +1 -1
- package/dist/core/features.d.ts +38 -0
- package/dist/core/features.js +28 -0
- package/dist/core/index.d.ts +285 -2
- package/dist/core/index.js +170 -21
- package/dist/core/index.js.map +1 -1
- package/dist/core/jev.d.ts +35 -0
- package/dist/core/jev.js +20 -0
- package/dist/core/skill-curator.d.ts +1 -0
- package/dist/core/skill-curator.js +41 -0
- package/dist/core/skill-evidence.d.ts +25 -0
- package/dist/core/skill-evidence.js +23 -0
- package/dist/core/skill-queue.d.ts +40 -0
- package/dist/core/skill-queue.js +38 -0
- package/dist/core/skill-review.d.ts +3 -0
- package/dist/core/skill-review.js +29 -0
- package/dist/core/skill-worker.d.ts +21 -0
- package/dist/core/skill-worker.js +70 -0
- package/dist/core/skill-worker.js.map +1 -0
- package/dist/{dossier-EN26WDKC.js → dossier-RXBLQOXC.js} +3 -3
- package/dist/failure-eval-IUJO47UC.js +18 -0
- package/dist/failure-eval-IUJO47UC.js.map +1 -0
- package/dist/{healthcheck-53OVXUQQ.js → healthcheck-S7D6YNTA.js} +7 -7
- package/dist/healthcheck-S7D6YNTA.js.map +1 -0
- package/dist/{lexicon-XNA5IFIA.js → lexicon-SEJSLBHO.js} +4 -4
- package/dist/lexicon-SEJSLBHO.js.map +1 -0
- package/dist/{lint-O4RQ47NX.js → lint-3JNLRM5U.js} +6 -6
- package/dist/lint-3JNLRM5U.js.map +1 -0
- package/dist/mcp/server.js +19 -17
- package/dist/mcp/server.js.map +1 -1
- package/dist/{profile-SIWAVD7O.js → profile-SNULTJ27.js} +4 -4
- package/dist/profile-SNULTJ27.js.map +1 -0
- package/dist/{schema-UJNYYRZZ.js → schema-RDYUK6CC.js} +2 -2
- package/dist/schema-RDYUK6CC.js.map +1 -0
- package/dist/skill-curator-D7HHZaUI.d.ts +71 -0
- package/dist/skill-land-XU2APPBJ.js +31 -0
- package/dist/skill-land-XU2APPBJ.js.map +1 -0
- package/dist/skill-ledger-AYXVNFX7.js +18 -0
- package/dist/skill-ledger-AYXVNFX7.js.map +1 -0
- package/dist/skill-merge-GXBQLBZA.js +24 -0
- package/dist/skill-merge-GXBQLBZA.js.map +1 -0
- package/dist/skill-review-BaW5n0Zg.d.ts +179 -0
- package/dist/skill-schema-RVB3EHEF.js +30 -0
- package/dist/skill-schema-RVB3EHEF.js.map +1 -0
- package/hooks/hooks.json +26 -1
- package/hooks/jev-bridge.py +109 -0
- package/hooks/session-start.js +5 -0
- package/hooks/skill-loop.js +65 -0
- package/hooks/vault-context.js +104 -6
- package/package.json +4 -2
- package/dist/chunk-5F5ZIAW2.js +0 -79
- package/dist/chunk-5F5ZIAW2.js.map +0 -1
- package/dist/chunk-GYBUBJYM.js.map +0 -1
- package/dist/chunk-HUG7E25D.js.map +0 -1
- package/dist/failure-eval-37JRNTIW.js +0 -10
- /package/dist/{chunk-TMMTOFHI.js.map → chunk-34VN47IW.js.map} +0 -0
- /package/dist/{chunk-ZLIHQZ4C.js.map → chunk-6TIKWOY4.js.map} +0 -0
- /package/dist/{chunk-KSH2QO6V.js.map → chunk-7UAVDOI6.js.map} +0 -0
- /package/dist/{chunk-PGVIBUL3.js.map → chunk-AIXQFTSB.js.map} +0 -0
- /package/dist/{chunk-HZ7B5KHY.js.map → chunk-EXAUVIGW.js.map} +0 -0
- /package/dist/{dossier-EN26WDKC.js.map → core/features.js.map} +0 -0
- /package/dist/{failure-eval-37JRNTIW.js.map → core/jev.js.map} +0 -0
- /package/dist/{healthcheck-53OVXUQQ.js.map → core/skill-curator.js.map} +0 -0
- /package/dist/{lexicon-XNA5IFIA.js.map → core/skill-evidence.js.map} +0 -0
- /package/dist/{lint-O4RQ47NX.js.map → core/skill-queue.js.map} +0 -0
- /package/dist/{profile-SIWAVD7O.js.map → core/skill-review.js.map} +0 -0
- /package/dist/{schema-UJNYYRZZ.js.map → dossier-RXBLQOXC.js.map} +0 -0
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Single-plugin marketplace for semantic-memory — the unified memory-layer plugin. v1.1.0 absorbs brain's strengths: AGENTS.md contract artifact, hard-gated verification sessions, multi-agent skill bundler (codex/copilot/pi), and SessionStart drift detection. v1.3 adds confidence decay — age-aware retrieval ranking with verify_note to reset the clock. All backwards-compatible with v1.0.x via deprecation shims.",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.7.0"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "semantic-memory",
|
|
14
14
|
"description": "Unified memory-layer plugin for Claude Code. One MCP server, 44 tools across multi-corpus search/read/write/graph/lint/sessions/contract. v1.1.0 adds: AGENTS.md contract artifact, hard-gated verification sessions (session_start/run/finish/status), proposal-mode synthesize_note + synthesize_promote, multi-agent skill bundler (codex/copilot/pi via the new `skills` CLI), and SessionStart drift detection. v1.2 completes the hygiene line: `/healthcheck --fix` auto-remediation, `lint_vault({checks:['code_symbols']})` code-path drift, and a fresh-install CI smoke gate. v1.3 adds confidence decay: age-aware search ranking (0.5^(age/half-life), type-aware, floored, composes with load_priority), a `decay` block surfaced on decayed results, `verify_note` to reset the clock, `evergreen` opt-out, and `decay-config`/`decay-trace` CLI introspection. v1.3.1 adds local-only selection-logging telemetry (which note each search result actually got used), the `decay_candidates` lint, and a `selection-stats` CLI. v1.4.0 is the resident-expert bridge: a learned human→artifact lexicon (`manage_lexicon`) with propose-and-confirm alias capture, verbatim symptom capture + symptom-keyed indexing (terse symptom queries match the note), Tier-1 lexicon query expansion in the hook, archive path-class down-weight, injection hygiene (score-gate + dedup + top-3 + compact instructions), section-targeted `read_note`, and opt-in conditional tool registration. v1.5.0 is the expert-character layer: entity dossiers (`manage_dossier`) — per-component living notes (purpose / failure modes / knobs / incident log / current state) that accrete in place, with two-hop retrieval (utterance→entity→dossier injected first); usage-feedback ranking that boosts repeatedly-cited notes (capped, composes with decay/path-class) plus a `decoys` lint for retrieved-but-never-cited notes (surfaced, never auto-down-ranked); session paging (Stop drafts a durable session digest as a proposal, SessionStart pages in a curated digest instead of a broad sweep); and a speaker profile (`manage_profile`) that models how the human communicates — severity calibration, chronic omissions, verbosity, shorthand — injected at SessionStart. Backwards-compatible with v1.0.x — every previous tool name remains callable through deprecation shims (removed in v2.0.0).",
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.7.0",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TheGlitchKing"
|
|
18
18
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "semantic-memory",
|
|
3
3
|
"description": "Unified memory-layer plugin for Claude Code. One MCP server, multiple typed corpora (vault / code / plans / docs / research / project-map), unified cross-corpus knowledge graph, conditional tool registration, in-process drift detection, and human↔code translation. Rebrand of `semantic-sidekick` at v1.0.0 — see CHANGELOG for migration.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.7.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TheGlitchKing",
|
|
7
7
|
"email": "theglitchking@users.noreply.github.com"
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,67 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to semantic-memory (formerly semantic-sidekick) will be documented here.
|
|
4
4
|
|
|
5
|
+
## [1.7.0] - 2026-10-26 — The skill loop and jev mode
|
|
6
|
+
|
|
7
|
+
Two new features, **both off by default**, each with its own switch. With both off, 1.7.0 behaves like 1.6.1.
|
|
8
|
+
|
|
9
|
+
### The skill loop (`SEMANTIC_MEMORY_SKILL_LOOP=1`, `/skill-loop on|off|status`)
|
|
10
|
+
|
|
11
|
+
semantic-memory now learns *procedures* as well as facts. After a session with something to learn from (a failed
|
|
12
|
+
command, a correction, a long session), a background review drafts a skill, or a patch to a learned skill that was
|
|
13
|
+
used and then failed. Nothing lands until you approve it.
|
|
14
|
+
|
|
15
|
+
- **Quiet.** `SessionEnd` only writes a job and starts a detached worker; the session never waits. One worker per
|
|
16
|
+
repo, at most 10 reviews a day across all repos (`SEMANTIC_MEMORY_SKILL_LOOP_DAILY_RUNS`).
|
|
17
|
+
- **Fenced reviewer.** Each review is one `claude -p` with Read/Grep/Glob only: no Bash, no MCP servers, no hooks,
|
|
18
|
+
no subagents, nothing outside the repo, no session file. It answers JSON; semantic-memory writes the draft after
|
|
19
|
+
redaction and lint. At most 5 skill-loop `claude` processes run at once on the machine
|
|
20
|
+
(`SEMANTIC_MEMORY_SKILL_LOOP_CONCURRENCY`, 1–5).
|
|
21
|
+
- **Evidence.** Hooks record skill use (PostToolUse `Skill`, and `/name`), failed commands and corrections to
|
|
22
|
+
redacted logs under `.claude/.semantic-memory/`.
|
|
23
|
+
- **Scope.** Directory or project by rule (the paths a skill names). Global only by promotion, when the same lesson
|
|
24
|
+
exists in two repos, and only on approval. Private repos (`SEMANTIC_MEMORY_PRIVATE_PATHS`, default
|
|
25
|
+
`~/workspace/kentro`) stay project and are never indexed.
|
|
26
|
+
- **Review.** `/skill-loop review` shows each draft's evidence, scope, Verify command and (for patches) diff:
|
|
27
|
+
approve, reject, or `--eval` it. Every change is in a ledger with exact bytes, so `skill-loop rollback <id>` undoes
|
|
28
|
+
one change.
|
|
29
|
+
- **Curator.** Weekly when the repo is quiet: learned skills unused 14 days go stale, 30 days are archived (never
|
|
30
|
+
deleted; `skill-loop restore <name>`). Pinned and hand-written skills are never touched. Merging is opt-in.
|
|
31
|
+
- **Skill format.** A normal `SKILL.md` plus a `skill_loop:` frontmatter block (scope, status, triggers, applies_to,
|
|
32
|
+
provenance, cites); `semantic-memory skills lint` and `lint_vault({checks: ["skills"]})` check it.
|
|
33
|
+
- **Skill evals.** `semantic-memory skill-eval trigger|task|tune`: does the skill trigger on its own prompts and
|
|
34
|
+
stay quiet on near-misses, does it beat doing the task without it, can its description be better. A TypeScript
|
|
35
|
+
rewrite of the method in Anthropic's skill-creator, with its prompts adapted (Apache-2.0, see
|
|
36
|
+
`THIRD_PARTY_NOTICES.md`).
|
|
37
|
+
|
|
38
|
+
### Jev mode (`SEMANTIC_MEMORY_JEV=1`, `/jev-mode on|off|status`)
|
|
39
|
+
|
|
40
|
+
semantic-memory can use Jev (TypeSafe's yes/no model) for closed-set decisions, always through jev-loop's
|
|
41
|
+
`router.ask` (redaction, cache, ledger, budget): `hooks/jev-bridge.py`. Callers `semantic-memory-hook` (the failure hook, with its own
|
|
42
|
+
ceiling `SEMANTIC_MEMORY_JEV_DAILY_TOKENS`, default 500,000 a day), `semantic-memory-judge` and
|
|
43
|
+
`semantic-memory-skill-loop`. jev-loop counts all three as batch callers (capped by its daily batch cap, cached)
|
|
44
|
+
until it lists `semantic-memory-hook` among its live callers.
|
|
45
|
+
|
|
46
|
+
- **Failure hook veto.** In jev mode a fact the failure hook picked is suppressed when Jev is confident it does not
|
|
47
|
+
fit (<= 0.05), inside the hook's 1.5 s budget; an error injects nothing. Measured on 1,171 labelled pairs: Jev's
|
|
48
|
+
no's were 99.7% right, but it never said yes at >= 0.95, so a yes-gate would inject nothing. Replayed, the veto
|
|
49
|
+
removed only wrong facts (12 wrong, 0 right at score 0.30) and none at the shipped 0.50, where precision stays
|
|
50
|
+
61%: well below the 95% bar, so jev mode does not turn the hook on by itself.
|
|
51
|
+
- **Failure-eval judge.** `scripts/eval-failures/judge-jev.mjs`, `calibrate-jev.mjs`, and
|
|
52
|
+
`eval-failures --gate jev`.
|
|
53
|
+
- **Skill loop.** The scope question (could this be general?) and duplicate checks before a merge.
|
|
54
|
+
|
|
55
|
+
### Also
|
|
56
|
+
|
|
57
|
+
- Every semantic-memory hook exits at once under `SEMANTIC_MEMORY_REVIEWER=1` (set on every skill-loop `claude` run).
|
|
58
|
+
- `healthcheck` reports both switches.
|
|
59
|
+
|
|
60
|
+
### Validation
|
|
61
|
+
|
|
62
|
+
- 664 tests (545 in 1.6.1). With both switches off, every hook's output matches 1.6.1 byte for byte. Golden eval and
|
|
63
|
+
failure eval unchanged. A real end-to-end run (sessions → review → draft → eval → approve → use) found three bugs,
|
|
64
|
+
fixed here. Details: `.documentation/changelog/v1-7-0-skill-loop-and-jev-mode.md`.
|
|
65
|
+
|
|
5
66
|
## [1.6.1] - 2026-10-24 — Release audit fixes (1.5.2 → 1.6.0)
|
|
6
67
|
|
|
7
68
|
A full validation of 1.5.2, 1.5.3 and 1.6.0 through the published packages and real entry points (fresh install, plugin update, upgrade from 1.5.1 state, a stdio MCP server, the read-only docs server, the live watcher, the hooks as real processes, the offline tarball) found seven defects. Each has a regression test.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -11,10 +11,11 @@ The original semantic-sidekick framing — activation / mode-routing / capture l
|
|
|
11
11
|
|
|
12
12
|
## Status
|
|
13
13
|
|
|
14
|
-
**v1.
|
|
14
|
+
**v1.7.0** (2026-10-26), on npm as `@theglitchking/semantic-memory`. Per-version notes are in [CHANGELOG.md](./CHANGELOG.md) and `.documentation/changelog/`. Recent releases:
|
|
15
15
|
|
|
16
16
|
| Version | What it adds |
|
|
17
17
|
|---|---|
|
|
18
|
+
| **1.7.0** | The skill loop (learned skills between sessions) and jev mode, both opt-in |
|
|
18
19
|
| **1.6.1** | Release-audit fixes: invalid frontmatter stays body-only on every reindex, frontmatter `api_key:` values are masked, `--read-only` servers no longer expose `log_event` |
|
|
19
20
|
| **1.6.0** | Opt-in fact lookup on failed commands (`PostToolUseFailure`), `eval-failures` |
|
|
20
21
|
| **1.5.3** | Secret redaction at every write point, `lint_vault({checks: ["secrets"]})` |
|
|
@@ -85,6 +86,21 @@ How the layers fit: [.documentation/architecture/architecture-layers.md](.docume
|
|
|
85
86
|
|
|
86
87
|
Set `SEMANTIC_MEMORY_FAILURE_HOOK=1` (e.g. in `~/.claude/settings.json` → `env`) and a failed Bash/MCP call gets the knowledge-base fact that most likely explains it, when one scores high enough. Measured on real failures it is right about 6 times in 10, so it is off by default. Facts with the literal error in `symptoms:` are found far more reliably (in a hit-em-with-the-docs tree, read the guide first: hewtd uses `symptoms:` for something else). If you already run another `PostToolUseFailure` hook, both can add a note to the same failure — see [the guide](.documentation/operational/failure-lookup-guide.md).
|
|
87
88
|
|
|
89
|
+
## The skill loop (opt-in, v1.7+)
|
|
90
|
+
|
|
91
|
+
`semantic-memory skill-loop on` (or `/skill-loop on`). After a session that failed a command and found the fix, or
|
|
92
|
+
was corrected, a background review drafts a skill — a short playbook Claude Code loads the next time the same
|
|
93
|
+
thing happens. The session never waits; the reviewer is a fenced, read-only `claude -p`; nothing lands until you
|
|
94
|
+
approve it in `/skill-loop review`; unused learned skills go stale at 14 days and are archived at 30. Off (the
|
|
95
|
+
default) changes nothing. See [the guide](.documentation/operational/skill-loop.md).
|
|
96
|
+
|
|
97
|
+
## Jev mode (opt-in, v1.7+)
|
|
98
|
+
|
|
99
|
+
`semantic-memory jev-mode on` (or `/jev-mode on`) lets semantic-memory use Jev, TypeSafe's yes/no model, through
|
|
100
|
+
[jev-loop](https://github.com/the-glitch-kingdom/jev-loop) for closed-set decisions — whether a fact fits a
|
|
101
|
+
failure before the failure hook shows it, whether a learned skill could be general, whether two skills are
|
|
102
|
+
duplicates. Needs jev-loop, `TYPESAFE_API_KEY` and python3. See [the guide](.documentation/operational/jev-mode.md).
|
|
103
|
+
|
|
88
104
|
## Privacy and secrets
|
|
89
105
|
|
|
90
106
|
Everything stays on your machine (local embedding model, no network telemetry). Since v1.5.3 every write masks API keys, tokens, passwords and private keys before they reach disk, and `lint_vault({ checks: ["secrets"] })` finds ones already in notes. Details and limits: [SECURITY.md](SECURITY.md#secret-redaction-v153).
|
|
@@ -99,4 +115,5 @@ node dist/cli/index.js --version
|
|
|
99
115
|
|
|
100
116
|
## License
|
|
101
117
|
|
|
102
|
-
MIT
|
|
118
|
+
MIT. The skill-eval prompts in `src/core/skill-eval/prompts/` are adapted from Anthropic's skill-creator
|
|
119
|
+
(Apache License 2.0): see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) and [LICENSES/Apache-2.0.txt](LICENSES/Apache-2.0.txt).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
semantic-memory is MIT-licensed (see `LICENSE`). The files below are adapted from other projects and keep
|
|
4
|
+
their original license.
|
|
5
|
+
|
|
6
|
+
## Anthropic skill-creator (Apache License 2.0)
|
|
7
|
+
|
|
8
|
+
Source: https://github.com/anthropics/claude-plugins-official (`plugins/skill-creator`). The full license text
|
|
9
|
+
is in `LICENSES/Apache-2.0.txt`. Each file below starts with a header saying it was adapted and modified.
|
|
10
|
+
|
|
11
|
+
| File | Adapted from |
|
|
12
|
+
|------|--------------|
|
|
13
|
+
| `src/core/skill-eval/prompts/improve-description.ts` | `scripts/improve_description.py` (description-improvement and shorten prompts) |
|
|
14
|
+
| `src/core/skill-eval/prompts/grader.ts` | `agents/grader.md` (grader rules) |
|
|
15
|
+
|
|
16
|
+
The `evals/evals.json` reader and writer (`src/core/skill-eval/evals-json.ts`) follows the format in
|
|
17
|
+
skill-creator's `references/schemas.md`; no code or text is copied. The rest of the skill-eval code is new.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Turn jev mode on or off, or show its state (Jev through jev-loop for semantic-memory's yes/no decisions)
|
|
3
|
+
argument-hint: "[on|off|status]"
|
|
4
|
+
allowed-tools: Bash(npx:*), Bash(node:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The user ran `/jev-mode $ARGUMENTS`. Empty arguments mean `status`.
|
|
8
|
+
|
|
9
|
+
Run `node ./node_modules/@theglitchking/semantic-memory/bin/semantic-memory jev-mode <on|off|status>`
|
|
10
|
+
(or `npx --no @theglitchking/semantic-memory jev-mode …` when there is no local install) and relay the
|
|
11
|
+
output verbatim.
|
|
12
|
+
|
|
13
|
+
- `on` / `off` edit only `SEMANTIC_MEMORY_JEV` in `~/.claude/settings.json` `env` (a `.bak` of the previous
|
|
14
|
+
file is kept). The change applies from the next Claude Code session: say so.
|
|
15
|
+
- `status` prints on / off / on-but-unavailable with the reason (jev-loop, `TYPESAFE_API_KEY` or python3
|
|
16
|
+
missing) and today's Jev spend by semantic-memory.
|
|
17
|
+
|
|
18
|
+
Never print or echo `TYPESAFE_API_KEY`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: The skill loop — on/off/status, review drafted skills, run the curator, restore or roll back
|
|
3
|
+
argument-hint: "[on|off|status|review|curate|restore <name>|rollback <ledger-id>|verify]"
|
|
4
|
+
allowed-tools: Bash(npx:*), Bash(node:*), Read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The user ran `/skill-loop $ARGUMENTS`. Empty arguments mean `status`.
|
|
8
|
+
|
|
9
|
+
Every subcommand is the CLI: `node ./node_modules/@theglitchking/semantic-memory/bin/semantic-memory skill-loop <args>`
|
|
10
|
+
(or `npx --no @theglitchking/semantic-memory skill-loop <args>` when there is no local install).
|
|
11
|
+
|
|
12
|
+
- `on` / `off` — edit only `SEMANTIC_MEMORY_SKILL_LOOP` in `~/.claude/settings.json` `env` (a `.bak` is kept);
|
|
13
|
+
applies from the next session — say so. `on --repo` / `off --repo` write the per-repo override in
|
|
14
|
+
`.claude/.semantic-memory/config` instead (it beats the global switch); `--repo --clear` removes it.
|
|
15
|
+
Off never deletes anything: skills already landed stay, as ordinary files.
|
|
16
|
+
- `status` — relay verbatim: on/off and why, drafts waiting, today's review budget, last curator run.
|
|
17
|
+
- `review` — run `skill-loop review --json`. For each draft show its name, scope, the evidence it cites
|
|
18
|
+
(sessions, failures, corrections), eval scores if any, and for a patch the diff. Ask the user, one draft at a
|
|
19
|
+
time: approve, reject, or edit. Then run `skill-loop review --approve <id>` / `--reject <id>`; for edit, change
|
|
20
|
+
the draft file it names, then approve it. Never approve on the user's behalf.
|
|
21
|
+
- `curate` — `skill-loop curate --dry-run` first, show what would go stale or be archived, and run without
|
|
22
|
+
`--dry-run` only when the user agrees. `curate --merge` proposes merges as review drafts.
|
|
23
|
+
- `restore <name>`, `rollback <ledger-id>`, `verify` — run them and relay the output.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lintVault
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VBLCUWT4.js";
|
|
4
4
|
|
|
5
5
|
// src/core/healthcheck.ts
|
|
6
6
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -338,4 +338,4 @@ export {
|
|
|
338
338
|
filterToDrift,
|
|
339
339
|
formatDriftBanner
|
|
340
340
|
};
|
|
341
|
-
//# sourceMappingURL=chunk-
|
|
341
|
+
//# sourceMappingURL=chunk-34VN47IW.js.map
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
featureOn
|
|
3
|
+
} from "./chunk-VQCNWRTL.js";
|
|
4
|
+
import {
|
|
5
|
+
askJev,
|
|
6
|
+
jevVerdict
|
|
7
|
+
} from "./chunk-GYR7U6KA.js";
|
|
8
|
+
import {
|
|
9
|
+
smHome
|
|
10
|
+
} from "./chunk-NIZLBNH4.js";
|
|
11
|
+
|
|
12
|
+
// src/core/skill-scope.ts
|
|
13
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync } from "fs";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { join, sep } from "path";
|
|
16
|
+
function privatePaths(env = process.env) {
|
|
17
|
+
const home = env.HOME || homedir();
|
|
18
|
+
return (env.SEMANTIC_MEMORY_PRIVATE_PATHS ?? "~/workspace/kentro").split(",").map((p) => p.trim().replace(/^~(?=$|\/)/, home)).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
var isPrivateRepo = (repo, env = process.env) => privatePaths(env).some((p) => repo === p || repo.startsWith(p.endsWith(sep) ? p : p + sep));
|
|
21
|
+
function repoReferences(repo, text, paths = []) {
|
|
22
|
+
const tokens = new Set(paths);
|
|
23
|
+
for (const m of text.matchAll(/(?:^|[\s`'"(])((?:\.\/)?[\w.@-]+(?:\/[\w.@-]+)+\/?|[\w-]+\.(?:sh|js|mjs|ts|py|json|toml|ya?ml|md))(?=[\s`'"),:;]|$)/gm)) {
|
|
24
|
+
tokens.add(m[1].replace(/^\.\//, "").replace(/\/$/, ""));
|
|
25
|
+
}
|
|
26
|
+
return [...tokens].filter((p) => p && !p.startsWith("/") && !p.startsWith("~") && !p.includes("..") && existsSync(join(repo, p)));
|
|
27
|
+
}
|
|
28
|
+
function commonSubdir(repo, refs) {
|
|
29
|
+
const dirs = refs.map((r) => {
|
|
30
|
+
let isDir = false;
|
|
31
|
+
try {
|
|
32
|
+
isDir = statSync(join(repo, r)).isDirectory();
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
const parts = r.split("/");
|
|
36
|
+
return (isDir ? parts : parts.slice(0, -1)).slice(0, 2).join("/");
|
|
37
|
+
});
|
|
38
|
+
if (!dirs.length || dirs.some((d) => !d)) return null;
|
|
39
|
+
const first = dirs[0];
|
|
40
|
+
return dirs.every((d) => d === first || d.startsWith(first + "/")) ? first : null;
|
|
41
|
+
}
|
|
42
|
+
var SCOPE_QUESTION = "Would these steps work unchanged in any software repository \u2014 they name no file, script, package, service or setting that belongs to one particular repository?";
|
|
43
|
+
async function decideScope(repo, draft, env = process.env) {
|
|
44
|
+
const refs = repoReferences(repo, `${draft.description}
|
|
45
|
+
${draft.body}`, draft.applies_to?.paths ?? []);
|
|
46
|
+
if (refs.length) {
|
|
47
|
+
const sub = commonSubdir(repo, refs);
|
|
48
|
+
if (sub && (existsSync(join(repo, sub, "package.json")) || existsSync(join(repo, sub, ".claude")) || existsSync(join(repo, sub, "pyproject.toml")))) {
|
|
49
|
+
return { scope: "directory", directory: sub, globalCandidate: false, why: `every path it names is under ${sub}/` };
|
|
50
|
+
}
|
|
51
|
+
return { scope: "project", globalCandidate: false, why: `names this repo's ${refs.slice(0, 3).join(", ")}` };
|
|
52
|
+
}
|
|
53
|
+
if (isPrivateRepo(repo, env)) return { scope: "project", globalCandidate: false, why: "private repo: stays here" };
|
|
54
|
+
if (!featureOn("jev", repo, env)) return { scope: "project", globalCandidate: false, why: "names nothing in the repo; jev mode off \u2192 project" };
|
|
55
|
+
const a = await askJev(
|
|
56
|
+
{
|
|
57
|
+
cwd: repo,
|
|
58
|
+
caller: "semantic-memory-skill-loop",
|
|
59
|
+
state: `Skill ${draft.name}: ${draft.description}
|
|
60
|
+
|
|
61
|
+
${draft.body}`.slice(0, 2e3),
|
|
62
|
+
questions: [{ key: "global", instructions: SCOPE_QUESTION }]
|
|
63
|
+
},
|
|
64
|
+
{ env, timeoutMs: 3e4 }
|
|
65
|
+
);
|
|
66
|
+
const yes = jevVerdict(a.scores.global) === true;
|
|
67
|
+
return {
|
|
68
|
+
scope: "project",
|
|
69
|
+
globalCandidate: yes,
|
|
70
|
+
why: yes ? `Jev: holds in any repo (p=${a.scores.global?.toFixed(2)}); promotion needs a second repo` : `names nothing in the repo; Jev not sure it is general (${a.error ?? `p=${a.scores.global?.toFixed(2)}`}) \u2192 project`
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
var skillIndexPath = (env = process.env) => join(smHome(env), "skill-index.jsonl");
|
|
74
|
+
function indexSkill(e, env = process.env) {
|
|
75
|
+
if (isPrivateRepo(e.repo, env)) return;
|
|
76
|
+
mkdirSync(smHome(env), { recursive: true });
|
|
77
|
+
appendFileSync(skillIndexPath(env), JSON.stringify({ ...e, ts: (/* @__PURE__ */ new Date()).toISOString() }) + "\n");
|
|
78
|
+
}
|
|
79
|
+
function readIndex(env = process.env) {
|
|
80
|
+
const latest = /* @__PURE__ */ new Map();
|
|
81
|
+
let raw = "";
|
|
82
|
+
try {
|
|
83
|
+
raw = readFileSync(skillIndexPath(env), "utf8");
|
|
84
|
+
} catch {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
for (const line of raw.split("\n")) {
|
|
88
|
+
try {
|
|
89
|
+
const e = JSON.parse(line);
|
|
90
|
+
latest.set(`${e.repo}\0${e.name}\0${e.path}`, e);
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return [...latest.values()];
|
|
95
|
+
}
|
|
96
|
+
var nameTokens = (n) => new Set(n.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1));
|
|
97
|
+
function sameLesson(a, b) {
|
|
98
|
+
if (a === b) return true;
|
|
99
|
+
const x = nameTokens(a), y = nameTokens(b);
|
|
100
|
+
const shared = [...x].filter((t) => y.has(t)).length;
|
|
101
|
+
return shared / Math.max(x.size, y.size, 1) >= 0.75;
|
|
102
|
+
}
|
|
103
|
+
function promotionPeers(repo, name, env = process.env) {
|
|
104
|
+
if (isPrivateRepo(repo, env)) return [];
|
|
105
|
+
return readIndex(env).filter((e) => e.repo !== repo && e.status !== "archived" && sameLesson(e.name, name) && !isPrivateRepo(e.repo, env));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
decideScope,
|
|
110
|
+
indexSkill,
|
|
111
|
+
promotionPeers
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=chunk-3FPUBAEW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/skill-scope.ts"],"sourcesContent":["/**\n * Where a learned skill lives (v1.7): directory, project, or — only by promotion — global.\n *\n * 1. Deterministic first: a draft that names this repo's paths is project-scoped; one whose paths all sit\n * under a single subdirectory is directory-scoped there.\n * 2. Unresolved (names nothing in the repo): in jev mode one batch Jev question, \"is this true in any\n * repository?\"; a yes at >= 0.95 only marks the draft a global *candidate*. Otherwise: project.\n * 3. Global only by promotion: when the cross-repo index shows the same skill name drafted or landed in\n * >= 2 repos, review offers one global skill that replaces the project copies. A person approves it.\n *\n * Private repos (SEMANTIC_MEMORY_PRIVATE_PATHS, default ~/workspace/kentro: a Claude-only boundary) are\n * always project, are never written to the cross-repo index, and never reach Jev.\n */\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, sep } from \"node:path\";\nimport { featureOn } from \"./features.js\";\nimport { askJev, jevVerdict } from \"./jev.js\";\nimport { smHome } from \"./skill-queue.js\";\nimport type { SkillScope } from \"./skill-schema.js\";\n\nexport interface ScopeDecision {\n scope: Exclude<SkillScope, \"global\">;\n /** directory scope: the subdirectory (repo-relative) */\n directory?: string;\n /** Jev said this holds in any repo: offer promotion once another repo has it too */\n globalCandidate: boolean;\n why: string;\n}\n\nexport function privatePaths(env = process.env): string[] {\n const home = env.HOME || homedir();\n return (env.SEMANTIC_MEMORY_PRIVATE_PATHS ?? \"~/workspace/kentro\")\n .split(\",\")\n .map((p) => p.trim().replace(/^~(?=$|\\/)/, home))\n .filter(Boolean);\n}\n\nexport const isPrivateRepo = (repo: string, env = process.env) =>\n privatePaths(env).some((p) => repo === p || repo.startsWith(p.endsWith(sep) ? p : p + sep));\n\n/** Repo-relative paths a draft mentions that exist in the repo (from applies_to.paths and path-like tokens). */\nexport function repoReferences(repo: string, text: string, paths: string[] = []): string[] {\n const tokens = new Set(paths);\n for (const m of text.matchAll(/(?:^|[\\s`'\"(])((?:\\.\\/)?[\\w.@-]+(?:\\/[\\w.@-]+)+\\/?|[\\w-]+\\.(?:sh|js|mjs|ts|py|json|toml|ya?ml|md))(?=[\\s`'\"),:;]|$)/gm)) {\n tokens.add(m[1].replace(/^\\.\\//, \"\").replace(/\\/$/, \"\"));\n }\n return [...tokens].filter((p) => p && !p.startsWith(\"/\") && !p.startsWith(\"~\") && !p.includes(\"..\") && existsSync(join(repo, p)));\n}\n\n/** The one subdirectory every referenced path sits under (two levels at most), or null. */\nfunction commonSubdir(repo: string, refs: string[]): string | null {\n const dirs = refs.map((r) => {\n let isDir = false;\n try {\n isDir = statSync(join(repo, r)).isDirectory();\n } catch {}\n const parts = r.split(\"/\");\n return (isDir ? parts : parts.slice(0, -1)).slice(0, 2).join(\"/\");\n });\n if (!dirs.length || dirs.some((d) => !d)) return null;\n const first = dirs[0];\n return dirs.every((d) => d === first || d.startsWith(first + \"/\")) ? first : null;\n}\n\nexport const SCOPE_QUESTION =\n \"Would these steps work unchanged in any software repository — they name no file, script, package, service or \" +\n \"setting that belongs to one particular repository?\";\n\nexport async function decideScope(\n repo: string,\n draft: { name: string; description: string; body: string; applies_to?: { paths?: string[] } },\n env = process.env,\n): Promise<ScopeDecision> {\n const refs = repoReferences(repo, `${draft.description}\\n${draft.body}`, draft.applies_to?.paths ?? []);\n if (refs.length) {\n const sub = commonSubdir(repo, refs);\n // a subdir that holds its own package/skills is a real unit; a bare src/ or scripts/ is still the whole project\n if (sub && (existsSync(join(repo, sub, \"package.json\")) || existsSync(join(repo, sub, \".claude\")) || existsSync(join(repo, sub, \"pyproject.toml\")))) {\n return { scope: \"directory\", directory: sub, globalCandidate: false, why: `every path it names is under ${sub}/` };\n }\n return { scope: \"project\", globalCandidate: false, why: `names this repo's ${refs.slice(0, 3).join(\", \")}` };\n }\n if (isPrivateRepo(repo, env)) return { scope: \"project\", globalCandidate: false, why: \"private repo: stays here\" };\n if (!featureOn(\"jev\", repo, env)) return { scope: \"project\", globalCandidate: false, why: \"names nothing in the repo; jev mode off → project\" };\n const a = await askJev(\n {\n cwd: repo,\n caller: \"semantic-memory-skill-loop\",\n state: `Skill ${draft.name}: ${draft.description}\\n\\n${draft.body}`.slice(0, 2000),\n questions: [{ key: \"global\", instructions: SCOPE_QUESTION }],\n },\n { env, timeoutMs: 30_000 },\n );\n const yes = jevVerdict(a.scores.global) === true;\n return {\n scope: \"project\",\n globalCandidate: yes,\n why: yes ? `Jev: holds in any repo (p=${a.scores.global?.toFixed(2)}); promotion needs a second repo` : `names nothing in the repo; Jev not sure it is general (${a.error ?? `p=${a.scores.global?.toFixed(2)}`}) → project`,\n };\n}\n\n// --- the cross-repo index: paths and names only ---\n\nexport interface IndexEntry {\n repo: string;\n name: string;\n path: string;\n status: \"draft\" | \"active\" | \"archived\";\n global_candidate?: boolean;\n ts: string;\n}\n\nexport const skillIndexPath = (env = process.env) => join(smHome(env), \"skill-index.jsonl\");\n\nexport function indexSkill(e: Omit<IndexEntry, \"ts\">, env = process.env): void {\n if (isPrivateRepo(e.repo, env)) return;\n mkdirSync(smHome(env), { recursive: true });\n appendFileSync(skillIndexPath(env), JSON.stringify({ ...e, ts: new Date().toISOString() }) + \"\\n\");\n}\n\n/** Latest entry per (repo, name, path). */\nexport function readIndex(env = process.env): IndexEntry[] {\n const latest = new Map<string, IndexEntry>();\n let raw = \"\";\n try {\n raw = readFileSync(skillIndexPath(env), \"utf8\");\n } catch {\n return [];\n }\n for (const line of raw.split(\"\\n\")) {\n try {\n const e = JSON.parse(line) as IndexEntry;\n latest.set(`${e.repo}\\0${e.name}\\0${e.path}`, e);\n } catch {}\n }\n return [...latest.values()];\n}\n\nconst nameTokens = (n: string) => new Set(n.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1));\n\n/** Same lesson: the same name, or names sharing >= 3/4 of their words. */\nexport function sameLesson(a: string, b: string): boolean {\n if (a === b) return true;\n const x = nameTokens(a), y = nameTokens(b);\n const shared = [...x].filter((t) => y.has(t)).length;\n return shared / Math.max(x.size, y.size, 1) >= 0.75;\n}\n\n/** Other repos holding the same lesson (not archived). Promotion is offered when this returns >= 1 repo. */\nexport function promotionPeers(repo: string, name: string, env = process.env): IndexEntry[] {\n if (isPrivateRepo(repo, env)) return [];\n return readIndex(env).filter((e) => e.repo !== repo && e.status !== \"archived\" && sameLesson(e.name, name) && !isPrivateRepo(e.repo, env));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
appendToSection,
|
|
3
3
|
replaceSectionBody
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
redact
|
|
7
|
-
} from "./chunk-H2BES6FU.js";
|
|
4
|
+
} from "./chunk-EXAUVIGW.js";
|
|
8
5
|
import {
|
|
9
6
|
matter_default
|
|
10
7
|
} from "./chunk-J7LYDRML.js";
|
|
8
|
+
import {
|
|
9
|
+
redact
|
|
10
|
+
} from "./chunk-H2BES6FU.js";
|
|
11
11
|
|
|
12
12
|
// src/core/profile.ts
|
|
13
13
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -108,4 +108,4 @@ export {
|
|
|
108
108
|
initSpeakerProfile,
|
|
109
109
|
updateProfileSection
|
|
110
110
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-6TIKWOY4.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listDossiers
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EXAUVIGW.js";
|
|
4
4
|
import {
|
|
5
5
|
deriveSessionDir
|
|
6
6
|
} from "./chunk-EYVFC745.js";
|
|
7
|
-
import {
|
|
8
|
-
redactDeep
|
|
9
|
-
} from "./chunk-H2BES6FU.js";
|
|
10
7
|
import {
|
|
11
8
|
matter_default
|
|
12
9
|
} from "./chunk-J7LYDRML.js";
|
|
10
|
+
import {
|
|
11
|
+
redactDeep
|
|
12
|
+
} from "./chunk-H2BES6FU.js";
|
|
13
13
|
|
|
14
14
|
// src/core/lexicon.ts
|
|
15
15
|
import { readFile, writeFile, mkdir, unlink } from "fs/promises";
|
|
@@ -180,4 +180,4 @@ export {
|
|
|
180
180
|
findAliasConflicts,
|
|
181
181
|
removeAlias
|
|
182
182
|
};
|
|
183
|
-
//# sourceMappingURL=chunk-
|
|
183
|
+
//# sourceMappingURL=chunk-7UAVDOI6.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadSchema,
|
|
3
3
|
validateNote
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y7H76Q7F.js";
|
|
5
|
+
import {
|
|
6
|
+
matter_default
|
|
7
|
+
} from "./chunk-J7LYDRML.js";
|
|
5
8
|
import {
|
|
6
9
|
redact,
|
|
7
10
|
redactDeep
|
|
8
11
|
} from "./chunk-H2BES6FU.js";
|
|
9
|
-
import {
|
|
10
|
-
matter_default
|
|
11
|
-
} from "./chunk-J7LYDRML.js";
|
|
12
12
|
|
|
13
13
|
// src/core/crud.ts
|
|
14
14
|
import { readFile, writeFile, unlink, rename, mkdir } from "fs/promises";
|
|
@@ -943,4 +943,4 @@ export {
|
|
|
943
943
|
regenerateAgentsContract,
|
|
944
944
|
inspectAgentsContract
|
|
945
945
|
};
|
|
946
|
-
//# sourceMappingURL=chunk-
|
|
946
|
+
//# sourceMappingURL=chunk-AIXQFTSB.js.map
|