@delorenj/pjangler 1.1.5 → 1.2.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 (139) hide show
  1. package/.mise/scripts/link-agentfiles.sh +9 -0
  2. package/dist/index.js +1492 -358
  3. package/dist/mcp-server.js +2391 -290
  4. package/package.json +5 -2
  5. package/templates/commonproject/.mise/scripts/create-plane-project.sh +26 -0
  6. package/templates/commonproject/copier.yml +2 -0
  7. package/templates/commonproject/template/.agents/hooks/README.md +151 -0
  8. package/templates/commonproject/template/.agents/hooks/hermes/hindsight-hook.sh +63 -0
  9. package/templates/commonproject/template/.agents/hooks/hindsight/hindsight-journal-write.sh +327 -0
  10. package/templates/commonproject/template/.agents/hooks/hindsight/hindsight-recall.sh +180 -0
  11. package/templates/commonproject/template/.agents/hooks/hindsight/hindsight-retain.sh +74 -0
  12. package/templates/commonproject/template/.agents/hooks/hindsight/hindsight-session-end.sh +57 -0
  13. package/templates/commonproject/template/.agents/hooks/hindsight/lib/hindsight-bank.sh +57 -0
  14. package/templates/commonproject/template/.agents/hooks/hindsight/lib/hindsight-journal.sh +48 -0
  15. package/templates/commonproject/template/.agents/hooks/hooks.master.json +97 -0
  16. package/templates/commonproject/template/.agents/hooks/lib/hook-guard.sh +30 -0
  17. package/templates/commonproject/template/.agents/hooks/lib/local-config.sh +51 -0
  18. package/templates/commonproject/template/.agents/hooks/reminder-for-skill-check.sh +20 -0
  19. package/templates/commonproject/template/.agents/hooks/sync.py +618 -0
  20. package/templates/commonproject/template/.agents/local.example.json +15 -0
  21. package/templates/commonproject/template/.mise/scripts/hindsight-setup.sh +62 -0
  22. package/templates/commonproject/template/.mise/scripts/link-project-skills-to-clis.sh +110 -0
  23. package/templates/commonproject/template/.mise/scripts/unlink-project-skills-from-clis.sh +45 -0
  24. package/templates/commonproject/template/mise.toml.jinja +42 -0
  25. package/templates/hermes-agent/.codegraph/daemon.pid +6 -0
  26. package/templates/hermes-agent/.omo/run-continuation/ses_0e5b28303ffeXxL53hZjKggXDW.json +10 -0
  27. package/templates/hermes-agent/README.md +16 -14
  28. package/templates/hermes-agent/copier.yml +7 -25
  29. package/templates/hermes-agent/docs/architecture.md +16 -18
  30. package/templates/hermes-agent/docs/fleet-control-plane/README.md +35 -0
  31. package/templates/hermes-agent/docs/fleet-control-plane/architecture.md +253 -0
  32. package/templates/hermes-agent/docs/fleet-control-plane/epics-and-stories.md +280 -0
  33. package/templates/hermes-agent/docs/fleet-control-plane/implementation-readiness-report-2026-06-27.md +118 -0
  34. package/templates/hermes-agent/docs/fleet-control-plane/prd.md +141 -0
  35. package/templates/hermes-agent/docs/operations.md +17 -19
  36. package/templates/hermes-agent/docs/runbooks/runtime-checkpoint-repair.md +17 -13
  37. package/templates/hermes-agent/docs/{scrum-master → sentinel}/README.md +47 -46
  38. package/templates/hermes-agent/docs/{scrum-master → sentinel}/architecture.md +39 -38
  39. package/templates/hermes-agent/docs/{scrum-master → sentinel}/development.md +63 -99
  40. package/templates/hermes-agent/docs/{scrum-master → sentinel}/providers.md +2 -2
  41. package/templates/hermes-agent/docs/sentinel.md +18 -0
  42. package/templates/hermes-agent/install-local.sh +22 -20
  43. package/templates/hermes-agent/runtime-scaffold/README.md +10 -9
  44. package/templates/hermes-agent/scripts/backfill-fleet-sot.sh +12 -21
  45. package/templates/hermes-agent/scripts/fleet-sync.sh +369 -0
  46. package/templates/hermes-agent/scripts/migrate-unify.sh +252 -0
  47. package/templates/hermes-agent/scripts/unify-pm.sh +148 -0
  48. package/templates/hermes-agent/template/.runtime-scaffold/README.md +10 -9
  49. package/templates/hermes-agent/template/.scripts/01-config.sh +2 -3
  50. package/templates/hermes-agent/template/.scripts/05-fleet-env.sh +0 -2
  51. package/templates/hermes-agent/template/.scripts/10-hermes-profile.sh +47 -19
  52. package/templates/hermes-agent/template/.scripts/20-runtime-repo.sh +71 -27
  53. package/templates/hermes-agent/template/.scripts/30-telegram.sh +3 -3
  54. package/templates/hermes-agent/template/.scripts/42-ticket-provider.sh +3 -3
  55. package/templates/hermes-agent/template/.scripts/70-systemd.sh +48 -43
  56. package/templates/hermes-agent/template/.scripts/80-registry.sh +2 -2
  57. package/templates/hermes-agent/template/.scripts/99-summary.sh +3 -4
  58. package/templates/hermes-agent/template/.scripts/_lib.sh +4 -19
  59. package/templates/hermes-agent/template/.scripts/checkpoint.sh +36 -0
  60. package/templates/hermes-agent/template/.scripts/config.example.toml +11 -5
  61. package/templates/hermes-agent/template/.scripts/{scrum-master/continuous-ticket-sentinel.sh → heartbeat.sh} +84 -31
  62. package/templates/hermes-agent/template/.scripts/lib/ticket-provider.sh +1 -1
  63. package/templates/hermes-agent/template/.scripts/providers/plane.sh +9 -3
  64. package/templates/hermes-agent/template/.scripts/{scrum-master → sentinel}/bin/emit-event.py +5 -5
  65. package/templates/hermes-agent/template/.scripts/{scrum-master → sentinel}/bin/issue-autonomous-review.sh +6 -6
  66. package/templates/hermes-agent/template/.scripts/{scrum-master → sentinel}/bin/issue-close-gate.sh +2 -2
  67. package/templates/hermes-agent/template/.scripts/{scrum-master → sentinel}/docs/autonomous-delegated-review.md +7 -7
  68. package/templates/hermes-agent/template/.scripts/{scrum-master → sentinel}/docs/bloodbank-events.md +3 -3
  69. package/templates/hermes-agent/template/.scripts/{scrum-master → sentinel}/docs/continuous-ticket-orchestration.md +6 -6
  70. package/templates/hermes-agent/template/.scripts/{scrum-master/continuous-ticket-sentinel.prompt.md.jinja → sentinel.prompt.md.jinja} +5 -4
  71. package/templates/hermes-agent/template/SOUL.md.jinja +3 -6
  72. package/templates/hermes-agent/template/hermes.jinja +6 -15
  73. package/templates/hermes-agent/template/role.yaml.jinja +9 -8
  74. package/templates/commonproject/.claude/commands/bmad/bmb/agents/agent-builder.md +0 -14
  75. package/templates/commonproject/.claude/commands/bmad/bmb/agents/module-builder.md +0 -14
  76. package/templates/commonproject/.claude/commands/bmad/bmb/agents/workflow-builder.md +0 -14
  77. package/templates/commonproject/.claude/commands/bmad/bmb/workflows/agent.md +0 -5
  78. package/templates/commonproject/.claude/commands/bmad/bmb/workflows/module.md +0 -5
  79. package/templates/commonproject/.claude/commands/bmad/bmb/workflows/workflow.md +0 -5
  80. package/templates/commonproject/.claude/commands/bmad/bmm/agents/analyst.md +0 -14
  81. package/templates/commonproject/.claude/commands/bmad/bmm/agents/architect.md +0 -14
  82. package/templates/commonproject/.claude/commands/bmad/bmm/agents/dev.md +0 -14
  83. package/templates/commonproject/.claude/commands/bmad/bmm/agents/pm.md +0 -14
  84. package/templates/commonproject/.claude/commands/bmad/bmm/agents/quick-flow-solo-dev.md +0 -14
  85. package/templates/commonproject/.claude/commands/bmad/bmm/agents/sm.md +0 -14
  86. package/templates/commonproject/.claude/commands/bmad/bmm/agents/tea.md +0 -14
  87. package/templates/commonproject/.claude/commands/bmad/bmm/agents/tech-writer.md +0 -14
  88. package/templates/commonproject/.claude/commands/bmad/bmm/agents/ux-designer.md +0 -14
  89. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/check-implementation-readiness.md +0 -5
  90. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/code-review.md +0 -13
  91. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/correct-course.md +0 -13
  92. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-architecture.md +0 -5
  93. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-epics-and-stories.md +0 -5
  94. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-excalidraw-dataflow.md +0 -13
  95. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-excalidraw-diagram.md +0 -13
  96. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-excalidraw-flowchart.md +0 -13
  97. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-excalidraw-wireframe.md +0 -13
  98. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-product-brief.md +0 -5
  99. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-story.md +0 -13
  100. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/create-ux-design.md +0 -5
  101. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/dev-story.md +0 -13
  102. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/document-project.md +0 -13
  103. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/generate-project-context.md +0 -5
  104. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/prd.md +0 -5
  105. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/quick-dev.md +0 -5
  106. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/quick-spec.md +0 -5
  107. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/research.md +0 -5
  108. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/retrospective.md +0 -13
  109. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/sprint-planning.md +0 -13
  110. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/sprint-status.md +0 -13
  111. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-atdd.md +0 -13
  112. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-automate.md +0 -13
  113. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-ci.md +0 -13
  114. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-framework.md +0 -13
  115. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-nfr.md +0 -13
  116. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-test-design.md +0 -13
  117. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-test-review.md +0 -13
  118. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/testarch-trace.md +0 -13
  119. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/workflow-init.md +0 -13
  120. package/templates/commonproject/.claude/commands/bmad/bmm/workflows/workflow-status.md +0 -13
  121. package/templates/commonproject/.claude/commands/bmad/cis/agents/brainstorming-coach.md +0 -14
  122. package/templates/commonproject/.claude/commands/bmad/cis/agents/creative-problem-solver.md +0 -14
  123. package/templates/commonproject/.claude/commands/bmad/cis/agents/design-thinking-coach.md +0 -14
  124. package/templates/commonproject/.claude/commands/bmad/cis/agents/innovation-strategist.md +0 -14
  125. package/templates/commonproject/.claude/commands/bmad/cis/agents/presentation-master.md +0 -14
  126. package/templates/commonproject/.claude/commands/bmad/cis/agents/storyteller.md +0 -14
  127. package/templates/commonproject/.claude/commands/bmad/cis/workflows/design-thinking.md +0 -13
  128. package/templates/commonproject/.claude/commands/bmad/cis/workflows/innovation-strategy.md +0 -13
  129. package/templates/commonproject/.claude/commands/bmad/cis/workflows/problem-solving.md +0 -13
  130. package/templates/commonproject/.claude/commands/bmad/cis/workflows/storytelling.md +0 -13
  131. package/templates/commonproject/.claude/commands/bmad/core/agents/bmad-master.md +0 -14
  132. package/templates/commonproject/.claude/commands/bmad/core/tasks/index-docs.md +0 -9
  133. package/templates/commonproject/.claude/commands/bmad/core/tasks/shard-doc.md +0 -9
  134. package/templates/commonproject/.claude/commands/bmad/core/workflows/brainstorming.md +0 -5
  135. package/templates/commonproject/.claude/commands/bmad/core/workflows/party-mode.md +0 -5
  136. package/templates/commonproject/.claude/commands/bmad/custom/workflows/ticket-lifecycle.md +0 -5
  137. package/templates/hermes-agent/docs/scrum-master-sentinel.md +0 -17
  138. package/templates/hermes-agent/template/.scripts/75-scrum-master.sh +0 -117
  139. package/templates/hermes-agent/template/.scripts/90-chain-scrum-master.sh +0 -45
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env bash
2
+ # Claude Code UserPromptSubmit hook: recall Hindsight memories.
3
+ # Reads JSON from stdin (Claude hook contract), outputs context to stdout.
4
+ # Gracefully exits if hindsight CLI is not installed.
5
+ set -euo pipefail
6
+
7
+ HS_BIN="${HOME}/.local/bin/hindsight"
8
+
9
+ # Parse Claude's JSON stdin
10
+ INPUT=$(cat)
11
+ PROMPT=$(echo "$INPUT" | jq -r '.prompt // .message // empty' 2>/dev/null || echo "")
12
+
13
+ # Load journal telemetry helper (no-ops gracefully on its own)
14
+ # shellcheck source=lib/hindsight-journal.sh
15
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/hindsight-journal.sh"
16
+ SESSION_ID=$(journal_session_id_from "$INPUT")
17
+ PROMPT_FIRST120=$(printf '%s' "$PROMPT" | head -c 120)
18
+
19
+ # Skip if hindsight CLI is missing — but still log the skip for the journal.
20
+ if [[ ! -x "$HS_BIN" ]]; then
21
+ journal_log "$SESSION_ID" "$(jq -nc \
22
+ --arg p "$PROMPT_FIRST120" \
23
+ --argjson plen ${#PROMPT} \
24
+ '{event:"recall_skipped", reason:"hindsight_binary_missing", prompt_len:$plen, prompt_first120:$p}')"
25
+ exit 0
26
+ fi
27
+
28
+ # Skip empty or noisy prompts — but still log the skip.
29
+ if [[ -z "$PROMPT" ]] || [[ ${#PROMPT} -lt 24 ]]; then
30
+ journal_log "$SESSION_ID" "$(jq -nc \
31
+ --arg p "$PROMPT_FIRST120" \
32
+ --argjson plen ${#PROMPT} \
33
+ '{event:"recall_skipped", reason:(if $plen==0 then "empty_prompt" else "prompt_under_min_length" end), min_length:24, prompt_len:$plen, prompt_first120:$p}')"
34
+ exit 0
35
+ fi
36
+
37
+ # Detect bank via shared resolver (GitHub remote name → repo basename → "general")
38
+ # shellcheck source=lib/hindsight-bank.sh
39
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/hindsight-bank.sh"
40
+ BANK=$(resolve_bank)
41
+
42
+ # Per-source line caps keep total bounded without starving later layers.
43
+ PRIMARY_LINES="${HINDSIGHT_PRIMARY_LINES:-30}"
44
+ FALLBACK_LINES="${HINDSIGHT_FALLBACK_LINES:-15}"
45
+ LINKED_LINES_PER_BANK="${HINDSIGHT_LINKED_LINES:-12}"
46
+
47
+ recall_cli() {
48
+ NO_COLOR=1 \
49
+ CLICOLOR=0 \
50
+ CLICOLOR_FORCE=0 \
51
+ FORCE_COLOR=0 \
52
+ TERM=dumb \
53
+ COLORTERM= \
54
+ RICH_NO_COLOR=1 \
55
+ HINDSIGHT_NO_COLOR=1 \
56
+ HINDSIGHT_NO_SPINNER=1 \
57
+ HINDSIGHT_DISABLE_SPINNER=1 \
58
+ "$HS_BIN" memory recall "$@"
59
+ }
60
+
61
+ sanitize_recall_output() {
62
+ if command -v perl >/dev/null 2>&1; then
63
+ perl -pe 's/\e\][^\a]*(?:\a|\e\\)//g; s/\e\[[0-?]*[ -\/]*[@-~]//g; s/[^\r\n]*\r//g; s/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]//g'
64
+ else
65
+ sed -E $'s|\x1B\\[[0-?]*[ -/]*[@-~]||g; s|\x1B\\][^\a]*(\a|\x1B\\\\)||g' | tr '\r' '\n'
66
+ fi
67
+ }
68
+
69
+ cap() { sanitize_recall_output | sed '/^[[:space:]]*$/d' | head -n "$1"; }
70
+
71
+ # Primary bank recall
72
+ MEMORIES=$(recall_cli "$BANK" "$PROMPT" 2>/dev/null | cap "$PRIMARY_LINES" || echo "")
73
+
74
+ # Fallback to general if primary bank is different
75
+ FALLBACK=""
76
+ if [[ "$BANK" != "general" ]]; then
77
+ FALLBACK=$(recall_cli "general" "$PROMPT" 2>/dev/null | cap "$FALLBACK_LINES" || echo "")
78
+ fi
79
+
80
+ # Always-on global banks — cross-project knowledge that should surface
81
+ # regardless of which repo we're in (homelab routing, infra conventions, etc).
82
+ # Override via HINDSIGHT_GLOBAL_BANKS="infra dotfiles ..." (space-separated).
83
+ GLOBAL_BANKS="${HINDSIGHT_GLOBAL_BANKS:-infra}"
84
+ GLOBAL=""
85
+ for gb in $GLOBAL_BANKS; do
86
+ [[ "$gb" == "$BANK" || "$gb" == "general" ]] && continue
87
+ gout=$(recall_cli "$gb" "$PROMPT" --budget low 2>/dev/null | cap "$FALLBACK_LINES" || echo "")
88
+ if [[ -n "$gout" ]]; then
89
+ GLOBAL="${GLOBAL}
90
+ <!-- global-bank: $gb -->
91
+ ${gout}"
92
+ fi
93
+ done
94
+
95
+ # Linked-bank fanout via Dream's graph cache.
96
+ # Cache path/threshold/fanout cap are env-tunable; set HINDSIGHT_FANOUT=0 to disable.
97
+ LINKED=""
98
+ GRAPH_CACHE="${HINDSIGHT_DREAM_GRAPH:-${HOME}/.hindsight/dream/bank-graph.json}"
99
+ FANOUT_ENABLED="${HINDSIGHT_FANOUT:-1}"
100
+ FANOUT_MAX="${HINDSIGHT_FANOUT_MAX:-2}"
101
+ FANOUT_MIN_SCORE="${HINDSIGHT_FANOUT_MIN_SCORE:-0.1}"
102
+
103
+ LINKED_BANKS_USED=()
104
+ if [[ "$FANOUT_ENABLED" != "0" ]] && [[ -f "$GRAPH_CACHE" ]] && command -v jq &>/dev/null; then
105
+ LINKED_BANKS=$(jq -r --arg b "$BANK" --argjson min "$FANOUT_MIN_SCORE" --argjson n "$FANOUT_MAX" \
106
+ '(.graph[$b] // []) | map(select(.score >= $min)) | .[:$n] | .[].bank' \
107
+ "$GRAPH_CACHE" 2>/dev/null || true)
108
+ for linked in $LINKED_BANKS; do
109
+ [[ -z "$linked" || "$linked" == "$BANK" || "$linked" == "general" ]] && continue
110
+ out=$(recall_cli "$linked" "$PROMPT" --budget low --max-tokens 1024 2>/dev/null | cap "$LINKED_LINES_PER_BANK" || echo "")
111
+ if [[ -n "$out" ]]; then
112
+ LINKED="${LINKED}
113
+ <!-- linked-bank: $linked -->
114
+ ${out}"
115
+ LINKED_BANKS_USED+=("$linked")
116
+ fi
117
+ done
118
+ fi
119
+
120
+ ALL=""
121
+ [[ -n "$MEMORIES" ]] && ALL="${MEMORIES}"
122
+ [[ -n "$FALLBACK" ]] && ALL="${ALL}
123
+ ${FALLBACK}"
124
+ [[ -n "$GLOBAL" ]] && ALL="${ALL}
125
+ ${GLOBAL}"
126
+ [[ -n "$LINKED" ]] && ALL="${ALL}
127
+ ${LINKED}"
128
+
129
+ # Final overall guard so we never dump unbounded recall output.
130
+ ALL=$({ printf '%s\n' "$ALL" | sanitize_recall_output | sed '/^[[:space:]]*$/d' | head -100; } || true)
131
+
132
+ if [[ -n "$ALL" ]]; then
133
+ printf '\n'
134
+ printf '<!-- hindsight:recall bank=%s -->\n' "$BANK"
135
+ printf '%s\n' "$ALL"
136
+ printf '<!-- /hindsight:recall -->\n'
137
+ fi
138
+
139
+ # --- Journal telemetry: record what we asked and what we got back ---
140
+ count_nonempty() { printf '%s\n' "$1" | awk 'NF{c++} END{print c+0}'; }
141
+ primary_lines=$(count_nonempty "$MEMORIES")
142
+ fallback_lines=$(count_nonempty "$FALLBACK")
143
+ global_lines=$(count_nonempty "$GLOBAL")
144
+ linked_lines=$(count_nonempty "$LINKED")
145
+ total_lines=$(count_nonempty "$ALL")
146
+ total_chars=${#ALL}
147
+ # Empty array if no linked banks; otherwise JSON array of names.
148
+ if [[ ${#LINKED_BANKS_USED[@]} -eq 0 ]]; then
149
+ linked_json='[]'
150
+ else
151
+ linked_json=$(printf '%s\n' "${LINKED_BANKS_USED[@]}" | jq -R . | jq -sc .)
152
+ fi
153
+
154
+ journal_log "$SESSION_ID" "$(jq -nc \
155
+ --arg bank "$BANK" \
156
+ --arg p "$PROMPT_FIRST120" \
157
+ --argjson plen ${#PROMPT} \
158
+ --argjson pl "$primary_lines" \
159
+ --argjson fl "$fallback_lines" \
160
+ --argjson gl "$global_lines" \
161
+ --argjson ll "$linked_lines" \
162
+ --argjson tl "$total_lines" \
163
+ --argjson tc "$total_chars" \
164
+ --argjson linked "$linked_json" \
165
+ --arg global "$GLOBAL_BANKS" \
166
+ '{
167
+ event: "recall",
168
+ bank: $bank,
169
+ prompt_len: $plen,
170
+ prompt_first120: $p,
171
+ primary_lines: $pl,
172
+ fallback_lines: $fl,
173
+ global_lines: $gl,
174
+ linked_lines: $ll,
175
+ total_lines: $tl,
176
+ total_chars: $tc,
177
+ linked_banks: $linked,
178
+ global_banks: $global,
179
+ returned_anything: ($tl > 0)
180
+ }')"
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env bash
2
+ # Claude Code PostToolUse hook: retain file edits in Hindsight.
3
+ # Reads JSON from stdin (Claude hook contract), fire-and-forget.
4
+ # Gracefully exits if hindsight CLI is not installed.
5
+ set -euo pipefail
6
+
7
+ HS_BIN="${HOME}/.local/bin/hindsight"
8
+ if [[ ! -x "$HS_BIN" ]]; then
9
+ exit 0
10
+ fi
11
+
12
+ INPUT=$(cat)
13
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null || echo "")
14
+
15
+ if [[ -z "$FILE_PATH" ]]; then
16
+ exit 0
17
+ fi
18
+
19
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null || echo "")
20
+ CONTENT_LEN=${#CONTENT}
21
+
22
+ if [[ $CONTENT_LEN -lt 50 ]]; then
23
+ exit 0
24
+ fi
25
+
26
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
27
+ if [[ -n "$REPO_ROOT" ]]; then
28
+ REL_PATH="${FILE_PATH#$REPO_ROOT/}"
29
+ else
30
+ REL_PATH="$FILE_PATH"
31
+ fi
32
+
33
+ EXT="${FILE_PATH##*.}"
34
+ SNIPPET=$(echo "$CONTENT" | head -5 | tr '\n' ' ' | cut -c1-200)
35
+
36
+ # Detect bank via shared resolver (GitHub remote name → repo basename → "general")
37
+ # shellcheck source=lib/hindsight-bank.sh
38
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/hindsight-bank.sh"
39
+ BANK=$(resolve_bank)
40
+
41
+ # Load journal telemetry helper
42
+ # shellcheck source=lib/hindsight-journal.sh
43
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/hindsight-journal.sh"
44
+ SESSION_ID=$(journal_session_id_from "$INPUT")
45
+
46
+ RETAIN_TEXT="Edited ${REL_PATH} (${EXT}): ${SNIPPET}"
47
+
48
+ # Fire-and-forget retain
49
+ "$HS_BIN" memory retain "$BANK" "$RETAIN_TEXT" \
50
+ --context "code-edit" &>/dev/null &
51
+
52
+ # Journal telemetry: record what we retained
53
+ journal_log "$SESSION_ID" "$(jq -nc \
54
+ --arg bank "$BANK" \
55
+ --arg file "$REL_PATH" \
56
+ --arg ext "$EXT" \
57
+ --arg ctx "code-edit" \
58
+ --arg text "$RETAIN_TEXT" \
59
+ --arg snippet "$SNIPPET" \
60
+ --argjson len $CONTENT_LEN \
61
+ --argjson auto true \
62
+ '{
63
+ event: "retain",
64
+ bank: $bank,
65
+ context: $ctx,
66
+ file: $file,
67
+ ext: $ext,
68
+ content_len: $len,
69
+ snippet: $snippet,
70
+ retained_text: $text,
71
+ source: "auto_posttooluse"
72
+ }')"
73
+
74
+ exit 0
@@ -0,0 +1,57 @@
1
+ #!/bin/bash
2
+ # Claude Code Stop hook: capture session summary in Hindsight + write journal entry.
3
+ # Gracefully exits if hindsight CLI is not installed.
4
+ #
5
+ # Hardening notes (2026-05-11):
6
+ # - Every external call is wrapped in `timeout` so a wedged Hindsight API
7
+ # (or any other downstream) can't anchor the hook beyond a known bound.
8
+ # - The journal-writer pipe was bugged: `printf ... | setsid CMD </dev/null`
9
+ # had its pipe data discarded because the explicit /dev/null redirect on
10
+ # the setsid command overrode the pipe. Fixed by writing $INPUT to a temp
11
+ # file and feeding it via `< $tmp` (no pipe → no override).
12
+ # - Claude Code's hook contract waits for the foreground process AND any
13
+ # inherited stdio fds to close. setsid+nohup+disown handles process
14
+ # detachment; the explicit fd redirects (now ordered correctly) close out
15
+ # the parent's pipe so the harness can return immediately.
16
+ set -uo pipefail
17
+
18
+ HS_BIN="${HOME}/.local/bin/hindsight"
19
+
20
+ # Stdin holds the Stop hook JSON ({session_id, transcript_path, cwd, ...}).
21
+ # Persist it to a temp file so we can deliver it to a detached child via
22
+ # fd redirect (not a pipe — see header note).
23
+ INPUT_TMP=$(mktemp -t hindsight-stop-input.XXXXXX)
24
+ cat > "$INPUT_TMP" 2>/dev/null || echo "{}" > "$INPUT_TMP"
25
+
26
+ # Best-effort cleanup. The journal writer reads $INPUT_TMP early in its run,
27
+ # so a short delay before unlinking is safe; we just trust the OS to GC the
28
+ # inode after both readers exit. No trap needed.
29
+
30
+ # 1. Write the journal entry (uses per-session telemetry log).
31
+ # Fully daemonize: setsid + nohup + closed std fds so the writer survives
32
+ # after this hook returns. The reflect step can take 30+ seconds, and
33
+ # Claude Code reaps the hook's process group when the foreground command
34
+ # returns — without setsid the writer gets SIGHUP'd mid-call.
35
+ JOURNAL_WRITER="$(dirname "${BASH_SOURCE[0]}")/hindsight-journal-write.sh"
36
+ if [[ -x "$JOURNAL_WRITER" ]]; then
37
+ setsid nohup "$JOURNAL_WRITER" \
38
+ < "$INPUT_TMP" \
39
+ > /dev/null 2>&1 &
40
+ disown 2>/dev/null || true
41
+ fi
42
+
43
+ # 2. Record a session-end marker memory (legacy behavior). Same detach pattern.
44
+ # Wrapped in `timeout 3` defensively even though it's already detached:
45
+ # if a future change ever runs this synchronously, the bound still holds.
46
+ if [[ -x "$HS_BIN" ]]; then
47
+ # shellcheck source=lib/hindsight-bank.sh
48
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/hindsight-bank.sh"
49
+ BANK=$(resolve_bank)
50
+ setsid nohup timeout 30 "$HS_BIN" memory retain "$BANK" \
51
+ "Session ended at $(date -u +%Y-%m-%dT%H:%M:%SZ)" \
52
+ --context "session-summary" \
53
+ < /dev/null > /dev/null 2>&1 &
54
+ disown 2>/dev/null || true
55
+ fi
56
+
57
+ exit 0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # Shared bank resolver for Hindsight hooks.
3
+ # Source this file, then call: BANK=$(resolve_bank)
4
+ #
5
+ # Resolution order (first non-empty wins):
6
+ # 1. .hindsight/bank file in repo root (explicit override; written by Dream agent or by hand)
7
+ # 2. HINDSIGHT_BANK env var (per-shell override; useful for ad-hoc routing)
8
+ # 3. GitHub remote repo name (origin) e.g. "delorenj/claude-runtime" -> "claude-runtime"
9
+ # 4. Local repo root basename (fallback when no remote configured)
10
+ # 5. "general" (when not in a git repo at all)
11
+ #
12
+ # The override file format is a single line containing the bank name. Whitespace
13
+ # and a trailing newline are stripped. Lines starting with '#' are treated as
14
+ # comments and ignored.
15
+
16
+ resolve_bank() {
17
+ local url name root override
18
+
19
+ root=$(git rev-parse --show-toplevel 2>/dev/null || true)
20
+
21
+ # 1. Repo-local override file
22
+ if [[ -n "$root" && -f "$root/.hindsight/bank" ]]; then
23
+ override=$(grep -v '^[[:space:]]*#' "$root/.hindsight/bank" 2>/dev/null \
24
+ | head -n1 \
25
+ | tr -d '[:space:]')
26
+ if [[ -n "$override" ]]; then
27
+ echo "$override"
28
+ return
29
+ fi
30
+ fi
31
+
32
+ # 2. Env var override
33
+ if [[ -n "${HINDSIGHT_BANK:-}" ]]; then
34
+ echo "$HINDSIGHT_BANK"
35
+ return
36
+ fi
37
+
38
+ # 3. GitHub remote name
39
+ url=$(git remote get-url origin 2>/dev/null || true)
40
+ if [[ -n "$url" ]]; then
41
+ url="${url%.git}"
42
+ name="${url##*/}"
43
+ if [[ -n "$name" ]]; then
44
+ echo "$name"
45
+ return
46
+ fi
47
+ fi
48
+
49
+ # 4. Repo root basename
50
+ if [[ -n "$root" ]]; then
51
+ basename "$root"
52
+ return
53
+ fi
54
+
55
+ # 5. Final fallback
56
+ echo "general"
57
+ }
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ # Shared telemetry-logging helpers for the Hindsight journal pipeline.
3
+ #
4
+ # Usage:
5
+ # source "~/.agents/hooks/lib/hindsight-journal.sh"
6
+ # journal_log "$SESSION_ID" '{"event":"recall","bank":"foo",...}'
7
+ #
8
+ # Each session's events are appended to a per-session JSONL file:
9
+ # ~/.agents/journal/sessions/<session_id>.jsonl
10
+ #
11
+ # The Stop hook reads this file at session-end to synthesize the journal entry,
12
+ # then the file is left in place (for forensics) or pruned by a separate sweeper.
13
+
14
+ HS_JOURNAL_DIR="${HS_JOURNAL_DIR:-${HOME}/.agents/journal}"
15
+ HS_JOURNAL_SESSIONS_DIR="${HS_JOURNAL_DIR}/sessions"
16
+
17
+ # Append one event (a JSON object string) to the current session's log.
18
+ # Silently no-ops if session_id is empty or jq is unavailable.
19
+ journal_log() {
20
+ local session_id="$1"
21
+ local event_json="$2"
22
+
23
+ [[ -z "$session_id" ]] && return 0
24
+ [[ -z "$event_json" ]] && return 0
25
+ command -v jq >/dev/null 2>&1 || return 0
26
+
27
+ mkdir -p "$HS_JOURNAL_SESSIONS_DIR" 2>/dev/null || return 0
28
+
29
+ local log_file="${HS_JOURNAL_SESSIONS_DIR}/${session_id}.jsonl"
30
+ local ts
31
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
32
+
33
+ # Validate + inject timestamp; drop the event silently if it isn't valid JSON.
34
+ printf '%s' "$event_json" |
35
+ jq -c --arg ts "$ts" '. + {ts: $ts}' >>"$log_file" 2>/dev/null ||
36
+ true
37
+ }
38
+
39
+ # Extract session_id from Claude Code hook JSON on stdin.
40
+ # Echoes the id (or empty string). The caller is expected to have already cat'd stdin.
41
+ journal_session_id_from() {
42
+ local input="$1"
43
+ command -v jq >/dev/null 2>&1 || {
44
+ echo ""
45
+ return 0
46
+ }
47
+ echo "$input" | jq -r '.session_id // empty' 2>/dev/null || echo ""
48
+ }
@@ -0,0 +1,97 @@
1
+ {
2
+ "$comment": "SINGLE SOURCE OF TRUTH for this repo's project-scoped agent hooks. Hand-edit ONLY this file, then run `mise run hooks-sync` (or re-cd into the repo) to regenerate every per-agent config. Generated files are NOT hand-edited. Pattern: the /ssot-fanout skill. See .agents/hooks/README.md.",
3
+ "version": 1,
4
+ "marker": "project-agent-hooks",
5
+ "hooks": [
6
+ {
7
+ "id": "skill-check-reminder",
8
+ "lifecycle": "user_prompt",
9
+ "script": "reminder-for-skill-check.sh",
10
+ "timeout_s": 3,
11
+ "reads_stdin": false,
12
+ "description": "Inject a project-skills + BMAD-recap reminder into each prompt."
13
+ },
14
+ {
15
+ "id": "hindsight-recall",
16
+ "lifecycle": "user_prompt",
17
+ "script": "hindsight/hindsight-recall.sh",
18
+ "timeout_s": 5,
19
+ "reads_stdin": true,
20
+ "description": "Recall Hindsight memories for the prompt (no-ops gracefully if the hindsight CLI is absent)."
21
+ },
22
+ {
23
+ "id": "hindsight-retain",
24
+ "lifecycle": "post_tool",
25
+ "script": "hindsight/hindsight-retain.sh",
26
+ "matcher": "Write|Edit|MultiEdit",
27
+ "timeout_s": 5,
28
+ "reads_stdin": true,
29
+ "description": "Retain file edits into Hindsight after Write/Edit/MultiEdit."
30
+ },
31
+ {
32
+ "id": "hindsight-session-end",
33
+ "lifecycle": "session_end",
34
+ "script": "hindsight/hindsight-session-end.sh",
35
+ "timeout_s": 5,
36
+ "reads_stdin": true,
37
+ "description": "Capture a session summary + write a Hindsight journal entry on stop."
38
+ }
39
+ ],
40
+ "agents": {
41
+ "claude": {
42
+ "enabled": true,
43
+ "dialect": "claude_settings",
44
+ "config_target": ".claude/settings.json",
45
+ "base_dir": "$CLAUDE_PROJECT_DIR/.agents/hooks",
46
+ "timeout_unit": "s",
47
+ "owns_hooks_key": true,
48
+ "lifecycle_events": {
49
+ "user_prompt": "UserPromptSubmit",
50
+ "post_tool": "PostToolUse",
51
+ "session_end": "Stop"
52
+ },
53
+ "notes": "Committed to the repo, so every dev gets these automatically with NO bootstrap. Claude expands $CLAUDE_PROJECT_DIR in hook commands at runtime. sync owns the entire `hooks` key of this file."
54
+ },
55
+ "codex": {
56
+ "enabled": true,
57
+ "dialect": "codex_hooks",
58
+ "config_target": "~/.codex/hooks.json",
59
+ "base_dir": "{repo}/.agents/hooks",
60
+ "timeout_unit": "ms",
61
+ "lifecycle_events": {
62
+ "user_prompt": "UserPromptSubmit",
63
+ "post_tool": "PostToolUse",
64
+ "session_end": "Stop"
65
+ },
66
+ "notes": "Codex has no project-scoped hook file, so sync INJECTS absolute-path entries into the per-user ~/.codex/hooks.json on `mise enter` and REMOVES them on `mise leave`. Idempotent; backs up to ~/.codex/hooks.json.caf-bak before first write. Set CAF_HOOKS_SKIP_CODEX=1 to opt out (e.g. if you already run these hooks globally)."
67
+ },
68
+ "hermes": {
69
+ "enabled": true,
70
+ "dialect": "hermes_config",
71
+ "config_target": "agents/hermes/pm/runtime/config.yaml",
72
+ "allowlist_target": "agents/hermes/pm/runtime/shell-hooks-allowlist.json",
73
+ "adapter": "hermes/hindsight-hook.sh",
74
+ "runner": "bash {repo}/.agents/hooks/hermes/hindsight-hook.sh",
75
+ "timeout_s": 5,
76
+ "lifecycle_events": {
77
+ "post_tool": "post_tool_call",
78
+ "session_end": "on_session_end"
79
+ },
80
+ "notes": "Hermes runs hooks shell=False (argv) with the payload as JSON on stdin and requires commands in shell-hooks-allowlist.json. It has NO user-prompt event, so hindsight-recall is not wired. Instead of the Claude-shaped scripts, Hermes uses the ADAPTER (hermes/hindsight-hook.sh) which translates the Hermes payload and pins the project bank (repo basename) (resolve_bank from the runtime cwd would resolve to the PM submodule). sync merges into the per-deployment runtime config.yaml + allowlist (idempotent, backed up, skipped if the runtime is absent). Disable per-dev via local.json disabled_agents:[\"hermes\"]."
81
+ },
82
+ "kimi": {
83
+ "enabled": true,
84
+ "dialect": "kimi_config",
85
+ "config_target": "~/.kimi-code/config.toml",
86
+ "config_home_env": "KIMI_CODE_HOME",
87
+ "base_dir": "{repo}/.agents/hooks",
88
+ "timeout_unit": "s",
89
+ "lifecycle_events": {
90
+ "user_prompt": "UserPromptSubmit",
91
+ "post_tool": "PostToolUse",
92
+ "session_end": "Stop"
93
+ },
94
+ "notes": "Kimi Code CLI hooks live in the per-user ~/.kimi-code/config.toml (KIMI_CODE_HOME overrides) as a `hooks` array of {event,command,matcher?,timeout}. Kimi is Claude-compatible: identical event names, payload {hook_event_name,session_id,cwd,prompt} / {tool_name,tool_input} on stdin, shell:true commands, timeout in SECONDS, edit tools named Write/Edit. So it reuses the SAME scripts (guard-wrapped) as claude/codex. No project-local config.toml exists, so sync INJECTS a sentinel-bounded [[hooks]] block on enter and removes it on leave (idempotent text-merge, backs up to config.toml.caf-bak). Disable per-dev via local.json disabled_agents:[\"kimi\"]."
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Per-dev runtime opt-out wrapper for a single hook.
3
+ #
4
+ # hook-guard.sh <hook-id> <command> [args...]
5
+ #
6
+ # If <hook-id> is listed in .agents/local.json `.hooks.disabled[]`, exits 0
7
+ # WITHOUT running the command (silent success — no "hook failed" noise in the
8
+ # agent). Otherwise execs the command, passing stdin straight through so the
9
+ # real hook still receives its JSON payload.
10
+ #
11
+ # This is how a dev disables an individual hook even for Claude, whose hook
12
+ # config is committed (and therefore the same for everyone): the committed
13
+ # command is the guarded form, and the guard consults each dev's local.json at
14
+ # runtime.
15
+ set -uo pipefail
16
+
17
+ HOOK_ID="${1:-}"
18
+ shift || true
19
+
20
+ # shellcheck source=local-config.sh
21
+ source "$(dirname "${BASH_SOURCE[0]}")/local-config.sh"
22
+
23
+ if [[ -n "$HOOK_ID" ]] && hook_disabled "$HOOK_ID"; then
24
+ exit 0
25
+ fi
26
+
27
+ # Nothing to run (defensive) — succeed quietly.
28
+ [[ $# -eq 0 ]] && exit 0
29
+
30
+ exec "$@"
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # Sourceable helpers for per-dev `.agents/local.json` overrides.
3
+ #
4
+ # Source this, then call:
5
+ # hook_disabled <hook-id> # true if .hooks.disabled[] contains the id
6
+ # agent_disabled <agent> # true if .hooks.disabled_agents[] contains the agent
7
+ # skill_disabled <skill-name> # true if .skills.disabled[] contains the name
8
+ #
9
+ # All FAIL OPEN (return 1 = "not disabled") when `.agents/local.json` or `jq` is
10
+ # absent, so a missing/garbled local config can never silently kill hooks/skills.
11
+ #
12
+ # `.agents/local.json` is gitignored — each dev owns their own. See
13
+ # `.agents/local.example.json` for the schema.
14
+
15
+ # Repo root, resolved from this lib's location (.agents/hooks/lib/ -> ../../..).
16
+ _caf_local_json() {
17
+ local d
18
+ d="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." 2>/dev/null && pwd)" || return 1
19
+ printf '%s/.agents/local.json' "$d"
20
+ }
21
+
22
+ # _caf_listed <jq-array-path> <value> -> 0 if value is in the array.
23
+ _caf_listed() {
24
+ local jq_path="$1" val="$2" f
25
+ f="$(_caf_local_json)" || return 1
26
+ [[ -f "$f" ]] || return 1
27
+ command -v jq >/dev/null 2>&1 || return 1
28
+ jq -e --arg v "$val" "((${jq_path} // []) | index(\$v)) != null" "$f" >/dev/null 2>&1
29
+ }
30
+
31
+ hook_disabled() { _caf_listed '.hooks.disabled' "$1"; }
32
+ agent_disabled() { _caf_listed '.hooks.disabled_agents' "$1"; }
33
+ skill_disabled() { _caf_listed '.skills.disabled' "$1"; }
34
+
35
+ # True if the dev opted to let their GLOBAL agent system (~/.agents/skills) provide
36
+ # any overlapping skill, so the project linker should skip it (no duplicates).
37
+ # Teammates with no global layer omit this and inherit the full project set.
38
+ skills_defer_to_global() {
39
+ local f
40
+ f="$(_caf_local_json)" || return 1
41
+ [[ -f "$f" ]] || return 1
42
+ command -v jq >/dev/null 2>&1 || return 1
43
+ jq -e '.skills.defer_to_global == true' "$f" >/dev/null 2>&1
44
+ }
45
+
46
+ # True if <skill-name> is provided by the global agent SSOT. `-e` follows the
47
+ # ~/.agents/skills symlink (-> skillex/skill-sets/global) and its entries.
48
+ skill_is_global() {
49
+ local name="$1" gdir="${AGENTS_GLOBAL_SKILLS_DIR:-$HOME/.agents/skills}"
50
+ [[ -e "${gdir}/${name}" ]]
51
+ }
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+ # UserPromptSubmit hook (PROJECT-SCOPED).
3
+ #
4
+ # Nudges the agent to check this project's skills and resume BMAD context.
5
+ # Portable across developers: the project's skills directory is resolved
6
+ # RELATIVE to this script's location, not a hardcoded home path — so the same
7
+ # committed copy works for every dev who clones the repo.
8
+ #
9
+ # stdout from a UserPromptSubmit hook is injected into the prompt as context.
10
+ set -uo pipefail
11
+
12
+ # This script lives at <repo>/.agents/hooks/reminder-for-skill-check.sh
13
+ HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
+ PROJECT_ROOT="$(cd "${HOOK_DIR}/../.." && pwd)"
15
+ SKILLS_DIR="${PROJECT_ROOT}/.agents/skills"
16
+
17
+ echo "⚡ REMINDER: Before starting, check if any skills in ${SKILLS_DIR}/ are relevant to this request."
18
+ echo " - If this project uses the BMAD method (a ./_bmad* folder exists) start the session with a brief recap of where the last session left off and suggest the next logical step. If there are options, lay them out, think it through, then offer your recommendation. ⚡"
19
+ echo "> NOTE: Treat docs with a grain of salt — this is a fast-moving solo project, so stale data is likely. Gauge confidence with recency: run 'llr' (alias for 'fdfind --type f --hidden --exclude .git -X ls -lt --time=ctime -r --color=auto') and use mtime as a compass correlating mutation-recency with local relevance."
20
+ exit 0