@bookedsolid/reagent 0.14.0 → 0.14.2

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.
Files changed (58) hide show
  1. package/README.md +147 -1
  2. package/dist/cli/commands/init/index.d.ts.map +1 -1
  3. package/dist/cli/commands/init/index.js +8 -1
  4. package/dist/cli/commands/init/index.js.map +1 -1
  5. package/dist/cli/commands/init/mcp-config.js +6 -6
  6. package/dist/cli/commands/init/mcp-config.js.map +1 -1
  7. package/dist/cli/commands/init/obsidian.d.ts +16 -0
  8. package/dist/cli/commands/init/obsidian.d.ts.map +1 -0
  9. package/dist/cli/commands/init/obsidian.js +95 -0
  10. package/dist/cli/commands/init/obsidian.js.map +1 -0
  11. package/dist/cli/commands/init/policy.d.ts.map +1 -1
  12. package/dist/cli/commands/init/policy.js +10 -1
  13. package/dist/cli/commands/init/policy.js.map +1 -1
  14. package/dist/cli/commands/obsidian.d.ts +2 -0
  15. package/dist/cli/commands/obsidian.d.ts.map +1 -0
  16. package/dist/cli/commands/obsidian.js +187 -0
  17. package/dist/cli/commands/obsidian.js.map +1 -0
  18. package/dist/cli/commands/upgrade-policy.d.ts +17 -0
  19. package/dist/cli/commands/upgrade-policy.d.ts.map +1 -0
  20. package/dist/cli/commands/upgrade-policy.js +123 -0
  21. package/dist/cli/commands/upgrade-policy.js.map +1 -0
  22. package/dist/cli/commands/upgrade.d.ts.map +1 -1
  23. package/dist/cli/commands/upgrade.js +8 -26
  24. package/dist/cli/commands/upgrade.js.map +1 -1
  25. package/dist/cli/index.js +9 -0
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/gateway/native-tools.d.ts.map +1 -1
  28. package/dist/gateway/native-tools.js +39 -0
  29. package/dist/gateway/native-tools.js.map +1 -1
  30. package/dist/obsidian/cli.d.ts +49 -0
  31. package/dist/obsidian/cli.d.ts.map +1 -0
  32. package/dist/obsidian/cli.js +128 -0
  33. package/dist/obsidian/cli.js.map +1 -0
  34. package/dist/obsidian/index.d.ts +7 -0
  35. package/dist/obsidian/index.d.ts.map +1 -0
  36. package/dist/obsidian/index.js +5 -0
  37. package/dist/obsidian/index.js.map +1 -0
  38. package/dist/obsidian/kanban-generator.d.ts +9 -0
  39. package/dist/obsidian/kanban-generator.d.ts.map +1 -0
  40. package/dist/obsidian/kanban-generator.js +63 -0
  41. package/dist/obsidian/kanban-generator.js.map +1 -0
  42. package/dist/obsidian/vault-config.d.ts +142 -0
  43. package/dist/obsidian/vault-config.d.ts.map +1 -0
  44. package/dist/obsidian/vault-config.js +92 -0
  45. package/dist/obsidian/vault-config.js.map +1 -0
  46. package/dist/obsidian/vault-writer.d.ts +52 -0
  47. package/dist/obsidian/vault-writer.d.ts.map +1 -0
  48. package/dist/obsidian/vault-writer.js +166 -0
  49. package/dist/obsidian/vault-writer.js.map +1 -0
  50. package/dist/types/policy.d.ts +4 -0
  51. package/dist/types/policy.d.ts.map +1 -1
  52. package/hooks/dangerous-bash-interceptor.sh +43 -0
  53. package/hooks/reagent-obsidian-journal.sh +69 -0
  54. package/hooks/reagent-obsidian-precompact.sh +88 -0
  55. package/hooks/reagent-obsidian-tasks.sh +126 -0
  56. package/package.json +1 -1
  57. package/profiles/bst-internal.json +1 -1
  58. package/profiles/client-engagement.json +1 -1
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bash
2
+ # reagent-obsidian-journal.sh — Session journaling to Obsidian daily note
3
+ #
4
+ # Hook type: Stop (fires when Claude Code session ends)
5
+ # Appends session summary to the project's daily note via Obsidian CLI.
6
+ # Fail-silent: exits 0 even on error (non-blocking).
7
+ #
8
+ # Requires:
9
+ # - /usr/local/bin/obsidian CLI installed
10
+ # - REAGENT_OBSIDIAN_VAULT env var set
11
+ # - obsidian_vault.sync.journal: true in .reagent/gateway.yaml
12
+
13
+ set -euo pipefail
14
+
15
+ OBSIDIAN_CLI="/usr/local/bin/obsidian"
16
+ POLICY_DIR=".reagent"
17
+ GATEWAY="${POLICY_DIR}/gateway.yaml"
18
+ TASK_STORE="${POLICY_DIR}/tasks.jsonl"
19
+
20
+ # ── Guard: CLI available ──────────────────────────────────────────────
21
+ if [[ ! -x "$OBSIDIAN_CLI" ]]; then
22
+ exit 0
23
+ fi
24
+
25
+ # ── Guard: gateway.yaml exists ────────────────────────────────────────
26
+ if [[ ! -f "$GATEWAY" ]]; then
27
+ exit 0
28
+ fi
29
+
30
+ # ── Guard: journal sync enabled ──────────────────────────────────────
31
+ # Anchored grep — must be indented (under sync: block), avoids matching comments
32
+ if ! grep -qE '^\s+journal:\s*true' "$GATEWAY" 2>/dev/null; then
33
+ exit 0
34
+ fi
35
+
36
+ # ── Resolve vault name ────────────────────────────────────────────────
37
+ VAULT_NAME=$(grep 'vault_name:' "$GATEWAY" 2>/dev/null | head -1 | sed "s/.*vault_name:\s*['\"]*//" | sed "s/['\"].*//")
38
+ if [[ -z "$VAULT_NAME" ]]; then
39
+ exit 0
40
+ fi
41
+
42
+ # ── Build session summary ─────────────────────────────────────────────
43
+ PROJECT_NAME=$(basename "$(pwd)")
44
+ TIMESTAMP=$(date '+%Y-%m-%d %H:%M')
45
+ SESSION_ID="${RANDOM}"
46
+
47
+ SUMMARY="### ${PROJECT_NAME} — Session ${SESSION_ID} (${TIMESTAMP})\n"
48
+
49
+ # Materialize task state if task store exists
50
+ if [[ -f "$TASK_STORE" ]]; then
51
+ COMPLETED=$(grep -c '"completed"' "$TASK_STORE" 2>/dev/null || echo "0")
52
+ IN_PROGRESS=$(grep -c '"started"' "$TASK_STORE" 2>/dev/null || echo "0")
53
+ BLOCKED=$(grep -c '"blocked"' "$TASK_STORE" 2>/dev/null || echo "0")
54
+ CREATED=$(grep -c '"created"' "$TASK_STORE" 2>/dev/null || echo "0")
55
+
56
+ SUMMARY="${SUMMARY}- Completed: ${COMPLETED}\n"
57
+ SUMMARY="${SUMMARY}- In Progress: ${IN_PROGRESS}\n"
58
+ SUMMARY="${SUMMARY}- Blocked: ${BLOCKED}\n"
59
+ SUMMARY="${SUMMARY}- Backlog: ${CREATED}\n"
60
+ else
61
+ SUMMARY="${SUMMARY}- No task store found\n"
62
+ fi
63
+
64
+ SUMMARY="${SUMMARY}\n---\n"
65
+
66
+ # ── Append to daily note ──────────────────────────────────────────────
67
+ "$OBSIDIAN_CLI" daily:append --vault "$VAULT_NAME" -- "$(echo -e "$SUMMARY")" 2>/dev/null || true
68
+
69
+ exit 0
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env bash
2
+ # reagent-obsidian-precompact.sh — Knowledge extraction before context compaction
3
+ #
4
+ # Hook type: PreCompact (fires before Claude Code compacts context)
5
+ # Spawns a subprocess to extract durable knowledge from the transcript
6
+ # and creates a session knowledge note in Obsidian.
7
+ #
8
+ # Disabled by default (precompact: false in gateway.yaml).
9
+ # Fail-silent: exits 0 even on error (non-blocking).
10
+ #
11
+ # Requires:
12
+ # - /usr/local/bin/obsidian CLI installed
13
+ # - REAGENT_OBSIDIAN_VAULT env var set
14
+ # - obsidian_vault.sync.precompact: true in .reagent/gateway.yaml
15
+
16
+ set -euo pipefail
17
+
18
+ OBSIDIAN_CLI="/usr/local/bin/obsidian"
19
+ POLICY_DIR=".reagent"
20
+ GATEWAY="${POLICY_DIR}/gateway.yaml"
21
+
22
+ # ── Guard: CLI available ──────────────────────────────────────────────
23
+ if [[ ! -x "$OBSIDIAN_CLI" ]]; then
24
+ exit 0
25
+ fi
26
+
27
+ # ── Guard: gateway.yaml exists ────────────────────────────────────────
28
+ if [[ ! -f "$GATEWAY" ]]; then
29
+ exit 0
30
+ fi
31
+
32
+ # ── Guard: precompact sync enabled ───────────────────────────────────
33
+ # Anchored grep — must be indented (under sync: block), avoids matching comments
34
+ if ! grep -qE '^\s+precompact:\s*true' "$GATEWAY" 2>/dev/null; then
35
+ exit 0
36
+ fi
37
+
38
+ # ── Resolve vault name and sessions path ──────────────────────────────
39
+ VAULT_NAME=$(grep 'vault_name:' "$GATEWAY" 2>/dev/null | head -1 | sed "s/.*vault_name:\s*['\"]*//" | sed "s/['\"].*//")
40
+ if [[ -z "$VAULT_NAME" ]]; then
41
+ exit 0
42
+ fi
43
+
44
+ SESSIONS_PATH=$(grep 'sessions:' "$GATEWAY" 2>/dev/null | head -1 | sed "s/.*sessions:\s*['\"]*//" | sed "s/['\"].*//")
45
+ SESSIONS_PATH="${SESSIONS_PATH:-Wiki/Sessions}"
46
+
47
+ # ── Resolve engine ────────────────────────────────────────────────────
48
+ ENGINE=$(grep 'engine:' "$GATEWAY" 2>/dev/null | head -1 | sed "s/.*engine:\s*['\"]*//" | sed "s/['\"].*//")
49
+ ENGINE="${ENGINE:-claude}"
50
+
51
+ PROJECT_NAME=$(basename "$(pwd)")
52
+ DATE=$(date '+%Y-%m-%d')
53
+ SESSION_ID="${RANDOM}"
54
+ NOTE_NAME="${PROJECT_NAME} Session ${DATE} ${SESSION_ID}"
55
+
56
+ # ── Stub: knowledge extraction ────────────────────────────────────────
57
+ # TODO: Implement actual transcript extraction via claude subprocess or ollama
58
+ # For now, create a placeholder session note
59
+
60
+ CONTENT="---
61
+ reagent_managed: true
62
+ project: ${PROJECT_NAME}
63
+ date: ${DATE}
64
+ session_id: ${SESSION_ID}
65
+ engine: ${ENGINE}
66
+ ---
67
+
68
+ # Session Knowledge — ${PROJECT_NAME}
69
+
70
+ *Auto-extracted at ${DATE} by reagent precompact hook.*
71
+
72
+ > Knowledge extraction engine: ${ENGINE}
73
+ > Status: stub — full extraction not yet implemented
74
+
75
+ ## Decisions
76
+
77
+ ## Discoveries
78
+
79
+ ## Open Questions
80
+ "
81
+
82
+ "$OBSIDIAN_CLI" create \
83
+ --vault "$VAULT_NAME" \
84
+ --path "$SESSIONS_PATH" \
85
+ --name "$NOTE_NAME" \
86
+ -- "$CONTENT" 2>/dev/null || true
87
+
88
+ exit 0
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env bash
2
+ # reagent-obsidian-tasks.sh — Task sync to Obsidian individual notes
3
+ #
4
+ # Hook type: PostToolUse (fires after task_create, task_update MCP tools)
5
+ # Materializes task events as individual Obsidian notes with typed frontmatter.
6
+ # One-way sync: JSONL → Obsidian. Reverse sync deferred.
7
+ #
8
+ # Fail-silent: exits 0 even on error (non-blocking).
9
+ #
10
+ # Requires:
11
+ # - /usr/local/bin/obsidian CLI installed
12
+ # - REAGENT_OBSIDIAN_VAULT env var set
13
+ # - obsidian_vault.sync.tasks: true in .reagent/gateway.yaml
14
+
15
+ set -euo pipefail
16
+
17
+ OBSIDIAN_CLI="/usr/local/bin/obsidian"
18
+ POLICY_DIR=".reagent"
19
+ GATEWAY="${POLICY_DIR}/gateway.yaml"
20
+ TASK_STORE="${POLICY_DIR}/tasks.jsonl"
21
+
22
+ # ── Guard: CLI available ──────────────────────────────────────────────
23
+ if [[ ! -x "$OBSIDIAN_CLI" ]]; then
24
+ exit 0
25
+ fi
26
+
27
+ # ── Guard: gateway.yaml exists ────────────────────────────────────────
28
+ if [[ ! -f "$GATEWAY" ]]; then
29
+ exit 0
30
+ fi
31
+
32
+ # ── Guard: tasks sync enabled ────────────────────────────────────────
33
+ # Match sync.tasks specifically — use awk to only match within the sync: block
34
+ if ! awk '/^\s+sync:/{found=1} found && /tasks:\s*true/{exit 0} END{exit 1}' "$GATEWAY" 2>/dev/null; then
35
+ exit 0
36
+ fi
37
+
38
+ # ── Guard: task store exists ──────────────────────────────────────────
39
+ if [[ ! -f "$TASK_STORE" ]]; then
40
+ exit 0
41
+ fi
42
+
43
+ # ── Resolve vault name and tasks path ─────────────────────────────────
44
+ VAULT_NAME=$(grep 'vault_name:' "$GATEWAY" 2>/dev/null | head -1 | sed "s/.*vault_name:\s*['\"]*//" | sed "s/['\"].*//")
45
+ if [[ -z "$VAULT_NAME" ]]; then
46
+ exit 0
47
+ fi
48
+
49
+ # Extract paths.tasks — use awk to match only within the paths: block (not sync:)
50
+ TASKS_PATH=$(awk '/^\s+paths:/{found=1} found && /tasks:/{print; exit}' "$GATEWAY" 2>/dev/null | sed "s/.*tasks:\s*['\"]*//" | sed "s/['\"].*//")
51
+ TASKS_PATH="${TASKS_PATH:-Tasks}"
52
+
53
+ PROJECT_NAME=$(basename "$(pwd)")
54
+
55
+ # ── Read tool result from stdin (Claude hook protocol) ────────────────
56
+ TOOL_RESULT=""
57
+ if [[ ! -t 0 ]]; then
58
+ TOOL_RESULT=$(cat 2>/dev/null || true)
59
+ fi
60
+
61
+ # Extract task ID from the tool result JSON
62
+ TASK_ID=""
63
+ if [[ -n "$TOOL_RESULT" ]]; then
64
+ TASK_ID=$(echo "$TOOL_RESULT" | grep -o '"id"\s*:\s*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
65
+ fi
66
+
67
+ # If we don't have a specific task ID, sync the most recent task
68
+ if [[ -z "$TASK_ID" ]]; then
69
+ TASK_ID=$(tail -1 "$TASK_STORE" | grep -o '"id"\s*:\s*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
70
+ fi
71
+
72
+ if [[ -z "$TASK_ID" ]]; then
73
+ exit 0
74
+ fi
75
+
76
+ # ── Extract task data from JSONL ──────────────────────────────────────
77
+ # Get the latest event for this task
78
+ TASK_LINE=$(grep "\"$TASK_ID\"" "$TASK_STORE" | tail -1)
79
+
80
+ if [[ -z "$TASK_LINE" ]]; then
81
+ exit 0
82
+ fi
83
+
84
+ # Extract fields (simple grep — avoids jq dependency)
85
+ TITLE=$(echo "$TASK_LINE" | grep -o '"title"\s*:\s*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
86
+ STATUS=$(echo "$TASK_LINE" | grep -o '"status"\s*:\s*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
87
+ URGENCY=$(echo "$TASK_LINE" | grep -o '"urgency"\s*:\s*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
88
+ ASSIGNEE=$(echo "$TASK_LINE" | grep -o '"assignee"\s*:\s*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
89
+
90
+ TITLE="${TITLE:-Untitled Task}"
91
+ STATUS="${STATUS:-created}"
92
+ URGENCY="${URGENCY:-normal}"
93
+
94
+ # ── Create/update note via Obsidian CLI ───────────────────────────────
95
+ NOTE_NAME="${TASK_ID} ${TITLE}"
96
+
97
+ CONTENT="---
98
+ reagent_managed: true
99
+ task_id: ${TASK_ID}
100
+ project: ${PROJECT_NAME}
101
+ status: ${STATUS}
102
+ urgency: ${URGENCY}
103
+ assignee: ${ASSIGNEE:-unassigned}
104
+ ---
105
+
106
+ # ${TITLE}
107
+
108
+ - **ID:** ${TASK_ID}
109
+ - **Status:** ${STATUS}
110
+ - **Urgency:** ${URGENCY}
111
+ - **Assignee:** ${ASSIGNEE:-unassigned}
112
+ - **Project:** ${PROJECT_NAME}
113
+ "
114
+
115
+ # Create note (overwrites if exists)
116
+ "$OBSIDIAN_CLI" create \
117
+ --vault "$VAULT_NAME" \
118
+ --path "$TASKS_PATH" \
119
+ --name "$NOTE_NAME" \
120
+ -- "$CONTENT" 2>/dev/null || true
121
+
122
+ # Set properties via CLI for proper frontmatter handling
123
+ "$OBSIDIAN_CLI" property:set --vault "$VAULT_NAME" --file "${TASKS_PATH}/${NOTE_NAME}.md" \
124
+ --name "status" --value "$STATUS" 2>/dev/null || true
125
+
126
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bookedsolid/reagent",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "Governance layer for Claude Code — policy enforcement, hook-based safety gates, and audit logging for AI-assisted projects",
5
5
  "license": "MIT",
6
6
  "author": "Booked Solid Technology <oss@bookedsolid.tech> (https://bookedsolid.tech)",
@@ -6,7 +6,7 @@
6
6
  "huskyPreCommit": true,
7
7
  "huskyPrePush": true,
8
8
  "cursorRules": ["001-no-hallucination", "002-verify-before-act", "003-attribution"],
9
- "blockedPaths": [".reagent/", ".env"],
9
+ "blockedPaths": [".reagent/policy.yaml", ".reagent/HALT", ".env"],
10
10
  "gitignoreEntries": [".claude/agents/", ".claude/hooks/", ".claude/settings.json", "RESTART.md"],
11
11
  "qualityGates": {
12
12
  "commitReview": { "enabled": true, "trivialThreshold": 20, "significantThreshold": 200 },
@@ -6,7 +6,7 @@
6
6
  "huskyPreCommit": true,
7
7
  "huskyPrePush": true,
8
8
  "cursorRules": ["001-no-hallucination", "002-verify-before-act", "003-attribution"],
9
- "blockedPaths": [".reagent/", ".github/workflows/", ".env", ".env.*"],
9
+ "blockedPaths": [".reagent/policy.yaml", ".reagent/HALT", ".github/workflows/", ".env", ".env.*"],
10
10
  "gitignoreEntries": [".claude/hooks/", ".claude/settings.json", "RESTART.md"],
11
11
  "qualityGates": {
12
12
  "commitReview": { "enabled": true, "trivialThreshold": 20, "significantThreshold": 200 },