astrocode-workflow 0.0.1

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.
Files changed (133) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +85 -0
  3. package/dist/agents/commands.d.ts +9 -0
  4. package/dist/agents/commands.js +121 -0
  5. package/dist/agents/prompts.d.ts +2 -0
  6. package/dist/agents/prompts.js +27 -0
  7. package/dist/agents/registry.d.ts +6 -0
  8. package/dist/agents/registry.js +223 -0
  9. package/dist/agents/types.d.ts +14 -0
  10. package/dist/agents/types.js +8 -0
  11. package/dist/config/config-handler.d.ts +4 -0
  12. package/dist/config/config-handler.js +46 -0
  13. package/dist/config/defaults.d.ts +3 -0
  14. package/dist/config/defaults.js +3 -0
  15. package/dist/config/loader.d.ts +11 -0
  16. package/dist/config/loader.js +48 -0
  17. package/dist/config/schema.d.ts +176 -0
  18. package/dist/config/schema.js +198 -0
  19. package/dist/hooks/continuation-enforcer.d.ts +26 -0
  20. package/dist/hooks/continuation-enforcer.js +166 -0
  21. package/dist/hooks/tool-output-truncator.d.ts +17 -0
  22. package/dist/hooks/tool-output-truncator.js +56 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.js +108 -0
  25. package/dist/shared/deep-merge.d.ts +8 -0
  26. package/dist/shared/deep-merge.js +25 -0
  27. package/dist/shared/hash.d.ts +1 -0
  28. package/dist/shared/hash.js +4 -0
  29. package/dist/shared/log.d.ts +7 -0
  30. package/dist/shared/log.js +24 -0
  31. package/dist/shared/model-tuning.d.ts +9 -0
  32. package/dist/shared/model-tuning.js +28 -0
  33. package/dist/shared/paths.d.ts +19 -0
  34. package/dist/shared/paths.js +51 -0
  35. package/dist/shared/text.d.ts +4 -0
  36. package/dist/shared/text.js +19 -0
  37. package/dist/shared/time.d.ts +1 -0
  38. package/dist/shared/time.js +3 -0
  39. package/dist/state/adapters/index.d.ts +39 -0
  40. package/dist/state/adapters/index.js +119 -0
  41. package/dist/state/db.d.ts +17 -0
  42. package/dist/state/db.js +83 -0
  43. package/dist/state/ids.d.ts +8 -0
  44. package/dist/state/ids.js +25 -0
  45. package/dist/state/schema.d.ts +2 -0
  46. package/dist/state/schema.js +247 -0
  47. package/dist/state/types.d.ts +70 -0
  48. package/dist/state/types.js +1 -0
  49. package/dist/tools/artifacts.d.ts +18 -0
  50. package/dist/tools/artifacts.js +71 -0
  51. package/dist/tools/index.d.ts +8 -0
  52. package/dist/tools/index.js +100 -0
  53. package/dist/tools/init.d.ts +8 -0
  54. package/dist/tools/init.js +41 -0
  55. package/dist/tools/injects.d.ts +23 -0
  56. package/dist/tools/injects.js +99 -0
  57. package/dist/tools/repair.d.ts +8 -0
  58. package/dist/tools/repair.js +25 -0
  59. package/dist/tools/run.d.ts +13 -0
  60. package/dist/tools/run.js +54 -0
  61. package/dist/tools/spec.d.ts +13 -0
  62. package/dist/tools/spec.js +41 -0
  63. package/dist/tools/stage.d.ts +23 -0
  64. package/dist/tools/stage.js +284 -0
  65. package/dist/tools/status.d.ts +8 -0
  66. package/dist/tools/status.js +107 -0
  67. package/dist/tools/story.d.ts +23 -0
  68. package/dist/tools/story.js +85 -0
  69. package/dist/tools/workflow.d.ts +8 -0
  70. package/dist/tools/workflow.js +197 -0
  71. package/dist/ui/inject.d.ts +5 -0
  72. package/dist/ui/inject.js +9 -0
  73. package/dist/ui/toasts.d.ts +13 -0
  74. package/dist/ui/toasts.js +39 -0
  75. package/dist/workflow/artifacts.d.ts +24 -0
  76. package/dist/workflow/artifacts.js +45 -0
  77. package/dist/workflow/baton.d.ts +66 -0
  78. package/dist/workflow/baton.js +101 -0
  79. package/dist/workflow/context.d.ts +12 -0
  80. package/dist/workflow/context.js +67 -0
  81. package/dist/workflow/directives.d.ts +37 -0
  82. package/dist/workflow/directives.js +111 -0
  83. package/dist/workflow/repair.d.ts +8 -0
  84. package/dist/workflow/repair.js +99 -0
  85. package/dist/workflow/state-machine.d.ts +43 -0
  86. package/dist/workflow/state-machine.js +127 -0
  87. package/dist/workflow/story-helpers.d.ts +9 -0
  88. package/dist/workflow/story-helpers.js +13 -0
  89. package/package.json +32 -0
  90. package/src/agents/commands.ts +137 -0
  91. package/src/agents/prompts.ts +28 -0
  92. package/src/agents/registry.ts +310 -0
  93. package/src/agents/types.ts +31 -0
  94. package/src/config/config-handler.ts +48 -0
  95. package/src/config/defaults.ts +4 -0
  96. package/src/config/loader.ts +55 -0
  97. package/src/config/schema.ts +236 -0
  98. package/src/hooks/continuation-enforcer.ts +217 -0
  99. package/src/hooks/tool-output-truncator.ts +82 -0
  100. package/src/index.ts +131 -0
  101. package/src/shared/deep-merge.ts +28 -0
  102. package/src/shared/hash.ts +5 -0
  103. package/src/shared/log.ts +30 -0
  104. package/src/shared/model-tuning.ts +48 -0
  105. package/src/shared/paths.ts +70 -0
  106. package/src/shared/text.ts +20 -0
  107. package/src/shared/time.ts +3 -0
  108. package/src/shims.node.d.ts +20 -0
  109. package/src/state/adapters/index.ts +155 -0
  110. package/src/state/db.ts +105 -0
  111. package/src/state/ids.ts +33 -0
  112. package/src/state/schema.ts +249 -0
  113. package/src/state/types.ts +76 -0
  114. package/src/tools/artifacts.ts +83 -0
  115. package/src/tools/index.ts +111 -0
  116. package/src/tools/init.ts +50 -0
  117. package/src/tools/injects.ts +108 -0
  118. package/src/tools/repair.ts +31 -0
  119. package/src/tools/run.ts +62 -0
  120. package/src/tools/spec.ts +50 -0
  121. package/src/tools/stage.ts +361 -0
  122. package/src/tools/status.ts +119 -0
  123. package/src/tools/story.ts +106 -0
  124. package/src/tools/workflow.ts +241 -0
  125. package/src/ui/inject.ts +13 -0
  126. package/src/ui/toasts.ts +48 -0
  127. package/src/workflow/artifacts.ts +69 -0
  128. package/src/workflow/baton.ts +141 -0
  129. package/src/workflow/context.ts +86 -0
  130. package/src/workflow/directives.ts +170 -0
  131. package/src/workflow/repair.ts +138 -0
  132. package/src/workflow/state-machine.ts +194 -0
  133. package/src/workflow/story-helpers.ts +18 -0
