woclaw-codex 0.1.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/README.md +72 -0
- package/bin/cli.js +27 -0
- package/install.py +178 -0
- package/package.json +28 -0
- package/session_start.py +75 -0
- package/stop.py +105 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# WoClaw Codex CLI Integration
|
|
2
|
+
|
|
3
|
+
Connect OpenAI Codex CLI sessions to a WoClaw Hub for **shared context across sessions and agents**.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
pip install aiohttp websockets # required by hook scripts
|
|
7
|
+
python3 install.py # one-command install
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## What It Does
|
|
11
|
+
|
|
12
|
+
- **SessionStart Hook**: When Codex starts, loads shared project context from WoClaw Hub and injects it as developer context
|
|
13
|
+
- **Stop Hook**: When Codex ends, saves a transcript summary back to WoClaw Hub so future sessions can pick up where you left off
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- Python 3.8+
|
|
18
|
+
- `aiohttp` or standard `urllib` (stdlib, no extra deps needed for REST)
|
|
19
|
+
- WoClaw Hub running at `ws://vm153:8082` / `http://vm153:8083`
|
|
20
|
+
|
|
21
|
+
## Quick Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Clone WoClaw repo (if you have it)
|
|
25
|
+
cd packages/codex-woclaw
|
|
26
|
+
|
|
27
|
+
# Install hooks (one command)
|
|
28
|
+
python3 install.py
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This will:
|
|
32
|
+
1. Copy `session_start.py` and `stop.py` to `~/.codex/hooks/`
|
|
33
|
+
2. Create `~/.codex/hooks.json` with WoClaw hook configuration
|
|
34
|
+
3. Enable `codex_hooks = true` in `~/.codex/config.toml`
|
|
35
|
+
|
|
36
|
+
Then start a Codex session — the hook runs automatically.
|
|
37
|
+
|
|
38
|
+
## Uninstall
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python3 install.py --uninstall
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Environment Variables
|
|
45
|
+
|
|
46
|
+
| Variable | Default | Description |
|
|
47
|
+
|----------|---------|-------------|
|
|
48
|
+
| `WOCLAW_HUB_URL` | `http://vm153:8083` | Hub REST API URL |
|
|
49
|
+
| `WOCLAW_TOKEN` | `WoClaw2026` | Hub auth token |
|
|
50
|
+
| `WOCLAW_KEY` | `codex:context` | Memory key for context |
|
|
51
|
+
|
|
52
|
+
## How It Works
|
|
53
|
+
|
|
54
|
+
The Codex CLI hooks system (`~/.codex/hooks.json`) fires Python scripts at key lifecycle events:
|
|
55
|
+
|
|
56
|
+
1. **SessionStart** → `session_start.py` reads `WOCLAW_KEY` from WoClaw Hub REST API → injects as `additionalContext`
|
|
57
|
+
2. **Stop** → `stop.py` reads session transcript → writes summary to WoClaw Hub under `WOCLAW_KEY`
|
|
58
|
+
|
|
59
|
+
## NPM Package
|
|
60
|
+
|
|
61
|
+
Publishing as `woclaw-codex` npm package for easy distribution:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd packages/codex-woclaw
|
|
65
|
+
npm publish --access public
|
|
66
|
+
# → woclaw-codex on npm
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
After npm install, users get:
|
|
70
|
+
```
|
|
71
|
+
npx woclaw-codex install # installs hooks
|
|
72
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* woclaw-codex CLI
|
|
4
|
+
* Entry point for npm-installed package.
|
|
5
|
+
* Delegates to the Python install script.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { spawn } = require("child_process");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
const action = process.argv[2] || "install";
|
|
12
|
+
|
|
13
|
+
if (action === "install") {
|
|
14
|
+
console.log("Installing WoClaw Codex CLI hooks...");
|
|
15
|
+
const script = path.join(__dirname, "..", "install.py");
|
|
16
|
+
const child = spawn("python3", [script], { stdio: "inherit" });
|
|
17
|
+
child.on("exit", (code) => process.exit(code || 0));
|
|
18
|
+
} else if (action === "uninstall") {
|
|
19
|
+
console.log("Uninstalling WoClaw Codex CLI hooks...");
|
|
20
|
+
const script = path.join(__dirname, "..", "install.py");
|
|
21
|
+
const child = spawn("python3", [script, "--uninstall"], { stdio: "inherit" });
|
|
22
|
+
child.on("exit", (code) => process.exit(code || 0));
|
|
23
|
+
} else {
|
|
24
|
+
console.error(`Unknown action: ${action}`);
|
|
25
|
+
console.error("Usage: woclaw-codex [install|uninstall]");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
package/install.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
WoClaw Codex CLI Installer
|
|
4
|
+
|
|
5
|
+
Configures Codex CLI to use WoClaw hooks for shared context across sessions.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python3 install.py [--uninstall]
|
|
9
|
+
|
|
10
|
+
This script:
|
|
11
|
+
1. Creates ~/.codex/hooks/ directory
|
|
12
|
+
2. Copies hook scripts to ~/.codex/hooks/
|
|
13
|
+
3. Creates ~/.codex/hooks.json with WoClaw hook configuration
|
|
14
|
+
4. Enables hooks in ~/.codex/config.toml
|
|
15
|
+
|
|
16
|
+
Requires: Python 3.8+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
import json
|
|
22
|
+
import shutil
|
|
23
|
+
import argparse
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
HOOKS_DIR = Path.home() / ".codex" / "hooks"
|
|
28
|
+
HOOKS_JSON = Path.home() / ".codex" / "hooks.json"
|
|
29
|
+
CONFIG_TOML = Path.home() / ".codex" / "config.toml"
|
|
30
|
+
SCRIPT_DIR = Path(__file__).parent
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def ensure_hooks_dir():
|
|
34
|
+
HOOKS_DIR.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
print(f"Created: {HOOKS_DIR}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def copy_hooks():
|
|
39
|
+
for script in ["session_start.py", "stop.py"]:
|
|
40
|
+
src = SCRIPT_DIR / script
|
|
41
|
+
dst = HOOKS_DIR / script
|
|
42
|
+
shutil.copy2(src, dst)
|
|
43
|
+
os.chmod(dst, 0o755)
|
|
44
|
+
print(f"Copied: {dst}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def create_hooks_json():
|
|
48
|
+
hooks_config = {
|
|
49
|
+
"hooks": {
|
|
50
|
+
"SessionStart": [
|
|
51
|
+
{
|
|
52
|
+
"matcher": "startup|resume",
|
|
53
|
+
"hooks": [
|
|
54
|
+
{
|
|
55
|
+
"type": "command",
|
|
56
|
+
"command": f"python3 {HOOKS_DIR / 'session_start.py'}",
|
|
57
|
+
"statusMessage": "Loading shared context from WoClaw"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"Stop": [
|
|
63
|
+
{
|
|
64
|
+
"hooks": [
|
|
65
|
+
{
|
|
66
|
+
"type": "command",
|
|
67
|
+
"command": f"python3 {HOOKS_DIR / 'stop.py'}",
|
|
68
|
+
"timeout": 30,
|
|
69
|
+
"statusMessage": "Saving session to WoClaw Hub"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
with open(HOOKS_JSON, "w") as f:
|
|
77
|
+
json.dump(hooks_config, f, indent=2)
|
|
78
|
+
print(f"Created: {HOOKS_JSON}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def update_config_toml():
|
|
82
|
+
"""Enable hooks feature in config.toml."""
|
|
83
|
+
content = ""
|
|
84
|
+
if CONFIG_TOML.exists():
|
|
85
|
+
content = CONFIG_TOML.read_text()
|
|
86
|
+
|
|
87
|
+
if "[features]" in content and "codex_hooks" in content:
|
|
88
|
+
print(f"Hooks already enabled in {CONFIG_TOML}")
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
# Append or create config
|
|
92
|
+
addition = (
|
|
93
|
+
"\n[features]\n"
|
|
94
|
+
"codex_hooks = true\n"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if content.strip():
|
|
98
|
+
if not content.rstrip().endswith("\n"):
|
|
99
|
+
addition = "\n" + addition
|
|
100
|
+
content += addition
|
|
101
|
+
else:
|
|
102
|
+
content = addition
|
|
103
|
+
|
|
104
|
+
CONFIG_TOML.parent.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
CONFIG_TOML.write_text(content)
|
|
106
|
+
print(f"Updated: {CONFIG_TOML} (enabled codex_hooks)")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def uninstall():
|
|
110
|
+
"""Remove WoClaw hooks from Codex configuration."""
|
|
111
|
+
# Remove hook scripts
|
|
112
|
+
for script in ["session_start.py", "stop.py"]:
|
|
113
|
+
dst = HOOKS_DIR / script
|
|
114
|
+
if dst.exists():
|
|
115
|
+
dst.unlink()
|
|
116
|
+
print(f"Removed: {dst}")
|
|
117
|
+
|
|
118
|
+
# Remove hooks.json
|
|
119
|
+
if HOOKS_JSON.exists():
|
|
120
|
+
try:
|
|
121
|
+
data = json.loads(HOOKS_JSON.read_text())
|
|
122
|
+
if "hooks" in data and (
|
|
123
|
+
"SessionStart" in data["hooks"] or "Stop" in data["hooks"]
|
|
124
|
+
):
|
|
125
|
+
# Clear WoClaw hooks from the config
|
|
126
|
+
for key in ["SessionStart", "Stop"]:
|
|
127
|
+
if key in data["hooks"]:
|
|
128
|
+
data["hooks"][key] = [
|
|
129
|
+
h for h in data["hooks"][key]
|
|
130
|
+
if not any(
|
|
131
|
+
"woclaw" in str(v).lower()
|
|
132
|
+
for v in (h.get("command") or "")
|
|
133
|
+
)
|
|
134
|
+
]
|
|
135
|
+
HOOKS_JSON.write_text(json.dumps(data, indent=2))
|
|
136
|
+
print(f"Updated: {HOOKS_JSON} (removed WoClaw hooks)")
|
|
137
|
+
except (json.JSONDecodeError, OSError):
|
|
138
|
+
HOOKS_JSON.unlink()
|
|
139
|
+
print(f"Removed: {HOOKS_JSON}")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def main():
|
|
143
|
+
parser = argparse.ArgumentParser(description="Install WoClaw hooks for Codex CLI")
|
|
144
|
+
parser.add_argument("--uninstall", action="store_true", help="Remove WoClaw hooks")
|
|
145
|
+
args = parser.parse_args()
|
|
146
|
+
|
|
147
|
+
if args.uninstall:
|
|
148
|
+
print("Uninstalling WoClaw Codex hooks...")
|
|
149
|
+
uninstall()
|
|
150
|
+
print("Done.")
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
print("Installing WoClaw Codex CLI hooks...")
|
|
154
|
+
print(f"Hub: {os.environ.get('WOCLAW_HUB_URL', 'http://vm153:8083')}")
|
|
155
|
+
print(f"Token: {'***' + os.environ.get('WOCLAW_TOKEN', 'WoClaw2026')[-4:]}")
|
|
156
|
+
print()
|
|
157
|
+
|
|
158
|
+
ensure_hooks_dir()
|
|
159
|
+
copy_hooks()
|
|
160
|
+
create_hooks_json()
|
|
161
|
+
update_config_toml()
|
|
162
|
+
|
|
163
|
+
print()
|
|
164
|
+
print("WoClaw hooks installed!")
|
|
165
|
+
print()
|
|
166
|
+
print("Next steps:")
|
|
167
|
+
print(" 1. Start a new Codex session: codex")
|
|
168
|
+
print(" 2. On session start, shared context will be loaded from WoClaw Hub")
|
|
169
|
+
print(" 3. On session end, your work will be saved to WoClaw Hub")
|
|
170
|
+
print()
|
|
171
|
+
print("Environment variables (optional):")
|
|
172
|
+
print(" WOCLAW_HUB_URL - Hub REST URL (default: http://vm153:8083)")
|
|
173
|
+
print(" WOCLAW_TOKEN - Auth token (default: WoClaw2026)")
|
|
174
|
+
print(" WOCLAW_KEY - Memory key (default: codex:context)")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
main()
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "woclaw-codex",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "WoClaw integration for OpenAI Codex CLI — shared context across sessions",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"woclaw",
|
|
7
|
+
"codex",
|
|
8
|
+
"openai",
|
|
9
|
+
"ai-agent",
|
|
10
|
+
"shared-memory",
|
|
11
|
+
"context"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
"*.py",
|
|
15
|
+
"install.py"
|
|
16
|
+
],
|
|
17
|
+
"bin": {
|
|
18
|
+
"woclaw-codex": "./bin/cli.js"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"install": "python3 install.py",
|
|
22
|
+
"uninstall": "python3 install.py --uninstall"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|
package/session_start.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
WoClaw SessionStart Hook for OpenAI Codex CLI
|
|
4
|
+
|
|
5
|
+
Reads shared project context from WoClaw Hub when a Codex session starts.
|
|
6
|
+
Injects the context as additional developer context for the new session.
|
|
7
|
+
|
|
8
|
+
Requires: aiohttp, websockets (or use REST for simplicity)
|
|
9
|
+
|
|
10
|
+
Install:
|
|
11
|
+
python3 session_start.py --install
|
|
12
|
+
|
|
13
|
+
Environment:
|
|
14
|
+
WOCLAW_HUB_URL - Hub REST URL (default: http://vm153:8083)
|
|
15
|
+
WOCLAW_TOKEN - Auth token (default: WoClaw2026)
|
|
16
|
+
WOCLAW_KEY - Memory key to read (default: codex:context)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
import json
|
|
22
|
+
import urllib.request
|
|
23
|
+
import urllib.error
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
HUB_URL = os.environ.get("WOCLAW_HUB_URL", "http://vm153:8083")
|
|
27
|
+
TOKEN = os.environ.get("WOCLAW_TOKEN", "WoClaw2026")
|
|
28
|
+
MEMORY_KEY = os.environ.get("WOCLAW_KEY", "codex:context")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def read_memory(key: str) -> str | None:
|
|
32
|
+
"""Read a value from WoClaw Hub REST API."""
|
|
33
|
+
url = f"{HUB_URL}/memory/{key}"
|
|
34
|
+
req = urllib.request.Request(
|
|
35
|
+
url,
|
|
36
|
+
headers={"Authorization": f"Bearer {TOKEN}"}
|
|
37
|
+
)
|
|
38
|
+
try:
|
|
39
|
+
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
40
|
+
data = json.loads(resp.read())
|
|
41
|
+
if data.get("exists"):
|
|
42
|
+
return data.get("value")
|
|
43
|
+
except urllib.error.URLError:
|
|
44
|
+
pass
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main():
|
|
49
|
+
# Read hook input from stdin
|
|
50
|
+
try:
|
|
51
|
+
hook_input = json.load(sys.stdin)
|
|
52
|
+
except (json.JSONDecodeError, EOFError):
|
|
53
|
+
hook_input = {}
|
|
54
|
+
|
|
55
|
+
source = hook_input.get("source", "startup")
|
|
56
|
+
cwd = hook_input.get("cwd", os.getcwd())
|
|
57
|
+
|
|
58
|
+
context = read_memory(MEMORY_KEY)
|
|
59
|
+
|
|
60
|
+
if context:
|
|
61
|
+
# Emit JSON output to inject as additional context
|
|
62
|
+
output = {
|
|
63
|
+
"hookSpecificOutput": {
|
|
64
|
+
"hookEventName": "SessionStart",
|
|
65
|
+
"additionalContext": f"[WoClaw] Shared project context:\n{context}"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
print(json.dumps(output))
|
|
69
|
+
else:
|
|
70
|
+
# No shared context found — just continue silently
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
main()
|
package/stop.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
WoClaw Stop Hook for OpenAI Codex CLI
|
|
4
|
+
|
|
5
|
+
Writes session summary to WoClaw Hub when a Codex session ends.
|
|
6
|
+
Helps preserve discoveries and decisions across sessions.
|
|
7
|
+
|
|
8
|
+
Install:
|
|
9
|
+
python3 stop.py --install
|
|
10
|
+
|
|
11
|
+
Environment:
|
|
12
|
+
WOCLAW_HUB_URL - Hub REST URL (default: http://vm153:8083)
|
|
13
|
+
WOCLAW_TOKEN - Auth token (default: WoClaw2026)
|
|
14
|
+
WOCLAW_KEY - Memory key to write (default: codex:context)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
import json
|
|
20
|
+
import urllib.request
|
|
21
|
+
import urllib.error
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
HUB_URL = os.environ.get("WOCLAW_HUB_URL", "http://vm153:8083")
|
|
26
|
+
TOKEN = os.environ.get("WOCLAW_TOKEN", "WoClaw2026")
|
|
27
|
+
MEMORY_KEY = os.environ.get("WOCLAW_KEY", "codex:context")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def write_memory(key: str, value: str, hostname: str) -> bool:
|
|
31
|
+
"""Write a value to WoClaw Hub REST API."""
|
|
32
|
+
url = f"{HUB_URL}/memory"
|
|
33
|
+
payload = json.dumps({
|
|
34
|
+
"key": key,
|
|
35
|
+
"value": value,
|
|
36
|
+
"updatedBy": f"codex:{hostname}",
|
|
37
|
+
"tags": ["codex", "session-summary"]
|
|
38
|
+
}).encode("utf-8")
|
|
39
|
+
|
|
40
|
+
req = urllib.request.Request(
|
|
41
|
+
url,
|
|
42
|
+
data=payload,
|
|
43
|
+
headers={
|
|
44
|
+
"Authorization": f"Bearer {TOKEN}",
|
|
45
|
+
"Content-Type": "application/json"
|
|
46
|
+
},
|
|
47
|
+
method="POST"
|
|
48
|
+
)
|
|
49
|
+
try:
|
|
50
|
+
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
51
|
+
return resp.status == 200
|
|
52
|
+
except urllib.error.URLError:
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_transcript(path: str | None, max_lines: int = 80) -> str | None:
|
|
57
|
+
"""Read the last lines of the session transcript."""
|
|
58
|
+
if not path:
|
|
59
|
+
return None
|
|
60
|
+
try:
|
|
61
|
+
with open(path, "r", encoding="utf-8", errors="ignore") as f:
|
|
62
|
+
lines = f.readlines()
|
|
63
|
+
return "".join(lines[-max_lines:])
|
|
64
|
+
except (OSError, IOError):
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def main():
|
|
69
|
+
# Read hook input from stdin
|
|
70
|
+
try:
|
|
71
|
+
hook_input = json.load(sys.stdin)
|
|
72
|
+
except (json.JSONDecodeError, EOFError):
|
|
73
|
+
hook_input = {}
|
|
74
|
+
|
|
75
|
+
session_id = hook_input.get("session_id", "unknown")
|
|
76
|
+
transcript_path = hook_input.get("transcript_path")
|
|
77
|
+
cwd = hook_input.get("cwd", os.getcwd())
|
|
78
|
+
stop_reason = hook_input.get("stopReason", "unknown")
|
|
79
|
+
hostname = os.uname().nodename
|
|
80
|
+
|
|
81
|
+
# Read transcript for context
|
|
82
|
+
transcript = read_transcript(transcript_path)
|
|
83
|
+
|
|
84
|
+
if transcript:
|
|
85
|
+
# Build a concise session summary
|
|
86
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
87
|
+
summary = (
|
|
88
|
+
f"[{timestamp}] Codex session ended ({stop_reason})\n"
|
|
89
|
+
f"Session: {session_id}\n"
|
|
90
|
+
f"CWD: {cwd}\n"
|
|
91
|
+
f"---Last transcript excerpt---\n"
|
|
92
|
+
f"{transcript[:1000]}"
|
|
93
|
+
)
|
|
94
|
+
success = write_memory(MEMORY_KEY, summary, hostname)
|
|
95
|
+
if success:
|
|
96
|
+
print(f"[WoClaw] Session summary saved to {MEMORY_KEY}", file=sys.stderr)
|
|
97
|
+
else:
|
|
98
|
+
print(f"[WoClaw] Failed to save session summary", file=sys.stderr)
|
|
99
|
+
|
|
100
|
+
# Emit continue response (don't block anything)
|
|
101
|
+
print(json.dumps({"continue": True}))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
main()
|