awesome-slash 2.4.2

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 (95) hide show
  1. package/.claude-plugin/marketplace.json +54 -0
  2. package/.claude-plugin/plugin.json +11 -0
  3. package/.mcp.json +8 -0
  4. package/CHANGELOG.md +261 -0
  5. package/LICENSE +21 -0
  6. package/README.md +363 -0
  7. package/SECURITY.md +101 -0
  8. package/adapters/README.md +256 -0
  9. package/adapters/codex/README.md +272 -0
  10. package/adapters/codex/install.sh +179 -0
  11. package/adapters/opencode/README.md +301 -0
  12. package/adapters/opencode/install.sh +223 -0
  13. package/lib/patterns/review-patterns.js +511 -0
  14. package/lib/patterns/slop-patterns.js +647 -0
  15. package/lib/platform/detect-platform.js +535 -0
  16. package/lib/platform/verify-tools.js +235 -0
  17. package/lib/state/workflow-state.js +635 -0
  18. package/lib/state/workflow-state.schema.json +282 -0
  19. package/lib/utils/context-optimizer.js +227 -0
  20. package/mcp-server/index.js +303 -0
  21. package/mcp-server/package.json +23 -0
  22. package/package.json +63 -0
  23. package/plugins/deslop-around/.claude-plugin/plugin.json +20 -0
  24. package/plugins/deslop-around/commands/deslop-around.md +220 -0
  25. package/plugins/deslop-around/lib/patterns/review-patterns.js +511 -0
  26. package/plugins/deslop-around/lib/patterns/slop-patterns.js +641 -0
  27. package/plugins/deslop-around/lib/platform/detect-platform.js +514 -0
  28. package/plugins/deslop-around/lib/platform/verify-tools.js +235 -0
  29. package/plugins/deslop-around/lib/state/workflow-state.js +635 -0
  30. package/plugins/deslop-around/lib/state/workflow-state.schema.json +282 -0
  31. package/plugins/deslop-around/lib/utils/context-optimizer.js +222 -0
  32. package/plugins/next-task/.claude-plugin/plugin.json +24 -0
  33. package/plugins/next-task/agents/ci-fixer.md +236 -0
  34. package/plugins/next-task/agents/ci-monitor.md +291 -0
  35. package/plugins/next-task/agents/delivery-validator.md +451 -0
  36. package/plugins/next-task/agents/deslop-work.md +272 -0
  37. package/plugins/next-task/agents/docs-updater.md +506 -0
  38. package/plugins/next-task/agents/exploration-agent.md +277 -0
  39. package/plugins/next-task/agents/implementation-agent.md +427 -0
  40. package/plugins/next-task/agents/planning-agent.md +236 -0
  41. package/plugins/next-task/agents/policy-selector.md +248 -0
  42. package/plugins/next-task/agents/review-orchestrator.md +521 -0
  43. package/plugins/next-task/agents/simple-fixer.md +136 -0
  44. package/plugins/next-task/agents/task-discoverer.md +357 -0
  45. package/plugins/next-task/agents/test-coverage-checker.md +447 -0
  46. package/plugins/next-task/agents/worktree-manager.md +419 -0
  47. package/plugins/next-task/commands/delivery-approval.md +331 -0
  48. package/plugins/next-task/commands/next-task.md +627 -0
  49. package/plugins/next-task/commands/update-docs-around.md +418 -0
  50. package/plugins/next-task/hooks/hooks.json +14 -0
  51. package/plugins/next-task/lib/patterns/review-patterns.js +511 -0
  52. package/plugins/next-task/lib/patterns/slop-patterns.js +641 -0
  53. package/plugins/next-task/lib/platform/detect-platform.js +514 -0
  54. package/plugins/next-task/lib/platform/verify-tools.js +235 -0
  55. package/plugins/next-task/lib/state/tasks-registry.schema.json +85 -0
  56. package/plugins/next-task/lib/state/workflow-state.js +635 -0
  57. package/plugins/next-task/lib/state/workflow-state.schema.json +282 -0
  58. package/plugins/next-task/lib/state/worktree-status.schema.json +219 -0
  59. package/plugins/next-task/lib/utils/context-optimizer.js +222 -0
  60. package/plugins/project-review/.claude-plugin/plugin.json +20 -0
  61. package/plugins/project-review/commands/project-review-agents.md +286 -0
  62. package/plugins/project-review/commands/project-review-github.md +142 -0
  63. package/plugins/project-review/commands/project-review.md +273 -0
  64. package/plugins/project-review/lib/patterns/review-patterns.js +511 -0
  65. package/plugins/project-review/lib/patterns/slop-patterns.js +641 -0
  66. package/plugins/project-review/lib/platform/detect-platform.js +514 -0
  67. package/plugins/project-review/lib/platform/verify-tools.js +235 -0
  68. package/plugins/project-review/lib/state/workflow-state.js +635 -0
  69. package/plugins/project-review/lib/state/workflow-state.schema.json +282 -0
  70. package/plugins/project-review/lib/utils/context-optimizer.js +222 -0
  71. package/plugins/reality-check/.claude-plugin/plugin.json +23 -0
  72. package/plugins/reality-check/README.md +156 -0
  73. package/plugins/reality-check/agents/code-explorer.md +353 -0
  74. package/plugins/reality-check/agents/doc-analyzer.md +337 -0
  75. package/plugins/reality-check/agents/issue-scanner.md +231 -0
  76. package/plugins/reality-check/agents/plan-synthesizer.md +479 -0
  77. package/plugins/reality-check/commands/scan.md +242 -0
  78. package/plugins/reality-check/commands/set.md +203 -0
  79. package/plugins/reality-check/lib/state/reality-check-state.js +509 -0
  80. package/plugins/reality-check/skills/reality-analysis/SKILL.md +317 -0
  81. package/plugins/ship/.claude-plugin/plugin.json +21 -0
  82. package/plugins/ship/commands/ship-ci-review-loop.md +443 -0
  83. package/plugins/ship/commands/ship-deployment.md +330 -0
  84. package/plugins/ship/commands/ship-error-handling.md +254 -0
  85. package/plugins/ship/commands/ship.md +370 -0
  86. package/plugins/ship/lib/patterns/review-patterns.js +511 -0
  87. package/plugins/ship/lib/patterns/slop-patterns.js +641 -0
  88. package/plugins/ship/lib/platform/detect-platform.js +514 -0
  89. package/plugins/ship/lib/platform/verify-tools.js +235 -0
  90. package/plugins/ship/lib/state/workflow-state.js +635 -0
  91. package/plugins/ship/lib/state/workflow-state.schema.json +282 -0
  92. package/plugins/ship/lib/utils/context-optimizer.js +222 -0
  93. package/scripts/install/claude.sh +50 -0
  94. package/scripts/install/codex.sh +181 -0
  95. package/scripts/install/opencode.sh +211 -0
