openclew 0.4.0 → 0.5.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/UPGRADING.md CHANGED
@@ -133,6 +133,20 @@ openclew migrate # should show "0 to migrate"
133
133
 
134
134
  ## Version-specific notes
135
135
 
136
+ ### → 0.5.0 (init guard + global config)
137
+
138
+ `init` now creates `~/.openclew/config.json` on every run. If you are not inside
139
+ a project directory (no `.git`, `package.json`, etc.), init stops after creating
140
+ the global config — it no longer creates `doc/` or `AGENTS.md` in random
141
+ directories.
142
+
143
+ New command: `openclew peek` — displays the doc index.
144
+
145
+ New: `scripts/qa.py` — QA checklist in box-drawing format (dev only, not
146
+ published to npm).
147
+
148
+ License changed from MIT to Apache 2.0.
149
+
136
150
  ### → 0.4.0 (format migration)
137
151
 
138
152
  First migration release. Converts from the legacy format (YAML frontmatter,
package/bin/openclew.js CHANGED
@@ -34,6 +34,7 @@ Usage:
34
34
  Advanced:
35
35
  openclew status Documentation health dashboard
36
36
  openclew index Regenerate doc/_INDEX.md
37
+ openclew session-header Format session header line
37
38
  openclew mcp Start MCP server (stdio JSON-RPC)
38
39
 
39
40
  Options (init):
@@ -72,6 +73,7 @@ const commands = {
72
73
  peek: () => require("../lib/peek"),
73
74
  status: () => require("../lib/status"),
74
75
  index: () => require("../lib/index-gen"),
76
+ "session-header": () => require("../lib/session-header"),
75
77
  mcp: () => require("../lib/mcp-server"),
76
78
  };
77
79
 
package/lib/init.js CHANGED
@@ -1,26 +1,67 @@
1
1
  /**
2
2
  * openclew init — set up openclew in the current project.
3
3
  *
4
- * 1. Create doc/ and doc/log/
5
- * 2. Detect entry point (AGENTS.md case-insensitive by default)
6
- * 3. Inject openclew block into entry point
7
- * 4. Install pre-commit hook for index generation
8
- * 5. Create guide + example docs
9
- * 6. Generate initial _INDEX.md
4
+ * 0. Create ~/.openclew/ (global config)
5
+ * 1. Detect if we're in a project (project markers)
6
+ * - If not: stop after global config
7
+ * - If yes: proceed with project init
8
+ * 2. Create doc/ and doc/log/
9
+ * 3. Detect entry point (AGENTS.md case-insensitive by default)
10
+ * 4. Inject openclew block into entry point
11
+ * 5. Install pre-commit hook for index generation
12
+ * 6. Create guide + example docs
13
+ * 7. Generate initial _INDEX.md
10
14
  */
11
15
 
12
16
  const fs = require("fs");
13
17
  const path = require("path");
18
+ const os = require("os");
14
19
  const readline = require("readline");
15
20
  const { detectInstructionFiles, findAgentsMdCaseInsensitive } = require("./detect");
16
21
  const { inject, isAlreadyInjected } = require("./inject");
17
22
  const { writeConfig } = require("./config");
18
- const { guideContent, frameworkIntegrationContent, exampleRefdocContent, exampleLogContent, today } = require("./templates");
23
+ const { guideContent, frameworkIntegrationContent, exampleRefdocContent, exampleLogContent, today, ocVersion } = require("./templates");
19
24
 
20
25
  const PROJECT_ROOT = process.cwd();
21
26
  const DOC_DIR = path.join(PROJECT_ROOT, "doc");
22
27
  const LOG_DIR = path.join(DOC_DIR, "log");
23
28
  const GIT_DIR = path.join(PROJECT_ROOT, ".git");
29
+ const OPENCLEW_HOME = path.join(os.homedir(), ".openclew");
30
+
31
+ const PROJECT_MARKERS = [".git", "package.json", "Cargo.toml", "pyproject.toml", "go.mod", "Gemfile", "composer.json", "Makefile", "pom.xml", "build.gradle", "CMakeLists.txt", "setup.py", "setup.cfg"];
32
+
33
+ function isProjectDir() {
34
+ return PROJECT_MARKERS.some((m) => fs.existsSync(path.join(PROJECT_ROOT, m)));
35
+ }
36
+
37
+ function ensureGlobalDir() {
38
+ const created = !fs.existsSync(OPENCLEW_HOME);
39
+ if (created) {
40
+ fs.mkdirSync(OPENCLEW_HOME, { recursive: true });
41
+ }
42
+
43
+ const configPath = path.join(OPENCLEW_HOME, "config.json");
44
+ if (!fs.existsSync(configPath)) {
45
+ fs.writeFileSync(
46
+ configPath,
47
+ JSON.stringify({ version: ocVersion(), installedAt: today() }, null, 2) + "\n",
48
+ "utf-8"
49
+ );
50
+ } else {
51
+ // Update version on each init
52
+ try {
53
+ const cfg = JSON.parse(fs.readFileSync(configPath, "utf-8"));
54
+ cfg.version = ocVersion();
55
+ fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
56
+ } catch {}
57
+ }
58
+
59
+ if (created) {
60
+ console.log(" Created ~/.openclew/");
61
+ } else {
62
+ console.log(" ~/.openclew/ already exists");
63
+ }
64
+ }
24
65
 
25
66
  const args = process.argv.slice(2);
26
67
  const noHook = args.includes("--no-hook");
@@ -295,8 +336,20 @@ function installSlashCommands() {
295
336
  async function main() {
296
337
  console.log("\nopenclew init\n");
297
338
 
339
+ // Step 0: Global config (~/.openclew/)
340
+ console.log("0. Global config");
341
+ ensureGlobalDir();
342
+
343
+ // Check if we're in a project
344
+ if (!isProjectDir()) {
345
+ console.log(`\n─── Done ───\n`);
346
+ console.log(` openclew installed → ~/.openclew/`);
347
+ console.log("");
348
+ return;
349
+ }
350
+
298
351
  // Step 1: Create directories
299
- console.log("1. Project structure");
352
+ console.log("\n1. Project structure");
300
353
  createDirs();
301
354
 
302
355
  // Step 2: Gitignore
@@ -0,0 +1,60 @@
1
+ /**
2
+ * openclew session-header — format a session header line.
3
+ *
4
+ * Usage:
5
+ * openclew session-header --name "Refonte checkout" --tags voice,stt,voxtral
6
+ * openclew session-header --name "Fix streaming" --tags streaming --date 2026-03-27
7
+ *
8
+ * Output:
9
+ * 📅 2026-03-28 🏷️ Refonte checkout ----- #voice #stt #voxtral
10
+ */
11
+
12
+ const { today } = require("./templates");
13
+
14
+ function formatSessionHeader(name, tags, date) {
15
+ if (!name) {
16
+ console.error("Error: --name is required");
17
+ console.error(
18
+ 'Usage: openclew session-header --name "Session title" --tags tag1,tag2'
19
+ );
20
+ process.exit(1);
21
+ }
22
+
23
+ const d = date || today();
24
+ const hashTags = tags.length > 0 ? tags.map((t) => `#${t}`).join(" ") : "";
25
+ const line = `📅 ${d} 🏷️ ${name} ----- ${hashTags}`.trimEnd();
26
+
27
+ console.log(line);
28
+ }
29
+
30
+ function main() {
31
+ const args = process.argv.slice(2);
32
+ let name = "";
33
+ let tags = [];
34
+ let date = "";
35
+
36
+ for (let i = 0; i < args.length; i++) {
37
+ if (args[i] === "--name" && args[i + 1]) {
38
+ name = args[++i];
39
+ } else if (args[i] === "--tags" && args[i + 1]) {
40
+ tags = args[++i].split(",").map((t) => t.trim()).filter(Boolean);
41
+ } else if (args[i] === "--date" && args[i + 1]) {
42
+ date = args[++i];
43
+ }
44
+ }
45
+
46
+ formatSessionHeader(name, tags, date);
47
+ }
48
+
49
+ module.exports = { formatSessionHeader };
50
+
51
+ if (require.main === module) {
52
+ main();
53
+ } else {
54
+ // Called via require from bin/openclew.js — run main
55
+ const callerIsOpenclew =
56
+ process.argv[1] && process.argv[1].includes("openclew");
57
+ if (callerIsOpenclew) {
58
+ main();
59
+ }
60
+ }
package/lib/templates.js CHANGED
@@ -541,4 +541,5 @@ module.exports = {
541
541
  slugify,
542
542
  slugifyLog,
543
543
  today,
544
+ ocVersion,
544
545
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclew",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Long Life Memory for LLMs — structured project knowledge for AI agents and humans",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {