@clawdactual/chitin 0.1.5 → 0.1.6

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
@@ -96,30 +96,46 @@ chitin review # (coming soon)
96
96
  chitin reflect --clear # process pending reflections
97
97
  ```
98
98
 
99
- **Integration with OpenClaw/Clawdbot:**
100
-
101
- 1. Create the hook directory in your workspace:
102
- ```
103
- ~/clawd/hooks/chitin/
104
- ├── HOOK.md # metadata + events
105
- └── handler.js # bootstrap injection + reflection queuing
106
- ```
107
-
108
- 2. `HOOK.md` registers for events:
109
- ```yaml
110
- metadata:
111
- openclaw:
112
- events: ["agent:bootstrap", "command:new", "command:reset"]
113
- ```
114
-
115
- 3. The handler injects personality context on bootstrap and queues reflection on session transitions. See the [hook source](hooks/chitin/) for the full implementation.
116
-
117
- 4. Enable in your gateway config:
118
- ```yaml
119
- hooks:
120
- internal:
121
- enabled: true
122
- ```
99
+ **Integration with OpenClaw / ClawdBot:**
100
+
101
+ The Chitin hook ships with the package and works with both OpenClaw and ClawdBot (legacy).
102
+
103
+ **Install from npm:**
104
+ ```bash
105
+ openclaw hooks install @clawdactual/chitin
106
+ openclaw hooks enable chitin
107
+ ```
108
+
109
+ **Install from local clone:**
110
+ ```bash
111
+ openclaw hooks install ./
112
+ openclaw hooks enable chitin
113
+ ```
114
+
115
+ Then restart your gateway. The hook handles:
116
+ - **agent:bootstrap** — injects PERSONALITY.md with your top insights
117
+ - **command:new / command:reset** queues reflection markers for the next heartbeat
118
+
119
+ See the [hook source](hooks/chitin/) for the full implementation.
120
+
121
+ <details>
122
+ <summary>Manual setup (alternative)</summary>
123
+
124
+ If you prefer manual installation, copy the hook files into your workspace:
125
+
126
+ ```
127
+ ~/clawd/hooks/chitin/
128
+ ├── HOOK.md # metadata + events
129
+ └── handler.js # bootstrap injection + reflection queuing
130
+ ```
131
+
132
+ Then enable hooks in your gateway config:
133
+ ```yaml
134
+ hooks:
135
+ internal:
136
+ enabled: true
137
+ ```
138
+ </details>
123
139
 
124
140
  **Standalone (any agent framework):**
125
141
  ```bash
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: chitin
3
+ description: "Personality persistence layer — injects structured behavioral insights into sessions and queues reflection on session transitions"
4
+ metadata:
5
+ {
6
+ "openclaw":
7
+ {
8
+ "emoji": "🦞",
9
+ "events": ["agent:bootstrap", "command:new", "command:reset"],
10
+ },
11
+ "clawdbot":
12
+ {
13
+ "emoji": "🦞",
14
+ "events": ["agent:bootstrap", "command:new", "command:reset"],
15
+ },
16
+ }
17
+ ---
18
+
19
+ # Chitin Hook
20
+
21
+ ## Bootstrap (agent:bootstrap)
22
+ Injects personality context from the Chitin insight store into every session.
23
+ Runs `chitin retrieve` with session context, adds output as synthetic `PERSONALITY.md`.
24
+
25
+ ## Session Reset (command:new, command:reset)
26
+ Queues a personality reflection marker so the next heartbeat knows to
27
+ extract and persist new insights learned during the ended session.
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Chitin — Personality persistence hook for OpenClaw
3
+ *
4
+ * Two responsibilities:
5
+ * 1. On agent:bootstrap — inject personality context into sessions
6
+ * 2. On command:new/reset — trigger reflection to capture new insights
7
+ */
8
+
9
+ import { execSync } from "node:child_process";
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ import os from "node:os";
13
+
14
+ const DB_PATH = path.join(os.homedir(), ".config", "chitin", "insights.db");
15
+ const CHITIN_DIR = path.join(os.homedir(), "Personal", "chitin");
16
+ const CHITIN_DIST = path.join(CHITIN_DIR, "dist", "index.js");
17
+ const DEFAULT_BUDGET = 2000;
18
+ const DEBUG = !!process.env.CHITIN_DEBUG;
19
+
20
+ function log(...args) {
21
+ if (DEBUG) console.error("[chitin]", ...args);
22
+ }
23
+
24
+ // ──────────────────────────────────────────────
25
+ // Bootstrap: inject personality context
26
+ // ──────────────────────────────────────────────
27
+
28
+ function handleBootstrap(event) {
29
+ const context = event.context;
30
+ if (!context?.bootstrapFiles || !Array.isArray(context.bootstrapFiles)) return;
31
+ if (!fs.existsSync(DB_PATH)) return;
32
+
33
+ const query = buildQueryFromContext(context, event.sessionKey || "unknown");
34
+ if (!query) return;
35
+
36
+ try {
37
+ const result = runChitin(
38
+ `retrieve --query "${escapeShell(query)}" --budget ${DEFAULT_BUDGET} --format compact`
39
+ );
40
+
41
+ if (!result || result.includes("No insights stored")) return;
42
+
43
+ context.bootstrapFiles.push({
44
+ name: "PERSONALITY.md",
45
+ content: [
46
+ "# Personality Context (Chitin)",
47
+ "",
48
+ "These are your learned behavioral patterns and insights.",
49
+ "They persist across sessions. You can update them with `chitin contribute`.",
50
+ "",
51
+ result,
52
+ ].join("\n"),
53
+ missing: false,
54
+ source: "chitin-hook",
55
+ });
56
+
57
+ log(`Injected personality context (${result.length} chars) for ${event.sessionKey}`);
58
+ } catch (err) {
59
+ log(`Bootstrap error: ${err.message || err}`);
60
+ }
61
+ }
62
+
63
+ // ──────────────────────────────────────────────
64
+ // Session reset: trigger reflection
65
+ // ──────────────────────────────────────────────
66
+
67
+ function handleSessionReset(event) {
68
+ // Push a reflection summary as a message that gets delivered before reset
69
+ // This tells the user (and any observers) that insights were captured
70
+ // The actual insight contribution happens via the reflection cron/heartbeat
71
+ // because we don't have LLM access in hooks to extract insights intelligently.
72
+
73
+ // What we CAN do: record that a session ended, so the next heartbeat
74
+ // knows to do a reflection pass.
75
+ try {
76
+ const markerPath = path.join(os.homedir(), ".config", "chitin", "pending-reflection.json");
77
+ const marker = {
78
+ sessionKey: event.sessionKey || "unknown",
79
+ timestamp: new Date().toISOString(),
80
+ reason: event.action, // "new" or "reset"
81
+ };
82
+
83
+ // Append to pending reflections (array)
84
+ let pending = [];
85
+ if (fs.existsSync(markerPath)) {
86
+ try {
87
+ pending = JSON.parse(fs.readFileSync(markerPath, "utf-8"));
88
+ if (!Array.isArray(pending)) pending = [];
89
+ } catch { pending = []; }
90
+ }
91
+ pending.push(marker);
92
+
93
+ // Keep only last 10
94
+ if (pending.length > 10) pending = pending.slice(-10);
95
+
96
+ fs.writeFileSync(markerPath, JSON.stringify(pending, null, 2));
97
+ log(`Queued reflection for session ${event.sessionKey} (${event.action})`);
98
+ } catch (err) {
99
+ log(`Reset handler error: ${err.message || err}`);
100
+ }
101
+ }
102
+
103
+ // ──────────────────────────────────────────────
104
+ // Main handler
105
+ // ──────────────────────────────────────────────
106
+
107
+ export default async function handler(event) {
108
+ if (event.type === "agent" && event.action === "bootstrap") {
109
+ handleBootstrap(event);
110
+ return;
111
+ }
112
+
113
+ if (event.type === "command" && (event.action === "new" || event.action === "reset")) {
114
+ handleSessionReset(event);
115
+ return;
116
+ }
117
+ }
118
+
119
+ // ──────────────────────────────────────────────
120
+ // Utilities
121
+ // ──────────────────────────────────────────────
122
+
123
+ function runChitin(args) {
124
+ // Use compiled dist if available (much faster than npx tsx)
125
+ const useCompiled = fs.existsSync(CHITIN_DIST);
126
+ const command = useCompiled
127
+ ? `node "${CHITIN_DIST}" --db "${DB_PATH}" ${args}`
128
+ : `npx tsx "${path.join(CHITIN_DIR, "src", "index.ts")}" --db "${DB_PATH}" ${args}`;
129
+
130
+ log(`Running chitin via ${useCompiled ? "compiled dist" : "tsx fallback"}`);
131
+
132
+ return execSync(command, {
133
+ cwd: CHITIN_DIR,
134
+ encoding: "utf-8",
135
+ timeout: useCompiled ? 3000 : 5000,
136
+ stdio: ["pipe", "pipe", "pipe"],
137
+ }).trim();
138
+ }
139
+
140
+ function buildQueryFromContext(context, sessionKey) {
141
+ const parts = [];
142
+ if (context.agentId) parts.push(`agent: ${context.agentId}`);
143
+
144
+ if (sessionKey.includes("telegram")) parts.push("telegram chat");
145
+ else if (sessionKey.includes("discord")) parts.push("discord group chat");
146
+ else if (sessionKey.includes("slack")) parts.push("slack work chat");
147
+ else if (sessionKey.includes("webchat")) parts.push("webchat direct conversation");
148
+ else if (sessionKey.includes("subagent")) parts.push("sub-agent task execution");
149
+
150
+ if (parts.length === 0) parts.push("general assistant session");
151
+ return parts.join(", ");
152
+ }
153
+
154
+ function escapeShell(str) {
155
+ return str.replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`");
156
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawdactual/chitin",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Personality persistence layer for AI agents. Structured insights about how you think, not what you remember.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,10 +9,16 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
+ "hooks",
12
13
  "seed.json",
13
14
  "LICENSE",
14
15
  "README.md"
15
16
  ],
17
+ "openclaw": {
18
+ "hooks": [
19
+ "./hooks/chitin"
20
+ ]
21
+ },
16
22
  "scripts": {
17
23
  "build": "tsc",
18
24
  "prepare": "npm run build",