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,617 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Hooks
|
|
3
|
+
*
|
|
4
|
+
* PostToolUse hooks that run diagnostics on edited files:
|
|
5
|
+
* - Python: pyright + ruff (if available)
|
|
6
|
+
* - TypeScript: tsc --noEmit
|
|
7
|
+
* - Schema validation for schema-managed markdown files
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
import type { Command } from 'commander';
|
|
12
|
+
import { existsSync, readFileSync } from 'fs';
|
|
13
|
+
import { dirname, extname, join, relative } from 'path';
|
|
14
|
+
import { minimatch } from 'minimatch';
|
|
15
|
+
import {
|
|
16
|
+
detectSchemaType,
|
|
17
|
+
type SchemaType,
|
|
18
|
+
loadSchema as loadSchemaFromLib,
|
|
19
|
+
extractFrontmatter,
|
|
20
|
+
validateFrontmatter as validateFrontmatterFromLib,
|
|
21
|
+
type ValidationError,
|
|
22
|
+
} from '../lib/schema.js';
|
|
23
|
+
import {
|
|
24
|
+
HookInput,
|
|
25
|
+
HookCategory,
|
|
26
|
+
RegisterFn,
|
|
27
|
+
allowTool,
|
|
28
|
+
blockTool,
|
|
29
|
+
denyTool,
|
|
30
|
+
FormatConfig,
|
|
31
|
+
getProjectDir,
|
|
32
|
+
loadProjectSettings,
|
|
33
|
+
outputContext,
|
|
34
|
+
registerCategory,
|
|
35
|
+
registerCategoryForDaemon,
|
|
36
|
+
} from './shared.js';
|
|
37
|
+
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
// Hook Names
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
const HOOK_DIAGNOSTICS = 'validation diagnostics';
|
|
43
|
+
const HOOK_SCHEMA = 'validation schema';
|
|
44
|
+
const HOOK_FORMAT = 'validation format';
|
|
45
|
+
const HOOK_SCHEMA_PRE = 'validation schema-pre';
|
|
46
|
+
|
|
47
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
48
|
+
// Types
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
interface DiagnosticResult {
|
|
52
|
+
tool: string;
|
|
53
|
+
errors: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
57
|
+
// Tool Detection
|
|
58
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
function isToolAvailable(tool: string): boolean {
|
|
61
|
+
try {
|
|
62
|
+
execSync(`which ${tool}`, { stdio: 'pipe' });
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
70
|
+
// Python Diagnostics
|
|
71
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
function runPyrightDiagnostics(filePath: string): DiagnosticResult | null {
|
|
74
|
+
if (!isToolAvailable('pyright')) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
execSync(`pyright --outputjson "${filePath}"`, {
|
|
80
|
+
encoding: 'utf-8',
|
|
81
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
82
|
+
});
|
|
83
|
+
return null; // No errors
|
|
84
|
+
} catch (e: unknown) {
|
|
85
|
+
const error = e as { stdout?: string };
|
|
86
|
+
if (error.stdout) {
|
|
87
|
+
try {
|
|
88
|
+
const output = JSON.parse(error.stdout);
|
|
89
|
+
const diagnostics = output.generalDiagnostics || [];
|
|
90
|
+
const errors = diagnostics
|
|
91
|
+
.filter((d: { severity: string }) => d.severity === 'error')
|
|
92
|
+
.map((d: { file: string; range: { start: { line: number } }; message: string }) => {
|
|
93
|
+
const line = d.range?.start?.line ?? 0;
|
|
94
|
+
return `${d.file}:${line}: ${d.message}`;
|
|
95
|
+
})
|
|
96
|
+
.slice(0, 5); // Limit to 5 errors
|
|
97
|
+
|
|
98
|
+
if (errors.length > 0) {
|
|
99
|
+
return { tool: 'pyright', errors };
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
// Parse error, skip
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function runRuffDiagnostics(filePath: string): DiagnosticResult | null {
|
|
110
|
+
if (!isToolAvailable('ruff')) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
execSync(`ruff check "${filePath}" --output-format=text`, {
|
|
116
|
+
encoding: 'utf-8',
|
|
117
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
118
|
+
});
|
|
119
|
+
return null; // No errors
|
|
120
|
+
} catch (e: unknown) {
|
|
121
|
+
const error = e as { stdout?: string };
|
|
122
|
+
if (error.stdout) {
|
|
123
|
+
const lines = error.stdout.trim().split('\n').filter(Boolean).slice(0, 5);
|
|
124
|
+
if (lines.length > 0) {
|
|
125
|
+
return { tool: 'ruff', errors: lines };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
133
|
+
// TypeScript Diagnostics
|
|
134
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Find the nearest tsconfig.json by walking up from the file's directory.
|
|
138
|
+
*/
|
|
139
|
+
function findTsConfig(filePath: string): string | null {
|
|
140
|
+
let dir = dirname(filePath);
|
|
141
|
+
const root = '/';
|
|
142
|
+
|
|
143
|
+
while (dir !== root) {
|
|
144
|
+
const candidate = join(dir, 'tsconfig.json');
|
|
145
|
+
if (existsSync(candidate)) {
|
|
146
|
+
return candidate;
|
|
147
|
+
}
|
|
148
|
+
const parent = dirname(dir);
|
|
149
|
+
if (parent === dir) break; // Reached filesystem root
|
|
150
|
+
dir = parent;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function runTscDiagnostics(filePath: string): DiagnosticResult | null {
|
|
157
|
+
if (!isToolAvailable('tsc')) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Find the project's tsconfig.json to get correct compiler options
|
|
162
|
+
const tsconfig = findTsConfig(filePath);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// Run tsc on the whole project and filter for this file's errors
|
|
166
|
+
// This ensures we use the correct tsconfig settings (esModuleInterop, target, etc.)
|
|
167
|
+
// tsc outputs paths relative to tsconfig dir, so convert the absolute filePath to match
|
|
168
|
+
const tscDir = tsconfig ? dirname(tsconfig) : undefined;
|
|
169
|
+
const grepPath = tsconfig ? relative(dirname(tsconfig), filePath) : filePath;
|
|
170
|
+
const tscCmd = tsconfig
|
|
171
|
+
? `tsc --noEmit -p "${tsconfig}" 2>&1 | grep -E "^${grepPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}" || true`
|
|
172
|
+
: `tsc --noEmit "${filePath}"`;
|
|
173
|
+
|
|
174
|
+
const output = execSync(tscCmd, {
|
|
175
|
+
encoding: 'utf-8',
|
|
176
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
177
|
+
cwd: tscDir,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
if (output.trim()) {
|
|
181
|
+
const lines = output.trim().split('\n').filter(Boolean).slice(0, 5);
|
|
182
|
+
if (lines.length > 0) {
|
|
183
|
+
return { tool: 'tsc', errors: lines };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return null; // No errors
|
|
187
|
+
} catch (e: unknown) {
|
|
188
|
+
const error = e as { stdout?: string; stderr?: string };
|
|
189
|
+
const output = error.stdout || error.stderr || '';
|
|
190
|
+
if (output) {
|
|
191
|
+
const lines = output.trim().split('\n').filter(Boolean).slice(0, 5);
|
|
192
|
+
if (lines.length > 0) {
|
|
193
|
+
return { tool: 'tsc', errors: lines };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
201
|
+
// Main Diagnostics Runner
|
|
202
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Run diagnostics on an edited file.
|
|
206
|
+
*
|
|
207
|
+
* Triggered by: PostToolUse matcher "(Write|Edit)"
|
|
208
|
+
*/
|
|
209
|
+
export function runDiagnostics(input: HookInput): void {
|
|
210
|
+
const filePath = input.tool_input?.file_path as string | undefined;
|
|
211
|
+
|
|
212
|
+
if (!filePath) {
|
|
213
|
+
allowTool(HOOK_DIAGNOSTICS);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const ext = extname(filePath!).toLowerCase();
|
|
217
|
+
const results: DiagnosticResult[] = [];
|
|
218
|
+
|
|
219
|
+
// Python files
|
|
220
|
+
if (ext === '.py') {
|
|
221
|
+
const pyright = runPyrightDiagnostics(filePath!);
|
|
222
|
+
if (pyright) results.push(pyright);
|
|
223
|
+
|
|
224
|
+
const ruff = runRuffDiagnostics(filePath!);
|
|
225
|
+
if (ruff) results.push(ruff);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// TypeScript files
|
|
229
|
+
if (ext === '.ts' || ext === '.tsx') {
|
|
230
|
+
const tsc = runTscDiagnostics(filePath!);
|
|
231
|
+
if (tsc) results.push(tsc);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Output context if there are errors
|
|
235
|
+
if (results.length > 0) {
|
|
236
|
+
const context = formatDiagnosticsContext(results);
|
|
237
|
+
outputContext(context, HOOK_DIAGNOSTICS);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
allowTool(HOOK_DIAGNOSTICS);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Format diagnostic results as context string.
|
|
245
|
+
*/
|
|
246
|
+
function formatDiagnosticsContext(results: DiagnosticResult[]): string {
|
|
247
|
+
const parts: string[] = ['## Diagnostics'];
|
|
248
|
+
|
|
249
|
+
for (const result of results) {
|
|
250
|
+
parts.push(`\n### ${result.tool}`);
|
|
251
|
+
result.errors.forEach((e) => parts.push(e));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
parts.push('\nPlease fix these issues before continuing.');
|
|
255
|
+
|
|
256
|
+
return parts.join('\n');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
260
|
+
// Schema Validation
|
|
261
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Detect which schema applies to a file path (wrapper for shared function)
|
|
265
|
+
*/
|
|
266
|
+
function detectSchemaTypeLocal(filePath: string): SchemaType | null {
|
|
267
|
+
return detectSchemaType(filePath, getProjectDir());
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Extract folder name from skill file path
|
|
272
|
+
* e.g., ".allhands/skills/my-skill/SKILL.md" -> "my-skill"
|
|
273
|
+
*/
|
|
274
|
+
function extractSkillFolderName(filePath: string): string | null {
|
|
275
|
+
const match = filePath.match(/\.allhands\/skills\/([^/]+)\/SKILL\.md$/);
|
|
276
|
+
return match ? match[1] : null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Validate skill-specific rules (name must match folder)
|
|
281
|
+
*/
|
|
282
|
+
function validateSkillSpecificRules(
|
|
283
|
+
filePath: string,
|
|
284
|
+
frontmatter: Record<string, unknown>
|
|
285
|
+
): ValidationError[] {
|
|
286
|
+
const errors: ValidationError[] = [];
|
|
287
|
+
const folderName = extractSkillFolderName(filePath);
|
|
288
|
+
|
|
289
|
+
if (folderName && frontmatter.name && frontmatter.name !== folderName) {
|
|
290
|
+
errors.push({
|
|
291
|
+
field: 'name',
|
|
292
|
+
message: `Skill name '${frontmatter.name}' must match containing folder '${folderName}'`,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return errors;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Run schema validation on a file.
|
|
301
|
+
* Delegates to lib/schema.ts for parsing and validation.
|
|
302
|
+
* Returns validation errors or null if valid.
|
|
303
|
+
*/
|
|
304
|
+
function runSchemaValidation(filePath: string): ValidationError[] | null {
|
|
305
|
+
// Detect schema type
|
|
306
|
+
const schemaType = detectSchemaTypeLocal(filePath);
|
|
307
|
+
if (!schemaType) {
|
|
308
|
+
// Not a schema-managed file
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Load schema (cached in lib)
|
|
313
|
+
const schema = loadSchemaFromLib(schemaType);
|
|
314
|
+
if (!schema) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Read file content
|
|
319
|
+
if (!existsSync(filePath)) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
324
|
+
|
|
325
|
+
// Parse frontmatter using lib
|
|
326
|
+
const { frontmatter } = extractFrontmatter(content);
|
|
327
|
+
if (!frontmatter) {
|
|
328
|
+
return [{
|
|
329
|
+
field: 'frontmatter',
|
|
330
|
+
message: 'File is missing valid YAML frontmatter (---...---)',
|
|
331
|
+
}];
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Validate against schema using lib
|
|
335
|
+
const result = validateFrontmatterFromLib(frontmatter, schema);
|
|
336
|
+
const errors: ValidationError[] = [...result.errors];
|
|
337
|
+
|
|
338
|
+
// Add skill-specific validation
|
|
339
|
+
if (schemaType === 'skill') {
|
|
340
|
+
errors.push(...validateSkillSpecificRules(filePath, frontmatter));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return errors;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Run schema validation on content (for PreToolUse before file is written).
|
|
348
|
+
* Delegates to lib/schema.ts for parsing and validation.
|
|
349
|
+
* Returns validation errors or null if valid/not schema-managed.
|
|
350
|
+
*/
|
|
351
|
+
function runSchemaValidationOnContent(filePath: string, content: string): ValidationError[] | null {
|
|
352
|
+
// Detect schema type
|
|
353
|
+
const schemaType = detectSchemaTypeLocal(filePath);
|
|
354
|
+
if (!schemaType) {
|
|
355
|
+
// Not a schema-managed file
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Load schema (cached in lib)
|
|
360
|
+
const schema = loadSchemaFromLib(schemaType);
|
|
361
|
+
if (!schema) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Parse frontmatter from content using lib
|
|
366
|
+
const { frontmatter } = extractFrontmatter(content);
|
|
367
|
+
if (!frontmatter) {
|
|
368
|
+
return [{
|
|
369
|
+
field: 'frontmatter',
|
|
370
|
+
message: 'File is missing valid YAML frontmatter (---...---)',
|
|
371
|
+
}];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Validate against schema using lib
|
|
375
|
+
const result = validateFrontmatterFromLib(frontmatter, schema);
|
|
376
|
+
const errors: ValidationError[] = [...result.errors];
|
|
377
|
+
|
|
378
|
+
// Add skill-specific validation
|
|
379
|
+
if (schemaType === 'skill') {
|
|
380
|
+
errors.push(...validateSkillSpecificRules(filePath, frontmatter));
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return errors;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Format validation errors as context string
|
|
388
|
+
*/
|
|
389
|
+
function formatSchemaErrors(errors: ValidationError[], schemaType: string): string {
|
|
390
|
+
const parts: string[] = [`## Schema Validation Errors (${schemaType})`];
|
|
391
|
+
|
|
392
|
+
for (const error of errors) {
|
|
393
|
+
parts.push(`- ${error.message}`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
parts.push('\nPlease fix the frontmatter to match the schema. Run `ah schema ' + schemaType + '` to see the expected format.');
|
|
397
|
+
|
|
398
|
+
return parts.join('\n');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
402
|
+
// Auto-Format
|
|
403
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Get the format command for a specific file.
|
|
407
|
+
* Checks patterns first, then falls back to default command.
|
|
408
|
+
*/
|
|
409
|
+
function getFormatCommand(config: FormatConfig, filePath: string): string | null {
|
|
410
|
+
// Check patterns first (most specific)
|
|
411
|
+
if (config.patterns) {
|
|
412
|
+
for (const pattern of config.patterns) {
|
|
413
|
+
if (minimatch(filePath, pattern.match) || filePath.endsWith(pattern.match.replace('*', ''))) {
|
|
414
|
+
return pattern.command;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Fall back to default command
|
|
420
|
+
return config.command || null;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Run auto-format on an edited file.
|
|
425
|
+
*
|
|
426
|
+
* Reads format configuration from .allhands/settings.json:
|
|
427
|
+
* {
|
|
428
|
+
* "validation": {
|
|
429
|
+
* "format": {
|
|
430
|
+
* "enabled": true,
|
|
431
|
+
* "command": "pnpm format",
|
|
432
|
+
* "patterns": [
|
|
433
|
+
* { "match": "*.py", "command": "ruff format" }
|
|
434
|
+
* ]
|
|
435
|
+
* }
|
|
436
|
+
* }
|
|
437
|
+
* }
|
|
438
|
+
*
|
|
439
|
+
* Triggered by: PostToolUse matcher "(Write|Edit)"
|
|
440
|
+
*/
|
|
441
|
+
export function runFormat(input: HookInput): void {
|
|
442
|
+
const settings = loadProjectSettings();
|
|
443
|
+
const formatConfig = settings?.validation?.format;
|
|
444
|
+
|
|
445
|
+
// Check if formatting is enabled
|
|
446
|
+
if (!formatConfig?.enabled) {
|
|
447
|
+
return allowTool(HOOK_FORMAT);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const filePath = input.tool_input?.file_path as string | undefined;
|
|
451
|
+
if (!filePath) {
|
|
452
|
+
return allowTool(HOOK_FORMAT);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const command = getFormatCommand(formatConfig!, filePath!);
|
|
456
|
+
if (!command) {
|
|
457
|
+
return allowTool(HOOK_FORMAT);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
try {
|
|
461
|
+
// Run format command on the file
|
|
462
|
+
// Use || true pattern to ensure non-blocking (format failures shouldn't stop the agent)
|
|
463
|
+
execSync(`${command} "${filePath}"`, {
|
|
464
|
+
cwd: getProjectDir(),
|
|
465
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
466
|
+
timeout: 30000, // 30 second timeout
|
|
467
|
+
});
|
|
468
|
+
} catch {
|
|
469
|
+
// Format failures are non-blocking
|
|
470
|
+
// Could optionally log or output context here
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
allowTool(HOOK_FORMAT);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Validate schema-managed markdown files (PostToolUse).
|
|
478
|
+
*
|
|
479
|
+
* Triggered by: PostToolUse matcher "(Write|Edit)"
|
|
480
|
+
*/
|
|
481
|
+
export function validateSchema(input: HookInput): void {
|
|
482
|
+
const filePath = input.tool_input?.file_path as string | undefined;
|
|
483
|
+
|
|
484
|
+
if (!filePath) {
|
|
485
|
+
return allowTool(HOOK_SCHEMA);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const errors = runSchemaValidation(filePath!);
|
|
489
|
+
|
|
490
|
+
if (errors && errors.length > 0) {
|
|
491
|
+
const schemaType = detectSchemaTypeLocal(filePath!) || 'unknown';
|
|
492
|
+
const context = formatSchemaErrors(errors, schemaType);
|
|
493
|
+
blockTool(context, HOOK_SCHEMA);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
allowTool(HOOK_SCHEMA);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Validate schema-managed markdown files before write/edit (PreToolUse).
|
|
501
|
+
*
|
|
502
|
+
* Triggered by: PreToolUse matcher "(Write|Edit)"
|
|
503
|
+
*/
|
|
504
|
+
export function validateSchemaPre(input: HookInput): void {
|
|
505
|
+
const toolName = input.tool_name as string | undefined;
|
|
506
|
+
const filePath = input.tool_input?.file_path as string | undefined;
|
|
507
|
+
|
|
508
|
+
if (!filePath) {
|
|
509
|
+
return allowTool(HOOK_SCHEMA_PRE);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
let contentToValidate: string | undefined;
|
|
513
|
+
|
|
514
|
+
if (toolName === 'Write') {
|
|
515
|
+
// Write tool provides content directly
|
|
516
|
+
contentToValidate = input.tool_input?.content as string | undefined;
|
|
517
|
+
} else if (toolName === 'Edit') {
|
|
518
|
+
// Edit tool provides old_string and new_string - we need to compute the result
|
|
519
|
+
const oldString = input.tool_input?.old_string as string | undefined;
|
|
520
|
+
const newString = input.tool_input?.new_string as string | undefined;
|
|
521
|
+
const replaceAll = input.tool_input?.replace_all as boolean | undefined;
|
|
522
|
+
|
|
523
|
+
if (oldString === undefined || newString === undefined) {
|
|
524
|
+
return allowTool(HOOK_SCHEMA_PRE);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Read current file content
|
|
528
|
+
if (!existsSync(filePath)) {
|
|
529
|
+
return allowTool(HOOK_SCHEMA_PRE);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const currentContent = readFileSync(filePath, 'utf-8');
|
|
533
|
+
|
|
534
|
+
// Apply the edit to get the resulting content
|
|
535
|
+
if (replaceAll) {
|
|
536
|
+
contentToValidate = currentContent.split(oldString).join(newString);
|
|
537
|
+
} else {
|
|
538
|
+
contentToValidate = currentContent.replace(oldString, newString);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (!contentToValidate) {
|
|
543
|
+
return allowTool(HOOK_SCHEMA_PRE);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const errors = runSchemaValidationOnContent(filePath, contentToValidate);
|
|
547
|
+
|
|
548
|
+
if (errors && errors.length > 0) {
|
|
549
|
+
const schemaType = detectSchemaTypeLocal(filePath) || 'unknown';
|
|
550
|
+
const context = formatSchemaErrors(errors, schemaType);
|
|
551
|
+
denyTool(context, HOOK_SCHEMA_PRE);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
allowTool(HOOK_SCHEMA_PRE);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
558
|
+
// Hook Category Definition
|
|
559
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
560
|
+
|
|
561
|
+
/** Validation hooks category */
|
|
562
|
+
export const category: HookCategory = {
|
|
563
|
+
name: 'validation',
|
|
564
|
+
description: 'Validation hooks',
|
|
565
|
+
hooks: [
|
|
566
|
+
{
|
|
567
|
+
name: 'diagnostics',
|
|
568
|
+
description: 'Run diagnostics on edited files (PostToolUse)',
|
|
569
|
+
handler: runDiagnostics,
|
|
570
|
+
errorFallback: { type: 'allowTool' },
|
|
571
|
+
logPayload: (input) => ({ tool: input.tool_name, file: input.tool_input?.file_path }),
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
name: 'schema',
|
|
575
|
+
description: 'Validate schema-managed markdown files (PostToolUse)',
|
|
576
|
+
handler: validateSchema,
|
|
577
|
+
errorFallback: { type: 'allowTool' },
|
|
578
|
+
logPayload: (input) => ({ tool: input.tool_name, file: input.tool_input?.file_path }),
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
name: 'format',
|
|
582
|
+
description: 'Auto-format edited files (PostToolUse)',
|
|
583
|
+
handler: runFormat,
|
|
584
|
+
errorFallback: { type: 'allowTool' },
|
|
585
|
+
logPayload: (input) => ({ tool: input.tool_name, file: input.tool_input?.file_path }),
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
name: 'schema-pre',
|
|
589
|
+
description: 'Validate schema-managed markdown files before write/edit (PreToolUse)',
|
|
590
|
+
handler: validateSchemaPre,
|
|
591
|
+
errorFallback: { type: 'allowTool' },
|
|
592
|
+
logPayload: (input) => ({ tool: input.tool_name, file: input.tool_input?.file_path }),
|
|
593
|
+
},
|
|
594
|
+
],
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
598
|
+
// Command Registration
|
|
599
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Register validation hook subcommands.
|
|
603
|
+
*/
|
|
604
|
+
export function register(parent: Command): void {
|
|
605
|
+
registerCategory(parent, category);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
609
|
+
// Daemon Handler Registration
|
|
610
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Register handlers for daemon mode.
|
|
614
|
+
*/
|
|
615
|
+
export function registerDaemonHandlers(register: RegisterFn): void {
|
|
616
|
+
registerCategoryForDaemon(category, register);
|
|
617
|
+
}
|