-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathtechpack.yaml
More file actions
352 lines (332 loc) · 15.3 KB
/
Copy pathtechpack.yaml
File metadata and controls
352 lines (332 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
schemaVersion: 1
identifier: memory
displayName: "The Memory Loop"
description: Persistent project memory that compounds across Claude Code sessions
author: Bruno Guidolim
minMCSVersion: "2026.4.12"
# ---------------------------------------------------------------------------
# Prompts — asked once per project during `mcs sync`, then remembered
# ---------------------------------------------------------------------------
prompts:
- key: KB_GATE_MODE
type: select
label: "KB gate — how strictly must Claude check the KB before delegating?"
default: "enforce"
options:
- value: "warn"
label: "Warn — remind and log when a sub-agent is spawned without KB findings"
- value: "enforce"
label: "Enforce — block the spawn until the KB has been searched this turn"
- value: "observe"
label: "Observe — never gate the main thread, just log (sub-agents are still briefed)"
- value: "off"
label: "Off — no gating and no sub-agent briefing; prompt reminder only"
# ---------------------------------------------------------------------------
# Components
# ---------------------------------------------------------------------------
components:
# ── Dependencies ────────────────────────────────────────────────────────
- id: node
displayName: Node.js
description: JavaScript runtime (for npx-based MCP servers)
brew: node
- id: gh
displayName: GitHub CLI
description: GitHub CLI for PR operations
brew: gh
- id: jq
displayName: jq
description: Lightweight JSON processor
brew: jq
# ── Retrieval engine ────────────────────────────────────────────────────
# Pinned so no package manager runs inside a hook, and because the rerank and
# generate neutralisation relies on undocumented fallback behaviour a version
# bump could change. See CLAUDE.md.
- id: qmd-install
displayName: qmd
description: Local search engine over markdown — no daemon, bundled SQLite + sqlite-vec
dependencies: [node]
type: configuration
shell: "npm install -g @tobilu/qmd@2.8.3"
doctorChecks:
# ONE check, deliberately: `isAlreadyInstalled` takes the first *passing*
# check as proof the component is installed, so any second check that can
# pass while the pin is unsatisfied silently disables the install.
# Asserts the version and that the binary loads (`--version` alone never
# loads the native modules); a qmd that runs proves Node is new enough.
# Anchored, so 2.8.30 and 12.8.3 do not pass. Keep the literal in step
# with `shell:` above.
#
# The load probe runs against a throwaway index. A bare `qmd status` adopts
# whatever config it finds by walking up from the working directory, so in a
# project where the user keeps their own .qmd/ it writes model keys into
# their config and creates their index.sqlite — the pack must not touch it.
# `--index` is what skips that discovery; QMD_CONFIG_DIR alone does not,
# because a discovered .qmd/ outranks it.
- type: commandExists
name: "qmd 2.8.3 installed and loading"
section: MCP Servers
command: /bin/sh
args:
- "-c"
- |
PATH="$PATH:/opt/homebrew/bin:/usr/local/bin"
qmd --version 2>/dev/null | grep -qE '^qmd 2\.8\.3( |$)' || exit 1
d=$(mktemp -d) || exit 1
QMD_CONFIG_DIR="$d" INDEX_PATH="$d/probe.sqlite" \
qmd --index probe status >/dev/null 2>&1
s=$?; rm -rf "$d"; exit $s
fixCommand: "npm install -g @tobilu/qmd@2.8.3"
# One model serves all three of qmd's slots, so this downloads a single file.
# It runs at sync time on purpose: a first-run download inside the async
# indexing hook would blow its timeout and leave the index silently empty.
- id: qmd-model
displayName: Embedding model
description: Downloads Qwen3-Embedding-0.6B once (~610 MB, shared across all projects)
dependencies: [qmd-install]
type: configuration
shell: |
d=$(mktemp -d)
m="hf:Qwen/Qwen3-Embedding-0.6B-GGUF/Qwen3-Embedding-0.6B-Q8_0.gguf"
QMD_CONFIG_DIR="$d" INDEX_PATH="$d/pull.sqlite" \
QMD_EMBED_MODEL="$m" QMD_GENERATE_MODEL="$m" QMD_RERANK_MODEL="$m" \
qmd --index pull pull --progress
s=$?; rm -rf "$d"; exit $s
doctorChecks:
- type: commandExists
name: "Qwen3-Embedding-0.6B downloaded"
section: AI Models
command: /bin/sh
args: ["-c", 'test -f "${XDG_CACHE_HOME:-$HOME/.cache}/qmd/models/hf_Qwen_Qwen3-Embedding-0.6B-Q8_0.gguf"']
# ── MCP Server ──────────────────────────────────────────────────────────
# One static command line serves every project, so the index path is computed
# at launch — Claude spawns MCP servers with the project as their working
# directory. Using `--index` rather than a project-local .qmd/ is an invariant;
# see CLAUDE.md.
#
# `mkdir -p` because qmd cannot open an index under a directory that does not
# exist, and the server would fail to connect in every project that has not
# captured a memory yet — which is every new project. The CLAUDE.md section is
# installed globally and tells Claude to search before each task, so the tool
# has to exist everywhere that instruction does. An empty index is honest: the
# server reports "0 markdown documents" in its own instructions.
#
# keep in sync with hooks/sync-memories.sh — it writes the index this opens.
- id: memory-loop
description: Semantic search over this project's memories, backed by local embeddings
isRequired: true
dependencies: [node, qmd-install, qmd-model]
mcp:
command: /bin/sh
args:
- "-c"
- |
r=$(git rev-parse --show-toplevel 2>/dev/null)
[ -n "$r" ] || r="${CLAUDE_PROJECT_DIR:-$PWD}"
d="$r/.claude/.kb-index"
PATH="$PATH:/opt/homebrew/bin:/usr/local/bin"
mkdir -p "$d"
export QMD_CONFIG_DIR="$d" INDEX_PATH="$d/memory-loop.sqlite"
exec qmd --index memory-loop mcp
# All three pass quietly when nothing is indexed yet, and each repeats the
# launcher's project-root ladder verbatim — every check is its own process, so
# the duplication is forced. Keep the copies identical. The two that invoke qmd
# also harden PATH, because `mcs doctor` runs them through Process, which has
# no login shell.
doctorChecks:
# Tier 2, and the only thing that would catch a silent model downgrade:
# a weaker embedding model produces no error, just worse results.
- type: commandExists
name: "Memory index uses the expected embedding model"
section: AI Models
command: /bin/sh
args:
- "-c"
- |
r=$(git rev-parse --show-toplevel 2>/dev/null)
[ -n "$r" ] || r="${CLAUDE_PROJECT_DIR:-$PWD}"
d="$r/.claude/.kb-index"
PATH="$PATH:/opt/homebrew/bin:/usr/local/bin"
command -v qmd >/dev/null 2>&1 || exit 0
[ -f "$d/memory-loop.yml" ] || exit 0
export QMD_CONFIG_DIR="$d" INDEX_PATH="$d/memory-loop.sqlite"
qmd --index memory-loop status 2>/dev/null | grep -q Qwen3-Embedding-0.6B
# The indexing hook removes its log on success, so the file existing at all
# means the last run failed or left documents unembedded — and the reason is
# inside it. Without this, a broken index is only visible as searches quietly
# returning nothing.
- type: commandExists
name: "Memory indexing completed"
section: AI Models
command: /bin/sh
args:
- "-c"
- |
r=$(git rev-parse --show-toplevel 2>/dev/null)
[ -n "$r" ] || r="${CLAUDE_PROJECT_DIR:-$PWD}"
[ ! -f "$r/.claude/.kb-index/memory-loop.log" ]
# Queries with *default* arguments — reranking on — because that is the path
# this pack neutralises. If an upgrade made that fatal rather than a warning,
# this check is where it surfaces. The alarm is the timeout `commandExists`
# does not provide.
- type: commandExists
name: "Memory search returns results"
section: MCP Servers
command: /bin/sh
args:
- "-c"
- |
r=$(git rev-parse --show-toplevel 2>/dev/null)
[ -n "$r" ] || r="${CLAUDE_PROJECT_DIR:-$PWD}"
d="$r/.claude/.kb-index"
PATH="$PATH:/opt/homebrew/bin:/usr/local/bin"
command -v qmd >/dev/null 2>&1 || exit 0
[ -f "$d/memory-loop.sqlite" ] || exit 0
export QMD_CONFIG_DIR="$d" INDEX_PATH="$d/memory-loop.sqlite"
v=$(qmd --index memory-loop status 2>/dev/null |
sed -n 's/.*Vectors: *\([0-9][0-9]*\).*/\1/p' | head -1)
[ "${v:-0}" -gt 0 ] || exit 0
perl -e 'alarm 30; exec @ARGV' \
qmd --index memory-loop query "vec: past decisions and learnings" \
-n 1 --format json 2>/dev/null | grep -q docid
# ── Skills ──────────────────────────────────────────────────────────────
- id: skill-continuous-learning
displayName: continuous-learning skill
description: Extracts learnings and decisions from sessions into memory
isRequired: true
skill:
source: skills/continuous-learning
destination: continuous-learning
- id: skill-memory-audit
displayName: memory-audit skill
description: Reviews and audits memories to keep the knowledge base lean and valuable
skill:
source: skills/memory-audit
destination: memory-audit
# ── Hooks ───────────────────────────────────────────────────────────────
- id: hook-continuous-learning
displayName: Continuous learning activator
description: Reminds to evaluate learnings on each prompt
isRequired: true
hookEvent: UserPromptSubmit
hook:
source: hooks/continuous-learning-activator.sh
destination: continuous-learning-activator.sh
- id: hook-sync-memories
displayName: Sync memories hook
description: Indexes memories into the project's search index on session start
dependencies: [qmd-install, jq]
hookEvent: SessionStart
hookAsync: true
hookTimeout: 120
hookStatusMessage: "Indexing memories..."
hook:
source: hooks/sync-memories.sh
destination: sync-memories.sh
- id: hook-reindex-memories
displayName: Reindex memories hook
description: Reindexes memories when they have changed mid-session
dependencies: [qmd-install, jq]
hookEvent: UserPromptSubmit
hookAsync: true
hookTimeout: 120
hookStatusMessage: "Reindexing memories..."
hook:
source: hooks/sync-memories.sh
destination: sync-memories.sh
# ── KB-first delegation ─────────────────────────────────────────────────
# Four registrations of one dispatcher script, which branches on
# `hook_event_name`. Together they keep KB lookups ahead of delegated discovery,
# so sub-agents don't rediscover what a memory already recorded. Matchers stay
# broad on purpose — which agent types count as "discovery" is decided in one
# place, by `GATED_AGENTS` in the script.
- id: hook-kb-gate-turn
displayName: KB gate — turn boundary
description: Marks each new prompt so the KB check is required again per turn
dependencies: [jq]
hookEvent: UserPromptSubmit
hook:
source: hooks/kb-gate.sh
destination: kb-gate.sh
# Checked because losing this one corrupts the gate rather than disabling it:
# with no turn stamp the counter reads 0 forever, so every search ever made in
# the session counts as fresh and the barrier silently widens from per-turn to
# per-session.
doctorChecks:
- type: hookEventExists
name: "UserPromptSubmit hook registered"
section: Hooks
event: UserPromptSubmit
- id: hook-kb-gate-record
displayName: KB gate — record searches
description: Records each KB search so delegated discovery can be checked against it
dependencies: [jq]
hookEvent: PostToolUse
hookMatcher: "mcp__memory-loop__query"
hook:
source: hooks/kb-gate.sh
destination: kb-gate.sh
# Same reasoning: without this, no search is ever recorded and the gate is
# pure friction until the budget releases it.
doctorChecks:
- type: hookEventExists
name: "PostToolUse hook registered"
section: Hooks
event: PostToolUse
- id: hook-kb-gate-check
displayName: KB gate — check sub-agent spawns
description: Warns or blocks when a discovery sub-agent is spawned without KB findings in its prompt
dependencies: [jq]
hookEvent: PreToolUse
# The spawn tool is `Agent` in current Claude Code and `Task` in older
# builds. Matching only `Task` silently matches nothing.
hookMatcher: "Agent|Task"
hook:
source: hooks/kb-gate.sh
destination: kb-gate.sh
doctorChecks:
- type: hookEventExists
name: "PreToolUse hook registered"
section: Hooks
event: PreToolUse
- id: hook-kb-subagent-brief
displayName: Sub-agent KB brief
description: Tells discovery sub-agents that the KB exists, so they search it instead of sweeping files
dependencies: [jq]
hookEvent: SubagentStart
hook:
source: hooks/kb-gate.sh
destination: kb-gate.sh
doctorChecks:
- type: hookEventExists
name: "SubagentStart hook registered"
section: Hooks
event: SubagentStart
# ── Configuration ───────────────────────────────────────────────────────
- id: settings
displayName: Settings
description: Disables built-in auto-memory in favor of continuous learning system
settingsFile: config/settings.json
- id: gitignore
displayName: Global gitignore
description: Ignores memory files from version control
gitignore:
- ".claude/.memories-last-indexed"
- ".claude/.kb-index/"
- ".claude/.kb-gate/"
- ".claude/.kb-gate.log"
# ---------------------------------------------------------------------------
# Ignore — suppress update notifications for changes to maintainer-only files.
# Does not affect what installs; that is decided by the components above.
# ---------------------------------------------------------------------------
ignore:
- CLAUDE.md
- SYNC-BLOCKS.md
- assets/
- tests/
# ---------------------------------------------------------------------------
# Templates — CLAUDE.local.md sections
# ---------------------------------------------------------------------------
templates:
- sectionIdentifier: continuous-learning
contentFile: templates/continuous-learning.md