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,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Tests - Hook Contracts
|
|
3
|
+
*
|
|
4
|
+
* Tests hook I/O contracts to ensure hooks behave correctly
|
|
5
|
+
* with various inputs. These tests run hooks as separate processes
|
|
6
|
+
* with mock stdin and validate their stdout JSON.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
10
|
+
import {
|
|
11
|
+
createFixture,
|
|
12
|
+
createMilestoneFixture,
|
|
13
|
+
runContextHook,
|
|
14
|
+
runEditInject,
|
|
15
|
+
runReadEnforcer,
|
|
16
|
+
runSearchRouter,
|
|
17
|
+
runValidationHook,
|
|
18
|
+
runHook,
|
|
19
|
+
testHookContracts,
|
|
20
|
+
assertHookAllowed,
|
|
21
|
+
assertHookDenied,
|
|
22
|
+
assertHookInjectedContext,
|
|
23
|
+
assertHookContextContains,
|
|
24
|
+
assertContractsPassed,
|
|
25
|
+
type TestFixture,
|
|
26
|
+
type HookContract,
|
|
27
|
+
PYTHON_SAMPLE,
|
|
28
|
+
TYPESCRIPT_SAMPLE,
|
|
29
|
+
} from '../harness/index.js';
|
|
30
|
+
|
|
31
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
32
|
+
// Test Setup
|
|
33
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
34
|
+
|
|
35
|
+
describe('Hook Contracts E2E', () => {
|
|
36
|
+
let fixture: TestFixture;
|
|
37
|
+
|
|
38
|
+
beforeAll(() => {
|
|
39
|
+
fixture = createFixture({
|
|
40
|
+
name: 'hook-contracts-test',
|
|
41
|
+
files: {
|
|
42
|
+
'src/sample.py': PYTHON_SAMPLE,
|
|
43
|
+
'src/sample.ts': TYPESCRIPT_SAMPLE,
|
|
44
|
+
'src/small.py': '# Small file\nx = 1\n',
|
|
45
|
+
'.planning/test/prompts/01.md': `---
|
|
46
|
+
status: pending
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
# Task 1
|
|
50
|
+
`,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
afterAll(() => {
|
|
56
|
+
fixture.cleanup();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
60
|
+
// Context Hooks - PreToolUse
|
|
61
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
62
|
+
|
|
63
|
+
describe('context hooks', () => {
|
|
64
|
+
describe('edit-inject', () => {
|
|
65
|
+
it('allows Edit tool and may inject context for Python files', async () => {
|
|
66
|
+
const result = await runEditInject(
|
|
67
|
+
`${fixture.root}/src/sample.py`,
|
|
68
|
+
fixture
|
|
69
|
+
);
|
|
70
|
+
// Hook should allow the edit
|
|
71
|
+
assertHookAllowed(result);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('allows Edit tool for TypeScript files', async () => {
|
|
75
|
+
const result = await runEditInject(
|
|
76
|
+
`${fixture.root}/src/sample.ts`,
|
|
77
|
+
fixture
|
|
78
|
+
);
|
|
79
|
+
assertHookAllowed(result);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('allows Edit tool for non-existent files', async () => {
|
|
83
|
+
const result = await runEditInject(
|
|
84
|
+
`${fixture.root}/src/new-file.py`,
|
|
85
|
+
fixture
|
|
86
|
+
);
|
|
87
|
+
// Should allow creating new files
|
|
88
|
+
assertHookAllowed(result);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('search-router', () => {
|
|
93
|
+
it('allows Grep with literal pattern', async () => {
|
|
94
|
+
const result = await runSearchRouter('hello', fixture);
|
|
95
|
+
assertHookAllowed(result);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('allows Grep with regex pattern', async () => {
|
|
99
|
+
const result = await runSearchRouter('def\\s+\\w+', fixture);
|
|
100
|
+
assertHookAllowed(result);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('allows Grep with function search pattern', async () => {
|
|
104
|
+
const result = await runSearchRouter('function add', fixture);
|
|
105
|
+
assertHookAllowed(result);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('read-enforcer', () => {
|
|
110
|
+
it('allows Read for small files without interception', async () => {
|
|
111
|
+
const result = await runReadEnforcer(
|
|
112
|
+
`${fixture.root}/src/small.py`,
|
|
113
|
+
fixture
|
|
114
|
+
);
|
|
115
|
+
assertHookAllowed(result);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('allows Read with offset/limit (explicit range)', async () => {
|
|
119
|
+
const result = await runReadEnforcer(
|
|
120
|
+
`${fixture.root}/src/sample.py`,
|
|
121
|
+
fixture,
|
|
122
|
+
{ offset: 1, limit: 10 }
|
|
123
|
+
);
|
|
124
|
+
// Offset/limit reads are allowed without interception
|
|
125
|
+
assertHookAllowed(result);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('handles Read for non-existent file', async () => {
|
|
129
|
+
const result = await runReadEnforcer(
|
|
130
|
+
`${fixture.root}/src/nonexistent.py`,
|
|
131
|
+
fixture
|
|
132
|
+
);
|
|
133
|
+
// Should allow (file not found error comes later)
|
|
134
|
+
assertHookAllowed(result);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('tldr-inject', () => {
|
|
139
|
+
it('handles Task tool for debugging intent', async () => {
|
|
140
|
+
const result = await runContextHook(
|
|
141
|
+
'tldr-inject',
|
|
142
|
+
{
|
|
143
|
+
tool_name: 'Task',
|
|
144
|
+
tool_input: {
|
|
145
|
+
prompt: 'Debug why the function fails',
|
|
146
|
+
description: 'Investigate bug',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
fixture
|
|
150
|
+
);
|
|
151
|
+
// Should allow, may inject context if TLDR available
|
|
152
|
+
assertHookAllowed(result);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('handles Task tool for dataflow intent', async () => {
|
|
156
|
+
const result = await runContextHook(
|
|
157
|
+
'tldr-inject',
|
|
158
|
+
{
|
|
159
|
+
tool_name: 'Task',
|
|
160
|
+
tool_input: {
|
|
161
|
+
prompt: 'Trace where variable x comes from',
|
|
162
|
+
description: 'Dataflow analysis',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
fixture
|
|
166
|
+
);
|
|
167
|
+
assertHookAllowed(result);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
173
|
+
// Validation Hooks (schema-pre hook)
|
|
174
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
175
|
+
|
|
176
|
+
describe('validation hooks', () => {
|
|
177
|
+
describe('schema-pre (PreWrite validation)', () => {
|
|
178
|
+
it('runs without crashing on valid prompt', async () => {
|
|
179
|
+
const result = await runHook(
|
|
180
|
+
'validation',
|
|
181
|
+
'schema-pre',
|
|
182
|
+
{
|
|
183
|
+
tool_name: 'Write',
|
|
184
|
+
tool_input: {
|
|
185
|
+
file_path: `${fixture.root}/.planning/test/prompts/02.md`,
|
|
186
|
+
content: `---
|
|
187
|
+
number: 2
|
|
188
|
+
title: New Task
|
|
189
|
+
type: planned
|
|
190
|
+
planning_session: 1
|
|
191
|
+
status: pending
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
# Tasks
|
|
195
|
+
|
|
196
|
+
- New Task
|
|
197
|
+
|
|
198
|
+
# Acceptance Criteria
|
|
199
|
+
|
|
200
|
+
- Works
|
|
201
|
+
`,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
fixture
|
|
205
|
+
);
|
|
206
|
+
// Should complete without crashing
|
|
207
|
+
expect(result.exitCode).toBeDefined();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('runs without crashing on non-schema file', async () => {
|
|
211
|
+
const result = await runHook(
|
|
212
|
+
'validation',
|
|
213
|
+
'schema-pre',
|
|
214
|
+
{
|
|
215
|
+
tool_name: 'Write',
|
|
216
|
+
tool_input: {
|
|
217
|
+
file_path: `${fixture.root}/src/new-file.py`,
|
|
218
|
+
content: '# New Python file\n',
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
fixture
|
|
222
|
+
);
|
|
223
|
+
expect(result.exitCode).toBeDefined();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
229
|
+
// Contract Testing
|
|
230
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
231
|
+
|
|
232
|
+
describe('contract compliance', () => {
|
|
233
|
+
const contracts: HookContract[] = [
|
|
234
|
+
{
|
|
235
|
+
name: 'edit-inject allows Python edit',
|
|
236
|
+
hookType: 'context',
|
|
237
|
+
hookName: 'edit-inject',
|
|
238
|
+
input: {
|
|
239
|
+
tool_name: 'Edit',
|
|
240
|
+
tool_input: { file_path: '/tmp/test.py', old_string: '', new_string: '' },
|
|
241
|
+
},
|
|
242
|
+
expect: {
|
|
243
|
+
success: true,
|
|
244
|
+
allowed: true,
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'search-router allows grep',
|
|
249
|
+
hookType: 'context',
|
|
250
|
+
hookName: 'search-router',
|
|
251
|
+
input: {
|
|
252
|
+
tool_name: 'Grep',
|
|
253
|
+
tool_input: { pattern: 'test' },
|
|
254
|
+
},
|
|
255
|
+
expect: {
|
|
256
|
+
success: true,
|
|
257
|
+
allowed: true,
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'read-enforcer allows small file read',
|
|
262
|
+
hookType: 'context',
|
|
263
|
+
hookName: 'read-enforcer',
|
|
264
|
+
input: {
|
|
265
|
+
tool_name: 'Read',
|
|
266
|
+
tool_input: { file_path: '/tmp/small.txt' },
|
|
267
|
+
},
|
|
268
|
+
expect: {
|
|
269
|
+
success: true,
|
|
270
|
+
allowed: true,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
it('all core contracts pass', async () => {
|
|
276
|
+
const results = await testHookContracts(contracts, fixture);
|
|
277
|
+
assertContractsPassed(results);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
282
|
+
// Edge Cases
|
|
283
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
284
|
+
|
|
285
|
+
describe('edge cases', () => {
|
|
286
|
+
it('handles empty hook input gracefully', async () => {
|
|
287
|
+
const result = await runContextHook(
|
|
288
|
+
'edit-inject',
|
|
289
|
+
{
|
|
290
|
+
tool_name: 'Edit',
|
|
291
|
+
tool_input: {},
|
|
292
|
+
},
|
|
293
|
+
fixture
|
|
294
|
+
);
|
|
295
|
+
// Should not crash
|
|
296
|
+
expect(result.exitCode).toBeDefined();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('handles missing tool_name gracefully', async () => {
|
|
300
|
+
const result = await runContextHook(
|
|
301
|
+
'edit-inject',
|
|
302
|
+
{
|
|
303
|
+
tool_input: { file_path: '/tmp/test.py' },
|
|
304
|
+
} as any,
|
|
305
|
+
fixture
|
|
306
|
+
);
|
|
307
|
+
expect(result.exitCode).toBeDefined();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('handles very long file paths', async () => {
|
|
311
|
+
const longPath = '/tmp/' + 'a'.repeat(200) + '.py';
|
|
312
|
+
const result = await runEditInject(longPath, fixture);
|
|
313
|
+
expect(result.exitCode).toBeDefined();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('handles unicode in patterns', async () => {
|
|
317
|
+
const result = await runSearchRouter('å½ę°.*å®ä¹', fixture);
|
|
318
|
+
expect(result.exitCode).toBeDefined();
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
324
|
+
// Hook Chain Tests
|
|
325
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
326
|
+
|
|
327
|
+
describe('Hook Chain E2E', () => {
|
|
328
|
+
let fixture: TestFixture;
|
|
329
|
+
|
|
330
|
+
beforeAll(() => {
|
|
331
|
+
fixture = createMilestoneFixture('hook-chain-test', 2);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
afterAll(() => {
|
|
335
|
+
fixture.cleanup();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe('search ā edit chain', () => {
|
|
339
|
+
it('search-router then edit-inject work in sequence', async () => {
|
|
340
|
+
// First, search for something
|
|
341
|
+
const searchResult = await runSearchRouter('Calculator', fixture);
|
|
342
|
+
assertHookAllowed(searchResult);
|
|
343
|
+
|
|
344
|
+
// Then, edit the file
|
|
345
|
+
const editResult = await runEditInject(
|
|
346
|
+
`${fixture.root}/src/sample.py`,
|
|
347
|
+
fixture
|
|
348
|
+
);
|
|
349
|
+
assertHookAllowed(editResult);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe('read ā edit chain', () => {
|
|
354
|
+
it('read-enforcer then edit-inject work in sequence', async () => {
|
|
355
|
+
// Read a file
|
|
356
|
+
const readResult = await runReadEnforcer(
|
|
357
|
+
`${fixture.root}/src/sample.py`,
|
|
358
|
+
fixture
|
|
359
|
+
);
|
|
360
|
+
assertHookAllowed(readResult);
|
|
361
|
+
|
|
362
|
+
// Edit the file
|
|
363
|
+
const editResult = await runEditInject(
|
|
364
|
+
`${fixture.root}/src/sample.py`,
|
|
365
|
+
fixture
|
|
366
|
+
);
|
|
367
|
+
assertHookAllowed(editResult);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
373
|
+
// Performance Tests
|
|
374
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
375
|
+
|
|
376
|
+
describe('Hook Performance E2E', () => {
|
|
377
|
+
let fixture: TestFixture;
|
|
378
|
+
|
|
379
|
+
beforeAll(() => {
|
|
380
|
+
fixture = createFixture({ name: 'hook-perf-test' });
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
afterAll(() => {
|
|
384
|
+
fixture.cleanup();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('edit-inject completes within 3 seconds', async () => {
|
|
388
|
+
const result = await runEditInject(`${fixture.root}/src/test.py`, fixture);
|
|
389
|
+
expect(result.duration).toBeLessThan(3000);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('search-router completes within 2 seconds', async () => {
|
|
393
|
+
const result = await runSearchRouter('test', fixture);
|
|
394
|
+
expect(result.duration).toBeLessThan(2000);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('schema-pre completes within 2 seconds', async () => {
|
|
398
|
+
const result = await runHook(
|
|
399
|
+
'validation',
|
|
400
|
+
'schema-pre',
|
|
401
|
+
{
|
|
402
|
+
tool_name: 'Write',
|
|
403
|
+
tool_input: {
|
|
404
|
+
file_path: `${fixture.root}/.planning/test/prompts/01.md`,
|
|
405
|
+
content: `---
|
|
406
|
+
number: 1
|
|
407
|
+
title: Test
|
|
408
|
+
type: planned
|
|
409
|
+
planning_session: 1
|
|
410
|
+
status: pending
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
# Tasks
|
|
414
|
+
|
|
415
|
+
- Task
|
|
416
|
+
|
|
417
|
+
# Acceptance Criteria
|
|
418
|
+
|
|
419
|
+
- Works
|
|
420
|
+
`,
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
fixture
|
|
424
|
+
);
|
|
425
|
+
expect(result.duration).toBeLessThan(2000);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* New Initiative Routing Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Validates:
|
|
5
|
+
* - Unified scoping flow routing (all spec types ā IDEATION_SCOPING.md)
|
|
6
|
+
* - WORKFLOW_DOMAIN_PATH resolution for each spec type
|
|
7
|
+
* - buildActionItems() always-visible guarantee for new-initiative and initiative-steering
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect } from 'vitest';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
|
+
import type { SpecType } from '../../lib/specs.js';
|
|
14
|
+
import { UNIFIED_SCOPING_FLOW } from '../../commands/tui.js';
|
|
15
|
+
import { buildActionItems, type ToggleState } from '../../tui/actions.js';
|
|
16
|
+
import { getFlowsDirectory } from '../../lib/flows.js';
|
|
17
|
+
|
|
18
|
+
/** Resolve .allhands/workflows/ relative to .allhands/flows/ */
|
|
19
|
+
const workflowsDir = join(dirname(getFlowsDirectory()), 'workflows');
|
|
20
|
+
|
|
21
|
+
const ALL_SPEC_TYPES: SpecType[] = [
|
|
22
|
+
'milestone',
|
|
23
|
+
'investigation',
|
|
24
|
+
'optimization',
|
|
25
|
+
'refactor',
|
|
26
|
+
'documentation',
|
|
27
|
+
'triage',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// āāā Task 1: Unified scoping flow routing āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
31
|
+
|
|
32
|
+
describe('Unified scoping flow routing', () => {
|
|
33
|
+
it('UNIFIED_SCOPING_FLOW is IDEATION_SCOPING.md', () => {
|
|
34
|
+
expect(UNIFIED_SCOPING_FLOW).toBe('IDEATION_SCOPING.md');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('unified scoping flow file exists on disk', () => {
|
|
38
|
+
const flowsDir = getFlowsDirectory();
|
|
39
|
+
const fullPath = join(flowsDir, UNIFIED_SCOPING_FLOW);
|
|
40
|
+
expect(existsSync(fullPath)).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it.each(ALL_SPEC_TYPES)(
|
|
44
|
+
'%s has a corresponding workflow domain config file on disk',
|
|
45
|
+
(specType) => {
|
|
46
|
+
const domainPath = join(workflowsDir, `${specType}.md`);
|
|
47
|
+
expect(existsSync(domainPath)).toBe(true);
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// āāā Task 2: WORKFLOW_DOMAIN_PATH resolution per spec type āāāāāāāāāāāāāāāāāāā
|
|
53
|
+
|
|
54
|
+
describe('WORKFLOW_DOMAIN_PATH resolution', () => {
|
|
55
|
+
it.each(ALL_SPEC_TYPES)(
|
|
56
|
+
'%s workflow domain config exists at .allhands/workflows/%s.md',
|
|
57
|
+
(specType) => {
|
|
58
|
+
const domainPath = join(workflowsDir, `${specType}.md`);
|
|
59
|
+
expect(existsSync(domainPath)).toBe(true);
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
it('initiative-steering action is present in buildActionItems', () => {
|
|
64
|
+
const items = buildActionItems({
|
|
65
|
+
loopEnabled: false,
|
|
66
|
+
parallelEnabled: false,
|
|
67
|
+
prActionState: 'create-pr',
|
|
68
|
+
});
|
|
69
|
+
const steering = items.find((item) => item.id === 'initiative-steering');
|
|
70
|
+
expect(steering).toBeDefined();
|
|
71
|
+
expect(steering!.type).toBe('action');
|
|
72
|
+
expect(steering!.key).toBe('=');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// āāā Task 3: buildActionItems() always-visible guarantee āāāāāāāāāāāāāāāāāāāāā
|
|
77
|
+
|
|
78
|
+
describe('buildActionItems always-visible guarantee', () => {
|
|
79
|
+
const prActionStates = ['create-pr', 'awaiting-review', 'rerun-pr-review'] as const;
|
|
80
|
+
|
|
81
|
+
const toggleCombinations: ToggleState[] = [];
|
|
82
|
+
for (const loop of [true, false]) {
|
|
83
|
+
for (const parallel of [true, false]) {
|
|
84
|
+
for (const pr of prActionStates) {
|
|
85
|
+
toggleCombinations.push({
|
|
86
|
+
loopEnabled: loop,
|
|
87
|
+
parallelEnabled: parallel,
|
|
88
|
+
prActionState: pr,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
it.each(toggleCombinations)(
|
|
95
|
+
'new-initiative is present with loop=$loopEnabled, parallel=$parallelEnabled, pr=$prActionState',
|
|
96
|
+
(toggleState) => {
|
|
97
|
+
const items = buildActionItems(toggleState);
|
|
98
|
+
const newInitiative = items.find((item) => item.id === 'new-initiative');
|
|
99
|
+
expect(newInitiative).toBeDefined();
|
|
100
|
+
expect(newInitiative!.type).toBe('action');
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
it.each(toggleCombinations)(
|
|
105
|
+
'initiative-steering is present with loop=$loopEnabled, parallel=$parallelEnabled, pr=$prActionState',
|
|
106
|
+
(toggleState) => {
|
|
107
|
+
const items = buildActionItems(toggleState);
|
|
108
|
+
const steering = items.find((item) => item.id === 'initiative-steering');
|
|
109
|
+
expect(steering).toBeDefined();
|
|
110
|
+
expect(steering!.type).toBe('action');
|
|
111
|
+
expect(steering!.key).toBe('=');
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
it('no action items have hidden or disabled properties', () => {
|
|
116
|
+
const items = buildActionItems({
|
|
117
|
+
loopEnabled: false,
|
|
118
|
+
parallelEnabled: false,
|
|
119
|
+
prActionState: 'create-pr',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
for (const item of items) {
|
|
123
|
+
// ActionItem interface at actions.ts:21 has: id, label, key?, type, highlight?, checked?
|
|
124
|
+
// No hidden or disabled fields exist
|
|
125
|
+
expect(item).not.toHaveProperty('hidden');
|
|
126
|
+
expect(item).not.toHaveProperty('disabled');
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('returns consistent action count across all toggle states', () => {
|
|
131
|
+
const counts = toggleCombinations.map(
|
|
132
|
+
(ts) => buildActionItems(ts).length
|
|
133
|
+
);
|
|
134
|
+
const uniqueCounts = new Set(counts);
|
|
135
|
+
expect(uniqueCounts.size).toBe(1);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* E2E Test Runner
|
|
4
|
+
*
|
|
5
|
+
* Standalone script to run E2E tests with proper setup.
|
|
6
|
+
* Can be run directly: npx tsx src/__tests__/e2e/run-e2e.ts
|
|
7
|
+
*
|
|
8
|
+
* Options:
|
|
9
|
+
* --verbose Show detailed output
|
|
10
|
+
* --filter Filter tests by name pattern
|
|
11
|
+
* --list List available test suites without running
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { spawn } from 'child_process';
|
|
15
|
+
import { join, dirname } from 'path';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
import { readdirSync } from 'fs';
|
|
18
|
+
|
|
19
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const harnessRoot = join(__dirname, '..', '..', '..');
|
|
21
|
+
|
|
22
|
+
interface RunOptions {
|
|
23
|
+
verbose: boolean;
|
|
24
|
+
filter?: string;
|
|
25
|
+
list: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseArgs(): RunOptions {
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
return {
|
|
31
|
+
verbose: args.includes('--verbose') || args.includes('-v'),
|
|
32
|
+
filter: args.find((a) => a.startsWith('--filter='))?.split('=')[1],
|
|
33
|
+
list: args.includes('--list'),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function listTestSuites(): void {
|
|
38
|
+
console.log('\nAvailable E2E Test Suites:\n');
|
|
39
|
+
|
|
40
|
+
const e2eDir = __dirname;
|
|
41
|
+
const files = readdirSync(e2eDir).filter((f) => f.endsWith('.test.ts'));
|
|
42
|
+
|
|
43
|
+
for (const file of files) {
|
|
44
|
+
console.log(` ⢠${file.replace('.test.ts', '')}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log('\nRun with: npm run test:e2e');
|
|
48
|
+
console.log('Or: npx vitest run src/__tests__/e2e/');
|
|
49
|
+
console.log('Filter: npx vitest run src/__tests__/e2e/ -t "pattern"');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function runTests(options: RunOptions): Promise<number> {
|
|
53
|
+
console.log('\nš§Ŗ Running E2E Tests\n');
|
|
54
|
+
console.log('ā'.repeat(60));
|
|
55
|
+
|
|
56
|
+
const args = ['vitest', 'run', 'src/__tests__/e2e/'];
|
|
57
|
+
|
|
58
|
+
if (options.verbose) {
|
|
59
|
+
args.push('--reporter=verbose');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (options.filter) {
|
|
63
|
+
args.push('-t', options.filter);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
const child = spawn('npx', args, {
|
|
68
|
+
cwd: harnessRoot,
|
|
69
|
+
stdio: 'inherit',
|
|
70
|
+
env: {
|
|
71
|
+
...process.env,
|
|
72
|
+
// Ensure consistent output
|
|
73
|
+
FORCE_COLOR: '1',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
child.on('close', (code) => {
|
|
78
|
+
console.log('\n' + 'ā'.repeat(60));
|
|
79
|
+
if (code === 0) {
|
|
80
|
+
console.log('ā
All E2E tests passed\n');
|
|
81
|
+
} else {
|
|
82
|
+
console.log(`ā E2E tests failed with code ${code}\n`);
|
|
83
|
+
}
|
|
84
|
+
resolve(code ?? 1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
child.on('error', (err) => {
|
|
88
|
+
console.error('Failed to run tests:', err);
|
|
89
|
+
resolve(1);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function main(): Promise<void> {
|
|
95
|
+
const options = parseArgs();
|
|
96
|
+
|
|
97
|
+
if (options.list) {
|
|
98
|
+
listTestSuites();
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const exitCode = await runTests(options);
|
|
103
|
+
process.exit(exitCode);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
main().catch((err) => {
|
|
107
|
+
console.error(err);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|