opencode-skills-collection 4.0.41 → 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 +5 -2
- package/bundled-skills/animejs-animation/SKILL.md +1 -1
- package/bundled-skills/anti-ui-slop/SKILL.md +51 -86
- package/bundled-skills/antigravity-design-expert/SKILL.md +1 -1
- package/bundled-skills/design-spells/SKILL.md +1 -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/famulor-skill/SKILL.md +132 -0
- package/bundled-skills/famulor-skill/references/assistant-design.md +137 -0
- package/bundled-skills/famulor-skill/references/toolsets/assistants.md +64 -0
- package/bundled-skills/famulor-skill/references/toolsets/automations.md +36 -0
- package/bundled-skills/famulor-skill/references/toolsets/billing.md +15 -0
- package/bundled-skills/famulor-skill/references/toolsets/calls.md +23 -0
- package/bundled-skills/famulor-skill/references/toolsets/campaigns.md +42 -0
- package/bundled-skills/famulor-skill/references/toolsets/dashboards.md +27 -0
- package/bundled-skills/famulor-skill/references/toolsets/knowledge.md +28 -0
- package/bundled-skills/famulor-skill/references/toolsets/messaging.md +52 -0
- package/bundled-skills/famulor-skill/references/toolsets/migration.md +10 -0
- package/bundled-skills/famulor-skill/references/toolsets/platform.md +14 -0
- package/bundled-skills/famulor-skill/references/toolsets/settings.md +28 -0
- package/bundled-skills/famulor-skill/references/toolsets/tasks.md +12 -0
- package/bundled-skills/famulor-skill/references/toolsets/telephony.md +35 -0
- package/bundled-skills/iconsax-library/SKILL.md +1 -1
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +4 -4
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +1 -1
- 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/magic-animator/SKILL.md +1 -1
- package/bundled-skills/magic-ui-generator/SKILL.md +1 -1
- package/bundled-skills/poka-yoke/SKILL.md +172 -0
- package/bundled-skills/project-state-governor/SKILL.md +483 -0
- package/bundled-skills/project-state-governor/references/manifest-routing.md +47 -0
- package/bundled-skills/project-state-governor/references/persistence-lifecycle.md +93 -0
- package/bundled-skills/project-state-governor/references/project-state-schema.md +173 -0
- package/bundled-skills/project-state-governor/references/reconstruction-workflow.md +183 -0
- package/bundled-skills/salesforce-development/SKILL.md +107 -37
- 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/bundled-skills/unsplash-integration/SKILL.md +1 -1
- package/bundled-skills/vizcom/SKILL.md +1 -1
- package/bundled-skills/x-twitter-scraper/SKILL.md +20 -7
- package/package.json +1 -1
- package/skills_index.json +136 -36
- 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
- package/bundled-skills/uizze-ui-research/SKILL.md +0 -128
|
@@ -4,18 +4,18 @@ This document defines how `lore` evolves without breaking existing user projects
|
|
|
4
4
|
|
|
5
5
|
## Three principles
|
|
6
6
|
|
|
7
|
-
1. **Add, never subtract.** New fields, scripts, sections, and reference docs always use new names. Removal
|
|
7
|
+
1. **Add, never subtract.** New fields, scripts, sections, and reference docs always use new names. Removal is a breaking change: the commit message must be prefixed `BREAKING:` and explain what users need to change.
|
|
8
8
|
2. **Readers are forward-compatible.** An older skill reading a newer `.lore/` ignores unknown fields, unknown files, and unknown tags. It never errors on unfamiliar content.
|
|
9
|
-
3. **Writers are backward-compatible (during transition).** A newer skill detecting an older `.lore/`
|
|
9
|
+
3. **Writers are backward-compatible (during transition).** A newer skill detecting an older `.lore/` reads the older shape, fills missing fields with defaults, and writes only what it intended to change. It never overwrites old data with new defaults.
|
|
10
10
|
|
|
11
11
|
## Layer-specific rules
|
|
12
12
|
|
|
13
13
|
### Layer 1: `.lore/.config.json` schema
|
|
14
14
|
|
|
15
15
|
- `schema_version` is **required** (integer). See `references/config.md` for handling missing/newer/older values.
|
|
16
|
-
- Adding a new field
|
|
17
|
-
- Removing a field
|
|
18
|
-
- Renaming a field:
|
|
16
|
+
- Adding a new optional field does not require a schema bump: old readers ignore unknown fields, new readers fill missing fields with defaults. See "Rule of thumb" under Examples below.
|
|
17
|
+
- Removing a field is a breaking change. The commit must be prefixed `BREAKING:` and name the field and the migration step the user must take.
|
|
18
|
+
- Renaming a field: keep both fields for one release, mark the old field as deprecated in the commit message, and remove in the next breaking release. The user edits their config by hand.
|
|
19
19
|
|
|
20
20
|
### Layer 2: Entry format
|
|
21
21
|
|
|
@@ -23,8 +23,8 @@ This document defines how `lore` evolves without breaking existing user projects
|
|
|
23
23
|
- [ARCH-2026-07-10-a3f2] Entry text; reason. #added:2026-07-10 #verified:2026-07-15
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
- IDs (`LAYER-DATE-HASH`) are stable as long as the entry text is unchanged. Editing an entry produces a new ID; old ID stays in history (via git) for `history` queries.
|
|
27
|
-
- Tag set is a closed set today: `#added`, `#verified`, `#stale`, `#
|
|
26
|
+
- IDs (`LAYER-DATE-HASH`) are stable as long as the entry text is unchanged. Editing an entry produces a new ID; old ID stays in history (via git) for `history` queries. `sync`'s `[REFINED]` respects this: tags-only updates keep the ID, body rewrites create a new ID and link the old entry via `#superseded-by`.
|
|
27
|
+
- Tag set is a closed set today: `#added`, `#verified`, `#stale`, `#superseded-by`. Adding a new tag is allowed; old skills' tag parsers (which match `(added|verified|stale|superseded-by)`) silently ignore unknown tags. The previous `#archived` tag is no longer part of the vocabulary; old entries carrying it are treated as unknown tags (still parse, semantic meaning is lost — use `#superseded-by` going forward).
|
|
28
28
|
- **Never make a tag required.** Required tags break every old entry in every old `.lore/`.
|
|
29
29
|
|
|
30
30
|
### Layer 3: `.lore/` directory structure
|
|
@@ -34,38 +34,40 @@ Current canonical layout:
|
|
|
34
34
|
```
|
|
35
35
|
.lore/
|
|
36
36
|
├── SUMMARY.md
|
|
37
|
+
├── .config.json
|
|
37
38
|
├── _global/
|
|
38
39
|
├── scopes/
|
|
40
|
+
├── .archive/ (My notes backups before a user-requested wipe)
|
|
39
41
|
├── draft/ (init only — temporary)
|
|
40
|
-
|
|
41
|
-
└── archive/ (referenced in spec; reserved for future)
|
|
42
|
+
└── audit/ (audit only)
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
Rules:
|
|
45
|
-
|
|
46
46
|
- Adding a new top-level directory (e.g., `rejected/` for rejected entries) is non-breaking.
|
|
47
47
|
- Renaming an existing directory is breaking — every reference in `references/*.md`, every script, and every user's project breaks.
|
|
48
|
-
- Removing a directory is breaking
|
|
48
|
+
- Removing a directory is non-breaking if it was never actually written. The previous `archive/` directory fell into this category and has been removed from the layout. Note: when the user asks to wipe a mirror's My notes, lore archives the old content to `.lore/.archive/` first (see `references/platform-mirrors.md`); that directory is part of the layout above and is **not** an entries archive — it is a user-confirmed backup location only.
|
|
49
49
|
|
|
50
50
|
### Layer 4: Python scripts
|
|
51
51
|
|
|
52
|
-
Current scripts: `id_hash.py`, `list_entries.py`, `find_stale.py`, `find_duplicates.py`, `history.py
|
|
52
|
+
Current scripts: `id_hash.py`, `list_entries.py`, `find_stale.py`, `find_duplicates.py`, `history.py`.
|
|
53
53
|
|
|
54
54
|
Rules:
|
|
55
55
|
|
|
56
|
-
- **Renaming is breaking.** All names are part of the public surface; they're referenced from `SKILL.md`, `references/*.md`, and downstream tooling. Don't rename;
|
|
57
|
-
- **Removing is breaking.**
|
|
56
|
+
- **Renaming is breaking.** All names are part of the public surface; they're referenced from `SKILL.md`, `references/*.md`, and downstream tooling. Don't rename; add a new one with a different name if needed.
|
|
57
|
+
- **Removing is breaking.** When a script is removed, the commit is prefixed `BREAKING:` and names the replacement. The removed file is deleted in the same commit.
|
|
58
58
|
- **Adding a new script is non-breaking.** Reference it from `SKILL.md` reference index on introduction.
|
|
59
|
-
- **Changing output format is breaking for `--json` consumers.** Add `--v2-output`
|
|
59
|
+
- **Changing output format is breaking for `--json` consumers.** Add a new flag (e.g. `--v2-output`) rather than changing existing output; the old flag keeps old behavior forever.
|
|
60
60
|
|
|
61
61
|
### Layer 5: Platform mirror files
|
|
62
62
|
|
|
63
63
|
Mirror files (`CLAUDE.md`, `.cursorrules`, `AGENTS.md`, etc.) follow this contract:
|
|
64
64
|
|
|
65
65
|
```markdown
|
|
66
|
+
<!-- LORE:START -->
|
|
66
67
|
## Lore (auto-managed)
|
|
67
68
|
|
|
68
69
|
... lore content ...
|
|
70
|
+
<!-- LORE:END -->
|
|
69
71
|
|
|
70
72
|
---
|
|
71
73
|
## My notes (free edit)
|
|
@@ -75,16 +77,18 @@ Mirror files (`CLAUDE.md`, `.cursorrules`, `AGENTS.md`, etc.) follow this contra
|
|
|
75
77
|
|
|
76
78
|
Rules:
|
|
77
79
|
|
|
78
|
-
-
|
|
80
|
+
- `<!-- LORE:START -->` and `<!-- LORE:END -->` are **contract strings** for new mirrors (post-v1). Never rename; never remove. They are the authoritative boundary the skill uses for detection.
|
|
81
|
+
- `## Lore (auto-managed)` is a **contract string**. Never rename; never remove. Mirror detection regexes depend on it as a secondary signal.
|
|
79
82
|
- `## My notes (free edit)` is a **contract string**. Never rename; never remove. User-written content depends on it.
|
|
80
|
-
- The content between
|
|
81
|
-
-
|
|
83
|
+
- The content between `<!-- LORE:START -->` and `<!-- LORE:END -->` is lore's domain; content after `<!-- LORE:END -->` and `---` is the user's. Respect the boundary on every regeneration.
|
|
84
|
+
- Pre-v1 mirrors without HTML comments are still detected and preserved via the `---` separator and `## My notes` header. The first `lore mirror` run on such a file offers the user an upgrade prompt (see `references/platform-mirrors.md` rule 5b).
|
|
85
|
+
- Adding a new auto-managed section (e.g., `## Sync history (auto-managed)`) is allowed; insert before `<!-- LORE:END -->`. Old skills ignore it.
|
|
82
86
|
- Changing the index template body (e.g., adding a "Last mirror:" line) is non-breaking: content-based dedup means unchanged mirrors are not rewritten, so old mirrors stay valid.
|
|
83
87
|
- **Backward-write safety**: if the existing mirror has no `## My notes (free edit)` section (e.g., a legacy single-section mirror from a pre-v1 project), the first `lore mirror` run must **append** an empty My notes section rather than overwriting the file.
|
|
84
88
|
|
|
85
89
|
### Layer 6: reference docs
|
|
86
90
|
|
|
87
|
-
Current docs: `entry-format.md`, `summary-template.md`, `audit-template.md`, `monorepo-detection.md`, `stale-new-markers.md`, `platform-mirrors.md`, `config.md`, `history-command.md`, `compatibility.md` (this file).
|
|
91
|
+
Current docs: `workflows.md`, `entry-format.md`, `summary-template.md`, `audit-template.md`, `monorepo-detection.md`, `stale-new-markers.md`, `platform-mirrors.md`, `config.md`, `history-command.md`, `compatibility.md` (this file).
|
|
88
92
|
|
|
89
93
|
Rules:
|
|
90
94
|
|
|
@@ -93,122 +97,47 @@ Rules:
|
|
|
93
97
|
- **Removing a doc** is breaking. Mark it `<!-- DEPRECATED: see new-location.md -->` for one schema version, then move to `archive/` (in `references/`, not in `.lore/`).
|
|
94
98
|
- **Adding a doc** is non-breaking. Add to `SKILL.md` reference index on introduction.
|
|
95
99
|
|
|
96
|
-
## Migration
|
|
97
|
-
|
|
98
|
-
`scripts/migrate.py` does not exist in v1. It will be added on the first `schema_version` bump.
|
|
99
|
-
|
|
100
|
-
**Triggers that will ship the first migration:**
|
|
101
|
-
|
|
102
|
-
- A new required field is added to `.lore/.config.json`.
|
|
103
|
-
- A field is renamed or its accepted values are tightened.
|
|
104
|
-
- The entry ID algorithm changes.
|
|
105
|
-
|
|
106
|
-
Until any of these happen, `schema_version: 1` is the only version and no migration is possible or needed.
|
|
107
|
-
|
|
108
|
-
Until the script ships, `list_entries.py` emits a one-time warning to stderr if `.lore/.config.json` is missing the `schema_version` field. This nudges users to add the field manually before the first breaking change ships, so future upgrades can detect the version mismatch correctly.
|
|
109
|
-
|
|
110
|
-
**Template — to be implemented when the first migration is needed:**
|
|
111
|
-
|
|
112
|
-
```python
|
|
113
|
-
#!/usr/bin/env python3
|
|
114
|
-
"""Migrate .lore/.config.json from version N to N+1.
|
|
115
|
-
|
|
116
|
-
Idempotent: running twice produces no further changes.
|
|
117
|
-
Refuses to run if schema_version > EXPECTED_FROM.
|
|
118
|
-
"""
|
|
119
|
-
import json, sys
|
|
120
|
-
from pathlib import Path
|
|
121
|
-
|
|
122
|
-
EXPECTED_FROM = 1 # versions this script can read
|
|
123
|
-
TARGET = 2 # current schema version after migration
|
|
124
|
-
|
|
100
|
+
## Migration
|
|
125
101
|
|
|
126
|
-
|
|
127
|
-
msgs = []
|
|
128
|
-
# v1 → v2 transformations go here. Example:
|
|
129
|
-
# if config.get("mirror_mode") == "summary":
|
|
130
|
-
# config.pop("mirror_mode", None)
|
|
131
|
-
# msgs.append('removed deprecated "mirror_mode": "summary"')
|
|
132
|
-
config["schema_version"] = TARGET
|
|
133
|
-
return config, msgs
|
|
102
|
+
There is no automatic migration tool in v1. Upgrades are `git pull` + read the commit history for any `BREAKING:` commits. The user edits their config by hand if a field was renamed or removed.
|
|
134
103
|
|
|
104
|
+
`list_entries.py` emits a one-time `[WARN]` to stderr if `.lore/.config.json` is missing the `schema_version` field. Add `"schema_version": 1` manually to silence it.
|
|
135
105
|
|
|
136
|
-
|
|
137
|
-
cfg_path = Path(".lore/.config.json")
|
|
138
|
-
if not cfg_path.exists():
|
|
139
|
-
print(f"error: {cfg_path} not found", file=sys.stderr)
|
|
140
|
-
return 1
|
|
141
|
-
cfg = json.loads(cfg_path.read_text(encoding="utf-8"))
|
|
142
|
-
current = cfg.get("schema_version", 1)
|
|
143
|
-
if current > EXPECTED_FROM:
|
|
144
|
-
print(
|
|
145
|
-
f"error: schema_version={current} is newer than this skill "
|
|
146
|
-
f"can read (max: {EXPECTED_FROM}). Pull latest lore.",
|
|
147
|
-
file=sys.stderr,
|
|
148
|
-
)
|
|
149
|
-
return 2
|
|
150
|
-
if current < TARGET:
|
|
151
|
-
print(f"migrating v{current} → v{TARGET}...")
|
|
152
|
-
cfg, msgs = migrate(cfg)
|
|
153
|
-
for m in msgs:
|
|
154
|
-
print(f" - {m}")
|
|
155
|
-
cfg_path.write_text(json.dumps(cfg, indent=2) + "\n", encoding="utf-8")
|
|
156
|
-
print("done.")
|
|
157
|
-
return 0
|
|
106
|
+
## Deprecation
|
|
158
107
|
|
|
159
|
-
|
|
160
|
-
if __name__ == "__main__":
|
|
161
|
-
sys.exit(main())
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## Deprecation workflow
|
|
165
|
-
|
|
166
|
-
Any capability slated for removal follows a three-stage cycle. The minimum cycle is **two schema versions** (typically 6–12 months).
|
|
167
|
-
|
|
168
|
-
| Stage | Schema version | Behavior |
|
|
169
|
-
|---|---|---|
|
|
170
|
-
| **Announce** | N | Add an entry to `references/deprecations.md` with the feature name, replacement, and removal target version. The skill prints a one-line notice when the feature is used. |
|
|
171
|
-
| **Warn** | N+1 | The skill prints a louder warning (with remediation steps) and writes a `_deprecation_warnings_shown` array to `.lore/.config.json` so the warning doesn't repeat. |
|
|
172
|
-
| **Remove** | N+2 | Hard delete. Old `.lore/` data is migrated by `migrate.py` to the new format. Users who skipped migrations will see explicit errors pointing at `migrate.py`. |
|
|
173
|
-
|
|
174
|
-
Skipping a stage is allowed only for security fixes or unreleased features that were never shipped.
|
|
108
|
+
There is no deprecation registry in v1. A capability slated for removal ships in a commit prefixed `BREAKING:` that names the capability and the migration step. The previous release may emit a one-line `[WARN]` notice when the deprecated capability is used, but there is no automated reminder system.
|
|
175
109
|
|
|
176
110
|
## CI enforcement
|
|
177
111
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
1. **New skill reads old `.lore/`**: checkout a fixture project from `fixtures/v0-project/`, run `list_entries.py`, `history.py`, `find_stale.py` on it. Pass = no exceptions, correct counts.
|
|
181
|
-
2. **Old skill reads new `.lore/`**: build a fixture with the latest schema, check out the previous release's scripts, run them. Pass = no exceptions on known fields; unknown fields silently ignored.
|
|
182
|
-
3. **No rename or delete in `scripts/` or `references/`**: PR diff against `scripts/` and `references/` filenames; any removed file = failure.
|
|
183
|
-
|
|
184
|
-
At least one fixture project must be checked in at `fixtures/v0-project/` and re-pinned to a known old schema version after each major release.
|
|
112
|
+
There is no compatibility CI in v1. Verification is the author's responsibility before each release: run `list_entries.py`, `history.py`, `find_stale.py` against a `.lore/` populated by the author's own work, and confirm the output is sensible.
|
|
185
113
|
|
|
186
114
|
## Examples
|
|
187
115
|
|
|
188
116
|
### Compatible change (additive)
|
|
189
117
|
|
|
190
|
-
Adding a new `compress_thresholds.max_entries_per_scope`
|
|
118
|
+
Adding a new optional field, for example `compress_thresholds.max_entries_per_scope` with default `100`:
|
|
191
119
|
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
-
|
|
120
|
+
- Old configs continue to operate; the new field reads as the default.
|
|
121
|
+
- Old skill reading a new config: sees only the fields it knows; ignores the new field.
|
|
122
|
+
- New skill reading an old config: detects the missing field and uses the default.
|
|
123
|
+
- No upgrade notes needed; the change is invisible to existing users.
|
|
124
|
+
|
|
125
|
+
**Rule of thumb.** An additive optional change is non-breaking: ship it without bumping anything, no migration steps, no warnings.
|
|
196
126
|
|
|
197
127
|
### Incompatible change (avoid)
|
|
198
128
|
|
|
199
|
-
Renaming `mirror_mode` to `render_mode
|
|
129
|
+
Renaming `mirror_mode` to `render_mode` in a single release:
|
|
200
130
|
|
|
201
|
-
- Every existing `.lore/.config.json` would silently lose its `mirror_mode: "index"` setting
|
|
202
|
-
- Bad. Instead:
|
|
131
|
+
- Every existing `.lore/.config.json` would silently lose its `mirror_mode: "index"` setting (old field dropped, new field absent → defaults kick in).
|
|
132
|
+
- Bad. Instead: keep both fields for one release, mark `mirror_mode` as deprecated in the commit message, then remove `mirror_mode` in the next breaking release.
|
|
203
133
|
|
|
204
|
-
### Breaking change (
|
|
134
|
+
### Breaking change (commit message)
|
|
205
135
|
|
|
206
|
-
Removing support for `mirror_mode: "full"`:
|
|
136
|
+
Removing support for a config value such as `mirror_mode: "full"`:
|
|
207
137
|
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
- Each version's release notes link to the deprecation entry in `references/deprecations.md`.
|
|
138
|
+
- The release prints a warning when the value is set; suggests `"index"`.
|
|
139
|
+
- A future release hard-rejects `"full"`. Users edit their config by hand.
|
|
140
|
+
- The breaking commit message names the change and the manual edit.
|
|
212
141
|
|
|
213
142
|
## Decision checklist
|
|
214
143
|
|
|
@@ -216,13 +145,14 @@ Before merging any change to lore, answer these questions:
|
|
|
216
145
|
|
|
217
146
|
1. Does this change add, modify, or remove anything in `.lore/`?
|
|
218
147
|
2. Does this change add, modify, or remove any script in `scripts/`?
|
|
219
|
-
3. Does this change add, modify, or remove any contract string (`## Lore (auto-managed)`, `## My notes (free edit)`, etc.)?
|
|
148
|
+
3. Does this change add, modify, or remove any contract string (`## Lore (auto-managed)`, `## My notes (free edit)`, `<!-- LORE:START -->`, `<!-- LORE:END -->`, etc.)?
|
|
220
149
|
4. Does this change add, modify, or remove any reference doc filename?
|
|
221
150
|
5. Does this change add, modify, or remove any entry tag?
|
|
222
151
|
|
|
223
|
-
If any answer is "
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
-
|
|
152
|
+
If any answer is "add" and the change is non-breaking (new optional field, new optional tag, new doc, new script), ship as-is with a regular commit prefix (`feat:`, `docs:`, `refactor:`).
|
|
153
|
+
|
|
154
|
+
If any answer is "modify" or "remove", the change is breaking and the commit must:
|
|
155
|
+
- Be prefixed `BREAKING:` instead of `feat:` / `refactor:`.
|
|
156
|
+
- Name what changed and what the user must do in the commit body.
|
|
227
157
|
|
|
228
|
-
If all answers are "add" or "no", the change is non-breaking and
|
|
158
|
+
If all answers are "add" or "no", the change is non-breaking and ships as a regular commit.
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"sync_thresholds": {
|
|
20
20
|
"min_lines_changed": 50,
|
|
21
21
|
"min_directories_changed": 2
|
|
22
|
-
}
|
|
22
|
+
},
|
|
23
|
+
"last_sync_sha": "f9ca271..." // set automatically by sync; null on first run
|
|
23
24
|
}
|
|
24
25
|
```
|
|
25
26
|
|
|
@@ -29,10 +30,9 @@
|
|
|
29
30
|
|
|
30
31
|
- **Missing** → treated as `schema_version: 1`. A `[WARN]` notice is printed to stderr by `list_entries.py`; add the field manually to silence it.
|
|
31
32
|
- **Equal to skill's expected version** → use as-is.
|
|
32
|
-
- **
|
|
33
|
-
- **Higher than expected** → refuse to read with an error; the user's skill is older than their `.lore/`. They need to upgrade lore (pull latest from upstream) before continuing.
|
|
33
|
+
- **Higher than expected** → warn and continue: `list_entries.py` prints a `[WARN]` to stderr and proceeds with best-effort reads (readers are forward-compatible — see `references/compatibility.md`). The user's skill is older than their `.lore/`; recommend pulling the latest lore from upstream.
|
|
34
34
|
|
|
35
|
-
For the full compatibility policy
|
|
35
|
+
For the full compatibility policy, see `references/compatibility.md`.
|
|
36
36
|
|
|
37
37
|
## Field semantics
|
|
38
38
|
|
|
@@ -40,10 +40,10 @@ For the full compatibility policy (migration tools, deprecation cycle, reader/wr
|
|
|
40
40
|
|
|
41
41
|
Default: `false`.
|
|
42
42
|
|
|
43
|
-
Controls whether `compress`
|
|
43
|
+
Controls whether `compress` regenerates platform mirrors automatically after it writes `SUMMARY.md`. It does **not** gate the explicit `lore mirror` command — `lore mirror` always regenerates (with content-based dedup) once `mirror_targets` is resolved.
|
|
44
44
|
|
|
45
|
-
- `true` —
|
|
46
|
-
- `false` —
|
|
45
|
+
- `true` — `compress` regenerates mirrors automatically
|
|
46
|
+
- `false` — `compress` asks per target before writing
|
|
47
47
|
|
|
48
48
|
Note: this flag does **not** affect `sync`. By default `sync` does not touch mirrors at all (see `sync_updates_mirror`).
|
|
49
49
|
|
|
@@ -63,8 +63,8 @@ Default: `"medium"`.
|
|
|
63
63
|
Controls how much confirmation `sync` requires for individual change types.
|
|
64
64
|
|
|
65
65
|
- `"high"` — auto-apply everything, including `NEW` and `STALE`. Only `ALERT` blocks interrupt.
|
|
66
|
-
- `"medium"` — auto-apply low-risk changes (de-duplicate hits,
|
|
67
|
-
- `"low"` — every change requires confirmation, including de-duplicate hits and
|
|
66
|
+
- `"medium"` — auto-apply low-risk changes (de-duplicate hits, tags-only REFINEDs). Body-changing REFINEDs, `NEW`, `STALE`, and `ALERT` require confirmation.
|
|
67
|
+
- `"low"` — every change requires confirmation, including de-duplicate hits and tags-only REFINEDs.
|
|
68
68
|
|
|
69
69
|
### `mirror_targets`
|
|
70
70
|
|
|
@@ -88,7 +88,7 @@ When auto-detection is in effect, `lore init` populates this field with the user
|
|
|
88
88
|
|
|
89
89
|
Default: `"index"`.
|
|
90
90
|
|
|
91
|
-
Only `"index"` is accepted. The mirror renders a small index structure pointing into `.lore/` (see `references/platform-mirrors.md` for the template and adaptive rendering rules). Per-session token cost stays flat (~
|
|
91
|
+
Only `"index"` is accepted. The mirror renders a small index structure pointing into `.lore/` (see `references/platform-mirrors.md` for the template and adaptive rendering rules). Per-session token cost stays flat (~600 B worst case, including a one-line operational opening and per-scope descriptions) regardless of entry count.
|
|
92
92
|
|
|
93
93
|
Any other value (e.g., the historical `"summary"` or `"full"`) is rejected at config-load time with an error. Remove the field, or set it to `"index"`.
|
|
94
94
|
|
|
@@ -104,22 +104,21 @@ Defaults: `{"min_lines_changed": 50, "min_directories_changed": 2}`.
|
|
|
104
104
|
|
|
105
105
|
`sync` only proposes an update when at least one trigger threshold is met (see `SKILL.md` sync trigger threshold). Lowering these values means `sync` proposes updates more often.
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
### `last_sync_sha`
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
Default: absent (treated as `null`).
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
- Invalid JSON → fall back to defaults + warn the user.
|
|
113
|
-
- After editing, verify with `python scripts/list_entries.py --config-check` (added in a future migration).
|
|
114
|
-
- For schema version changes, see `references/compatibility.md`.
|
|
111
|
+
The git commit SHA from which the next `sync` will compute its delta. Written automatically by `sync` after every successful `.lore/*` update. Optional and additive — old configs without it keep working.
|
|
115
112
|
|
|
116
|
-
|
|
113
|
+
- **Absent or `null`** — `sync` falls back to working-tree diff alone (`git diff`, working tree vs. `HEAD`). Already-committed changes between two syncs are invisible.
|
|
114
|
+
- **Set to a reachable SHA** — `sync` uses `git diff <last_sync_sha>..HEAD` for committed changes plus working-tree diff for uncommitted changes. This is the recommended state and lets batched commits be captured by a single later sync.
|
|
115
|
+
- **Set to an unreachable SHA** (e.g., after `git rebase` or a force-push that orphaned the SHA) — `sync` prints a `[WARN]` to stderr and falls back to the working-tree diff alone. The next successful sync resets the baseline.
|
|
116
|
+
- **Empty repo** (no commits yet) — the field is `null`; only the working-tree diff applies.
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
## Editing the config
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
2. The skill prompts: "Your `.lore/.config.json` is v1; lore now expects v2. Run `python scripts/migrate.py` to upgrade."
|
|
122
|
-
3. `migrate.py` is idempotent — running it twice is a no-op.
|
|
123
|
-
4. After migration, the file is updated in place; no manual editing needed.
|
|
120
|
+
Edit `.lore/.config.json` directly. After editing:
|
|
124
121
|
|
|
125
|
-
|
|
122
|
+
- `sync` and `compress` re-read the config on every run; no restart needed.
|
|
123
|
+
- Invalid JSON → fall back to defaults + warn the user.
|
|
124
|
+
- For breaking-config changes, see `references/compatibility.md` and look for the `BREAKING:` commit in the project's git history.
|
|
@@ -27,14 +27,18 @@ The 4-char `xxxx` is the first 4 hex chars of `sha256(entry text)`. This makes I
|
|
|
27
27
|
|
|
28
28
|
If two entries have identical content (hash collision, statistically rare), add a distinguishing word to one and recompute.
|
|
29
29
|
|
|
30
|
+
### Updating an entry (REFINED)
|
|
31
|
+
|
|
32
|
+
Because the ID hashes the body, **any body change produces a new ID**. `sync`'s `[REFINED]` proposal follows this rule: tags-only updates (body unchanged) keep the ID; body rewrites create a new entry with a freshly hashed ID and link the old one via `#superseded-by:<new-id>` (see `references/stale-new-markers.md`).
|
|
33
|
+
|
|
30
34
|
## Tag specification
|
|
31
35
|
|
|
32
36
|
| Tag | Meaning |
|
|
33
37
|
|---|---|
|
|
34
38
|
| `#added:YYYY-MM-DD` | When the entry was created |
|
|
35
39
|
| `#verified:YYYY-MM-DD` | Last time a human or audit confirmed the entry is still true |
|
|
36
|
-
| `#stale:YYYY-MM-DD` | Flagged by `sync` as superseded
|
|
37
|
-
| `#
|
|
40
|
+
| `#stale:YYYY-MM-DD` | Flagged by `sync` as no longer accurate. Two cases: (a) the entry was superseded — pair with `#superseded-by:<new-id>`; (b) deprecated with no successor — alone. |
|
|
41
|
+
| `#superseded-by:LAYER-YYYY-MM-DD-xxxx` | Points to the entry that replaces this one. When present, it implies staleness; the `#stale:<date>` tag is optional but encouraged for clarity. The `xxxx` is the 4-hex content hash of the replacement. |
|
|
38
42
|
|
|
39
43
|
Multiple tags can co-exist on one entry (e.g. `#added:2026-01-15 #verified:2026-06-01`).
|
|
40
44
|
|
|
@@ -60,6 +64,32 @@ If a fact can't fit in ≤ 2 lines, split into multiple entries and cross-refere
|
|
|
60
64
|
|
|
61
65
|
Instead of stuffing them into a single overly long bullet.
|
|
62
66
|
|
|
67
|
+
## Superseded-by chain
|
|
68
|
+
|
|
69
|
+
When an entry is replaced by another (e.g. a tech-stack swap, a convention reversal), the old entry carries `#superseded-by:<new-id>` alongside `#stale:<date>`. This turns the replacement relationship from prose into data that scripts can walk.
|
|
70
|
+
|
|
71
|
+
Syntax: `#superseded-by:LAYER-YYYY-MM-DD-xxxx` — the replacement entry's full ID. The replacement entry itself carries no back-reference; its `#verified:DATE` and `#added:DATE` are sufficient.
|
|
72
|
+
|
|
73
|
+
Worked example — bcrypt replaces SHA-256 in `scopes/backend/DECISIONS.md`:
|
|
74
|
+
|
|
75
|
+
```markdown
|
|
76
|
+
- [DEC-2026-07-10-ee31] SHA-256 + salt for password hashing; reason: no native dep, deterministic. #added:2026-07-10 #stale:2026-07-10 #superseded-by:DEC-2026-07-10-e45d
|
|
77
|
+
- [DEC-2026-07-10-e45d] Use bcrypt (rounds=12) for password hashing; reason: industry standard, built-in salt. #added:2026-07-10
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Consumers:
|
|
81
|
+
|
|
82
|
+
- `find_stale.py --json` — groups stale entries by their `replaced_by` target; flags chains where the target ID does not exist (broken chain).
|
|
83
|
+
- `history.py --follow-superseded <id>` — prints the entry plus every successor along the chain (newest first).
|
|
84
|
+
- `compress` — skips entries with `replaced_by` set when selecting the 3–5 entries per (scope, layer).
|
|
85
|
+
- `audit` — when reporting CONFLICT between two entries, surfaces the chain if both belong to one.
|
|
86
|
+
|
|
87
|
+
Constraints:
|
|
88
|
+
|
|
89
|
+
- The tag is **optional**. Old entries without it continue to work; old skills ignore it.
|
|
90
|
+
- **At most one `#superseded-by` tag per entry.** Successive replacements form a chain (A → B → C), never a fork: an entry is replaced by one successor at a time. If an entry carries more than one tag, `list_entries.py` warns and keeps the first.
|
|
91
|
+
- Cross-file references: the ID is sufficient because the LAYER prefix plus hash makes collisions across files vanishingly rare. If two files contain the same ID, prefer the one in the same scope as the entry being read.
|
|
92
|
+
|
|
63
93
|
## What counts as "atomic"
|
|
64
94
|
|
|
65
95
|
A fact is atomic if it answers exactly one question:
|
|
@@ -67,4 +97,4 @@ A fact is atomic if it answers exactly one question:
|
|
|
67
97
|
- "What is the frontend framework?" → `ARCH` entry about Next.js
|
|
68
98
|
- "Why Next.js not Remix?" → `DEC` entry referencing the `ARCH` entry
|
|
69
99
|
|
|
70
|
-
If your entry answers two questions, split it.
|
|
100
|
+
If your entry answers two questions, split it.
|
|
@@ -11,6 +11,7 @@ lore history <entry-id>
|
|
|
11
11
|
lore history <file-path>
|
|
12
12
|
lore history --scope=<name>
|
|
13
13
|
lore history --since=<YYYY-MM-DD>
|
|
14
|
+
lore history --follow-superseded
|
|
14
15
|
lore history --json
|
|
15
16
|
```
|
|
16
17
|
|
|
@@ -19,9 +20,36 @@ lore history --json
|
|
|
19
20
|
| Form | Argument shape | Example | Behavior |
|
|
20
21
|
|---|---|---|---|
|
|
21
22
|
| Entry | `[A-Z]+-\d{4}-\d{2}-\d{2}-[a-f0-9]{4}` | `lore history DEC-2026-02-03-7c19` | Locate entry in `.lore/`, derive its `#added` date and code file, then `git log` since that date. |
|
|
22
|
-
| File | contains `/` or starts with `.` | `lore history frontend/src/store/index.ts` | Run `git log
|
|
23
|
+
| File | contains `/` or starts with `.` | `lore history frontend/src/store/index.ts` | Run the full `git log` on the given path unless `--since` is provided. |
|
|
23
24
|
| Scope | `--scope=<name>` only | `lore history --scope=frontend` | For each `*.md` in `.lore/scopes/<name>/`, run file form on the lore file path itself. |
|
|
24
25
|
|
|
26
|
+
### `--follow-superseded`
|
|
27
|
+
|
|
28
|
+
When set on the entry form, prints not just the requested entry's git history, but the history of every entry in its `#superseded-by` chain (newest successor first). Stops when an entry has no `#superseded-by` tag or the chain reaches a non-existent ID. Output prepends a `## Chain` section listing each entry's ID and file path before the per-entry `git log` blocks.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
$ lore history --follow-superseded DEC-2026-07-10-ee31
|
|
34
|
+
|
|
35
|
+
# history: [DEC-2026-07-10-ee31] --follow-superseded
|
|
36
|
+
|
|
37
|
+
## Chain
|
|
38
|
+
1. [DEC-2026-07-10-ee31] (scopes/backend/DECISIONS.md) — SHA-256 + salt
|
|
39
|
+
→ superseded-by → DEC-2026-07-10-e45d
|
|
40
|
+
2. [DEC-2026-07-10-e45d] (scopes/backend/DECISIONS.md) — bcrypt (rounds=12)
|
|
41
|
+
→ no successor
|
|
42
|
+
|
|
43
|
+
# history: [DEC-2026-07-10-ee31]
|
|
44
|
+
|
|
45
|
+
> Entry: scopes/backend/DECISIONS.md
|
|
46
|
+
> Since: 2026-07-10
|
|
47
|
+
> File: backend/app/auth.py
|
|
48
|
+
> Commits: 3 (showing all)
|
|
49
|
+
|
|
50
|
+
...
|
|
51
|
+
```
|
|
52
|
+
|
|
25
53
|
## Code-file resolution (entry form)
|
|
26
54
|
|
|
27
55
|
Priority:
|
|
@@ -59,6 +87,7 @@ appears only when at least one commit is found.
|
|
|
59
87
|
"code_file": "frontend/src/store/index.ts",
|
|
60
88
|
"since": "2026-02-03",
|
|
61
89
|
"since_source": "entry_added",
|
|
90
|
+
"chain": null,
|
|
62
91
|
"commits": [
|
|
63
92
|
{
|
|
64
93
|
"hash": "...",
|
|
@@ -73,6 +102,8 @@ appears only when at least one commit is found.
|
|
|
73
102
|
}
|
|
74
103
|
```
|
|
75
104
|
|
|
105
|
+
When `--follow-superseded` is set, `chain` is an array of `{entry_id, lore_file, code_file, since}` for each successor; otherwise `null`.
|
|
106
|
+
|
|
76
107
|
## Error handling
|
|
77
108
|
|
|
78
109
|
| Condition | Exit code | Message |
|
|
@@ -103,4 +134,69 @@ appears only when at least one commit is found.
|
|
|
103
134
|
commit history. `lore history` fills that gap: given a memory entry,
|
|
104
135
|
it shows the commits that introduced or modified the underlying code,
|
|
105
136
|
letting the agent answer "why does this decision exist?" with a pointer
|
|
106
|
-
to the original commit instead of an LLM-generated guess.
|
|
137
|
+
to the original commit instead of an LLM-generated guess.
|
|
138
|
+
|
|
139
|
+
## `--since` normalization (same-day commit safety)
|
|
140
|
+
|
|
141
|
+
`git log --since=YYYY-MM-DD` interpretation is version-dependent.
|
|
142
|
+
Older git versions parse a bare date as the user's local-timezone
|
|
143
|
+
midnight; newer versions parse it as UTC midnight. A commit made early
|
|
144
|
+
in the day can therefore be silently dropped when filtering by a
|
|
145
|
+
same-day `#added` tag.
|
|
146
|
+
|
|
147
|
+
To avoid this, `lore history` normalizes date-only inputs to an explicit
|
|
148
|
+
ISO-8601 timestamp before passing them to `git log`. The transformation
|
|
149
|
+
is `YYYY-MM-DD` → `YYYY-MM-DD T 00:00:00`, applied at the entry-form
|
|
150
|
+
extraction point and at the file-form `--since` argument. Strings that
|
|
151
|
+
already contain a time component (a `T` or a space) are passed through
|
|
152
|
+
unchanged.
|
|
153
|
+
|
|
154
|
+
This means the JSON output's `since` field will show e.g.
|
|
155
|
+
`"2026-07-13T00:00:00"` for a date-only `#added`, not `"2026-07-13"`.
|
|
156
|
+
The same-day commit you want to surface is now guaranteed to be in the
|
|
157
|
+
result, regardless of the host's git version or the user's timezone.
|
|
158
|
+
|
|
159
|
+
File and scope forms without an explicit `--since`, and entry forms
|
|
160
|
+
without an `#added` tag, omit Git's `--since` option so they truly scan
|
|
161
|
+
the full history. Their JSON metadata retains
|
|
162
|
+
`"1970-01-01T00:00:00"` as the backward-compatible default marker;
|
|
163
|
+
that marker is not passed to Git because the exact Unix epoch can be
|
|
164
|
+
treated as a sentinel and incorrectly return no commits.
|
|
165
|
+
|
|
166
|
+
## Known issues
|
|
167
|
+
|
|
168
|
+
### Cross-timezone `#added` interpretation (unfixed)
|
|
169
|
+
|
|
170
|
+
The normalization in the previous section only resolves the *git version*
|
|
171
|
+
ambiguity. A second, deeper ambiguity remains: `#added` is a date-only
|
|
172
|
+
string written by the **author** of the entry, but `git log --since=`
|
|
173
|
+
interprets it in the **runner's** local timezone.
|
|
174
|
+
|
|
175
|
+
**Scenario** (uncovered, not yet reproduced in a real project):
|
|
176
|
+
|
|
177
|
+
1. Author in `+0800` commits at 2026-07-13 02:00 `+0800`
|
|
178
|
+
(= 2026-07-12 18:00 UTC) and adds an entry with
|
|
179
|
+
`#added: 2026-07-13` (their local date).
|
|
180
|
+
2. Runner in `PST (UTC-8)` invokes `lore history DEC-…`.
|
|
181
|
+
3. `git log --since=2026-07-13T00:00:00` is interpreted in the
|
|
182
|
+
runner's PST as 2026-07-13 00:00 PST (= 2026-07-13 08:00 UTC).
|
|
183
|
+
4. The commit at 2026-07-12 18:00 UTC is **before** 2026-07-13 08:00
|
|
184
|
+
UTC, so the commit is **excluded** from history.
|
|
185
|
+
5. From the runner's perspective, the commit looks "older than the
|
|
186
|
+
entry" even though both occurred on the same calendar day in the
|
|
187
|
+
author's timezone.
|
|
188
|
+
|
|
189
|
+
This is a real bug, but it is **not fixed** in this release because:
|
|
190
|
+
|
|
191
|
+
- No cross-timezone reproduction has been reported.
|
|
192
|
+
- Fixing it would require either (a) subtracting one day from
|
|
193
|
+
`#added` before passing to `git log`, which occasionally over-includes
|
|
194
|
+
the previous day, or (b) capturing the exact UTC time of `#added` in
|
|
195
|
+
the entry format, which is a breaking change to the entry schema.
|
|
196
|
+
|
|
197
|
+
**Workaround today**: pass `--since=<#added - 1 day>` explicitly when
|
|
198
|
+
the runner is in a timezone west of the author's.
|
|
199
|
+
|
|
200
|
+
**Trigger to fix**: any user report of "history missed a commit that I
|
|
201
|
+
know is related" with cross-timezone evidence. Until then, the field
|
|
202
|
+
remains a known issue rather than a known fix.
|