astrocode-workflow 0.1.29 → 0.1.34

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.
@@ -1,2 +1,2 @@
1
1
  export declare const BASE_ORCH_PROMPT = "You are Astro (Orchestrator) for Astrocode.\n\nMission:\n- Advance a deterministic pipeline: frame \u2192 plan \u2192 spec \u2192 implement \u2192 review \u2192 verify \u2192 close.\n- The SQLite DB is the source of truth. Prefer tools over prose.\n- Never narrate what prompts you received.\n- Keep outputs short; store large outputs as artifacts and reference paths.\n\nOperating rules:\n- Only start new runs when the user explicitly requests implementation, workflow management, or story processing.\n- Answer questions directly when possible without starting workflows.\n- Prefer calling astro_workflow_proceed (step/loop) and astro_status only when actively managing a workflow.\n- Delegate stage work only to the stage subagent matching the current stage.\n- If a stage subagent returns status=blocked, inject the BLOCKED directive and stop.\n- Never delegate from subagents (enforced by permissions).\n- Be discretionary: assess if the user's request requires workflow initiation or just information.\n";
2
- export declare const BASE_STAGE_PROMPT = "You are an Astro stage subagent.\n\n***CRITICAL: Follow the latest [SYSTEM DIRECTIVE: ASTROCODE \u2014 STAGE_*] you receive EXACTLY.***\n\nYour output MUST be in this EXACT format (no deviations):\n\n1) First, a short baton markdown summary (1-3 sentences).\n\n2) Then, immediately after, the ASTRO JSON markers with valid JSON:\n\n<!-- ASTRO_JSON_BEGIN -->\n{\n \"schema_version\": 1,\n \"stage_key\": \"CURRENT_STAGE\",\n \"status\": \"ok\",\n \"summary\": \"Brief summary\",\n \"decisions\": [],\n \"next_actions\": [],\n \"tasks\": [],\n \"files\": [],\n \"evidence\": [],\n \"new_stories\": [],\n \"questions\": [],\n \"metrics\": {}\n}\n<!-- ASTRO_JSON_END -->\n\nMANDATORY RULES:\n- ALL fields above MUST be present (use empty arrays/objects if no data)\n- stage_key MUST be \"CURRENT_STAGE\" (will be replaced)\n- status MUST be \"ok\" unless blocked/failed\n- summary MUST be a string describing the work\n- JSON MUST be valid and parseable\n- Markers MUST be exact as shown\n\nIf blocked, set status=\"blocked\" and put ONE question in questions array. Do not output anything else.\n";
2
+ export declare const BASE_STAGE_PROMPT = "You are an Astro stage subagent.\n\n***CRITICAL: Follow the latest [SYSTEM DIRECTIVE: ASTROCODE \u2014 STAGE_*] you receive EXACTLY.***\n\nYour output MUST be in this EXACT format (copy/paste this template):\n\n1) First, a short summary paragraph (1-3 sentences).\n\n2) Then, immediately after, the ASTRO JSON between markers:\n\n<!-- ASTRO_JSON_BEGIN -->\n{\n \"stage_key\": \"CURRENT_STAGE\",\n \"status\": \"ok\",\n \"summary\": \"Brief summary of work done\",\n \"tasks\": [],\n \"files\": [],\n \"questions\": []\n}\n<!-- ASTRO_JSON_END -->\n\nMANDATORY:\n- stage_key must be \"CURRENT_STAGE\"\n- status must be \"ok\", \"blocked\", or \"failed\"\n- summary must be a non-empty string\n- JSON must be valid syntax\n- Use exact markers shown above\n\nEXAMPLE OUTPUT:\nI analyzed the requirements and identified key components for implementation.\n\n<!-- ASTRO_JSON_BEGIN -->\n{\n \"stage_key\": \"plan\",\n \"status\": \"ok\",\n \"summary\": \"Created implementation plan with 5 main tasks\",\n \"tasks\": [\n {\"title\": \"Setup database\", \"complexity\": 3},\n {\"title\": \"Build API endpoints\", \"complexity\": 5}\n ],\n \"files\": [\n {\"path\": \"schema.prisma\", \"kind\": \"file\"}\n ],\n \"questions\": []\n}\n<!-- ASTRO_JSON_END -->\n\nIf blocked, set status=\"blocked\" and add ONE question to questions array. Do not deviate from this format.\n";
@@ -19,36 +19,48 @@ export const BASE_STAGE_PROMPT = `You are an Astro stage subagent.
19
19
 
20
20
  ***CRITICAL: Follow the latest [SYSTEM DIRECTIVE: ASTROCODE — STAGE_*] you receive EXACTLY.***
21
21
 
22
- Your output MUST be in this EXACT format (no deviations):
22
+ Your output MUST be in this EXACT format (copy/paste this template):
23
23
 
24
- 1) First, a short baton markdown summary (1-3 sentences).
24
+ 1) First, a short summary paragraph (1-3 sentences).
25
25
 
26
- 2) Then, immediately after, the ASTRO JSON markers with valid JSON:
26
+ 2) Then, immediately after, the ASTRO JSON between markers:
27
27
 
28
28
  <!-- ASTRO_JSON_BEGIN -->
29
29
  {
30
- "schema_version": 1,
31
30
  "stage_key": "CURRENT_STAGE",
32
31
  "status": "ok",
33
- "summary": "Brief summary",
34
- "decisions": [],
35
- "next_actions": [],
32
+ "summary": "Brief summary of work done",
36
33
  "tasks": [],
37
34
  "files": [],
38
- "evidence": [],
39
- "new_stories": [],
40
- "questions": [],
41
- "metrics": {}
35
+ "questions": []
42
36
  }
43
37
  <!-- ASTRO_JSON_END -->
44
38
 
45
- MANDATORY RULES:
46
- - ALL fields above MUST be present (use empty arrays/objects if no data)
47
- - stage_key MUST be "CURRENT_STAGE" (will be replaced)
48
- - status MUST be "ok" unless blocked/failed
49
- - summary MUST be a string describing the work
50
- - JSON MUST be valid and parseable
51
- - Markers MUST be exact as shown
39
+ MANDATORY:
40
+ - stage_key must be "CURRENT_STAGE"
41
+ - status must be "ok", "blocked", or "failed"
42
+ - summary must be a non-empty string
43
+ - JSON must be valid syntax
44
+ - Use exact markers shown above
52
45
 
53
- If blocked, set status="blocked" and put ONE question in questions array. Do not output anything else.
46
+ EXAMPLE OUTPUT:
47
+ I analyzed the requirements and identified key components for implementation.
48
+
49
+ <!-- ASTRO_JSON_BEGIN -->
50
+ {
51
+ "stage_key": "plan",
52
+ "status": "ok",
53
+ "summary": "Created implementation plan with 5 main tasks",
54
+ "tasks": [
55
+ {"title": "Setup database", "complexity": 3},
56
+ {"title": "Build API endpoints", "complexity": 5}
57
+ ],
58
+ "files": [
59
+ {"path": "schema.prisma", "kind": "file"}
60
+ ],
61
+ "questions": []
62
+ }
63
+ <!-- ASTRO_JSON_END -->
64
+
65
+ If blocked, set status="blocked" and add ONE question to questions array. Do not deviate from this format.
54
66
  `;
