Add initial Go CLI skeleton
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package inbox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/db"
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newInitCmd(opts *rootOptions) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Initialize the shared SQLite database schema",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
|
||||
sqlDB, err := db.Open(ctx, opts.dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sqlDB.Close()
|
||||
|
||||
if err := db.ApplyMigrations(ctx, sqlDB); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp := protocol.Success{
|
||||
OK: true,
|
||||
Command: "init",
|
||||
Data: map[string]any{
|
||||
"db_path": opts.dbPath,
|
||||
"status": "initialized",
|
||||
},
|
||||
}
|
||||
|
||||
if opts.json {
|
||||
return protocol.WriteJSON(cmd.OutOrStdout(), resp)
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintf(cmd.OutOrStdout(), "initialized database: %s\n", opts.dbPath)
|
||||
return err
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package inbox
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type rootOptions struct {
|
||||
dbPath string
|
||||
json bool
|
||||
agent string
|
||||
}
|
||||
|
||||
func NewRootCmd() *cobra.Command {
|
||||
opts := &rootOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "inbox",
|
||||
Short: "Worker-facing durable coordination bus",
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().StringVar(&opts.dbPath, "db", ".agents/coord.db", "SQLite database path")
|
||||
cmd.PersistentFlags().BoolVar(&opts.json, "json", false, "Emit machine-readable JSON")
|
||||
cmd.PersistentFlags().StringVar(&opts.agent, "agent", "", "Agent identity")
|
||||
|
||||
cmd.AddCommand(newInitCmd(opts))
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package orch
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type rootOptions struct {
|
||||
dbPath string
|
||||
json bool
|
||||
}
|
||||
|
||||
func NewRootCmd() *cobra.Command {
|
||||
opts := &rootOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "orch",
|
||||
Short: "Leader-facing scheduler and control plane",
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().StringVar(&opts.dbPath, "db", ".agents/coord.db", "SQLite database path")
|
||||
cmd.PersistentFlags().BoolVar(&opts.json, "json", false, "Emit machine-readable JSON")
|
||||
|
||||
cmd.AddCommand(newRunCmd())
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package orch
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
func newRunCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run management commands",
|
||||
}
|
||||
|
||||
cmd.AddCommand(&cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Stub for future run initialization",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cmd.Help()
|
||||
},
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
//go:embed schema/*.sql
|
||||
var schemaFS embed.FS
|
||||
|
||||
func ApplyMigrations(ctx context.Context, db *sql.DB) error {
|
||||
files, err := schemaFS.ReadDir("schema")
|
||||
if err != nil {
|
||||
return fmt.Errorf("read embedded schema directory: %w", err)
|
||||
}
|
||||
|
||||
names := make([]string, 0, len(files))
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
names = append(names, file.Name())
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("begin schema transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
for _, name := range names {
|
||||
content, err := schemaFS.ReadFile("schema/" + name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read embedded schema file %q: %w", name, err)
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, string(content)); err != nil {
|
||||
return fmt.Errorf("apply schema file %q: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return fmt.Errorf("commit schema transaction: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func Open(ctx context.Context, dbPath string) (*sql.DB, error) {
|
||||
if err := ensureParentDir(dbPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlite", dbPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open sqlite database: %w", err)
|
||||
}
|
||||
|
||||
if err := applyPragmas(ctx, db); err != nil {
|
||||
_ = db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func ensureParentDir(dbPath string) error {
|
||||
parent := filepath.Dir(dbPath)
|
||||
if parent == "." || parent == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.MkdirAll(parent, 0o755)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func applyPragmas(ctx context.Context, db *sql.DB) error {
|
||||
pragmas := []string{
|
||||
"PRAGMA foreign_keys = ON;",
|
||||
"PRAGMA journal_mode = WAL;",
|
||||
"PRAGMA busy_timeout = 5000;",
|
||||
}
|
||||
|
||||
for _, pragma := range pragmas {
|
||||
if _, err := db.ExecContext(ctx, pragma); err != nil {
|
||||
return fmt.Errorf("apply pragma %q: %w", pragma, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
CREATE TABLE IF NOT EXISTS threads (
|
||||
thread_id TEXT PRIMARY KEY,
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
subject TEXT NOT NULL,
|
||||
created_by TEXT NOT NULL,
|
||||
assigned_to TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
priority TEXT NOT NULL DEFAULT 'normal',
|
||||
latest_message_id TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
message_id TEXT PRIMARY KEY,
|
||||
thread_id TEXT NOT NULL,
|
||||
from_agent TEXT NOT NULL,
|
||||
to_agent TEXT NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
body TEXT NOT NULL DEFAULT '',
|
||||
payload_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY(thread_id) REFERENCES threads(thread_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS leases (
|
||||
thread_id TEXT PRIMARY KEY,
|
||||
agent_id TEXT NOT NULL,
|
||||
lease_token TEXT NOT NULL,
|
||||
claimed_at TEXT NOT NULL,
|
||||
expires_at TEXT NOT NULL,
|
||||
released_at TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS artifacts (
|
||||
artifact_id TEXT PRIMARY KEY,
|
||||
message_id TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
metadata_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY(message_id) REFERENCES messages(message_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_threads_status_assigned
|
||||
ON threads(status, assigned_to, updated_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_messages_thread_created
|
||||
ON messages(thread_id, created_at);
|
||||
@@ -0,0 +1,52 @@
|
||||
CREATE TABLE IF NOT EXISTS runs (
|
||||
run_id TEXT PRIMARY KEY,
|
||||
goal TEXT NOT NULL,
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL,
|
||||
default_to TEXT,
|
||||
priority TEXT NOT NULL DEFAULT 'normal',
|
||||
acceptance_json TEXT NOT NULL DEFAULT '[]',
|
||||
latest_attempt_no INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
PRIMARY KEY (run_id, task_id),
|
||||
FOREIGN KEY(run_id) REFERENCES runs(run_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_dependencies (
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
depends_on_task_id TEXT NOT NULL,
|
||||
PRIMARY KEY (run_id, task_id, depends_on_task_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_attempts (
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
attempt_no INTEGER NOT NULL,
|
||||
assigned_to TEXT NOT NULL,
|
||||
thread_id TEXT NOT NULL,
|
||||
base_ref TEXT,
|
||||
base_commit TEXT,
|
||||
branch_name TEXT,
|
||||
worktree_path TEXT,
|
||||
workspace_status TEXT,
|
||||
result_commit TEXT,
|
||||
status TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
PRIMARY KEY (run_id, task_id, attempt_no)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_run_status
|
||||
ON tasks(run_id, status, priority, updated_at);
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
thread_id TEXT,
|
||||
source TEXT NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
message_id TEXT,
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
payload_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_events_run_event
|
||||
ON events(run_id, event_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_events_thread_event
|
||||
ON events(thread_id, event_id);
|
||||
@@ -0,0 +1,45 @@
|
||||
CREATE TABLE IF NOT EXISTS council_runs (
|
||||
run_id TEXT PRIMARY KEY,
|
||||
mode TEXT NOT NULL,
|
||||
target_type TEXT NOT NULL,
|
||||
output_mode TEXT NOT NULL,
|
||||
only_unanimous INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS council_reviewers (
|
||||
run_id TEXT NOT NULL,
|
||||
reviewer_role TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
PRIMARY KEY (run_id, reviewer_role)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS council_findings (
|
||||
run_id TEXT NOT NULL,
|
||||
reviewer_role TEXT NOT NULL,
|
||||
finding_id TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
proposal TEXT NOT NULL,
|
||||
rationale TEXT NOT NULL,
|
||||
confidence TEXT NOT NULL,
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
target_refs_json TEXT NOT NULL DEFAULT '{}',
|
||||
PRIMARY KEY (run_id, reviewer_role, finding_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS council_groups (
|
||||
run_id TEXT NOT NULL,
|
||||
group_id TEXT NOT NULL,
|
||||
proposal TEXT NOT NULL,
|
||||
bucket TEXT NOT NULL,
|
||||
support_count INTEGER NOT NULL,
|
||||
supporters_json TEXT NOT NULL DEFAULT '[]',
|
||||
dissenters_json TEXT NOT NULL DEFAULT '[]',
|
||||
rationale_summary TEXT NOT NULL DEFAULT '',
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
source_finding_ids_json TEXT NOT NULL DEFAULT '[]',
|
||||
PRIMARY KEY (run_id, group_id)
|
||||
);
|
||||
@@ -0,0 +1,28 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Success struct {
|
||||
OK bool `json:"ok"`
|
||||
Command string `json:"command"`
|
||||
Data map[string]any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
OK bool `json:"ok"`
|
||||
Error ErrorPayload `json:"error"`
|
||||
}
|
||||
|
||||
type ErrorPayload struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func WriteJSON(w io.Writer, v any) error {
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(v)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package store
|
||||
|
||||
// Package store contains higher-level database access helpers.
|
||||
Reference in New Issue
Block a user