astrocode-workflow 0.4.4 → 0.4.6

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 (46) hide show
  1. package/dist/src/hooks/continuation-enforcer.d.ts +1 -9
  2. package/dist/src/hooks/continuation-enforcer.js +12 -2
  3. package/dist/src/hooks/inject-provider.d.ts +1 -9
  4. package/dist/src/hooks/inject-provider.js +14 -5
  5. package/dist/src/state/types.d.ts +9 -0
  6. package/dist/src/tools/artifacts.d.ts +4 -4
  7. package/dist/src/tools/artifacts.js +12 -3
  8. package/dist/src/tools/health.d.ts +2 -2
  9. package/dist/src/tools/health.js +18 -11
  10. package/dist/src/tools/index.js +27 -34
  11. package/dist/src/tools/injects.d.ts +8 -8
  12. package/dist/src/tools/injects.js +24 -6
  13. package/dist/src/tools/metrics.d.ts +6 -5
  14. package/dist/src/tools/repair.d.ts +2 -2
  15. package/dist/src/tools/repair.js +5 -1
  16. package/dist/src/tools/reset.d.ts +2 -2
  17. package/dist/src/tools/reset.js +9 -1
  18. package/dist/src/tools/run.d.ts +3 -3
  19. package/dist/src/tools/run.js +8 -2
  20. package/dist/src/tools/spec.d.ts +2 -2
  21. package/dist/src/tools/spec.js +3 -2
  22. package/dist/src/tools/stage.d.ts +5 -5
  23. package/dist/src/tools/stage.js +16 -4
  24. package/dist/src/tools/status.d.ts +2 -2
  25. package/dist/src/tools/status.js +25 -2
  26. package/dist/src/tools/story.d.ts +5 -5
  27. package/dist/src/tools/story.js +16 -4
  28. package/dist/src/tools/workflow.d.ts +2 -2
  29. package/dist/src/tools/workflow.js +5 -1
  30. package/package.json +1 -1
  31. package/src/hooks/continuation-enforcer.ts +11 -9
  32. package/src/hooks/inject-provider.ts +16 -12
  33. package/src/state/types.ts +11 -0
  34. package/src/tools/artifacts.ts +16 -7
  35. package/src/tools/health.ts +22 -13
  36. package/src/tools/index.ts +32 -40
  37. package/src/tools/injects.ts +32 -14
  38. package/src/tools/metrics.ts +3 -6
  39. package/src/tools/repair.ts +8 -4
  40. package/src/tools/reset.ts +11 -3
  41. package/src/tools/run.ts +11 -5
  42. package/src/tools/spec.ts +5 -4
  43. package/src/tools/stage.ts +22 -10
  44. package/src/tools/status.ts +28 -5
  45. package/src/tools/story.ts +21 -9
  46. package/src/tools/workflow.ts +8 -3
@@ -1,5 +1,5 @@
1
1
  import type { AstrocodeConfig } from "../config/schema";
2
- import type { SqliteDb } from "../state/db";
2
+ import type { RuntimeState } from "../state/types";
3
3
  type ToolExecuteAfterInput = {
4
4
  tool: string;
5
5
  sessionID?: string;
@@ -14,14 +14,6 @@ type EventInput = {
14
14
  properties: any;
15
15
  };
16
16
  };