@@ -166,6 +166,11 @@ export function createAstroWorkflowProceedTool(opts) {
166
166
  if (systemConfig.agent && systemConfig.agent[name]) {
167
167
  return true;
168
168
  }
169
+ // For known stage agents, assume they exist (they are system-provided subagents)
170
+ const knownStageAgents = ["Frame", "Plan", "Spec", "Implement", "Review", "Verify", "Close"];
171
+ if (knownStageAgents.includes(name)) {
172
+ return true;
173
+ }
169
174
  return false;
170
175
  };
171
176
  if (!agentExists(agentName)) {
@@ -1,6 +1,5 @@
1
1
  import { clampLines, normalizeNewlines, stripCodeFences } from "../shared/text";
2
2
  import { z, ZodError } from "zod";
3
- import { debug } from "../shared/log";
4
3
  export const ASTRO_JSON_BEGIN = "<!-- ASTRO_JSON_BEGIN -->";
5
4
  export const ASTRO_JSON_END = "<!-- ASTRO_JSON_END -->";
6
5
  export const StageKeySchema = z.enum(["frame", "plan", "spec", "implement", "review", "verify", "close"]);
@@ -45,11 +44,27 @@ export function parseStageOutputText(text) {
45
44
  let jsonRaw = "";
46
45
  let fallbackUsed = false;
47
46
  if (beginIdx === -1 || endIdx === -1 || endIdx <= beginIdx) {
47
+ // Enhanced fallback: scan for any JSON object in the text
48
+ const jsonMatch = norm.match(/\{[\s\S]*\}/);
49
+ if (jsonMatch) {
50
+ try {
51
+ const parsed = JSON.parse(jsonMatch[0]);
52
+ const astroJson = AstroJsonSchema.parse(parsed);
53
+ return {
54
+ baton_md: norm.replace(jsonMatch[0], "").trim(),
55
+ astro_json: astroJson,
56
+ astro_json_raw: jsonMatch[0],
57
+ error: null
58
+ };
59
+ }
60
+ catch (e) {
61
+ // JSON found but invalid, continue to marker error
62
+ }
63
+ }
48
64
  // Fallback: if no markers, check if the entire text is JSON
49
65
  try {
50
66
  const parsed = JSON.parse(norm);
51
67
  const astroJson = AstroJsonSchema.parse(parsed);
52
- debug("Used JSON fallback parsing (no markers found)", { stage_key: astroJson.stage_key });
53
68
  return {
54
69
  baton_md: "",
55
70
  astro_json: astroJson,
@@ -64,7 +79,7 @@ export function parseStageOutputText(text) {
64
79
  baton_md: norm,
65
80
  astro_json: null,
66
81
  astro_json_raw: null,
67
- error: `Missing ASTRO JSON markers. Expected markers ${ASTRO_JSON_BEGIN} ... ${ASTRO_JSON_END}. If output is pure JSON, ensure it's wrapped in markers.`,
82
+ error: `Missing ASTRO JSON markers. Expected markers ${ASTRO_JSON_BEGIN} ... ${ASTRO_JSON_END}. If output contains JSON, ensure it's valid.`,
68
83
  };
69
84
  }
70
85
  const before = norm.slice(0, beginIdx).trim();
@@ -64,7 +64,10 @@ export function createRunForStory(db, config, storyKey) {
64
64
  const isGenesisCandidate = storyKey === 'S-0001' || isInitialStory(db, storyKey) ||
65
65
  (story.body_md && story.body_md.length > 100 &&
66
66
  (story.title.toLowerCase().includes('implement') || story.body_md.toLowerCase().includes('implement')));
67
- if (isGenesisCandidate) {
67
+ // Skip conversion if there are already many stories (spec already decomposed)
68
+ const existingStoriesCount = db.prepare("SELECT COUNT(*) as count FROM stories").get();
69
+ const alreadyDecomposed = existingStoriesCount.count > 10; // Arbitrary threshold
70
+ if (isGenesisCandidate && !alreadyDecomposed) {
68
71
  const planningTitle = `Plan and decompose: ${story.title}`;
69
72
  const planningBody = `Analyze the requirements and break down "${story.title}" into 50-200 detailed, granular implementation stories. Each story should be focused on a specific, implementable task with clear acceptance criteria.\n\nOriginal request: ${story.body_md || ''}`;
70
73
  db.prepare("UPDATE stories SET title=?, body_md=? WHERE story_key=?").run(planningTitle, planningBody, storyKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astrocode-workflow",
3
- "version": "0.1.29",
3
+ "version": "0.1.34",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,36 +20,48 @@ export const BASE_STAGE_PROMPT = `You are an Astro stage subagent.
20
20
 
21
21
  ***CRITICAL: Follow the latest [SYSTEM DIRECTIVE: ASTROCODE — STAGE_*] you receive EXACTLY.***
22
22
 
23
- Your output MUST be in this EXACT format (no deviations):
23
+ Your output MUST be in this EXACT format (copy/paste this template):
24
24
 
25
- 1) First, a short baton markdown summary (1-3 sentences).
25
+ 1) First, a short summary paragraph (1-3 sentences).
26
26
 
27
- 2) Then, immediately after, the ASTRO JSON markers with valid JSON:
27
+ 2) Then, immediately after, the ASTRO JSON between markers:
28
28
 
29
29
  <!-- ASTRO_JSON_BEGIN -->
30
30
  {
31
- "schema_version": 1,
32
31
  "stage_key": "CURRENT_STAGE",
33
32
  "status": "ok",
34
- "summary": "Brief summary",
35
- "decisions": [],
36
- "next_actions": [],
33
+ "summary": "Brief summary of work done",
37
34
  "tasks": [],
38
35
  "files": [],
39
- "evidence": [],
40
- "new_stories": [],
41
- "questions": [],
42
- "metrics": {}
36
+ "questions": []
43
37
  }
44
38
  <!-- ASTRO_JSON_END -->
45
39
 
46
- MANDATORY RULES:
47
- - ALL fields above MUST be present (use empty arrays/objects if no data)
48
- - stage_key MUST be "CURRENT_STAGE" (will be replaced)
49
- - status MUST be "ok" unless blocked/failed
50
- - summary MUST be a string describing the work
51
- - JSON MUST be valid and parseable
52
- - Markers MUST be exact as shown
40
+ MANDATORY:
41
+ - stage_key must be "CURRENT_STAGE"
42
+ - status must be "ok", "blocked", or "failed"
43
+ - summary must be a non-empty string
44
+ - JSON must be valid syntax
45
+ - Use exact markers shown above
53
46
 
54
- If blocked, set status="blocked" and put ONE question in questions array. Do not output anything else.
47
+ EXAMPLE OUTPUT:
48
+ I analyzed the requirements and identified key components for implementation.
49
+
50
+ <!-- ASTRO_JSON_BEGIN -->
51
+ {
52
+ "stage_key": "plan",
53
+ "status": "ok",
54
+ "summary": "Created implementation plan with 5 main tasks",
55
+ "tasks": [
56
+ {"title": "Setup database", "complexity": 3},
57
+ {"title": "Build API endpoints", "complexity": 5}
58
+ ],
59
+ "files": [
60
+ {"path": "schema.prisma", "kind": "file"}
61
+ ],
62
+ "questions": []
63
+ }
64
+ <!-- ASTRO_JSON_END -->
65
+
66
+ If blocked, set status="blocked" and add ONE question to questions array. Do not deviate from this format.
55
67
  `;
@@ -204,6 +204,11 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
204
204
  if (systemConfig.agent && systemConfig.agent[name]) {
205
205
  return true;
206
206
  }
207
+ // For known stage agents, assume they exist (they are system-provided subagents)
208
+ const knownStageAgents = ["Frame", "Plan", "Spec", "Implement", "Review", "Verify", "Close"];
209
+ if (knownStageAgents.includes(name)) {
210
+ return true;
211
+ }
207
212
  return false;
208
213
  };
209
214
 
@@ -80,11 +80,27 @@ export function parseStageOutputText(text: string): ParsedStageOutput {
80
80
  let fallbackUsed = false;
81
81
 
82
82
  if (beginIdx === -1 || endIdx === -1 || endIdx <= beginIdx) {
83
+ // Enhanced fallback: scan for any JSON object in the text
84
+ const jsonMatch = norm.match(/\{[\s\S]*\}/);
85
+ if (jsonMatch) {
86
+ try {
87
+ const parsed = JSON.parse(jsonMatch[0]) as unknown;
88
+ const astroJson = AstroJsonSchema.parse(parsed);
89
+ return {
90
+ baton_md: norm.replace(jsonMatch[0], "").trim(),
91
+ astro_json: astroJson,
92
+ astro_json_raw: jsonMatch[0],
93
+ error: null
94
+ };
95
+ } catch (e) {
96
+ // JSON found but invalid, continue to marker error
97
+ }
98
+ }
99
+
83
100
  // Fallback: if no markers, check if the entire text is JSON
84
101
  try {
85
102
  const parsed = JSON.parse(norm) as unknown;
86
103
  const astroJson = AstroJsonSchema.parse(parsed);
87
- debug("Used JSON fallback parsing (no markers found)", { stage_key: astroJson.stage_key });
88
104
  return {
89
105
  baton_md: "",
90
106
  astro_json: astroJson,
@@ -99,7 +115,7 @@ export function parseStageOutputText(text: string): ParsedStageOutput {
99
115
  baton_md: norm,
100
116
  astro_json: null,
101
117
  astro_json_raw: null,
102
- error: `Missing ASTRO JSON markers. Expected markers ${ASTRO_JSON_BEGIN} ... ${ASTRO_JSON_END}. If output is pure JSON, ensure it's wrapped in markers.`,
118
+ error: `Missing ASTRO JSON markers. Expected markers ${ASTRO_JSON_BEGIN} ... ${ASTRO_JSON_END}. If output contains JSON, ensure it's valid.`,
103
119
  };
104
120
  }
105
121
 
@@ -94,7 +94,11 @@ export function createRunForStory(db: SqliteDb, config: AstrocodeConfig, storyKe
94
94
  (story.body_md && story.body_md.length > 100 &&
95
95
  (story.title.toLowerCase().includes('implement') || story.body_md.toLowerCase().includes('implement')));
96
96
 
97
- if (isGenesisCandidate) {
97
+ // Skip conversion if there are already many stories (spec already decomposed)
98
+ const existingStoriesCount = db.prepare("SELECT COUNT(*) as count FROM stories").get() as { count: number };
99
+ const alreadyDecomposed = existingStoriesCount.count > 10; // Arbitrary threshold
100
+
101
+ if (isGenesisCandidate && !alreadyDecomposed) {
98
102
  const planningTitle = `Plan and decompose: ${story.title}`;
99
103
  const planningBody = `Analyze the requirements and break down "${story.title}" into 50-200 detailed, granular implementation stories. Each story should be focused on a specific, implementable task with clear acceptance criteria.\n\nOriginal request: ${story.body_md || ''}`;
100
104
  db.prepare("UPDATE stories SET title=?, body_md=? WHERE story_key=?").run(planningTitle, planningBody, storyKey);