all-hands-cli 0.1.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/.allhands/README.md +75 -0
- package/.allhands/agents/compounder.yaml +15 -0
- package/.allhands/agents/coordinator.yaml +17 -0
- package/.allhands/agents/documentor.yaml +15 -0
- package/.allhands/agents/e2e-test-planner.yaml +17 -0
- package/.allhands/agents/emergent.yaml +22 -0
- package/.allhands/agents/executor.yaml +14 -0
- package/.allhands/agents/ideation.yaml +11 -0
- package/.allhands/agents/initiative-steering.yaml +19 -0
- package/.allhands/agents/judge.yaml +13 -0
- package/.allhands/agents/planner.yaml +19 -0
- package/.allhands/agents/pr-reviewer.yaml +15 -0
- package/.allhands/docs.json +5 -0
- package/.allhands/docs.local.json +26 -0
- package/.allhands/flows/COMPOUNDING.md +203 -0
- package/.allhands/flows/COORDINATION.md +89 -0
- package/.allhands/flows/CORE.md +87 -0
- package/.allhands/flows/DOCUMENTATION.md +218 -0
- package/.allhands/flows/E2E_TEST_PLAN_BUILDING.md +140 -0
- package/.allhands/flows/EMERGENT_PLANNING.md +57 -0
- package/.allhands/flows/IDEATION_SCOPING.md +154 -0
- package/.allhands/flows/INITIATIVE_STEERING.md +110 -0
- package/.allhands/flows/JUDGE_REVIEWING.md +79 -0
- package/.allhands/flows/PROMPT_TASK_EXECUTION.md +68 -0
- package/.allhands/flows/PR_REVIEWING.md +43 -0
- package/.allhands/flows/SPEC_PLANNING.md +216 -0
- package/.allhands/flows/harness/WRITING_HARNESS_FLOWS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_KNOWLEDGE.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_ORCHESTRATION.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_SKILLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_TOOLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_VALIDATION_TOOLING.md +27 -0
- package/.allhands/flows/shared/CODEBASE_UNDERSTANDING.md +72 -0
- package/.allhands/flows/shared/CREATE_HARNESS_SPEC.md +48 -0
- package/.allhands/flows/shared/CREATE_SPEC.md +41 -0
- package/.allhands/flows/shared/CREATE_VALIDATION_TOOLING_SPEC.md +70 -0
- package/.allhands/flows/shared/DOCUMENTATION_DISCOVERY.md +123 -0
- package/.allhands/flows/shared/DOCUMENTATION_WRITER.md +101 -0
- package/.allhands/flows/shared/EMERGENT_REFINEMENT_ANALYSIS.md +76 -0
- package/.allhands/flows/shared/EXTERNAL_TECH_GUIDANCE.md +97 -0
- package/.allhands/flows/shared/IDEATION_CODEBASE_GROUNDING.md +49 -0
- package/.allhands/flows/shared/PLAN_DEEPENING.md +152 -0
- package/.allhands/flows/shared/PROMPT_TASKS_CURATION.md +113 -0
- package/.allhands/flows/shared/PROMPT_VALIDATION_REVIEW.MD +99 -0
- package/.allhands/flows/shared/QUICK_PREMORTEM.md +70 -0
- package/.allhands/flows/shared/RESEARCH_GUIDANCE.md +38 -0
- package/.allhands/flows/shared/REVIEW_OPTIONS_BREAKDOWN.md +68 -0
- package/.allhands/flows/shared/SKILL_EXTRACTION.md +84 -0
- package/.allhands/flows/shared/SPEC_FLOW_ANALYSIS.md +119 -0
- package/.allhands/flows/shared/TDD_WORKFLOW.md +109 -0
- package/.allhands/flows/shared/UTILIZE_VALIDATION_TOOLING.md +84 -0
- package/.allhands/flows/shared/WRITING_HARNESS_FLOWS.md +11 -0
- package/.allhands/flows/shared/WRITING_HARNESS_MCP_TOOLS.md +84 -0
- package/.allhands/flows/shared/jury/ARCHITECTURE_REVIEW.md +91 -0
- package/.allhands/flows/shared/jury/BEST_PRACTICES_REVIEW.md +80 -0
- package/.allhands/flows/shared/jury/CLAIM_VERIFICATION_REVIEW.md +101 -0
- package/.allhands/flows/shared/jury/EXPECTATIONS_FIT_REVIEW.md +78 -0
- package/.allhands/flows/shared/jury/MAINTAINABILITY_REVIEW.md +110 -0
- package/.allhands/flows/shared/jury/PROMPTS_EXPECTATIONS_FIT.md +74 -0
- package/.allhands/flows/shared/jury/PROMPTS_FLOW_ANALYSIS.md +92 -0
- package/.allhands/flows/shared/jury/PROMPTS_YAGNI.md +78 -0
- package/.allhands/flows/shared/jury/PROMPT_PREMORTEM.md +125 -0
- package/.allhands/flows/shared/jury/SECURITY_REVIEW.md +86 -0
- package/.allhands/flows/shared/jury/YAGNI_REVIEW.md +82 -0
- package/.allhands/flows/wip/DEBUG_INVESTIGATION.md +162 -0
- package/.allhands/flows/wip/MEMORY_RECALL.md +62 -0
- package/.allhands/harness/ah +131 -0
- package/.allhands/harness/package-lock.json +5292 -0
- package/.allhands/harness/package.json +52 -0
- package/.allhands/harness/src/__tests__/e2e/commands.test.ts +307 -0
- package/.allhands/harness/src/__tests__/e2e/event-loop.test.ts +539 -0
- package/.allhands/harness/src/__tests__/e2e/hooks.test.ts +427 -0
- package/.allhands/harness/src/__tests__/e2e/new-initiative-routing.test.ts +137 -0
- package/.allhands/harness/src/__tests__/e2e/run-e2e.ts +109 -0
- package/.allhands/harness/src/__tests__/e2e/specs-type.test.ts +210 -0
- package/.allhands/harness/src/__tests__/e2e/validation-hooks.test.ts +669 -0
- package/.allhands/harness/src/__tests__/e2e/validation-path-consistency.test.ts +354 -0
- package/.allhands/harness/src/__tests__/e2e/validation.test.ts +528 -0
- package/.allhands/harness/src/__tests__/harness/assertions.ts +318 -0
- package/.allhands/harness/src/__tests__/harness/cli-runner.ts +359 -0
- package/.allhands/harness/src/__tests__/harness/fixture.ts +384 -0
- package/.allhands/harness/src/__tests__/harness/hook-runner.ts +411 -0
- package/.allhands/harness/src/__tests__/harness/index.ts +122 -0
- package/.allhands/harness/src/cli.ts +36 -0
- package/.allhands/harness/src/commands/complexity.ts +177 -0
- package/.allhands/harness/src/commands/context7.ts +202 -0
- package/.allhands/harness/src/commands/docs.ts +557 -0
- package/.allhands/harness/src/commands/hooks.ts +24 -0
- package/.allhands/harness/src/commands/index.ts +51 -0
- package/.allhands/harness/src/commands/knowledge.ts +382 -0
- package/.allhands/harness/src/commands/memories.ts +302 -0
- package/.allhands/harness/src/commands/notify.ts +61 -0
- package/.allhands/harness/src/commands/oracle.ts +158 -0
- package/.allhands/harness/src/commands/perplexity.ts +220 -0
- package/.allhands/harness/src/commands/planning.ts +245 -0
- package/.allhands/harness/src/commands/schema.ts +73 -0
- package/.allhands/harness/src/commands/skills.ts +128 -0
- package/.allhands/harness/src/commands/solutions.ts +353 -0
- package/.allhands/harness/src/commands/spawn.ts +158 -0
- package/.allhands/harness/src/commands/specs.ts +532 -0
- package/.allhands/harness/src/commands/tavily.ts +226 -0
- package/.allhands/harness/src/commands/tools.ts +579 -0
- package/.allhands/harness/src/commands/trace.ts +327 -0
- package/.allhands/harness/src/commands/tui.ts +960 -0
- package/.allhands/harness/src/commands/validate.ts +143 -0
- package/.allhands/harness/src/commands/validation-tools.ts +108 -0
- package/.allhands/harness/src/hooks/context.ts +1442 -0
- package/.allhands/harness/src/hooks/enforcement.ts +170 -0
- package/.allhands/harness/src/hooks/index.ts +54 -0
- package/.allhands/harness/src/hooks/lifecycle.ts +229 -0
- package/.allhands/harness/src/hooks/notification.ts +104 -0
- package/.allhands/harness/src/hooks/observability.ts +551 -0
- package/.allhands/harness/src/hooks/session.ts +88 -0
- package/.allhands/harness/src/hooks/shared.ts +815 -0
- package/.allhands/harness/src/hooks/transcript-parser.ts +208 -0
- package/.allhands/harness/src/hooks/validation.ts +617 -0
- package/.allhands/harness/src/lib/__tests__/ctags.test.ts +244 -0
- package/.allhands/harness/src/lib/__tests__/docs-validation.test.ts +344 -0
- package/.allhands/harness/src/lib/__tests__/mcp-runtime.test.ts +190 -0
- package/.allhands/harness/src/lib/__tests__/schema.test.ts +861 -0
- package/.allhands/harness/src/lib/base-command.ts +198 -0
- package/.allhands/harness/src/lib/cli-daemon.ts +343 -0
- package/.allhands/harness/src/lib/compaction.ts +313 -0
- package/.allhands/harness/src/lib/ctags.ts +497 -0
- package/.allhands/harness/src/lib/docs-validation.ts +907 -0
- package/.allhands/harness/src/lib/event-loop.ts +662 -0
- package/.allhands/harness/src/lib/flows.ts +155 -0
- package/.allhands/harness/src/lib/git.ts +276 -0
- package/.allhands/harness/src/lib/knowledge-worker.ts +72 -0
- package/.allhands/harness/src/lib/knowledge.ts +810 -0
- package/.allhands/harness/src/lib/llm.ts +255 -0
- package/.allhands/harness/src/lib/mcp-client.ts +432 -0
- package/.allhands/harness/src/lib/mcp-daemon.ts +486 -0
- package/.allhands/harness/src/lib/mcp-runtime.ts +418 -0
- package/.allhands/harness/src/lib/notification.ts +115 -0
- package/.allhands/harness/src/lib/opencode/index.ts +70 -0
- package/.allhands/harness/src/lib/opencode/profiles.ts +300 -0
- package/.allhands/harness/src/lib/opencode/prompts/codesearch.md +98 -0
- package/.allhands/harness/src/lib/opencode/prompts/knowledge-aggregator.md +67 -0
- package/.allhands/harness/src/lib/opencode/runner.ts +281 -0
- package/.allhands/harness/src/lib/oracle.ts +926 -0
- package/.allhands/harness/src/lib/planning-utils.ts +150 -0
- package/.allhands/harness/src/lib/planning.ts +605 -0
- package/.allhands/harness/src/lib/pr-review.ts +225 -0
- package/.allhands/harness/src/lib/prompts.ts +522 -0
- package/.allhands/harness/src/lib/schema.ts +418 -0
- package/.allhands/harness/src/lib/schemas/agent-profile.ts +141 -0
- package/.allhands/harness/src/lib/schemas/template-vars.ts +138 -0
- package/.allhands/harness/src/lib/session.ts +164 -0
- package/.allhands/harness/src/lib/specs.ts +348 -0
- package/.allhands/harness/src/lib/tldr.ts +829 -0
- package/.allhands/harness/src/lib/tmux.ts +1051 -0
- package/.allhands/harness/src/lib/trace-store.ts +714 -0
- package/.allhands/harness/src/mcp/__tests__/index.test.ts +46 -0
- package/.allhands/harness/src/mcp/_template.ts +47 -0
- package/.allhands/harness/src/mcp/filesystem.ts +33 -0
- package/.allhands/harness/src/mcp/index.ts +69 -0
- package/.allhands/harness/src/mcp/playwright.ts +34 -0
- package/.allhands/harness/src/mcp/xcodebuild.ts +29 -0
- package/.allhands/harness/src/schemas/docs.schema.json +44 -0
- package/.allhands/harness/src/schemas/settings.schema.json +214 -0
- package/.allhands/harness/src/tui/actions.ts +227 -0
- package/.allhands/harness/src/tui/file-viewer-modal.ts +270 -0
- package/.allhands/harness/src/tui/index.ts +1574 -0
- package/.allhands/harness/src/tui/modal.ts +232 -0
- package/.allhands/harness/src/tui/prompts-pane.ts +186 -0
- package/.allhands/harness/src/tui/status-pane.ts +434 -0
- package/.allhands/harness/tsconfig.json +22 -0
- package/.allhands/harness/vitest.config.ts +13 -0
- package/.allhands/pillars.md +33 -0
- package/.allhands/principles.md +88 -0
- package/.allhands/schemas/alignment.yaml +51 -0
- package/.allhands/schemas/documentation.yaml +10 -0
- package/.allhands/schemas/prompt.yaml +92 -0
- package/.allhands/schemas/skill.yaml +34 -0
- package/.allhands/schemas/solution.yaml +131 -0
- package/.allhands/schemas/spec.yaml +67 -0
- package/.allhands/schemas/validation-suite.yaml +49 -0
- package/.allhands/schemas/workflow.yaml +51 -0
- package/.allhands/settings.json +57 -0
- package/.allhands/skills/claude-code-patterns/SKILL.md +60 -0
- package/.allhands/skills/claude-code-patterns/docs/context-hygiene.md +19 -0
- package/.allhands/skills/harness-maintenance/SKILL.md +449 -0
- package/.allhands/skills/harness-maintenance/references/core-architecture.md +187 -0
- package/.allhands/skills/harness-maintenance/references/harness-skills.md +87 -0
- package/.allhands/skills/harness-maintenance/references/knowledge-compounding.md +78 -0
- package/.allhands/skills/harness-maintenance/references/tools-commands-mcp-hooks.md +115 -0
- package/.allhands/skills/harness-maintenance/references/validation-tooling.md +77 -0
- package/.allhands/skills/harness-maintenance/references/writing-flows.md +84 -0
- package/.allhands/validation/browser-automation.md +109 -0
- package/.allhands/validation/xcode-automation.md +195 -0
- package/.allhands/workflows/documentation.md +86 -0
- package/.allhands/workflows/investigation.md +81 -0
- package/.allhands/workflows/milestone.md +91 -0
- package/.allhands/workflows/optimization.md +85 -0
- package/.allhands/workflows/refactor.md +99 -0
- package/.allhands/workflows/triage.md +81 -0
- package/.claude/README.md +1 -0
- package/.claude/agents/explorer.md +10 -0
- package/.claude/agents/researcher.md +11 -0
- package/.claude/agents/task-runner.md +8 -0
- package/.claude/settings.json +231 -0
- package/.env.ai.example +7 -0
- package/.github/workflows/npm-publish.yml +69 -0
- package/.internal.json +45 -0
- package/.tldr/config.json +11 -0
- package/.tldrignore +90 -0
- package/CLAUDE.md +6 -0
- package/README.md +98 -0
- package/bin/sync-cli.js +7552 -0
- package/concerns.md +7 -0
- package/docs/README.md +41 -0
- package/docs/agents/README.md +24 -0
- package/docs/agents/agent-configuration-system.md +86 -0
- package/docs/agents/execution-agents.md +50 -0
- package/docs/agents/knowledge-agents.md +61 -0
- package/docs/agents/orchestration-agent.md +57 -0
- package/docs/agents/planning-agents.md +84 -0
- package/docs/agents/quality-review-agents.md +67 -0
- package/docs/agents/workflow-agent-orchestration.md +69 -0
- package/docs/flows/README.md +44 -0
- package/docs/flows/compounding.md +126 -0
- package/docs/flows/coordination.md +72 -0
- package/docs/flows/core-harness-integration.md +63 -0
- package/docs/flows/documentation-orchestration.md +98 -0
- package/docs/flows/e2e-test-plan-building.md +83 -0
- package/docs/flows/emergent-refinement.md +104 -0
- package/docs/flows/flow-authoring-and-mcp-tools.md +89 -0
- package/docs/flows/judge-reviewing.md +112 -0
- package/docs/flows/plan-deepening-and-research.md +107 -0
- package/docs/flows/plan-review-jury.md +114 -0
- package/docs/flows/pr-reviewing.md +54 -0
- package/docs/flows/prompt-task-execution.md +119 -0
- package/docs/flows/spec-planning.md +162 -0
- package/docs/flows/type-specific-scoping-flows.md +49 -0
- package/docs/flows/validation-and-skills-integration.md +145 -0
- package/docs/flows/wip/wip-flows.md +102 -0
- package/docs/harness/README.md +23 -0
- package/docs/harness/agent-profiles.md +84 -0
- package/docs/harness/cli/README.md +24 -0
- package/docs/harness/cli/cli-entry-and-command-discovery.md +91 -0
- package/docs/harness/cli/docs-command.md +87 -0
- package/docs/harness/cli/knowledge-command.md +91 -0
- package/docs/harness/cli/minor-cli-commands.md +65 -0
- package/docs/harness/cli/oracle-command.md +113 -0
- package/docs/harness/cli/planning-command.md +95 -0
- package/docs/harness/cli/schema-and-validation-commands.md +154 -0
- package/docs/harness/cli/search-commands.md +97 -0
- package/docs/harness/cli/spawn-command.md +136 -0
- package/docs/harness/cli/specs-command.md +102 -0
- package/docs/harness/cli/tools-command.md +122 -0
- package/docs/harness/cli/trace-command.md +122 -0
- package/docs/harness/cli-daemon.md +92 -0
- package/docs/harness/event-loop.md +184 -0
- package/docs/harness/hooks/README.md +15 -0
- package/docs/harness/hooks/context-hooks.md +96 -0
- package/docs/harness/hooks/lifecycle-and-observability-hooks.md +135 -0
- package/docs/harness/hooks/validation-hooks.md +97 -0
- package/docs/harness/test-harness.md +149 -0
- package/docs/harness/tui.md +176 -0
- package/docs/memories.md +20 -0
- package/docs/solutions/agentic-issues/premature-agent-deletion-tui-action-dependency-20260130.md +49 -0
- package/docs/solutions/agentic-issues/ref-anchor-scope-mismatch-skill-references-20260131.md +55 -0
- package/docs/solutions/agentic-issues/tautological-tests-routing-20260131.md +52 -0
- package/docs/solutions/integration_issue/blocktool-output-format-mismatch-hook-runner-20260130.md +52 -0
- package/docs/solutions/integration_issue/dual-validation-path-divergence-schema-20260130.md +66 -0
- package/docs/solutions/security-issues/unsanitized-domain-path-join-20260131.md +52 -0
- package/docs/solutions/test-failures/event-loop-mock-ordering-checkAgentWindows-20260130.md +63 -0
- package/docs/sync-cli/README.md +19 -0
- package/docs/sync-cli/cli-entrypoint-and-commands.md +39 -0
- package/docs/sync-cli/commands/README.md +11 -0
- package/docs/sync-cli/commands/pull-manifest-command.md +36 -0
- package/docs/sync-cli/commands/push-command.md +84 -0
- package/docs/sync-cli/commands/sync-command.md +71 -0
- package/docs/sync-cli/systems/README.md +14 -0
- package/docs/sync-cli/systems/git-and-github-integration.md +49 -0
- package/docs/sync-cli/systems/interactive-ui.md +43 -0
- package/docs/sync-cli/systems/manifest-and-distribution.md +51 -0
- package/docs/sync-cli/systems/path-resolution.md +42 -0
- package/package.json +46 -0
- package/scripts/install-shim.sh +40 -0
- package/scripts/pre-pack.sh +25 -0
- package/specs/harness-maintenance-skill.spec.md +138 -0
- package/specs/roadmap/git-spec-lifecycle-management.spec.md +113 -0
- package/specs/sync-init-flag.spec.md +117 -0
- package/specs/unified-workflow-orchestration.spec.md +250 -0
- package/specs/validation-tooling-practice.spec.md +98 -0
- package/specs/workflow-domain-configuration.spec.md +265 -0
- package/src/commands/pull-manifest.ts +31 -0
- package/src/commands/push.ts +344 -0
- package/src/commands/sync.ts +289 -0
- package/src/lib/constants.ts +10 -0
- package/src/lib/dotfiles.ts +36 -0
- package/src/lib/fs-utils.ts +18 -0
- package/src/lib/gh.ts +40 -0
- package/src/lib/git.ts +63 -0
- package/src/lib/gitignore.ts +167 -0
- package/src/lib/manifest.ts +121 -0
- package/src/lib/marker-sync.ts +39 -0
- package/src/lib/paths.ts +38 -0
- package/src/lib/target-lines.ts +66 -0
- package/src/lib/ui.ts +78 -0
- package/src/sync-cli.ts +120 -0
- package/target-lines.json +23 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enforcement Hooks
|
|
3
|
+
*
|
|
4
|
+
* PreToolUse hooks that enforce usage patterns:
|
|
5
|
+
* - Block GitHub URLs in WebFetch/Bash (suggest gh CLI)
|
|
6
|
+
* - Block WebFetch (suggest research tools)
|
|
7
|
+
* - Block WebSearch (suggest research delegation)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Command } from 'commander';
|
|
11
|
+
import {
|
|
12
|
+
HookInput,
|
|
13
|
+
HookCategory,
|
|
14
|
+
RegisterFn,
|
|
15
|
+
allowTool,
|
|
16
|
+
denyTool,
|
|
17
|
+
registerCategory,
|
|
18
|
+
registerCategoryForDaemon,
|
|
19
|
+
} from './shared.js';
|
|
20
|
+
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
// Constants
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
const GITHUB_DOMAINS = ['github.com', 'raw.githubusercontent.com', 'gist.github.com'];
|
|
26
|
+
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
// GitHub URL Enforcement
|
|
29
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
const HOOK_GITHUB_URL = 'enforcement github-url';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Block GitHub URLs in WebFetch and Bash fetch commands.
|
|
35
|
+
* Suggests using the gh CLI instead.
|
|
36
|
+
*
|
|
37
|
+
* Triggered by: PreToolUse matcher "(WebFetch|Bash)"
|
|
38
|
+
*/
|
|
39
|
+
export function enforceGitHubUrl(input: HookInput): void {
|
|
40
|
+
const toolName = input.tool_name || '';
|
|
41
|
+
const toolInput = input.tool_input || {};
|
|
42
|
+
|
|
43
|
+
// Check WebFetch URLs
|
|
44
|
+
if (toolName === 'WebFetch') {
|
|
45
|
+
const url = (toolInput.url as string) || '';
|
|
46
|
+
for (const domain of GITHUB_DOMAINS) {
|
|
47
|
+
if (url.includes(domain)) {
|
|
48
|
+
denyTool("GitHub URL detected. Use 'gh' CLI: gh api repos/OWNER/REPO/contents/PATH", HOOK_GITHUB_URL);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
allowTool(HOOK_GITHUB_URL);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check Bash commands for curl, wget, tavily extract
|
|
55
|
+
if (toolName === 'Bash') {
|
|
56
|
+
const command = (toolInput.command as string) || '';
|
|
57
|
+
|
|
58
|
+
// Check for fetch-like commands
|
|
59
|
+
const isFetchCmd = ['curl', 'wget', 'tavily extract'].some((cmd) => command.includes(cmd));
|
|
60
|
+
if (!isFetchCmd) {
|
|
61
|
+
allowTool(HOOK_GITHUB_URL);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check for GitHub URLs
|
|
65
|
+
for (const domain of GITHUB_DOMAINS) {
|
|
66
|
+
if (command.includes(domain)) {
|
|
67
|
+
denyTool("GitHub URL detected. Use 'gh' CLI: gh api repos/OWNER/REPO/contents/PATH", HOOK_GITHUB_URL);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
allowTool(HOOK_GITHUB_URL);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
// Research Fetch Enforcement
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
const HOOK_RESEARCH_FETCH = 'enforcement research-fetch';
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Block WebFetch and suggest research tools.
|
|
83
|
+
*
|
|
84
|
+
* Triggered by: PreToolUse matcher "WebFetch"
|
|
85
|
+
*/
|
|
86
|
+
export function enforceResearchFetch(input: HookInput): void {
|
|
87
|
+
const url = (input.tool_input?.url as string) || '';
|
|
88
|
+
|
|
89
|
+
if (!url) {
|
|
90
|
+
allowTool(HOOK_RESEARCH_FETCH);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
denyTool(
|
|
94
|
+
'WebFetch blocked. Use `ah tavily extract "<url>"` instead.',
|
|
95
|
+
HOOK_RESEARCH_FETCH
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
100
|
+
// Research Search Enforcement
|
|
101
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
const HOOK_RESEARCH_SEARCH = 'enforcement research-search';
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Block WebSearch and suggest research delegation.
|
|
107
|
+
*
|
|
108
|
+
* Triggered by: PreToolUse matcher "WebSearch"
|
|
109
|
+
*/
|
|
110
|
+
export function enforceResearchSearch(_input: HookInput): void {
|
|
111
|
+
denyTool(
|
|
112
|
+
'WebSearch blocked. Use `ah perplexity research "<query>"` instead.',
|
|
113
|
+
HOOK_RESEARCH_SEARCH
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
// Hook Category Definition
|
|
119
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/** Enforcement hooks category */
|
|
122
|
+
export const category: HookCategory = {
|
|
123
|
+
name: 'enforcement',
|
|
124
|
+
description: 'Enforcement hooks (PreToolUse)',
|
|
125
|
+
hooks: [
|
|
126
|
+
{
|
|
127
|
+
name: 'github-url',
|
|
128
|
+
description: 'Block GitHub URLs in fetch commands',
|
|
129
|
+
handler: enforceGitHubUrl,
|
|
130
|
+
errorFallback: { type: 'allowTool' },
|
|
131
|
+
logPayload: (input) => ({ tool: input.tool_name }),
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'research-fetch',
|
|
135
|
+
description: 'Block WebFetch and suggest `ah tavily extract "<url>"` instead',
|
|
136
|
+
handler: enforceResearchFetch,
|
|
137
|
+
errorFallback: { type: 'allowTool' },
|
|
138
|
+
logPayload: (input) => ({ tool: input.tool_name }),
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'research-search',
|
|
142
|
+
description: 'Block WebSearch and suggest `ah perplexity research "<query>"` instead',
|
|
143
|
+
handler: enforceResearchSearch,
|
|
144
|
+
errorFallback: { type: 'allowTool' },
|
|
145
|
+
logPayload: (input) => ({ tool: input.tool_name }),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
151
|
+
// Command Registration
|
|
152
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Register enforcement hook subcommands.
|
|
156
|
+
*/
|
|
157
|
+
export function register(parent: Command): void {
|
|
158
|
+
registerCategory(parent, category);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
162
|
+
// Daemon Handler Registration
|
|
163
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Register handlers for daemon mode.
|
|
167
|
+
*/
|
|
168
|
+
export function registerDaemonHandlers(register: RegisterFn): void {
|
|
169
|
+
registerCategoryForDaemon(category, register);
|
|
170
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Registry - Auto-discovers hook modules.
|
|
3
|
+
*
|
|
4
|
+
* Each hook file should export a `register` function that takes
|
|
5
|
+
* a Commander Command (the parent 'hooks' command) and registers its subcommands.
|
|
6
|
+
*
|
|
7
|
+
* Skips: index.ts, shared.ts, transcript-parser.ts (utilities)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readdirSync, statSync } from 'fs';
|
|
11
|
+
import { dirname, join } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
import type { Command } from 'commander';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
// Files to skip (utilities, not hook commands)
|
|
18
|
+
const SKIP_FILES = ['index.ts', 'shared.ts', 'transcript-parser.ts'];
|
|
19
|
+
|
|
20
|
+
export interface HookModule {
|
|
21
|
+
register: (parent: Command) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Auto-discover and register all hook modules.
|
|
26
|
+
*
|
|
27
|
+
* @param parent - The parent 'hooks' command to register subcommands on
|
|
28
|
+
*/
|
|
29
|
+
export async function discoverAndRegisterHooks(parent: Command): Promise<void> {
|
|
30
|
+
const entries = readdirSync(__dirname);
|
|
31
|
+
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
const entryPath = join(__dirname, entry);
|
|
34
|
+
const stat = statSync(entryPath);
|
|
35
|
+
|
|
36
|
+
// Skip directories and non-ts files
|
|
37
|
+
if (stat.isDirectory()) continue;
|
|
38
|
+
if (!entry.endsWith('.ts')) continue;
|
|
39
|
+
if (SKIP_FILES.includes(entry)) continue;
|
|
40
|
+
|
|
41
|
+
const moduleName = entry.replace('.ts', '');
|
|
42
|
+
const importPath = `./${moduleName}.js`;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const module = (await import(importPath)) as HookModule;
|
|
46
|
+
if (typeof module.register === 'function') {
|
|
47
|
+
module.register(parent);
|
|
48
|
+
}
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// Skip modules with errors - log for debugging
|
|
51
|
+
console.error(`Warning: Could not load hook module ${moduleName}: ${e}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Hooks
|
|
3
|
+
*
|
|
4
|
+
* Hooks for agent lifecycle events:
|
|
5
|
+
* - Stop: Send notification and kill tmux window
|
|
6
|
+
* - PreCompact: Summarize progress, append to prompt file, kill tmux window
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
import type { Command } from 'commander';
|
|
11
|
+
import {
|
|
12
|
+
HookInput,
|
|
13
|
+
HookCategory,
|
|
14
|
+
RegisterFn,
|
|
15
|
+
outputStopHook,
|
|
16
|
+
outputPreCompact,
|
|
17
|
+
registerCategory,
|
|
18
|
+
registerCategoryForDaemon,
|
|
19
|
+
} from './shared.js';
|
|
20
|
+
import { logHookSuccess } from '../lib/trace-store.js';
|
|
21
|
+
import { sendNotification } from '../lib/notification.js';
|
|
22
|
+
import { killWindow, SESSION_NAME, windowExists, getCurrentSession } from '../lib/tmux.js';
|
|
23
|
+
import { getPromptByNumber } from '../lib/prompts.js';
|
|
24
|
+
import { getCurrentBranch, sanitizeBranchForDir } from '../lib/planning.js';
|
|
25
|
+
import { runCompaction } from '../lib/compaction.js';
|
|
26
|
+
|
|
27
|
+
const HOOK_AGENT_STOP = 'lifecycle agent-stop';
|
|
28
|
+
const HOOK_AGENT_COMPACT = 'lifecycle agent-compact';
|
|
29
|
+
|
|
30
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
31
|
+
// Agent Stop
|
|
32
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Handle agent stop lifecycle event.
|
|
36
|
+
*
|
|
37
|
+
* - Sends desktop notification
|
|
38
|
+
* - Kills the tmux window (for prompt-scoped agents that may not close naturally)
|
|
39
|
+
* - Approves the stop
|
|
40
|
+
*
|
|
41
|
+
* Triggered by: Stop matcher "*"
|
|
42
|
+
*/
|
|
43
|
+
export function handleAgentStop(_input: HookInput): void {
|
|
44
|
+
const agentId = process.env.AGENT_ID;
|
|
45
|
+
const agentType = process.env.AGENT_TYPE || 'Agent';
|
|
46
|
+
const isPromptScoped = process.env.PROMPT_SCOPED === 'true';
|
|
47
|
+
|
|
48
|
+
// Send notification
|
|
49
|
+
const title = agentId ? `${agentType} Stopped` : 'Agent Stopped';
|
|
50
|
+
const message = agentId ? `Agent ${agentId} has stopped` : 'The agent session has stopped';
|
|
51
|
+
|
|
52
|
+
sendNotification({
|
|
53
|
+
title,
|
|
54
|
+
message,
|
|
55
|
+
type: 'banner',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Kill the tmux window for prompt-scoped agents AND the emergent planner.
|
|
59
|
+
// Non-prompt-scoped agents (except emergent) should remain running (their window stays open).
|
|
60
|
+
// Prompt-scoped agents may not close naturally even with exec, so we
|
|
61
|
+
// explicitly kill them here. The emergent planner is prompt_scoped, but
|
|
62
|
+
// this condition also covers it by agent type for clarity.
|
|
63
|
+
if (isPromptScoped || agentType === 'emergent') {
|
|
64
|
+
const sessionName = getCurrentSession() || SESSION_NAME;
|
|
65
|
+
if (agentId && windowExists(sessionName, agentId)) {
|
|
66
|
+
killWindow(sessionName, agentId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Approve stop
|
|
71
|
+
outputStopHook('approve', undefined, HOOK_AGENT_STOP);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
75
|
+
// Pre-Compact
|
|
76
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Handle pre-compaction lifecycle event.
|
|
80
|
+
*
|
|
81
|
+
* When context gets too long and compaction is triggered for a prompt-scoped agent:
|
|
82
|
+
* 1. Run full compaction analysis via oracle:
|
|
83
|
+
* - Analyze conversation for progress, learnings, blockers
|
|
84
|
+
* - Recommend action: continue (keep code) or scratch (discard)
|
|
85
|
+
* - Increment attempts counter in prompt frontmatter
|
|
86
|
+
* - Append detailed progress update to prompt file
|
|
87
|
+
* - Execute recommendation (commit or discard changes)
|
|
88
|
+
* 2. Kill the tmux window (terminate the Claude instance)
|
|
89
|
+
* 3. Event loop can then re-run the prompt with learnings
|
|
90
|
+
*
|
|
91
|
+
* Criteria to run full compaction:
|
|
92
|
+
* - PROMPT_SCOPED=true (agent is prompt-scoped)
|
|
93
|
+
* - PROMPT_NUMBER is set (we know which prompt file to update)
|
|
94
|
+
* - transcript_path exists (we have conversation logs to analyze)
|
|
95
|
+
*
|
|
96
|
+
* Triggered by: PreCompact matcher "*"
|
|
97
|
+
*/
|
|
98
|
+
export async function handleAgentCompact(input: HookInput): Promise<void> {
|
|
99
|
+
const agentId = process.env.AGENT_ID;
|
|
100
|
+
const promptNumber = process.env.PROMPT_NUMBER;
|
|
101
|
+
const isPromptScoped = process.env.PROMPT_SCOPED === 'true';
|
|
102
|
+
const transcriptPath = input.transcript_path;
|
|
103
|
+
|
|
104
|
+
const agentType = process.env.AGENT_TYPE;
|
|
105
|
+
|
|
106
|
+
// Only process compaction for prompt executor agents (prompt-scoped with PROMPT_NUMBER).
|
|
107
|
+
// The emergent planner is prompt_scoped (for window naming) but must NOT run compaction —
|
|
108
|
+
// it plans hypotheses, not implementation. Non-prompt-scoped agents pass through too.
|
|
109
|
+
if (!isPromptScoped || !promptNumber || agentType === 'emergent') {
|
|
110
|
+
logHookSuccess(HOOK_AGENT_COMPACT, {
|
|
111
|
+
action: 'skip',
|
|
112
|
+
reason: !isPromptScoped ? 'not_prompt_scoped' : agentType === 'emergent' ? 'emergent_planner' : 'no_prompt_number',
|
|
113
|
+
});
|
|
114
|
+
return outputPreCompact(undefined);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Use current session (not hardcoded SESSION_NAME) since agents may be
|
|
118
|
+
// spawned in whatever session is active, not necessarily 'ah-hub'
|
|
119
|
+
const sessionName = getCurrentSession() || SESSION_NAME;
|
|
120
|
+
|
|
121
|
+
// Get the planning key (sanitized branch name) for directory lookups.
|
|
122
|
+
// The planning directory is .planning/<sanitized-branch>/ (e.g., "feature-core-taskflow-crud").
|
|
123
|
+
const branch = getCurrentBranch();
|
|
124
|
+
const planningKey = sanitizeBranchForDir(branch);
|
|
125
|
+
|
|
126
|
+
// Get the prompt file
|
|
127
|
+
const promptNum = parseInt(promptNumber, 10);
|
|
128
|
+
const prompt = getPromptByNumber(promptNum, planningKey);
|
|
129
|
+
|
|
130
|
+
if (!prompt) {
|
|
131
|
+
// Prompt file not found, kill window and exit
|
|
132
|
+
logHookSuccess(HOOK_AGENT_COMPACT, { action: 'skip', reason: 'no_prompt', promptNum, planningKey });
|
|
133
|
+
if (agentId && windowExists(sessionName, agentId)) {
|
|
134
|
+
killWindow(sessionName, agentId);
|
|
135
|
+
}
|
|
136
|
+
return outputPreCompact(undefined);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Need transcript to run compaction analysis
|
|
140
|
+
if (!transcriptPath || !existsSync(transcriptPath)) {
|
|
141
|
+
sendNotification({
|
|
142
|
+
title: 'Compaction Skipped',
|
|
143
|
+
message: `No transcript available for prompt ${promptNumber}`,
|
|
144
|
+
type: 'banner',
|
|
145
|
+
});
|
|
146
|
+
if (agentId && windowExists(sessionName, agentId)) {
|
|
147
|
+
killWindow(sessionName, agentId);
|
|
148
|
+
}
|
|
149
|
+
return outputPreCompact(undefined, HOOK_AGENT_COMPACT);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Notify that compaction is starting
|
|
153
|
+
sendNotification({
|
|
154
|
+
title: 'Compaction Starting',
|
|
155
|
+
message: `Analyzing prompt ${promptNumber}...`,
|
|
156
|
+
type: 'banner',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Run full compaction analysis (result written to prompt file)
|
|
161
|
+
await runCompaction({
|
|
162
|
+
conversationLogs: transcriptPath,
|
|
163
|
+
promptFile: prompt.path,
|
|
164
|
+
});
|
|
165
|
+
} catch {
|
|
166
|
+
// Compaction failed - error logged to trace store
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Kill the tmux window after compaction completes.
|
|
170
|
+
// PreCompact hook does NOT stop the Claude session - we must explicitly kill it.
|
|
171
|
+
if (agentId && windowExists(sessionName, agentId)) {
|
|
172
|
+
killWindow(sessionName, agentId);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
outputPreCompact(undefined, HOOK_AGENT_COMPACT);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
179
|
+
// Hook Category Definition
|
|
180
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
/** Lifecycle hooks category */
|
|
183
|
+
export const category: HookCategory = {
|
|
184
|
+
name: 'lifecycle',
|
|
185
|
+
description: 'Lifecycle hooks (Stop, PreCompact)',
|
|
186
|
+
hooks: [
|
|
187
|
+
{
|
|
188
|
+
name: 'agent-stop',
|
|
189
|
+
description: 'Handle agent stop event',
|
|
190
|
+
handler: handleAgentStop,
|
|
191
|
+
errorFallback: { type: 'outputStopHook', decision: 'approve' },
|
|
192
|
+
logPayload: () => ({ agentId: process.env.AGENT_ID }),
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'agent-compact',
|
|
196
|
+
description: 'Handle pre-compaction event',
|
|
197
|
+
handler: handleAgentCompact,
|
|
198
|
+
errorFallback: { type: 'continue' },
|
|
199
|
+
logPayload: () => ({
|
|
200
|
+
agentId: process.env.AGENT_ID,
|
|
201
|
+
promptNumber: process.env.PROMPT_NUMBER,
|
|
202
|
+
promptScoped: process.env.PROMPT_SCOPED,
|
|
203
|
+
specName: process.env.SPEC_NAME,
|
|
204
|
+
}),
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
210
|
+
// Command Registration
|
|
211
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Register lifecycle hook subcommands.
|
|
215
|
+
*/
|
|
216
|
+
export function register(parent: Command): void {
|
|
217
|
+
registerCategory(parent, category);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
221
|
+
// Daemon Handler Registration
|
|
222
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Register handlers for daemon mode.
|
|
226
|
+
*/
|
|
227
|
+
export function registerDaemonHandlers(register: RegisterFn): void {
|
|
228
|
+
registerCategoryForDaemon(category, register);
|
|
229
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Hooks
|
|
3
|
+
*
|
|
4
|
+
* Hooks that send desktop notifications for various Claude Code events:
|
|
5
|
+
* - stop: Agent has stopped working (Stop:*)
|
|
6
|
+
* - compact: Context is being compacted (PreCompact:*)
|
|
7
|
+
*
|
|
8
|
+
* Uses jamf/Notifier for macOS notifications.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Command } from 'commander';
|
|
12
|
+
import { HookInput, readHookInput } from './shared.js';
|
|
13
|
+
import { sendGateNotification } from '../lib/notification.js';
|
|
14
|
+
import { logHookStart, logHookSuccess } from '../lib/trace-store.js';
|
|
15
|
+
|
|
16
|
+
const HOOK_STOP = 'notification stop';
|
|
17
|
+
const HOOK_COMPACT = 'notification compact';
|
|
18
|
+
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
// Stop: Agent stopped
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Handle stop notification.
|
|
25
|
+
*
|
|
26
|
+
* Sends a notification when the agent has stopped working.
|
|
27
|
+
*
|
|
28
|
+
* Triggered by: Stop matcher "*"
|
|
29
|
+
*/
|
|
30
|
+
function handleStopNotification(_input: HookInput): void {
|
|
31
|
+
sendGateNotification('Stopped', 'Agent has finished');
|
|
32
|
+
|
|
33
|
+
// Output approval to allow stop
|
|
34
|
+
logHookSuccess(HOOK_STOP, { action: 'approve' });
|
|
35
|
+
console.log(JSON.stringify({ decision: 'approve' }));
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
+
// PreCompact: Context compaction
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Handle pre-compact notification.
|
|
45
|
+
*
|
|
46
|
+
* Sends a notification when the agent is about to compact context.
|
|
47
|
+
*
|
|
48
|
+
* Triggered by: PreCompact matcher "*"
|
|
49
|
+
*/
|
|
50
|
+
function handleCompactNotification(_input: HookInput): void {
|
|
51
|
+
sendGateNotification('Compacting', 'Context is being summarized');
|
|
52
|
+
|
|
53
|
+
// Output to allow compaction to proceed
|
|
54
|
+
logHookSuccess(HOOK_COMPACT, { action: 'continue' });
|
|
55
|
+
console.log(JSON.stringify({ continue: true }));
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
60
|
+
// Command Registration
|
|
61
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Register notification hook subcommands.
|
|
65
|
+
*/
|
|
66
|
+
export function register(parent: Command): void {
|
|
67
|
+
const notification = parent
|
|
68
|
+
.command('notification')
|
|
69
|
+
.description('Notification hooks (desktop alerts for events)');
|
|
70
|
+
|
|
71
|
+
// ah hooks notification stop
|
|
72
|
+
notification
|
|
73
|
+
.command('stop')
|
|
74
|
+
.description('Handle stop notification (Stop:*)')
|
|
75
|
+
.action(async () => {
|
|
76
|
+
try {
|
|
77
|
+
const input = await readHookInput();
|
|
78
|
+
logHookStart(HOOK_STOP, {});
|
|
79
|
+
handleStopNotification(input);
|
|
80
|
+
} catch {
|
|
81
|
+
// On error, approve stop
|
|
82
|
+
logHookSuccess(HOOK_STOP, { action: 'approve', error: true });
|
|
83
|
+
console.log(JSON.stringify({ decision: 'approve' }));
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ah hooks notification compact
|
|
89
|
+
notification
|
|
90
|
+
.command('compact')
|
|
91
|
+
.description('Handle compact notification (PreCompact:*)')
|
|
92
|
+
.action(async () => {
|
|
93
|
+
try {
|
|
94
|
+
const input = await readHookInput();
|
|
95
|
+
logHookStart(HOOK_COMPACT, {});
|
|
96
|
+
handleCompactNotification(input);
|
|
97
|
+
} catch {
|
|
98
|
+
// On error, allow compaction
|
|
99
|
+
logHookSuccess(HOOK_COMPACT, { action: 'continue', error: true });
|
|
100
|
+
console.log(JSON.stringify({ continue: true }));
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|