workflow-ai 1.0.1 → 1.0.3

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.
@@ -256,6 +256,8 @@ pipeline:
256
256
  agent: script-move-to-review
257
257
  timeout: 120
258
258
  goto:
259
+ skipped:
260
+ stage: pick-next-task
259
261
  default:
260
262
  stage: review-result
261
263
  params:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow-ai",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "AI Agent Workflow Coordinator — kanban-based pipeline for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -101,11 +101,44 @@ function readTickets(dir) {
101
101
  return tickets;
102
102
  }
103
103
 
104
+ /**
105
+ * Нормализует входное значение в формат PLAN-NNN.
106
+ * Принимает: "PLAN-007", "7", "007", "plan-7", "plans/PLAN-007.md", "/abs/path/PLAN-007.md"
107
+ */
108
+ function normalizePlanId(raw) {
109
+ if (!raw) return null;
110
+
111
+ // Извлекаем имя файла если передан путь
112
+ const basename = path.basename(raw, '.md');
113
+
114
+ // Уже в формате PLAN-NNN (регистронезависимо)
115
+ const full = basename.match(/^plan-(\d+)$/i);
116
+ if (full) return `PLAN-${String(parseInt(full[1], 10)).padStart(3, '0')}`;
117
+
118
+ // Просто цифра или число: "7", "007"
119
+ const num = raw.trim().match(/^(\d+)$/);
120
+ if (num) return `PLAN-${String(parseInt(num[1], 10)).padStart(3, '0')}`;
121
+
122
+ return null;
123
+ }
124
+
125
+ /**
126
+ * Извлекает plan_id из аргументов командной строки (контекст пайплайна)
127
+ */
128
+ function extractPlanId() {
129
+ const prompt = process.argv.slice(2)[0] || '';
130
+ const match = prompt.match(/plan_id:\s*(\S+)/i);
131
+ return match ? normalizePlanId(match[1]) : null;
132
+ }
133
+
104
134
  /**
105
135
  * Проверяет все тикеты в backlog/ и возвращает список готовых
106
136
  */
107
- function checkBacklog() {
108
- const tickets = readTickets(BACKLOG_DIR);
137
+ function checkBacklog(planId) {
138
+ const allTickets = readTickets(BACKLOG_DIR);
139
+ const tickets = planId
140
+ ? allTickets.filter(t => t.frontmatter.parent_plan === planId)
141
+ : allTickets;
109
142
 
110
143
  const ready = [];
111
144
  const waiting = [];
@@ -134,11 +167,17 @@ function checkBacklog() {
134
167
  }
135
168
 
136
169
  async function main() {
170
+ const planId = extractPlanId();
171
+
172
+ if (planId) {
173
+ console.log(`[INFO] Filtering by plan_id: ${planId}`);
174
+ }
175
+
137
176
  console.log(`[INFO] Scanning backlog/: ${BACKLOG_DIR}`);
138
177
 
139
- const { ready, waiting, total } = checkBacklog();
178
+ const { ready, waiting, total } = checkBacklog(planId);
140
179
 
141
- console.log(`[INFO] Total in backlog: ${total}`);
180
+ console.log(`[INFO] Total in backlog${planId ? ` (plan ${planId})` : ''}: ${total}`);
142
181
  console.log(`[INFO] Ready: ${ready.length}, Waiting: ${waiting.length}`);
143
182
 
144
183
  if (ready.length > 0) {
@@ -39,6 +39,11 @@ function moveToReview(ticketId) {
39
39
  const targetPath = path.join(REVIEW_DIR, `${ticketId}.md`);
40
40
 
41
41
  if (!fs.existsSync(sourcePath)) {
42
+ // Ticket may have been moved directly to done/ by the agent — treat as already done
43
+ const donePath = path.join(TICKETS_DIR, 'done', `${ticketId}.md`);
44
+ if (fs.existsSync(donePath)) {
45
+ return { status: 'skipped', ticket_id: ticketId, reason: `${ticketId} already in done/` };
46
+ }
42
47
  return { status: 'error', ticket_id: ticketId, error: `${ticketId} not found in in-progress/` };
43
48
  }
44
49
 
@@ -78,6 +83,10 @@ async function main() {
78
83
  if (result.status === 'error') {
79
84
  process.exit(1);
80
85
  }
86
+
87
+ if (result.status === 'skipped') {
88
+ console.log(`[INFO] Skipped: ${result.reason}`);
89
+ }
81
90
  }
82
91
 
83
92
  main().catch(e => {
@@ -5,6 +5,10 @@ description: Проверить результат выполнения зада
5
5
 
6
6
  # Проверка результата (Review Result)
7
7
 
8
+ > **ОБЯЗАТЕЛЬНО:** Последние строки твоего ответа ВСЕГДА должны быть блоком `---RESULT---`.
9
+ > Без этого блока пайплайн не распознает статус и тикет попадёт в `blocked`.
10
+ > Даже если ревью очевидно — всё равно заверши ответ блоком результата.
11
+
8
12
  Этот skill проверяет результат выполнения задачи на соответствие критериям готовности (Definition of Done) из тикета.
9
13
 
10
14
  ## Когда использовать