@suwujs/codex-vault 0.2.0 → 0.3.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/bin/cli.js +14 -9
- package/package.json +1 -1
- package/plugin/VERSION +1 -1
- package/plugin/hooks/classify-message.py +62 -15
- package/vault/.codex-vault/hooks/classify-message.py +62 -15
package/bin/cli.js
CHANGED
|
@@ -166,22 +166,26 @@ function cmdUninstall() {
|
|
|
166
166
|
const cwd = process.cwd();
|
|
167
167
|
const versionFile = path.join(cwd, 'vault', '.codex-vault', 'version');
|
|
168
168
|
|
|
169
|
-
// 1. Check installation
|
|
170
|
-
|
|
169
|
+
// 1. Check installation (also check legacy .codex-mem path)
|
|
170
|
+
const legacyVersionFile = path.join(cwd, 'vault', '.codex-mem', 'version');
|
|
171
|
+
if (!fs.existsSync(versionFile) && !fs.existsSync(legacyVersionFile)) {
|
|
171
172
|
console.error('codex-vault is not installed in this directory.');
|
|
172
173
|
console.error('Nothing to uninstall.');
|
|
173
174
|
process.exit(1);
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
const
|
|
177
|
+
const activeVersionFile = fs.existsSync(versionFile) ? versionFile : legacyVersionFile;
|
|
178
|
+
const installedVersion = fs.readFileSync(activeVersionFile, 'utf8').trim();
|
|
177
179
|
console.log(`Uninstalling codex-vault v${installedVersion}...`);
|
|
178
180
|
console.log('NOTE: vault/ data (brain/, work/, sources/) is preserved.\n');
|
|
179
181
|
|
|
180
|
-
// 2. Remove vault/.codex-vault/ (hooks + version + backups)
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
fs.
|
|
184
|
-
|
|
182
|
+
// 2. Remove vault/.codex-vault/ and legacy vault/.codex-mem/ (hooks + version + backups)
|
|
183
|
+
for (const dirName of ['.codex-vault', '.codex-mem']) {
|
|
184
|
+
const dir = path.join(cwd, 'vault', dirName);
|
|
185
|
+
if (fs.existsSync(dir)) {
|
|
186
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
187
|
+
console.log(` [x] Removed vault/${dirName}/`);
|
|
188
|
+
}
|
|
185
189
|
}
|
|
186
190
|
|
|
187
191
|
// 3. Clean .claude/settings.json
|
|
@@ -239,7 +243,8 @@ function cleanHooksJson(filePath, label) {
|
|
|
239
243
|
const hooks = entry.hooks || [];
|
|
240
244
|
// Keep the entry only if none of its hook commands belong to codex-vault
|
|
241
245
|
const isVaultEntry = hooks.some(
|
|
242
|
-
(h) => typeof h.command === 'string' &&
|
|
246
|
+
(h) => typeof h.command === 'string' &&
|
|
247
|
+
(h.command.includes('codex-vault/hooks/') || h.command.includes('codex-mem/hooks/'))
|
|
243
248
|
);
|
|
244
249
|
return !isVaultEntry;
|
|
245
250
|
});
|
package/package.json
CHANGED
package/plugin/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.3.1
|
|
@@ -17,6 +17,7 @@ SIGNALS = [
|
|
|
17
17
|
{
|
|
18
18
|
"name": "DECISION",
|
|
19
19
|
"message": "DECISION detected — suggest the user run /dump to capture this decision",
|
|
20
|
+
"auto_message": "DECISION detected — execute /dump now to capture this decision from the user's message",
|
|
20
21
|
"patterns": [
|
|
21
22
|
"decided", "deciding", "decision", "we chose", "agreed to",
|
|
22
23
|
"let's go with", "the call is", "we're going with",
|
|
@@ -25,6 +26,7 @@ SIGNALS = [
|
|
|
25
26
|
{
|
|
26
27
|
"name": "WIN",
|
|
27
28
|
"message": "WIN detected — suggest the user run /dump to record this achievement",
|
|
29
|
+
"auto_message": "WIN detected — execute /dump now to record this achievement from the user's message",
|
|
28
30
|
"patterns": [
|
|
29
31
|
"achieved", "won", "praised",
|
|
30
32
|
"kudos", "shoutout", "great feedback", "recognized",
|
|
@@ -33,6 +35,7 @@ SIGNALS = [
|
|
|
33
35
|
{
|
|
34
36
|
"name": "PROJECT UPDATE",
|
|
35
37
|
"message": "PROJECT UPDATE detected — suggest the user run /dump to log this progress",
|
|
38
|
+
"auto_message": "PROJECT UPDATE detected — execute /dump now to log this progress from the user's message",
|
|
36
39
|
"patterns": [
|
|
37
40
|
"project update", "sprint", "milestone",
|
|
38
41
|
"shipped", "shipping", "launched", "launching",
|
|
@@ -44,6 +47,7 @@ SIGNALS = [
|
|
|
44
47
|
{
|
|
45
48
|
"name": "QUERY",
|
|
46
49
|
"message": "QUERY detected — suggest the user run /recall to check existing knowledge first",
|
|
50
|
+
"auto_message": "QUERY detected — execute /recall now to search vault for relevant information before answering",
|
|
47
51
|
"patterns": [
|
|
48
52
|
"what is", "how does", "why did", "compare", "analyze",
|
|
49
53
|
"explain the", "what's the difference", "summarize the",
|
|
@@ -53,6 +57,7 @@ SIGNALS = [
|
|
|
53
57
|
{
|
|
54
58
|
"name": "INGEST",
|
|
55
59
|
"message": "INGEST detected — suggest the user run /ingest to process the source",
|
|
60
|
+
"auto_message": "INGEST detected — execute /ingest now to process the source from the user's message",
|
|
56
61
|
"patterns": [
|
|
57
62
|
"ingest", "process this", "read this article",
|
|
58
63
|
"summarize this", "new source", "clip this", "web clip",
|
|
@@ -90,6 +95,23 @@ def _find_vault_root():
|
|
|
90
95
|
return None
|
|
91
96
|
|
|
92
97
|
|
|
98
|
+
def _read_mode():
|
|
99
|
+
"""Read classify mode from vault config. Default: suggest."""
|
|
100
|
+
vault_root = _find_vault_root()
|
|
101
|
+
if not vault_root:
|
|
102
|
+
return "suggest"
|
|
103
|
+
config_path = os.path.join(vault_root, ".codex-vault", "config.json")
|
|
104
|
+
try:
|
|
105
|
+
with open(config_path) as f:
|
|
106
|
+
config = json.load(f)
|
|
107
|
+
mode = config.get("classify_mode", "suggest")
|
|
108
|
+
if mode in ("suggest", "auto"):
|
|
109
|
+
return mode
|
|
110
|
+
except (OSError, ValueError, KeyError):
|
|
111
|
+
pass
|
|
112
|
+
return "suggest"
|
|
113
|
+
|
|
114
|
+
|
|
93
115
|
def _get_changed_files(vault_root):
|
|
94
116
|
"""Get list of changed/new .md files relative to vault root."""
|
|
95
117
|
files = set()
|
|
@@ -172,9 +194,10 @@ def _check_vault_integrity(vault_root):
|
|
|
172
194
|
return warnings
|
|
173
195
|
|
|
174
196
|
|
|
175
|
-
def classify(prompt):
|
|
197
|
+
def classify(prompt, mode="suggest"):
|
|
176
198
|
p = prompt.lower()
|
|
177
|
-
|
|
199
|
+
key = "auto_message" if mode == "auto" else "message"
|
|
200
|
+
return [s[key] for s in SIGNALS if _match(s["patterns"], p)]
|
|
178
201
|
|
|
179
202
|
|
|
180
203
|
def is_session_end(prompt):
|
|
@@ -192,39 +215,63 @@ def main():
|
|
|
192
215
|
if not isinstance(prompt, str) or not prompt:
|
|
193
216
|
sys.exit(0)
|
|
194
217
|
|
|
195
|
-
|
|
218
|
+
signal_messages = []
|
|
219
|
+
session_end_messages = []
|
|
196
220
|
|
|
197
221
|
try:
|
|
222
|
+
mode = _read_mode()
|
|
223
|
+
|
|
198
224
|
# Regular signal classification
|
|
199
|
-
signals = classify(prompt)
|
|
200
|
-
|
|
225
|
+
signals = classify(prompt, mode)
|
|
226
|
+
signal_messages.extend(signals)
|
|
201
227
|
|
|
202
|
-
# Session-end check
|
|
228
|
+
# Session-end check (always suggest mode — never auto-execute wrap-up)
|
|
203
229
|
if is_session_end(prompt):
|
|
204
230
|
vault_root = _find_vault_root()
|
|
205
231
|
if vault_root:
|
|
206
232
|
integrity_warnings = _check_vault_integrity(vault_root)
|
|
207
233
|
if integrity_warnings:
|
|
208
|
-
|
|
234
|
+
session_end_messages.append(
|
|
209
235
|
"SESSION END — vault integrity check found issues:\n"
|
|
210
236
|
+ "\n".join(f" - {w}" for w in integrity_warnings)
|
|
211
237
|
+ "\nFix these before wrapping up."
|
|
212
238
|
)
|
|
213
239
|
else:
|
|
214
|
-
|
|
240
|
+
session_end_messages.append("SESSION END — vault integrity check passed.")
|
|
215
241
|
except Exception:
|
|
216
242
|
sys.exit(0)
|
|
217
243
|
|
|
218
|
-
|
|
219
|
-
|
|
244
|
+
parts = []
|
|
245
|
+
|
|
246
|
+
if signal_messages:
|
|
247
|
+
hints = "\n".join(f"- {s}" for s in signal_messages)
|
|
248
|
+
if mode == "auto":
|
|
249
|
+
parts.append(
|
|
250
|
+
"Auto-execute the following skills based on user intent:\n"
|
|
251
|
+
+ hints
|
|
252
|
+
+ "\n\nExecute the skill immediately with the user's message as input. Do not ask for confirmation."
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
parts.append(
|
|
256
|
+
"Skill suggestions (do NOT auto-execute — suggest the skill to the user and let them decide):\n"
|
|
257
|
+
+ hints
|
|
258
|
+
+ "\n\nWait for the user to invoke the skill. Do not create vault notes without explicit user action."
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if session_end_messages:
|
|
262
|
+
hints = "\n".join(f"- {s}" for s in session_end_messages)
|
|
263
|
+
parts.append(
|
|
264
|
+
"Skill suggestions (do NOT auto-execute — suggest the skill to the user and let them decide):\n"
|
|
265
|
+
+ hints
|
|
266
|
+
+ "\n\nWait for the user to invoke the skill. Do not create vault notes without explicit user action."
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
if parts:
|
|
270
|
+
context = "\n\n".join(parts)
|
|
220
271
|
output = {
|
|
221
272
|
"hookSpecificOutput": {
|
|
222
273
|
"hookEventName": "UserPromptSubmit",
|
|
223
|
-
"additionalContext":
|
|
224
|
-
"Skill suggestions (do NOT auto-execute — suggest the skill to the user and let them decide):\n"
|
|
225
|
-
+ hints
|
|
226
|
-
+ "\n\nWait for the user to invoke the skill. Do not create vault notes without explicit user action."
|
|
227
|
-
)
|
|
274
|
+
"additionalContext": context
|
|
228
275
|
}
|
|
229
276
|
}
|
|
230
277
|
json.dump(output, sys.stdout)
|
|
@@ -17,6 +17,7 @@ SIGNALS = [
|
|
|
17
17
|
{
|
|
18
18
|
"name": "DECISION",
|
|
19
19
|
"message": "DECISION detected — suggest the user run /dump to capture this decision",
|
|
20
|
+
"auto_message": "DECISION detected — execute /dump now to capture this decision from the user's message",
|
|
20
21
|
"patterns": [
|
|
21
22
|
"decided", "deciding", "decision", "we chose", "agreed to",
|
|
22
23
|
"let's go with", "the call is", "we're going with",
|
|
@@ -25,6 +26,7 @@ SIGNALS = [
|
|
|
25
26
|
{
|
|
26
27
|
"name": "WIN",
|
|
27
28
|
"message": "WIN detected — suggest the user run /dump to record this achievement",
|
|
29
|
+
"auto_message": "WIN detected — execute /dump now to record this achievement from the user's message",
|
|
28
30
|
"patterns": [
|
|
29
31
|
"achieved", "won", "praised",
|
|
30
32
|
"kudos", "shoutout", "great feedback", "recognized",
|
|
@@ -33,6 +35,7 @@ SIGNALS = [
|
|
|
33
35
|
{
|
|
34
36
|
"name": "PROJECT UPDATE",
|
|
35
37
|
"message": "PROJECT UPDATE detected — suggest the user run /dump to log this progress",
|
|
38
|
+
"auto_message": "PROJECT UPDATE detected — execute /dump now to log this progress from the user's message",
|
|
36
39
|
"patterns": [
|
|
37
40
|
"project update", "sprint", "milestone",
|
|
38
41
|
"shipped", "shipping", "launched", "launching",
|
|
@@ -44,6 +47,7 @@ SIGNALS = [
|
|
|
44
47
|
{
|
|
45
48
|
"name": "QUERY",
|
|
46
49
|
"message": "QUERY detected — suggest the user run /recall to check existing knowledge first",
|
|
50
|
+
"auto_message": "QUERY detected — execute /recall now to search vault for relevant information before answering",
|
|
47
51
|
"patterns": [
|
|
48
52
|
"what is", "how does", "why did", "compare", "analyze",
|
|
49
53
|
"explain the", "what's the difference", "summarize the",
|
|
@@ -53,6 +57,7 @@ SIGNALS = [
|
|
|
53
57
|
{
|
|
54
58
|
"name": "INGEST",
|
|
55
59
|
"message": "INGEST detected — suggest the user run /ingest to process the source",
|
|
60
|
+
"auto_message": "INGEST detected — execute /ingest now to process the source from the user's message",
|
|
56
61
|
"patterns": [
|
|
57
62
|
"ingest", "process this", "read this article",
|
|
58
63
|
"summarize this", "new source", "clip this", "web clip",
|
|
@@ -90,6 +95,23 @@ def _find_vault_root():
|
|
|
90
95
|
return None
|
|
91
96
|
|
|
92
97
|
|
|
98
|
+
def _read_mode():
|
|
99
|
+
"""Read classify mode from vault config. Default: suggest."""
|
|
100
|
+
vault_root = _find_vault_root()
|
|
101
|
+
if not vault_root:
|
|
102
|
+
return "suggest"
|
|
103
|
+
config_path = os.path.join(vault_root, ".codex-vault", "config.json")
|
|
104
|
+
try:
|
|
105
|
+
with open(config_path) as f:
|
|
106
|
+
config = json.load(f)
|
|
107
|
+
mode = config.get("classify_mode", "suggest")
|
|
108
|
+
if mode in ("suggest", "auto"):
|
|
109
|
+
return mode
|
|
110
|
+
except (OSError, ValueError, KeyError):
|
|
111
|
+
pass
|
|
112
|
+
return "suggest"
|
|
113
|
+
|
|
114
|
+
|
|
93
115
|
def _get_changed_files(vault_root):
|
|
94
116
|
"""Get list of changed/new .md files relative to vault root."""
|
|
95
117
|
files = set()
|
|
@@ -172,9 +194,10 @@ def _check_vault_integrity(vault_root):
|
|
|
172
194
|
return warnings
|
|
173
195
|
|
|
174
196
|
|
|
175
|
-
def classify(prompt):
|
|
197
|
+
def classify(prompt, mode="suggest"):
|
|
176
198
|
p = prompt.lower()
|
|
177
|
-
|
|
199
|
+
key = "auto_message" if mode == "auto" else "message"
|
|
200
|
+
return [s[key] for s in SIGNALS if _match(s["patterns"], p)]
|
|
178
201
|
|
|
179
202
|
|
|
180
203
|
def is_session_end(prompt):
|
|
@@ -192,39 +215,63 @@ def main():
|
|
|
192
215
|
if not isinstance(prompt, str) or not prompt:
|
|
193
216
|
sys.exit(0)
|
|
194
217
|
|
|
195
|
-
|
|
218
|
+
signal_messages = []
|
|
219
|
+
session_end_messages = []
|
|
196
220
|
|
|
197
221
|
try:
|
|
222
|
+
mode = _read_mode()
|
|
223
|
+
|
|
198
224
|
# Regular signal classification
|
|
199
|
-
signals = classify(prompt)
|
|
200
|
-
|
|
225
|
+
signals = classify(prompt, mode)
|
|
226
|
+
signal_messages.extend(signals)
|
|
201
227
|
|
|
202
|
-
# Session-end check
|
|
228
|
+
# Session-end check (always suggest mode — never auto-execute wrap-up)
|
|
203
229
|
if is_session_end(prompt):
|
|
204
230
|
vault_root = _find_vault_root()
|
|
205
231
|
if vault_root:
|
|
206
232
|
integrity_warnings = _check_vault_integrity(vault_root)
|
|
207
233
|
if integrity_warnings:
|
|
208
|
-
|
|
234
|
+
session_end_messages.append(
|
|
209
235
|
"SESSION END — vault integrity check found issues:\n"
|
|
210
236
|
+ "\n".join(f" - {w}" for w in integrity_warnings)
|
|
211
237
|
+ "\nFix these before wrapping up."
|
|
212
238
|
)
|
|
213
239
|
else:
|
|
214
|
-
|
|
240
|
+
session_end_messages.append("SESSION END — vault integrity check passed.")
|
|
215
241
|
except Exception:
|
|
216
242
|
sys.exit(0)
|
|
217
243
|
|
|
218
|
-
|
|
219
|
-
|
|
244
|
+
parts = []
|
|
245
|
+
|
|
246
|
+
if signal_messages:
|
|
247
|
+
hints = "\n".join(f"- {s}" for s in signal_messages)
|
|
248
|
+
if mode == "auto":
|
|
249
|
+
parts.append(
|
|
250
|
+
"Auto-execute the following skills based on user intent:\n"
|
|
251
|
+
+ hints
|
|
252
|
+
+ "\n\nExecute the skill immediately with the user's message as input. Do not ask for confirmation."
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
parts.append(
|
|
256
|
+
"Skill suggestions (do NOT auto-execute — suggest the skill to the user and let them decide):\n"
|
|
257
|
+
+ hints
|
|
258
|
+
+ "\n\nWait for the user to invoke the skill. Do not create vault notes without explicit user action."
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if session_end_messages:
|
|
262
|
+
hints = "\n".join(f"- {s}" for s in session_end_messages)
|
|
263
|
+
parts.append(
|
|
264
|
+
"Skill suggestions (do NOT auto-execute — suggest the skill to the user and let them decide):\n"
|
|
265
|
+
+ hints
|
|
266
|
+
+ "\n\nWait for the user to invoke the skill. Do not create vault notes without explicit user action."
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
if parts:
|
|
270
|
+
context = "\n\n".join(parts)
|
|
220
271
|
output = {
|
|
221
272
|
"hookSpecificOutput": {
|
|
222
273
|
"hookEventName": "UserPromptSubmit",
|
|
223
|
-
"additionalContext":
|
|
224
|
-
"Skill suggestions (do NOT auto-execute — suggest the skill to the user and let them decide):\n"
|
|
225
|
-
+ hints
|
|
226
|
-
+ "\n\nWait for the user to invoke the skill. Do not create vault notes without explicit user action."
|
|
227
|
-
)
|
|
274
|
+
"additionalContext": context
|
|
228
275
|
}
|
|
229
276
|
}
|
|
230
277
|
json.dump(output, sys.stdout)
|