summaryrefslogtreecommitdiff
path: root/makima/src
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-21 19:33:44 +0000
committerGitHub <noreply@github.com>2026-02-21 19:33:44 +0000
commitd670dcb72984cfa483063d161bb468704038895c (patch)
tree885ea969d2c5ea5c026d1caf25cd0a15f6753ca1 /makima/src
parent61442ea1cb92ce8c28fe0622aa19d4e2947a8fd0 (diff)
downloadsoryu-d670dcb72984cfa483063d161bb468704038895c.tar.gz
soryu-d670dcb72984cfa483063d161bb468704038895c.zip
feat: add directive ask command, log backfill & specialized DAG steps (#75)
* feat: soryu-co/soryu - makima: Add makima directive ask CLI command * feat: soryu-co/soryu - makima: Update directive skill docs and planning prompt to support asking questions * feat: soryu-co/soryu - makima: Add log stream backfill for directive tasks * feat: soryu-co/soryu - makima: Update planning prompts to inform tasks they can ask questions * WIP: heartbeat checkpoint * feat: soryu-co/soryu - makima: Add ask command to directive SKILL.md documentation * feat: soryu-co/soryu - makima: Add log stream backfill for directive task output history * feat: soryu-co/soryu - makima: Update planning prompt to tell planning tasks they can ask questions * WIP: heartbeat checkpoint * feat: soryu-co/soryu - makima: Show Planning, PR, and Cleanup tasks as specialized steps in DAG
Diffstat (limited to 'makima/src')
-rw-r--r--makima/src/bin/makima.rs12
-rw-r--r--makima/src/daemon/cli/directive.rs39
-rw-r--r--makima/src/daemon/cli/mod.rs3
-rw-r--r--makima/src/daemon/skills/directive.md27
-rw-r--r--makima/src/orchestration/directive.rs64
5 files changed, 142 insertions, 3 deletions
diff --git a/makima/src/bin/makima.rs b/makima/src/bin/makima.rs
index 070e28e..aaf5a08 100644
--- a/makima/src/bin/makima.rs
+++ b/makima/src/bin/makima.rs
@@ -800,6 +800,18 @@ async fn run_directive(
.await?;
println!("{}", serde_json::to_string(&result.0)?);
}
+ DirectiveCommand::Ask(args) => {
+ let client = ApiClient::new(args.common.api_url.clone(), args.common.api_key.clone())?;
+ eprintln!("Asking user: {}...", args.question);
+ let choices = args
+ .choices
+ .map(|c| c.split(',').map(|s| s.trim().to_string()).collect())
+ .unwrap_or_default();
+ let result = client
+ .supervisor_ask(&args.question, choices, args.context, args.timeout, args.phaseguard, args.multi_select, args.non_blocking, args.question_type)
+ .await?;
+ println!("{}", serde_json::to_string(&result.0)?);
+ }
}
Ok(())
diff --git a/makima/src/daemon/cli/directive.rs b/makima/src/daemon/cli/directive.rs
index 8a6a9f2..7c8451f 100644
--- a/makima/src/daemon/cli/directive.rs
+++ b/makima/src/daemon/cli/directive.rs
@@ -111,6 +111,45 @@ pub struct BatchAddStepsArgs {
pub json: String,
}
+/// Arguments for ask command (ask user a question from directive context).
+#[derive(Args, Debug)]
+pub struct AskArgs {
+ #[command(flatten)]
+ pub common: DirectiveArgs,
+
+ /// The question to ask
+ #[arg(index = 1)]
+ pub question: String,
+
+ /// Optional choices (comma-separated)
+ #[arg(long)]
+ pub choices: Option<String>,
+
+ /// Context about what this relates to
+ #[arg(long)]
+ pub context: Option<String>,
+
+ /// Timeout in seconds (default: 3600 = 1 hour)
+ #[arg(long, default_value = "3600")]
+ pub timeout: i32,
+
+ /// Block indefinitely until user responds (no timeout)
+ #[arg(long, default_value = "false")]
+ pub phaseguard: bool,
+
+ /// Allow selecting multiple choices (response will be comma-separated)
+ #[arg(long, default_value = "false")]
+ pub multi_select: bool,
+
+ /// Non-blocking mode - returns immediately without waiting for response
+ #[arg(long, default_value = "false")]
+ pub non_blocking: bool,
+
+ /// Question type (general, phase_confirmation, contract_complete)
+ #[arg(long, default_value = "general")]
+ pub question_type: String,
+}
+
/// Arguments for update command.
#[derive(Args, Debug)]
pub struct UpdateArgs {
diff --git a/makima/src/daemon/cli/mod.rs b/makima/src/daemon/cli/mod.rs
index bcaaa70..8063541 100644
--- a/makima/src/daemon/cli/mod.rs
+++ b/makima/src/daemon/cli/mod.rs
@@ -249,6 +249,9 @@ pub enum DirectiveCommand {
/// Update directive metadata (PR URL, etc.)
Update(directive::UpdateArgs),
+
+ /// Ask a question and wait for user feedback
+ Ask(directive::AskArgs),
}
impl Cli {
diff --git a/makima/src/daemon/skills/directive.md b/makima/src/daemon/skills/directive.md
index 9d2b644..02de836 100644
--- a/makima/src/daemon/skills/directive.md
+++ b/makima/src/daemon/skills/directive.md
@@ -82,6 +82,32 @@ makima directive update --pr-url "<url>" --pr-branch "<branch>"
```
Updates the directive's PR URL and/or PR branch. Used by completion tasks to store the PR URL after creating it.
+### Ask User a Question
+```bash
+makima directive ask "<question>"
+```
+Asks the user a question and waits for their response. Questions appear on the directive page with a yellow indicator and can be answered inline.
+
+Options:
+- `--choices "opt1,opt2,opt3"` - Provide choices for the user to select from
+- `--context "<context>"` - Additional context to help the user understand the question
+- `--timeout <seconds>` - Wait timeout (default: 3600 = 1 hour)
+- `--phaseguard` - Block indefinitely until the user responds (no timeout). Recommended for critical decisions during planning.
+- `--multi-select` - Allow the user to select multiple choices
+- `--non-blocking` - Return immediately without waiting for a response
+- `--question-type <general|phase_confirmation|contract_complete>` - Question type
+
+**When to use:**
+- During planning, when you need clarification on requirements or approach
+- When there are multiple valid approaches and user preference matters
+- When a decision requires domain knowledge you don't have
+- Always use `--phaseguard` for questions that block progress (the reconcile mode on the directive also controls this)
+
+**Example:**
+```bash
+makima directive ask "Should we use REST or GraphQL for the new API?" --choices "REST,GraphQL" --context "The existing codebase uses REST but the frontend team prefers GraphQL" --phaseguard
+```
+
## Memory Commands
Directives have an optional key-value memory system that persists across steps and planning cycles. Use memory to share context, decisions, and learned information between steps — so downstream tasks don't need to re-discover what earlier steps already figured out.
@@ -167,6 +193,7 @@ makima directive memory-batch-set --json '{"framework": "axum", "orm": "sqlx", "
### Initial Setup
1. Check the directive status to understand the goal
2. Decompose the goal into steps with clear dependencies
+ - If requirements are unclear, use `makima directive ask` to get clarification before finalizing the plan
3. Add steps using `add-step` with appropriate `--depends-on` flags
4. Start the directive with `start`
5. Steps with no dependencies will become `ready` immediately
diff --git a/makima/src/orchestration/directive.rs b/makima/src/orchestration/directive.rs
index 420b3e1..b91781c 100644
--- a/makima/src/orchestration/directive.rs
+++ b/makima/src/orchestration/directive.rs
@@ -165,7 +165,9 @@ impl DirectiveOrchestrator {
{merge_preamble}\
INSTRUCTIONS:\n{task_plan}\n\
When done, the system will automatically mark this step as completed.\n\
- If you cannot complete the task, report the failure clearly.",
+ If you cannot complete the task, report the failure clearly.\n\n\
+ If you need clarification or encounter a decision that requires user input, you can ask:\n\
+ \x20 makima directive ask \"Your question\" --phaseguard",
directive_title = step.directive_title,
step_name = step.step_name,
description = step.step_description.as_deref().unwrap_or("(none)"),
@@ -1308,7 +1310,9 @@ fn build_planning_prompt(
\x20 makima directive remove-step <step_id>\n\
2. Then, add new steps for the updated goal. Use generation {}.\n\
3. New steps that build on completed work MUST use --depends-on to inherit the worktree.\n\
- 4. Ensure the new plan fully addresses the UPDATED goal.\n\n",
+ 4. Ensure the new plan fully addresses the UPDATED goal.\n\
+ 5. If the updated goal is unclear or ambiguous, ask the user for clarification using:\n\
+ \x20 makima directive ask \"<question>\" --phaseguard\n\n",
generation
));
}
@@ -1359,6 +1363,28 @@ Guidelines:
they should BOTH list that prior step in dependsOn.
IMPORTANT: Each step's taskPlan must be self-contained. The executing instance won't have your planning context.
+
+ASKING QUESTIONS:
+If you need clarification from the user before finalizing the plan, you can ask questions:
+ makima directive ask "Your question here"
+ makima directive ask "Which approach?" --choices "Option A,Option B" --phaseguard
+ makima directive ask "Confirm this approach?" --context "Additional context here" --phaseguard
+
+Use --phaseguard for questions that block progress (the question will wait indefinitely for a response).
+Without --phaseguard, questions timeout based on the directive's reconcile mode:
+- Reconcile ON: questions block indefinitely until answered
+- Reconcile OFF: questions timeout after 30 seconds with no response
+
+When to ask:
+- Requirements are ambiguous and multiple interpretations are valid
+- There are multiple equally valid technical approaches
+- You need domain-specific knowledge that cannot be inferred from the codebase
+- A decision has significant downstream impact and user preference matters
+
+Do NOT ask questions for:
+- Implementation details you can determine from the codebase
+- Standard engineering decisions with clear best practices
+- Trivial choices that do not significantly affect the outcome
"#,
title = directive.title,
goal = directive.goal,
@@ -1480,6 +1506,9 @@ Already-merged branches will be a no-op. If a merge fails with conflicts:
1. First try: `git merge --abort` then retry with `git merge <the-failing-branch> -X theirs --no-edit`
2. If that also fails, manually resolve the conflicts, `git add .`, and `git commit --no-edit`
3. Continue with remaining merges
+
+If you encounter issues you cannot resolve (e.g., persistent merge conflicts, PR update failures), you can ask for help:
+ makima directive ask "Your question" --phaseguard
"#,
title = directive.title,
goal = directive.goal,
@@ -1539,6 +1568,9 @@ For each step branch merge, if a merge fails with conflicts:
1. First try: `git merge --abort` then retry with `git merge <the-failing-branch> -X theirs --no-edit`
2. If that also fails, manually resolve the conflicts, `git add .`, and `git commit --no-edit`
3. Continue with remaining merges
+
+If you encounter issues you cannot resolve (e.g., persistent merge conflicts, PR creation failures), you can ask for help:
+ makima directive ask "Your question" --phaseguard
"#,
title = directive.title,
goal = directive.goal,
@@ -1679,7 +1711,10 @@ makima directive remove-step <step_id>
6. After processing all steps, report a summary of what was cleaned up and what was left.
-IMPORTANT: Only remove steps whose task branches have been verified as merged. Never remove unmerged steps."#,
+IMPORTANT: Only remove steps whose task branches have been verified as merged. Never remove unmerged steps.
+
+If you encounter issues you cannot resolve during cleanup, you can ask for help:
+ makima directive ask "Your question" --phaseguard"#,
title = directive.title,
pr_branch = pr_branch,
base_branch = base_branch,
@@ -1900,6 +1935,29 @@ Guidelines:
they should BOTH list that prior step in dependsOn.
IMPORTANT: Each step's taskPlan must be self-contained. The executing instance won't have your planning context.
+
+## Asking Questions
+
+If you need clarification about the goal, requirements, or implementation approach, you can ask the user:
+```bash
+makima directive ask "Your question here"
+```
+
+Options:
+- `--choices "opt1,opt2,opt3"` - Provide choices
+- `--context "<context>"` - Additional context
+- `--phaseguard` - Block until response (recommended for important questions)
+
+The question will appear in the directive UI. Behavior depends on reconcile mode:
+- Reconcile ON: blocks until user responds
+- Reconcile OFF: times out after 30s (use for non-critical questions)
+
+Use this when:
+- The goal is ambiguous and could be interpreted multiple ways
+- You need to choose between significantly different implementation approaches
+- You discover constraints that affect the plan
+
+Do NOT ask questions for trivial decisions — use your best judgment.
"#,
generation = generation,
));