openclew 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 R.AlphA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,299 @@
1
+ # openclew
2
+
3
+ > Long Life Memory for LLMs
4
+
5
+ **Your agent forgets. Your project remembers.**
6
+
7
+ In Greek mythology, Ariadne gave Theseus a *clew* — a ball of thread — to find his way out of the Minotaur's labyrinth. That thread is the etymological origin of the word "clue." It wasn't a map. It wasn't a search engine. It was a continuous trail that connected where you've been to where you are.
8
+
9
+ That's what openclew does for your project. Every decision, every architectural choice, every hard-won lesson — laid down as a thread that any reader (human or AI) can follow. Not scattered across wikis, chat logs, and CLAUDE.md files that grow until they're unreadable. One trail. One source of truth.
10
+
11
+ ---
12
+
13
+ ## Why this exists
14
+
15
+ AI agents are powerful, but they're amnesiac. Every new session starts from zero. The usual fixes don't work:
16
+
17
+ | Approach | What goes wrong |
18
+ |----------|----------------|
19
+ | CLAUDE.md / .cursorrules | Grows into an unreadable wall of text. Agent loads everything, wastes tokens on irrelevant context |
20
+ | Agent memory (Claude, Copilot) | Opaque, not versioned, not shareable with the team |
21
+ | Wiki / Notion | Disconnected from the code, goes stale |
22
+ | README.md | Not structured for AI consumption |
23
+ | Nothing | Re-explain everything every session |
24
+
25
+ The deeper problem isn't *storage* — it's **navigation**. A project with 50 documents and 200K tokens of knowledge can't be loaded in full. The real question an agent (or a human) needs to answer is:
26
+
27
+ > **"Should I read this document?"**
28
+
29
+ Not "does this file contain the word `auth`?" — that's pattern matching. The question is about *relevance*. And you can only answer it if documents are designed to be skimmed before they're read.
30
+
31
+ ---
32
+
33
+ ## The idea: 3 levels of depth
34
+
35
+ Every openclew document has 3 levels. Same file, different depths — for different needs.
36
+
37
+ ```
38
+ ┌─────────────────────────────────────────────┐
39
+ │ L1 — Metadata │
40
+ │ type, subject, status, keywords │
41
+ │ → "Should I read this?" — decidable in │
42
+ │ 2 seconds, ~40 tokens per doc │
43
+ │ → Auto-indexed, machine-parseable │
44
+ ├─────────────────────────────────────────────┤
45
+ │ L2 — Summary │
46
+ │ Objective, key points, solution │
47
+ │ → The full picture in 30 seconds │
48
+ │ → Enough for most decisions │
49
+ ├─────────────────────────────────────────────┤
50
+ │ L3 — Details │
51
+ │ Code, examples, history, edge cases │
52
+ │ → Deep-dive only when actually needed │
53
+ │ → Most readers never go here │
54
+ └─────────────────────────────────────────────┘
55
+ ```
56
+
57
+ This isn't just an organizational trick — it's a **token efficiency strategy**. A project with 50 docs:
58
+
59
+ | Strategy | Tokens consumed | Relevance |
60
+ |----------|----------------|-----------|
61
+ | Load everything | ~200K | Mostly noise |
62
+ | Grep for keywords | Variable | Misses context, false positives |
63
+ | **Read all L1s, then L2 of relevant docs** | **~2K + 2-3 docs** | **Precise, contextual** |
64
+
65
+ L1 answers "should I read this?" L2 answers "what do I need to know?" L3 is there when you need the details. Most of the time, you don't.
66
+
67
+ ---
68
+
69
+ ## Two types of docs
70
+
71
+ | Type | Location | Role | Mutability |
72
+ |------|----------|------|------------|
73
+ | **Living** | `doc/_SUBJECT.md` | Living knowledge (architecture, conventions, decisions) | Updated over time |
74
+ | **Log** | `doc/log/YYYY-MM-DD_subject.md` | Frozen facts (what happened, what was decided) | Never modified |
75
+
76
+ **Living docs** are your project's brain — they evolve as the project evolves.
77
+ **Logs** are your project's journal — immutable records of what happened and why.
78
+
79
+ Together, they form the thread. The living docs tell you where you are. The logs tell you how you got here.
80
+
81
+ ---
82
+
83
+ ## Quick start (5 minutes)
84
+
85
+ ### 1. Create the structure
86
+
87
+ ```bash
88
+ mkdir -p doc/log
89
+ ```
90
+
91
+ ### 2. Copy the templates
92
+
93
+ Download from [`templates/`](templates/) or create manually:
94
+
95
+ <details>
96
+ <summary><b>templates/living.md</b> — for living knowledge</summary>
97
+
98
+ ```markdown
99
+ <!-- L1_START -->
100
+ # L1 - Metadata
101
+ type: Reference | Architecture | Guide | Analysis
102
+ subject: Short title (< 60 chars)
103
+ created: YYYY-MM-DD
104
+ updated: YYYY-MM-DD
105
+ short_story: 1-2 sentences. What this doc covers and what it concludes.
106
+ status: Active | Stable | Archived
107
+ category: Main domain (e.g. Auth, API, Database, UI...)
108
+ keywords: [tag1, tag2, tag3]
109
+ <!-- L1_END -->
110
+
111
+ ---
112
+
113
+ <!-- L2_START -->
114
+ # L2 - Summary
115
+
116
+ ## Objective
117
+ <!-- Why this document exists -->
118
+
119
+ ## Key points
120
+ <!-- 3-5 essential takeaways -->
121
+
122
+ ## Solution
123
+ <!-- Recommended approach or pattern -->
124
+ <!-- L2_END -->
125
+
126
+ ---
127
+
128
+ <!-- L3_START -->
129
+ # L3 - Details
130
+
131
+ <!-- Full technical content: examples, code, references... -->
132
+
133
+ ## Changelog
134
+
135
+ | Date | Change |
136
+ |------|--------|
137
+ | YYYY-MM-DD | Initial creation |
138
+ <!-- L3_END -->
139
+ ```
140
+
141
+ </details>
142
+
143
+ <details>
144
+ <summary><b>templates/log.md</b> — for frozen facts</summary>
145
+
146
+ ```markdown
147
+ <!-- L1_START -->
148
+ # L1 - Metadata
149
+ date: YYYY-MM-DD
150
+ type: Bug | Feature | Refactor | Doc | Deploy
151
+ subject: Short title (< 60 chars)
152
+ short_story: 1-2 sentences. What happened and what was the outcome.
153
+ status: Done | In progress | Abandoned
154
+ category: Main domain
155
+ keywords: [tag1, tag2, tag3]
156
+ <!-- L1_END -->
157
+
158
+ ---
159
+
160
+ <!-- L2_START -->
161
+ # L2 - Summary
162
+
163
+ ## Problem
164
+ <!-- What was observed -->
165
+
166
+ ## Solution
167
+ <!-- How it was resolved -->
168
+ <!-- L2_END -->
169
+
170
+ ---
171
+
172
+ <!-- L3_START -->
173
+ # L3 - Details
174
+
175
+ <!-- Technical details: code changes, debugging steps, references... -->
176
+ <!-- L3_END -->
177
+ ```
178
+
179
+ </details>
180
+
181
+ ### 3. Write your first doc
182
+
183
+ ```bash
184
+ cp templates/living.md doc/_ARCHITECTURE.md
185
+ ```
186
+
187
+ Edit it — describe your project's architecture. Fill in L1 (metadata), L2 (summary), skip L3 if you don't need it yet.
188
+
189
+ ### 4. Point your agent to it
190
+
191
+ Add this to your `CLAUDE.md`, `.cursorrules`, or `AGENTS.md`:
192
+
193
+ ```markdown
194
+ ## Project knowledge
195
+
196
+ Documentation lives in `doc/`. Each doc has 3 levels (L1/L2/L3).
197
+ - Read L1 first to decide if you need more
198
+ - Living docs: `doc/_*.md` (living knowledge, updated)
199
+ - Logs: `doc/log/YYYY-MM-DD_*.md` (frozen facts, never modified)
200
+ - Index: `doc/_INDEX.md` (auto-generated, start here)
201
+ ```
202
+
203
+ ### 5. Auto-generate the index (optional)
204
+
205
+ Copy [`hooks/generate-index.py`](hooks/generate-index.py) to your project and add it as a pre-commit hook:
206
+
207
+ ```bash
208
+ # Option A: git hook
209
+ cp hooks/generate-index.py .git/hooks/generate-index.py
210
+ echo 'python .git/hooks/generate-index.py && git add doc/_INDEX.md' >> .git/hooks/pre-commit
211
+ chmod +x .git/hooks/pre-commit
212
+
213
+ # Option B: pre-commit framework
214
+ # See hooks/README.md for .pre-commit-config.yaml setup
215
+ ```
216
+
217
+ The index auto-regenerates on every commit. Never edit it manually.
218
+
219
+ ---
220
+
221
+ ## How it works in practice
222
+
223
+ **Session 1** — You're setting up auth:
224
+ ```
225
+ doc/
226
+ ├── _ARCHITECTURE.md # Your stack, main patterns
227
+ └── log/
228
+ └── 2026-03-07_setup-auth.md # What you did, decisions made
229
+ ```
230
+
231
+ **Session 5** — New agent session, different feature:
232
+ ```
233
+ Agent reads doc/_INDEX.md (auto-generated)
234
+ → Scans all L1s: "Should I read this?"
235
+ → _ARCHITECTURE.md → yes → reads L2
236
+ → setup-auth log → relevant → reads L2
237
+ → Skips the rest
238
+ → Full context in ~1K tokens instead of 50K
239
+ ```
240
+
241
+ **Session 20** — Your project has grown:
242
+ ```
243
+ doc/
244
+ ├── _INDEX.md # Auto-generated, 30 entries
245
+ ├── _ARCHITECTURE.md # Updated 12 times
246
+ ├── _AUTH.md # Extracted when auth got complex
247
+ ├── _API_CONVENTIONS.md # Team conventions
248
+ ├── _KNOWN_ISSUES.md # Active gotchas
249
+ └── log/
250
+ ├── 2026-03-07_setup-auth.md
251
+ ├── 2026-03-10_migrate-db.md
252
+ ├── 2026-03-15_fix-token-refresh.md
253
+ └── ... (20 more)
254
+ ```
255
+
256
+ 30 docs. The agent scans all L1s in 2 seconds, reads the 3 that matter, and starts working with full context. A new teammate does the same — reads L2s to get up to speed in minutes. Same docs, same truth, different depth.
257
+
258
+ ---
259
+
260
+ ## Principles
261
+
262
+ - **"Should I read this?"** — L1 exists to answer this question. If it can't, the L1 is poorly written.
263
+ - **Shared knowledge** — Same docs for humans and AI. One source, multiple readers.
264
+ - **SSOT** (Single Source of Truth) — Each piece of information lives in one place.
265
+ - **Logs are immutable** — Once written, never modified. Frozen facts.
266
+ - **Living docs evolve** — They evolve as the project evolves.
267
+ - **Index is auto-generated** — Never edit `_INDEX.md` manually.
268
+
269
+ ---
270
+
271
+ ## Works with everything
272
+
273
+ **AI agents:** Claude Code, Cursor, Copilot, Windsurf, Codex, Zed, Kiro, Aider, Cline, Gemini CLI...
274
+
275
+ **Workflow frameworks:** BMAD, Spec Kit, or any methodology — openclew handles knowledge, your framework handles process.
276
+
277
+ **It's just Markdown.** No runtime, no dependencies, no lock-in. Git-versioned, diffable, reviewable in PRs. If you stop using it, the docs are still useful — to humans and agents alike.
278
+
279
+ ---
280
+
281
+ ## Compared to alternatives
282
+
283
+ | Feature | CLAUDE.md | Cline Memory Bank | BMAD | openclew |
284
+ |---------|-----------|-------------------|------|----------|
285
+ | Readable by humans AND agents | partial | partial | yes | **yes** |
286
+ | Levels of depth (L1/L2/L3) | - | - | - | **yes** |
287
+ | "Should I read this?" (L1 triage) | - | - | - | **yes** |
288
+ | Token-efficient navigation | - | - | partial | **yes** |
289
+ | Auto-generated index | - | - | CSV | **yes** |
290
+ | Immutable logs | - | - | - | **yes** |
291
+ | Git-versioned | yes | yes | yes | **yes** |
292
+ | Cross-project | - | - | - | **yes** |
293
+ | Tool-agnostic | Claude only | Cline only | multi | **yes** |
294
+
295
+ ---
296
+
297
+ ## License
298
+
299
+ MIT — use it however you want.
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { resolve } = require("path");
4
+
5
+ const args = process.argv.slice(2);
6
+ const command = args[0];
7
+
8
+ const USAGE = `
9
+ openclew — Long Life Memory for LLMs
10
+
11
+ Usage:
12
+ openclew init Set up openclew in the current project
13
+ openclew new <title> Create a living doc (evolves with the project)
14
+ openclew log <title> Create a session log (frozen facts)
15
+ openclew checkout End-of-session summary + log creation
16
+ openclew index Regenerate doc/_INDEX.md
17
+ openclew help Show this help
18
+
19
+ Options:
20
+ --no-hook Skip pre-commit hook installation (init)
21
+ --no-inject Skip instruction file injection (init)
22
+
23
+ Getting started:
24
+ npx openclew init 1. Set up doc/ + guide + examples + git hook
25
+ # Edit doc/_ARCHITECTURE.md 2. Replace the example with your project's architecture
26
+ openclew new "API design" 3. Create your own living docs
27
+ git commit 4. Index auto-regenerates on commit
28
+
29
+ Docs have 3 levels: L1 (metadata) → L2 (summary) → L3 (details).
30
+ Agents read L1 to decide what's relevant, then L2 for context.
31
+ More at: https://github.com/openclew/openclew
32
+ `.trim();
33
+
34
+ if (!command || command === "help" || command === "--help" || command === "-h") {
35
+ console.log(USAGE);
36
+ process.exit(0);
37
+ }
38
+
39
+ const commands = {
40
+ init: () => require("../lib/init"),
41
+ new: () => require("../lib/new-doc"),
42
+ log: () => require("../lib/new-log"),
43
+ checkout: () => require("../lib/checkout"),
44
+ index: () => require("../lib/index-gen"),
45
+ };
46
+
47
+ if (!commands[command]) {
48
+ console.error(`Unknown command: ${command}`);
49
+ console.error(`Run 'openclew help' for usage.`);
50
+ process.exit(1);
51
+ }
52
+
53
+ commands[command]();
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ openclew index generator.
4
+
5
+ Scans doc/_*.md (living docs) and doc/log/*.md (logs),
6
+ parses L1 metadata blocks, and generates doc/_INDEX.md.
7
+
8
+ Usage:
9
+ python generate-index.py # from project root
10
+ python generate-index.py /path/to/doc # custom doc directory
11
+
12
+ Idempotent: running twice produces the same output.
13
+ Zero dependencies: Python 3.8+ standard library only.
14
+ """
15
+
16
+ import os
17
+ import re
18
+ import sys
19
+ from datetime import datetime
20
+ from pathlib import Path
21
+
22
+
23
+ def find_doc_dir():
24
+ """Find the doc/ directory."""
25
+ if len(sys.argv) > 1:
26
+ doc_dir = Path(sys.argv[1])
27
+ else:
28
+ doc_dir = Path("doc")
29
+
30
+ if not doc_dir.is_dir():
31
+ print(f"No '{doc_dir}' directory found. Nothing to index.")
32
+ sys.exit(0)
33
+
34
+ return doc_dir
35
+
36
+
37
+ def parse_l1(filepath):
38
+ """Extract L1 metadata from a file."""
39
+ try:
40
+ content = filepath.read_text(encoding="utf-8")
41
+ except (OSError, UnicodeDecodeError):
42
+ return None
43
+
44
+ match = re.search(
45
+ r"<!--\s*L1_START\s*-->(.+?)<!--\s*L1_END\s*-->",
46
+ content,
47
+ re.DOTALL,
48
+ )
49
+ if not match:
50
+ return None
51
+
52
+ block = match.group(1)
53
+ meta = {}
54
+ for line in block.splitlines():
55
+ line = line.strip()
56
+ if line.startswith("#") or not line:
57
+ continue
58
+ if ":" in line:
59
+ key, _, value = line.partition(":")
60
+ meta[key.strip().lower()] = value.strip()
61
+
62
+ return meta
63
+
64
+
65
+ def collect_docs(doc_dir):
66
+ """Collect living docs and logs with their L1 metadata."""
67
+ living_docs = []
68
+ logs = []
69
+
70
+ # Living docs: doc/_*.md
71
+ for f in sorted(doc_dir.glob("_*.md")):
72
+ if f.name == "_INDEX.md":
73
+ continue
74
+ meta = parse_l1(f)
75
+ if meta:
76
+ living_docs.append((f, meta))
77
+
78
+ # Log docs: doc/log/*.md
79
+ log_dir = doc_dir / "log"
80
+ if log_dir.is_dir():
81
+ for f in sorted(log_dir.glob("*.md"), reverse=True):
82
+ meta = parse_l1(f)
83
+ if meta:
84
+ logs.append((f, meta))
85
+
86
+ return living_docs, logs
87
+
88
+
89
+ def generate_index(doc_dir, living_docs, logs):
90
+ """Generate _INDEX.md content."""
91
+ now = datetime.now().strftime("%Y-%m-%d %H:%M")
92
+ lines = [
93
+ f"# Project Knowledge Index",
94
+ f"",
95
+ f"> Auto-generated by [openclew](https://github.com/openclew/openclew) on {now}.",
96
+ f"> Do not edit manually — rebuilt from L1 metadata on every commit.",
97
+ f"",
98
+ ]
99
+
100
+ # Living docs section
101
+ lines.append("## Living docs")
102
+ lines.append("")
103
+ if living_docs:
104
+ lines.append("| Document | Subject | Status | Category |")
105
+ lines.append("|----------|---------|--------|----------|")
106
+ for f, meta in living_docs:
107
+ name = f.name
108
+ subject = meta.get("subject", "—")
109
+ status = meta.get("status", "—")
110
+ category = meta.get("category", "—")
111
+ rel_path = f.relative_to(doc_dir.parent)
112
+ lines.append(f"| [{name}]({rel_path}) | {subject} | {status} | {category} |")
113
+ else:
114
+ lines.append("_No living docs yet. Create one with `templates/living.md`._")
115
+ lines.append("")
116
+
117
+ # Logs section (last 20)
118
+ lines.append("## Recent logs")
119
+ lines.append("")
120
+ display_logs = logs[:20]
121
+ if display_logs:
122
+ lines.append("| Date | Subject | Status | Category |")
123
+ lines.append("|------|---------|--------|----------|")
124
+ for f, meta in display_logs:
125
+ date = meta.get("date", f.stem[:10])
126
+ subject = meta.get("subject", "—")
127
+ status = meta.get("status", "—")
128
+ category = meta.get("category", "—")
129
+ rel_path = f.relative_to(doc_dir.parent)
130
+ lines.append(f"| {date} | [{subject}]({rel_path}) | {status} | {category} |")
131
+ if len(logs) > 20:
132
+ lines.append(f"")
133
+ lines.append(f"_{len(logs) - 20} older logs not shown._")
134
+ else:
135
+ lines.append("_No logs yet. Create one with `templates/log.md`._")
136
+ lines.append("")
137
+
138
+ # Stats
139
+ lines.append("---")
140
+ lines.append(f"**{len(living_docs)}** living docs, **{len(logs)}** logs.")
141
+ lines.append("")
142
+
143
+ return "\n".join(lines)
144
+
145
+
146
+ def main():
147
+ doc_dir = find_doc_dir()
148
+ living_docs, logs = collect_docs(doc_dir)
149
+ index_content = generate_index(doc_dir, living_docs, logs)
150
+
151
+ index_path = doc_dir / "_INDEX.md"
152
+ index_path.write_text(index_content, encoding="utf-8")
153
+ print(f"Generated {index_path} ({len(living_docs)} living docs, {len(logs)} logs)")
154
+
155
+
156
+ if __name__ == "__main__":
157
+ main()