summaryrefslogtreecommitdiff
path: root/makima/src/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/daemon')
-rw-r--r--makima/src/daemon/api/directive.rs124
-rw-r--r--makima/src/daemon/api/mod.rs1
-rw-r--r--makima/src/daemon/cli/directive.rs101
-rw-r--r--makima/src/daemon/cli/mod.rs49
-rw-r--r--makima/src/daemon/skills/directive.md111
-rw-r--r--makima/src/daemon/skills/mod.rs4
6 files changed, 390 insertions, 0 deletions
diff --git a/makima/src/daemon/api/directive.rs b/makima/src/daemon/api/directive.rs
new file mode 100644
index 0000000..fbd27fe
--- /dev/null
+++ b/makima/src/daemon/api/directive.rs
@@ -0,0 +1,124 @@
+//! Directive API methods.
+
+use serde::Serialize;
+use uuid::Uuid;
+
+use super::client::{ApiClient, ApiError};
+use super::supervisor::JsonValue;
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateStepRequest {
+ pub name: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub description: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub task_plan: Option<String>,
+ pub depends_on: Vec<Uuid>,
+ pub order_index: i32,
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct UpdateGoalRequest {
+ pub goal: String,
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct UpdateStepDepsRequest {
+ pub depends_on: Vec<Uuid>,
+}
+
+impl ApiClient {
+ /// List all directives.
+ pub async fn list_directives(&self) -> Result<JsonValue, ApiError> {
+ self.get("/api/v1/directives").await
+ }
+
+ /// Get a directive with its steps.
+ pub async fn get_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.get(&format!("/api/v1/directives/{}", directive_id)).await
+ }
+
+ /// Add a step to a directive.
+ pub async fn directive_add_step(
+ &self,
+ directive_id: Uuid,
+ req: CreateStepRequest,
+ ) -> Result<JsonValue, ApiError> {
+ self.post(&format!("/api/v1/directives/{}/steps", directive_id), &req).await
+ }
+
+ /// Remove a step from a directive.
+ pub async fn directive_remove_step(
+ &self,
+ directive_id: Uuid,
+ step_id: Uuid,
+ ) -> Result<(), ApiError> {
+ self.delete(&format!("/api/v1/directives/{}/steps/{}", directive_id, step_id)).await
+ }
+
+ /// Set dependencies for a step.
+ pub async fn directive_set_deps(
+ &self,
+ directive_id: Uuid,
+ step_id: Uuid,
+ depends_on: Vec<Uuid>,
+ ) -> Result<JsonValue, ApiError> {
+ let req = UpdateStepDepsRequest { depends_on };
+ self.put(&format!("/api/v1/directives/{}/steps/{}", directive_id, step_id), &req).await
+ }
+
+ /// Start a directive.
+ pub async fn directive_start(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/start", directive_id)).await
+ }
+
+ /// Pause a directive.
+ pub async fn directive_pause(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/pause", directive_id)).await
+ }
+
+ /// Advance the directive DAG.
+ pub async fn directive_advance(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/advance", directive_id)).await
+ }
+
+ /// Mark a step as completed.
+ pub async fn directive_complete_step(
+ &self,
+ directive_id: Uuid,
+ step_id: Uuid,
+ ) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/steps/{}/complete", directive_id, step_id)).await
+ }
+
+ /// Mark a step as failed.
+ pub async fn directive_fail_step(
+ &self,
+ directive_id: Uuid,
+ step_id: Uuid,
+ ) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/steps/{}/fail", directive_id, step_id)).await
+ }
+
+ /// Mark a step as skipped.
+ pub async fn directive_skip_step(
+ &self,
+ directive_id: Uuid,
+ step_id: Uuid,
+ ) -> Result<JsonValue, ApiError> {
+ self.post_empty(&format!("/api/v1/directives/{}/steps/{}/skip", directive_id, step_id)).await
+ }
+
+ /// Update the directive's goal.
+ pub async fn directive_update_goal(
+ &self,
+ directive_id: Uuid,
+ goal: &str,
+ ) -> Result<JsonValue, ApiError> {
+ let req = UpdateGoalRequest { goal: goal.to_string() };
+ self.put(&format!("/api/v1/directives/{}/goal", directive_id), &req).await
+ }
+}
diff --git a/makima/src/daemon/api/mod.rs b/makima/src/daemon/api/mod.rs
index 49d80e0..2d1efbf 100644
--- a/makima/src/daemon/api/mod.rs
+++ b/makima/src/daemon/api/mod.rs
@@ -2,6 +2,7 @@
pub mod client;
pub mod contract;
+pub mod directive;
pub mod supervisor;
pub use client::ApiClient;
diff --git a/makima/src/daemon/cli/directive.rs b/makima/src/daemon/cli/directive.rs
new file mode 100644
index 0000000..5de60ed
--- /dev/null
+++ b/makima/src/daemon/cli/directive.rs
@@ -0,0 +1,101 @@
+//! Directive subcommand - directive management commands for orchestrator tasks.
+
+use clap::Args;
+use uuid::Uuid;
+
+/// Common arguments for directive commands.
+#[derive(Args, Debug, Clone)]
+pub struct DirectiveArgs {
+ /// API URL
+ #[arg(long, env = "MAKIMA_API_URL", default_value = "https://api.makima.jp", global = true)]
+ pub api_url: String,
+
+ /// API key for authentication
+ #[arg(long, env = "MAKIMA_API_KEY", global = true)]
+ pub api_key: String,
+
+ /// Directive ID
+ #[arg(long, env = "MAKIMA_DIRECTIVE_ID", global = true)]
+ pub directive_id: Uuid,
+}
+
+/// Arguments for listing directives (no directive_id required).
+#[derive(Args, Debug, Clone)]
+pub struct DirectiveListArgs {
+ /// API URL
+ #[arg(long, env = "MAKIMA_API_URL", default_value = "https://api.makima.jp", global = true)]
+ pub api_url: String,
+
+ /// API key for authentication
+ #[arg(long, env = "MAKIMA_API_KEY", global = true)]
+ pub api_key: String,
+}
+
+/// Arguments for add-step command.
+#[derive(Args, Debug)]
+pub struct AddStepArgs {
+ #[command(flatten)]
+ pub common: DirectiveArgs,
+
+ /// Step name
+ pub name: String,
+
+ /// Step description
+ #[arg(long)]
+ pub description: Option<String>,
+
+ /// Task plan for the step
+ #[arg(long)]
+ pub task_plan: Option<String>,
+
+ /// Comma-separated UUIDs of dependency steps
+ #[arg(long)]
+ pub depends_on: Option<String>,
+
+ /// Order index
+ #[arg(long, default_value = "0")]
+ pub order_index: i32,
+}
+
+/// Arguments for remove-step command.
+#[derive(Args, Debug)]
+pub struct RemoveStepArgs {
+ #[command(flatten)]
+ pub common: DirectiveArgs,
+
+ /// Step ID to remove
+ pub step_id: Uuid,
+}
+
+/// Arguments for set-deps command.
+#[derive(Args, Debug)]
+pub struct SetDepsArgs {
+ #[command(flatten)]
+ pub common: DirectiveArgs,
+
+ /// Step ID to update
+ pub step_id: Uuid,
+
+ /// Comma-separated UUIDs of dependency steps
+ pub depends_on: String,
+}
+
+/// Arguments for complete-step/fail-step/skip-step commands.
+#[derive(Args, Debug)]
+pub struct StepActionArgs {
+ #[command(flatten)]
+ pub common: DirectiveArgs,
+
+ /// Step ID
+ pub step_id: Uuid,
+}
+
+/// Arguments for update-goal command.
+#[derive(Args, Debug)]
+pub struct UpdateGoalArgs {
+ #[command(flatten)]
+ pub common: DirectiveArgs,
+
+ /// New goal text
+ pub goal: String,
+}
diff --git a/makima/src/daemon/cli/mod.rs b/makima/src/daemon/cli/mod.rs
index 0805edd..faafaea 100644
--- a/makima/src/daemon/cli/mod.rs
+++ b/makima/src/daemon/cli/mod.rs
@@ -3,6 +3,7 @@
pub mod config;
pub mod contract;
pub mod daemon;
+pub mod directive;
pub mod server;
pub mod supervisor;
pub mod view;
@@ -12,6 +13,7 @@ use clap::{Parser, Subcommand};
pub use config::CliConfig;
pub use contract::ContractArgs;
pub use daemon::DaemonArgs;
+pub use directive::DirectiveArgs;
pub use server::ServerArgs;
pub use supervisor::SupervisorArgs;
pub use view::ViewArgs;
@@ -41,6 +43,10 @@ pub enum Commands {
#[command(subcommand)]
Contract(ContractCommand),
+ /// Directive commands for DAG-based project management
+ #[command(subcommand)]
+ Directive(DirectiveCommand),
+
/// Interactive TUI browser for contracts and tasks
///
/// Provides a drill-down interface for browsing contracts, viewing their
@@ -196,6 +202,49 @@ pub enum ContractCommand {
CreateFile(contract::CreateFileArgs),
}
+/// Directive subcommands for DAG-based project management.
+#[derive(Subcommand, Debug)]
+pub enum DirectiveCommand {
+ /// List all directives
+ List(directive::DirectiveListArgs),
+
+ /// Get directive status with steps
+ Get(DirectiveArgs),
+
+ /// Get directive status (alias for get)
+ Status(DirectiveArgs),
+
+ /// Add a step to the directive
+ AddStep(directive::AddStepArgs),
+
+ /// Remove a step from the directive
+ RemoveStep(directive::RemoveStepArgs),
+
+ /// Set dependencies for a step
+ SetDeps(directive::SetDepsArgs),
+
+ /// Start the directive (begin executing steps)
+ Start(DirectiveArgs),
+
+ /// Pause the directive
+ Pause(DirectiveArgs),
+
+ /// Advance the DAG (find newly-ready steps)
+ Advance(DirectiveArgs),
+
+ /// Mark a step as completed
+ CompleteStep(directive::StepActionArgs),
+
+ /// Mark a step as failed
+ FailStep(directive::StepActionArgs),
+
+ /// Mark a step as skipped
+ SkipStep(directive::StepActionArgs),
+
+ /// Update the directive's goal (triggers re-planning)
+ UpdateGoal(directive::UpdateGoalArgs),
+}
+
impl Cli {
/// Parse command-line arguments
pub fn parse_args() -> Self {
diff --git a/makima/src/daemon/skills/directive.md b/makima/src/daemon/skills/directive.md
new file mode 100644
index 0000000..7c55cf8
--- /dev/null
+++ b/makima/src/daemon/skills/directive.md
@@ -0,0 +1,111 @@
+---
+name: makima-directive
+description: Directive commands for makima DAG-based project orchestration. Use these commands to manage long-lived directives with auto-progressing steps.
+---
+
+# Makima Directive Skill
+
+You are orchestrating a **directive** — a long-lived project managed through a DAG (directed acyclic graph) of steps. Unlike contracts which are finite and phase-based, directives are **ongoing and continuous**: they stay active as the project evolves, and new features/requirements can be added at any time.
+
+## Key Concepts
+
+- **Directive**: A long-lived top-level entity with a goal, repository info, and a mutable DAG of steps
+- **Steps**: Nodes in the DAG. Each step can spawn a task using the mesh infrastructure
+- **Auto-progression**: When a step completes, newly-ready steps (whose dependencies are met) automatically become ready
+- **Continuous evolution**: The goal can be updated at any time. When all steps complete, the directive goes `idle` (not completed) — waiting for new work
+- **Statuses**: `draft` → `active` ↔ `idle` → `archived`. Directives are never "completed" — they go idle and wait
+
+## Commands
+
+### Check Status
+```bash
+makima directive status
+```
+Returns the directive with all steps, their statuses, and dependency information.
+
+### Add a Step
+```bash
+makima directive add-step "Step Name" --description "What this step does" --task-plan "Detailed instructions for the task" --depends-on "uuid1,uuid2" --order-index 1
+```
+
+### Remove a Step
+```bash
+makima directive remove-step <step_id>
+```
+
+### Set Dependencies
+```bash
+makima directive set-deps <step_id> "dep_uuid1,dep_uuid2"
+```
+
+### Start the Directive
+```bash
+makima directive start
+```
+Sets status to `active` and advances any steps with no dependencies to `ready`.
+
+### Advance the DAG
+```bash
+makima directive advance
+```
+Finds newly-ready steps (all dependencies met) and marks them ready. If all steps are in terminal states, sets the directive to `idle`.
+
+### Complete a Step
+```bash
+makima directive complete-step <step_id>
+```
+
+### Fail a Step
+```bash
+makima directive fail-step <step_id>
+```
+
+### Skip a Step
+```bash
+makima directive skip-step <step_id>
+```
+
+### Update the Goal
+```bash
+makima directive update-goal "New or expanded goal text"
+```
+Updates the goal and bumps `goalUpdatedAt`. If the directive is `idle`, it reactivates to `active`.
+
+### Pause
+```bash
+makima directive pause
+```
+
+## Orchestration Workflow
+
+### Initial Setup
+1. Check the directive status to understand the goal
+2. Decompose the goal into steps with clear dependencies
+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
+
+### Monitoring and Advancing
+1. Periodically check status to see step progress
+2. When tasks complete, the DAG auto-advances — newly-ready steps appear
+3. Use `advance` to manually trigger DAG progression if needed
+4. Mark steps as complete/failed/skipped as appropriate
+
+### Re-planning (When Goal Updates)
+When the goal is updated (you'll see a new `goalUpdatedAt` timestamp):
+1. Check the current status to see completed and in-progress steps
+2. Identify what's new in the updated goal
+3. Add new steps that depend on existing completed steps as appropriate
+4. The DAG will auto-advance any newly-ready steps
+
+### Idle State
+When all steps complete, the directive enters `idle` state. This is normal — it means:
+- All current work is done
+- The directive is waiting for new requirements
+- When the user updates the goal, it reactivates automatically
+- You should add new steps based on the updated goal
+
+## Environment Variables
+- `MAKIMA_API_URL` - API server URL
+- `MAKIMA_API_KEY` - Authentication key
+- `MAKIMA_DIRECTIVE_ID` - Current directive ID (set automatically)
diff --git a/makima/src/daemon/skills/mod.rs b/makima/src/daemon/skills/mod.rs
index 0b05f3a..0c015ba 100644
--- a/makima/src/daemon/skills/mod.rs
+++ b/makima/src/daemon/skills/mod.rs
@@ -9,8 +9,12 @@ pub const SUPERVISOR_SKILL: &str = include_str!("supervisor.md");
/// Contract skill content - task-contract interaction commands
pub const CONTRACT_SKILL: &str = include_str!("contract.md");
+/// Directive skill content - DAG-based project orchestration commands
+pub const DIRECTIVE_SKILL: &str = include_str!("directive.md");
+
/// All skills as (name, content) pairs for installation
pub const ALL_SKILLS: &[(&str, &str)] = &[
("makima-supervisor", SUPERVISOR_SKILL),
("makima-contract", CONTRACT_SKILL),
+ ("makima-directive", DIRECTIVE_SKILL),
];