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,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Runner - Execute hooks with mock stdin and capture output
|
|
3
|
+
*
|
|
4
|
+
* Hooks communicate via stdin JSON and stdout JSON. This runner
|
|
5
|
+
* provides utilities for testing hook I/O contracts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { runCli, type RunOptions, type RunResult } from './cli-runner.js';
|
|
9
|
+
import type { TestFixture } from './fixture.js';
|
|
10
|
+
import type {
|
|
11
|
+
HookInput,
|
|
12
|
+
PreToolUseOutput,
|
|
13
|
+
PostToolUseOutput,
|
|
14
|
+
StopHookOutput,
|
|
15
|
+
PreCompactOutput,
|
|
16
|
+
} from '../../hooks/shared.js';
|
|
17
|
+
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
// Types
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
export type HookType = 'context' | 'validation' | 'lifecycle';
|
|
23
|
+
|
|
24
|
+
export interface HookResult extends RunResult {
|
|
25
|
+
/** Parsed hook output (PreToolUse, PostToolUse, Stop, or PreCompact format) */
|
|
26
|
+
hookOutput?: PreToolUseOutput | PostToolUseOutput | StopHookOutput | PreCompactOutput;
|
|
27
|
+
/** Whether the hook allowed the tool (for PreToolUse) */
|
|
28
|
+
allowed?: boolean;
|
|
29
|
+
/** Whether the hook denied the tool (for PreToolUse) */
|
|
30
|
+
denied?: boolean;
|
|
31
|
+
/** Whether the hook blocked the tool (for PostToolUse) */
|
|
32
|
+
blocked?: boolean;
|
|
33
|
+
/** The system message if present */
|
|
34
|
+
systemMessage?: string;
|
|
35
|
+
/** Denial reason if denied */
|
|
36
|
+
denialReason?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PreToolUseInput extends HookInput {
|
|
40
|
+
tool_name: string;
|
|
41
|
+
tool_input: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface PostToolUseInput extends HookInput {
|
|
45
|
+
tool_name: string;
|
|
46
|
+
tool_input: Record<string, unknown>;
|
|
47
|
+
tool_result: unknown;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
51
|
+
// Hook Execution
|
|
52
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Run a hook with the given input.
|
|
56
|
+
*/
|
|
57
|
+
export async function runHook(
|
|
58
|
+
hookType: HookType,
|
|
59
|
+
hookName: string,
|
|
60
|
+
input: HookInput,
|
|
61
|
+
fixture?: TestFixture,
|
|
62
|
+
options: Omit<RunOptions, 'stdin' | 'expectJson'> = {}
|
|
63
|
+
): Promise<HookResult> {
|
|
64
|
+
const args = ['hooks', hookType, hookName];
|
|
65
|
+
const stdin = JSON.stringify(input);
|
|
66
|
+
|
|
67
|
+
const runOptions: RunOptions = {
|
|
68
|
+
...options,
|
|
69
|
+
stdin,
|
|
70
|
+
expectJson: true,
|
|
71
|
+
cwd: fixture?.root ?? options.cwd,
|
|
72
|
+
env: {
|
|
73
|
+
...fixture?.env,
|
|
74
|
+
...options.env,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const result = await runCli(args, runOptions);
|
|
79
|
+
const hookResult: HookResult = { ...result };
|
|
80
|
+
|
|
81
|
+
// Parse hook output if present
|
|
82
|
+
if (result.json) {
|
|
83
|
+
hookResult.hookOutput = result.json as
|
|
84
|
+
| PreToolUseOutput
|
|
85
|
+
| PostToolUseOutput
|
|
86
|
+
| StopHookOutput
|
|
87
|
+
| PreCompactOutput;
|
|
88
|
+
|
|
89
|
+
// Extract common fields
|
|
90
|
+
const output = hookResult.hookOutput as Record<string, unknown>;
|
|
91
|
+
|
|
92
|
+
if ('systemMessage' in output) {
|
|
93
|
+
hookResult.systemMessage = output.systemMessage as string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// PreToolUse output parsing
|
|
97
|
+
if ('hookSpecificOutput' in output) {
|
|
98
|
+
const specific = output.hookSpecificOutput as Record<string, unknown>;
|
|
99
|
+
if (specific.permissionDecision === 'allow') {
|
|
100
|
+
hookResult.allowed = true;
|
|
101
|
+
hookResult.denied = false;
|
|
102
|
+
} else if (specific.permissionDecision === 'deny') {
|
|
103
|
+
hookResult.allowed = false;
|
|
104
|
+
hookResult.denied = true;
|
|
105
|
+
hookResult.denialReason = specific.permissionDecisionReason as string | undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// PostToolUse output parsing
|
|
110
|
+
if ('continue' in output) {
|
|
111
|
+
hookResult.blocked = output.continue === false;
|
|
112
|
+
} else if ('decision' in output && output.decision === 'block') {
|
|
113
|
+
hookResult.blocked = true;
|
|
114
|
+
}
|
|
115
|
+
} else if (result.success && !result.stdout.trim()) {
|
|
116
|
+
// Empty output = allow (for PreToolUse hooks)
|
|
117
|
+
hookResult.allowed = true;
|
|
118
|
+
hookResult.denied = false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return hookResult;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
125
|
+
// PreToolUse Hooks
|
|
126
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Run a context hook (PreToolUse).
|
|
130
|
+
*/
|
|
131
|
+
export async function runContextHook(
|
|
132
|
+
hookName: string,
|
|
133
|
+
input: PreToolUseInput,
|
|
134
|
+
fixture?: TestFixture,
|
|
135
|
+
options: Omit<RunOptions, 'stdin' | 'expectJson'> = {}
|
|
136
|
+
): Promise<HookResult> {
|
|
137
|
+
return runHook('context', hookName, input, fixture, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Test the edit-inject hook.
|
|
142
|
+
*/
|
|
143
|
+
export async function runEditInject(
|
|
144
|
+
filePath: string,
|
|
145
|
+
fixture?: TestFixture
|
|
146
|
+
): Promise<HookResult> {
|
|
147
|
+
return runContextHook(
|
|
148
|
+
'edit-inject',
|
|
149
|
+
{
|
|
150
|
+
tool_name: 'Edit',
|
|
151
|
+
tool_input: { file_path: filePath, old_string: '', new_string: '' },
|
|
152
|
+
},
|
|
153
|
+
fixture
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Test the read-enforcer hook.
|
|
159
|
+
*/
|
|
160
|
+
export async function runReadEnforcer(
|
|
161
|
+
filePath: string,
|
|
162
|
+
fixture?: TestFixture,
|
|
163
|
+
options: { offset?: number; limit?: number } = {}
|
|
164
|
+
): Promise<HookResult> {
|
|
165
|
+
return runContextHook(
|
|
166
|
+
'read-enforcer',
|
|
167
|
+
{
|
|
168
|
+
tool_name: 'Read',
|
|
169
|
+
tool_input: { file_path: filePath, ...options },
|
|
170
|
+
},
|
|
171
|
+
fixture,
|
|
172
|
+
{ timeout: 20000 } // read-enforcer has longer timeout
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Test the search-router hook.
|
|
178
|
+
*/
|
|
179
|
+
export async function runSearchRouter(
|
|
180
|
+
pattern: string,
|
|
181
|
+
fixture?: TestFixture
|
|
182
|
+
): Promise<HookResult> {
|
|
183
|
+
return runContextHook(
|
|
184
|
+
'search-router',
|
|
185
|
+
{
|
|
186
|
+
tool_name: 'Grep',
|
|
187
|
+
tool_input: { pattern },
|
|
188
|
+
},
|
|
189
|
+
fixture
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Test the tldr-inject hook for Task tool.
|
|
195
|
+
*/
|
|
196
|
+
export async function runTldrInject(
|
|
197
|
+
prompt: string,
|
|
198
|
+
fixture?: TestFixture
|
|
199
|
+
): Promise<HookResult> {
|
|
200
|
+
return runContextHook(
|
|
201
|
+
'tldr-inject',
|
|
202
|
+
{
|
|
203
|
+
tool_name: 'Task',
|
|
204
|
+
tool_input: { prompt, description: 'Test task' },
|
|
205
|
+
},
|
|
206
|
+
fixture
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
211
|
+
// Validation Hooks
|
|
212
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Run a validation hook (PreWrite or PostWrite).
|
|
216
|
+
*/
|
|
217
|
+
export async function runValidationHook(
|
|
218
|
+
hookName: string,
|
|
219
|
+
input: PreToolUseInput | PostToolUseInput,
|
|
220
|
+
fixture?: TestFixture
|
|
221
|
+
): Promise<HookResult> {
|
|
222
|
+
return runHook('validation', hookName, input, fixture);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Test the schema-check hook (PreWrite validation).
|
|
227
|
+
*/
|
|
228
|
+
export async function runSchemaCheck(
|
|
229
|
+
filePath: string,
|
|
230
|
+
content: string,
|
|
231
|
+
fixture?: TestFixture
|
|
232
|
+
): Promise<HookResult> {
|
|
233
|
+
return runValidationHook(
|
|
234
|
+
'schema-check',
|
|
235
|
+
{
|
|
236
|
+
tool_name: 'Write',
|
|
237
|
+
tool_input: { file_path: filePath, content },
|
|
238
|
+
},
|
|
239
|
+
fixture
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
244
|
+
// Lifecycle Hooks
|
|
245
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Run a lifecycle hook (Stop, PreCompact).
|
|
249
|
+
*/
|
|
250
|
+
export async function runLifecycleHook(
|
|
251
|
+
hookName: string,
|
|
252
|
+
input: HookInput,
|
|
253
|
+
fixture?: TestFixture
|
|
254
|
+
): Promise<HookResult> {
|
|
255
|
+
return runHook('lifecycle', hookName, input, fixture);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Test the stop hook.
|
|
260
|
+
*/
|
|
261
|
+
export async function runStopHook(
|
|
262
|
+
sessionId: string,
|
|
263
|
+
fixture?: TestFixture
|
|
264
|
+
): Promise<HookResult> {
|
|
265
|
+
return runLifecycleHook(
|
|
266
|
+
'stop',
|
|
267
|
+
{
|
|
268
|
+
session_id: sessionId,
|
|
269
|
+
stop_hook_active: true,
|
|
270
|
+
},
|
|
271
|
+
fixture
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
276
|
+
// Hook Contract Testing
|
|
277
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
export interface HookContract {
|
|
280
|
+
name: string;
|
|
281
|
+
hookType: HookType;
|
|
282
|
+
hookName: string;
|
|
283
|
+
input: HookInput;
|
|
284
|
+
expect: {
|
|
285
|
+
success?: boolean;
|
|
286
|
+
allowed?: boolean;
|
|
287
|
+
denied?: boolean;
|
|
288
|
+
blocked?: boolean;
|
|
289
|
+
hasSystemMessage?: boolean;
|
|
290
|
+
systemMessageContains?: string[];
|
|
291
|
+
denialReasonContains?: string;
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface ContractResult {
|
|
296
|
+
contract: HookContract;
|
|
297
|
+
result: HookResult;
|
|
298
|
+
passed: boolean;
|
|
299
|
+
failures: string[];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Test a hook against its contract.
|
|
304
|
+
*/
|
|
305
|
+
export async function testHookContract(
|
|
306
|
+
contract: HookContract,
|
|
307
|
+
fixture?: TestFixture
|
|
308
|
+
): Promise<ContractResult> {
|
|
309
|
+
const result = await runHook(
|
|
310
|
+
contract.hookType,
|
|
311
|
+
contract.hookName,
|
|
312
|
+
contract.input,
|
|
313
|
+
fixture
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const failures: string[] = [];
|
|
317
|
+
const { expect: exp } = contract;
|
|
318
|
+
|
|
319
|
+
if (exp.success !== undefined && result.success !== exp.success) {
|
|
320
|
+
failures.push(`Expected success=${exp.success}, got ${result.success}`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (exp.allowed !== undefined && result.allowed !== exp.allowed) {
|
|
324
|
+
failures.push(`Expected allowed=${exp.allowed}, got ${result.allowed}`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (exp.denied !== undefined && result.denied !== exp.denied) {
|
|
328
|
+
failures.push(`Expected denied=${exp.denied}, got ${result.denied}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (exp.blocked !== undefined && result.blocked !== exp.blocked) {
|
|
332
|
+
failures.push(`Expected blocked=${exp.blocked}, got ${result.blocked}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (exp.hasSystemMessage !== undefined) {
|
|
336
|
+
const hasMsg = !!result.systemMessage;
|
|
337
|
+
if (hasMsg !== exp.hasSystemMessage) {
|
|
338
|
+
failures.push(`Expected hasSystemMessage=${exp.hasSystemMessage}, got ${hasMsg}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (exp.systemMessageContains && result.systemMessage) {
|
|
343
|
+
for (const expected of exp.systemMessageContains) {
|
|
344
|
+
if (!result.systemMessage.includes(expected)) {
|
|
345
|
+
failures.push(`Expected systemMessage to contain "${expected}"`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (exp.denialReasonContains && result.denialReason) {
|
|
351
|
+
if (!result.denialReason.includes(exp.denialReasonContains)) {
|
|
352
|
+
failures.push(`Expected denialReason to contain "${exp.denialReasonContains}"`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
contract,
|
|
358
|
+
result,
|
|
359
|
+
passed: failures.length === 0,
|
|
360
|
+
failures,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Test multiple hook contracts.
|
|
366
|
+
*/
|
|
367
|
+
export async function testHookContracts(
|
|
368
|
+
contracts: HookContract[],
|
|
369
|
+
fixture?: TestFixture
|
|
370
|
+
): Promise<ContractResult[]> {
|
|
371
|
+
const results: ContractResult[] = [];
|
|
372
|
+
|
|
373
|
+
for (const contract of contracts) {
|
|
374
|
+
results.push(await testHookContract(contract, fixture));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return results;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
381
|
+
// Debug Helpers
|
|
382
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Print a hook result for debugging.
|
|
386
|
+
*/
|
|
387
|
+
export function debugHookResult(result: HookResult, label?: string): void {
|
|
388
|
+
console.log('\n' + '='.repeat(60));
|
|
389
|
+
if (label) {
|
|
390
|
+
console.log(`HOOK DEBUG: ${label}`);
|
|
391
|
+
console.log('-'.repeat(60));
|
|
392
|
+
}
|
|
393
|
+
console.log(`Exit Code: ${result.exitCode} (${result.success ? 'success' : 'failure'})`);
|
|
394
|
+
console.log(`Allowed: ${result.allowed}, Denied: ${result.denied}, Blocked: ${result.blocked}`);
|
|
395
|
+
if (result.denialReason) {
|
|
396
|
+
console.log(`Denial Reason: ${result.denialReason}`);
|
|
397
|
+
}
|
|
398
|
+
if (result.systemMessage) {
|
|
399
|
+
console.log(`\n--- SYSTEM MESSAGE ---`);
|
|
400
|
+
console.log(result.systemMessage.substring(0, 500) + (result.systemMessage.length > 500 ? '...' : ''));
|
|
401
|
+
}
|
|
402
|
+
if (result.hookOutput) {
|
|
403
|
+
console.log(`\n--- RAW HOOK OUTPUT ---`);
|
|
404
|
+
console.log(JSON.stringify(result.hookOutput, null, 2).substring(0, 1000));
|
|
405
|
+
}
|
|
406
|
+
if (result.stderr) {
|
|
407
|
+
console.log(`\n--- STDERR ---`);
|
|
408
|
+
console.log(result.stderr);
|
|
409
|
+
}
|
|
410
|
+
console.log('='.repeat(60) + '\n');
|
|
411
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness Test Utilities - Main Export
|
|
3
|
+
*
|
|
4
|
+
* Provides everything needed for headless E2E testing of the ah CLI.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import {
|
|
9
|
+
* createFixture,
|
|
10
|
+
* createSpecFixture,
|
|
11
|
+
* runInFixture,
|
|
12
|
+
* runHook,
|
|
13
|
+
* assertSuccess,
|
|
14
|
+
* assertHookAllowed,
|
|
15
|
+
* } from '../harness/index.js';
|
|
16
|
+
*
|
|
17
|
+
* describe('My E2E Tests', () => {
|
|
18
|
+
* let fixture: TestFixture;
|
|
19
|
+
*
|
|
20
|
+
* beforeAll(() => {
|
|
21
|
+
* fixture = createSpecFixture('test-spec');
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* afterAll(() => {
|
|
25
|
+
* fixture.cleanup();
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* it('runs a command', async () => {
|
|
29
|
+
* const result = await runInFixture(fixture, ['validate', 'file.md']);
|
|
30
|
+
* assertSuccess(result);
|
|
31
|
+
* });
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// Fixture creation and management
|
|
37
|
+
export {
|
|
38
|
+
createFixture,
|
|
39
|
+
createSpecFixture,
|
|
40
|
+
createMilestoneFixture,
|
|
41
|
+
createMultiSpecFixture,
|
|
42
|
+
getPooledFixture,
|
|
43
|
+
cleanupPool,
|
|
44
|
+
PROMPT_TEMPLATE,
|
|
45
|
+
ALIGNMENT_TEMPLATE,
|
|
46
|
+
SPEC_TEMPLATE,
|
|
47
|
+
PYTHON_SAMPLE,
|
|
48
|
+
TYPESCRIPT_SAMPLE,
|
|
49
|
+
type TestFixture,
|
|
50
|
+
type FixtureOptions,
|
|
51
|
+
} from './fixture.js';
|
|
52
|
+
|
|
53
|
+
// CLI runner
|
|
54
|
+
export {
|
|
55
|
+
runCli,
|
|
56
|
+
runInFixture,
|
|
57
|
+
runKnowledgeSearch,
|
|
58
|
+
runValidate,
|
|
59
|
+
runCodeSearch,
|
|
60
|
+
runToolsList,
|
|
61
|
+
runSpecsList,
|
|
62
|
+
runBatch,
|
|
63
|
+
debugResult,
|
|
64
|
+
type RunOptions,
|
|
65
|
+
type RunResult,
|
|
66
|
+
type BatchCommand,
|
|
67
|
+
type BatchResult,
|
|
68
|
+
} from './cli-runner.js';
|
|
69
|
+
|
|
70
|
+
// Hook runner
|
|
71
|
+
export {
|
|
72
|
+
runHook,
|
|
73
|
+
runContextHook,
|
|
74
|
+
runEditInject,
|
|
75
|
+
runReadEnforcer,
|
|
76
|
+
runSearchRouter,
|
|
77
|
+
runTldrInject,
|
|
78
|
+
runValidationHook,
|
|
79
|
+
runSchemaCheck,
|
|
80
|
+
runLifecycleHook,
|
|
81
|
+
runStopHook,
|
|
82
|
+
testHookContract,
|
|
83
|
+
testHookContracts,
|
|
84
|
+
debugHookResult,
|
|
85
|
+
type HookType,
|
|
86
|
+
type HookResult,
|
|
87
|
+
type PreToolUseInput,
|
|
88
|
+
type PostToolUseInput,
|
|
89
|
+
type HookContract,
|
|
90
|
+
type ContractResult,
|
|
91
|
+
} from './hook-runner.js';
|
|
92
|
+
|
|
93
|
+
// Assertions
|
|
94
|
+
export {
|
|
95
|
+
// CLI assertions
|
|
96
|
+
assertSuccess,
|
|
97
|
+
assertFailure,
|
|
98
|
+
assertStdoutContains,
|
|
99
|
+
assertStderrContains,
|
|
100
|
+
assertStdoutMatches,
|
|
101
|
+
assertJsonOutput,
|
|
102
|
+
assertTimedWithin,
|
|
103
|
+
// Hook assertions
|
|
104
|
+
assertHookAllowed,
|
|
105
|
+
assertHookDenied,
|
|
106
|
+
assertHookBlocked,
|
|
107
|
+
assertHookInjectedContext,
|
|
108
|
+
assertHookContextContains,
|
|
109
|
+
assertDenialReasonContains,
|
|
110
|
+
// Fixture assertions
|
|
111
|
+
assertFileExists,
|
|
112
|
+
assertFileNotExists,
|
|
113
|
+
assertFileContains,
|
|
114
|
+
assertFileMatches,
|
|
115
|
+
assertValidFrontmatter,
|
|
116
|
+
// Git assertions
|
|
117
|
+
assertGitTracked,
|
|
118
|
+
assertGitDirty,
|
|
119
|
+
// Composite assertions
|
|
120
|
+
assertWorkflowSuccess,
|
|
121
|
+
assertContractsPassed,
|
|
122
|
+
} from './assertions.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* All Hands CLI - Main Entry Point
|
|
4
|
+
*
|
|
5
|
+
* Running `ah` with no command launches the TUI.
|
|
6
|
+
* Commands are auto-discovered from the commands/ directory.
|
|
7
|
+
* Each command module exports a `register` function.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
import { discoverAndRegister } from './commands/index.js';
|
|
12
|
+
import { launchTUI } from './commands/tui.js';
|
|
13
|
+
|
|
14
|
+
async function main(): Promise<void> {
|
|
15
|
+
const program = new Command();
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.name('ah')
|
|
19
|
+
.description('All Hands - Agentic harness for model-first software development')
|
|
20
|
+
.version('0.1.0')
|
|
21
|
+
.option('-s, --use-spec <spec>', 'Spec to use for TUI (defaults to active)')
|
|
22
|
+
.action(async (options: { useSpec?: string }) => {
|
|
23
|
+
// Default action when no subcommand - launch TUI
|
|
24
|
+
await launchTUI({ spec: options.useSpec });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Auto-discover and register all commands
|
|
28
|
+
await discoverAndRegister(program);
|
|
29
|
+
|
|
30
|
+
await program.parseAsync();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
main().catch((e) => {
|
|
34
|
+
console.error(e);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|