opencode-skills-collection 4.0.42 → 4.0.43
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/bundled-skills/.antigravity-install-manifest.json +3 -1
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +1 -1
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/users/aas-core.md +1 -1
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +3 -3
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/lore/SKILL.md +104 -298
- package/bundled-skills/lore/references/audit-template.md +21 -4
- package/bundled-skills/lore/references/compatibility.md +51 -121
- package/bundled-skills/lore/references/config.md +22 -23
- package/bundled-skills/lore/references/entry-format.md +33 -3
- package/bundled-skills/lore/references/history-command.md +98 -2
- package/bundled-skills/lore/references/platform-mirrors.md +45 -16
- package/bundled-skills/lore/references/stale-new-markers.md +10 -8
- package/bundled-skills/lore/references/summary-template.md +8 -1
- package/bundled-skills/lore/references/workflows.md +192 -0
- package/bundled-skills/lore/scripts/README.md +14 -13
- package/bundled-skills/lore/scripts/README.zh-CN.md +14 -13
- package/bundled-skills/lore/scripts/find_duplicates.py +14 -4
- package/bundled-skills/lore/scripts/find_stale.py +68 -16
- package/bundled-skills/lore/scripts/history.py +235 -23
- package/bundled-skills/lore/scripts/id_hash.py +7 -4
- package/bundled-skills/lore/scripts/list_entries.py +82 -13
- package/bundled-skills/poka-yoke/SKILL.md +172 -0
- package/bundled-skills/project-state-governor/SKILL.md +1 -1
- package/bundled-skills/project-state-governor/references/project-state-schema.md +1 -1
- package/bundled-skills/spec-driven-loop/SKILL.md +203 -0
- package/bundled-skills/spec-driven-loop/references/agent-and-judge-contracts.md +120 -0
- package/bundled-skills/spec-driven-loop/references/document-templates.md +184 -0
- package/package.json +1 -1
- package/skills_index.json +67 -1
- package/bundled-skills/lore/README.md +0 -386
- package/bundled-skills/lore/README.zh-CN.md +0 -386
- package/bundled-skills/lore/WORKFLOWS.md +0 -216
- package/bundled-skills/lore/WORKFLOWS.zh-CN.md +0 -216
|
@@ -121,6 +121,7 @@ Both paths use the same function. Once `init` has run, `mirror_targets` is set,
|
|
|
121
121
|
Every mirror file is split into two sections by a `---` separator. The top section is Skill-managed and rewritten on mirror regeneration. The bottom section is user-editable and preserved verbatim.
|
|
122
122
|
|
|
123
123
|
```markdown
|
|
124
|
+
<!-- LORE:START -->
|
|
124
125
|
## Lore (auto-managed)
|
|
125
126
|
|
|
126
127
|
# .lore SUMMARY (synced 2026-07-09)
|
|
@@ -131,6 +132,7 @@ Every mirror file is split into two sections by a `---` separator. The top secti
|
|
|
131
132
|
## Global
|
|
132
133
|
- Monorepo with pnpm workspaces + Turborepo — [_global/ARCHITECTURE.md#ARCH-2026-01-15-d7a3]
|
|
133
134
|
...
|
|
135
|
+
<!-- LORE:END -->
|
|
134
136
|
|
|
135
137
|
---
|
|
136
138
|
|
|
@@ -141,7 +143,7 @@ Every mirror file is split into two sections by a `---` separator. The top secti
|
|
|
141
143
|
- Prefer English
|
|
142
144
|
```
|
|
143
145
|
|
|
144
|
-
The `---` separator is a literal Markdown horizontal rule. Both sections are plain Markdown so any agent or editor can render them normally.
|
|
146
|
+
The `<!-- LORE:START -->` and `<!-- LORE:END -->` HTML comments are the canonical boundary markers for new mirrors (see rule 5a). The `---` separator is a literal Markdown horizontal rule. Both sections are plain Markdown so any agent or editor can render them normally.
|
|
145
147
|
|
|
146
148
|
### Section detection rules
|
|
147
149
|
|
|
@@ -151,6 +153,30 @@ When syncing a mirror file:
|
|
|
151
153
|
2. If the file contains a `## My notes` header, the My notes section starts at that header and goes to EOF.
|
|
152
154
|
3. If neither marker is present, the entire file is treated as the Lore section (i.e. no My notes section). Subsequent sync appends a separator + empty My notes section.
|
|
153
155
|
4. If the file is missing the `## Lore` header but has `## My notes`, the entire file is treated as user notes. Skill does not write to it. User is asked to confirm before sync restructures the file.
|
|
156
|
+
5. **Section boundary markers (canonical form is HTML comments)**:
|
|
157
|
+
|
|
158
|
+
a) **New mirrors (post-v1 skill release)**: the canonical boundary is
|
|
159
|
+
`<!-- LORE:START -->` and `<!-- LORE:END -->` HTML comments. The
|
|
160
|
+
skill emits these on every regeneration. The `## Lore (auto-managed)`
|
|
161
|
+
header inside the start marker and the `---` separator after the
|
|
162
|
+
end marker are still required for human readers and as secondary
|
|
163
|
+
signals, but the HTML comments are the **authoritative** boundary
|
|
164
|
+
the skill uses for detection. New mirrors **must** include the HTML
|
|
165
|
+
comment markers.
|
|
166
|
+
|
|
167
|
+
b) **Existing pre-v1 mirrors (legacy form)**: the `---` line and
|
|
168
|
+
`## My notes (free edit)` header form continues to be detected
|
|
169
|
+
and preserved by rules 1–4. The skill does **not** restructure
|
|
170
|
+
an existing mirror that lacks HTML comments. The first
|
|
171
|
+
`lore mirror` run on such a file asks the user once:
|
|
172
|
+
"Add HTML markers (recommended)" or "Keep legacy form". The user
|
|
173
|
+
can upgrade a legacy mirror later by running `lore mirror` and
|
|
174
|
+
accepting the prompt, or by manually adding the HTML comments.
|
|
175
|
+
|
|
176
|
+
c) **Detection priority when both forms are present**: HTML comments
|
|
177
|
+
win. The skill uses them as the authoritative boundary; the `---`
|
|
178
|
+
and `## My notes` are not consulted for boundary detection but are
|
|
179
|
+
still respected for content placement.
|
|
154
180
|
|
|
155
181
|
## Sync-time behavior
|
|
156
182
|
|
|
@@ -196,7 +222,7 @@ The `init` command extends the resolution algorithm above with classification an
|
|
|
196
222
|
|
|
197
223
|
Class (a) files are auto-included in `mirror_targets`.
|
|
198
224
|
|
|
199
|
-
4. **Multi-select question.** "Which agents do you use in this project?" Default pre-selection: every agent corresponding to a class (a) file. Empty selection is allowed — but class (a) files still get included via Step 5.
|
|
225
|
+
4. **Multi-select question.** "Which agents do you use in this project?" Default pre-selection: every agent corresponding to a class (a) file. Empty selection is allowed — but class (a) files still get included via Step 5. Unlike `mirror` / `compress` (which follow the resolution algorithm's silent return-on-detect), `init` always asks — this is how the user adds agents whose files don't exist yet.
|
|
200
226
|
|
|
201
227
|
5. **Compute final `mirror_targets`** by combining three sources and deduplicating:
|
|
202
228
|
- All class (a) files from Step 3 (always included, regardless of Step 4 selection).
|
|
@@ -208,7 +234,7 @@ The `init` command extends the resolution algorithm above with classification an
|
|
|
208
234
|
6. **Write `.lore/.config.json`** with `mirror_targets` populated.
|
|
209
235
|
|
|
210
236
|
7. **Generate initial mirror files** for each target:
|
|
211
|
-
- File absent → full template (`## Lore` + `---` + empty `## My notes`).
|
|
237
|
+
- File absent → full template (`<!-- LORE:START -->` + `## Lore` + content + `<!-- LORE:END -->` + `---` + empty `## My notes`).
|
|
212
238
|
- File present with `## Lore` → refresh Lore section, preserve My notes verbatim.
|
|
213
239
|
- File present and "take over" chosen → old content becomes My notes, new `## Lore` above.
|
|
214
240
|
- File present and "preserve" chosen → no write.
|
|
@@ -216,9 +242,11 @@ The `init` command extends the resolution algorithm above with classification an
|
|
|
216
242
|
For each generated mirror file, the section template is:
|
|
217
243
|
|
|
218
244
|
```
|
|
245
|
+
<!-- LORE:START -->
|
|
219
246
|
## Lore (auto-managed)
|
|
220
247
|
|
|
221
248
|
<initial or refreshed Lore content>
|
|
249
|
+
<!-- LORE:END -->
|
|
222
250
|
|
|
223
251
|
---
|
|
224
252
|
|
|
@@ -229,16 +257,17 @@ For each generated mirror file, the section template is:
|
|
|
229
257
|
|
|
230
258
|
## What gets mirrored
|
|
231
259
|
|
|
232
|
-
The mirror's Lore section is an **index** into `.lore/` — not a copy of its content. This keeps per-session token cost flat (~
|
|
260
|
+
The mirror's Lore section is an **index** into `.lore/` — not a copy of its content. This keeps per-session token cost flat (~600 B worst case, regardless of project size) and aligns with how platform instruction files (`CLAUDE.md`, `.cursorrules`, etc.) are designed to be used: as small pointers that tell the agent where to find detail on demand.
|
|
233
261
|
|
|
234
262
|
The agent generating the mirror walks `.lore/` and emits the structure below. Sections appear only when their content exists (adaptive rendering).
|
|
235
263
|
|
|
236
264
|
### Index template
|
|
237
265
|
|
|
238
266
|
```
|
|
267
|
+
<!-- LORE:START -->
|
|
239
268
|
## Lore (auto-managed)
|
|
240
269
|
|
|
241
|
-
Project memory.
|
|
270
|
+
Project memory at `.lore/`. Before project-specific questions, read `.lore/SUMMARY.md` as the digest, then open the referenced entries (`.lore/_global/`, `.lore/scopes/`) for the full text before answering or deciding; cite entry IDs (e.g. `_global/ARCHITECTURE.md#ARCH-2026-01-15-d7a3`) when using memory.
|
|
242
271
|
|
|
243
272
|
**Structure**:
|
|
244
273
|
- Digest: `.lore/SUMMARY.md` (top-level overview)
|
|
@@ -249,13 +278,14 @@ Project memory. Read deeper on demand.
|
|
|
249
278
|
...
|
|
250
279
|
|
|
251
280
|
**Query**: `lore query <term>` or `lore query <scope>:<term>`
|
|
252
|
-
**Update**: see the `lore` skill (init / sync / query / audit / compress / mirror)
|
|
281
|
+
**Update**: see the `lore` skill (init / sync / query / audit / compress / mirror / history)
|
|
282
|
+
<!-- LORE:END -->
|
|
253
283
|
|
|
254
284
|
---
|
|
255
285
|
## My notes (free edit)
|
|
256
286
|
```
|
|
257
287
|
|
|
258
|
-
The `## Lore (auto-managed)` opener, `---` separator, and `## My notes (free edit)` closer are **always present** — only the `**Structure**:` body varies with adaptive rendering. Agent preserves the My notes section verbatim across regenerations.
|
|
288
|
+
The `<!-- LORE:START -->` / `<!-- LORE:END -->` markers, `## Lore (auto-managed)` opener, `---` separator, and `## My notes (free edit)` closer are **always present** in new mirrors — only the `**Structure**:` body varies with adaptive rendering. Agent preserves the My notes section verbatim across regenerations.
|
|
259
289
|
|
|
260
290
|
### Field sources
|
|
261
291
|
|
|
@@ -276,20 +306,24 @@ The index does **not** track the project's source-directory mapping for each sco
|
|
|
276
306
|
|
|
277
307
|
### Adaptive renderings
|
|
278
308
|
|
|
279
|
-
Only the `**Structure**:` body varies. The `## Lore (auto-managed)` opener, `---` separator, and `## My notes (free edit)` closer are always present and unchanged.
|
|
309
|
+
Only the `**Structure**:` body varies. The `<!-- LORE:START -->` / `<!-- LORE:END -->` markers, `## Lore (auto-managed)` opener, the **first-line instruction** (see below), `---` separator, and `## My notes (free edit)` closer are always present and unchanged in new mirrors.
|
|
310
|
+
|
|
311
|
+
**First-line instruction.** The opening sentence after `## Lore (auto-managed)` is the agent-facing imperative that triggers memory lookup (e.g. "Before project-specific questions, read `.lore/SUMMARY.md` as the digest, then open the referenced entries for the full text before answering or deciding."). It is constant across all renderings (empty / single-scope / multi-scope) because the agent's responsibility is the same regardless of project shape. Editing this sentence is a template-body change (non-breaking per `references/compatibility.md`); content-based dedup means existing mirrors keep their old opening until regenerated.
|
|
280
312
|
|
|
281
313
|
**Empty project** (just initialized, no entries yet):
|
|
282
314
|
|
|
283
315
|
```
|
|
316
|
+
<!-- LORE:START -->
|
|
284
317
|
## Lore (auto-managed)
|
|
285
318
|
|
|
286
|
-
Project memory.
|
|
319
|
+
Project memory at `.lore/`. Before project-specific questions, read `.lore/SUMMARY.md` as the digest, then open the referenced entries (`.lore/_global/`, `.lore/scopes/`) for the full text before answering or deciding; cite entry IDs (e.g. `_global/ARCHITECTURE.md#ARCH-2026-01-15-d7a3`) when using memory.
|
|
287
320
|
|
|
288
321
|
**Structure**:
|
|
289
322
|
- Digest: `.lore/SUMMARY.md` (top-level overview)
|
|
290
323
|
|
|
291
324
|
**Query**: `lore query <term>`
|
|
292
325
|
**Update**: see the `lore` skill
|
|
326
|
+
<!-- LORE:END -->
|
|
293
327
|
|
|
294
328
|
---
|
|
295
329
|
## My notes (free edit)
|
|
@@ -359,12 +393,7 @@ Index content changes require regeneration when:
|
|
|
359
393
|
|
|
360
394
|
## Manual operations
|
|
361
395
|
|
|
362
|
-
|
|
363
|
-
|---|---|
|
|
364
|
-
| `lore mirror` | Force-regenerate all configured platform mirrors from current `.lore/*` state. Content-based dedup: skips targets whose new Lore section matches the existing one. |
|
|
365
|
-
| `lore mirror reset <file>` | Archive current My notes content to `.lore/.archive/<file>-<date>.md`, then write a clean mirror with only the Lore section. User must confirm. |
|
|
366
|
-
| `lore mirror show <file>` | Print the file with the two sections clearly delimited in the output. Pure read. |
|
|
367
|
-
| `lore mirror check` | For each configured target, verify it has a `---` separator and a `## My notes` section. Report any structural problems. Read-only. |
|
|
396
|
+
Regeneration is not a blind rewrite: each target's two-section structure is validated first, and anomalies are reported to the user instead of overwritten (see "Section detection rules" above). My notes is preserved verbatim across regenerations; if the user asks to wipe a target's My notes, archive the old content to `.lore/.archive/<file>-<date>.md` first, then write a clean mirror.
|
|
368
397
|
|
|
369
398
|
## Trigger rules
|
|
370
399
|
|
|
@@ -373,5 +402,5 @@ Index content changes require regeneration when:
|
|
|
373
402
|
| `init` confirms draft | Auto-generate mirrors for all configured targets using the init-time rules above. |
|
|
374
403
|
| `sync` proposal accepted | Writes to `.lore/*.md` only. Does **not** touch mirrors. User runs `lore mirror` separately to publish. (Override: set `sync_updates_mirror: true` in config to restore old behavior.) |
|
|
375
404
|
| `compress` completes | If `auto_mirror: true`, regenerate mirrors (with content-based dedup). Otherwise ask per target. |
|
|
376
|
-
| `lore mirror` |
|
|
405
|
+
| `lore mirror` | Regenerate all configured targets with content-based dedup. |
|
|
377
406
|
| `query` / `audit` | Never touches mirrors. |
|
|
@@ -8,7 +8,7 @@ When `sync` proposes a change, it never silently mutates files. Instead it emits
|
|
|
8
8
|
|---|---|
|
|
9
9
|
| `[NEW]` | Propose adding a new entry |
|
|
10
10
|
| `[STALE]` | Propose marking an existing entry as superseded/contradicted |
|
|
11
|
-
| `[REFINED]` | Propose updating an existing entry
|
|
11
|
+
| `[REFINED]` | Propose updating an existing entry: a tags-only refresh (body unchanged), or a wording/scope refinement that rewrites the body |
|
|
12
12
|
| `[ALERT]` | Conflicting signal detected during sync that needs human resolution |
|
|
13
13
|
| `[COMPRESS NOTICE]` | Threshold tripped; suggest running `compress` after this sync |
|
|
14
14
|
|
|
@@ -19,12 +19,14 @@ When `sync` proposes a change, it never silently mutates files. Instead it emits
|
|
|
19
19
|
- [scopes/frontend/ARCHITECTURE.md] [ARCH-2026-07-09-b4d2] Use `react-hook-form` for all forms. #added:2026-07-09
|
|
20
20
|
- [scopes/frontend/CONVENTIONS.md] [CONV-2026-07-09-c5e1] Never use `any` in TypeScript; prefer `unknown` + narrowing. #added:2026-07-09
|
|
21
21
|
|
|
22
|
-
## [STALE] Candidates for
|
|
23
|
-
- [scopes/frontend/ARCHITECTURE.md] [ARCH-2026-01-15-d7a3] Use Pages Router (Next.js). #stale:2026-07-09
|
|
22
|
+
## [STALE] Candidates for review
|
|
23
|
+
- [scopes/frontend/ARCHITECTURE.md] [ARCH-2026-01-15-d7a3] Use Pages Router (Next.js). #stale:2026-07-09 #superseded-by:ARCH-2026-07-09-b4d2
|
|
24
24
|
Evidence: `frontend/package.json` shows `"next": "^14.0.0"` with `app/` directory present.
|
|
25
|
+
Replaced by: `[ARCH-2026-07-09-b4d2] Use App Router (Next.js 14)` (new entry in this proposal).
|
|
25
26
|
|
|
26
27
|
## [REFINED] Existing entries updated
|
|
27
|
-
- [scopes/frontend/DECISIONS.md] [DEC-2026-02-03-7c19] (was: "use Zustand") →
|
|
28
|
+
- [scopes/frontend/DECISIONS.md] [DEC-2026-02-03-7c19] (was: "use Zustand") → body rewritten; old entry gets `#stale:2026-07-09 #superseded-by:DEC-2026-07-09-e3f7` in this same proposal.
|
|
29
|
+
- [scopes/frontend/DECISIONS.md] [DEC-2026-07-09-e3f7] "use Zustand v4+ with slices pattern" #added:2026-07-09
|
|
28
30
|
|
|
29
31
|
## [ALERT] Conflicting signals detected during sync
|
|
30
32
|
- Sync proposes `[CONV-2026-07-09-c5e1]` (no `any`), but `[CONV-2026-06-01-f0a1]` already says "use `any` sparingly in test mocks". Resolution: refined entry above clarifies the exception.
|
|
@@ -50,13 +52,13 @@ For partial acceptance, the user should explicitly list which items to apply.
|
|
|
50
52
|
| Marker | File action |
|
|
51
53
|
|---|---|
|
|
52
54
|
| `[NEW]` | Append a new bullet to the named file, with `#added:<today>` |
|
|
53
|
-
| `[STALE]` | Append `#stale:<today>`
|
|
54
|
-
| `[REFINED]` |
|
|
55
|
+
| `[STALE]` | Append `#stale:<today>` and (if a replacement exists) `#superseded-by:<replacement-id>` to the existing entry; entry stays in the file. Two cases: (a) the entry was superseded by a `[NEW]` entry in the same proposal — set both tags and carry the new ID forward; (b) the entry is deprecated with no successor — set `#stale:<today>` only; the user can backfill the chain later if a replacement appears. |
|
|
56
|
+
| `[REFINED]` | Body unchanged → update tags only (e.g. bump `#verified:<today>`), keep the ID. Body changed → write a new entry with a freshly hashed ID and mark the old entry `#stale:<today>` + `#superseded-by:<new-id>` (the ID is the content hash, so a rewritten body can never keep its old ID) |
|
|
55
57
|
| `[ALERT]` | No direct file change; only marks the conflict for user resolution |
|
|
56
58
|
| `[COMPRESS NOTICE]` | No file change; advisory only |
|
|
57
59
|
|
|
58
|
-
Note: `[STALE]` does not delete or move anything. The entry remains in its file with
|
|
60
|
+
Note: `[STALE]` does not delete or move anything. The entry remains in its file with `#stale` (and optionally `#superseded-by`) tags. There is no `archive/` step — git history is the archive, and `#superseded-by` (when present) tells `compress`/`audit`/`history` how to walk the replacement chain.
|
|
59
61
|
|
|
60
62
|
## When audit uses these markers
|
|
61
63
|
|
|
62
|
-
`audit` does **not** use these markers. It writes its own severity tags (`[CONFLICT]`, `[STALE]`, `[UNVERIFIED]`) into the audit report file under `.lore/audit/`. The naming overlap (`[STALE]` in sync vs `[STALE]` severity in audit) is intentional — both refer to the same concept (entry no longer accurate) but operate in different files with different downstream actions.
|
|
64
|
+
`audit` does **not** use these markers. It writes its own severity tags (`[CONFLICT]`, `[STALE]`, `[UNVERIFIED]`) into the audit report file under `.lore/audit/`. The naming overlap (`[STALE]` in sync vs `[STALE]` severity in audit) is intentional — both refer to the same concept (entry no longer accurate) but operate in different files with different downstream actions.
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
> Last compressed: <YYYY-MM-DD>
|
|
11
11
|
> Total entries: <N> across <M> scopes
|
|
12
|
+
> This digest highlights key entries — open the referenced files under `_global/` and `scopes/` for the full text.
|
|
12
13
|
|
|
13
14
|
## Global (`_global/`)
|
|
14
15
|
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
|
|
42
43
|
For each (scope, layer) tuple, pick entries by this priority:
|
|
43
44
|
|
|
45
|
+
0. **Skip entries with `#superseded-by:<id>`.** These are historical entries replaced by a newer one; the SUMMARY should reflect current state, not chain history.
|
|
44
46
|
1. Most recent `#verified` date wins
|
|
45
47
|
2. Tiebreaker: most recent `#added` date
|
|
46
48
|
3. Tiebreaker: entries that contain "primary" / "main" / "core" / "use <X>" — these are typically the anchor facts
|
|
@@ -56,6 +58,7 @@ If a (scope, layer) is empty, omit the subsection entirely.
|
|
|
56
58
|
|
|
57
59
|
> Last compressed: 2026-07-09
|
|
58
60
|
> Total entries: 247 across 3 scopes
|
|
61
|
+
> This digest highlights key entries — open the referenced files under `_global/` and `scopes/` for the full text.
|
|
59
62
|
|
|
60
63
|
## Global (`_global/`)
|
|
61
64
|
|
|
@@ -93,6 +96,10 @@ If a (scope, layer) is empty, omit the subsection entirely.
|
|
|
93
96
|
- All DB queries go through repository pattern — [scopes/backend/CONVENTIONS.md#CONV-2026-03-01-b1d2]
|
|
94
97
|
```
|
|
95
98
|
|
|
99
|
+
## Staleness note
|
|
100
|
+
|
|
101
|
+
Between a `sync` and the next `compress`, `SUMMARY.md` may lag behind `.lore/*`. Treat entry files as source of truth; SUMMARY is a locating index only (see `references/workflows.md#query`).
|
|
102
|
+
|
|
96
103
|
## Idempotency
|
|
97
104
|
|
|
98
|
-
Running `compress` twice without intervening `sync`s produces identical content (modulo the `Last compressed:` date). This is intentional — compress is a pure projection of the underlying entries.
|
|
105
|
+
Running `compress` twice without intervening `sync`s produces identical content (modulo the `Last compressed:` date). This is intentional — compress is a pure projection of the underlying entries.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# lore workflows — operational specification
|
|
2
|
+
|
|
3
|
+
The step-by-step procedures for all seven lore commands. Load this file when executing any `lore <command>`; [`SKILL.md`](../SKILL.md) routes each user request to the section below. Each section also points to the reference that backs it (entry format, marker conventions, summary/audit templates, config, platform mirrors, history).
|
|
4
|
+
|
|
5
|
+
### `init` — Initialize the memory bank
|
|
6
|
+
|
|
7
|
+
Runs once per project (or to start over).
|
|
8
|
+
|
|
9
|
+
0. **Resolve targets and takeover check.** Targets are determined by the resolution algorithm — see `references/platform-mirrors.md`. `init` **always** asks the user via multi-select which agents they use (pre-selected: agents whose platform files already exist or are already lore mirrors), so additional agents can be added even when files were detected; the resolution algorithm's silent return-on-detect applies to `mirror` / `compress`, not `init`. Explicit `mirror_targets` in `.lore/.config.json` overrides auto-detect (Replace semantics). For each resolved target:
|
|
10
|
+
- If the file does not exist -> no action; it will be created later in step 7.
|
|
11
|
+
- If the file exists AND contains a `## Lore` section -> it's already a lore mirror; note it and continue (its My notes will be processed as seed in step 5).
|
|
12
|
+
- If the file exists AND does NOT contain a `## Lore` section -> it's likely from the agent's native `/init` or hand-written. Show the user:
|
|
13
|
+
- (a) **Take over** — rewrite the file as a two-section mirror. The existing content becomes the My notes section (preserved verbatim, treated as seed knowledge in step 5).
|
|
14
|
+
- (b) **Preserve as-is** — leave the file alone. Remove it from `mirror_targets` for this project (lore won't write to it). `.lore/` is still generated normally; the user can read `SUMMARY.md` directly or merge manually later.
|
|
15
|
+
- (c) **Abort** — exit init. Nothing is created. The user can decide later.
|
|
16
|
+
- Repeat for each resolved target before proceeding.
|
|
17
|
+
1. Check if `.lore/` already exists. If yes, warn and ask: archive the current one and re-init, or abort?
|
|
18
|
+
2. Detect monorepo structure (per `references/monorepo-detection.md`). Propose scope list to the user; let them rename / merge / split before proceeding. No monorepo -> `_global/` only.
|
|
19
|
+
3. Scan the project (per scope if applicable):
|
|
20
|
+
- Top-level structure, entry points, package manager, language version
|
|
21
|
+
- Config files: `package.json`, `pyproject.toml`, `Cargo.toml`, `tsconfig.json`, `Dockerfile`, `Makefile`, CI
|
|
22
|
+
- `README*`, `CONTRIBUTING*`, existing docs
|
|
23
|
+
- Key dependencies from lockfiles
|
|
24
|
+
4. Write proposals to `.lore/draft/` mirroring the target layout (`_global/` and per-scope subdirs). Classify scanned facts per the Layer semantics table in `SKILL.md`, and apply the same layer checks as sync step 3: a picked-over-alternative with a reason is a `DEC` entry, not ARCH; a rule future agents must follow is a `CONV` entry, not ARCH or code comments. Every entry gets `#added:<today>` and a deterministic hash-based ID (see `references/entry-format.md`).
|
|
25
|
+
5. For any mirror file that already has a `## Lore` section (from step 0), read its My notes section as user-supplied seed knowledge. Parse as atomic bullets into the right layer/scope.
|
|
26
|
+
6. **Stop and show the user a summary**: which scopes, how many entries per layer per scope, sample of 5-10 entries, and what mirror files will be (re)generated (or skipped per step 0).
|
|
27
|
+
7. On user confirmation: `mv .lore/draft/* .lore/`, run an initial `compress` to generate `SUMMARY.md`, then (re)generate platform mirrors per the two-section structure — auto-create missing files, refresh Lore sections, leave My notes sections intact. Skip any target the user chose "preserve as-is" in step 0.
|
|
28
|
+
8. On user rejection: `rm -rf .lore/draft/`. Nothing persists.
|
|
29
|
+
|
|
30
|
+
The `draft/` directory gives a clean rollback path: nothing in `.lore/` is real until the user approves.
|
|
31
|
+
|
|
32
|
+
### `sync` — Update after a change
|
|
33
|
+
|
|
34
|
+
Runs after the user completes a feature, refactor, or bug fix.
|
|
35
|
+
|
|
36
|
+
**Trigger threshold — only propose sync when at least one is true:**
|
|
37
|
+
- `git diff --stat HEAD` shows 50+ changed lines across 2+ directories
|
|
38
|
+
- A new top-level module / directory / dependency was added or removed
|
|
39
|
+
- A new convention was explicitly discussed (e.g. user said "from now on we use X")
|
|
40
|
+
- The user explicitly invokes `sync` regardless of diff size
|
|
41
|
+
|
|
42
|
+
Pure typo fixes, lockfile-only changes, README rewording, or tweaks below the 50-line / 2-directory threshold do **not** warrant `sync`.
|
|
43
|
+
|
|
44
|
+
**Compress threshold check (silent, runs before sync proposal):**
|
|
45
|
+
- Total entry count across all files > 500, **or**
|
|
46
|
+
- `SUMMARY.md` is missing, **or**
|
|
47
|
+
- `SUMMARY.md` last `Last compressed:` date is > 30 days ago
|
|
48
|
+
|
|
49
|
+
If any of these are true, the skill appends a `[COMPRESS NOTICE]` to the sync proposal. It does not block the sync — the user can defer.
|
|
50
|
+
|
|
51
|
+
**Procedure:**
|
|
52
|
+
|
|
53
|
+
1. **Detect the delta** from two sources, combined and de-duplicated:
|
|
54
|
+
- `git diff <last_sync_sha>..HEAD` if `.lore/.config.json#last_sync_sha` is set and reachable from any local ref. This captures every commit since the last successful `sync`.
|
|
55
|
+
- `git diff` (working tree vs. `HEAD`) — always included. Catches uncommitted changes that are not yet in any commit.
|
|
56
|
+
- **Re-scan any new files**.
|
|
57
|
+
- **Fallback** when `last_sync_sha` is absent (older config) or no longer reachable (e.g. after `git rebase` or a force-push that orphaned the SHA): use `git diff HEAD` alone and emit a one-line `[WARN]` to stderr noting that incremental sync is degraded. Working tree alone will not pick up commits made before the next sync ran — the user should re-run `sync` after `git pull --rebase` to re-establish the baseline.
|
|
58
|
+
- **Empty repo** (no commits yet): `last_sync_sha` is `null`; only the working tree diff applies.
|
|
59
|
+
2. **Determine target scope(s)** for each change. Use `git diff --name-only` paths (over the combined commit + working-tree diff) to map files -> scopes (e.g. `frontend/src/...` -> `scopes/frontend/`). Cross-scope changes (root config files) -> `_global/`. If a change introduces a scope with no directory under `.lore/scopes/` yet, create `scopes/<name>/ARCHITECTURE.md`, `DECISIONS.md`, and `CONVENTIONS.md` (same layout as `init`) and route the entries there.
|
|
60
|
+
3. **Classify each change** into one layer:
|
|
61
|
+
- New module, new dependency, new file structure -> `ARCHITECTURE.md`
|
|
62
|
+
- "We picked X over Y because Z" -> `DECISIONS.md`
|
|
63
|
+
- New lint rule, new naming pattern, new "we never do X" -> `CONVENTIONS.md`
|
|
64
|
+
- Boundary: the choice itself ("we use X") -> `ARCHITECTURE.md`; the reasoning ("why X over Y") -> `DECISIONS.md`. If both apply, write two entries and cross-reference them by ID.
|
|
65
|
+
- **Decision check (mandatory before step 4).** Ask explicitly for every change: did we pick one option over an alternative ("use X instead of Y", "we chose X over Y", "reason: ..."), or does any draft `ARCHITECTURE` text explain *why* (signals: `reason:`, `because`, `for <purpose>`, any mention of an alternative)? If yes, that reasoning is a `DEC` candidate and must not be silently folded into an ARCH entry — emit the fact in `ARCHITECTURE.md` and the reasoning in `DECISIONS.md`, cross-referenced by ID. If you conclude no DEC is warranted, state that explicitly in the proposal so the user can veto.
|
|
66
|
+
- **Convention check (mandatory before step 4).** Ask explicitly for every change: does it introduce or change a *rule* future agents must follow — a lint/format/tool-config policy, a naming or structural pattern ("every X must Y"), a "we never do X", or an implicit rule visible in code (guard/validation logic, `must`/`required` checks, new or updated tool config)? If yes, that rule is a `CONV` candidate; it must not be silently folded into an ARCH entry or left only in code and comments. Signals: `must`/`never`/`always`/`required`, changes to lint or tool config (e.g. `.eslintrc*`, `pyproject.toml` tool sections, `tsconfig.json` compiler options), repeated structural patterns, "from now on..." statements. If you conclude no CONV is warranted, state that explicitly in the proposal so the user can veto.
|
|
67
|
+
4. **For each candidate entry**:
|
|
68
|
+
- **Contradicts an existing entry** in the same scope/layer -> mark the old one `#stale:<today>` and `#superseded-by:<new-id>` (where `<new-id>` is the entry in this proposal that replaces it). Emit an `ALERT`.
|
|
69
|
+
- **No replacement entry exists yet** (user is removing a fact without substituting) -> mark the old one `#stale:<today>` only; the chain can be backfilled later.
|
|
70
|
+
- **Refines an existing entry** -> if the body is unchanged, update tags only (bump `#verified:<today>`) and keep the ID. If the body changes, write a new entry with a freshly hashed ID and mark the old one `#stale:<today>` + `#superseded-by:<new-id>` — the ID hashes the body, so a body rewrite always produces a new ID (see `references/entry-format.md`).
|
|
71
|
+
- **Genuinely new** -> append with `#added:<today>` and a new hash ID.
|
|
72
|
+
5. **De-duplicate**: before appending, run `python skill/scripts/find_duplicates.py --json` (when lore is installed as a skill the path is `<skill>/scripts/find_duplicates.py`) to identify any candidate entry that overlaps with existing entries (same hash, or Jaccard >= `--threshold`). For each match, skip the new entry and bump `#verified` on the existing one. If the new entry is genuinely different in meaning (the script flags but doesn't decide), keep both.
|
|
73
|
+
6. **Apply trust level** (controlled by `.lore/.config.json#sync_trust`, default `"medium"`):
|
|
74
|
+
|
|
75
|
+
| Change type | `high` | `medium` (default) | `low` |
|
|
76
|
+
|---|---|---|---|
|
|
77
|
+
| De-duplicate hit (same fact already present) | auto-apply | auto-apply | confirm |
|
|
78
|
+
| REFINED, tags only (body unchanged) | auto-apply | auto-apply | confirm |
|
|
79
|
+
| REFINED, body changed (new ID + supersede link) | auto-apply | confirm | confirm |
|
|
80
|
+
| `NEW` entry | auto-apply | confirm | confirm |
|
|
81
|
+
| `STALE` mark | auto-apply | confirm | confirm |
|
|
82
|
+
| `ALERT` | confirm | confirm | confirm |
|
|
83
|
+
|
|
84
|
+
Auto-applied changes are written silently and reported at the end. Confirmation-required changes are bundled into a single diff proposal and shown together.
|
|
85
|
+
7. **Generate the proposed diff** (for any confirmation-required changes) using the `[NEW]/[STALE]/[REFINED]/[ALERT]/[COMPRESS NOTICE]` markers. See `references/stale-new-markers.md` for the full convention and user reply semantics.
|
|
86
|
+
8. **Stop and wait for user confirmation** for any pending changes. Auto-applied changes need no confirmation.
|
|
87
|
+
9. After the user accepts, write to `.lore/*` only. **Do not** regenerate platform mirrors from `sync` (unless `sync_updates_mirror: true` is set in `.lore/.config.json`) — this is intentional. See "Mirror update triggers" in `SKILL.md` and the dedicated `lore mirror` command.
|
|
88
|
+
10. **Update `.lore/.config.json#last_sync_sha`** to the current `git rev-parse HEAD`. Idempotent: re-running sync without new commits writes the same SHA. If HEAD does not exist (empty repo), set to `null`. The field is optional and additive; older configs without it keep working through the fallback in step 1.
|
|
89
|
+
|
|
90
|
+
**Source priority** (when sources disagree):
|
|
91
|
+
|
|
92
|
+
1. Git diff of changed code (most reliable — shows what actually happened)
|
|
93
|
+
2. Static scan of new files (reliable for facts, not for intent)
|
|
94
|
+
3. Conversation context (lowest priority — see below)
|
|
95
|
+
4. Test/build output (auxiliary — only consulted if 1-3 are ambiguous)
|
|
96
|
+
|
|
97
|
+
**Conversation context is opt-in.** The skill does **not** automatically mine chat messages for memory updates. It only extracts from conversation when the user explicitly says things like "note this down" / "remember this" / "this is important". Reason: chat context is high-noise, and silent extraction creates false entries.
|
|
98
|
+
|
|
99
|
+
### `query` — Answer from memory
|
|
100
|
+
|
|
101
|
+
Read-only.
|
|
102
|
+
|
|
103
|
+
1. Determine which scope(s) the question targets:
|
|
104
|
+
- "this project" / "the whole codebase" / unspecified -> `_global/` first, then SUMMARY.md
|
|
105
|
+
- "frontend" / "in the web app" / "the React side" -> `scopes/frontend/`
|
|
106
|
+
- "backend" / "the API" -> `scopes/backend/`
|
|
107
|
+
- If ambiguous, search SUMMARY.md for clues.
|
|
108
|
+
2. Grep the target files for relevant entries. If multi-layer or multi-scope, check all relevant ones.
|
|
109
|
+
- **Skip entries with `#superseded-by:<id>`.** The replacement is current; the superseded entry is historical. Same filter `compress` applies (see `references/summary-template.md` selection rule 0). If the user's question is about how something evolved ("why did we switch from X to Y?"), use the `history` workflow instead — `history --follow-superseded <id>` walks the chain.
|
|
110
|
+
- **SUMMARY is an index, not a source of truth for a claim.** A one-line summary is a locating hint; when citing a fact or making a decision, read the full referenced entry (including its tags) in `_global/` or `scopes/` first.
|
|
111
|
+
3. If found: answer concisely, citing fully-qualified entry IDs (e.g. `[scopes/frontend/DECISIONS.md#DEC-2026-02-03-7c19]`). Mention `#verified` date.
|
|
112
|
+
4. If not found but inferable from the code: say so explicitly ("Not in memory, but inferable from `frontend/src/store/index.ts`..."). Offer to add it.
|
|
113
|
+
5. Never fabricate an entry. If memory doesn't have it, say it doesn't have it.
|
|
114
|
+
|
|
115
|
+
### `audit` — Check memory vs. reality
|
|
116
|
+
|
|
117
|
+
Read-only with respect to canonical memory. It reports drift without changing entries or `SUMMARY.md`, but it does write the dated report described below.
|
|
118
|
+
|
|
119
|
+
1. For each entry in `_global/*` and `scopes/*/*`, find the code/config it claims to describe (scoped to the relevant scope's source tree) and compare against current state.
|
|
120
|
+
2. Also flag: entries whose reference date — `#verified` if present, else `#added` — is older than 90 days. Run `python skill/scripts/find_stale.py --days=90 --json` (or `<skill>/scripts/find_stale.py` when installed) to enumerate them mechanically.
|
|
121
|
+
3. Write the report to `.lore/audit/audit-YYYY-MM-DD.md`, organized by scope. **Do not** mark anything as stale in the main files. **Do not** emit ALERT blocks. See `references/audit-template.md` for the full report format and severity definitions.
|
|
122
|
+
4. **Stop.** User reviews the report and decides what to do. To act on findings, the user runs `sync`.
|
|
123
|
+
|
|
124
|
+
This separation keeps `audit` honest: it observes, it does not edit. ALERT noise is contained to `sync` and `query`, where the agent is about to act on the memory.
|
|
125
|
+
|
|
126
|
+
### `compress` — Build the top-level summary
|
|
127
|
+
|
|
128
|
+
Long-term compression. Generates `SUMMARY.md` and, when `auto_mirror: true` (or the user accepts the per-target prompt), regenerates platform mirrors. Underlying ARCHITECTURE / DECISIONS / CONVENTIONS files are untouched.
|
|
129
|
+
|
|
130
|
+
1. Run `python skill/scripts/list_entries.py --json` (or `<skill>/scripts/list_entries.py` when installed) to enumerate every entry. Use the JSON output as the input for the selection step.
|
|
131
|
+
2. Optionally run `python skill/scripts/find_stale.py --json` to identify entries that shouldn't anchor the summary (recently-stale or long-unverified).
|
|
132
|
+
3. For each (scope, layer) pair, pick 3-5 most important entries using the selection rule in `references/summary-template.md`.
|
|
133
|
+
4. Write `SUMMARY.md` per the template in `references/summary-template.md`. (This is the only file written on the canonical `.lore/` side.)
|
|
134
|
+
5. If `auto_mirror: true` in config, regenerate platform mirrors (this is one of the three mirror update triggers — see "Mirror update triggers" in `SKILL.md`). If `auto_mirror: false`, ask per target and only write the mirrors the user accepts. Content-based dedup: if the new Lore section equals the current one, skip the write. The My notes section is always preserved.
|
|
135
|
+
6. **Stop.** Once mirror regeneration has either written or been declined per target, `compress` is done.
|
|
136
|
+
|
|
137
|
+
**Compress is idempotent.** Running it twice produces the same `SUMMARY.md` content (modulo the date stamp). Re-running after new `sync`s picks up new entries automatically.
|
|
138
|
+
|
|
139
|
+
### `mirror` — Regenerate platform mirrors
|
|
140
|
+
|
|
141
|
+
Regenerate all configured platform mirrors from the current state of `.lore/*`. Content-based dedup skips targets whose Lore section is unchanged.
|
|
142
|
+
|
|
143
|
+
1. Read current `.lore/SUMMARY.md` and the scope-tagged index.
|
|
144
|
+
2. For each configured mirror target (per `references/platform-mirrors.md`), read the existing file and detect the section boundary.
|
|
145
|
+
3. **Validate the two-section structure** for each target: if it lacks the `---` separator, lacks a `## My notes` section, or is a user-notes-only file without `## Lore`, stop for that target and ask the user how to proceed — never overwrite an anomalous file silently (section detection rules: `references/platform-mirrors.md`).
|
|
146
|
+
4. For each target, compare the new Lore section content against the existing one. **Skip writing if content is identical** (content-based dedup; avoids empty `git diff`).
|
|
147
|
+
5. If different, replace the Lore section; preserve the My notes section verbatim. If the user asked to wipe My notes, archive it to `.lore/.archive/<file>-<date>.md` first.
|
|
148
|
+
6. **Stop.** Report: "Mirror updated: `<file>`" or "No changes needed: `<file>`" per target.
|
|
149
|
+
|
|
150
|
+
This command exists because most users want `sync` to be fast and unobtrusive, but occasionally need the agent-facing files to reflect recent knowledge. `mirror` is that explicit "publish to agent view" step. Structure validation happens automatically during each regeneration (step 3), and a user-requested My notes wipe is handled as a normal conversation request.
|
|
151
|
+
|
|
152
|
+
### `history` — Show git commits related to a memory entry
|
|
153
|
+
|
|
154
|
+
Read-only. Surfaces the git history that backs a memory entry, a file, or a scope, so the agent can answer "why does this decision exist?" with a pointer to the actual commits rather than a guess.
|
|
155
|
+
|
|
156
|
+
**When to trigger:** only when the user explicitly invokes `lore history` or names a subcommand ("show me the git history", "show me the commits behind this entry"). Generic "history" or "git log" alone does not trigger — defer to the user's intent.
|
|
157
|
+
|
|
158
|
+
| User says (examples) | Command |
|
|
159
|
+
|---|---|
|
|
160
|
+
| "lore history DEC-2026-02-03-7c19" | `lore history <entry-id>` |
|
|
161
|
+
| "lore history frontend/src/store/index.ts" | `lore history <file-path>` |
|
|
162
|
+
| "lore history --scope=frontend" | `lore history --scope=<name>` |
|
|
163
|
+
|
|
164
|
+
**Procedure (entry form):**
|
|
165
|
+
|
|
166
|
+
1. Resolve project root (`.lore/` must exist), confirm git repo + git CLI on PATH.
|
|
167
|
+
2. Load the entry index (`list_entries.py --json`), locate the entry, derive `#added` as the default `--since` (fallback `1970-01-01`).
|
|
168
|
+
3. Resolve the code file (backtick path in entry text -> scope directory -> project root), run `git log`, render Markdown or JSON, print to stdout.
|
|
169
|
+
4. **Stop.** No files are written.
|
|
170
|
+
|
|
171
|
+
**Data source contract:** local git CLI only. No GitHub / GitLab API. No LLM call. The agent invoking the command does the semantic work (interpreting commit messages, deciding relevance).
|
|
172
|
+
|
|
173
|
+
**Relationship to other commands:** fills the previously-empty cell of "read git history" (other commands read either the current file system or `git diff` only).
|
|
174
|
+
|
|
175
|
+
Supported flags: `--since=<YYYY-MM-DD>`, `--follow-superseded`, `--json`. Full dispatch rules, `--since` normalization (same-day commit safety), output format, and the error/exit-code table live in `references/history-command.md`.
|
|
176
|
+
|
|
177
|
+
## Cross-workflow notes
|
|
178
|
+
|
|
179
|
+
**Who writes what:**
|
|
180
|
+
|
|
181
|
+
| File | Written by |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `.lore/SUMMARY.md` | `compress` (and by `init`, via its initial compress) |
|
|
184
|
+
| `.lore/{_global,scopes/<scope>}/<LAYER>.md` | `init`, `sync`, manual edits |
|
|
185
|
+
| `.lore/.config.json` | `init`, manual edits |
|
|
186
|
+
| `.lore/audit/audit-<date>.md` | `audit` |
|
|
187
|
+
| `.lore/draft/` | `init` (proposals; moved into `.lore/` on confirm, removed on reject) |
|
|
188
|
+
| `<project-root>/<platform files>` | `init`, `mirror`, `compress` (if `auto_mirror: true`), `sync` (if `sync_updates_mirror: true`) |
|
|
189
|
+
|
|
190
|
+
**What never happens silently:** file mutation (sync proposes; user accepts/rejects); platform mirror rewrite on every sync (separate command); `compress` deleting entries (only writes SUMMARY.md); entry marked as `[STALE]` without proposal; `init` overwriting user-written platform files without explicit takeover.
|
|
191
|
+
|
|
192
|
+
**Typical sequence:** `init` -> `[sync <-> query <-> audit]` (interchangeable, agent picks by context) -> `compress` (when SUMMARY.md grows stale) -> `mirror` (or auto via `compress` if `auto_mirror: true`).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# lore scripts
|
|
2
2
|
|
|
3
|
-
Cross-platform Python 3.6+ helpers that reduce repetitive mechanical work. No third-party dependencies. Called by `init` / `sync` / `audit` / `compress` / `
|
|
3
|
+
Cross-platform Python 3.6+ helpers that reduce repetitive mechanical work. No third-party dependencies. Called by `init` / `sync` / `query` / `audit` / `compress` / `history`; can also be run standalone for ad-hoc inspection.
|
|
4
4
|
|
|
5
5
|
The script list and quick-reference command examples live in the project root `README.md` "Scripts" section. This file covers the things that don't fit there: design intent, integration points, and limits.
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ The script list and quick-reference command examples live in the project root `R
|
|
|
10
10
|
|
|
11
11
|
**JSON-friendly output.** Every script supports `--json` for machine consumption. Agent callers parse the output; humans pipe to `less` or `jq` (if available).
|
|
12
12
|
|
|
13
|
-
**Composition.** `find_duplicates.py` and `
|
|
13
|
+
**Composition.** `find_duplicates.py`, `find_stale.py`, and `history.py` shell out to `list_entries.py --json` rather than re-implementing the parser. One source of truth for entry format — if the format ever changes, only `list_entries.py` needs updating.
|
|
14
14
|
|
|
15
15
|
**Read-only by default.** None of these scripts write to `.lore/`. They observe; the agent decides what to do with findings.
|
|
16
16
|
|
|
@@ -20,35 +20,36 @@ The script list and quick-reference command examples live in the project root `R
|
|
|
20
20
|
|
|
21
21
|
| Script | Call site | Purpose |
|
|
22
22
|
|---|---|---|
|
|
23
|
-
| `history.py` | lore history | List git commits related to a memory entry / file / scope |
|
|
23
|
+
| `history.py` | lore history | List git commits related to a memory entry / file / scope; with `--follow-superseded`, walks the `#superseded-by` chain forward |
|
|
24
24
|
| `id_hash.py` | Any time a new entry is written (init / sync) | Compute the 4-char content hash for the entry ID |
|
|
25
|
-
| `list_entries.py` | Pre-step of query / audit / compress | Enumerate all entries as JSON for downstream processing |
|
|
25
|
+
| `list_entries.py` | Pre-step of query / audit / compress / history | Enumerate all entries as JSON for downstream processing; emits `replaced_by` per entry when `#superseded-by` is present |
|
|
26
26
|
| `find_duplicates.py` | sync step 5 (de-duplication) | Identify candidate duplicate entries before writing |
|
|
27
|
-
| `find_stale.py` | audit step 2; compress step 2
|
|
27
|
+
| `find_stale.py` | audit step 2; compress step 2 | Identify entries past the reference-date threshold (`#verified` if present, else `#added`) or superseded (carrying `#stale` or `#superseded-by`, which implies staleness); groups pending-review entries by their `#superseded-by` target and reports `BROKEN_CHAIN` orphans |
|
|
28
28
|
|
|
29
29
|
## Output channels
|
|
30
30
|
|
|
31
|
-
**stdout is the data channel; stderr is the warning channel.** All scripts follow this split so `--json` consumers never have to filter noise out of their parsers.
|
|
31
|
+
**stdout is the data channel; stderr is the warning channel.** All scripts follow this split so `--json` consumers never have to filter noise out of their parsers. `list_entries.py` is the only script that emits warnings, all on stderr:
|
|
32
32
|
|
|
33
33
|
- `[WARN] .lore/.config.json has no schema_version field.` — fires once per invocation when the config file exists but lacks the version field. Add `"schema_version": 1` to silence it.
|
|
34
34
|
- `[WARN] .lore/.config.json#schema_version=N is newer than this lore skill expects (max: 1).` — fires when the config version exceeds what this skill understands. Pull the latest lore from upstream.
|
|
35
|
+
- `[WARN] entry <id> carries multiple #superseded-by tags; keeping the first only.` — fires when one entry has more than one valid `#superseded-by` tag.
|
|
36
|
+
- `[WARN] entry <id> has a malformed #superseded-by value '<value>' (expected LAYER-YYYY-MM-DD-xxxx); chain not resolved.` — fires when a `#superseded-by` value is not a valid entry ID. The tag stays in the entry text and `replaced_by` stays `None`.
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
All warnings are informational; `list_entries.py` always produces the same stdout regardless of config state. See `references/compatibility.md` for the full schema versioning policy.
|
|
37
39
|
|
|
38
40
|
## Testing
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
Regression tests live in `tests/` (stdlib-only `unittest`, black-box subprocess runs of the real scripts). Run from the repo root:
|
|
41
43
|
|
|
42
44
|
```bash
|
|
43
|
-
python
|
|
44
|
-
python scripts/list_entries.py # should print "(no entries)" or exit with a clear error
|
|
45
|
+
python -m unittest discover -s tests -v
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
The suite builds isolated `.lore/` fixtures in temp directories; the `history.py` cases create throwaway git repos and skip automatically when git is not on PATH.
|
|
48
49
|
|
|
49
50
|
## Limitations
|
|
50
51
|
|
|
51
52
|
- **Token-overlap dedup, not semantic.** Jaccard similarity catches rewrites with similar words but misses semantic equivalence (e.g. "use TypeScript" vs "TypeScript-only codebase"). Deeper checks still need an LLM pass.
|
|
52
53
|
- **Naive date math.** `find_stale.py` uses wall-clock dates from `#verified` / `#added` tags. If the system's clock is wrong, results will be off.
|
|
53
|
-
- **No automatic
|
|
54
|
-
- **Hash collisions on identical text are theoretically possible** (4 hex chars = 16 bits = 1 in 65536). In practice a lore project will not hit this. If it does, slightly edit the entry text to bump the hash.
|
|
54
|
+
- **No automatic archival.** The script reports superseded entries (tagged `#stale`, or carrying `#superseded-by`) and broken chains but does not move or delete anything. Outdated entries stay in their scope file with their tags; git history preserves the rest.
|
|
55
|
+
- **Hash collisions on identical text are theoretically possible** (4 hex chars = 16 bits = 1 in 65536). In practice a lore project will not hit this. If it does, slightly edit the entry text to bump the hash.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# lore 脚本
|
|
2
2
|
|
|
3
|
-
跨平台 Python 3.6+ 辅助脚本,减少重复的机械工作。无第三方依赖。被 `init` / `sync` / `audit` / `compress` / `
|
|
3
|
+
跨平台 Python 3.6+ 辅助脚本,减少重复的机械工作。无第三方依赖。被 `init` / `sync` / `query` / `audit` / `compress` / `history` 调用,也可独立运行做临时检查。
|
|
4
4
|
|
|
5
5
|
脚本清单和命令速查在仓库根 `README.md` 的"Scripts"章节里。本文件覆盖根 README 不适合放的内容:设计意图、集成点、局限。
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
**JSON 友好输出。** 每个脚本都支持 `--json` 便于机器消费。Agent 调用方解析输出;人类可以直接 `less` 或 `jq`(如果装了)。
|
|
12
12
|
|
|
13
|
-
**组合而非重复。** `find_duplicates.py` 和 `
|
|
13
|
+
**组合而非重复。** `find_duplicates.py`、`find_stale.py` 和 `history.py` 通过 `list_entries.py --json` 复用解析器,不重复实现 entry 格式解析。Entry 格式只在一处定义——将来格式变更只需改 `list_entries.py`。
|
|
14
14
|
|
|
15
15
|
**默认只读。** 这些脚本不写 `.lore/`,只观察。Agent 决定如何处理发现的问题。
|
|
16
16
|
|
|
@@ -20,35 +20,36 @@
|
|
|
20
20
|
|
|
21
21
|
| 脚本 | 调用点 | 用途 |
|
|
22
22
|
|---|---|---|
|
|
23
|
-
| `history.py` | lore history | 列出与 memory entry / file / scope 相关的 git commits |
|
|
23
|
+
| `history.py` | lore history | 列出与 memory entry / file / scope 相关的 git commits;带 `--follow-superseded` 时沿 `#superseded-by` 链向前遍历 |
|
|
24
24
|
| `id_hash.py` | 写新 entry 时(init / sync)| 计算 entry ID 的 4 字符内容 hash |
|
|
25
|
-
| `list_entries.py` | query / audit / compress 的预步骤 | 把所有 entry 枚举为 JSON
|
|
25
|
+
| `list_entries.py` | query / audit / compress / history 的预步骤 | 把所有 entry 枚举为 JSON 供后续处理;当 entry 含 `#superseded-by` 时额外输出 `replaced_by` 字段 |
|
|
26
26
|
| `find_duplicates.py` | sync 步骤 5(去重)| 写之前找出可能的重复 entry |
|
|
27
|
-
| `find_stale.py` | audit 步骤 2;compress 步骤 2
|
|
27
|
+
| `find_stale.py` | audit 步骤 2;compress 步骤 2 | 找出参考日期(有 `#verified` 用 `#verified`,否则用 `#added`)过期的 entry,或已被取代的 entry(带 `#stale` 或 `#superseded-by`,后者隐式表示过时);按 `#superseded-by` 目标对 pending-review 分组,并报告 `BROKEN_CHAIN` 孤儿 |
|
|
28
28
|
|
|
29
29
|
## 输出通道
|
|
30
30
|
|
|
31
|
-
**stdout 是数据通道;stderr 是警告通道。** 所有脚本遵循这个分离,这样 `--json`
|
|
31
|
+
**stdout 是数据通道;stderr 是警告通道。** 所有脚本遵循这个分离,这样 `--json` 消费者就不必从解析结果里过滤噪音。只有 `list_entries.py` 会发警告,全部走 stderr:
|
|
32
32
|
|
|
33
33
|
- `[WARN] .lore/.config.json has no schema_version field.` —— 配置文件存在但缺 `schema_version` 字段时,每个调用触发一次。加 `"schema_version": 1` 即可消除。
|
|
34
34
|
- `[WARN] .lore/.config.json#schema_version=N is newer than this lore skill expects (max: 1).` —— 配置版本超过本 skill 能理解的范围时触发。从上游 pull 最新 lore。
|
|
35
|
+
- `[WARN] entry <id> carries multiple #superseded-by tags; keeping the first only.` —— 一个 entry 上出现多个合法的 `#superseded-by` 标签时触发。
|
|
36
|
+
- `[WARN] entry <id> has a malformed #superseded-by value '<value>' (expected LAYER-YYYY-MM-DD-xxxx); chain not resolved.` —— `#superseded-by` 的值不是合法 entry ID 时触发;标签保留在文本里,`replaced_by` 保持 `None`。
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
所有警告都是告知性质;`list_entries.py` 不管配置状态如何,stdout 输出始终一致。完整 schema 版本策略见 `references/compatibility.md`。
|
|
37
39
|
|
|
38
40
|
## 测试
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
回归测试在 `tests/`(纯 stdlib `unittest`,以子进程黑盒方式调用真实脚本)。在仓库根目录运行:
|
|
41
43
|
|
|
42
44
|
```bash
|
|
43
|
-
python
|
|
44
|
-
python scripts/list_entries.py # 应输出 "(no entries)" 或清晰报错
|
|
45
|
+
python -m unittest discover -s tests -v
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
套件会在临时目录里搭建独立的 `.lore/` 夹具;`history.py` 的用例会创建一次性 git 仓库(PATH 上没有 git 时自动跳过)。
|
|
48
49
|
|
|
49
50
|
## 局限
|
|
50
51
|
|
|
51
52
|
- **去重只到词袋重叠程度。** Jaccard 相似度能抓到词汇相似的改写,但抓不到语义等价(如 "use TypeScript" vs "TypeScript-only codebase")。更深的检查仍需 LLM 介入。
|
|
52
53
|
- **日期计算比较朴素。** `find_stale.py` 直接用 `#verified` / `#added` 标签的日期。如果系统时钟不对,结果会偏差。
|
|
53
|
-
- **不自动 archive。**
|
|
54
|
-
- **理论上可能有 hash 冲突**(4 个十六进制字符 = 16 位 = 1/65536 概率)。实际项目基本不会遇到。如果遇到了,对 entry 文本做微调以改变 hash。
|
|
54
|
+
- **不自动 archive。** 脚本会报告已被取代的 entry(带 `#stale`,或带 `#superseded-by`)和坏链,但不会移动或删除任何东西。过期的 entry 留在原 scope 文件、保留原 tag;git 历史保留全部。
|
|
55
|
+
- **理论上可能有 hash 冲突**(4 个十六进制字符 = 16 位 = 1/65536 概率)。实际项目基本不会遇到。如果遇到了,对 entry 文本做微调以改变 hash。
|