@rkarim08/sia 1.0.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/.claude-plugin/marketplace.json +35 -0
- package/.claude-plugin/plugin.json +27 -0
- package/.mcp.json +13 -0
- package/CLAUDE.md +226 -0
- package/LICENSE +202 -0
- package/PLUGIN_README.md +253 -0
- package/README.md +1013 -0
- package/agents/sia-changelog-writer.md +89 -0
- package/agents/sia-code-reviewer.md +86 -0
- package/agents/sia-conflict-resolver.md +100 -0
- package/agents/sia-convention-enforcer.md +69 -0
- package/agents/sia-debug.md +106 -0
- package/agents/sia-decision-reviewer.md +101 -0
- package/agents/sia-dependency-tracker.md +80 -0
- package/agents/sia-explain.md +126 -0
- package/agents/sia-feature.md +116 -0
- package/agents/sia-knowledge-capture.md +117 -0
- package/agents/sia-lead-architecture-advisor.md +93 -0
- package/agents/sia-lead-team-health.md +107 -0
- package/agents/sia-migration.md +100 -0
- package/agents/sia-onboarding.md +115 -0
- package/agents/sia-orientation.md +99 -0
- package/agents/sia-pm-briefing.md +106 -0
- package/agents/sia-pm-risk-advisor.md +82 -0
- package/agents/sia-qa-analyst.md +116 -0
- package/agents/sia-qa-regression-map.md +94 -0
- package/agents/sia-refactor.md +115 -0
- package/agents/sia-regression.md +112 -0
- package/agents/sia-security-audit.md +125 -0
- package/agents/sia-test-advisor.md +91 -0
- package/hooks/hooks.json +98 -0
- package/migrations/bridge/001_initial.sql +34 -0
- package/migrations/episodic/001_initial.sql +35 -0
- package/migrations/meta/001_initial.sql +68 -0
- package/migrations/semantic/001_initial.sql +292 -0
- package/migrations/semantic/002_ontology.sql +89 -0
- package/migrations/semantic/003_freshness.sql +63 -0
- package/migrations/semantic/004_v5_unified_schema.sql +194 -0
- package/migrations/semantic/005_backfill_event_kinds.sql +8 -0
- package/migrations/semantic/006_tree_sitter.sql +6 -0
- package/migrations/semantic/007_branch_snapshots.sql +22 -0
- package/package.json +110 -0
- package/scripts/branch-switch.sh +13 -0
- package/scripts/build-wasm-grammars.sh +81 -0
- package/scripts/post-compact.sh +8 -0
- package/scripts/post-tool-use.sh +10 -0
- package/scripts/pre-compact.sh +8 -0
- package/scripts/session-end.sh +8 -0
- package/scripts/session-start.sh +8 -0
- package/scripts/start-mcp.ts +45 -0
- package/scripts/stop-hook.sh +8 -0
- package/scripts/user-prompt-submit.sh +8 -0
- package/scripts/viz-server.ts +152 -0
- package/skills/sia-brainstorm/SKILL.md +156 -0
- package/skills/sia-brainstorm/scripts/frame-template.html +214 -0
- package/skills/sia-brainstorm/scripts/helper.js +95 -0
- package/skills/sia-brainstorm/scripts/server.cjs +338 -0
- package/skills/sia-brainstorm/scripts/start-server.sh +153 -0
- package/skills/sia-brainstorm/scripts/stop-server.sh +55 -0
- package/skills/sia-brainstorm/spec-document-reviewer-prompt.md +49 -0
- package/skills/sia-brainstorm/visual-companion.md +286 -0
- package/skills/sia-capture/SKILL.md +64 -0
- package/skills/sia-compare/SKILL.md +33 -0
- package/skills/sia-conflicts/SKILL.md +38 -0
- package/skills/sia-debug-workflow/SKILL.md +120 -0
- package/skills/sia-debug-workflow/root-cause-tracing.md +70 -0
- package/skills/sia-debug-workflow/scripts/find-polluter.sh +64 -0
- package/skills/sia-debug-workflow/temporal-investigation.md +72 -0
- package/skills/sia-digest/SKILL.md +23 -0
- package/skills/sia-dispatch/SKILL.md +69 -0
- package/skills/sia-dispatch/agent-task-template.md +99 -0
- package/skills/sia-doctor/SKILL.md +39 -0
- package/skills/sia-execute/SKILL.md +70 -0
- package/skills/sia-execute-plan/SKILL.md +85 -0
- package/skills/sia-export-import/SKILL.md +49 -0
- package/skills/sia-export-knowledge/SKILL.md +46 -0
- package/skills/sia-finish/SKILL.md +100 -0
- package/skills/sia-finish/pr-summary-template.md +54 -0
- package/skills/sia-freshness/SKILL.md +38 -0
- package/skills/sia-history/SKILL.md +42 -0
- package/skills/sia-impact/SKILL.md +70 -0
- package/skills/sia-index/SKILL.md +54 -0
- package/skills/sia-install/SKILL.md +39 -0
- package/skills/sia-lead-compliance/SKILL.md +16 -0
- package/skills/sia-lead-drift-report/SKILL.md +16 -0
- package/skills/sia-lead-knowledge-map/SKILL.md +16 -0
- package/skills/sia-learn/SKILL.md +58 -0
- package/skills/sia-plan/SKILL.md +68 -0
- package/skills/sia-plan/plan-reviewer-prompt.md +63 -0
- package/skills/sia-playbooks/SKILL.md +29 -0
- package/skills/sia-playbooks/reference-feature.md +100 -0
- package/skills/sia-playbooks/reference-flagging.md +50 -0
- package/skills/sia-playbooks/reference-orientation.md +92 -0
- package/skills/sia-playbooks/reference-regression.md +115 -0
- package/skills/sia-playbooks/reference-review.md +64 -0
- package/skills/sia-playbooks/reference-tools.md +239 -0
- package/skills/sia-pm-decision-log/SKILL.md +28 -0
- package/skills/sia-pm-risk-dashboard/SKILL.md +24 -0
- package/skills/sia-pm-sprint-summary/SKILL.md +27 -0
- package/skills/sia-prune/SKILL.md +45 -0
- package/skills/sia-qa-coverage/SKILL.md +28 -0
- package/skills/sia-qa-flaky/SKILL.md +20 -0
- package/skills/sia-qa-report/SKILL.md +26 -0
- package/skills/sia-reindex/SKILL.md +30 -0
- package/skills/sia-review-respond/SKILL.md +88 -0
- package/skills/sia-review-respond/pushback-patterns.md +90 -0
- package/skills/sia-search/SKILL.md +47 -0
- package/skills/sia-setup/SKILL.md +82 -0
- package/skills/sia-setup/setup-checklist.md +97 -0
- package/skills/sia-stats/SKILL.md +36 -0
- package/skills/sia-status/SKILL.md +44 -0
- package/skills/sia-sync/SKILL.md +46 -0
- package/skills/sia-team/SKILL.md +64 -0
- package/skills/sia-test/SKILL.md +92 -0
- package/skills/sia-test/testing-anti-patterns.md +104 -0
- package/skills/sia-tour/SKILL.md +29 -0
- package/skills/sia-upgrade/SKILL.md +43 -0
- package/skills/sia-verify/SKILL.md +81 -0
- package/skills/sia-visualize/SKILL.md +28 -0
- package/skills/sia-visualize-live/SKILL.md +55 -0
- package/skills/sia-visualize-live/scripts/graph-template.html +389 -0
- package/skills/sia-visualize-live/scripts/start-visualizer.sh +161 -0
- package/skills/sia-visualize-live/scripts/stop-visualizer.sh +55 -0
- package/skills/sia-visualize-live/scripts/visualizer-server.cjs +264 -0
- package/skills/sia-workspace/SKILL.md +57 -0
- package/src/agent/claude-md-template-flagging.md +219 -0
- package/src/agent/claude-md-template.md +213 -0
- package/src/agent/modules/sia-feature.md +100 -0
- package/src/agent/modules/sia-flagging.md +50 -0
- package/src/agent/modules/sia-orientation.md +92 -0
- package/src/agent/modules/sia-regression.md +115 -0
- package/src/agent/modules/sia-review.md +64 -0
- package/src/agent/modules/sia-tools.md +239 -0
- package/src/ast/extractors/c-include.ts +189 -0
- package/src/ast/extractors/csharp-project.ts +260 -0
- package/src/ast/extractors/prisma-schema.ts +44 -0
- package/src/ast/extractors/project-manifest.ts +111 -0
- package/src/ast/extractors/sql-schema.ts +67 -0
- package/src/ast/extractors/tier-a.ts +423 -0
- package/src/ast/extractors/tier-b.ts +289 -0
- package/src/ast/extractors/tier-dispatch.ts +247 -0
- package/src/ast/index-worker.ts +108 -0
- package/src/ast/indexer.ts +484 -0
- package/src/ast/languages.ts +408 -0
- package/src/ast/pagerank-builder.ts +125 -0
- package/src/ast/path-utils.ts +137 -0
- package/src/ast/tree-sitter/backends/native.ts +57 -0
- package/src/ast/tree-sitter/backends/wasm.ts +39 -0
- package/src/ast/tree-sitter/call-walker.ts +44 -0
- package/src/ast/tree-sitter/edit-computer.ts +55 -0
- package/src/ast/tree-sitter/query-runner.ts +46 -0
- package/src/ast/tree-sitter/service.ts +174 -0
- package/src/ast/tree-sitter/tree-cache.ts +39 -0
- package/src/ast/tree-sitter/types.ts +79 -0
- package/src/ast/watcher.ts +322 -0
- package/src/capture/chunker.ts +169 -0
- package/src/capture/consolidate.ts +127 -0
- package/src/capture/edge-inferrer.ts +161 -0
- package/src/capture/embedder.ts +166 -0
- package/src/capture/embedding-cache.ts +73 -0
- package/src/capture/flag-processor.ts +64 -0
- package/src/capture/hook.ts +67 -0
- package/src/capture/pipeline.ts +450 -0
- package/src/capture/prompts/consolidate.ts +25 -0
- package/src/capture/prompts/edge-infer.ts +29 -0
- package/src/capture/prompts/extract-flagged.ts +36 -0
- package/src/capture/prompts/extract.ts +42 -0
- package/src/capture/tokenizer.ts +147 -0
- package/src/capture/track-a-ast.ts +93 -0
- package/src/capture/track-b-llm.ts +149 -0
- package/src/capture/types.ts +64 -0
- package/src/cli/commands/community.ts +137 -0
- package/src/cli/commands/compare.ts +123 -0
- package/src/cli/commands/conflicts.ts +41 -0
- package/src/cli/commands/digest.ts +197 -0
- package/src/cli/commands/disable-flagging.ts +34 -0
- package/src/cli/commands/doctor.ts +240 -0
- package/src/cli/commands/download-model.ts +161 -0
- package/src/cli/commands/enable-flagging.ts +34 -0
- package/src/cli/commands/export-knowledge.ts +208 -0
- package/src/cli/commands/export.ts +85 -0
- package/src/cli/commands/freshness.ts +164 -0
- package/src/cli/commands/graph.ts +51 -0
- package/src/cli/commands/history.ts +139 -0
- package/src/cli/commands/import.ts +335 -0
- package/src/cli/commands/install.ts +156 -0
- package/src/cli/commands/lead-report.ts +241 -0
- package/src/cli/commands/learn.ts +321 -0
- package/src/cli/commands/pm-report.ts +413 -0
- package/src/cli/commands/prune.ts +75 -0
- package/src/cli/commands/qa-report.ts +278 -0
- package/src/cli/commands/reindex.ts +104 -0
- package/src/cli/commands/rollback.ts +70 -0
- package/src/cli/commands/search.ts +103 -0
- package/src/cli/commands/server.ts +91 -0
- package/src/cli/commands/share.ts +33 -0
- package/src/cli/commands/stats.ts +79 -0
- package/src/cli/commands/status.ts +176 -0
- package/src/cli/commands/sync.ts +96 -0
- package/src/cli/commands/team.ts +118 -0
- package/src/cli/commands/tour.ts +157 -0
- package/src/cli/commands/visualize-live.ts +162 -0
- package/src/cli/commands/workspace.ts +117 -0
- package/src/cli/index.ts +424 -0
- package/src/cli/learn-progress.ts +87 -0
- package/src/community/detection-bridge.ts +344 -0
- package/src/community/leiden.ts +462 -0
- package/src/community/raptor.ts +210 -0
- package/src/community/scheduler.ts +74 -0
- package/src/community/summarize.ts +115 -0
- package/src/decay/archiver.ts +73 -0
- package/src/decay/bridge-orphan-cleanup.ts +212 -0
- package/src/decay/consolidation-sweep.ts +112 -0
- package/src/decay/decay.ts +116 -0
- package/src/decay/deep-validator.ts +62 -0
- package/src/decay/episodic-promoter.ts +132 -0
- package/src/decay/maintenance-scheduler.ts +326 -0
- package/src/decay/scheduler.ts +6 -0
- package/src/decay/session-sweeper.ts +79 -0
- package/src/decay/types.ts +17 -0
- package/src/freshness/confidence-decay.ts +122 -0
- package/src/freshness/cuckoo-filter.ts +176 -0
- package/src/freshness/deep-validation.ts +345 -0
- package/src/freshness/dirty-tracker.ts +237 -0
- package/src/freshness/file-watcher-layer.ts +119 -0
- package/src/freshness/firewall.ts +64 -0
- package/src/freshness/git-reconcile-layer.ts +161 -0
- package/src/freshness/inverted-index.ts +158 -0
- package/src/freshness/stale-read-layer.ts +222 -0
- package/src/graph/audit.ts +69 -0
- package/src/graph/bridge-db.ts +141 -0
- package/src/graph/communities.ts +195 -0
- package/src/graph/db-interface.ts +259 -0
- package/src/graph/edges.ts +163 -0
- package/src/graph/entities.ts +327 -0
- package/src/graph/episodic-db.ts +113 -0
- package/src/graph/flags.ts +31 -0
- package/src/graph/meta-db.ts +200 -0
- package/src/graph/semantic-db.ts +101 -0
- package/src/graph/session-resume.ts +56 -0
- package/src/graph/snapshots.ts +342 -0
- package/src/graph/staging.ts +151 -0
- package/src/graph/types.ts +128 -0
- package/src/hooks/adapters/claude-code.ts +21 -0
- package/src/hooks/adapters/cline.ts +43 -0
- package/src/hooks/adapters/cursor.ts +65 -0
- package/src/hooks/adapters/generic.ts +12 -0
- package/src/hooks/agent-detect.ts +34 -0
- package/src/hooks/claude-md-directives.ts +32 -0
- package/src/hooks/event-router.ts +182 -0
- package/src/hooks/extractors/pattern-detector.ts +111 -0
- package/src/hooks/handlers/post-compact.ts +30 -0
- package/src/hooks/handlers/post-tool-use.ts +403 -0
- package/src/hooks/handlers/pre-compact.ts +100 -0
- package/src/hooks/handlers/session-end.ts +47 -0
- package/src/hooks/handlers/session-start.ts +154 -0
- package/src/hooks/handlers/stop.ts +128 -0
- package/src/hooks/handlers/user-prompt-submit.ts +68 -0
- package/src/hooks/plugin-branch-switch.ts +68 -0
- package/src/hooks/plugin-common.ts +47 -0
- package/src/hooks/plugin-post-compact.ts +28 -0
- package/src/hooks/plugin-post-tool-use.ts +38 -0
- package/src/hooks/plugin-pre-compact.ts +37 -0
- package/src/hooks/plugin-session-end.ts +37 -0
- package/src/hooks/plugin-session-start.ts +75 -0
- package/src/hooks/plugin-stop.ts +61 -0
- package/src/hooks/plugin-user-prompt-submit.ts +47 -0
- package/src/hooks/types.ts +43 -0
- package/src/knowledge/discovery.ts +238 -0
- package/src/knowledge/external-refs.ts +98 -0
- package/src/knowledge/freshness.ts +221 -0
- package/src/knowledge/ingest.ts +330 -0
- package/src/knowledge/markdown-export.ts +229 -0
- package/src/knowledge/markdown-import.ts +359 -0
- package/src/knowledge/patterns.ts +74 -0
- package/src/knowledge/templates.ts +307 -0
- package/src/llm/ai-sdk-adapter.ts +46 -0
- package/src/llm/config.ts +88 -0
- package/src/llm/cost-tracker.ts +110 -0
- package/src/llm/prompts/extraction.ts +55 -0
- package/src/llm/prompts/summarization.ts +36 -0
- package/src/llm/prompts/validation.ts +37 -0
- package/src/llm/provider-registry.ts +68 -0
- package/src/llm/reliability.ts +179 -0
- package/src/llm/schemas.ts +52 -0
- package/src/mcp/freshness-annotator.ts +69 -0
- package/src/mcp/server.ts +949 -0
- package/src/mcp/tools/sia-ast-query.ts +225 -0
- package/src/mcp/tools/sia-at-time.ts +151 -0
- package/src/mcp/tools/sia-backlinks.ts +87 -0
- package/src/mcp/tools/sia-batch-execute.ts +169 -0
- package/src/mcp/tools/sia-by-file.ts +89 -0
- package/src/mcp/tools/sia-community.ts +113 -0
- package/src/mcp/tools/sia-doctor.ts +73 -0
- package/src/mcp/tools/sia-execute-file.ts +122 -0
- package/src/mcp/tools/sia-execute.ts +104 -0
- package/src/mcp/tools/sia-expand.ts +158 -0
- package/src/mcp/tools/sia-fetch-and-index.ts +241 -0
- package/src/mcp/tools/sia-flag.ts +65 -0
- package/src/mcp/tools/sia-index.ts +111 -0
- package/src/mcp/tools/sia-note.ts +134 -0
- package/src/mcp/tools/sia-search.ts +105 -0
- package/src/mcp/tools/sia-stats.ts +63 -0
- package/src/mcp/tools/sia-sync-status.ts +44 -0
- package/src/mcp/tools/sia-upgrade.ts +247 -0
- package/src/mcp/truncate.ts +231 -0
- package/src/native/bridge.ts +167 -0
- package/src/native/fallback-ast-diff.ts +144 -0
- package/src/native/fallback-graph.ts +325 -0
- package/src/ontology/constraints.ts +56 -0
- package/src/ontology/errors.ts +8 -0
- package/src/ontology/middleware.ts +266 -0
- package/src/retrieval/bm25-search.ts +151 -0
- package/src/retrieval/context-assembly.ts +76 -0
- package/src/retrieval/graph-traversal.ts +168 -0
- package/src/retrieval/pagerank.ts +40 -0
- package/src/retrieval/query-classifier.ts +106 -0
- package/src/retrieval/reranker.ts +156 -0
- package/src/retrieval/search.ts +236 -0
- package/src/retrieval/throttle.ts +102 -0
- package/src/retrieval/vector-search.ts +203 -0
- package/src/retrieval/workspace-search.ts +130 -0
- package/src/sandbox/context-mode.ts +285 -0
- package/src/sandbox/credential-pass.ts +55 -0
- package/src/sandbox/executor.ts +235 -0
- package/src/security/pattern-detector.ts +127 -0
- package/src/security/rule-of-two.ts +50 -0
- package/src/security/sanitize.ts +46 -0
- package/src/security/semantic-consistency.ts +93 -0
- package/src/security/staging-promoter.ts +154 -0
- package/src/shared/config.ts +302 -0
- package/src/shared/diagnostics.ts +210 -0
- package/src/shared/errors.ts +48 -0
- package/src/shared/git-utils.ts +143 -0
- package/src/shared/llm-client.ts +120 -0
- package/src/shared/logger.ts +99 -0
- package/src/shared/types.ts +79 -0
- package/src/sync/client.ts +43 -0
- package/src/sync/conflict.ts +106 -0
- package/src/sync/dedup.ts +183 -0
- package/src/sync/hlc.ts +117 -0
- package/src/sync/keychain.ts +144 -0
- package/src/sync/pull.ts +232 -0
- package/src/sync/push.ts +131 -0
- package/src/types/chokidar.d.ts +23 -0
- package/src/visualization/graph-renderer.ts +312 -0
- package/src/visualization/subgraph-extract.ts +208 -0
- package/src/visualization/views/community-clusters.ts +246 -0
- package/src/visualization/views/dependency-map.ts +189 -0
- package/src/visualization/views/graph-explorer.ts +364 -0
- package/src/visualization/views/timeline.ts +247 -0
- package/src/workspace/api-contracts.ts +226 -0
- package/src/workspace/cross-repo.ts +61 -0
- package/src/workspace/detector.ts +190 -0
- package/src/workspace/manifest.ts +141 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Module: stop — Stop hook handler
|
|
2
|
+
//
|
|
3
|
+
// Fires when Claude Code is about to exit. Reads the recent segment of
|
|
4
|
+
// the transcript (JSONL), checks whether sia_note was already called,
|
|
5
|
+
// and scans assistant messages for uncaptured knowledge patterns.
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
9
|
+
import { insertEntity } from "@/graph/entities";
|
|
10
|
+
import { detectKnowledgePatterns } from "@/hooks/extractors/pattern-detector";
|
|
11
|
+
import type { HookEvent, HookHandler, HookResponse } from "@/hooks/types";
|
|
12
|
+
|
|
13
|
+
/** How many lines from the tail of the transcript to scan. */
|
|
14
|
+
const RECENT_SEGMENT_SIZE = 50;
|
|
15
|
+
|
|
16
|
+
/** Shape of a single JSONL transcript line. */
|
|
17
|
+
interface TranscriptLine {
|
|
18
|
+
role?: string;
|
|
19
|
+
content?: string;
|
|
20
|
+
tool_calls?: Array<{ name?: string; input?: Record<string, unknown> }>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Read and parse the recent segment of a JSONL transcript file.
|
|
25
|
+
* Returns the last RECENT_SEGMENT_SIZE parsed lines.
|
|
26
|
+
*/
|
|
27
|
+
function readRecentTranscript(path: string): TranscriptLine[] {
|
|
28
|
+
let raw: string;
|
|
29
|
+
try {
|
|
30
|
+
raw = readFileSync(path, "utf-8");
|
|
31
|
+
} catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
36
|
+
const recentLines = allLines.slice(-RECENT_SEGMENT_SIZE);
|
|
37
|
+
|
|
38
|
+
const parsed: TranscriptLine[] = [];
|
|
39
|
+
for (const line of recentLines) {
|
|
40
|
+
try {
|
|
41
|
+
parsed.push(JSON.parse(line) as TranscriptLine);
|
|
42
|
+
} catch {
|
|
43
|
+
// Skip malformed lines
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if sia_note was called in the recent transcript segment.
|
|
52
|
+
*/
|
|
53
|
+
function hasSiaNoteCall(lines: TranscriptLine[]): boolean {
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
if (!line.tool_calls) continue;
|
|
56
|
+
for (const call of line.tool_calls) {
|
|
57
|
+
if (call.name === "sia_note") return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Collect assistant message content from transcript lines.
|
|
65
|
+
*/
|
|
66
|
+
function collectAssistantContent(lines: TranscriptLine[]): string[] {
|
|
67
|
+
const contents: string[] = [];
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
if (line.role === "assistant" && typeof line.content === "string") {
|
|
70
|
+
contents.push(line.content);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return contents;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Factory
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create a Stop hook handler bound to the given graph database.
|
|
82
|
+
* Scans the transcript tail for uncaptured knowledge patterns.
|
|
83
|
+
*/
|
|
84
|
+
export function createStopHandler(db: SiaDb): HookHandler {
|
|
85
|
+
return async (event: HookEvent): Promise<HookResponse> => {
|
|
86
|
+
const lines = readRecentTranscript(event.transcript_path);
|
|
87
|
+
|
|
88
|
+
// If transcript is empty or unreadable, nothing to do
|
|
89
|
+
if (lines.length === 0) {
|
|
90
|
+
return { status: "no_new_knowledge", nodes_created: 0 };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// If sia_note was already called, knowledge was captured inline
|
|
94
|
+
if (hasSiaNoteCall(lines)) {
|
|
95
|
+
return { status: "already_captured", nodes_created: 0 };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Scan assistant messages for knowledge patterns
|
|
99
|
+
const assistantContents = collectAssistantContent(lines);
|
|
100
|
+
if (assistantContents.length === 0) {
|
|
101
|
+
return { status: "no_new_knowledge", nodes_created: 0 };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let nodesCreated = 0;
|
|
105
|
+
|
|
106
|
+
for (const content of assistantContents) {
|
|
107
|
+
const patterns = detectKnowledgePatterns(content);
|
|
108
|
+
for (const p of patterns) {
|
|
109
|
+
await insertEntity(db, {
|
|
110
|
+
type: p.type,
|
|
111
|
+
name: `${p.type}: ${p.content.slice(0, 60)}`,
|
|
112
|
+
content: p.content,
|
|
113
|
+
summary: `${p.type} detected in session ${event.session_id}`,
|
|
114
|
+
confidence: p.confidence,
|
|
115
|
+
extraction_method: "hook:stop:pattern",
|
|
116
|
+
source_episode: event.session_id,
|
|
117
|
+
});
|
|
118
|
+
nodesCreated++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (nodesCreated === 0) {
|
|
123
|
+
return { status: "no_new_knowledge", nodes_created: 0 };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { status: "processed", nodes_created: nodesCreated };
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Module: user-prompt-submit — UserPromptSubmit hook handler
|
|
2
|
+
//
|
|
3
|
+
// When a user submits a prompt:
|
|
4
|
+
// - Always creates a UserPrompt node in the graph.
|
|
5
|
+
// - If the prompt contains correction/preference patterns (e.g. "use X instead
|
|
6
|
+
// of Y", "don't use Z", "always do X"), also creates a UserDecision node
|
|
7
|
+
// with trust_tier 1.
|
|
8
|
+
|
|
9
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
10
|
+
import { insertEntity } from "@/graph/entities";
|
|
11
|
+
import type { SiaConfig } from "@/shared/config";
|
|
12
|
+
|
|
13
|
+
export interface UserPromptEvent {
|
|
14
|
+
session_id: string;
|
|
15
|
+
prompt: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const CORRECTION_PATTERNS = [
|
|
19
|
+
/use\s+\S+\s+instead\s+of/i,
|
|
20
|
+
/don't use/i,
|
|
21
|
+
/do not use/i,
|
|
22
|
+
/switch to/i,
|
|
23
|
+
/prefer\s+\S+/i,
|
|
24
|
+
/always\s+\S+/i,
|
|
25
|
+
/never\s+\S+/i,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export async function handleUserPromptSubmit(
|
|
29
|
+
db: SiaDb,
|
|
30
|
+
event: UserPromptEvent,
|
|
31
|
+
_config: SiaConfig,
|
|
32
|
+
): Promise<{ nodesCreated: number }> {
|
|
33
|
+
if (!event.prompt?.trim()) return { nodesCreated: 0 };
|
|
34
|
+
|
|
35
|
+
let nodesCreated = 0;
|
|
36
|
+
|
|
37
|
+
// Always create a UserPrompt node
|
|
38
|
+
await insertEntity(db, {
|
|
39
|
+
type: "Concept",
|
|
40
|
+
name: event.prompt.slice(0, 50),
|
|
41
|
+
content: event.prompt,
|
|
42
|
+
summary: event.prompt.slice(0, 80),
|
|
43
|
+
tags: JSON.stringify(["user-prompt"]),
|
|
44
|
+
kind: "UserPrompt",
|
|
45
|
+
session_id: event.session_id,
|
|
46
|
+
});
|
|
47
|
+
nodesCreated++;
|
|
48
|
+
|
|
49
|
+
// Check for correction/preference patterns → UserDecision
|
|
50
|
+
for (const pattern of CORRECTION_PATTERNS) {
|
|
51
|
+
if (pattern.test(event.prompt)) {
|
|
52
|
+
await insertEntity(db, {
|
|
53
|
+
type: "Decision",
|
|
54
|
+
name: event.prompt.slice(0, 50),
|
|
55
|
+
content: event.prompt,
|
|
56
|
+
summary: event.prompt.slice(0, 80),
|
|
57
|
+
tags: JSON.stringify(["user-preference"]),
|
|
58
|
+
trust_tier: 1,
|
|
59
|
+
kind: "UserDecision",
|
|
60
|
+
session_id: event.session_id,
|
|
61
|
+
});
|
|
62
|
+
nodesCreated++;
|
|
63
|
+
break; // Only one UserDecision per prompt
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { nodesCreated };
|
|
68
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Plugin hook: Branch switch detection
|
|
3
|
+
//
|
|
4
|
+
// Triggered by PostToolUse on Bash commands matching git checkout/switch.
|
|
5
|
+
// Saves a snapshot of the current graph under the old branch name,
|
|
6
|
+
// then restores (or creates) a snapshot for the new branch.
|
|
7
|
+
|
|
8
|
+
import { resolveRepoHash } from "@/capture/hook";
|
|
9
|
+
import { openGraphDb } from "@/graph/semantic-db";
|
|
10
|
+
import { createBranchSnapshot, restoreBranchSnapshot } from "@/graph/snapshots";
|
|
11
|
+
import { parsePluginHookEvent, readStdin } from "@/hooks/plugin-common";
|
|
12
|
+
import { currentBranch, currentCommit, previousBranch } from "@/shared/git-utils";
|
|
13
|
+
|
|
14
|
+
async function main() {
|
|
15
|
+
try {
|
|
16
|
+
const input = await readStdin();
|
|
17
|
+
if (!input.trim()) return;
|
|
18
|
+
|
|
19
|
+
const event = parsePluginHookEvent(input);
|
|
20
|
+
|
|
21
|
+
if (event.tool_name !== "Bash") return;
|
|
22
|
+
|
|
23
|
+
const command = event.tool_input?.command as string | undefined;
|
|
24
|
+
if (!command) return;
|
|
25
|
+
|
|
26
|
+
const isCheckout = /^git\s+(checkout|switch)\b/.test(command);
|
|
27
|
+
if (!isCheckout) return;
|
|
28
|
+
|
|
29
|
+
const cwd = event.cwd || process.cwd();
|
|
30
|
+
const repoHash = resolveRepoHash(cwd);
|
|
31
|
+
const db = openGraphDb(repoHash);
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const newBranch = currentBranch(cwd);
|
|
35
|
+
const newCommit = currentCommit(cwd);
|
|
36
|
+
|
|
37
|
+
if (!newBranch) return; // detached HEAD — nothing to snapshot
|
|
38
|
+
|
|
39
|
+
// Save current graph state under the OLD branch name before restoring.
|
|
40
|
+
// Note: since this is a PostToolUse hook, checkout has already completed.
|
|
41
|
+
// We cannot recover the old branch's true commit hash — newCommit (the
|
|
42
|
+
// current HEAD) is stored as an approximation. This is acceptable because
|
|
43
|
+
// the snapshot data (nodes/edges) is the authoritative state, not the hash.
|
|
44
|
+
const oldBranch = previousBranch(cwd);
|
|
45
|
+
if (oldBranch && oldBranch !== newBranch) {
|
|
46
|
+
await createBranchSnapshot(db, oldBranch, newCommit);
|
|
47
|
+
process.stderr.write(`sia: saved graph snapshot for branch '${oldBranch}'\n`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Restore snapshot for the new branch, or create an initial one
|
|
51
|
+
const restored = await restoreBranchSnapshot(db, newBranch);
|
|
52
|
+
|
|
53
|
+
if (restored) {
|
|
54
|
+
process.stderr.write(`sia: restored graph snapshot for branch '${newBranch}'\n`);
|
|
55
|
+
} else {
|
|
56
|
+
await createBranchSnapshot(db, newBranch, newCommit);
|
|
57
|
+
process.stderr.write(`sia: created initial graph snapshot for branch '${newBranch}'\n`);
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
await db.close();
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
process.stderr.write(`sia branch-switch hook error: ${err}\n`);
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
main();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Module: plugin-common — Shared utilities for plugin hook wrappers
|
|
2
|
+
//
|
|
3
|
+
// Claude Code hook events arrive as JSON on stdin. This module parses
|
|
4
|
+
// them into SIA's HookEvent type.
|
|
5
|
+
|
|
6
|
+
import type { HookEvent } from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parse a Claude Code hook event from a JSON string.
|
|
10
|
+
* Validates required fields and returns a typed HookEvent.
|
|
11
|
+
*/
|
|
12
|
+
export function parsePluginHookEvent(input: string): HookEvent {
|
|
13
|
+
let parsed: Record<string, unknown>;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(input);
|
|
16
|
+
} catch {
|
|
17
|
+
throw new Error("Invalid JSON in hook event");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!parsed.session_id || typeof parsed.session_id !== "string") {
|
|
21
|
+
throw new Error("Missing required field: session_id");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
session_id: parsed.session_id as string,
|
|
26
|
+
transcript_path: (parsed.transcript_path as string) ?? "",
|
|
27
|
+
cwd: (parsed.cwd as string) ?? process.cwd(),
|
|
28
|
+
hook_event_name: (parsed.hook_event_name as string) ?? "unknown",
|
|
29
|
+
tool_name: parsed.tool_name as string | undefined,
|
|
30
|
+
tool_input: parsed.tool_input as Record<string, unknown> | undefined,
|
|
31
|
+
tool_response: parsed.tool_response,
|
|
32
|
+
tool_use_id: parsed.tool_use_id as string | undefined,
|
|
33
|
+
source: parsed.source as HookEvent["source"],
|
|
34
|
+
reason: parsed.reason as HookEvent["reason"],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Read all of stdin as a string.
|
|
40
|
+
*/
|
|
41
|
+
export async function readStdin(): Promise<string> {
|
|
42
|
+
const chunks: Buffer[] = [];
|
|
43
|
+
for await (const chunk of process.stdin) {
|
|
44
|
+
chunks.push(chunk as Buffer);
|
|
45
|
+
}
|
|
46
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
47
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// Plugin hook wrapper: PostCompact
|
|
4
|
+
//
|
|
5
|
+
// Reads Claude Code PostCompact event from stdin. Lightweight handler
|
|
6
|
+
// that logs compaction coverage info for observability.
|
|
7
|
+
|
|
8
|
+
import { createPostCompactHandler } from "@/hooks/handlers/post-compact";
|
|
9
|
+
import { parsePluginHookEvent, readStdin } from "@/hooks/plugin-common";
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
try {
|
|
13
|
+
const input = await readStdin();
|
|
14
|
+
if (!input.trim()) {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const event = parsePluginHookEvent(input);
|
|
19
|
+
const handler = createPostCompactHandler();
|
|
20
|
+
const result = await handler(event);
|
|
21
|
+
process.stdout.write(JSON.stringify(result));
|
|
22
|
+
} catch (err) {
|
|
23
|
+
process.stderr.write(`sia PostCompact hook error: ${err}\n`);
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
main();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Plugin hook wrapper: PostToolUse
|
|
3
|
+
//
|
|
4
|
+
// Reads Claude Code hook event from stdin, delegates to the
|
|
5
|
+
// existing PostToolUse handler, writes response to stdout.
|
|
6
|
+
|
|
7
|
+
import { resolveRepoHash } from "@/capture/hook";
|
|
8
|
+
import { openGraphDb } from "@/graph/semantic-db";
|
|
9
|
+
import { createPostToolUseHandler } from "@/hooks/handlers/post-tool-use";
|
|
10
|
+
import { parsePluginHookEvent, readStdin } from "@/hooks/plugin-common";
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
try {
|
|
14
|
+
const input = await readStdin();
|
|
15
|
+
if (!input.trim()) {
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const event = parsePluginHookEvent(input);
|
|
20
|
+
const cwd = event.cwd || process.cwd();
|
|
21
|
+
const repoHash = resolveRepoHash(cwd);
|
|
22
|
+
const db = openGraphDb(repoHash);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const handler = createPostToolUseHandler(db);
|
|
26
|
+
const result = await handler(event);
|
|
27
|
+
process.stdout.write(JSON.stringify(result));
|
|
28
|
+
} finally {
|
|
29
|
+
await db.close();
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
// Hooks must not crash Claude Code — fail silently
|
|
33
|
+
process.stderr.write(`sia PostToolUse hook error: ${err}\n`);
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
main();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Plugin hook wrapper: PreCompact
|
|
3
|
+
//
|
|
4
|
+
// Reads Claude Code PreCompact event from stdin, scans transcript
|
|
5
|
+
// tail for unextracted knowledge before compaction discards detail.
|
|
6
|
+
|
|
7
|
+
import { resolveRepoHash } from "@/capture/hook";
|
|
8
|
+
import { openGraphDb } from "@/graph/semantic-db";
|
|
9
|
+
import { createPreCompactHandler } from "@/hooks/handlers/pre-compact";
|
|
10
|
+
import { parsePluginHookEvent, readStdin } from "@/hooks/plugin-common";
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
try {
|
|
14
|
+
const input = await readStdin();
|
|
15
|
+
if (!input.trim()) {
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const event = parsePluginHookEvent(input);
|
|
20
|
+
const cwd = event.cwd || process.cwd();
|
|
21
|
+
const repoHash = resolveRepoHash(cwd);
|
|
22
|
+
const db = openGraphDb(repoHash);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const handler = createPreCompactHandler(db);
|
|
26
|
+
const result = await handler(event);
|
|
27
|
+
process.stdout.write(JSON.stringify(result));
|
|
28
|
+
} finally {
|
|
29
|
+
await db.close();
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
process.stderr.write(`sia PreCompact hook error: ${err}\n`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
main();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Plugin hook wrapper: SessionEnd
|
|
3
|
+
//
|
|
4
|
+
// Reads Claude Code SessionEnd event from stdin, records session
|
|
5
|
+
// statistics and updates ended_at timestamp in the graph.
|
|
6
|
+
|
|
7
|
+
import { resolveRepoHash } from "@/capture/hook";
|
|
8
|
+
import { openGraphDb } from "@/graph/semantic-db";
|
|
9
|
+
import { createSessionEndHandler } from "@/hooks/handlers/session-end";
|
|
10
|
+
import { parsePluginHookEvent, readStdin } from "@/hooks/plugin-common";
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
try {
|
|
14
|
+
const input = await readStdin();
|
|
15
|
+
if (!input.trim()) {
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const event = parsePluginHookEvent(input);
|
|
20
|
+
const cwd = event.cwd || process.cwd();
|
|
21
|
+
const repoHash = resolveRepoHash(cwd);
|
|
22
|
+
const db = openGraphDb(repoHash);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const handler = createSessionEndHandler(db);
|
|
26
|
+
const result = await handler(event);
|
|
27
|
+
process.stdout.write(JSON.stringify(result));
|
|
28
|
+
} finally {
|
|
29
|
+
await db.close();
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
process.stderr.write(`sia SessionEnd hook error: ${err}\n`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
main();
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Plugin hook wrapper: SessionStart
|
|
3
|
+
//
|
|
4
|
+
// Injects recent decisions, conventions, and known bugs as context
|
|
5
|
+
// at the beginning of a Claude Code session.
|
|
6
|
+
|
|
7
|
+
import { resolveRepoHash } from "@/capture/hook";
|
|
8
|
+
import { openGraphDb } from "@/graph/semantic-db";
|
|
9
|
+
import { buildSessionContext, formatSessionContext } from "@/hooks/handlers/session-start";
|
|
10
|
+
import { parsePluginHookEvent, readStdin } from "@/hooks/plugin-common";
|
|
11
|
+
import type { HookEvent } from "@/hooks/types";
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
try {
|
|
15
|
+
const input = await readStdin();
|
|
16
|
+
// SessionStart may be invoked without event data on initial install
|
|
17
|
+
let event: HookEvent;
|
|
18
|
+
if (input.trim()) {
|
|
19
|
+
event = parsePluginHookEvent(input);
|
|
20
|
+
} else {
|
|
21
|
+
event = {
|
|
22
|
+
session_id: "unknown",
|
|
23
|
+
cwd: process.cwd(),
|
|
24
|
+
transcript_path: "",
|
|
25
|
+
hook_event_name: "SessionStart",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const cwd = event.cwd || process.cwd();
|
|
30
|
+
const repoHash = resolveRepoHash(cwd);
|
|
31
|
+
const db = openGraphDb(repoHash);
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const isResume = event.source === "resume";
|
|
35
|
+
const context = await buildSessionContext(db, cwd, isResume);
|
|
36
|
+
let formatted = formatSessionContext(context);
|
|
37
|
+
|
|
38
|
+
// Load previous session subgraph if resuming
|
|
39
|
+
if (isResume && event.session_id && event.session_id !== "unknown") {
|
|
40
|
+
try {
|
|
41
|
+
const { loadSubgraph } = await import("@/graph/session-resume");
|
|
42
|
+
const resume = await loadSubgraph(db, event.session_id);
|
|
43
|
+
if (resume) {
|
|
44
|
+
const subgraph = JSON.parse(resume.subgraph_json);
|
|
45
|
+
const entities = subgraph.entities as Array<{
|
|
46
|
+
name: string;
|
|
47
|
+
summary: string;
|
|
48
|
+
type: string;
|
|
49
|
+
}>;
|
|
50
|
+
if (entities.length > 0) {
|
|
51
|
+
formatted += "\n## Previous Session Context\n";
|
|
52
|
+
formatted += "These entities were active in your previous session:\n\n";
|
|
53
|
+
for (const entity of entities.slice(0, 10)) {
|
|
54
|
+
formatted += `- **${entity.name}** (${entity.type}): ${entity.summary || "no summary"}\n`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
process.stderr.write(`sia: session resume load failed (non-fatal): ${err}\n`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// SessionStart hooks output to stdout — Claude Code injects
|
|
64
|
+
// the output as context into the conversation.
|
|
65
|
+
process.stdout.write(formatted);
|
|
66
|
+
} finally {
|
|
67
|
+
await db.close();
|
|
68
|
+
}
|
|
69
|
+
} catch (err) {
|
|
70
|
+
process.stderr.write(`sia SessionStart hook error: ${err}\n`);
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
main();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Plugin hook wrapper: Stop
|
|
3
|
+
//
|
|
4
|
+
// Reads Claude Code Stop event from stdin, runs pattern detection
|
|
5
|
+
// on recent transcript, captures uncaptured knowledge.
|
|
6
|
+
|
|
7
|
+
import { resolveRepoHash } from "@/capture/hook";
|
|
8
|
+
import { openGraphDb } from "@/graph/semantic-db";
|
|
9
|
+
import { createStopHandler } from "@/hooks/handlers/stop";
|
|
10
|
+
import { parsePluginHookEvent, readStdin } from "@/hooks/plugin-common";
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
try {
|
|
14
|
+
const input = await readStdin();
|
|
15
|
+
if (!input.trim()) {
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const event = parsePluginHookEvent(input);
|
|
20
|
+
const cwd = event.cwd || process.cwd();
|
|
21
|
+
const repoHash = resolveRepoHash(cwd);
|
|
22
|
+
const db = openGraphDb(repoHash);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const handler = createStopHandler(db);
|
|
26
|
+
const result = await handler(event);
|
|
27
|
+
process.stdout.write(JSON.stringify(result));
|
|
28
|
+
|
|
29
|
+
// Save session subgraph for resume
|
|
30
|
+
try {
|
|
31
|
+
const { saveSubgraph } = await import("@/graph/session-resume");
|
|
32
|
+
|
|
33
|
+
const recentNodes = await db.execute(
|
|
34
|
+
`SELECT id, type, name, summary, kind, trust_tier, file_paths
|
|
35
|
+
FROM graph_nodes
|
|
36
|
+
WHERE session_id = ? OR last_accessed > ?
|
|
37
|
+
ORDER BY last_accessed DESC
|
|
38
|
+
LIMIT 20`,
|
|
39
|
+
[event.session_id, Date.now() - 3600000],
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const subgraph = {
|
|
43
|
+
entities: recentNodes.rows,
|
|
44
|
+
timestamp: Date.now(),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
await saveSubgraph(db, event.session_id, JSON.stringify(subgraph), null, 0);
|
|
48
|
+
process.stderr.write("sia: saved session subgraph for resume\n");
|
|
49
|
+
} catch (err) {
|
|
50
|
+
process.stderr.write(`sia: session save failed (non-fatal): ${err}\n`);
|
|
51
|
+
}
|
|
52
|
+
} finally {
|
|
53
|
+
await db.close();
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {
|
|
56
|
+
process.stderr.write(`sia Stop hook error: ${err}\n`);
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
main();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Plugin hook wrapper: UserPromptSubmit
|
|
3
|
+
//
|
|
4
|
+
// Reads Claude Code UserPromptSubmit event from stdin, creates a
|
|
5
|
+
// UserPrompt node and optionally a UserDecision if the prompt
|
|
6
|
+
// contains correction/preference patterns.
|
|
7
|
+
|
|
8
|
+
import { resolveRepoHash } from "@/capture/hook";
|
|
9
|
+
import { openGraphDb } from "@/graph/semantic-db";
|
|
10
|
+
import { handleUserPromptSubmit } from "@/hooks/handlers/user-prompt-submit";
|
|
11
|
+
import { parsePluginHookEvent, readStdin } from "@/hooks/plugin-common";
|
|
12
|
+
import { getConfig } from "@/shared/config";
|
|
13
|
+
|
|
14
|
+
async function main() {
|
|
15
|
+
try {
|
|
16
|
+
const input = await readStdin();
|
|
17
|
+
if (!input.trim()) {
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const event = parsePluginHookEvent(input);
|
|
22
|
+
const cwd = event.cwd || process.cwd();
|
|
23
|
+
const repoHash = resolveRepoHash(cwd);
|
|
24
|
+
const db = openGraphDb(repoHash);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const config = getConfig();
|
|
28
|
+
const prompt = (event.tool_input?.prompt as string) ?? "";
|
|
29
|
+
const result = await handleUserPromptSubmit(
|
|
30
|
+
db,
|
|
31
|
+
{
|
|
32
|
+
session_id: event.session_id,
|
|
33
|
+
prompt,
|
|
34
|
+
},
|
|
35
|
+
config,
|
|
36
|
+
);
|
|
37
|
+
process.stdout.write(JSON.stringify(result));
|
|
38
|
+
} finally {
|
|
39
|
+
await db.close();
|
|
40
|
+
}
|
|
41
|
+
} catch (err) {
|
|
42
|
+
process.stderr.write(`sia UserPromptSubmit hook error: ${err}\n`);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
main();
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/hooks/types.ts — Shared types for the hook system
|
|
2
|
+
|
|
3
|
+
/** JSON envelope received from Claude Code hook events */
|
|
4
|
+
export interface HookEvent {
|
|
5
|
+
session_id: string;
|
|
6
|
+
transcript_path: string;
|
|
7
|
+
cwd: string;
|
|
8
|
+
hook_event_name: string;
|
|
9
|
+
permission_mode?: string;
|
|
10
|
+
|
|
11
|
+
// Tool-specific (PostToolUse, PreToolUse)
|
|
12
|
+
tool_name?: string;
|
|
13
|
+
tool_input?: Record<string, unknown>;
|
|
14
|
+
tool_response?: unknown;
|
|
15
|
+
tool_use_id?: string;
|
|
16
|
+
|
|
17
|
+
// Compaction-specific
|
|
18
|
+
trigger?: "auto" | "manual";
|
|
19
|
+
compact_summary?: string;
|
|
20
|
+
custom_instructions?: string;
|
|
21
|
+
|
|
22
|
+
// Session-specific
|
|
23
|
+
source?: "startup" | "resume" | "clear";
|
|
24
|
+
reason?: "exit" | "sigint" | "error";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Standard response from a hook handler */
|
|
28
|
+
export interface HookResponse {
|
|
29
|
+
status:
|
|
30
|
+
| "processed"
|
|
31
|
+
| "skipped"
|
|
32
|
+
| "error"
|
|
33
|
+
| "already_captured"
|
|
34
|
+
| "needs_semantic_analysis"
|
|
35
|
+
| "no_new_knowledge";
|
|
36
|
+
nodes_created?: number;
|
|
37
|
+
edges_created?: number;
|
|
38
|
+
error?: string;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Handler function signature */
|
|
43
|
+
export type HookHandler = (event: HookEvent) => Promise<HookResponse>;
|