@@ -0,0 +1,635 @@
1
+ /**
2
+ * Workflow State Management
3
+ *
4
+ * Persistent state management for next-task workflow orchestration.
5
+ * Enables resume capability, multi-agent coordination, and progress tracking.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const crypto = require('crypto');
11
+
12
+ const SCHEMA_VERSION = '2.0.0';
13
+ const STATE_DIR = '.claude';
14
+ const STATE_FILE = 'workflow-state.json';
15
+
16
+ const PHASES = [
17
+ 'policy-selection',
18
+ 'task-discovery',
19
+ 'worktree-setup',
20
+ 'exploration',
21
+ 'planning',
22
+ 'user-approval',
23
+ 'implementation',
24
+ 'review-loop',
25
+ 'delivery-approval',
26
+ 'ship-prep',
27
+ 'create-pr',
28
+ 'ci-wait',
29
+ 'comment-fix',
30
+ 'merge',
31
+ 'production-ci',
32
+ 'deploy',
33
+ 'production-release',
34
+ 'complete'
35
+ ];
36
+
37
+ const DEFAULT_POLICY = {
38
+ taskSource: 'gh-issues',
39
+ priorityFilter: 'continue',
40
+ platform: 'detected',
41
+ stoppingPoint: 'merged',
42
+ mergeStrategy: 'squash',
43
+ autoFix: true,
44
+ maxReviewIterations: 3
45
+ };
46
+
47
+ /**
48
+ * Generate a unique workflow ID
49
+ * @returns {string} Workflow ID in format: workflow-YYYYMMDD-HHMMSS-random
50
+ */
51
+ function generateWorkflowId() {
52
+ const now = new Date();
53
+ const date = now.toISOString().slice(0, 10).replace(/-/g, '');
54
+ const time = now.toISOString().slice(11, 19).replace(/:/g, '');
55
+ const random = crypto.randomBytes(4).toString('hex');
56
+ return `workflow-${date}-${time}-${random}`;
57
+ }
58
+
59
+ /**
60
+ * Get the state file path
61
+ * @param {string} [baseDir=process.cwd()] - Base directory
62
+ * @returns {string} Full path to state file
63
+ */
64
+ function getStatePath(baseDir = process.cwd()) {
65
+ return path.join(baseDir, STATE_DIR, STATE_FILE);
66
+ }
67
+
68
+ /**
69
+ * Ensure state directory exists
70
+ * @param {string} [baseDir=process.cwd()] - Base directory
71
+ */
72
+ function ensureStateDir(baseDir = process.cwd()) {
73
+ const stateDir = path.join(baseDir, STATE_DIR);
74
+ if (!fs.existsSync(stateDir)) {
75
+ fs.mkdirSync(stateDir, { recursive: true });
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Create a new workflow state
81
+ * @param {string} [type='next-task'] - Workflow type
82
+ * @param {Object} [policy={}] - Policy overrides
83
+ * @returns {Object} New workflow state
84
+ */
85
+ function createState(type = 'next-task', policy = {}) {
86
+ const now = new Date().toISOString();
87
+
88
+ return {
89
+ version: SCHEMA_VERSION,
90
+ workflow: {
91
+ id: generateWorkflowId(),
92
+ type,
93
+ status: 'pending',
94
+ startedAt: now,
95
+ lastUpdatedAt: now,
96
+ completedAt: null
97
+ },
98
+ policy: { ...DEFAULT_POLICY, ...policy },
99
+ task: null,
100
+ git: null,
101
+ pr: null,
102
+ phases: {
103
+ current: 'policy-selection',
104
+ currentIteration: 0,
105
+ history: []
106
+ },
107
+ agents: null,
108
+ checkpoints: {
109
+ canResume: true,
110
+ resumeFrom: null,
111
+ resumeContext: null
112
+ },
113
+ metrics: {
114
+ totalDuration: 0,
115
+ tokensUsed: 0,
116
+ toolCalls: 0,
117
+ filesModified: 0,
118
+ linesAdded: 0,
119
+ linesRemoved: 0
120
+ }
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Read workflow state from file
126
+ * @param {string} [baseDir=process.cwd()] - Base directory
127
+ * @returns {Object|Error|null} Workflow state, Error if corrupted, or null if not found
128
+ */
129
+ function readState(baseDir = process.cwd()) {
130
+ const statePath = getStatePath(baseDir);
131
+
132
+ if (!fs.existsSync(statePath)) {
133
+ return null;
134
+ }
135
+
136
+ try {
137
+ const content = fs.readFileSync(statePath, 'utf8');
138
+ const state = JSON.parse(content);
139
+
140
+ // Version check
141
+ if (state.version !== SCHEMA_VERSION) {
142
+ console.warn(`State version mismatch: ${state.version} vs ${SCHEMA_VERSION}`);
143
+ // Future: Add migration logic here
144
+ }
145
+
146
+ return state;
147
+ } catch (error) {
148
+ const corrupted = new Error(`Corrupted workflow state: ${error.message}`);
149
+ corrupted.code = 'ERR_STATE_CORRUPTED';
150
+ corrupted.cause = error;
151
+ console.error(corrupted.message);
152
+ return corrupted;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Write workflow state to file
158
+ * @param {Object} state - Workflow state
159
+ * @param {string} [baseDir=process.cwd()] - Base directory
160
+ * @returns {boolean} Success status
161
+ */
162
+ function writeState(state, baseDir = process.cwd()) {
163
+ ensureStateDir(baseDir);
164
+ const statePath = getStatePath(baseDir);
165
+
166
+ try {
167
+ // Update timestamp
168
+ state.workflow.lastUpdatedAt = new Date().toISOString();
169
+
170
+ const content = JSON.stringify(state, null, 2);
171
+ fs.writeFileSync(statePath, content, 'utf8');
172
+ return true;
173
+ } catch (error) {
174
+ console.error(`Error writing state: ${error.message}`);
175
+ return false;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Update specific fields in workflow state
181
+ * @param {Object} updates - Fields to update (deep merge)
182
+ * @param {string} [baseDir=process.cwd()] - Base directory
183
+ * @returns {Object|null} Updated state or null on error
184
+ */
185
+ function updateState(updates, baseDir = process.cwd()) {
186
+ let state = readState(baseDir);
187
+
188
+ if (state instanceof Error) {
189
+ console.error(`Cannot update state: ${state.message}`);
190
+ return null;
191
+ }
192
+ if (!state) {
193
+ console.error('No existing state to update');
194
+ return null;
195
+ }
196
+
197
+ // Deep merge updates
198
+ state = deepMerge(state, updates);
199
+
200
+ if (writeState(state, baseDir)) {
201
+ return state;
202
+ }
203
+
204
+ return null;
205
+ }
206
+
207
+ /**
208
+ * Deep merge two objects (with prototype pollution protection)
209
+ * @param {Object} target - Target object
210
+ * @param {Object} source - Source object
211
+ * @returns {Object} Merged object
212
+ */
213
+ function deepMerge(target, source) {
214
+ // Handle null/undefined cases
215
+ if (!source || typeof source !== 'object') return target;
216
+ if (!target || typeof target !== 'object') return source;
217
+
218
+ const result = { ...target };
219
+
220
+ for (const key of Object.keys(source)) {
221
+ // Protect against prototype pollution
222
+ if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
223
+ continue;
224
+ }
225
+
226
+ const sourceVal = source[key];
227
+ const targetVal = result[key];
228
+
229
+ // Handle Date objects - preserve as-is
230
+ if (sourceVal instanceof Date) {
231
+ result[key] = new Date(sourceVal.getTime());
232
+ }
233
+ // Handle null explicitly - allow overwriting with null
234
+ else if (sourceVal === null) {
235
+ result[key] = null;
236
+ }
237
+ // Recursively merge plain objects
238
+ else if (sourceVal && typeof sourceVal === 'object' && !Array.isArray(sourceVal)) {
239
+ result[key] = deepMerge(targetVal || {}, sourceVal);
240
+ }
241
+ // Replace arrays and primitives
242
+ else {
243
+ result[key] = sourceVal;
244
+ }
245
+ }
246
+
247
+ return result;
248
+ }
249
+
250
+ /**
251
+ * Start a new phase
252
+ * @param {string} phaseName - Phase name
253
+ * @param {string} [baseDir=process.cwd()] - Base directory
254
+ * @returns {Object|null} Updated state or null on error
255
+ */
256
+ function startPhase(phaseName, baseDir = process.cwd()) {
257
+ if (!PHASES.includes(phaseName)) {
258
+ console.error(`Invalid phase: ${phaseName}`);
259
+ return null;
260
+ }
261
+
262
+ const state = readState(baseDir);
263
+ if (state instanceof Error) {
264
+ console.error(`Cannot start phase: ${state.message}`);
265
+ return null;
266
+ }
267
+ if (!state) {
268
+ console.error('No workflow state exists. Create a workflow first.');
269
+ return null;
270
+ }
271
+
272
+ const history = state.phases?.history || [];
273
+
274
+ history.push({
275
+ phase: phaseName,
276
+ status: 'in_progress',
277
+ startedAt: new Date().toISOString(),
278
+ completedAt: null,
279
+ duration: null,
280
+ result: null
281
+ });
282
+
283
+ return updateState({
284
+ workflow: { status: 'in_progress' },
285
+ phases: { current: phaseName, history },
286
+ checkpoints: { canResume: true, resumeFrom: phaseName, resumeContext: null }
287
+ }, baseDir);
288
+ }
289
+
290
+ /**
291
+ * Update the current phase entry with completion data
292
+ * @param {Object} state - Current state
293
+ * @param {string} status - New status (completed/failed)
294
+ * @param {Object} result - Result data
295
+ * @returns {Object} Updated history
296
+ */
297
+ function finalizePhaseEntry(state, status, result) {
298
+ const history = state.phases.history || [];
299
+ const entry = history[history.length - 1];
300
+
301
+ if (entry) {
302
+ const now = new Date().toISOString();
303
+ entry.status = status;
304
+ entry.completedAt = now;
305
+ entry.duration = new Date(now).getTime() - new Date(entry.startedAt).getTime();
306
+ entry.result = result;
307
+ }
308
+
309
+ return history;
310
+ }
311
+
312
+ /**
313
+ * Complete the current phase
314
+ * @param {Object} [result={}] - Phase result data
315
+ * @param {string} [baseDir=process.cwd()] - Base directory
316
+ * @returns {Object|null} Updated state or null on error
317
+ */
318
+ function completePhase(result = {}, baseDir = process.cwd()) {
319
+ const state = readState(baseDir);
320
+ if (state instanceof Error) {
321
+ console.error(`Cannot complete phase: ${state.message}`);
322
+ return null;
323
+ }
324
+ if (!state) return null;
325
+
326
+ const history = finalizePhaseEntry(state, 'completed', result);
327
+ const currentIndex = PHASES.indexOf(state.phases.current);
328
+ const nextPhase = currentIndex < PHASES.length - 1 ? PHASES[currentIndex + 1] : 'complete';
329
+
330
+ return updateState({
331
+ phases: { current: nextPhase, history },
332
+ checkpoints: { resumeFrom: nextPhase, resumeContext: null }
333
+ }, baseDir);
334
+ }
335
+
336
+ /**
337
+ * Fail the current phase
338
+ * @param {string} reason - Failure reason
339
+ * @param {Object} [context={}] - Context for resume
340
+ * @param {string} [baseDir=process.cwd()] - Base directory
341
+ * @returns {Object|null} Updated state or null on error
342
+ */
343
+ function failPhase(reason, context = {}, baseDir = process.cwd()) {
344
+ const state = readState(baseDir);
345
+ if (state instanceof Error) {
346
+ console.error(`Cannot fail phase: ${state.message}`);
347
+ return null;
348
+ }
349
+ if (!state) return null;
350
+
351
+ const history = finalizePhaseEntry(state, 'failed', { error: reason });
352
+
353
+ return updateState({
354
+ workflow: { status: 'failed' },
355
+ phases: { history },
356
+ checkpoints: {
357
+ canResume: true,
358
+ resumeFrom: state.phases.current,
359
+ resumeContext: { reason, ...context }
360
+ }
361
+ }, baseDir);
362
+ }
363
+
364
+ /**
365
+ * Skip to a specific phase
366
+ * @param {string} phaseName - Phase to skip to
367
+ * @param {string} [reason='manual skip'] - Skip reason
368
+ * @param {string} [baseDir=process.cwd()] - Base directory
369
+ * @returns {Object|null} Updated state or null on error
370
+ */
371
+ function skipToPhase(phaseName, reason = 'manual skip', baseDir = process.cwd()) {
372
+ if (!PHASES.includes(phaseName)) {
373
+ console.error(`Invalid phase: ${phaseName}`);
374
+ return null;
375
+ }
376
+
377
+ const state = readState(baseDir);
378
+ if (state instanceof Error) {
379
+ console.error(`Cannot skip to phase: ${state.message}`);
380
+ return null;
381
+ }
382
+ if (!state) return null;
383
+
384
+ const currentIndex = PHASES.indexOf(state.phases.current);
385
+ const targetIndex = PHASES.indexOf(phaseName);
386
+
387
+ // Add skipped entries for phases we're jumping over
388
+ const history = [...(state.phases.history || [])];
389
+ const now = new Date().toISOString();
390
+
391
+ for (let i = currentIndex; i < targetIndex; i++) {
392
+ history.push({
393
+ phase: PHASES[i],
394
+ status: 'skipped',
395
+ startedAt: now,
396
+ completedAt: now,
397
+ duration: 0,
398
+ result: { skippedReason: reason }
399
+ });
400
+ }
401
+
402
+ return updateState({
403
+ phases: {
404
+ current: phaseName,
405
+ history
406
+ },
407
+ checkpoints: {
408
+ resumeFrom: phaseName
409
+ }
410
+ }, baseDir);
411
+ }
412
+
413
+ /**
414
+ * Complete the entire workflow
415
+ * @param {Object} [result={}] - Final result data
416
+ * @param {string} [baseDir=process.cwd()] - Base directory
417
+ * @returns {Object|null} Updated state or null on error
418
+ */
419
+ function completeWorkflow(result = {}, baseDir = process.cwd()) {
420
+ const state = readState(baseDir);
421
+ if (state instanceof Error) {
422
+ console.error(`Cannot complete workflow: ${state.message}`);
423
+ return null;
424
+ }
425
+ if (!state) return null;
426
+
427
+ const now = new Date().toISOString();
428
+ const startTime = new Date(state.workflow.startedAt).getTime();
429
+ const endTime = new Date(now).getTime();
430
+
431
+ return updateState({
432
+ workflow: {
433
+ status: 'completed',
434
+ completedAt: now
435
+ },
436
+ phases: {
437
+ current: 'complete'
438
+ },
439
+ checkpoints: {
440
+ canResume: false,
441
+ resumeFrom: null
442
+ },
443
+ metrics: {
444
+ totalDuration: endTime - startTime,
445
+ ...result.metrics
446
+ }
447
+ }, baseDir);
448
+ }
449
+
450
+ /**
451
+ * Abort the workflow
452
+ * @param {string} [reason='user aborted'] - Abort reason
453
+ * @param {string} [baseDir=process.cwd()] - Base directory
454
+ * @returns {Object|null} Updated state or null on error
455
+ */
456
+ function abortWorkflow(reason = 'user aborted', baseDir = process.cwd()) {
457
+ return updateState({
458
+ workflow: {
459
+ status: 'aborted',
460
+ completedAt: new Date().toISOString()
461
+ },
462
+ checkpoints: {
463
+ canResume: false,
464
+ resumeFrom: null,
465
+ resumeContext: { abortReason: reason }
466
+ }
467
+ }, baseDir);
468
+ }
469
+
470
+ /**
471
+ * Delete workflow state (cleanup)
472
+ * @param {string} [baseDir=process.cwd()] - Base directory
473
+ * @returns {boolean} Success status
474
+ */
475
+ function deleteState(baseDir = process.cwd()) {
476
+ const statePath = getStatePath(baseDir);
477
+
478
+ try {
479
+ if (fs.existsSync(statePath)) {
480
+ fs.unlinkSync(statePath);
481
+ }
482
+ return true;
483
+ } catch (error) {
484
+ console.error(`Error deleting state: ${error.message}`);
485
+ return false;
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Check if a workflow is in progress
491
+ * @param {string} [baseDir=process.cwd()] - Base directory
492
+ * @returns {boolean} True if workflow is active
493
+ */
494
+ function hasActiveWorkflow(baseDir = process.cwd()) {
495
+ const state = readState(baseDir);
496
+ if (state instanceof Error) return false;
497
+ if (!state) return false;
498
+
499
+ return ['pending', 'in_progress', 'paused'].includes(state.workflow.status);
500
+ }
501
+
502
+ /**
503
+ * Get workflow summary for display
504
+ * @param {string} [baseDir=process.cwd()] - Base directory
505
+ * @returns {Object|null} Summary object or null
506
+ */
507
+ function getWorkflowSummary(baseDir = process.cwd()) {
508
+ const state = readState(baseDir);
509
+ if (state instanceof Error) {
510
+ return { error: state.message, code: state.code };
511
+ }
512
+ if (!state) return null;
513
+
514
+ const completedPhases = state.phases.history?.filter(p => p.status === 'completed').length || 0;
515
+ const totalPhases = PHASES.length - 1; // Exclude 'complete'
516
+
517
+ return {
518
+ id: state.workflow.id,
519
+ type: state.workflow.type,
520
+ status: state.workflow.status,
521
+ currentPhase: state.phases.current,
522
+ progress: `${completedPhases}/${totalPhases}`,
523
+ progressPercent: Math.round((completedPhases / totalPhases) * 100),
524
+ task: state.task ? {
525
+ id: state.task.id,
526
+ title: state.task.title,
527
+ source: state.task.source
528
+ } : null,
529
+ pr: state.pr ? {
530
+ number: state.pr.number,
531
+ url: state.pr.url,
532
+ ciStatus: state.pr.ciStatus
533
+ } : null,
534
+ canResume: state.checkpoints.canResume,
535
+ resumeFrom: state.checkpoints.resumeFrom,
536
+ startedAt: state.workflow.startedAt,
537
+ duration: state.metrics?.totalDuration || 0
538
+ };
539
+ }
540
+
541
+ /**
542
+ * Update agent results
543
+ * @param {string} agentName - Agent identifier
544
+ * @param {Object} result - Agent result
545
+ * @param {string} [baseDir=process.cwd()] - Base directory
546
+ * @returns {Object|null} Updated state or null on error
547
+ */
548
+ function updateAgentResult(agentName, result, baseDir = process.cwd()) {
549
+ const state = readState(baseDir);
550
+ if (state instanceof Error) {
551
+ console.error(`Cannot update agent result: ${state.message}`);
552
+ return null;
553
+ }
554
+ if (!state) return null;
555
+
556
+ const agents = state.agents || {
557
+ lastRun: {},
558
+ totalIterations: 0,
559
+ totalIssuesFound: 0,
560
+ totalIssuesFixed: 0
561
+ };
562
+
563
+ agents.lastRun[agentName] = result;
564
+ agents.totalIssuesFound += result.issues || 0;
565
+
566
+ return updateState({ agents }, baseDir);
567
+ }
568
+
569
+ /**
570
+ * Increment review iteration
571
+ * @param {Object} [result={}] - Iteration result
572
+ * @param {string} [baseDir=process.cwd()] - Base directory
573
+ * @returns {Object|null} Updated state or null on error
574
+ */
575
+ function incrementIteration(result = {}, baseDir = process.cwd()) {
576
+ const state = readState(baseDir);
577
+ if (state instanceof Error) {
578
+ console.error(`Cannot increment iteration: ${state.message}`);
579
+ return null;
580
+ }
581
+ if (!state) return null;
582
+
583
+ const agents = state.agents || {
584
+ lastRun: {},
585
+ totalIterations: 0,
586
+ totalIssuesFound: 0,
587
+ totalIssuesFixed: 0
588
+ };
589
+
590
+ agents.totalIterations += 1;
591
+ agents.totalIssuesFixed += result.fixed || 0;
592
+
593
+ return updateState({
594
+ phases: {
595
+ currentIteration: state.phases.currentIteration + 1
596
+ },
597
+ agents
598
+ }, baseDir);
599
+ }
600
+
601
+ // Export all functions
602
+ module.exports = {
603
+ // Constants
604
+ SCHEMA_VERSION,
605
+ PHASES,
606
+ DEFAULT_POLICY,
607
+
608
+ // Core functions
609
+ generateWorkflowId,
610
+ getStatePath,
611
+ ensureStateDir,
612
+
613
+ // CRUD operations
614
+ createState,
615
+ readState,
616
+ writeState,
617
+ updateState,
618
+ deleteState,
619
+
620
+ // Phase management
621
+ startPhase,
622
+ completePhase,
623
+ failPhase,
624
+ skipToPhase,
625
+
626
+ // Workflow lifecycle
627
+ completeWorkflow,
628
+ abortWorkflow,
629
+ hasActiveWorkflow,
630
+ getWorkflowSummary,
631
+
632
+ // Agent management
633
+ updateAgentResult,
634
+ incrementIteration
635
+ };