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,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Commands
|
|
3
|
+
*
|
|
4
|
+
* Desktop notifications via jamf/Notifier (macOS).
|
|
5
|
+
* For direct CLI notification sending.
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* - ah notify send <title> <message> - Send a notification
|
|
9
|
+
*
|
|
10
|
+
* For hook-based notifications (idle, elicitation), see:
|
|
11
|
+
* ah hooks notification --help
|
|
12
|
+
*
|
|
13
|
+
* Requires: https://github.com/jamf/Notifier
|
|
14
|
+
* Install: brew install --cask notifier
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import { sendNotification } from '../lib/notification.js';
|
|
19
|
+
import { tracedAction } from '../lib/base-command.js';
|
|
20
|
+
|
|
21
|
+
export function register(program: Command): void {
|
|
22
|
+
const notify = program
|
|
23
|
+
.command('notify')
|
|
24
|
+
.description('Desktop notifications (direct CLI use)');
|
|
25
|
+
|
|
26
|
+
// ah notify send <title> <message>
|
|
27
|
+
notify
|
|
28
|
+
.command('send <title> <message>')
|
|
29
|
+
.description('Send a system notification')
|
|
30
|
+
.option('--sound <name>', 'Sound name (macOS system sounds)')
|
|
31
|
+
.option('-t, --type <type>', 'Notification type: banner or alert', 'banner')
|
|
32
|
+
.option('--json', 'Output as JSON')
|
|
33
|
+
.action(
|
|
34
|
+
tracedAction('notify send', async (
|
|
35
|
+
title: string,
|
|
36
|
+
message: string,
|
|
37
|
+
options: { sound?: string; type?: string; json?: boolean }
|
|
38
|
+
) => {
|
|
39
|
+
const type = options.type as 'banner' | 'alert' | undefined;
|
|
40
|
+
const sent = sendNotification({
|
|
41
|
+
title,
|
|
42
|
+
message,
|
|
43
|
+
sound: options.sound,
|
|
44
|
+
type,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const result = sent
|
|
48
|
+
? { success: true, sent: true, title, message }
|
|
49
|
+
: { success: false, sent: false, reason: 'notifier not available or failed' };
|
|
50
|
+
|
|
51
|
+
if (options.json) {
|
|
52
|
+
console.log(JSON.stringify(result, null, 2));
|
|
53
|
+
} else if (sent) {
|
|
54
|
+
console.log(`Notification sent: ${title}`);
|
|
55
|
+
} else {
|
|
56
|
+
console.error('Failed to send notification (notifier not installed?)');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oracle Commands (Agent-Facing)
|
|
3
|
+
*
|
|
4
|
+
* Multi-provider LLM inference for agent tasks.
|
|
5
|
+
* Uses the standardized oracle library for provider integration.
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* - ah oracle ask <query> - Raw LLM inference with file context
|
|
9
|
+
* - ah oracle pr-build [--branch] [--dry-run] - Create PR with generated description
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { tracedAction } from '../lib/base-command.js';
|
|
15
|
+
import {
|
|
16
|
+
ask,
|
|
17
|
+
getDefaultProvider,
|
|
18
|
+
PROVIDERS,
|
|
19
|
+
type ProviderName,
|
|
20
|
+
} from '../lib/llm.js';
|
|
21
|
+
import { buildPR } from '../lib/oracle.js';
|
|
22
|
+
|
|
23
|
+
export function register(program: Command): void {
|
|
24
|
+
const oracle = program
|
|
25
|
+
.command('oracle')
|
|
26
|
+
.description('Multi-provider LLM inference');
|
|
27
|
+
|
|
28
|
+
// ah oracle ask
|
|
29
|
+
oracle
|
|
30
|
+
.command('ask <query>')
|
|
31
|
+
.description('Raw LLM inference with optional file context')
|
|
32
|
+
.option('--provider <provider>', 'LLM provider (gemini | openai)', getDefaultProvider())
|
|
33
|
+
.option('--model <model>', 'Override default model')
|
|
34
|
+
.option('--files <files...>', 'Files to include as context')
|
|
35
|
+
.option('--context <context>', 'Additional context')
|
|
36
|
+
.option('--json', 'Output as JSON')
|
|
37
|
+
.action(tracedAction('oracle ask', async (query: string, options: {
|
|
38
|
+
provider: ProviderName;
|
|
39
|
+
model?: string;
|
|
40
|
+
files?: string[];
|
|
41
|
+
context?: string;
|
|
42
|
+
json?: boolean;
|
|
43
|
+
}) => {
|
|
44
|
+
// Validate provider
|
|
45
|
+
if (!PROVIDERS[options.provider]) {
|
|
46
|
+
if (options.json) {
|
|
47
|
+
console.log(JSON.stringify({ success: false, error: 'Invalid provider. Use: gemini, openai' }));
|
|
48
|
+
} else {
|
|
49
|
+
console.error('Error: Invalid provider. Use: gemini, openai');
|
|
50
|
+
}
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const result = await ask(query, {
|
|
56
|
+
provider: options.provider,
|
|
57
|
+
model: options.model,
|
|
58
|
+
files: options.files,
|
|
59
|
+
context: options.context,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (options.json) {
|
|
63
|
+
console.log(JSON.stringify({
|
|
64
|
+
success: true,
|
|
65
|
+
content: result.text,
|
|
66
|
+
model: result.model,
|
|
67
|
+
provider: result.provider,
|
|
68
|
+
duration_ms: result.durationMs,
|
|
69
|
+
}, null, 2));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(result.text);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
const error = e instanceof Error ? e.message : String(e);
|
|
76
|
+
if (options.json) {
|
|
77
|
+
console.log(JSON.stringify({ success: false, error }));
|
|
78
|
+
} else {
|
|
79
|
+
console.error(`Error: ${error}`);
|
|
80
|
+
}
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
// ah oracle pr-build
|
|
86
|
+
oracle
|
|
87
|
+
.command('pr-build')
|
|
88
|
+
.description('Create PR with generated description')
|
|
89
|
+
.option('--spec <spec>', 'Spec to create PR for (defaults to active)')
|
|
90
|
+
.option('--dry-run', 'Generate description without creating PR')
|
|
91
|
+
.option('--json', 'Output as JSON')
|
|
92
|
+
.action(tracedAction('oracle pr-build', async (options: {
|
|
93
|
+
spec?: string;
|
|
94
|
+
dryRun?: boolean;
|
|
95
|
+
json?: boolean;
|
|
96
|
+
}) => {
|
|
97
|
+
try {
|
|
98
|
+
// Import planning utils here to avoid circular dependency
|
|
99
|
+
const { getCurrentBranch, sanitizeBranchForDir, planningDirExists } = await import('../lib/planning.js');
|
|
100
|
+
const { getSpecForBranch } = await import('../lib/specs.js');
|
|
101
|
+
|
|
102
|
+
let spec = options.spec;
|
|
103
|
+
if (!spec) {
|
|
104
|
+
// Use current branch to find spec
|
|
105
|
+
const branch = getCurrentBranch();
|
|
106
|
+
const currentSpec = getSpecForBranch(branch);
|
|
107
|
+
if (currentSpec) {
|
|
108
|
+
spec = sanitizeBranchForDir(branch);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!spec) {
|
|
112
|
+
console.error('Error: No spec for current branch. Checkout a spec branch first.');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const result = await buildPR(spec, undefined, options.dryRun);
|
|
116
|
+
|
|
117
|
+
if (options.json) {
|
|
118
|
+
console.log(JSON.stringify({
|
|
119
|
+
success: result.success,
|
|
120
|
+
pr_url: result.prUrl,
|
|
121
|
+
pr_number: result.prNumber,
|
|
122
|
+
title: result.title,
|
|
123
|
+
body: result.body,
|
|
124
|
+
review_steps: result.reviewSteps,
|
|
125
|
+
dry_run: options.dryRun || false,
|
|
126
|
+
}, null, 2));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (options.dryRun) {
|
|
131
|
+
console.log(`\n=== PR Preview (Dry Run) ===\n`);
|
|
132
|
+
console.log(`Title: ${result.title}`);
|
|
133
|
+
console.log(`\nBody:\n${result.body}`);
|
|
134
|
+
console.log(`\n=== Review Steps (Posted as Comment) ===\n`);
|
|
135
|
+
console.log(result.reviewSteps);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (result.success && result.prUrl) {
|
|
140
|
+
console.log(`\n=== PR Created ===\n`);
|
|
141
|
+
console.log(`URL: ${result.prUrl}`);
|
|
142
|
+
console.log(`Number: #${result.prNumber}`);
|
|
143
|
+
console.log(`Title: ${result.title}`);
|
|
144
|
+
} else {
|
|
145
|
+
console.error(`Failed to create PR: ${result.body}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
} catch (e) {
|
|
149
|
+
const error = e instanceof Error ? e.message : String(e);
|
|
150
|
+
if (options.json) {
|
|
151
|
+
console.log(JSON.stringify({ success: false, error }));
|
|
152
|
+
} else {
|
|
153
|
+
console.error(`Error: ${error}`);
|
|
154
|
+
}
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Perplexity Commands (Agent-Facing)
|
|
3
|
+
*
|
|
4
|
+
* Web search with citations using Perplexity's sonar-pro model.
|
|
5
|
+
* Matches the configuration used by the Perplexity MCP server.
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* - ah perplexity research <query> - Web search with citations
|
|
9
|
+
* --challenge: Challenge findings using Grok X/Twitter search
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Command } from 'commander';
|
|
13
|
+
import { tracedAction } from '../lib/base-command.js';
|
|
14
|
+
|
|
15
|
+
interface PerplexityResponse {
|
|
16
|
+
choices?: Array<{
|
|
17
|
+
message?: {
|
|
18
|
+
content?: string;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
citations?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface GrokResponse {
|
|
25
|
+
choices?: Array<{
|
|
26
|
+
message?: {
|
|
27
|
+
content?: string;
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
30
|
+
citations?: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const GROK_CHALLENGER_PROMPT = `You are a critical research challenger. Given research findings, search X to:
|
|
34
|
+
|
|
35
|
+
1. CHALLENGE: Find contradicting opinions, failed implementations, known issues
|
|
36
|
+
2. ALTERNATIVES: Surface newer/better tools the research may have missed
|
|
37
|
+
3. TRENDS: Identify emerging patterns that could affect the recommendations
|
|
38
|
+
4. SENTIMENT: Gauge real developer satisfaction vs marketing claims
|
|
39
|
+
5. DISCUSSIONS: Find where the best practitioners are discussing this topic
|
|
40
|
+
|
|
41
|
+
Be skeptical. Surface what the research missed or got wrong. Focus on recent posts (last 6 months).`;
|
|
42
|
+
|
|
43
|
+
const GROK_TIMEOUT = 120000;
|
|
44
|
+
|
|
45
|
+
const PERPLEXITY_TIMEOUT = parseInt(process.env.PERPLEXITY_TIMEOUT_MS ?? '60000', 10);
|
|
46
|
+
|
|
47
|
+
export function register(program: Command): void {
|
|
48
|
+
const perplexity = program
|
|
49
|
+
.command('perplexity')
|
|
50
|
+
.description('Web search with citations');
|
|
51
|
+
|
|
52
|
+
// ah perplexity research
|
|
53
|
+
perplexity
|
|
54
|
+
.command('research <query>')
|
|
55
|
+
.description('Web search with citations (sonar-pro model)')
|
|
56
|
+
.option('--json', 'Output as JSON')
|
|
57
|
+
.option('--challenge', 'Challenge findings using Grok X/Twitter search')
|
|
58
|
+
.action(tracedAction('perplexity research', async (query: string, options: { json?: boolean; challenge?: boolean }) => {
|
|
59
|
+
const apiKey = process.env.PERPLEXITY_API_KEY;
|
|
60
|
+
if (!apiKey) {
|
|
61
|
+
if (options.json) {
|
|
62
|
+
console.log(JSON.stringify({ success: false, error: 'PERPLEXITY_API_KEY not set' }));
|
|
63
|
+
} else {
|
|
64
|
+
console.error('Error: PERPLEXITY_API_KEY not set in environment');
|
|
65
|
+
}
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const response = await callPerplexityApi(apiKey, query);
|
|
71
|
+
|
|
72
|
+
const content = response.choices?.[0]?.message?.content ?? '';
|
|
73
|
+
const citations = response.citations ?? [];
|
|
74
|
+
|
|
75
|
+
// If --challenge flag, use Grok to challenge the findings
|
|
76
|
+
let challengeContent: string | undefined;
|
|
77
|
+
let challengeCitations: string[] | undefined;
|
|
78
|
+
|
|
79
|
+
if (options.challenge) {
|
|
80
|
+
const grokApiKey = process.env.X_AI_API_KEY;
|
|
81
|
+
if (!grokApiKey) {
|
|
82
|
+
if (options.json) {
|
|
83
|
+
console.log(JSON.stringify({ success: false, error: 'X_AI_API_KEY not set (required for --challenge)' }));
|
|
84
|
+
} else {
|
|
85
|
+
console.error('Error: X_AI_API_KEY not set in environment (required for --challenge)');
|
|
86
|
+
}
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const grokResponse = await callGrokChallengeApi(grokApiKey, query, content);
|
|
91
|
+
challengeContent = grokResponse.choices?.[0]?.message?.content ?? '';
|
|
92
|
+
challengeCitations = grokResponse.citations ?? [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (options.json) {
|
|
96
|
+
const result: Record<string, unknown> = {
|
|
97
|
+
success: true,
|
|
98
|
+
query,
|
|
99
|
+
content,
|
|
100
|
+
citations,
|
|
101
|
+
};
|
|
102
|
+
if (options.challenge) {
|
|
103
|
+
result.challenge = {
|
|
104
|
+
content: challengeContent,
|
|
105
|
+
citations: challengeCitations,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
console.log(JSON.stringify(result, null, 2));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('Research Results:');
|
|
113
|
+
console.log();
|
|
114
|
+
console.log(content);
|
|
115
|
+
|
|
116
|
+
if (citations.length > 0) {
|
|
117
|
+
console.log();
|
|
118
|
+
console.log('Citations:');
|
|
119
|
+
for (let i = 0; i < citations.length; i++) {
|
|
120
|
+
console.log(` [${i + 1}] ${citations[i]}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (options.challenge && challengeContent) {
|
|
125
|
+
console.log();
|
|
126
|
+
console.log('---');
|
|
127
|
+
console.log();
|
|
128
|
+
console.log('Challenge (via X/Twitter):');
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(challengeContent);
|
|
131
|
+
|
|
132
|
+
if (challengeCitations && challengeCitations.length > 0) {
|
|
133
|
+
console.log();
|
|
134
|
+
console.log('Challenge Sources:');
|
|
135
|
+
for (const citation of challengeCitations) {
|
|
136
|
+
console.log(` - ${citation}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch (e) {
|
|
141
|
+
const error = e instanceof Error ? e.message : String(e);
|
|
142
|
+
if (options.json) {
|
|
143
|
+
console.log(JSON.stringify({ success: false, error }));
|
|
144
|
+
} else {
|
|
145
|
+
console.error(`Error: ${error}`);
|
|
146
|
+
}
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function callPerplexityApi(apiKey: string, query: string): Promise<PerplexityResponse> {
|
|
153
|
+
const controller = new AbortController();
|
|
154
|
+
const timeout = setTimeout(() => controller.abort(), PERPLEXITY_TIMEOUT);
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const response = await fetch('https://api.perplexity.ai/chat/completions', {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
headers: {
|
|
160
|
+
Authorization: `Bearer ${apiKey}`,
|
|
161
|
+
'Content-Type': 'application/json',
|
|
162
|
+
},
|
|
163
|
+
body: JSON.stringify({
|
|
164
|
+
model: 'sonar-pro',
|
|
165
|
+
messages: [{ role: 'user', content: query }],
|
|
166
|
+
}),
|
|
167
|
+
signal: controller.signal,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return (await response.json()) as PerplexityResponse;
|
|
175
|
+
} finally {
|
|
176
|
+
clearTimeout(timeout);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function callGrokChallengeApi(
|
|
181
|
+
apiKey: string,
|
|
182
|
+
query: string,
|
|
183
|
+
findings: string
|
|
184
|
+
): Promise<GrokResponse> {
|
|
185
|
+
const controller = new AbortController();
|
|
186
|
+
const timeout = setTimeout(() => controller.abort(), GROK_TIMEOUT);
|
|
187
|
+
|
|
188
|
+
const userPrompt = `Original query: ${query}
|
|
189
|
+
|
|
190
|
+
Research findings to challenge:
|
|
191
|
+
${findings}
|
|
192
|
+
|
|
193
|
+
Search X to challenge these findings.`;
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const response = await fetch('https://api.x.ai/v1/chat/completions', {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
headers: {
|
|
199
|
+
Authorization: `Bearer ${apiKey}`,
|
|
200
|
+
'Content-Type': 'application/json',
|
|
201
|
+
},
|
|
202
|
+
body: JSON.stringify({
|
|
203
|
+
model: 'grok-4-1-fast',
|
|
204
|
+
messages: [
|
|
205
|
+
{ role: 'system', content: GROK_CHALLENGER_PROMPT },
|
|
206
|
+
{ role: 'user', content: userPrompt },
|
|
207
|
+
],
|
|
208
|
+
}),
|
|
209
|
+
signal: controller.signal,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (!response.ok) {
|
|
213
|
+
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return (await response.json()) as GrokResponse;
|
|
217
|
+
} finally {
|
|
218
|
+
clearTimeout(timeout);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planning Command - Manage .planning/ directories (branch-keyed)
|
|
3
|
+
*
|
|
4
|
+
* Commands:
|
|
5
|
+
* - ah planning status - Show planning status for current branch
|
|
6
|
+
* - ah planning list - List all planning directories
|
|
7
|
+
* - ah planning ensure - Ensure planning dir exists for current branch
|
|
8
|
+
*
|
|
9
|
+
* In the branch-keyed model:
|
|
10
|
+
* - Planning directories are keyed by sanitized branch name (feature/foo → feature-foo)
|
|
11
|
+
* - The spec's frontmatter.branch field is the source of truth
|
|
12
|
+
* - Current git branch determines the active context
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
import {
|
|
17
|
+
initializeStatus,
|
|
18
|
+
readStatus,
|
|
19
|
+
getPlanningPaths,
|
|
20
|
+
planningDirExists,
|
|
21
|
+
getCurrentBranch,
|
|
22
|
+
sanitizeBranchForDir,
|
|
23
|
+
ensurePlanningDir,
|
|
24
|
+
listPlanningDirs,
|
|
25
|
+
resetPlanningArtifacts,
|
|
26
|
+
} from '../lib/planning.js';
|
|
27
|
+
import { getSpecForBranch } from '../lib/specs.js';
|
|
28
|
+
import { tracedAction } from '../lib/base-command.js';
|
|
29
|
+
|
|
30
|
+
export function register(program: Command): void {
|
|
31
|
+
const cmd = program
|
|
32
|
+
.command('planning')
|
|
33
|
+
.description('Manage .planning/ directories (branch-keyed)');
|
|
34
|
+
|
|
35
|
+
// ah planning status
|
|
36
|
+
cmd
|
|
37
|
+
.command('status')
|
|
38
|
+
.description('Show planning status for current branch')
|
|
39
|
+
.option('--json', 'Output as JSON')
|
|
40
|
+
.action(tracedAction('planning status', async (options: { json?: boolean }) => {
|
|
41
|
+
const cwd = process.cwd();
|
|
42
|
+
const branch = getCurrentBranch(cwd);
|
|
43
|
+
const dirKey = sanitizeBranchForDir(branch);
|
|
44
|
+
|
|
45
|
+
// Find spec for current branch
|
|
46
|
+
const spec = getSpecForBranch(branch, cwd);
|
|
47
|
+
const status = readStatus(dirKey, cwd);
|
|
48
|
+
|
|
49
|
+
if (!spec) {
|
|
50
|
+
if (options.json) {
|
|
51
|
+
console.log(JSON.stringify({
|
|
52
|
+
success: true,
|
|
53
|
+
hasSpec: false,
|
|
54
|
+
hasPlanning: !!status,
|
|
55
|
+
branch,
|
|
56
|
+
message: 'No spec for this branch',
|
|
57
|
+
}, null, 2));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(`Branch: ${branch}`);
|
|
60
|
+
console.log('No spec linked to this branch.');
|
|
61
|
+
if (status) {
|
|
62
|
+
console.log(` (Planning dir exists: .planning/${dirKey}/)`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!status) {
|
|
69
|
+
if (options.json) {
|
|
70
|
+
console.log(JSON.stringify({
|
|
71
|
+
success: true,
|
|
72
|
+
hasSpec: true,
|
|
73
|
+
hasPlanning: false,
|
|
74
|
+
branch,
|
|
75
|
+
spec: {
|
|
76
|
+
id: spec.id,
|
|
77
|
+
path: spec.path,
|
|
78
|
+
},
|
|
79
|
+
message: 'Spec exists but no planning directory. Run "ah planning ensure".',
|
|
80
|
+
}, null, 2));
|
|
81
|
+
} else {
|
|
82
|
+
console.log(`Branch: ${branch}`);
|
|
83
|
+
console.log(`Spec: ${spec.id}`);
|
|
84
|
+
console.log('No planning directory. Run "ah planning ensure" to create it.');
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (options.json) {
|
|
90
|
+
console.log(JSON.stringify({
|
|
91
|
+
success: true,
|
|
92
|
+
hasSpec: true,
|
|
93
|
+
hasPlanning: true,
|
|
94
|
+
branch,
|
|
95
|
+
planningDir: `.planning/${dirKey}/`,
|
|
96
|
+
specInfo: {
|
|
97
|
+
id: spec.id,
|
|
98
|
+
path: spec.path,
|
|
99
|
+
},
|
|
100
|
+
...status,
|
|
101
|
+
}, null, 2));
|
|
102
|
+
} else {
|
|
103
|
+
console.log(`Branch: ${branch}`);
|
|
104
|
+
console.log(`Spec: ${spec.id} (${spec.path})`);
|
|
105
|
+
console.log(`Planning: .planning/${dirKey}/`);
|
|
106
|
+
console.log(`Stage: ${status.stage}`);
|
|
107
|
+
if (status.pr) {
|
|
108
|
+
console.log(`PR: ${status.pr.url}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
// ah planning list
|
|
114
|
+
cmd
|
|
115
|
+
.command('list')
|
|
116
|
+
.description('List all planning directories')
|
|
117
|
+
.option('--json', 'Output as JSON')
|
|
118
|
+
.action(tracedAction('planning list', async (options: { json?: boolean }) => {
|
|
119
|
+
const cwd = process.cwd();
|
|
120
|
+
const dirs = listPlanningDirs(cwd);
|
|
121
|
+
|
|
122
|
+
if (options.json) {
|
|
123
|
+
console.log(JSON.stringify({
|
|
124
|
+
success: true,
|
|
125
|
+
count: dirs.length,
|
|
126
|
+
dirs,
|
|
127
|
+
}, null, 2));
|
|
128
|
+
} else {
|
|
129
|
+
if (dirs.length === 0) {
|
|
130
|
+
console.log('No planning directories found.');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log(`Found ${dirs.length} planning director${dirs.length === 1 ? 'y' : 'ies'}:\n`);
|
|
135
|
+
for (const dir of dirs) {
|
|
136
|
+
const marker = dir.isCurrent ? '→ ' : ' ';
|
|
137
|
+
console.log(`${marker}${dir.key}/`);
|
|
138
|
+
console.log(` Spec: ${dir.specPath}`);
|
|
139
|
+
console.log(` Stage: ${dir.stage}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
|
|
144
|
+
// ah planning ensure
|
|
145
|
+
cmd
|
|
146
|
+
.command('ensure')
|
|
147
|
+
.description('Ensure planning directory exists for current branch')
|
|
148
|
+
.option('--json', 'Output as JSON')
|
|
149
|
+
.action(tracedAction('planning ensure', async (options: { json?: boolean }) => {
|
|
150
|
+
const cwd = process.cwd();
|
|
151
|
+
const branch = getCurrentBranch(cwd);
|
|
152
|
+
|
|
153
|
+
// Find spec for this branch
|
|
154
|
+
const spec = getSpecForBranch(branch, cwd);
|
|
155
|
+
|
|
156
|
+
if (!spec) {
|
|
157
|
+
if (options.json) {
|
|
158
|
+
console.log(JSON.stringify({
|
|
159
|
+
success: false,
|
|
160
|
+
error: `No spec found for branch: ${branch}`,
|
|
161
|
+
branch,
|
|
162
|
+
}, null, 2));
|
|
163
|
+
} else {
|
|
164
|
+
console.error(`No spec found for branch: ${branch}`);
|
|
165
|
+
console.error('Use "ah specs current" to check branch-spec mapping.');
|
|
166
|
+
}
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Use sanitized branch name for directory
|
|
171
|
+
const dirKey = sanitizeBranchForDir(branch);
|
|
172
|
+
const alreadyExists = planningDirExists(dirKey, cwd);
|
|
173
|
+
|
|
174
|
+
if (!alreadyExists) {
|
|
175
|
+
// Create planning directory structure
|
|
176
|
+
ensurePlanningDir(dirKey, cwd);
|
|
177
|
+
|
|
178
|
+
// Initialize status file with original branch for collision detection
|
|
179
|
+
initializeStatus(dirKey, spec.path, branch, cwd);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const status = readStatus(dirKey, cwd);
|
|
183
|
+
|
|
184
|
+
if (options.json) {
|
|
185
|
+
console.log(JSON.stringify({
|
|
186
|
+
success: true,
|
|
187
|
+
branch,
|
|
188
|
+
specId: spec.id,
|
|
189
|
+
specPath: spec.path,
|
|
190
|
+
planningDir: `.planning/${dirKey}/`,
|
|
191
|
+
created: !alreadyExists,
|
|
192
|
+
status,
|
|
193
|
+
}, null, 2));
|
|
194
|
+
} else {
|
|
195
|
+
if (alreadyExists) {
|
|
196
|
+
console.log(`Planning directory exists: .planning/${dirKey}/`);
|
|
197
|
+
} else {
|
|
198
|
+
console.log(`Created planning directory: .planning/${dirKey}/`);
|
|
199
|
+
}
|
|
200
|
+
console.log(` Branch: ${branch}`);
|
|
201
|
+
console.log(` Spec: ${spec.id} (${spec.path})`);
|
|
202
|
+
console.log(` Stage: ${status?.stage || 'planning'}`);
|
|
203
|
+
}
|
|
204
|
+
}));
|
|
205
|
+
|
|
206
|
+
// ah planning reset
|
|
207
|
+
cmd
|
|
208
|
+
.command('reset')
|
|
209
|
+
.description('Reset planning artifacts for current branch (deletes prompts and alignment doc, resets stage to planning)')
|
|
210
|
+
.option('--json', 'Output as JSON')
|
|
211
|
+
.action(tracedAction('planning reset', async (options: { json?: boolean }) => {
|
|
212
|
+
const cwd = process.cwd();
|
|
213
|
+
const branch = getCurrentBranch(cwd);
|
|
214
|
+
const dirKey = sanitizeBranchForDir(branch);
|
|
215
|
+
|
|
216
|
+
if (!planningDirExists(dirKey, cwd)) {
|
|
217
|
+
if (options.json) {
|
|
218
|
+
console.log(JSON.stringify({
|
|
219
|
+
success: false,
|
|
220
|
+
error: `No planning directory for branch: ${branch}`,
|
|
221
|
+
branch,
|
|
222
|
+
}, null, 2));
|
|
223
|
+
} else {
|
|
224
|
+
console.error(`No planning directory for branch: ${branch}`);
|
|
225
|
+
}
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const wasReset = resetPlanningArtifacts(dirKey, cwd);
|
|
230
|
+
|
|
231
|
+
if (options.json) {
|
|
232
|
+
console.log(JSON.stringify({
|
|
233
|
+
success: wasReset,
|
|
234
|
+
branch,
|
|
235
|
+
planningDir: `.planning/${dirKey}/`,
|
|
236
|
+
message: 'Planning artifacts cleared — spec revision requires re-planning',
|
|
237
|
+
}, null, 2));
|
|
238
|
+
} else {
|
|
239
|
+
console.log('Planning artifacts cleared — spec revision requires re-planning');
|
|
240
|
+
console.log(` Branch: ${branch}`);
|
|
241
|
+
console.log(` Planning: .planning/${dirKey}/`);
|
|
242
|
+
console.log(` Stage: planning`);
|
|
243
|
+
}
|
|
244
|
+
}));
|
|
245
|
+
}
|