openclew 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,152 +70,54 @@ L1 answers "should I read this?" L2 answers "what do I need to know?" L3 is ther
70
70
 
71
71
  | Type | Location | Role | Mutability |
72
72
  |------|----------|------|------------|
73
- | **Living** | `doc/_SUBJECT.md` | Living knowledge (architecture, conventions, decisions) | Updated over time |
73
+ | **Refdoc** | `doc/_SUBJECT.md` | Reference knowledge (architecture, conventions, decisions) | Updated over time |
74
74
  | **Log** | `doc/log/YYYY-MM-DD_subject.md` | Frozen facts (what happened, what was decided) | Never modified |
75
75
 
76
- **Living docs** are your project's brain — they evolve as the project evolves.
76
+ **Refdocs** are your project's brain — they evolve as the project evolves.
77
77
  **Logs** are your project's journal — immutable records of what happened and why.
78
78
 
79
- Together, they form the thread. The living docs tell you where you are. The logs tell you how you got here.
79
+ Together, they form the thread. The refdocs tell you where you are. The logs tell you how you got here.
80
80
 
81
81
  ---
82
82
 
83
- ## Quick start (5 minutes)
83
+ ## Quick start (2 minutes)
84
84
 
85
- ### 1. Create the structure
85
+ ### 1. Install
86
86
 
87
87
  ```bash
88
- mkdir -p doc/log
88
+ npx openclew init
89
89
  ```
90
90
 
91
- ### 2. Copy the templates
91
+ This:
92
+ - Creates `doc/` with a guide, an example doc, and an example log
93
+ - Detects your instruction file (CLAUDE.md, .cursorrules, AGENTS.md...)
94
+ - Injects a block that teaches your agent about the doc structure
95
+ - Installs a pre-commit hook that auto-generates `doc/_INDEX.md`
92
96
 
93
- Download from [`templates/`](templates/) or create manually:
97
+ ### 2. Start a session with your agent
94
98
 
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 -->
99
+ Ask it:
118
100
 
119
- ## Key points
120
- <!-- 3-5 essential takeaways -->
101
+ > Read doc/_USING_OPENCLEW.md and document our architecture.
121
102
 
122
- ## Solution
123
- <!-- Recommended approach or pattern -->
124
- <!-- L2_END -->
103
+ Your agent reads the guide, understands the L1/L2/L3 format, and creates `doc/_ARCHITECTURE.md` with your project's actual architecture.
125
104
 
126
- ---
127
-
128
- <!-- L3_START -->
129
- # L3 - Details
130
-
131
- <!-- Full technical content: examples, code, references... -->
105
+ ### 3. There is no step 3
132
106
 
133
- ## Changelog
134
-
135
- | Date | Change |
136
- |------|--------|
137
- | YYYY-MM-DD | Initial creation |
138
- <!-- L3_END -->
139
- ```
107
+ Next session, your agent reads the index, finds the doc, has the context. No re-explanation needed. As your project evolves, your agent creates and updates docs during sessions — refdocs for ongoing knowledge, logs for frozen facts.
140
108
 
141
- </details>
109
+ The index auto-regenerates on every commit. Never edit it manually.
142
110
 
143
111
  <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
112
+ <summary><b>Manual setup</b> — if you prefer not to use the CLI</summary>
162
113
 
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
- ```
114
+ 1. Create `doc/` and `doc/log/`
115
+ 2. Copy templates from [`templates/`](templates/) (refdoc.md, log.md)
116
+ 3. Add the openclew block to your instruction file (see `doc/_USING_OPENCLEW.md` after init for the exact format)
117
+ 4. Copy [`hooks/generate-index.py`](hooks/generate-index.py) and wire it as a pre-commit hook
178
118
 
179
119
  </details>
180
120
 
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
121
  ---
220
122
 
221
123
  ## How it works in practice
@@ -263,7 +165,7 @@ doc/
263
165
  - **Shared knowledge** — Same docs for humans and AI. One source, multiple readers.
264
166
  - **SSOT** (Single Source of Truth) — Each piece of information lives in one place.
265
167
  - **Logs are immutable** — Once written, never modified. Frozen facts.
266
- - **Living docs evolve** — They evolve as the project evolves.
168
+ - **Refdocs evolve** — They evolve as the project evolves.
267
169
  - **Index is auto-generated** — Never edit `_INDEX.md` manually.
268
170
 
269
171
  ---
package/bin/openclew.js CHANGED
@@ -10,7 +10,7 @@ openclew — Long Life Memory for LLMs
10
10
 
11
11
  Usage:
12
12
  openclew init Set up openclew in the current project
13
- openclew new <title> Create a living doc (evolves with the project)
13
+ openclew new <title> Create a refdoc (evolves with the project)
14
14
  openclew log <title> Create a session log (frozen facts)