17
- type RuntimeState = {
18
- db: SqliteDb | null;
19
- limitedMode: boolean;
20
- limitedModeReason: null | {
21
- code: "db_init_failed" | "schema_too_old" | "schema_downgrade" | "schema_migration_failed";
22
- details: any;
23
- };
24
- };
25
17
  export declare function createContinuationEnforcer(opts: {
26
18
  ctx: any;
27
19
  config: AstrocodeConfig;
@@ -10,7 +10,6 @@ function msFromIso(iso) {
10
10
  }
11
11
  export function createContinuationEnforcer(opts) {
12
12
  const { ctx, config, runtime } = opts;
13
- const { db } = runtime;
14
13
  const toasts = createToastManager({ ctx, throttleMs: config.ui.toasts.throttle_ms });
15
14
  const sessions = new Map();
16
15
  function getState(sessionId) {
@@ -61,6 +60,9 @@ export function createContinuationEnforcer(opts) {
61
60
  }, interval);
62
61
  }
63
62
  function shouldDedupe(sessionId, directive) {
63
+ const { db } = runtime;
64
+ if (!db)
65
+ return false;
64
66
  const s = getState(sessionId);
65
67
  const now = Date.now();
66
68
  // Memory window
@@ -78,6 +80,9 @@ export function createContinuationEnforcer(opts) {
78
80
  return false;
79
81
  }
80
82
  async function recordContinuation(sessionId, runId, directive, reason) {
83
+ const { db } = runtime;
84
+ if (!db)
85
+ return;
81
86
  db.prepare("INSERT INTO continuations (session_id, run_id, directive_hash, kind, reason, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(sessionId, runId, directive.hash, directive.kind, reason, nowISO());
82
87
  const s = getState(sessionId);
83
88
  const now = Date.now();
@@ -109,7 +114,8 @@ export function createContinuationEnforcer(opts) {
109
114
  }
110
115
  }
111
116
  async function maybeInjectContinue(sessionId, reason) {
112
- if (!config.continuation.enabled)
117
+ const { db } = runtime;
118
+ if (!config.continuation.enabled || !db)
113
119
  return;
114
120
  // Require active run
115
121
  const active = getActiveRun(db);
@@ -154,6 +160,8 @@ export function createContinuationEnforcer(opts) {
154
160
  const sessionId = input.sessionID ?? ctx.sessionID;
155
161
  if (!sessionId)
156
162
  return;
163
+ if (!config.continuation.enabled)
164
+ return;
157
165
  if (!config.continuation.inject_on_tool_done_if_run_active)
158
166
  return;
159
167
  // Inject continuation immediately after any tool execution
@@ -161,6 +169,8 @@ export function createContinuationEnforcer(opts) {
161
169
  scheduleIdleInjection(sessionId);
162
170
  },
163
171
  async onChatMessage(_input) {
172
+ if (!config.continuation.enabled)
173
+ return;
164
174
  if (!config.continuation.inject_on_message_done_if_run_active)
165
175
  return;
166
176
  scheduleIdleInjection(_input.sessionID);
@@ -1,5 +1,5 @@
1
1
  import type { AstrocodeConfig } from "../config/schema";
2
- import type { SqliteDb } from "../state/db";
2
+ import type { RuntimeState } from "../state/types";
3
3
  type ChatMessageInput = {
4
4
  sessionID: string;
5
5
  agent: string;
@@ -8,14 +8,6 @@ type ToolExecuteAfterInput = {
8
8
  tool: string;
9
9
  sessionID?: string;
10
10
  };
11
- type RuntimeState = {
12
- db: SqliteDb | null;
13
- limitedMode: boolean;
14
- limitedModeReason: null | {
15
- code: "db_init_failed" | "schema_too_old" | "schema_downgrade" | "schema_migration_failed";
16
- details: any;
17
- };
18
- };
19
11
  export declare function createInjectProvider(opts: {
20
12
  ctx: any;
21
13
  config: AstrocodeConfig;
@@ -4,7 +4,6 @@ import { nowISO } from "../shared/time";
4
4
  import { getActiveRun } from "../workflow/state-machine";
5
5
  export function createInjectProvider(opts) {
6
6
  const { ctx, config, runtime } = opts;
7
- const { db } = runtime;
8
7
  // Cache to avoid re-injecting the same injects repeatedly
9
8
  // Map of inject_id -> last injected timestamp
10
9
  const injectedCache = new Map();
@@ -29,6 +28,9 @@ export function createInjectProvider(opts) {
29
28
  }
30
29
  }
31
30
  function getInjectionDiagnostics(nowIso, scopeAllowlist, typeAllowlist) {
31
+ const { db } = runtime;
32
+ if (!db)
33
+ return null;
32
34
  // Get ALL injects to analyze filtering
33
35
  const allInjects = db.prepare("SELECT * FROM injects").all();
34
36
  let total = allInjects.length;
@@ -72,6 +74,9 @@ export function createInjectProvider(opts) {
72
74
  };
73
75
  }
74
76
  async function injectEligibleInjects(sessionId, context) {
77
+ const { db } = runtime;
78
+ if (!db)
79
+ return;
75
80
  const now = nowISO();
76
81
  const nowMs = Date.now();
77
82
  // Get allowlists from config or defaults
@@ -90,7 +95,7 @@ export function createInjectProvider(opts) {
90
95
  let skippedDeduped = 0;
91
96
  if (eligibleInjects.length === 0) {
92
97
  // Log when no injects are eligible
93
- if (EMIT_TELEMETRY) {
98
+ if (EMIT_TELEMETRY && diagnostics) {
94
99
  // eslint-disable-next-line no-console
95
100
  console.log(`[Astrocode:inject] ${now} context=${context ?? 'unknown'} selected=0 injected=0 skipped={expired:${diagnostics.skipped.expired} scope:${diagnostics.skipped.scope} type:${diagnostics.skipped.type} deduped:0}`);
96
101
  }
@@ -103,7 +108,7 @@ export function createInjectProvider(opts) {
103
108
  continue;
104
109
  }
105
110
  // Format as injection message
106
- const formattedText = `[Inject: ${inject.title}]\n\n${inject.body_md}`;
111
+ const formattedText = `### 🔖 ASTROCODE: INJECTED NOTE\n**Title:** ${inject.title}\n\n${inject.body_md}`;
107
112
  try {
108
113
  await injectChatPrompt({
109
114
  ctx,
@@ -121,7 +126,7 @@ export function createInjectProvider(opts) {
121
126
  }
122
127
  }
123
128
  // Log diagnostic summary
124
- if (EMIT_TELEMETRY || injected > 0) {
129
+ if ((EMIT_TELEMETRY || injected > 0) && diagnostics) {
125
130
  // eslint-disable-next-line no-console
126
131
  console.log(`[Astrocode:inject] ${now} context=${context ?? 'unknown'} selected=${diagnostics.selected_eligible} injected=${injected} skipped={expired:${diagnostics.skipped.expired} scope:${diagnostics.skipped.scope} type:${diagnostics.skipped.type} deduped:${skippedDeduped}}`);
127
132
  }
@@ -138,7 +143,8 @@ export function createInjectProvider(opts) {
138
143
  ]);
139
144
  // Auto-approve queued stories if enabled
140
145
  async function maybeAutoApprove(sessionId) {
141
- if (!config.inject?.auto_approve_queued_stories)
146
+ const { db } = runtime;
147
+ if (!config.inject?.auto_approve_queued_stories || !db)
142
148
  return;
143
149
  try {
144
150
  // Get all queued stories
@@ -170,6 +176,8 @@ export function createInjectProvider(opts) {
170
176
  return;
171
177
  // Inject eligible injects before processing the user's message
172
178
  await injectEligibleInjects(input.sessionID, 'chat_message');
179
+ // Also inject Workflow Pulse on chat messages if a run is active
180
+ await maybeInjectWorkflowPulse(input.sessionID);
173
181
  },
174
182
  async onToolAfter(input) {
175
183
  if (!config.inject?.enabled)
@@ -190,6 +198,7 @@ export function createInjectProvider(opts) {
190
198
  },
191
199
  };
192
200
  async function maybeInjectWorkflowPulse(sessionId) {
201
+ const { db } = runtime;
193
202
  if (!db)
194
203
  return;
195
204
  try {
@@ -69,3 +69,12 @@ export type InjectRow = {
69
69
  created_at: string;
70
70
  updated_at: string;
71
71
  };
72
+ import { SqliteDb } from "./db";
73
+ export type RuntimeState = {
74
+ db: SqliteDb | null;
75
+ limitedMode: boolean;
76
+ limitedModeReason: null | {
77
+ code: "db_init_failed" | "schema_too_old" | "schema_downgrade" | "schema_migration_failed";
78
+ details: any;
79
+ };
80
+ };
@@ -1,18 +1,18 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
2
  import type { AstrocodeConfig } from "../config/schema";
3
- import type { SqliteDb } from "../state/db";
3
+ import type { RuntimeState } from "../state/types";
4
4
  export declare function createAstroArtifactPutTool(opts: {
5
5
  ctx: any;
6
6
  config: AstrocodeConfig;
7
- db: SqliteDb;
7
+ runtime: RuntimeState;
8
8
  }): ToolDefinition;
9
9
  export declare function createAstroArtifactListTool(opts: {
10
10
  ctx: any;
11
11
  config: AstrocodeConfig;
12
- db: SqliteDb;
12
+ runtime: RuntimeState;
13
13
  }): ToolDefinition;
14
14
  export declare function createAstroArtifactGetTool(opts: {
15
15
  ctx: any;
16
16
  config: AstrocodeConfig;
17
- db: SqliteDb;
17
+ runtime: RuntimeState;
18
18
  }): ToolDefinition;
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import { tool } from "@opencode-ai/plugin/tool";
4
4
  import { putArtifact, listArtifacts, getArtifact } from "../workflow/artifacts";
5
5
  export function createAstroArtifactPutTool(opts) {
6
- const { ctx, db } = opts;
6
+ const { ctx, runtime } = opts;
7
7
  return tool({
8
8
  description: "Write an artifact file under .astro and record it in the DB.",
9
9
  args: {
@@ -15,6 +15,9 @@ export function createAstroArtifactPutTool(opts) {
15
15
  meta_json: tool.schema.string().default("{}"),
16
16
  },
17
17
  execute: async ({ run_id, stage_key, type, rel_path, content, meta_json }) => {
18
+ const { db } = runtime;
19
+ if (!db)
20
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
18
21
  const repoRoot = ctx.directory;
19
22
  const meta = JSON.parse(meta_json || "{}");
20
23
  const res = putArtifact({
@@ -32,7 +35,7 @@ export function createAstroArtifactPutTool(opts) {
32
35
  });
33
36
  }
34
37
  export function createAstroArtifactListTool(opts) {
35
- const { db } = opts;
38
+ const { runtime } = opts;
36
39
  return tool({
37
40
  description: "List artifacts (optionally filtered by run_id, stage_key, type).",
38
41
  args: {
@@ -41,13 +44,16 @@ export function createAstroArtifactListTool(opts) {
41
44
  type: tool.schema.string().optional(),
42
45
  },
43
46
  execute: async ({ run_id, stage_key, type }) => {
47
+ const { db } = runtime;
48
+ if (!db)
49
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
44
50
  const rows = listArtifacts(db, { run_id, stage_key, type });
45
51
  return JSON.stringify(rows, null, 2);
46
52
  },
47
53
  });
48
54
  }
49
55
  export function createAstroArtifactGetTool(opts) {
50
- const { ctx, config, db } = opts;
56
+ const { ctx, runtime } = opts;
51
57
  return tool({
52
58
  description: "Get artifact metadata (and optionally file contents).",
53
59
  args: {
@@ -56,6 +62,9 @@ export function createAstroArtifactGetTool(opts) {
56
62
  max_body_chars: tool.schema.number().int().positive().default(50_000),
57
63
  },
58
64
  execute: async ({ artifact_id, include_body, max_body_chars }) => {
65
+ const { db } = runtime;
66
+ if (!db)
67
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
59
68
  const row = getArtifact(db, artifact_id);
60
69
  if (!row)
61
70
  throw new Error(`Artifact not found: ${artifact_id}`);
@@ -1,8 +1,8 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
2
  import type { AstrocodeConfig } from "../config/schema";
3
- import type { SqliteDb } from "../state/db";
3
+ import type { RuntimeState } from "../state/types";
4
4
  export declare function createAstroHealthTool(opts: {
5
5
  ctx: any;
6
6
  config: AstrocodeConfig;
7
- db: SqliteDb;
7
+ runtime: RuntimeState;
8
8
  }): ToolDefinition;
@@ -5,7 +5,7 @@ import { getActiveRun } from "../workflow/state-machine";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
7
  export function createAstroHealthTool(opts) {
8
- const { ctx, config, db } = opts;
8
+ const { ctx, config, runtime } = opts;
9
9
  return tool({
10
10
  description: "Check Astrocode health: DB status, locks, schema, active runs, recent events.",
11
11
  args: {},
@@ -23,12 +23,21 @@ export function createAstroHealthTool(opts) {
23
23
  const dbExists = fs.existsSync(fullDbPath);
24
24
  const walExists = fs.existsSync(`${fullDbPath}-wal`);
25
25
  const shmExists = fs.existsSync(`${fullDbPath}-shm`);
26
- lines.push(`- DB Files:`);
27
- lines.push(` - Main: ${dbExists ? "EXISTS" : "MISSING"}`);
28
- lines.push(` - WAL: ${walExists ? "EXISTS" : "MISSING"}`);
29
- lines.push(` - SHM: ${shmExists ? "EXISTS" : "MISSING"}`);
30
- if (!dbExists) {
31
- lines.push(`- STATUS: DB MISSING - run astro_init first`);
26
+ lines.push(`## Database File`);
27
+ lines.push(`- Exists: ${dbExists ? "" : ""}`);
28
+ lines.push(`- WAL Mode Active: ${walExists ? "YES" : "NO"}`);
29
+ if (dbExists) {
30
+ const stats = fs.statSync(fullDbPath);
31
+ lines.push(`- Size: ${Math.round(stats.size / 1024)} KB`);
32
+ lines.push(`- Last Modified: ${stats.mtime.toISOString()}`);
33
+ }
34
+ const { db } = runtime;
35
+ if (!db) {
36
+ lines.push(``, `## Status`);
37
+ lines.push(`❌ DB not connected (Limited Mode)`);
38
+ if (runtime.limitedModeReason) {
39
+ lines.push(`- Reason: ${runtime.limitedModeReason.code}`);
40
+ }
32
41
  return lines.join("\n");
33
42
  }
34
43
  // Schema version
@@ -38,8 +47,6 @@ export function createAstroHealthTool(opts) {
38
47
  }
39
48
  catch (e) {
40
49
  lines.push(`- Schema Version: ERROR (${String(e)})`);
41
- lines.push(`- STATUS: DB CORRUPTED`);
42
- return lines.join("\n");
43
50
  }
44
51
  // Active run
45
52
  try {
@@ -51,7 +58,7 @@ export function createAstroHealthTool(opts) {
51
58
  lines.push(` - Started: ${activeRun.started_at}`);
52
59
  }
53
60
  else {
54
- lines.push(`- Active Run: NONE`);
61
+ lines.push(`- Active Run: *(none)*`);
55
62
  }
56
63
  }
57
64
  catch (e) {
@@ -80,7 +87,7 @@ export function createAstroHealthTool(opts) {
80
87
  lines.push(`✅ DB accessible`);
81
88
  lines.push(`✅ Schema valid`);
82
89
  if (walExists || shmExists) {
83
- lines.push(`⚠️ WAL/SHM files present - indicates unclean shutdown or active transaction`);
90
+ lines.push(`ℹ️ SQLite temporary files present (normal during active use)`);
84
91
  }
85
92
  return lines.join("\n");
86
93
  },
@@ -13,47 +13,40 @@ import { createAstroResetTool } from "./reset";
13
13
  import { createAstroMetricsTool } from "./metrics";
14
14
  export function createAstroTools(opts) {
15
15
  const { ctx, config, agents, runtime } = opts;
16
- const { db } = runtime;
17
- const hasDatabase = db !== null; // Source of truth: DB availability
18
16
  const tools = {};
19
17
  // Always available tools (work without database - guaranteed DB-independent)
20
- tools.astro_status = createAstroStatusTool({ ctx, config });
18
+ tools.astro_status = createAstroStatusTool({ ctx, config, runtime });
21
19
  tools.astro_spec_get = createAstroSpecGetTool({ ctx, config });
22
- tools.astro_health = createAstroHealthTool({ ctx, config, db });
23
- tools.astro_reset = createAstroResetTool({ ctx, config, db });
20
+ tools.astro_health = createAstroHealthTool({ ctx, config, runtime });
21
+ tools.astro_reset = createAstroResetTool({ ctx, config, runtime });
24
22
  tools.astro_metrics = createAstroMetricsTool({ ctx, config });
25
23
  // Recovery tool - available even in limited mode to allow DB initialization
26
24
  tools.astro_init = createAstroInitTool({ ctx, config, runtime });
27
25
  // Database-dependent tools
28
- if (hasDatabase) {
29
- // Ensure agents are available for workflow tools that require them
30
- if (!agents) {
31
- throw new Error("astro_workflow_proceed requires agents to be provided in normal mode.");
32
- }
33
- tools.astro_story_queue = createAstroStoryQueueTool({ ctx, config, db });
34
- tools.astro_story_approve = createAstroStoryApproveTool({ ctx, config, db });
35
- tools.astro_story_board = createAstroStoryBoardTool({ ctx, config, db });
36
- tools.astro_story_set_state = createAstroStorySetStateTool({ ctx, config, db });
37
- tools.astro_spec_set = createAstroSpecSetTool({ ctx, config, db });
38
- tools.astro_run_get = createAstroRunGetTool({ ctx, config, db });
39
- tools.astro_run_abort = createAstroRunAbortTool({ ctx, config, db });
40
- tools.astro_workflow_proceed = createAstroWorkflowProceedTool({ ctx, config, db, agents });
41
- tools.astro_stage_start = createAstroStageStartTool({ ctx, config, db });
42
- tools.astro_stage_complete = createAstroStageCompleteTool({ ctx, config, db });
43
- tools.astro_stage_fail = createAstroStageFailTool({ ctx, config, db });
44
- tools.astro_stage_reset = createAstroStageResetTool({ ctx, config, db });
45
- tools.astro_artifact_put = createAstroArtifactPutTool({ ctx, config, db });
46
- tools.astro_artifact_list = createAstroArtifactListTool({ ctx, config, db });
47
- tools.astro_artifact_get = createAstroArtifactGetTool({ ctx, config, db });
48
- tools.astro_inject_put = createAstroInjectPutTool({ ctx, config, db });
49
- tools.astro_inject_list = createAstroInjectListTool({ ctx, config, db });
50
- tools.astro_inject_search = createAstroInjectSearchTool({ ctx, config, db });
51
- tools.astro_inject_get = createAstroInjectGetTool({ ctx, config, db });
52
- tools.astro_inject_eligible = createAstroInjectEligibleTool({ ctx, config, db });
53
- tools.astro_inject_debug_due = createAstroInjectDebugDueTool({ ctx, config, db });
54
- tools.astro_repair = createAstroRepairTool({ ctx, config, db });
55
- }
56
- // Create aliases for backward compatibility
26
+ // We register these even if runtime.db is null, so they can become active if astro_init succeeds in-process.
27
+ // The tools themselves must handle the missing DB case gracefully (returning "not initialized").
28
+ tools.astro_story_queue = createAstroStoryQueueTool({ ctx, config, runtime });
29
+ tools.astro_story_approve = createAstroStoryApproveTool({ ctx, config, runtime });
30
+ tools.astro_story_board = createAstroStoryBoardTool({ ctx, config, runtime });
31
+ tools.astro_story_set_state = createAstroStorySetStateTool({ ctx, config, runtime });
32
+ tools.astro_spec_set = createAstroSpecSetTool({ ctx, config, runtime });
33
+ tools.astro_run_get = createAstroRunGetTool({ ctx, config, runtime });
34
+ tools.astro_run_abort = createAstroRunAbortTool({ ctx, config, runtime });
35
+ tools.astro_workflow_proceed = createAstroWorkflowProceedTool({ ctx, config, runtime, agents });
36
+ tools.astro_stage_start = createAstroStageStartTool({ ctx, config, runtime });
37
+ tools.astro_stage_complete = createAstroStageCompleteTool({ ctx, config, runtime });
38
+ tools.astro_stage_fail = createAstroStageFailTool({ ctx, config, runtime });
39
+ tools.astro_stage_reset = createAstroStageResetTool({ ctx, config, runtime });
40
+ tools.astro_artifact_put = createAstroArtifactPutTool({ ctx, config, runtime });
41
+ tools.astro_artifact_list = createAstroArtifactListTool({ ctx, config, runtime });
42
+ tools.astro_artifact_get = createAstroArtifactGetTool({ ctx, config, runtime });
43
+ tools.astro_inject_put = createAstroInjectPutTool({ ctx, config, runtime });
44
+ tools.astro_inject_list = createAstroInjectListTool({ ctx, config, runtime });
45
+ tools.astro_inject_search = createAstroInjectSearchTool({ ctx, config, runtime });
46
+ tools.astro_inject_get = createAstroInjectGetTool({ ctx, config, runtime });
47
+ tools.astro_inject_eligible = createAstroInjectEligibleTool({ ctx, config, runtime });
48
+ tools.astro_inject_debug_due = createAstroInjectDebugDueTool({ ctx, config, runtime });
49
+ tools.astro_repair = createAstroRepairTool({ ctx, config, runtime });
57
50
  const aliases = [
58
51
  ["_astro_init", "astro_init"],
59
52
  ["_astro_status", "astro_status"],
@@ -1,25 +1,25 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
2
  import type { AstrocodeConfig } from "../config/schema";
3
- import type { SqliteDb } from "../state/db";
3
+ import type { RuntimeState } from "../state/types";
4
4
  export declare function createAstroInjectPutTool(opts: {
5
5
  ctx: any;
6
6
  config: AstrocodeConfig;
7
- db: SqliteDb;
7
+ runtime: RuntimeState;
8
8
  }): ToolDefinition;
9
9
  export declare function createAstroInjectListTool(opts: {
10
10
  ctx: any;
11
11
  config: AstrocodeConfig;
12
- db: SqliteDb;
12
+ runtime: RuntimeState;
13
13
  }): ToolDefinition;
14
14
  export declare function createAstroInjectGetTool(opts: {
15
15
  ctx: any;
16
16
  config: AstrocodeConfig;
17
- db: SqliteDb;
17
+ runtime: RuntimeState;
18
18
  }): ToolDefinition;
19
19
  export declare function createAstroInjectSearchTool(opts: {
20
20
  ctx: any;
21
21
  config: AstrocodeConfig;
22
- db: SqliteDb;
22
+ runtime: RuntimeState;
23
23
  }): ToolDefinition;
24
24
  export type InjectRow = {
25
25
  inject_id: string;
@@ -35,7 +35,7 @@ export type InjectRow = {
35
35
  created_at: string;
36
36
  updated_at: string;
37
37
  };
38
- export declare function selectEligibleInjects(db: SqliteDb, opts: {
38
+ export declare function selectEligibleInjects(db: any, opts: {
39
39
  nowIso: string;
40
40
  scopeAllowlist: string[];
41
41
  typeAllowlist: string[];
@@ -44,10 +44,10 @@ export declare function selectEligibleInjects(db: SqliteDb, opts: {
44
44
  export declare function createAstroInjectEligibleTool(opts: {
45
45
  ctx: any;
46
46
  config: AstrocodeConfig;
47
- db: SqliteDb;
47
+ runtime: RuntimeState;
48
48
  }): ToolDefinition;
49
49
  export declare function createAstroInjectDebugDueTool(opts: {
50
50
  ctx: any;
51
51
  config: AstrocodeConfig;
52
- db: SqliteDb;
52
+ runtime: RuntimeState;
53
53
  }): ToolDefinition;
@@ -41,7 +41,7 @@ function newInjectId() {
41
41
  return `inj_${Date.now()}_${Math.random().toString(16).slice(2)}`;
42
42
  }
43
43
  export function createAstroInjectPutTool(opts) {
44
- const { db } = opts;
44
+ const { runtime } = opts;
45
45
  return tool({
46
46
  description: "Create/update an inject (note/policy) stored in the DB. Useful for persistent rules.",
47
47
  args: {
@@ -56,6 +56,9 @@ export function createAstroInjectPutTool(opts) {
56
56
  expires_at: tool.schema.string().nullable().optional(),
57
57
  },
58
58
  execute: async ({ inject_id, type, title, body_md, tags_json, scope, source, priority, expires_at }) => {
59
+ const { db } = runtime;
60
+ if (!db)
61
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
59
62
  const id = inject_id ?? newInjectId();
60
63
  const now = nowISO();
61
64
  const sha = sha256Hex(body_md);
@@ -109,7 +112,7 @@ export function createAstroInjectPutTool(opts) {
109
112
  });
110
113
  }
111
114
  export function createAstroInjectListTool(opts) {
112
- const { db } = opts;
115
+ const { runtime } = opts;
113
116
  return tool({
114
117
  description: "List injects (optionally filtered by scope/type).",
115
118
  args: {
@@ -118,6 +121,9 @@ export function createAstroInjectListTool(opts) {
118
121
  limit: tool.schema.number().int().positive().default(50),
119
122
  },
120
123
  execute: async ({ scope, type, limit }) => {
124
+ const { db } = runtime;
125
+ if (!db)
126
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
121
127
  const where = [];
122
128
  const params = [];
123
129
  if (scope) {
@@ -143,13 +149,16 @@ export function createAstroInjectListTool(opts) {
143
149
  });
144
150
  }
145
151
  export function createAstroInjectGetTool(opts) {
146
- const { db } = opts;
152
+ const { runtime } = opts;
147
153
  return tool({
148
154
  description: "Get an inject by id (full body).",
149
155
  args: {
150
156
  inject_id: tool.schema.string().min(1),
151
157
  },
152
158
  execute: async ({ inject_id }) => {
159
+ const { db } = runtime;
160
+ if (!db)
161
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
153
162
  const row = db.prepare("SELECT * FROM injects WHERE inject_id=?").get(inject_id);
154
163
  if (!row)
155
164
  throw new Error(`Inject not found: ${inject_id}`);
@@ -158,7 +167,7 @@ export function createAstroInjectGetTool(opts) {
158
167
  });
159
168
  }
160
169
  export function createAstroInjectSearchTool(opts) {
161
- const { db } = opts;
170
+ const { runtime } = opts;
162
171
  return tool({
163
172
  description: "Search injects by query substring over title/body/tags. Returns matches ordered by priority/recency.",
164
173
  args: {
@@ -167,6 +176,9 @@ export function createAstroInjectSearchTool(opts) {
167
176
  limit: tool.schema.number().int().positive().default(20),
168
177
  },
169
178
  execute: async ({ q, scope, limit }) => {
179
+ const { db } = runtime;
180
+ if (!db)
181
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
170
182
  const like = `%${q}%`;
171
183
  const where = ["(title LIKE ? OR body_md LIKE ? OR tags_json LIKE ?)"];
172
184
  const params = [like, like, like];
@@ -210,7 +222,7 @@ export function selectEligibleInjects(db, opts) {
210
222
  return db.prepare(sql).all(...params);
211
223
  }
212
224
  export function createAstroInjectEligibleTool(opts) {
213
- const { db } = opts;
225
+ const { runtime } = opts;
214
226
  return tool({
215
227
  description: "Debug: show which injects are eligible right now for injection.",
216
228
  args: {
@@ -219,6 +231,9 @@ export function createAstroInjectEligibleTool(opts) {
219
231
  limit: tool.schema.number().int().positive().default(50),
220
232
  },
221
233
  execute: async ({ scopes_json, types_json, limit }) => {
234
+ const { db } = runtime;
235
+ if (!db)
236
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
222
237
  const now = nowISO();
223
238
  const scopes = parseJsonStringArray("scopes_json", scopes_json);
224
239
  const types = parseJsonStringArray("types_json", types_json);
@@ -236,7 +251,7 @@ export function createAstroInjectEligibleTool(opts) {
236
251
  });
237
252
  }
238
253
  export function createAstroInjectDebugDueTool(opts) {
239
- const { db } = opts;
254
+ const { runtime } = opts;
240
255
  return tool({
241
256
  description: "Debug: show comprehensive injection diagnostics - why injects were selected/skipped.",
242
257
  args: {
@@ -244,6 +259,9 @@ export function createAstroInjectDebugDueTool(opts) {
244
259
  types_json: tool.schema.string().default('["note","policy"]'),
245
260
  },
246
261
  execute: async ({ scopes_json, types_json }) => {
262
+ const { db } = runtime;
263
+ if (!db)
264
+ return "⚠️ Astrocode not initialized. Run **astro_init** first.";
247
265
  const now = nowISO();
248
266
  const nowMs = Date.parse(now);
249
267
  const scopes = parseJsonStringArray("scopes_json", scopes_json);
@@ -1,7 +1,8 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
- type CreateAstroMetricsToolOptions = {
2
+ import type { AstrocodeConfig } from "../config/schema";
3
+ import type { RuntimeState } from "../state/types";
4
+ export declare function createAstroMetricsTool(opts: {
3
5
  ctx: any;
4
- config: any;
5
- };
6
- export declare function createAstroMetricsTool(opts: CreateAstroMetricsToolOptions): ToolDefinition;
7
- export {};
6
+ config: AstrocodeConfig;
7
+ runtime?: RuntimeState;
8
+ }): ToolDefinition;
@@ -1,8 +1,8 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
2
  import type { AstrocodeConfig } from "../config/schema";
3
- import type { SqliteDb } from "../state/db";
3
+ import type { RuntimeState } from "../state/types";
4
4
  export declare function createAstroRepairTool(opts: {
5
5
  ctx: any;
6
6
  config: AstrocodeConfig;
7
- db: SqliteDb;
7
+ runtime: RuntimeState;
8
8
  }): ToolDefinition;
@@ -4,13 +4,17 @@ import { repairState, formatRepairReport } from "../workflow/repair";
4
4
  import { putArtifact } from "../workflow/artifacts";
5
5
  import { nowISO } from "../shared/time";
6
6
  export function createAstroRepairTool(opts) {
7
- const { ctx, config, db } = opts;
7
+ const { ctx, config, runtime } = opts;
8
8
  return tool({
9
9
  description: "Repair Astrocode invariants and recover from inconsistent DB state. Writes a repair report artifact.",
10
10
  args: {
11
11
  write_report_artifact: tool.schema.boolean().default(true),
12
12
  },
13
13
  execute: async ({ write_report_artifact }) => {
14
+ const { db } = runtime;
15
+ if (!db) {
16
+ return "⚠️ Cannot run repair: Astrocode is not initialized. Run **astro_init** first.";
17
+ }
14
18
  const repoRoot = ctx.directory;
15
19
  // Repair database state
16
20
  const report = withTx(db, () => repairState(db, config));
@@ -1,8 +1,8 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
2
  import type { AstrocodeConfig } from "../config/schema";
3
- import type { SqliteDb } from "../state/db";
3
+ import type { RuntimeState } from "../state/types";
4
4
  export declare function createAstroResetTool(opts: {
5
5
  ctx: any;
6
6
  config: AstrocodeConfig;
7
- db: SqliteDb;
7
+ runtime: RuntimeState;
8
8
  }): ToolDefinition;