@zhuan-ai/zhuanspec 1.3.0 → 2.1.0

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 (36) hide show
  1. package/README.zh.md +1 -1
  2. package/dist/cli/index.js +1 -1
  3. package/dist/commands/artifact-workflow.js +45 -2
  4. package/dist/commands/validate.d.ts +14 -0
  5. package/dist/commands/validate.js +160 -4
  6. package/dist/core/skill-discovery.d.ts +2 -2
  7. package/dist/core/skill-discovery.js +16 -3
  8. package/dist/core/task-graph/execution-planner.d.ts +17 -0
  9. package/dist/core/task-graph/execution-planner.js +119 -0
  10. package/dist/core/task-graph/index.d.ts +13 -0
  11. package/dist/core/task-graph/index.js +16 -0
  12. package/dist/core/task-graph/mermaid-renderer.d.ts +22 -0
  13. package/dist/core/task-graph/mermaid-renderer.js +128 -0
  14. package/dist/core/task-graph/task-graph.d.ts +74 -0
  15. package/dist/core/task-graph/task-graph.js +219 -0
  16. package/dist/core/task-graph/task-parser.d.ts +14 -0
  17. package/dist/core/task-graph/task-parser.js +111 -0
  18. package/dist/core/task-graph/types.d.ts +53 -0
  19. package/dist/core/task-graph/types.js +7 -0
  20. package/dist/core/task-graph/xml-renderer.d.ts +21 -0
  21. package/dist/core/task-graph/xml-renderer.js +81 -0
  22. package/dist/core/templates/agents-template.d.ts +1 -1
  23. package/dist/core/templates/agents-template.js +23 -0
  24. package/dist/core/templates/skill-templates.js +42 -0
  25. package/dist/core/templates/slash-command-templates.js +25 -2
  26. package/dist/core/templates/tasks-template.d.ts +23 -0
  27. package/dist/core/templates/tasks-template.js +79 -0
  28. package/dist/core/templates/tdd-tasks-template.d.ts +24 -0
  29. package/dist/core/templates/tdd-tasks-template.js +116 -0
  30. package/dist/core/validation/strict-rules.d.ts +60 -0
  31. package/dist/core/validation/strict-rules.js +287 -0
  32. package/dist/core/validation/types.d.ts +10 -0
  33. package/dist/core/validation/validator.d.ts +5 -0
  34. package/dist/core/validation/validator.js +103 -1
  35. package/package.json +22 -20
  36. package/schemas/spec-driven/templates/tasks.md +17 -5
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Mermaid Renderer for Execution Plan
3
+ *
4
+ * Renders ExecutionPlan to Mermaid flowchart format for visualization in tasks.md.
5
+ */
6
+ /**
7
+ * Escapes special Mermaid characters in a string.
8
+ * Mermaid uses quotes, brackets, and other characters that need escaping.
9
+ */
10
+ function escapeMermaid(str) {
11
+ return str
12
+ .replace(/"/g, '#quot;')
13
+ .replace(/\[/g, '#91;')
14
+ .replace(/\]/g, '#93;')
15
+ .replace(/\(/g, '#40;')
16
+ .replace(/\)/g, '#41;')
17
+ .replace(/</g, '#lt;')
18
+ .replace(/>/g, '#gt;')
19
+ .replace(/\{/g, '#123;')
20
+ .replace(/\}/g, '#125;')
21
+ .replace(/\|/g, '#124;');
22
+ }
23
+ /**
24
+ * Converts a task ID to a valid Mermaid node ID.
25
+ * Mermaid node IDs cannot contain dots or hyphens.
26
+ *
27
+ * Example: "1.1" -> "T1_1", "2.3" -> "T2_3"
28
+ */
29
+ function toNodeId(taskId) {
30
+ return 'T' + taskId.replace(/\./g, '_').replace(/-/g, '_');
31
+ }
32
+ /**
33
+ * Truncates a string to a maximum length, appending "..." if truncated.
34
+ */
35
+ function truncate(str, maxLength) {
36
+ if (str.length <= maxLength) {
37
+ return str;
38
+ }
39
+ return str.slice(0, maxLength) + '...';
40
+ }
41
+ /**
42
+ * Generates the wave subtitle based on task count and max parallelism.
43
+ */
44
+ function getWaveSubtitle(taskCount, maxParallelism) {
45
+ if (taskCount === 1) {
46
+ return '串行';
47
+ }
48
+ if (taskCount <= maxParallelism) {
49
+ return `并行度: ${taskCount}`;
50
+ }
51
+ // Calculate batch count: ceil(taskCount / maxParallelism)
52
+ const batchCount = Math.ceil(taskCount / maxParallelism);
53
+ return `并行度: ${taskCount}, 建议分批: ${batchCount}批`;
54
+ }
55
+ /**
56
+ * Renders a single task node label.
57
+ */
58
+ function renderNodeLabel(task, showSkills) {
59
+ const truncatedDesc = truncate(task.description, 30);
60
+ let label = `${task.id} ${truncatedDesc}`;
61
+ if (showSkills && task.skills.length > 0) {
62
+ // Show first skill
63
+ label += `\\n@skill:${task.skills[0]}`;
64
+ }
65
+ return escapeMermaid(label);
66
+ }
67
+ /**
68
+ * Renders an ExecutionPlan to Mermaid flowchart format.
69
+ *
70
+ * @param plan - The execution plan to render
71
+ * @param tasks - The parsed tasks (used for task details)
72
+ * @param options - Render options
73
+ * @returns Complete markdown section with Mermaid diagram
74
+ */
75
+ export function renderMermaidDiagram(plan, tasks, options) {
76
+ const maxParallelism = options?.maxParallelism ?? 3;
77
+ const showSkills = options?.showSkills ?? true;
78
+ // Handle error cases - return empty string for cycle
79
+ if (plan.hasCycle) {
80
+ return '';
81
+ }
82
+ // Handle empty plan (all tasks complete or no tasks)
83
+ if (plan.waves.length === 0) {
84
+ return '';
85
+ }
86
+ // Build task map for quick lookup
87
+ const taskMap = new Map();
88
+ for (const task of tasks) {
89
+ taskMap.set(task.id, task);
90
+ }
91
+ const lines = [];
92
+ // Markdown header with auto-generated notice
93
+ lines.push('## Workflow Diagram');
94
+ lines.push('');
95
+ lines.push('<!-- Auto-generated by zhuanspec validate. DO NOT edit manually. -->');
96
+ lines.push('');
97
+ lines.push('```mermaid');
98
+ lines.push('graph TD');
99
+ // Render each wave as a subgraph
100
+ for (const wave of plan.waves) {
101
+ const subtitle = getWaveSubtitle(wave.tasks.length, maxParallelism);
102
+ lines.push(` subgraph Wave${wave.wave}["Wave ${wave.wave} (${subtitle})"]`);
103
+ for (const task of wave.tasks) {
104
+ const nodeId = toNodeId(task.id);
105
+ const label = renderNodeLabel(task, showSkills);
106
+ lines.push(` ${nodeId}["${label}"]`);
107
+ }
108
+ lines.push(' end');
109
+ }
110
+ // Render dependency arrows after all subgraphs
111
+ // Arrow direction: from dependency -> to dependent (被依赖者 -> 依赖者)
112
+ for (const wave of plan.waves) {
113
+ for (const task of wave.tasks) {
114
+ for (const depId of task.depends) {
115
+ // Check if dependency exists in the plan (might be completed already)
116
+ const depTask = taskMap.get(depId);
117
+ if (depTask && !depTask.completed) {
118
+ const fromNode = toNodeId(depId);
119
+ const toNode = toNodeId(task.id);
120
+ lines.push(` ${fromNode} --> ${toNode}`);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ lines.push('```');
126
+ return lines.join('\n');
127
+ }
128
+ //# sourceMappingURL=mermaid-renderer.js.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Task Graph
3
+ *
4
+ * Builds a DAG from parsed tasks and provides graph operations.
5
+ * Uses Kahn's algorithm for topological sorting.
6
+ */
7
+ import type { ParsedTask } from './types.js';
8
+ /**
9
+ * Validation result for the task graph
10
+ */
11
+ export interface ValidationResult {
12
+ valid: boolean;
13
+ errors: string[];
14
+ }
15
+ /**
16
+ * Represents a task dependency graph.
17
+ * Provides methods for querying build order, ready tasks, and completion status.
18
+ */
19
+ export declare class TaskGraph {
20
+ private tasks;
21
+ private inDegree;
22
+ private dependents;
23
+ private dependencies;
24
+ private completedTasks;
25
+ constructor(tasks: ParsedTask[]);
26
+ /**
27
+ * Validates the task graph.
28
+ *
29
+ * Checks:
30
+ * - All depends references exist
31
+ * - No self-dependencies
32
+ * - No circular dependencies
33
+ */
34
+ validate(): ValidationResult;
35
+ /**
36
+ * Performs topological sort using Kahn's algorithm.
37
+ * Returns tasks in execution order.
38
+ */
39
+ topologicalSort(): ParsedTask[];
40
+ /**
41
+ * Detects circular dependencies in the graph.
42
+ * Returns an array of cycle paths.
43
+ */
44
+ detectCycles(): string[][];
45
+ /**
46
+ * Gets tasks that are ready to execute (in-degree 0 and not completed).
47
+ */
48
+ getReady(): ParsedTask[];
49
+ /**
50
+ * Marks a task as completed and updates graph state.
51
+ */
52
+ markCompleted(taskId: string): void;
53
+ /**
54
+ * Gets the direct dependencies of a task (tasks it depends on).
55
+ */
56
+ getDependencies(taskId: string): ParsedTask[];
57
+ /**
58
+ * Gets the direct dependents of a task (tasks that depend on it).
59
+ */
60
+ getDependents(taskId: string): ParsedTask[];
61
+ /**
62
+ * Gets a task by ID.
63
+ */
64
+ getTask(taskId: string): ParsedTask | undefined;
65
+ /**
66
+ * Gets all tasks in the graph.
67
+ */
68
+ getAllTasks(): ParsedTask[];
69
+ /**
70
+ * Checks if all tasks are completed.
71
+ */
72
+ isComplete(): boolean;
73
+ }
74
+ //# sourceMappingURL=task-graph.d.ts.map
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Task Graph
3
+ *
4
+ * Builds a DAG from parsed tasks and provides graph operations.
5
+ * Uses Kahn's algorithm for topological sorting.
6
+ */
7
+ /**
8
+ * Represents a task dependency graph.
9
+ * Provides methods for querying build order, ready tasks, and completion status.
10
+ */
11
+ export class TaskGraph {
12
+ tasks;
13
+ inDegree;
14
+ dependents; // task -> tasks that depend on it
15
+ dependencies; // task -> tasks it depends on
16
+ completedTasks;
17
+ constructor(tasks) {
18
+ this.tasks = new Map(tasks.map(t => [t.id, t]));
19
+ this.inDegree = new Map();
20
+ this.dependents = new Map();
21
+ this.dependencies = new Map();
22
+ this.completedTasks = new Set();
23
+ // Initialize data structures
24
+ for (const task of tasks) {
25
+ this.inDegree.set(task.id, 0);
26
+ this.dependents.set(task.id, []);
27
+ this.dependencies.set(task.id, []);
28
+ // Mark already completed tasks
29
+ if (task.completed) {
30
+ this.completedTasks.add(task.id);
31
+ }
32
+ }
33
+ // Build graph edges
34
+ for (const task of tasks) {
35
+ // Filter depends to only include existing tasks
36
+ const validDepends = task.depends.filter(dep => this.tasks.has(dep));
37
+ this.dependencies.set(task.id, validDepends);
38
+ this.inDegree.set(task.id, validDepends.length);
39
+ for (const dep of validDepends) {
40
+ this.dependents.get(dep).push(task.id);
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Validates the task graph.
46
+ *
47
+ * Checks:
48
+ * - All depends references exist
49
+ * - No self-dependencies
50
+ * - No circular dependencies
51
+ */
52
+ validate() {
53
+ const errors = [];
54
+ // Check for missing dependencies and self-dependencies
55
+ for (const task of this.tasks.values()) {
56
+ for (const dep of task.depends) {
57
+ if (!this.tasks.has(dep)) {
58
+ errors.push(`Task ${task.id}: depends on non-existent task "${dep}"`);
59
+ }
60
+ if (dep === task.id) {
61
+ errors.push(`Task ${task.id}: self-dependency not allowed`);
62
+ }
63
+ }
64
+ // Check for duplicate dependencies (warning, not error)
65
+ const uniqueDeps = new Set(task.depends);
66
+ if (uniqueDeps.size < task.depends.length) {
67
+ const duplicates = task.depends.filter((d, i) => task.depends.indexOf(d) !== i);
68
+ errors.push(`Task ${task.id}: duplicate dependencies detected: ${[...new Set(duplicates)].join(', ')}`);
69
+ }
70
+ }
71
+ // Check for cycles
72
+ const cycles = this.detectCycles();
73
+ if (cycles.length > 0) {
74
+ for (const cycle of cycles) {
75
+ errors.push(`Circular dependency detected: ${cycle.join(' → ')}`);
76
+ }
77
+ }
78
+ return {
79
+ valid: errors.length === 0,
80
+ errors,
81
+ };
82
+ }
83
+ /**
84
+ * Performs topological sort using Kahn's algorithm.
85
+ * Returns tasks in execution order.
86
+ */
87
+ topologicalSort() {
88
+ // Create a copy of in-degrees for this operation
89
+ const inDegree = new Map();
90
+ for (const [id, degree] of this.inDegree) {
91
+ inDegree.set(id, degree);
92
+ }
93
+ // Start with roots (in-degree 0), sorted for determinism
94
+ const queue = [...this.tasks.keys()]
95
+ .filter(id => inDegree.get(id) === 0)
96
+ .sort();
97
+ const result = [];
98
+ while (queue.length > 0) {
99
+ const current = queue.shift();
100
+ result.push(this.tasks.get(current));
101
+ // Collect newly ready tasks, then sort before adding
102
+ const newlyReady = [];
103
+ for (const dep of this.dependents.get(current)) {
104
+ const newDegree = inDegree.get(dep) - 1;
105
+ inDegree.set(dep, newDegree);
106
+ if (newDegree === 0) {
107
+ newlyReady.push(dep);
108
+ }
109
+ }
110
+ queue.push(...newlyReady.sort());
111
+ }
112
+ return result;
113
+ }
114
+ /**
115
+ * Detects circular dependencies in the graph.
116
+ * Returns an array of cycle paths.
117
+ */
118
+ detectCycles() {
119
+ const cycles = [];
120
+ const visited = new Set();
121
+ const recStack = new Set();
122
+ const path = [];
123
+ const dfs = (taskId) => {
124
+ visited.add(taskId);
125
+ recStack.add(taskId);
126
+ path.push(taskId);
127
+ const deps = this.dependencies.get(taskId) || [];
128
+ for (const dep of deps) {
129
+ if (!visited.has(dep)) {
130
+ if (dfs(dep)) {
131
+ return true;
132
+ }
133
+ }
134
+ else if (recStack.has(dep)) {
135
+ // Found a cycle - extract the cycle path
136
+ const cycleStart = path.indexOf(dep);
137
+ const cyclePath = [...path.slice(cycleStart), dep];
138
+ cycles.push(cyclePath);
139
+ return true;
140
+ }
141
+ }
142
+ path.pop();
143
+ recStack.delete(taskId);
144
+ return false;
145
+ };
146
+ for (const taskId of this.tasks.keys()) {
147
+ if (!visited.has(taskId)) {
148
+ dfs(taskId);
149
+ }
150
+ }
151
+ return cycles;
152
+ }
153
+ /**
154
+ * Gets tasks that are ready to execute (in-degree 0 and not completed).
155
+ */
156
+ getReady() {
157
+ const ready = [];
158
+ for (const task of this.tasks.values()) {
159
+ if (this.completedTasks.has(task.id)) {
160
+ continue; // Already completed
161
+ }
162
+ // Check if all dependencies are completed
163
+ const deps = this.dependencies.get(task.id) || [];
164
+ const allDepsCompleted = deps.every(dep => this.completedTasks.has(dep));
165
+ if (allDepsCompleted) {
166
+ ready.push(task);
167
+ }
168
+ }
169
+ // Sort for deterministic ordering
170
+ return ready.sort((a, b) => a.id.localeCompare(b.id));
171
+ }
172
+ /**
173
+ * Marks a task as completed and updates graph state.
174
+ */
175
+ markCompleted(taskId) {
176
+ if (!this.tasks.has(taskId)) {
177
+ throw new Error(`Task "${taskId}" not found in graph`);
178
+ }
179
+ this.completedTasks.add(taskId);
180
+ }
181
+ /**
182
+ * Gets the direct dependencies of a task (tasks it depends on).
183
+ */
184
+ getDependencies(taskId) {
185
+ const deps = this.dependencies.get(taskId) || [];
186
+ return deps.map(id => this.tasks.get(id)).filter(Boolean);
187
+ }
188
+ /**
189
+ * Gets the direct dependents of a task (tasks that depend on it).
190
+ */
191
+ getDependents(taskId) {
192
+ const deps = this.dependents.get(taskId) || [];
193
+ return deps.map(id => this.tasks.get(id)).filter(Boolean);
194
+ }
195
+ /**
196
+ * Gets a task by ID.
197
+ */
198
+ getTask(taskId) {
199
+ return this.tasks.get(taskId);
200
+ }
201
+ /**
202
+ * Gets all tasks in the graph.
203
+ */
204
+ getAllTasks() {
205
+ return Array.from(this.tasks.values());
206
+ }
207
+ /**
208
+ * Checks if all tasks are completed.
209
+ */
210
+ isComplete() {
211
+ for (const task of this.tasks.values()) {
212
+ if (!this.completedTasks.has(task.id)) {
213
+ return false;
214
+ }
215
+ }
216
+ return true;
217
+ }
218
+ }
219
+ //# sourceMappingURL=task-graph.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Task Parser
3
+ *
4
+ * Parses tasks.md content into structured ParsedTask array.
5
+ */
6
+ import type { ParsedTask } from './types.js';
7
+ /**
8
+ * Parse tasks.md content into an array of ParsedTask objects.
9
+ *
10
+ * @param content - The raw content of tasks.md
11
+ * @returns Array of parsed tasks
12
+ */
13
+ export declare function parseTasks(content: string): ParsedTask[];
14
+ //# sourceMappingURL=task-parser.d.ts.map
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Task Parser
3
+ *
4
+ * Parses tasks.md content into structured ParsedTask array.
5
+ */
6
+ // Regex patterns for parsing
7
+ // Group header: ## N. Group Name
8
+ const GROUP_HEADER_REGEX = /^##\s+(\d+)\.\s+(.+)$/;
9
+ // Task line: - [ ] N.M description @skill:name1,name2 @depends:id1,id2
10
+ // or: - [x] N.M description ...
11
+ const TASK_LINE_REGEX = /^-\s+\[([ xX])\]\s+(\d+\.\d+)\s+(.*)$/;
12
+ // Annotations (can be inside or outside HTML comments)
13
+ const SKILL_REGEX = /@skill:([^\s@]+)/g;
14
+ const DEPENDS_REGEX = /@depends:([^\s@]+)/g;
15
+ // HTML comment pattern to extract content
16
+ const HTML_COMMENT_REGEX = /<!--\s*(.*?)\s*-->/g;
17
+ /**
18
+ * Extract annotations from text (handles both plain text and HTML comments)
19
+ */
20
+ function extractAnnotations(text) {
21
+ const skills = [];
22
+ const depends = [];
23
+ // Collect all text to search (including content inside HTML comments)
24
+ let searchText = text;
25
+ // Extract content from HTML comments and add to search text
26
+ let match;
27
+ while ((match = HTML_COMMENT_REGEX.exec(text)) !== null) {
28
+ searchText += ' ' + match[1];
29
+ }
30
+ // Reset lastIndex for subsequent regex operations
31
+ HTML_COMMENT_REGEX.lastIndex = 0;
32
+ // Extract skills
33
+ let skillMatch;
34
+ while ((skillMatch = SKILL_REGEX.exec(searchText)) !== null) {
35
+ const skillList = skillMatch[1].split(',').filter(s => s.trim());
36
+ skills.push(...skillList);
37
+ }
38
+ SKILL_REGEX.lastIndex = 0;
39
+ // Extract depends
40
+ let dependsMatch;
41
+ while ((dependsMatch = DEPENDS_REGEX.exec(searchText)) !== null) {
42
+ const dependsList = dependsMatch[1].split(',').filter(d => d.trim());
43
+ depends.push(...dependsList);
44
+ }
45
+ DEPENDS_REGEX.lastIndex = 0;
46
+ return {
47
+ skills: [...new Set(skills)], // dedupe
48
+ depends: [...new Set(depends)], // dedupe
49
+ };
50
+ }
51
+ /**
52
+ * Clean description by removing annotations
53
+ */
54
+ function cleanDescription(text) {
55
+ let cleaned = text
56
+ // Remove HTML comments with annotations
57
+ .replace(/<!--\s*@(?:skill|depends):[^\s]*(?:\s+@(?:skill|depends):[^\s]*)?\s*-->/g, '')
58
+ // Remove plain annotations
59
+ .replace(/@skill:[^\s@]+/g, '')
60
+ .replace(/@depends:[^\s@]+/g, '')
61
+ .trim();
62
+ return cleaned;
63
+ }
64
+ /**
65
+ * Parse tasks.md content into an array of ParsedTask objects.
66
+ *
67
+ * @param content - The raw content of tasks.md
68
+ * @returns Array of parsed tasks
69
+ */
70
+ export function parseTasks(content) {
71
+ if (!content || !content.trim()) {
72
+ return [];
73
+ }
74
+ const lines = content.split('\n');
75
+ const tasks = [];
76
+ let currentGroup = '';
77
+ let currentGroupName = '';
78
+ for (const line of lines) {
79
+ // Check for group header
80
+ const groupMatch = line.match(GROUP_HEADER_REGEX);
81
+ if (groupMatch) {
82
+ currentGroup = groupMatch[1];
83
+ currentGroupName = groupMatch[2].trim();
84
+ continue;
85
+ }
86
+ // Check for task line
87
+ const taskMatch = line.match(TASK_LINE_REGEX);
88
+ if (taskMatch) {
89
+ const checkboxState = taskMatch[1];
90
+ const taskId = taskMatch[2];
91
+ const rest = taskMatch[3];
92
+ // Extract annotations
93
+ const { skills, depends } = extractAnnotations(rest);
94
+ // Clean description
95
+ const description = cleanDescription(rest);
96
+ // Parse group from task ID if not already set
97
+ const taskGroup = taskId.split('.')[0];
98
+ tasks.push({
99
+ id: taskId,
100
+ group: currentGroup || taskGroup,
101
+ groupName: currentGroupName,
102
+ description,
103
+ skills,
104
+ depends,
105
+ completed: checkboxState.toLowerCase() === 'x',
106
+ });
107
+ }
108
+ }
109
+ return tasks;
110
+ }
111
+ //# sourceMappingURL=task-parser.js.map
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Task Graph Types
3
+ *
4
+ * Type definitions for task dependency graph module.
5
+ */
6
+ /**
7
+ * Represents a parsed task from tasks.md
8
+ */
9
+ export interface ParsedTask {
10
+ /** Task ID, e.g., "1.1", "2.3" */
11
+ id: string;
12
+ /** Group number, e.g., "1", "2" */
13
+ group: string;
14
+ /** Group name */
15
+ groupName: string;
16
+ /** Task description text */
17
+ description: string;
18
+ /** Skills required, e.g., ["java-dao-standards"] */
19
+ skills: string[];
20
+ /** Task IDs this task depends on, e.g., ["1.1", "2.1"] */
21
+ depends: string[];
22
+ /** Whether the task is completed (checkbox state [x] vs [ ]) */
23
+ completed: boolean;
24
+ }
25
+ /**
26
+ * Represents a wave of tasks that can be executed together
27
+ */
28
+ export interface ExecutionWave {
29
+ /** Wave number (starting from 1) */
30
+ wave: number;
31
+ /** Tasks in this wave */
32
+ tasks: ParsedTask[];
33
+ /** Whether tasks can run in parallel (tasks.length > 1) */
34
+ parallel: boolean;
35
+ }
36
+ /**
37
+ * Represents the complete execution plan
38
+ */
39
+ export interface ExecutionPlan {
40
+ /** Waves of tasks to execute */
41
+ waves: ExecutionWave[];
42
+ /** Total number of tasks */
43
+ totalTasks: number;
44
+ /** Number of tasks that can be executed in parallel (tasks in multi-task waves) */
45
+ parallelTasks: number;
46
+ /** Number of tasks that must be executed serially (tasks in single-task waves) */
47
+ serialTasks: number;
48
+ /** Whether there is a cycle in the dependency graph */
49
+ hasCycle: boolean;
50
+ /** Validation errors */
51
+ errors: string[];
52
+ }
53
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Task Graph Types
3
+ *
4
+ * Type definitions for task dependency graph module.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * XML Renderer for Execution Plan
3
+ *
4
+ * Renders ExecutionPlan to XML format for injection into apply instructions.
5
+ */
6
+ import type { ExecutionPlan, ParsedTask } from './types.js';
7
+ /**
8
+ * Renders an ExecutionPlan to XML format.
9
+ *
10
+ * @param plan - The execution plan to render
11
+ * @returns XML string representation of the plan
12
+ */
13
+ export declare function renderExecutionPlanXml(plan: ExecutionPlan): string;
14
+ /**
15
+ * Checks if any task in the parsed tasks has @depends annotation.
16
+ *
17
+ * @param tasks - Array of parsed tasks
18
+ * @returns True if at least one task has dependencies
19
+ */
20
+ export declare function hasDependsAnnotations(tasks: ParsedTask[]): boolean;
21
+ //# sourceMappingURL=xml-renderer.d.ts.map