@suwujs/codex-vault 0.2.0 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suwujs/codex-vault",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Persistent knowledge vault for LLM agents (Claude Code, Codex CLI)",
5
5
  "license": "MIT",
6
6
  "repository": {
package/plugin/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -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
- return [s["message"] for s in SIGNALS if _match(s["patterns"], p)]
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
- messages = []
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
- messages.extend(signals)
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
- messages.append(
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
- messages.append("SESSION END — vault integrity check passed.")
240
+ session_end_messages.append("SESSION END — vault integrity check passed.")
215
241
  except Exception:
216
242
  sys.exit(0)
217
243
 
218
- if messages:
219
- hints = "\n".join(f"- {s}" for s in messages)
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
- return [s["message"] for s in SIGNALS if _match(s["patterns"], p)]
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
- messages = []
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
- messages.extend(signals)
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
- messages.append(
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
- messages.append("SESSION END — vault integrity check passed.")
240
+ session_end_messages.append("SESSION END — vault integrity check passed.")
215
241
  except Exception:
216
242
  sys.exit(0)
217
243
 
218
- if messages:
219
- hints = "\n".join(f"- {s}" for s in messages)
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)