claude-alfred 0.2.0 → 0.3.1
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/README.ja.md +36 -20
- package/README.md +36 -20
- package/content/hooks/hooks.json +25 -0
- package/dist/{audit-DM4QaS4L.mjs → audit-BrTztz4z.mjs} +5 -7
- package/dist/cli.mjs +10 -12
- package/dist/directives-iFMV2Mmx.mjs +57 -0
- package/dist/{dispatcher-DNBIBPJS.mjs → dispatcher-xZAhoK7h.mjs} +25 -9
- package/dist/{embedder-qPO74Ar-.mjs → embedder-BRG2Xpr4.mjs} +4 -5
- package/dist/{fts-WNIfvJ7g.mjs → fts-CDEAxhSV.mjs} +3 -3
- package/dist/{helpers-BR57P8BX.mjs → helpers-DVDJYnXA.mjs} +7 -6
- package/dist/{knowledge-DACIiaOh.mjs → knowledge-DItYbraO.mjs} +4 -5
- package/dist/{post-tool-DCMLZ8BU.mjs → post-tool-G2PAotAa.mjs} +28 -33
- package/dist/postinstall.mjs +2 -2
- package/dist/{pre-compact-BtHyMSFR.mjs → pre-compact-CyNPLjSK.mjs} +6 -6
- package/dist/pre-tool-B_CYjJPV.mjs +29 -0
- package/dist/{schema-CcIFwr_0.mjs → schema-BoqQZmCK.mjs} +1 -1
- package/dist/{server-HMIKVikr.mjs → server-23A-5yhs.mjs} +505 -286
- package/dist/{server-BVBSt-GA.mjs → server-Dfefx0Gm.mjs} +2404 -2295
- package/dist/session-start-D177nOGO.mjs +224 -0
- package/dist/spec-guard-YaW8olGC.mjs +100 -0
- package/dist/state-CdHO66SV.mjs +53 -0
- package/dist/stop-uQEMYJUS.mjs +21 -0
- package/dist/{store-wuybYYaO.mjs → store-CuN3_dWu.mjs} +1 -1
- package/dist/{user-prompt-BZ9VhKi6.mjs → user-prompt-X18njRds.mjs} +55 -42
- package/dist/{vectors-DByIf3R2.mjs → vectors-C_Yj-4sR.mjs} +2 -2
- package/package.json +4 -1
- package/web/dist/assets/activity-BEGGrVcS.js +1 -0
- package/web/dist/assets/api-BpnabbKu.js +1 -0
- package/web/dist/assets/badge-CBVw5E1L.js +1 -0
- package/web/dist/assets/button-DnlE4Vv_.js +14 -0
- package/web/dist/assets/checkbox-B_KjCpy7.js +1 -0
- package/web/dist/assets/circle-D8Rr9amD.js +1 -0
- package/web/dist/assets/{circle-dot-BPxCviEE.js → circle-dot-BMf8wYDE.js} +1 -1
- package/web/dist/assets/createLucideIcon-MCX4k61-.js +1 -0
- package/web/dist/assets/dist--KR51Ecg.js +1 -0
- package/web/dist/assets/index-BeDDkww-.css +1 -0
- package/web/dist/assets/index-DRPc84bA.js +10 -0
- package/web/dist/assets/knowledge-d1qgwDQ0.js +59 -0
- package/web/dist/assets/{link-DqICqa6t.js → link-D7DRVryP.js} +1 -1
- package/web/dist/assets/matchContext-Dst1Emk1.js +1 -0
- package/web/dist/assets/progress-CokWPQNM.js +6 -0
- package/web/dist/assets/routes-DuxHjsHU.js +1 -0
- package/web/dist/assets/separator-DgaTFrP0.js +1 -0
- package/web/dist/assets/skeleton-BjX-jJYO.js +1 -0
- package/web/dist/assets/tasks-BVHxHTf5.js +1 -0
- package/web/dist/assets/tasks._slug-GHRGy5XE.js +35 -0
- package/web/dist/index.html +6 -6
- package/dist/directives-D2A8mdOs.mjs +0 -37
- package/dist/session-start-DCp2_K0Z.mjs +0 -242
- package/web/dist/assets/activity-BQIta63V.js +0 -1
- package/web/dist/assets/api-Cq0fYMlI.js +0 -1
- package/web/dist/assets/card-BcSoTIRX.js +0 -1
- package/web/dist/assets/circle-BL_hGKys.js +0 -1
- package/web/dist/assets/createLucideIcon-BsT1VhJi.js +0 -1
- package/web/dist/assets/dist-BoQ-HzPG.js +0 -1
- package/web/dist/assets/index-CT2wUCM0.js +0 -10
- package/web/dist/assets/index-D3MHfJUi.css +0 -1
- package/web/dist/assets/knowledge-BVglB57u.js +0 -10
- package/web/dist/assets/lib-C3W60Lg0.js +0 -58
- package/web/dist/assets/matchContext-Djmy2MvT.js +0 -1
- package/web/dist/assets/progress-Dq-nvFJj.js +0 -6
- package/web/dist/assets/routes-FGDdpj0K.js +0 -1
- package/web/dist/assets/separator-CMQXCJRe.js +0 -1
- package/web/dist/assets/skeleton-CG9s_VrV.js +0 -1
- package/web/dist/assets/tasks-k0AEdG6F.js +0 -1
- package/web/dist/assets/tasks._slug-CDoTqYi5.js +0 -42
- package/web/dist/assets/utils-muoz4bGA.js +0 -1
package/README.ja.md
CHANGED
|
@@ -24,7 +24,7 @@ alfred は3つとも潰す。
|
|
|
24
24
|
|
|
25
25
|
**適応する仕様。** 小さなバグ修正なら3ファイル。中規模なら5。大規模なら7。タスクに応じてスペックの深さを自動調整。bugfix専用テンプレート(再現手順、原因分析、修正戦略)も。
|
|
26
26
|
|
|
27
|
-
**積み重なる記憶。** 「あの時こう決めた」「このバグ前にも見た」「Xは試したけどダメだった」— 全てが `.alfred/knowledge/`
|
|
27
|
+
**積み重なる記憶。** 「あの時こう決めた」「このバグ前にも見た」「Xは試したけどダメだった」— 全てが `.alfred/knowledge/` に構造化JSONとして保存される。3分類のみ: **decision**(一回きりの選択 + 理由 + 却下した代替案)、**pattern**(繰り返し実践 + 適用条件 + 期待結果)、**rule**(強制ルール + 優先度 + 根拠)。Git フレンドリーで、チームで共有できる。SQLite検索インデックスがセマンティック検索を提供。矛盾は自動検出。次に似た問題に当たったとき、聞く前に alfred が出してくる。
|
|
28
28
|
|
|
29
29
|
**信頼性シグナル。** 仕様の全項目にgrounding level(`verified`/`reviewed`/`inferred`/`speculative`)が付く。どの要件が実証済みで、どれが推測なのか一目でわかる。typoも検出する。
|
|
30
30
|
|
|
@@ -36,7 +36,7 @@ alfred は3つとも潰す。
|
|
|
36
36
|
|
|
37
37
|
**能動的スキル提案。** alfred は呼ばれるのを待たない。調査中か、設計中か、実装中か、バグ修正中かを検出し、適切なタイミングで適切なスキルを提案する。コードを探索し続けてる?「`/alfred:survey` 使ったら?」。調査結果が出た?「`ledger` に保存しよう」。タスクが溜まってきた?「`roster` でまとめよう」。
|
|
38
38
|
|
|
39
|
-
**バイパスできない承認ゲート。** 実装前に仕様のレビューサイクルを回す。ブラウザダッシュボードで任意の行にコメントして、承認か差し戻し — GitHub の PR
|
|
39
|
+
**バイパスできない承認ゲート。** 実装前に仕様のレビューサイクルを回す。ブラウザダッシュボードで任意の行にコメントして、承認か差し戻し — GitHub の PR レビューと同じ体験を、仕様に対してやる。ステータスの手動書き換えでは突破できない。レビューファイルの存在まで検証する。**ハード強制**: PreToolUse フックが未承認 M/L/XL スペックへの Edit/Write を物理的にブロック。Stop フックがタスク完了・セルフレビュー・spec close まで Claude の停止を阻止。
|
|
40
40
|
|
|
41
41
|
**張り付くプロジェクトコンテキスト。** Steering文書(プロダクトの目的、コード構造、技術スタック)がプロジェクトから自動生成され、すべての仕様に注入される。AIは常にアーキテクチャを理解している。
|
|
42
42
|
|
|
@@ -123,7 +123,7 @@ npm update -g claude-alfred # CLI、hooks、MCP サーバー、ダッシ
|
|
|
123
123
|
|--------|------|
|
|
124
124
|
| `dossier` | 仕様のライフサイクル管理 — init(サイズ/タイプ指定可), update, status, switch, complete, delete, history, rollback, review, validate |
|
|
125
125
|
| `roster` | エピック管理 — タスクのグループ化、依存関係、進捗追跡 |
|
|
126
|
-
| `ledger` | ナレッジ — search, save
|
|
126
|
+
| `ledger` | ナレッジ — search, save(構造化JSON: decision/pattern/rule), promote(pattern→rule), reflect, audit-conventions |
|
|
127
127
|
|
|
128
128
|
## Hook
|
|
129
129
|
|
|
@@ -131,10 +131,12 @@ npm update -g claude-alfred # CLI、hooks、MCP サーバー、ダッシ
|
|
|
131
131
|
|
|
132
132
|
| イベント | 動作 |
|
|
133
133
|
|----------|------|
|
|
134
|
-
| SessionStart | 仕様コンテキスト復元 +
|
|
135
|
-
| PreCompact |
|
|
136
|
-
| UserPromptSubmit | セマンティック検索 +
|
|
137
|
-
| PostToolUse |
|
|
134
|
+
| SessionStart | 仕様コンテキスト復元 + ナレッジ同期 + 1%ルール(スキル発動促進)+ 成熟度適応 |
|
|
135
|
+
| PreCompact | スナップショット保存 + 決定抽出 + エピック進捗同期 + 調査パターン検出 |
|
|
136
|
+
| UserPromptSubmit | セマンティック検索 + スキルナッジ + **spec承認ゲート**(未承認 M/L/XL に DIRECTIVE) |
|
|
137
|
+
| PostToolUse | エラー検出 + Next Steps 自動チェック + ドリフト検出 + コミット時決定保存 |
|
|
138
|
+
| **PreToolUse** | **ハード強制**: 未承認 M/L/XL スペックへの Edit/Write をブロック。`.alfred/` 編集と Read/Grep は常に許可 |
|
|
139
|
+
| **Stop** | **品質ゲート**: Next Steps 全チェック + Wave セルフレビュー + dossier complete 完了まで停止を阻止 |
|
|
138
140
|
|
|
139
141
|
## ブラウザダッシュボード
|
|
140
142
|
|
|
@@ -147,7 +149,7 @@ alfred dashboard --url-only # URLだけ出力
|
|
|
147
149
|
| タブ | 表示内容 |
|
|
148
150
|
|------|----------|
|
|
149
151
|
| **Overview** | プロジェクトの健康状態 — タスク進捗とバリデーション結果、メモリ健康度(陳腐化数・矛盾数)、仕様の信頼度分布、エピック進捗、最近の意思決定 |
|
|
150
|
-
| **Tasks** |
|
|
152
|
+
| **Tasks** | Active/Completed セクション分離。タスクをクリックで2カラム詳細ビュー: 左にメタデータ、右に折りたたみ可能なspecセクション(色分け付き)。Review タブでインラインコメント |
|
|
151
153
|
| **Knowledge** | メモリ一覧(サブタイプ別タグ付き)。セマンティック検索(Voyage AI、300msデバウンス)。ローカルテキストフィルタ。メモリの有効/無効切り替え |
|
|
152
154
|
| **Activity** | 操作タイムライン。イベントタイプ別フィルタ(init/complete/review)。エピックドリルダウン |
|
|
153
155
|
|
|
@@ -171,21 +173,32 @@ alfred dashboard --url-only # URLだけ出力
|
|
|
171
173
|
|
|
172
174
|
## ナレッジアーキテクチャ
|
|
173
175
|
|
|
174
|
-
|
|
176
|
+
ナレッジは構造化JSONファイルとして保存される。3分類、曖昧さゼロ。真のソースはプロジェクトディレクトリにあり、バイナリDBではない。
|
|
175
177
|
|
|
176
178
|
```
|
|
177
179
|
.alfred/knowledge/
|
|
178
|
-
├── decisions/
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
├── decisions/
|
|
181
|
+
│ └── dec-auth-jwt.json # 一回きりの選択 + 理由 + 却下した代替案
|
|
182
|
+
├── patterns/
|
|
183
|
+
│ └── pat-error-handling.json # 繰り返し実践 + 適用条件 + 期待結果
|
|
184
|
+
└── rules/
|
|
185
|
+
└── rule-no-mock-db.json # 強制ルール + 優先度 + 根拠
|
|
181
186
|
```
|
|
182
187
|
|
|
188
|
+
各タイプには厳密なスキーマ([mneme](https://github.com/hir4ta/mneme) 互換):
|
|
189
|
+
- **Decision**: `title`, `decision`, `reasoning`, `alternatives[]`, `tags[]`, `status`
|
|
190
|
+
- **Pattern**: `type` (good/bad/error-solution), `context`, `pattern`, `applicationConditions`, `expectedOutcomes`
|
|
191
|
+
- **Rule**: `key`, `text`(命令形), `category`, `priority` (p0/p1/p2), `rationale`, `sourceRef`
|
|
192
|
+
|
|
193
|
+
全エントリはテンプレート化されたパラメータで保存(フリーテキスト不可)— セッション間のフォーマット揺れゼロ。
|
|
194
|
+
|
|
183
195
|
- **Git フレンドリー**: ナレッジをコミットしてチームと共有、PRでレビュー
|
|
184
|
-
-
|
|
196
|
+
- **アトミック書き込み**: temp ファイル + rename でクラッシュ時の破損防止
|
|
185
197
|
- **再構築可能**: SQLite検索インデックスはこれらのファイルから派生 — DB を削除しても次のセッションで再構築される
|
|
186
|
-
- **サブタイプ減衰**:
|
|
198
|
+
- **サブタイプ減衰**: パターンは90日で減衰。実証されたルールは120日持つ。種類ごとに半減期が違う。
|
|
199
|
+
- **昇格**: パターンは検索ヒット15回以上でルールに自動昇格
|
|
187
200
|
- **矛盾検出**: 2つのエントリが矛盾していたら(「JWT使え」vs「JWT避けろ」)、自動でフラグ。
|
|
188
|
-
-
|
|
201
|
+
- **多言語対応**: `ALFRED_LANG` で保存されるナレッジの言語を制御
|
|
189
202
|
|
|
190
203
|
## 適応的スペック
|
|
191
204
|
|
|
@@ -239,14 +252,16 @@ alfred dashboard --url-only # URLだけ出力
|
|
|
239
252
|
|
|
|
240
253
|
v
|
|
241
254
|
Hook(見えない)
|
|
242
|
-
|-- SessionStart ->
|
|
243
|
-
|-- PreCompact ->
|
|
244
|
-
|-- UserPromptSubmit -> ベクトル検索 + FTS5 +
|
|
245
|
-
|-- PostToolUse ->
|
|
255
|
+
|-- SessionStart -> コンテキスト復元、1%ルール、成熟度適応
|
|
256
|
+
|-- PreCompact -> スナップショット保存、決定抽出、エピック進捗
|
|
257
|
+
|-- UserPromptSubmit -> ベクトル検索 + FTS5 + スキルナッジ + spec承認チェック
|
|
258
|
+
|-- PostToolUse -> エラー検出、Next Steps自動チェック、ドリフト検出
|
|
259
|
+
|-- PreToolUse -> 未承認 M/L/XL への Edit/Write をブロック
|
|
260
|
+
|-- Stop -> 全タスク完了 + セルフレビュー + complete まで停止阻止
|
|
246
261
|
|
|
|
247
262
|
v
|
|
248
263
|
ストレージ
|
|
249
|
-
|-- .alfred/knowledge/ ->
|
|
264
|
+
|-- .alfred/knowledge/ -> JSON(decisions/, patterns/, rules/)— 真のソース
|
|
250
265
|
|-- .alfred/specs/ -> 仕様ファイル + バージョン履歴 + レビュー
|
|
251
266
|
|-- .alfred/epics/ -> エピック YAML + タスク依存関係
|
|
252
267
|
|-- .alfred/steering/ -> プロジェクトコンテキスト(product, structure, tech)
|
|
@@ -266,6 +281,7 @@ Hook(見えない)
|
|
|
266
281
|
| `.alfred/epics/` | 最初のエピック作成時 | `roster action=init` |
|
|
267
282
|
| `.alfred/steering/` | `/alfred:init` 実行時 | プロジェクト初期化スキル |
|
|
268
283
|
| `.alfred/templates/` | ユーザーが仕様・Steeringテンプレートをカスタマイズする時 | テンプレートオーバーライド用に手動作成 |
|
|
284
|
+
| `.alfred/.state/` | 最初のセッションローカル状態保存時 | ナッジ抑制カウント、探索カウンター(gitignore対象) |
|
|
269
285
|
| `.alfred/audit.jsonl` | 最初の仕様操作時またはコミット後ドリフト検出時 | `dossier init`、レビュー送信、PostToolUse ドリフト |
|
|
270
286
|
|
|
271
287
|
## トラブルシューティング
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ alfred fixes all three.
|
|
|
24
24
|
|
|
25
25
|
**Specs that adapt.** Small bug? 3 files. Medium feature? 5. Large system? All 7. alfred auto-detects the right scope — or you can pick a bugfix template with surgical precision (reproduction steps, root cause, fix strategy).
|
|
26
26
|
|
|
27
|
-
**Memory that compounds.** Every decision, every bug fix, every "we tried X and it didn't work" gets stored as
|
|
27
|
+
**Memory that compounds.** Every decision, every bug fix, every "we tried X and it didn't work" gets stored as structured JSON files in `.alfred/knowledge/` — three types only: **decisions** (one-time choices with reasoning and rejected alternatives), **patterns** (repeatable practices with conditions and expected outcomes), and **rules** (enforceable standards with priority and rationale). Git-friendly, human-readable, team-shareable. A SQLite search index provides semantic search across all knowledge. Contradictions are detected automatically. Next time you hit a similar problem, alfred surfaces the relevant experience — before you even ask.
|
|
28
28
|
|
|
29
29
|
**Reliability signals.** Every spec item gets a grounding level — `verified`, `reviewed`, `inferred`, or `speculative`. You can instantly see which requirements are battle-tested and which are guesswork. Typos in grounding values get caught, not silently ignored.
|
|
30
30
|
|
|
@@ -36,7 +36,7 @@ alfred fixes all three.
|
|
|
36
36
|
|
|
37
37
|
**Proactive skill suggestions.** alfred doesn't wait to be asked. It detects what you're doing — researching, designing, implementing, fixing bugs — and suggests the right skill at the right time. Explored code for a while? "Try `/alfred:survey`." Got research findings? "Save them with `ledger`." Three tasks piling up? "Group them with `roster`."
|
|
38
38
|
|
|
39
|
-
**Approval gates that can't be bypassed.** Specs go through a review cycle before implementation. Comment on any line in the browser dashboard, approve or request changes — like a GitHub PR review, but for your specs. The gate verifies both the review status *and* the existence of a signed review file, so manually editing the status won't get you past it.
|
|
39
|
+
**Approval gates that can't be bypassed.** Specs go through a review cycle before implementation. Comment on any line in the browser dashboard, approve or request changes — like a GitHub PR review, but for your specs. The gate verifies both the review status *and* the existence of a signed review file, so manually editing the status won't get you past it. **Hard enforcement**: PreToolUse hooks physically block Edit/Write operations on unapproved M/L/XL specs. Stop hooks prevent Claude from finishing until all tasks are checked off, self-reviews are done, and the spec is closed.
|
|
40
40
|
|
|
41
41
|
**Project context that sticks.** Steering documents (product purpose, code structure, tech stack) are auto-generated from your project and injected into every spec. Your AI always knows your architecture.
|
|
42
42
|
|
|
@@ -123,7 +123,7 @@ Run `alfred doctor` to verify both are in sync.
|
|
|
123
123
|
|------|---------|
|
|
124
124
|
| `dossier` | Spec lifecycle — init (with size/type), update, status, switch, complete, delete, history, rollback, review, validate |
|
|
125
125
|
| `roster` | Epic management — group tasks with dependencies, track progress |
|
|
126
|
-
| `ledger` | Knowledge — search, save (
|
|
126
|
+
| `ledger` | Knowledge — search, save (structured JSON: decision/pattern/rule), promote (pattern→rule), reflect, audit-conventions |
|
|
127
127
|
|
|
128
128
|
## Hooks
|
|
129
129
|
|
|
@@ -131,10 +131,12 @@ Run automatically. You don't touch these.
|
|
|
131
131
|
|
|
132
132
|
| Event | What happens |
|
|
133
133
|
|-------|-------------|
|
|
134
|
-
| SessionStart | Restores spec context,
|
|
135
|
-
| PreCompact | Extracts decisions, saves
|
|
136
|
-
| UserPromptSubmit | Semantic search + file context boost + **skill nudge** (
|
|
137
|
-
| PostToolUse | Detects Bash errors + searches memory
|
|
134
|
+
| SessionStart | Restores spec context, syncs knowledge index, adapts injection depth to project maturity, 1% rule skill activation |
|
|
135
|
+
| PreCompact | Extracts decisions, saves chapter snapshots, syncs epic progress, detects research patterns |
|
|
136
|
+
| UserPromptSubmit | Semantic search + file context boost + **skill nudge** + **spec approval gate** (blocks implement intent on unapproved M/L/XL specs) |
|
|
137
|
+
| PostToolUse | Detects Bash errors + searches memory. After commits: spec drift detection + auto-save decisions. Edit/Write: auto-check Next Steps progress |
|
|
138
|
+
| **PreToolUse** | **Hard enforcement**: blocks Edit/Write when active M/L/XL spec is not approved. `.alfred/` edits and Read/Grep always allowed |
|
|
139
|
+
| **Stop** | **Quality gate**: blocks Claude from stopping until all Next Steps are checked, Wave self-reviews are done, and `dossier complete` is called |
|
|
138
140
|
|
|
139
141
|
## Browser dashboard
|
|
140
142
|
|
|
@@ -147,7 +149,7 @@ alfred dashboard --url-only # print URL only
|
|
|
147
149
|
| Tab | What you see |
|
|
148
150
|
|-----|-------------|
|
|
149
151
|
| **Overview** | Project health at a glance — task progress with validation badges, memory health (stale count, conflicts), confidence distribution across specs, epic progress, recent decisions |
|
|
150
|
-
| **Tasks** |
|
|
152
|
+
| **Tasks** | Active/Completed sections. Drill into any task for a 2-column detail view: metadata on the left, collapsible spec sections (color-coded) on the right. Switch to Review tab for inline comments |
|
|
151
153
|
| **Knowledge** | Browse all memories with sub-type tags. Semantic search (Voyage AI) with 300ms debounce. Local text filter. Toggle any memory on/off |
|
|
152
154
|
| **Activity** | Timeline of all operations. Filter by event type (init/complete/review). Epic drill-down with task status badges |
|
|
153
155
|
|
|
@@ -171,21 +173,32 @@ Fuzzy matching catches typos: "authetication" still finds "authentication".
|
|
|
171
173
|
|
|
172
174
|
## Knowledge architecture
|
|
173
175
|
|
|
174
|
-
Knowledge is stored as
|
|
176
|
+
Knowledge is stored as structured JSON files — three types, no ambiguity. The source of truth lives in your project directory, not a binary database.
|
|
175
177
|
|
|
176
178
|
```
|
|
177
179
|
.alfred/knowledge/
|
|
178
|
-
├── decisions/
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
├── decisions/
|
|
181
|
+
│ └── dec-auth-jwt.json # one-time choices with reasoning + rejected alternatives
|
|
182
|
+
├── patterns/
|
|
183
|
+
│ └── pat-error-handling.json # repeatable practices with conditions + expected outcomes
|
|
184
|
+
└── rules/
|
|
185
|
+
└── rule-no-mock-db.json # enforceable standards with priority + rationale
|
|
181
186
|
```
|
|
182
187
|
|
|
188
|
+
Each type has a strict schema (inspired by [mneme](https://github.com/hir4ta/mneme)):
|
|
189
|
+
- **Decisions**: `title`, `decision`, `reasoning`, `alternatives[]`, `tags[]`, `status`
|
|
190
|
+
- **Patterns**: `type` (good/bad/error-solution), `context`, `pattern`, `applicationConditions`, `expectedOutcomes`
|
|
191
|
+
- **Rules**: `key`, `text` (imperative), `category`, `priority` (p0/p1/p2), `rationale`, `sourceRef`
|
|
192
|
+
|
|
193
|
+
All entries are saved via templated parameters (no freetext) — zero format drift across sessions.
|
|
194
|
+
|
|
183
195
|
- **Git-friendly**: commit knowledge to share with your team, review in PRs
|
|
184
|
-
- **
|
|
196
|
+
- **Atomic writes**: temp file + rename prevents corruption on crash
|
|
185
197
|
- **Rebuildable**: the SQLite search index is derived from these files — delete the DB, it rebuilds on next session
|
|
186
|
-
- **Sub-type decay**:
|
|
198
|
+
- **Sub-type decay**: Patterns decay in 90 days. Proven rules last 120 days. Each type has its own half-life.
|
|
199
|
+
- **Promotion**: patterns auto-promote to rules at 15+ search hits
|
|
187
200
|
- **Contradiction detection**: When two entries say opposite things ("use JWT" vs "avoid JWT"), alfred flags the conflict.
|
|
188
|
-
- **
|
|
201
|
+
- **Multilingual**: `ALFRED_LANG` controls the language of saved knowledge content
|
|
189
202
|
|
|
190
203
|
## Adaptive specs
|
|
191
204
|
|
|
@@ -239,14 +252,16 @@ You
|
|
|
239
252
|
|
|
|
240
253
|
v
|
|
241
254
|
Hooks (invisible)
|
|
242
|
-
|-- SessionStart -> restore context, adapt to project maturity
|
|
243
|
-
|-- PreCompact -> save
|
|
244
|
-
|-- UserPromptSubmit -> vector search + FTS5 +
|
|
245
|
-
|-- PostToolUse -> detect errors
|
|
255
|
+
|-- SessionStart -> restore context, sync knowledge, 1% rule, adapt to project maturity
|
|
256
|
+
|-- PreCompact -> save snapshots, extract decisions, epic progress
|
|
257
|
+
|-- UserPromptSubmit -> vector search + FTS5 + skill nudge + spec approval check
|
|
258
|
+
|-- PostToolUse -> detect errors, auto-check Next Steps, drift detection
|
|
259
|
+
|-- PreToolUse -> BLOCK Edit/Write on unapproved M/L/XL specs
|
|
260
|
+
|-- Stop -> BLOCK stop until all tasks done + self-review + complete
|
|
246
261
|
|
|
|
247
262
|
v
|
|
248
263
|
Storage
|
|
249
|
-
|-- .alfred/knowledge/ ->
|
|
264
|
+
|-- .alfred/knowledge/ -> JSON (decisions/, patterns/, rules/) — source of truth
|
|
250
265
|
|-- .alfred/specs/ -> spec files + version history + reviews
|
|
251
266
|
|-- .alfred/epics/ -> epic YAML + task dependencies
|
|
252
267
|
|-- .alfred/steering/ -> project context (product, structure, tech)
|
|
@@ -266,6 +281,7 @@ Nothing is generated at install time. Files appear as you use alfred:
|
|
|
266
281
|
| `.alfred/epics/` | First epic is created | `roster action=init` |
|
|
267
282
|
| `.alfred/steering/` | Running `/alfred:init` | Project initialization skill |
|
|
268
283
|
| `.alfred/templates/` | User customizes spec or steering templates | Manual creation for template override |
|
|
284
|
+
| `.alfred/.state/` | First hook that needs session-local state | Nudge dismissals, exploration counter (gitignored) |
|
|
269
285
|
| `.alfred/audit.jsonl` | First spec operation or commit drift detection | `dossier init`, `dossier delete`, review submission, PostToolUse drift |
|
|
270
286
|
|
|
271
287
|
## Troubleshooting
|
package/content/hooks/hooks.json
CHANGED
|
@@ -48,6 +48,31 @@
|
|
|
48
48
|
}
|
|
49
49
|
]
|
|
50
50
|
}
|
|
51
|
+
],
|
|
52
|
+
"PreToolUse": [
|
|
53
|
+
{
|
|
54
|
+
"matcher": "Edit|Write",
|
|
55
|
+
"hooks": [
|
|
56
|
+
{
|
|
57
|
+
"command": "alfred hook PreToolUse",
|
|
58
|
+
"statusMessage": "alfred: checking spec approval...",
|
|
59
|
+
"timeout": 5,
|
|
60
|
+
"type": "command"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"Stop": [
|
|
66
|
+
{
|
|
67
|
+
"hooks": [
|
|
68
|
+
{
|
|
69
|
+
"command": "alfred hook Stop",
|
|
70
|
+
"statusMessage": "alfred: checking completion...",
|
|
71
|
+
"timeout": 5,
|
|
72
|
+
"type": "command"
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
51
76
|
]
|
|
52
77
|
}
|
|
53
78
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { E as require_dist, S as rootDir, m as VALID_SLUG } from "./knowledge-
|
|
2
|
+
import { E as require_dist, S as rootDir, m as VALID_SLUG } from "./knowledge-DItYbraO.mjs";
|
|
3
3
|
import { appendFileSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
//#region src/epic/index.ts
|
|
@@ -39,7 +39,7 @@ var EpicDir = class {
|
|
|
39
39
|
}
|
|
40
40
|
save(ep) {
|
|
41
41
|
const data = (0, import_dist.stringify)(ep);
|
|
42
|
-
const tmp = this.epicPath()
|
|
42
|
+
const tmp = `${this.epicPath()}.tmp`;
|
|
43
43
|
writeFileSync(tmp, data);
|
|
44
44
|
renameSync(tmp, this.epicPath());
|
|
45
45
|
}
|
|
@@ -161,9 +161,7 @@ function listAllEpics(projectPath) {
|
|
|
161
161
|
total: tasks.length,
|
|
162
162
|
tasks
|
|
163
163
|
});
|
|
164
|
-
} catch {
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
164
|
+
} catch {}
|
|
167
165
|
}
|
|
168
166
|
return summaries;
|
|
169
167
|
}
|
|
@@ -219,7 +217,7 @@ function writeActiveEpics(projectPath, state) {
|
|
|
219
217
|
mkdirSync(epicsDir(projectPath), { recursive: true });
|
|
220
218
|
const data = (0, import_dist.stringify)(state);
|
|
221
219
|
const path = epicActivePath(projectPath);
|
|
222
|
-
const tmp = path
|
|
220
|
+
const tmp = `${path}.tmp`;
|
|
223
221
|
writeFileSync(tmp, data);
|
|
224
222
|
renameSync(tmp, path);
|
|
225
223
|
}
|
|
@@ -233,7 +231,7 @@ function appendAudit(projectPath, entry) {
|
|
|
233
231
|
...entry,
|
|
234
232
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
235
233
|
});
|
|
236
|
-
appendFileSync(join(dir, "audit.jsonl"), line
|
|
234
|
+
appendFileSync(join(dir, "audit.jsonl"), `${line}\n`);
|
|
237
235
|
} catch {}
|
|
238
236
|
}
|
|
239
237
|
//#endregion
|
package/dist/cli.mjs
CHANGED
|
@@ -369,9 +369,9 @@ const main = defineCommand({
|
|
|
369
369
|
serve: defineCommand({
|
|
370
370
|
meta: { description: "Start MCP server (stdio)" },
|
|
371
371
|
async run() {
|
|
372
|
-
const { Store } = await import("./store-
|
|
373
|
-
const { Embedder } = await import("./embedder-
|
|
374
|
-
const { serveMCP } = await import("./server-
|
|
372
|
+
const { Store } = await import("./store-CuN3_dWu.mjs");
|
|
373
|
+
const { Embedder } = await import("./embedder-BRG2Xpr4.mjs");
|
|
374
|
+
const { serveMCP } = await import("./server-23A-5yhs.mjs");
|
|
375
375
|
const store = Store.openDefault();
|
|
376
376
|
let emb = null;
|
|
377
377
|
try {
|
|
@@ -397,9 +397,9 @@ const main = defineCommand({
|
|
|
397
397
|
}
|
|
398
398
|
},
|
|
399
399
|
async run({ args }) {
|
|
400
|
-
const { Store } = await import("./store-
|
|
401
|
-
const { Embedder } = await import("./embedder-
|
|
402
|
-
const { startDashboard } = await import("./server-
|
|
400
|
+
const { Store } = await import("./store-CuN3_dWu.mjs");
|
|
401
|
+
const { Embedder } = await import("./embedder-BRG2Xpr4.mjs");
|
|
402
|
+
const { startDashboard } = await import("./server-Dfefx0Gm.mjs");
|
|
403
403
|
const projectPath = process.cwd();
|
|
404
404
|
const store = Store.openDefault();
|
|
405
405
|
let emb = null;
|
|
@@ -422,7 +422,7 @@ const main = defineCommand({
|
|
|
422
422
|
description: "Event name"
|
|
423
423
|
} },
|
|
424
424
|
async run({ args }) {
|
|
425
|
-
const { runHook } = await import("./dispatcher-
|
|
425
|
+
const { runHook } = await import("./dispatcher-xZAhoK7h.mjs").then((n) => n.t);
|
|
426
426
|
await runHook(args.event);
|
|
427
427
|
}
|
|
428
428
|
}),
|
|
@@ -456,8 +456,8 @@ const main = defineCommand({
|
|
|
456
456
|
}
|
|
457
457
|
const dbPath = join(home, ".claude-alfred", "alfred.db");
|
|
458
458
|
check(existsSync(dbPath), `DB: ${dbPath}`, "not found — run: alfred (any command) to create");
|
|
459
|
-
check(!!process.env
|
|
460
|
-
const lang = process.env
|
|
459
|
+
check(!!process.env.VOYAGE_API_KEY, "VOYAGE_API_KEY set", "not set — semantic search disabled, FTS5 fallback active");
|
|
460
|
+
const lang = process.env.ALFRED_LANG;
|
|
461
461
|
check(true, `ALFRED_LANG: ${lang || "(not set, default: en)"}`);
|
|
462
462
|
const rulesDir = join(home, ".claude", "rules");
|
|
463
463
|
try {
|
|
@@ -498,9 +498,7 @@ async function resolveVersion() {
|
|
|
498
498
|
for (const rel of ["..", "../.."]) try {
|
|
499
499
|
const pkg = JSON.parse(readFileSync(join(thisDir, rel, "package.json"), "utf-8"));
|
|
500
500
|
if (pkg.version) return pkg.version;
|
|
501
|
-
} catch {
|
|
502
|
-
continue;
|
|
503
|
-
}
|
|
501
|
+
} catch {}
|
|
504
502
|
} catch {}
|
|
505
503
|
return "dev";
|
|
506
504
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as emitAdditionalContext } from "./dispatcher-xZAhoK7h.mjs";
|
|
3
|
+
//#region src/hooks/directives.ts
|
|
4
|
+
const LEVEL_ORDER = {
|
|
5
|
+
DIRECTIVE: 0,
|
|
6
|
+
WARNING: 1,
|
|
7
|
+
CONTEXT: 2
|
|
8
|
+
};
|
|
9
|
+
const MAX_DIRECTIVES = 3;
|
|
10
|
+
const MAX_DIRECTIVE_BLOCK_CHARS = 500;
|
|
11
|
+
const SPIRIT_VS_LETTER = "Adapting or shortcutting these steps violates the rule, even if you believe the spirit is preserved.";
|
|
12
|
+
/**
|
|
13
|
+
* Build a single additionalContext string from directive items.
|
|
14
|
+
* Order: DIRECTIVE → WARNING → CONTEXT.
|
|
15
|
+
* Max 3 DIRECTIVE items (NFR-5). Excess DIRECTIVEs downgraded to WARNING.
|
|
16
|
+
*/
|
|
17
|
+
function buildDirectiveOutput(items) {
|
|
18
|
+
if (items.length === 0) return "";
|
|
19
|
+
let directiveCount = 0;
|
|
20
|
+
return items.map((item) => {
|
|
21
|
+
if (item.level === "DIRECTIVE") {
|
|
22
|
+
directiveCount++;
|
|
23
|
+
if (directiveCount > MAX_DIRECTIVES) return {
|
|
24
|
+
level: "WARNING",
|
|
25
|
+
message: item.message
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return item;
|
|
29
|
+
}).slice().sort((a, b) => LEVEL_ORDER[a.level] - LEVEL_ORDER[b.level]).map((item) => {
|
|
30
|
+
let block = `[${item.level}] ${item.message}`;
|
|
31
|
+
if (item.level === "DIRECTIVE") {
|
|
32
|
+
const rats = (item.rationalizations ?? []).slice();
|
|
33
|
+
const suffix = item.spiritVsLetter ? `\n${SPIRIT_VS_LETTER}` : "";
|
|
34
|
+
while (rats.length > 0) {
|
|
35
|
+
if (`${block}\n${rats.map((r) => `- ${r}`).join("\n")}${suffix}`.length <= MAX_DIRECTIVE_BLOCK_CHARS) break;
|
|
36
|
+
rats.pop();
|
|
37
|
+
}
|
|
38
|
+
if (rats.length > 0) block += `\n${rats.map((r) => `- ${r}`).join("\n")}`;
|
|
39
|
+
block += suffix;
|
|
40
|
+
if (block.length > MAX_DIRECTIVE_BLOCK_CHARS) {
|
|
41
|
+
const messageOnly = `[${item.level}] ${item.message}`;
|
|
42
|
+
if (messageOnly.length > MAX_DIRECTIVE_BLOCK_CHARS) block = `${messageOnly.slice(0, MAX_DIRECTIVE_BLOCK_CHARS - 3)}...`;
|
|
43
|
+
else block = messageOnly;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return block;
|
|
47
|
+
}).join("\n");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Emit directives via single emitAdditionalContext call (NFR-4).
|
|
51
|
+
*/
|
|
52
|
+
function emitDirectives(eventName, items) {
|
|
53
|
+
const output = buildDirectiveOutput(items);
|
|
54
|
+
if (output) emitAdditionalContext(eventName, output);
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { emitDirectives as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { O as __exportAll } from "./knowledge-
|
|
2
|
+
import { O as __exportAll } from "./knowledge-DItYbraO.mjs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
//#region src/hooks/dispatcher.ts
|
|
5
5
|
var dispatcher_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -19,7 +19,7 @@ function emitAdditionalContext(eventName, context) {
|
|
|
19
19
|
additionalContext: context
|
|
20
20
|
} };
|
|
21
21
|
try {
|
|
22
|
-
process.stdout.write(JSON.stringify(out)
|
|
22
|
+
process.stdout.write(`${JSON.stringify(out)}\n`);
|
|
23
23
|
} catch {}
|
|
24
24
|
}
|
|
25
25
|
function extractSection(content, heading) {
|
|
@@ -27,7 +27,7 @@ function extractSection(content, heading) {
|
|
|
27
27
|
const result = [];
|
|
28
28
|
let inSection = false;
|
|
29
29
|
for (const line of lines) {
|
|
30
|
-
if (line === heading || line.startsWith(heading
|
|
30
|
+
if (line === heading || line.startsWith(`${heading} `)) {
|
|
31
31
|
inSection = true;
|
|
32
32
|
continue;
|
|
33
33
|
}
|
|
@@ -51,13 +51,15 @@ async function runHook(event) {
|
|
|
51
51
|
} catch {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
-
if (ev.stop_hook_active) return;
|
|
54
|
+
if (ev.stop_hook_active && event !== "Stop" && event !== "PreToolUse") return;
|
|
55
55
|
if (ev.cwd) ev.cwd = resolve(ev.cwd);
|
|
56
56
|
const timeout = {
|
|
57
57
|
SessionStart: 4500,
|
|
58
58
|
PreCompact: 9e3,
|
|
59
59
|
UserPromptSubmit: 9e3,
|
|
60
|
-
PostToolUse: 5e3
|
|
60
|
+
PostToolUse: 5e3,
|
|
61
|
+
PreToolUse: 4500,
|
|
62
|
+
Stop: 4500
|
|
61
63
|
}[event] ?? 5e3;
|
|
62
64
|
const controller = new AbortController();
|
|
63
65
|
const timer = setTimeout(() => controller.abort(), timeout);
|
|
@@ -75,26 +77,40 @@ async function runHook(event) {
|
|
|
75
77
|
case "PostToolUse":
|
|
76
78
|
await handlePostToolUse(ev, controller.signal);
|
|
77
79
|
break;
|
|
80
|
+
case "PreToolUse":
|
|
81
|
+
await handlePreToolUse(ev, controller.signal);
|
|
82
|
+
break;
|
|
83
|
+
case "Stop":
|
|
84
|
+
await handleStop(ev, controller.signal);
|
|
85
|
+
break;
|
|
78
86
|
}
|
|
79
87
|
} finally {
|
|
80
88
|
clearTimeout(timer);
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
91
|
async function handleSessionStart(ev, signal) {
|
|
84
|
-
const { sessionStart } = await import("./session-start-
|
|
92
|
+
const { sessionStart } = await import("./session-start-D177nOGO.mjs");
|
|
85
93
|
await sessionStart(ev, signal);
|
|
86
94
|
}
|
|
87
95
|
async function handlePreCompact(ev, signal) {
|
|
88
|
-
const { preCompact } = await import("./pre-compact-
|
|
96
|
+
const { preCompact } = await import("./pre-compact-CyNPLjSK.mjs");
|
|
89
97
|
await preCompact(ev, signal);
|
|
90
98
|
}
|
|
91
99
|
async function handleUserPromptSubmit(ev, signal) {
|
|
92
|
-
const { userPromptSubmit } = await import("./user-prompt-
|
|
100
|
+
const { userPromptSubmit } = await import("./user-prompt-X18njRds.mjs");
|
|
93
101
|
await userPromptSubmit(ev, signal);
|
|
94
102
|
}
|
|
95
103
|
async function handlePostToolUse(ev, signal) {
|
|
96
|
-
const { postToolUse } = await import("./post-tool-
|
|
104
|
+
const { postToolUse } = await import("./post-tool-G2PAotAa.mjs");
|
|
97
105
|
await postToolUse(ev, signal);
|
|
98
106
|
}
|
|
107
|
+
async function handlePreToolUse(ev, _signal) {
|
|
108
|
+
const { preToolUse } = await import("./pre-tool-B_CYjJPV.mjs");
|
|
109
|
+
await preToolUse(ev);
|
|
110
|
+
}
|
|
111
|
+
async function handleStop(ev, _signal) {
|
|
112
|
+
const { stop } = await import("./stop-uQEMYJUS.mjs");
|
|
113
|
+
await stop(ev);
|
|
114
|
+
}
|
|
99
115
|
//#endregion
|
|
100
116
|
export { notifyUser as i, emitAdditionalContext as n, extractSection as r, dispatcher_exports as t };
|
|
@@ -12,7 +12,7 @@ function envIntOr(key, fallback) {
|
|
|
12
12
|
const v = process.env[key];
|
|
13
13
|
if (v) {
|
|
14
14
|
const n = parseInt(v, 10);
|
|
15
|
-
if (!isNaN(n) && n > 0) return n;
|
|
15
|
+
if (!Number.isNaN(n) && n > 0) return n;
|
|
16
16
|
}
|
|
17
17
|
return fallback;
|
|
18
18
|
}
|
|
@@ -82,7 +82,6 @@ async function retryVoyage(fn, signal) {
|
|
|
82
82
|
if (err.status === 400 && (isVoyageTransient(err.detail) || isVoyageTransientStructured(err.errResp))) continue;
|
|
83
83
|
throw err;
|
|
84
84
|
}
|
|
85
|
-
continue;
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
throw lastErr;
|
|
@@ -116,7 +115,7 @@ var VoyageClient = class {
|
|
|
116
115
|
method: "POST",
|
|
117
116
|
headers: {
|
|
118
117
|
"Content-Type": "application/json",
|
|
119
|
-
|
|
118
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
120
119
|
},
|
|
121
120
|
body: JSON.stringify(payload),
|
|
122
121
|
signal: signal ?? AbortSignal.timeout(3e4)
|
|
@@ -157,7 +156,7 @@ var VoyageClient = class {
|
|
|
157
156
|
method: "POST",
|
|
158
157
|
headers: {
|
|
159
158
|
"Content-Type": "application/json",
|
|
160
|
-
|
|
159
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
161
160
|
},
|
|
162
161
|
body: JSON.stringify(payload),
|
|
163
162
|
signal: signal ?? AbortSignal.timeout(3e4)
|
|
@@ -185,7 +184,7 @@ var Embedder = class Embedder {
|
|
|
185
184
|
this.client = client;
|
|
186
185
|
}
|
|
187
186
|
static create() {
|
|
188
|
-
const apiKey = process.env
|
|
187
|
+
const apiKey = process.env.VOYAGE_API_KEY;
|
|
189
188
|
if (!apiKey) throw new Error("VOYAGE_API_KEY is required but not set (get a key at https://dash.voyageai.com/)");
|
|
190
189
|
return new Embedder(new VoyageClient(apiKey));
|
|
191
190
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { c as mapRow, r as getKnowledgeByIDs, u as searchKnowledgeKeyword } from "./knowledge-
|
|
3
|
-
import { n as deserializeFloat32, t as cosineSimilarity } from "./vectors-
|
|
2
|
+
import { c as mapRow, r as getKnowledgeByIDs, u as searchKnowledgeKeyword } from "./knowledge-DItYbraO.mjs";
|
|
3
|
+
import { n as deserializeFloat32, t as cosineSimilarity } from "./vectors-C_Yj-4sR.mjs";
|
|
4
4
|
//#region src/store/fts.ts
|
|
5
5
|
function subTypeHalfLife(subType) {
|
|
6
6
|
switch (subType) {
|
|
7
7
|
case "assumption": return 30;
|
|
8
8
|
case "inference": return 45;
|
|
9
|
-
case "
|
|
9
|
+
case "snapshot": return 30;
|
|
10
10
|
case "pattern": return 90;
|
|
11
11
|
case "decision": return 90;
|
|
12
12
|
case "rule": return 120;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as getKnowledgeByIDs, s as incrementHitCount, u as searchKnowledgeKeyword } from "./knowledge-
|
|
3
|
-
import { r as vectorSearchKnowledge } from "./vectors-
|
|
4
|
-
import { i as subTypeHalfLife, n as searchKnowledgeFTS, r as subTypeBoost } from "./fts-
|
|
2
|
+
import { r as getKnowledgeByIDs, s as incrementHitCount, u as searchKnowledgeKeyword } from "./knowledge-DItYbraO.mjs";
|
|
3
|
+
import { r as vectorSearchKnowledge } from "./vectors-C_Yj-4sR.mjs";
|
|
4
|
+
import { i as subTypeHalfLife, n as searchKnowledgeFTS, r as subTypeBoost } from "./fts-CDEAxhSV.mjs";
|
|
5
5
|
//#region src/mcp/helpers.ts
|
|
6
6
|
const RECENCY_FLOOR = .5;
|
|
7
7
|
function truncate(s, maxLen) {
|
|
8
8
|
const runes = [...s];
|
|
9
9
|
if (runes.length <= maxLen) return s;
|
|
10
|
-
return runes.slice(0, maxLen).join("")
|
|
10
|
+
return `${runes.slice(0, maxLen).join("")}...`;
|
|
11
11
|
}
|
|
12
12
|
function recencyFactor(createdAt, subType, now) {
|
|
13
13
|
const halfLife = subTypeHalfLife(subType);
|
|
14
14
|
if (halfLife <= 0) return 1;
|
|
15
15
|
const parsed = Date.parse(createdAt);
|
|
16
|
-
if (isNaN(parsed)) return 1;
|
|
16
|
+
if (Number.isNaN(parsed)) return 1;
|
|
17
17
|
const ageDays = (now.getTime() - parsed) / (1e3 * 60 * 60 * 24);
|
|
18
18
|
if (ageDays <= 0) return 1;
|
|
19
19
|
const factor = Math.exp(-Math.LN2 * ageDays / halfLife);
|
|
@@ -55,7 +55,7 @@ async function searchPipeline(store, emb, query, limit, overRetrieve, precompute
|
|
|
55
55
|
rawDocs = ordered;
|
|
56
56
|
res.searchMethod = "vector";
|
|
57
57
|
if (rawDocs.length > limit) try {
|
|
58
|
-
const contents = rawDocs.map((d) => d.title
|
|
58
|
+
const contents = rawDocs.map((d) => `${d.title}\n${d.content}`);
|
|
59
59
|
const reranked = await emb.rerank(query, contents, limit);
|
|
60
60
|
if (reranked.length > 0) {
|
|
61
61
|
const reorderedDocs = [];
|
|
@@ -84,6 +84,7 @@ async function searchPipeline(store, emb, query, limit, overRetrieve, precompute
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
+
rawDocs = rawDocs.filter((d) => d.subType !== "snapshot");
|
|
87
88
|
res.scoredDocs = applyRecencySignal(rawDocs, res.searchMethod, /* @__PURE__ */ new Date());
|
|
88
89
|
if (res.scoredDocs.length > limit) res.scoredDocs = res.scoredDocs.slice(0, limit);
|
|
89
90
|
return res;
|