@@ -0,0 +1,247 @@
1
+ // src/state/schema.ts
2
+ // NOTE: This schema is intentionally additive over the original Astrocode schema.
3
+ // vNext adds continuation/snapshot/session tables and stronger indexes.
4
+ //
5
+ // Source of truth: SQLite file at .astro/astro.db
6
+ export const SCHEMA_VERSION = 2;
7
+ export const SCHEMA_SQL = `
8
+ PRAGMA foreign_keys = ON;
9
+
10
+ CREATE TABLE IF NOT EXISTS repo_state (
11
+ id INTEGER PRIMARY KEY CHECK (id = 1),
12
+ schema_version INTEGER NOT NULL,
13
+ created_at TEXT NOT NULL,
14
+ updated_at TEXT NOT NULL,
15
+ spec_hash_before TEXT,
16
+ spec_hash_after TEXT,
17
+ last_run_id TEXT,
18
+ last_story_key TEXT,
19
+ last_event_at TEXT
20
+ );
21
+
22
+ CREATE TABLE IF NOT EXISTS settings (
23
+ key TEXT PRIMARY KEY,
24
+ value TEXT NOT NULL,
25
+ updated_at TEXT NOT NULL
26
+ );
27
+
28
+ CREATE TABLE IF NOT EXISTS epics (
29
+ epic_key TEXT PRIMARY KEY,
30
+ title TEXT NOT NULL,
31
+ body_md TEXT NOT NULL DEFAULT '',
32
+ state TEXT NOT NULL DEFAULT 'active',
33
+ priority INTEGER NOT NULL DEFAULT 0,
34
+ created_at TEXT NOT NULL,
35
+ updated_at TEXT NOT NULL
36
+ );
37
+
38
+ CREATE TABLE IF NOT EXISTS story_drafts (
39
+ draft_id TEXT PRIMARY KEY,
40
+ title TEXT NOT NULL,
41
+ body_md TEXT NOT NULL DEFAULT '',
42
+ meta_json TEXT NOT NULL DEFAULT '{}',
43
+ created_at TEXT NOT NULL,
44
+ updated_at TEXT NOT NULL
45
+ );
46
+
47
+ CREATE TABLE IF NOT EXISTS story_keyseq (
48
+ id INTEGER PRIMARY KEY CHECK (id = 1),
49
+ next_story_num INTEGER NOT NULL
50
+ );
51
+
52
+ CREATE TABLE IF NOT EXISTS stories (
53
+ story_key TEXT PRIMARY KEY,
54
+ epic_key TEXT,
55
+ title TEXT NOT NULL,
56
+ body_md TEXT NOT NULL DEFAULT '',
57
+ state TEXT NOT NULL DEFAULT 'queued', -- queued|approved|in_progress|done|blocked|archived
58
+ priority INTEGER NOT NULL DEFAULT 0,
59
+ approved_at TEXT,
60
+ locked_by_run_id TEXT,
61
+ locked_at TEXT,
62
+ in_progress INTEGER NOT NULL DEFAULT 0,
63
+ created_at TEXT NOT NULL,
64
+ updated_at TEXT NOT NULL,
65
+ FOREIGN KEY (epic_key) REFERENCES epics(epic_key)
66
+ );
67
+
68
+ CREATE TABLE IF NOT EXISTS runs (
69
+ run_id TEXT PRIMARY KEY,
70
+ story_key TEXT NOT NULL,
71
+ status TEXT NOT NULL DEFAULT 'created', -- created|running|completed|failed|aborted
72
+ pipeline_stages_json TEXT NOT NULL DEFAULT '[]',
73
+ current_stage_key TEXT,
74
+ created_at TEXT NOT NULL,
75
+ started_at TEXT,
76
+ completed_at TEXT,
77
+ updated_at TEXT NOT NULL,
78
+ error_text TEXT,
79
+ FOREIGN KEY (story_key) REFERENCES stories(story_key)
80
+ );
81
+
82
+ CREATE TABLE IF NOT EXISTS stage_runs (
83
+ stage_run_id TEXT PRIMARY KEY,
84
+ run_id TEXT NOT NULL,
85
+ stage_key TEXT NOT NULL,
86
+ stage_index INTEGER NOT NULL,
87
+ status TEXT NOT NULL DEFAULT 'pending', -- pending|running|completed|failed|skipped
88
+ subagent_type TEXT,
89
+ subagent_session_id TEXT,
90
+ started_at TEXT,
91
+ completed_at TEXT,
92
+ updated_at TEXT NOT NULL,
93
+ baton_path TEXT,
94
+ summary_md TEXT,
95
+ output_json TEXT,
96
+ error_text TEXT,
97
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
98
+ );
99
+
100
+ CREATE TABLE IF NOT EXISTS artifacts (
101
+ artifact_id TEXT PRIMARY KEY,
102
+ run_id TEXT,
103
+ stage_key TEXT,
104
+ type TEXT NOT NULL, -- plan|baton|evidence|diff|log|summary|commit|tool_output|snapshot
105
+ path TEXT NOT NULL,
106
+ sha256 TEXT,
107
+ meta_json TEXT NOT NULL DEFAULT '{}',
108
+ created_at TEXT NOT NULL,
109
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
110
+ );
111
+
112
+ CREATE TABLE IF NOT EXISTS tool_runs (
113
+ tool_run_id TEXT PRIMARY KEY,
114
+ run_id TEXT,
115
+ stage_key TEXT,
116
+ tool_name TEXT NOT NULL,
117
+ args_json TEXT NOT NULL DEFAULT '{}',
118
+ output_summary TEXT NOT NULL DEFAULT '',
119
+ output_artifact_id TEXT,
120
+ created_at TEXT NOT NULL,
121
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
122
+ );
123
+
124
+ CREATE TABLE IF NOT EXISTS events (
125
+ event_id TEXT PRIMARY KEY,
126
+ run_id TEXT,
127
+ stage_key TEXT,
128
+ type TEXT NOT NULL,
129
+ body_json TEXT NOT NULL DEFAULT '{}',
130
+ created_at TEXT NOT NULL,
131
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
132
+ );
133
+
134
+ CREATE TABLE IF NOT EXISTS injects (
135
+ inject_id TEXT PRIMARY KEY,
136
+ type TEXT NOT NULL DEFAULT 'note',
137
+ title TEXT NOT NULL,
138
+ body_md TEXT NOT NULL,
139
+ tags_json TEXT NOT NULL DEFAULT '[]',
140
+ scope TEXT NOT NULL DEFAULT 'repo', -- repo|run:<id>|story:<key>|global
141
+ source TEXT NOT NULL DEFAULT 'user', -- user|tool|agent|import
142
+ priority INTEGER NOT NULL DEFAULT 50,
143
+ expires_at TEXT,
144
+ sha256 TEXT,
145
+ created_at TEXT NOT NULL,
146
+ updated_at TEXT NOT NULL
147
+ );
148
+
149
+ CREATE TABLE IF NOT EXISTS running_batches (
150
+ batch_id TEXT PRIMARY KEY,
151
+ run_id TEXT,
152
+ session_id TEXT,
153
+ status TEXT NOT NULL DEFAULT 'running', -- running|completed|failed|aborted
154
+ created_at TEXT NOT NULL,
155
+ updated_at TEXT NOT NULL,
156
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
157
+ );
158
+
159
+ CREATE TABLE IF NOT EXISTS workflow_metrics (
160
+ metric_id TEXT PRIMARY KEY,
161
+ run_id TEXT,
162
+ stage_key TEXT,
163
+ name TEXT NOT NULL,
164
+ value_num REAL,
165
+ value_text TEXT,
166
+ created_at TEXT NOT NULL,
167
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
168
+ );
169
+
170
+ CREATE TABLE IF NOT EXISTS template_intents (
171
+ intent_key TEXT PRIMARY KEY,
172
+ body_md TEXT NOT NULL,
173
+ updated_at TEXT NOT NULL
174
+ );
175
+
176
+ -- vNext tables
177
+
178
+ CREATE TABLE IF NOT EXISTS story_relations (
179
+ parent_story_key TEXT NOT NULL,
180
+ child_story_key TEXT NOT NULL,
181
+ relation_type TEXT NOT NULL DEFAULT 'split',
182
+ reason TEXT NOT NULL DEFAULT '',
183
+ created_at TEXT NOT NULL,
184
+ PRIMARY KEY (parent_story_key, child_story_key),
185
+ FOREIGN KEY (parent_story_key) REFERENCES stories(story_key),
186
+ FOREIGN KEY (child_story_key) REFERENCES stories(story_key)
187
+ );
188
+
189
+ CREATE TABLE IF NOT EXISTS continuations (
190
+ continuation_id INTEGER PRIMARY KEY AUTOINCREMENT,
191
+ session_id TEXT NOT NULL,
192
+ run_id TEXT,
193
+ directive_hash TEXT NOT NULL,
194
+ kind TEXT NOT NULL, -- continue|stage|blocked|repair
195
+ reason TEXT NOT NULL DEFAULT '',
196
+ created_at TEXT NOT NULL,
197
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
198
+ );
199
+
200
+ CREATE INDEX IF NOT EXISTS idx_continuations_session_created ON continuations(session_id, created_at DESC);
201
+ CREATE INDEX IF NOT EXISTS idx_continuations_run_created ON continuations(run_id, created_at DESC);
202
+
203
+ CREATE TABLE IF NOT EXISTS context_snapshots (
204
+ snapshot_id TEXT PRIMARY KEY,
205
+ run_id TEXT NOT NULL,
206
+ stage_key TEXT NOT NULL,
207
+ summary_md TEXT NOT NULL,
208
+ created_at TEXT NOT NULL,
209
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
210
+ );
211
+
212
+ CREATE INDEX IF NOT EXISTS idx_context_snapshots_run_created ON context_snapshots(run_id, created_at DESC);
213
+
214
+ CREATE TABLE IF NOT EXISTS agent_sessions (
215
+ session_id TEXT PRIMARY KEY,
216
+ parent_session_id TEXT,
217
+ agent_name TEXT NOT NULL,
218
+ run_id TEXT,
219
+ stage_key TEXT,
220
+ status TEXT NOT NULL DEFAULT 'active',
221
+ created_at TEXT NOT NULL,
222
+ updated_at TEXT NOT NULL
223
+ );
224
+
225
+ -- Indexes
226
+
227
+ CREATE INDEX IF NOT EXISTS idx_stories_state ON stories(state);
228
+ CREATE INDEX IF NOT EXISTS idx_runs_story ON runs(story_key);
229
+ CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status);
230
+ CREATE INDEX IF NOT EXISTS idx_stage_runs_run ON stage_runs(run_id, stage_index);
231
+ CREATE INDEX IF NOT EXISTS idx_artifacts_run_stage ON artifacts(run_id, stage_key, created_at DESC);
232
+ CREATE INDEX IF NOT EXISTS idx_events_run ON events(run_id, created_at DESC);
233
+ CREATE INDEX IF NOT EXISTS idx_tool_runs_run ON tool_runs(run_id, created_at DESC);
234
+ CREATE INDEX IF NOT EXISTS idx_injects_scope_priority ON injects(scope, priority DESC, created_at DESC);
235
+
236
+ -- Stronger invariants (SQLite partial indexes)
237
+ -- Only one run may be 'running' at a time (single-repo harness by default).
238
+ CREATE UNIQUE INDEX IF NOT EXISTS uniq_single_running_run
239
+ ON runs(status)
240
+ WHERE status = 'running';
241
+
242
+ -- Only one story may be in_progress=1 at a time (pairs with single running run).
243
+ CREATE UNIQUE INDEX IF NOT EXISTS uniq_single_in_progress_story
244
+ ON stories(in_progress)
245
+ WHERE in_progress = 1;
246
+
247
+ `;
@@ -0,0 +1,70 @@
1
+ export type StoryState = "queued" | "approved" | "in_progress" | "done" | "blocked" | "archived";
2
+ export type RunStatus = "created" | "running" | "completed" | "failed" | "aborted";
3
+ export type StageStatus = "pending" | "running" | "completed" | "failed" | "skipped";
4
+ export type StageKey = "frame" | "plan" | "spec" | "implement" | "review" | "verify" | "close";
5
+ export type StoryRow = {
6
+ story_key: string;
7
+ epic_key: string | null;
8
+ title: string;
9
+ body_md: string;
10
+ state: StoryState;
11
+ priority: number;
12
+ approved_at: string | null;
13
+ locked_by_run_id: string | null;
14
+ locked_at: string | null;
15
+ in_progress: 0 | 1;
16
+ created_at: string;
17
+ updated_at: string;
18
+ };
19
+ export type RunRow = {
20
+ run_id: string;
21
+ story_key: string;
22
+ status: RunStatus;
23
+ pipeline_stages_json: string;
24
+ current_stage_key: string | null;
25
+ created_at: string;
26
+ started_at: string | null;
27
+ completed_at: string | null;
28
+ updated_at: string;
29
+ error_text: string | null;
30
+ };
31
+ export type StageRunRow = {
32
+ stage_run_id: string;
33
+ run_id: string;
34
+ stage_key: StageKey;
35
+ stage_index: number;
36
+ status: StageStatus;
37
+ subagent_type: string | null;
38
+ subagent_session_id: string | null;
39
+ started_at: string | null;
40
+ completed_at: string | null;
41
+ updated_at: string;
42
+ baton_path: string | null;
43
+ summary_md: string | null;
44
+ output_json: string | null;
45
+ error_text: string | null;
46
+ };
47
+ export type ArtifactRow = {
48
+ artifact_id: string;
49
+ run_id: string | null;
50
+ stage_key: string | null;
51
+ type: string;
52
+ path: string;
53
+ sha256: string | null;
54
+ meta_json: string;
55
+ created_at: string;
56
+ };
57
+ export type InjectRow = {
58
+ inject_id: string;
59
+ type: string;
60
+ title: string;
61
+ body_md: string;
62
+ tags_json: string;
63
+ scope: string;
64
+ source: string;
65
+ priority: number;
66
+ expires_at: string | null;
67
+ sha256: string | null;
68
+ created_at: string;
69
+ updated_at: string;
70
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
+ import type { AstrocodeConfig } from "../config/schema";
3
+ import type { SqliteDb } from "../state/db";
4
+ export declare function createAstroArtifactPutTool(opts: {
5
+ ctx: any;
6
+ config: AstrocodeConfig;
7
+ db: SqliteDb;
8
+ }): ToolDefinition;
9
+ export declare function createAstroArtifactListTool(opts: {
10
+ ctx: any;
11
+ config: AstrocodeConfig;
12
+ db: SqliteDb;
13
+ }): ToolDefinition;
14
+ export declare function createAstroArtifactGetTool(opts: {
15
+ ctx: any;
16
+ config: AstrocodeConfig;
17
+ db: SqliteDb;
18
+ }): ToolDefinition;
@@ -0,0 +1,71 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { tool } from "@opencode-ai/plugin/tool";
4
+ import { putArtifact, listArtifacts, getArtifact } from "../workflow/artifacts";
5
+ export function createAstroArtifactPutTool(opts) {
6
+ const { ctx, db } = opts;
7
+ return tool({
8
+ description: "Write an artifact file under .astro and record it in the DB.",
9
+ args: {
10
+ run_id: tool.schema.string().nullable().optional(),
11
+ stage_key: tool.schema.string().nullable().optional(),
12
+ type: tool.schema.string().default("log"),
13
+ rel_path: tool.schema.string().min(1),
14
+ content: tool.schema.string().default(""),
15
+ meta_json: tool.schema.string().default("{}"),
16
+ },
17
+ execute: async ({ run_id, stage_key, type, rel_path, content, meta_json }) => {
18
+ const repoRoot = ctx.directory;
19
+ const meta = JSON.parse(meta_json || "{}");
20
+ const res = putArtifact({
21
+ repoRoot,
22
+ db,
23
+ run_id: run_id ?? null,
24
+ stage_key: stage_key ?? null,
25
+ type,
26
+ rel_path,
27
+ content,
28
+ meta,
29
+ });
30
+ return `✅ Wrote artifact ${res.artifact_id} (${type}) at ${rel_path} (sha256=${res.sha256})`;
31
+ },
32
+ });
33
+ }
34
+ export function createAstroArtifactListTool(opts) {
35
+ const { db } = opts;
36
+ return tool({
37
+ description: "List artifacts (optionally filtered by run_id, stage_key, type).",
38
+ args: {
39
+ run_id: tool.schema.string().optional(),
40
+ stage_key: tool.schema.string().optional(),
41
+ type: tool.schema.string().optional(),
42
+ },
43
+ execute: async ({ run_id, stage_key, type }) => {
44
+ const rows = listArtifacts(db, { run_id, stage_key, type });
45
+ return JSON.stringify(rows, null, 2);
46
+ },
47
+ });
48
+ }
49
+ export function createAstroArtifactGetTool(opts) {
50
+ const { ctx, config, db } = opts;
51
+ return tool({
52
+ description: "Get artifact metadata (and optionally file contents).",
53
+ args: {
54
+ artifact_id: tool.schema.string().min(1),
55
+ include_body: tool.schema.boolean().default(false),
56
+ max_body_chars: tool.schema.number().int().positive().default(50_000),
57
+ },
58
+ execute: async ({ artifact_id, include_body, max_body_chars }) => {
59
+ const row = getArtifact(db, artifact_id);
60
+ if (!row)
61
+ throw new Error(`Artifact not found: ${artifact_id}`);
62
+ if (!include_body)
63
+ return JSON.stringify(row, null, 2);
64
+ const repoRoot = ctx.directory;
65
+ const abs = path.join(repoRoot, row.path);
66
+ const body = fs.existsSync(abs) ? fs.readFileSync(abs, "utf-8") : "(missing file)";
67
+ const clipped = body.length > max_body_chars ? body.slice(0, max_body_chars) + "\n…(truncated)" : body;
68
+ return JSON.stringify({ ...row, body: clipped }, null, 2);
69
+ },
70
+ });
71
+ }
@@ -0,0 +1,8 @@
1
+ import type { ToolDefinition } from "@opencode-ai/plugin/tool";
2
+ import type { AstrocodeConfig } from "../config/schema";
3
+ import type { SqliteDb } from "../state/db";
4
+ export declare function createAstroTools(opts: {
5
+ ctx: any;
6
+ config: AstrocodeConfig;
7
+ db: SqliteDb;
8
+ }): Record<string, ToolDefinition>;
@@ -0,0 +1,100 @@
1
+ import { createAstroInitTool } from "./init";
2
+ import { createAstroStatusTool } from "./status";
3
+ import { createAstroStoryQueueTool, createAstroStoryApproveTool, createAstroStoryBoardTool, createAstroStorySetStateTool } from "./story";
4
+ import { createAstroSpecGetTool, createAstroSpecSetTool } from "./spec";
5
+ import { createAstroRunGetTool, createAstroRunAbortTool } from "./run";
6
+ import { createAstroWorkflowProceedTool } from "./workflow";
7
+ import { createAstroStageStartTool, createAstroStageCompleteTool, createAstroStageFailTool, createAstroStageResetTool } from "./stage";
8
+ import { createAstroArtifactPutTool, createAstroArtifactListTool, createAstroArtifactGetTool } from "./artifacts";
9
+ import { createAstroInjectPutTool, createAstroInjectListTool, createAstroInjectSearchTool, createAstroInjectGetTool } from "./injects";
10
+ import { createAstroRepairTool } from "./repair";
11
+ export function createAstroTools(opts) {
12
+ const { ctx, config, db } = opts;
13
+ const hasDatabase = !!db;
14
+ const tools = {};
15
+ // Always available tools
16
+ tools.astro_status = createAstroStatusTool({ ctx, config, db });
17
+ // Always available tools (work without database)
18
+ tools.astro_status = createAstroStatusTool({ ctx, config, db });
19
+ tools.astro_spec_get = createAstroSpecGetTool({ ctx, config, db });
20
+ // Database-dependent tools
21
+ if (hasDatabase) {
22
+ tools.astro_init = createAstroInitTool({ ctx, config, db });
23
+ tools.astro_story_queue = createAstroStoryQueueTool({ ctx, config, db });
24
+ tools.astro_story_approve = createAstroStoryApproveTool({ ctx, config, db });
25
+ tools.astro_story_board = createAstroStoryBoardTool({ ctx, config, db });
26
+ tools.astro_story_set_state = createAstroStorySetStateTool({ ctx, config, db });
27
+ tools.astro_spec_set = createAstroSpecSetTool({ ctx, config, db });
28
+ tools.astro_run_get = createAstroRunGetTool({ ctx, config, db });
29
+ tools.astro_run_abort = createAstroRunAbortTool({ ctx, config, db });
30
+ tools.astro_workflow_proceed = createAstroWorkflowProceedTool({ ctx, config, db });
31
+ tools.astro_stage_start = createAstroStageStartTool({ ctx, config, db });
32
+ tools.astro_stage_complete = createAstroStageCompleteTool({ ctx, config, db });
33
+ tools.astro_stage_fail = createAstroStageFailTool({ ctx, config, db });
34
+ tools.astro_stage_reset = createAstroStageResetTool({ ctx, config, db });
35
+ tools.astro_artifact_put = createAstroArtifactPutTool({ ctx, config, db });
36
+ tools.astro_artifact_list = createAstroArtifactListTool({ ctx, config, db });
37
+ tools.astro_artifact_get = createAstroArtifactGetTool({ ctx, config, db });
38
+ tools.astro_inject_put = createAstroInjectPutTool({ ctx, config, db });
39
+ tools.astro_inject_list = createAstroInjectListTool({ ctx, config, db });
40
+ tools.astro_inject_search = createAstroInjectSearchTool({ ctx, config, db });
41
+ tools.astro_inject_get = createAstroInjectGetTool({ ctx, config, db });
42
+ tools.astro_repair = createAstroRepairTool({ ctx, config, db });
43
+ }
44
+ else {
45
+ // Limited mode tools - provide helpful messages instead of failing
46
+ tools.astro_init = {
47
+ description: "Initialize Astrocode (requires database - currently unavailable)",
48
+ args: {},
49
+ execute: async () => "❌ Database not available. Astrocode is running in limited mode."
50
+ };
51
+ tools.astro_story_queue = {
52
+ description: "Queue a story (requires database - currently unavailable)",
53
+ args: {},
54
+ execute: async () => "❌ Database not available. Astrocode is running in limited mode."
55
+ };
56
+ tools.astro_spec_set = {
57
+ description: "Set project spec (requires database for hash tracking - currently unavailable)",
58
+ args: {},
59
+ execute: async () => "❌ Database not available. Astrocode is running in limited mode."
60
+ };
61
+ tools.astro_workflow_proceed = {
62
+ description: "Advance workflow (requires database - currently unavailable)",
63
+ args: {},
64
+ execute: async () => "❌ Database not available. Astrocode is running in limited mode."
65
+ };
66
+ }
67
+ // Create aliases for backward compatibility
68
+ const aliases = [
69
+ ["_astro_init", "astro_init"],
70
+ ["_astro_status", "astro_status"],
71
+ ["_astro_story_queue", "astro_story_queue"],
72
+ ["_astro_story_approve", "astro_story_approve"],
73
+ ["_astro_story_board", "astro_story_board"],
74
+ ["_astro_story_set_state", "astro_story_set_state"],
75
+ ["_astro_spec_get", "astro_spec_get"],
76
+ ["_astro_spec_set", "astro_spec_set"],
77
+ ["_astro_run_get", "astro_run_get"],
78
+ ["_astro_run_abort", "astro_run_abort"],
79
+ ["_astro_workflow_proceed", "astro_workflow_proceed"],
80
+ ["_astro_stage_start", "astro_stage_start"],
81
+ ["_astro_stage_complete", "astro_stage_complete"],
82
+ ["_astro_stage_fail", "astro_stage_fail"],
83
+ ["_astro_stage_reset", "astro_stage_reset"],
84
+ ["_astro_artifact_put", "astro_artifact_put"],
85
+ ["_astro_artifact_list", "astro_artifact_list"],
86
+ ["_astro_artifact_get", "astro_artifact_get"],
87
+ ["_astro_inject_put", "astro_inject_put"],
88
+ ["_astro_inject_list", "astro_inject_list"],
89
+ ["_astro_inject_search", "astro_inject_search"],
90
+ ["_astro_inject_get", "astro_inject_get"],
91
+ ["_astro_repair", "astro_repair"],
92
+ ];
93
+ // Only add aliases for tools that exist
94
+ for (const [alias, target] of aliases) {
95
+ if (tools[target]) {
96
+ tools[alias] = tools[target];
97
+ }
98
+ }
99
+ return tools;
100
+ }
@@ -0,0 +1,8 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
+ import type { AstrocodeConfig } from "../config/schema";
3
+ import type { SqliteDb } from "../state/db";
4
+ export declare function createAstroInitTool(opts: {
5
+ ctx: any;
6
+ config: AstrocodeConfig;
7
+ db: SqliteDb;
8
+ }): ToolDefinition;
@@ -0,0 +1,41 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { tool } from "@opencode-ai/plugin/tool";
4
+ import { ensureSchema } from "../state/db";
5
+ import { getAstroPaths, ensureAstroDirs } from "../shared/paths";
6
+ import { nowISO } from "../shared/time";
7
+ import { sha256Hex } from "../shared/hash";
8
+ export function createAstroInitTool(opts) {
9
+ const { ctx, config, db } = opts;
10
+ return tool({
11
+ description: "Initialize Astrocode vNext in this repo: create .astro directories, ensure SQLite schema, and create a placeholder spec if missing. Idempotent.",
12
+ args: {
13
+ ensure_spec: tool.schema.boolean().default(true),
14
+ spec_placeholder: tool.schema.string().default("# Project Spec\n\n(Write the spec here.)\n"),
15
+ },
16
+ execute: async ({ ensure_spec, spec_placeholder }) => {
17
+ const repoRoot = ctx.directory;
18
+ const paths = getAstroPaths(repoRoot, config.db.path);
19
+ ensureAstroDirs(paths);
20
+ ensureSchema(db, { allowAutoMigrate: config.db.allow_auto_migrate, failOnDowngrade: config.db.fail_on_downgrade });
21
+ if (ensure_spec) {
22
+ if (!fs.existsSync(paths.specPath)) {
23
+ fs.writeFileSync(paths.specPath, spec_placeholder);
24
+ const specHash = sha256Hex(spec_placeholder);
25
+ const now = nowISO();
26
+ db.prepare("UPDATE repo_state SET spec_hash_after=?, updated_at=? WHERE id=1").run(specHash, now);
27
+ }
28
+ }
29
+ const stat = db.prepare("SELECT schema_version, created_at, updated_at FROM repo_state WHERE id=1").get();
30
+ return [
31
+ `✅ Astrocode initialized.`,
32
+ ``,
33
+ `- Repo: ${repoRoot}`,
34
+ `- DB: ${path.relative(repoRoot, paths.dbPath)} (schema_version=${stat?.schema_version ?? "?"})`,
35
+ `- Spec: ${path.relative(repoRoot, paths.specPath)}`,
36
+ ``,
37
+ `Next: queue a story (astro_story_queue) and approve it (astro_story_approve), or run /astro-status.`,
38
+ ].join("\n");
39
+ },
40
+ });
41
+ }
@@ -0,0 +1,23 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
+ import type { AstrocodeConfig } from "../config/schema";
3
+ import type { SqliteDb } from "../state/db";
4
+ export declare function createAstroInjectPutTool(opts: {
5
+ ctx: any;
6
+ config: AstrocodeConfig;
7
+ db: SqliteDb;
8
+ }): ToolDefinition;
9
+ export declare function createAstroInjectListTool(opts: {
10
+ ctx: any;
11
+ config: AstrocodeConfig;
12
+ db: SqliteDb;
13
+ }): ToolDefinition;
14
+ export declare function createAstroInjectGetTool(opts: {
15
+ ctx: any;
16
+ config: AstrocodeConfig;
17
+ db: SqliteDb;
18
+ }): ToolDefinition;
19
+ export declare function createAstroInjectSearchTool(opts: {
20
+ ctx: any;
21
+ config: AstrocodeConfig;
22
+ db: SqliteDb;
23
+ }): ToolDefinition;