15
15
  openclew checkout End-of-session summary + log creation
16
16
  openclew index Regenerate doc/_INDEX.md
@@ -23,7 +23,7 @@ Options:
23
23
  Getting started:
24
24
  npx openclew init 1. Set up doc/ + guide + examples + git hook
25
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
26
+ openclew new "API design" 3. Create your own refdocs
27
27
  git commit 4. Index auto-regenerates on commit
28
28
 
29
29
  Docs have 3 levels: L1 (metadata) → L2 (summary) → L3 (details).
@@ -2,8 +2,8 @@
2
2
  """
3
3
  openclew index generator.
4
4
 
5
- Scans doc/_*.md (living docs) and doc/log/*.md (logs),
6
- parses L1 metadata blocks, and generates doc/_INDEX.md.
5
+ Scans doc/_*.md (refdocs) and doc/log/*.md (logs),
6
+ parses metadata line + L1 blocks, and generates doc/_INDEX.md.
7
7
 
8
8
  Usage:
9
9
  python generate-index.py # from project root
@@ -34,23 +34,67 @@ def find_doc_dir():
34
34
  return doc_dir
35
35
 
36
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
37
+ def parse_metadata_line(content):
38
+ """Extract metadata from the first line (before L1_START).
39
+
40
+ Format: openclew@VERSION · key: value · key: value · ...
41
+ """
42
+ meta = {}
43
+ first_line = content.split("\n", 1)[0].strip()
44
+ if not first_line.startswith("openclew@"):
45
+ return meta
46
+
47
+ parts = first_line.split(" · ")
48
+ for part in parts:
49
+ part = part.strip()
50
+ if part.startswith("openclew@"):
51
+ meta["version"] = part.split("@", 1)[1]
52
+ continue
53
+ if ":" in part:
54
+ key, _, value = part.partition(":")
55
+ meta[key.strip().lower()] = value.strip()
56
+
57
+ return meta
43
58
 
59
+
60
+ def parse_l1(content):
61
+ """Extract L1 fields (subject, doc_brief) from L1_START/L1_END block."""
62
+ meta = {}
44
63
  match = re.search(
45
64
  r"<!--\s*L1_START\s*-->(.+?)<!--\s*L1_END\s*-->",
46
65
  content,
47
66
  re.DOTALL,
48
67
  )
49
68
  if not match:
50
- return None
69
+ return meta
51
70
 
52
71
  block = match.group(1)
72
+
73
+ # Extract **subject:** value
74
+ subject_match = re.search(r"\*\*subject:\*\*\s*(.+)", block)
75
+ if subject_match:
76
+ meta["subject"] = subject_match.group(1).strip()
77
+
78
+ # Extract **doc_brief:** value
79
+ brief_match = re.search(r"\*\*doc_brief:\*\*\s*(.+)", block)
80
+ if brief_match:
81
+ meta["doc_brief"] = brief_match.group(1).strip()
82
+
83
+ return meta
84
+
85
+
86
+ def parse_l1_legacy(content):
87
+ """Fallback parser for old format (key: value lines inside L1 block)."""
53
88
  meta = {}
89
+ match = re.search(
90
+ r"<!--\s*L1_START\s*-->(.+?)<!--\s*L1_END\s*-->",
91
+ content,
92
+ re.DOTALL,
93
+ )
94
+ if not match:
95
+ return meta
96
+
97
+ block = match.group(1)
54
98
  for line in block.splitlines():
55
99
  line = line.strip()
56
100
  if line.startswith("#") or not line:
@@ -62,31 +106,56 @@ def parse_l1(filepath):
62
106
  return meta
63
107
 
64
108
 
109
+ def parse_file(filepath):
110
+ """Parse a file and return combined metadata + L1 fields."""
111
+ try:
112
+ content = filepath.read_text(encoding="utf-8")
113
+ except (OSError, UnicodeDecodeError):
114
+ return None
115
+
116
+ # Try new format first
117
+ meta_line = parse_metadata_line(content)
118
+ l1 = parse_l1(content)
119
+
120
+ if l1.get("subject"):
121
+ # New format
122
+ meta_line.update(l1)
123
+ return meta_line
124
+
125
+ # Fallback to legacy format
126
+ legacy = parse_l1_legacy(content)
127
+ if legacy:
128
+ meta_line.update(legacy)
129
+ return meta_line
130
+
131
+ return None
132
+
133
+
65
134
  def collect_docs(doc_dir):
66
- """Collect living docs and logs with their L1 metadata."""
67
- living_docs = []
135
+ """Collect refdocs and logs with their metadata."""
136
+ refdocs = []
68
137
  logs = []
69
138
 
70
- # Living docs: doc/_*.md
139
+ # Refdocs: doc/_*.md
71
140
  for f in sorted(doc_dir.glob("_*.md")):
72
141
  if f.name == "_INDEX.md":
73
142
  continue
74
- meta = parse_l1(f)
143
+ meta = parse_file(f)
75
144
  if meta:
76
- living_docs.append((f, meta))
145
+ refdocs.append((f, meta))
77
146
 
78
147
  # Log docs: doc/log/*.md
79
148
  log_dir = doc_dir / "log"
80
149
  if log_dir.is_dir():
81
150
  for f in sorted(log_dir.glob("*.md"), reverse=True):
82
- meta = parse_l1(f)
151
+ meta = parse_file(f)
83
152
  if meta:
84
153
  logs.append((f, meta))
85
154
 
86
- return living_docs, logs
155
+ return refdocs, logs
87
156
 
88
157
 
89
- def generate_index(doc_dir, living_docs, logs):
158
+ def generate_index(doc_dir, refdocs, logs):
90
159
  """Generate _INDEX.md content."""
91
160
  now = datetime.now().strftime("%Y-%m-%d %H:%M")
92
161
  lines = [
@@ -97,13 +166,13 @@ def generate_index(doc_dir, living_docs, logs):
97
166
  f"",
98
167
  ]
99
168
 
100
- # Living docs section
101
- lines.append("## Living docs")
169
+ # Refdocs section
170
+ lines.append("## Refdocs")
102
171
  lines.append("")
103
- if living_docs:
172
+ if refdocs:
104
173
  lines.append("| Document | Subject | Status | Category |")
105
174
  lines.append("|----------|---------|--------|----------|")
106
- for f, meta in living_docs:
175
+ for f, meta in refdocs:
107
176
  name = f.name
108
177
  subject = meta.get("subject", "—")
109
178
  status = meta.get("status", "—")
@@ -111,7 +180,7 @@ def generate_index(doc_dir, living_docs, logs):
111
180
  rel_path = f.relative_to(doc_dir.parent)
112
181
  lines.append(f"| [{name}]({rel_path}) | {subject} | {status} | {category} |")
113
182
  else:
114
- lines.append("_No living docs yet. Create one with `templates/living.md`._")
183
+ lines.append("_No refdocs yet. Create one with `templates/refdoc.md`._")
115
184
  lines.append("")
116
185
 
117
186
  # Logs section (last 20)
@@ -137,7 +206,7 @@ def generate_index(doc_dir, living_docs, logs):
137
206
 
138
207
  # Stats
139
208
  lines.append("---")
140
- lines.append(f"**{len(living_docs)}** living docs, **{len(logs)}** logs.")
209
+ lines.append(f"**{len(refdocs)}** refdocs, **{len(logs)}** logs.")
141
210
  lines.append("")
142
211
 
143
212
  return "\n".join(lines)
@@ -145,12 +214,12 @@ def generate_index(doc_dir, living_docs, logs):
145
214
 
146
215
  def main():
147
216
  doc_dir = find_doc_dir()
148
- living_docs, logs = collect_docs(doc_dir)
149
- index_content = generate_index(doc_dir, living_docs, logs)
217
+ refdocs, logs = collect_docs(doc_dir)
218
+ index_content = generate_index(doc_dir, refdocs, logs)
150
219
 
151
220
  index_path = doc_dir / "_INDEX.md"
152
221
  index_path.write_text(index_content, encoding="utf-8")
153
- print(f"Generated {index_path} ({len(living_docs)} living docs, {len(logs)} logs)")
222
+ print(f"Generated {index_path} ({len(refdocs)} refdocs, {len(logs)} logs)")
154
223
 
155
224
 
156
225
  if __name__ == "__main__":
package/lib/checkout.js CHANGED
@@ -10,9 +10,18 @@
10
10
  const fs = require("fs");
11
11
  const path = require("path");
12
12
  const { execSync } = require("child_process");
13
- const { logContent, slugifyLog, today } = require("./templates");
13
+ const { slugifyLog, today } = require("./templates");
14
14
  const { readConfig } = require("./config");
15
15
 
16
+ function ocVersion() {
17
+ try {
18
+ const pkg = require(path.join(__dirname, "..", "package.json"));
19
+ return pkg.version;
20
+ } catch {
21
+ return "0.0.0";
22
+ }
23
+ }
24
+
16
25
  const PROJECT_ROOT = process.cwd();
17
26
  const DOC_DIR = path.join(PROJECT_ROOT, "doc");
18
27
  const LOG_DIR = path.join(DOC_DIR, "log");
@@ -53,12 +62,12 @@ function collectGitActivity() {
53
62
  ? fs.readdirSync(LOG_DIR).filter((f) => f.startsWith(date))
54
63
  : [];
55
64
 
56
- // Living docs
57
- const livingDocs = fs.existsSync(DOC_DIR)
65
+ // Refdocs
66
+ const refdocs = fs.existsSync(DOC_DIR)
58
67
  ? fs.readdirSync(DOC_DIR).filter((f) => f.startsWith("_") && f !== "_INDEX.md" && f.endsWith(".md"))
59
68
  : [];
60
69
 
61
- return { date, commits, uncommitted, files, existingLogs, livingDocs };
70
+ return { date, commits, uncommitted, files, existingLogs, refdocs };
62
71
  }
63
72
 
64
73
  function extractActions(commits) {
@@ -98,7 +107,7 @@ function typeLabel(type) {
98
107
  }
99
108
 
100
109
  function displaySummary(activity) {
101
- const { date, commits, uncommitted, existingLogs, livingDocs } = activity;
110
+ const { date, commits, uncommitted, existingLogs, refdocs } = activity;
102
111
  const actions = extractActions(commits);
103
112
 
104
113
  console.log(`\nopenclew checkout — ${date}\n`);
@@ -152,10 +161,10 @@ function displaySummary(activity) {
152
161
  }
153
162
  console.log("");
154
163
 
155
- // Living docs reminder
156
- if (livingDocs.length > 0) {
157
- console.log(" 📚 Living docs — check if any need updating:");
158
- for (const doc of livingDocs) {
164
+ // Refdocs reminder
165
+ if (refdocs.length > 0) {
166
+ console.log(" 📚 Refdocs — check if any need updating:");
167
+ for (const doc of refdocs) {
159
168
  console.log(` ${doc}`);
160
169
  }
161
170
  console.log("");
@@ -189,15 +198,14 @@ function generateSessionLog(activity, actions) {
189
198
  .map((a) => `- ${typeLabel(a.type)}: ${a.desc} (${a.hash})`)
190
199
  .join("\n");
191
200
 
192
- const content = `<!-- L1_START -->
193
- # L1 - Metadata
194
- date: ${date}
195
- type: ${actions.length === 1 ? actions[0].type === "fix" ? "Bug" : "Feature" : "Feature"}
196
- subject: ${sessionTitle}
197
- short_story: ${actions.map((a) => a.desc).join(". ")}.
198
- status: Done
199
- category:
200
- keywords: ${keywordsStr}
201
+ const ver = ocVersion();
202
+ const logType = actions.length === 1 ? actions[0].type === "fix" ? "Bug" : "Feature" : "Feature";
203
+ const content = `openclew@${ver} · date: ${date} · type: ${logType} · status: Done · category: · keywords: ${keywordsStr}
204
+
205
+ <!-- L1_START -->
206
+ **subject:** ${sessionTitle}
207
+
208
+ **doc_brief:** ${actions.map((a) => a.desc).join(". ")}.
201
209
  <!-- L1_END -->
202
210
 
203
211
  ---
package/lib/init.js CHANGED
@@ -15,7 +15,7 @@ const readline = require("readline");
15
15
  const { detectInstructionFiles, findAgentsMdCaseInsensitive } = require("./detect");
16
16
  const { inject, isAlreadyInjected } = require("./inject");
17
17
  const { writeConfig } = require("./config");
18
- const { guideContent, exampleLivingDocContent, exampleLogContent, today } = require("./templates");
18
+ const { guideContent, exampleRefdocContent, exampleLogContent, today } = require("./templates");
19
19
 
20
20
  const PROJECT_ROOT = process.cwd();
21
21
  const DOC_DIR = path.join(PROJECT_ROOT, "doc");
@@ -150,6 +150,24 @@ fi`;
150
150
  return true;
151
151
  }
152
152
 
153
+ function updateGitignore() {
154
+ const gitignorePath = path.join(PROJECT_ROOT, ".gitignore");
155
+ const entry = "doc/log/";
156
+
157
+ if (fs.existsSync(gitignorePath)) {
158
+ const content = fs.readFileSync(gitignorePath, "utf-8");
159
+ if (content.includes(entry)) {
160
+ console.log(" .gitignore already ignores doc/log/");
161
+ return;
162
+ }
163
+ fs.appendFileSync(gitignorePath, `\n${entry}\n`, "utf-8");
164
+ console.log(" Added doc/log/ to .gitignore");
165
+ } else {
166
+ fs.writeFileSync(gitignorePath, `${entry}\n`, "utf-8");
167
+ console.log(" Created .gitignore with doc/log/");
168
+ }
169
+ }
170
+
153
171
  function copyGenerateIndex() {
154
172
  const src = path.join(__dirname, "..", "hooks", "generate-index.py");
155
173
  const dst = path.join(DOC_DIR, "generate-index.py");
@@ -179,11 +197,11 @@ function createDocs() {
179
197
  console.log(" doc/_USING_OPENCLEW.md already exists");
180
198
  }
181
199
 
182
- // Example living doc
200
+ // Example refdoc
183
201
  const examplePath = path.join(DOC_DIR, "_ARCHITECTURE.md");
184
202
  if (!fs.existsSync(examplePath)) {
185
- fs.writeFileSync(examplePath, exampleLivingDocContent(), "utf-8");
186
- console.log(" Created doc/_ARCHITECTURE.md (example living doc)");
203
+ fs.writeFileSync(examplePath, exampleRefdocContent(), "utf-8");
204
+ console.log(" Created doc/_ARCHITECTURE.md (example refdoc)");
187
205
  } else {
188
206
  console.log(" doc/_ARCHITECTURE.md already exists");
189
207
  }
@@ -218,12 +236,16 @@ async function main() {
218
236
  console.log("1. Project structure");
219
237
  createDirs();
220
238
 
221
- // Step 2: Copy index generator
222
- console.log("\n2. Index generator");
239
+ // Step 2: Gitignore
240
+ console.log("\n2. Gitignore");
241
+ updateGitignore();
242
+
243
+ // Step 3: Copy index generator
244
+ console.log("\n3. Index generator");
223
245
  copyGenerateIndex();
224
246
 
225
- // Step 3: Entry point
226
- console.log("\n3. Entry point");
247
+ // Step 4: Entry point
248
+ console.log("\n4. Entry point");
227
249
  const entryPoint = await resolveEntryPoint();
228
250
 
229
251
  if (entryPoint) {
@@ -241,20 +263,20 @@ async function main() {
241
263
  writeConfig({ entryPoint: null }, PROJECT_ROOT);
242
264
  }
243
265
 
244
- // Step 4: Pre-commit hook
245
- console.log("\n4. Pre-commit hook");
266
+ // Step 5: Pre-commit hook
267
+ console.log("\n5. Pre-commit hook");
246
268
  if (noHook) {
247
269
  console.log(" Skipping (--no-hook)");
248
270
  } else {
249
271
  installPreCommitHook();
250
272
  }
251
273
 
252
- // Step 5: Docs
253
- console.log("\n5. Docs");
274
+ // Step 6: Docs
275
+ console.log("\n6. Docs");
254
276
  createDocs();
255
277
 
256
- // Step 6: Generate index
257
- console.log("\n6. Index");
278
+ // Step 7: Generate index
279
+ console.log("\n7. Index");
258
280
  runIndexGenerator();
259
281
 
260
282
  // Done
package/lib/inject.js CHANGED
@@ -12,7 +12,7 @@ This file is the **entry point** for project documentation.
12
12
  **Doc-first rule:** before any task, read \`doc/_INDEX.md\` to find docs related to the task. Read them before exploring code.
13
13
 
14
14
  Two types of docs in \`doc/\`:
15
- - **Living docs** (\`doc/_*.md\`) — evolve with the project (architecture, conventions, decisions)
15
+ - **Refdocs** (\`doc/_*.md\`) — evolve with the project (architecture, conventions, decisions)
16
16
  - **Logs** (\`doc/log/YYYY-MM-DD_*.md\`) — frozen facts from a session, never modified after
17
17
 
18
18
  Each doc has 3 levels: **L1** (metadata — read first to decide relevance) → **L2** (summary) → **L3** (full details, only when needed).
package/lib/new-doc.js CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * openclew new <title> — create a new living doc.
2
+ * openclew new <title> — create a new refdoc.
3
3
  */
4
4
 
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
- const { livingContent, slugify } = require("./templates");
7
+ const { refdocContent, slugify } = require("./templates");
8
8
  const { readConfig } = require("./config");
9
9
 
10
10
  const args = process.argv.slice(2);
@@ -37,7 +37,7 @@ if (fs.existsSync(filepath)) {
37
37
  process.exit(1);
38
38
  }
39
39
 
40
- fs.writeFileSync(filepath, livingContent(title), "utf-8");
40
+ fs.writeFileSync(filepath, refdocContent(title), "utf-8");
41
41
  console.log(`Created doc/${filename}`);
42
42
  console.log("");
43
43
  console.log("Next: open the file and fill in:");
package/lib/new-log.js CHANGED
@@ -42,7 +42,7 @@ fs.writeFileSync(filepath, logContent(title), "utf-8");
42
42
  console.log(`Created doc/log/${filename}`);
43
43
  console.log("");
44
44
  console.log("Next: open the file and fill in:");
45
- console.log(" L1 — type, status, short_story (what happened in 1-2 sentences)");
45
+ console.log(" L1 — subject + doc_brief (what happened in 1-2 sentences)");
46
46
  console.log(" L2 — problem + solution (the facts, frozen after this session)");
47
47
  console.log("");
48
48
  console.log("Logs are immutable — once written, never modified.");
package/lib/templates.js CHANGED
@@ -1,8 +1,10 @@
1
1
  /**
2
- * Template content for living docs and logs.
2
+ * Template content for refdocs and logs.
3
3
  * Embedded here so the CLI works standalone without needing to locate template files.
4
4
  */
5
5
 
6
+ const path = require("path");
7
+
6
8
  function today() {
7
9
  return new Date().toISOString().slice(0, 10);
8
10
  }
@@ -21,18 +23,24 @@ function slugifyLog(title) {
21
23
  .replace(/^-|-$/g, "");
22
24
  }
23
25
 
24
- function livingContent(title) {
26
+ function ocVersion() {
27
+ try {
28
+ const pkg = require(path.join(__dirname, "..", "package.json"));
29
+ return pkg.version;
30
+ } catch {
31
+ return "0.0.0";
32
+ }
33
+ }
34
+
35
+ function refdocContent(title) {
25
36
  const date = today();
26
- return `<!-- L1_START -->
27
- # L1 - Metadata
28
- type: Reference
29
- subject: ${title}
30
- created: ${date}
31
- updated: ${date}
32
- short_story:
33
- status: Active
34
- category:
35
- keywords: []
37
+ const ver = ocVersion();
38
+ return `openclew@${ver} · created: ${date} · updated: ${date} · type: Reference · status: Active · category: · keywords: []
39
+
40
+ <!-- L1_START -->
41
+ **subject:** ${title}
42
+
43
+ **doc_brief:**
36
44
  <!-- L1_END -->
37
45
 
38
46
  ---
@@ -67,15 +75,13 @@ keywords: []
67
75
 
68
76
  function logContent(title) {
69
77
  const date = today();
70
- return `<!-- L1_START -->
71
- # L1 - Metadata
72
- date: ${date}
73
- type: Feature
74
- subject: ${title}
75
- short_story:
76
- status: In progress
77
- category:
78
- keywords: []
78
+ const ver = ocVersion();
79
+ return `openclew@${ver} · date: ${date} · type: Feature · status: In progress · category: · keywords: []
80
+
81
+ <!-- L1_START -->
82
+ **subject:** ${title}
83
+
84
+ **doc_brief:**
79
85
  <!-- L1_END -->
80
86
 
81
87
  ---
@@ -109,16 +115,13 @@ keywords: []
109
115
  */
110
116
  function guideContent() {
111
117
  const date = today();
112
- return `<!-- L1_START -->
113
- # L1 - Metadata
114
- type: Guide
115
- subject: How openclew works
116
- created: ${date}
117
- updated: ${date}
118
- short_story: How openclew structures project knowledge in 3 levels (L1/L2/L3) so AI agents and humans navigate efficiently.
119
- status: Active
120
- category: Documentation
121
- keywords: [openclew, L1, L2, L3, index, living-doc, log]
118
+ const ver = ocVersion();
119
+ return `openclew@${ver} · created: ${date} · updated: ${date} · type: Guide · status: Active · category: Documentation · keywords: [openclew, L1, L2, L3, index, refdoc, log]
120
+
121
+ <!-- L1_START -->
122
+ **subject:** How openclew works
123
+
124
+ **doc_brief:** How openclew structures project knowledge in 3 levels (L1/L2/L3) so AI agents and humans navigate efficiently.
122
125
  <!-- L1_END -->
123
126
 
124
127
  ---
@@ -136,7 +139,7 @@ Before starting any task, read \`doc/_INDEX.md\` to find docs related to the tas
136
139
 
137
140
  ## Two types of docs
138
141
 
139
- **Living docs** (\`doc/_*.md\`): knowledge that evolves with the project.
142
+ **Refdocs** (\`doc/_*.md\`): knowledge that evolves with the project.
140
143
  Architecture decisions, conventions, known pitfalls — anything that stays relevant over time.
141
144
  Naming: \`doc/_UPPER_SNAKE_CASE.md\` (e.g. \`doc/_AUTH_DESIGN.md\`)
142
145
 
@@ -144,13 +147,17 @@ Naming: \`doc/_UPPER_SNAKE_CASE.md\` (e.g. \`doc/_AUTH_DESIGN.md\`)
144
147
  What happened, what was decided, what was tried. Never modified after the session.
145
148
  Naming: \`doc/log/YYYY-MM-DD_lowercase-slug.md\` (e.g. \`doc/log/2026-01-15_setup-auth.md\`)
146
149
 
147
- ## Three levels per doc
150
+ ## Document structure
151
+
152
+ Every doc has a metadata line + 3 levels. Read only what you need:
148
153
 
149
- Every doc has 3 levels. Read only what you need:
154
+ **Line 1 Metadata**: version, date, type, status, category, keywords. For indexing and triage.
150
155
 
151
- - **L1 — Metadata** (~40 tokens): subject, keywords, status. Read this first to decide if the doc is relevant.
152
- - **L2 — Summary**: the essential context — objective, key points, decisions.
153
- - **L3Details**: full technical content. Only read when deep-diving.
156
+ **L1 — Subject + Brief** (~40 tokens): what the doc is about and what it concludes. Read this first to decide if the doc is relevant.
157
+
158
+ **L2Summary**: the essential context objective, key points, decisions.
159
+
160
+ **L3 — Details**: full technical content. Only read when deep-diving.
154
161
 
155
162
  ## Index
156
163
 
@@ -163,21 +170,17 @@ Never edit it manually. To force a rebuild: \`openclew index\`
163
170
  <!-- L3_START -->
164
171
  # L3 - Details
165
172
 
166
- ## Creating a living doc
173
+ ## Creating a refdoc
167
174
 
168
175
  Create \`doc/_TITLE.md\` (uppercase snake_case) with this structure:
169
176
 
170
177
  \`\`\`
178
+ openclew@${ver} · created: YYYY-MM-DD · updated: YYYY-MM-DD · type: Reference · status: Active · category: · keywords: []
179
+
171
180
  <!-- L1_START -->
172
- # L1 - Metadata
173
- type: Reference
174
- subject: Title
175
- created: YYYY-MM-DD
176
- updated: YYYY-MM-DD
177
- short_story:
178
- status: Active
179
- category:
180
- keywords: []
181
+ **subject:** Title
182
+
183
+ **doc_brief:**
181
184
  <!-- L1_END -->
182
185
 
183
186
  ---
@@ -205,15 +208,12 @@ keywords: []
205
208
  Create \`doc/log/YYYY-MM-DD_slug.md\` (lowercase, hyphens) with this structure:
206
209
 
207
210
  \`\`\`
211
+ openclew@${ver} · date: YYYY-MM-DD · type: Feature · status: In progress · category: · keywords: []
212
+
208
213
  <!-- L1_START -->
209
- # L1 - Metadata
210
- date: YYYY-MM-DD
211
- type: Feature
212
- subject: Title
213
- short_story:
214
- status: In progress
215
- category:
216
- keywords: []
214
+ **subject:** Title
215
+
216
+ **doc_brief:**
217
217
  <!-- L1_END -->
218
218
 
219
219
  ---
@@ -243,9 +243,10 @@ Logs are immutable — once the session ends, the log is never modified.
243
243
 
244
244
  1. At session start: read the entry point file
245
245
  2. Before any task: read \`doc/_INDEX.md\`, scan L1 metadata, identify relevant docs
246
- 3. Read L2 of relevant docs for context
247
- 4. Only read L3 when you need implementation details
248
- 5. After significant work: create or update living docs and logs directly
246
+ 3. Read L1 (subject + brief) of relevant docs to confirm relevance
247
+ 4. Read L2 for context
248
+ 5. Only read L3 when you need implementation details
249
+ 6. After significant work: create or update refdocs and logs directly
249
250
 
250
251
  The index (\`doc/_INDEX.md\`) auto-regenerates on every git commit. To force a rebuild: \`openclew index\`
251
252
 
@@ -261,20 +262,17 @@ The index (\`doc/_INDEX.md\`) auto-regenerates on every git commit. To force a r
261
262
  }
262
263
 
263
264
  /**
264
- * Example living doc — shows what a filled-in doc looks like.
265
+ * Example refdoc — shows what a filled-in doc looks like.
265
266
  */
266
- function exampleLivingDocContent() {
267
+ function exampleRefdocContent() {
267
268
  const date = today();
268
- return `<!-- L1_START -->
269
- # L1 - Metadata
270
- type: Reference
271
- subject: Architecture overview
272
- created: ${date}
273
- updated: ${date}
274
- short_story: High-level architecture of the project — components, data flow, key decisions.
275
- status: Active
276
- category: Architecture
277
- keywords: [architecture, overview, components]
269
+ const ver = ocVersion();
270
+ return `openclew@${ver} · created: ${date} · updated: ${date} · type: Reference · status: Active · category: Architecture · keywords: [architecture, overview, components]
271
+
272
+ <!-- L1_START -->
273
+ **subject:** Architecture overview
274
+
275
+ **doc_brief:** High-level architecture of the project — components, data flow, key decisions.
278
276
  <!-- L1_END -->
279
277
 
280
278
  ---
@@ -298,7 +296,7 @@ Document the high-level architecture so new contributors and AI agents understan
298
296
 
299
297
  <!-- Replace this with your actual architecture details -->
300
298
 
301
- This is an example living doc created by \`openclew init\`.
299
+ This is an example refdoc created by \`openclew init\`.
302
300
  Edit it to document your project's architecture, or delete it and create your own.
303
301
 
304
302
  ---
@@ -317,15 +315,13 @@ Edit it to document your project's architecture, or delete it and create your ow
317
315
  */
318
316
  function exampleLogContent() {
319
317
  const date = today();
320
- return `<!-- L1_START -->
321
- # L1 - Metadata
322
- date: ${date}
323
- type: Feature
324
- subject: Set up openclew
325
- short_story: Initialized openclew for structured project knowledge. Created doc/ structure, git hook, guide, and example docs.
326
- status: Done
327
- category: Tooling
328
- keywords: [openclew, setup, documentation]
318
+ const ver = ocVersion();
319
+ return `openclew@${ver} · date: ${date} · type: Feature · status: Done · category: Tooling · keywords: [openclew, setup, documentation]
320
+
321
+ <!-- L1_START -->
322
+ **subject:** Set up openclew
323
+
324
+ **doc_brief:** Initialized openclew for structured project knowledge. Created doc/ structure, git hook, guide, and example docs.
329
325
  <!-- L1_END -->
330
326
 
331
327
  ---
@@ -340,7 +336,7 @@ Set up structured documentation so AI agents and new contributors can navigate p
340
336
  Project knowledge was scattered — README, inline comments, tribal knowledge. Each new AI session started from zero.
341
337
 
342
338
  ## Solution
343
- Installed openclew. Every doc now has L1 (metadata for triage), L2 (summary for context), L3 (details when needed).
339
+ Installed openclew. Every doc now has a metadata line (for triage) + L1 (subject and brief), L2 (summary for context), L3 (details when needed).
344
340
  The index auto-regenerates on each commit via a git hook.
345
341
  <!-- L2_END -->
346
342
 
@@ -351,16 +347,16 @@ The index auto-regenerates on each commit via a git hook.
351
347
 
352
348
  This log was created by \`openclew init\`.
353
349
  It shows what a filled-in log looks like. Logs are immutable — once the session ends, the log is frozen.
354
- For evolving knowledge, use living docs (\`doc/_*.md\`).
350
+ For evolving knowledge, use refdocs (\`doc/_*.md\`).
355
351
  <!-- L3_END -->
356
352
  `;
357
353
  }
358
354
 
359
355
  module.exports = {
360
- livingContent,
356
+ refdocContent,
361
357
  logContent,
362
358
  guideContent,
363
- exampleLivingDocContent,
359
+ exampleRefdocContent,
364
360
  exampleLogContent,
365
361
  slugify,
366
362
  slugifyLog,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclew",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Long Life Memory for LLMs — structured project knowledge for AI agents and humans",
5
5
  "license": "MIT",
6
6
  "bin": {
package/templates/log.md CHANGED
@@ -1,12 +1,9 @@
1
+ openclew@VERSION · date: YYYY-MM-DD · type: Bug | Feature | Refactor | Doc | Deploy · status: Done | In progress | Abandoned · category: Main domain · keywords: [tag1, tag2, tag3]
2
+
1
3
  <!-- L1_START -->
2
- # L1 - Metadata
3
- date: YYYY-MM-DD
4
- type: Bug | Feature | Refactor | Doc | Deploy
5
- subject: Short title (< 60 chars)
6
- short_story: 1-2 sentences. What happened and what was the outcome. Include conclusions, not just process.
7
- status: Done | In progress | Abandoned
8
- category: Main domain (e.g. Auth, API, Database, UI...)
9
- keywords: [tag1, tag2, tag3]
4
+ **subject:** Short title (< 60 chars)
5
+
6
+ **doc_brief:** 1-2 sentences. What happened and what was the outcome. Include conclusions, not just process.
10
7
  <!-- L1_END -->
11
8
 
12
9
  ---
@@ -1,13 +1,9 @@
1
+ openclew@VERSION · created: YYYY-MM-DD · updated: YYYY-MM-DD · type: Reference | Architecture | Guide | Analysis · status: Active | Stable | Archived · category: Main domain · keywords: [tag1, tag2, tag3]
2
+
1
3
  <!-- L1_START -->
2
- # L1 - Metadata
3
- type: Reference | Architecture | Guide | Analysis
4
- subject: Short title (< 60 chars)
5
- created: YYYY-MM-DD
6
- updated: YYYY-MM-DD
7
- short_story: 1-2 sentences. What this doc covers and what it concludes. Must be enough to decide if you need to read further.
8
- status: Active | Stable | Archived
9
- category: Main domain (e.g. Auth, API, Database, UI...)
10
- keywords: [tag1, tag2, tag3]
4
+ **subject:** Short title (< 60 chars)
5
+
6
+ **doc_brief:** 1-2 sentences. What this doc covers and what it concludes. Must be enough to decide if you need to read further.
11
7
  <!-- L1_END -->
12
8
 
13
9
  ---