agileflow 3.3.0 → 3.4.1
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.
- package/CHANGELOG.md +10 -0
- package/README.md +6 -6
- package/lib/skill-loader.js +0 -1
- package/package.json +1 -1
- package/scripts/agileflow-statusline.sh +81 -0
- package/scripts/agileflow-welcome.js +79 -0
- package/scripts/claude-tmux.sh +90 -23
- package/scripts/claude-watchdog.sh +225 -0
- package/scripts/generators/agent-registry.js +14 -1
- package/scripts/generators/inject-babysit.js +22 -9
- package/scripts/generators/inject-help.js +19 -9
- package/scripts/lib/ac-test-matcher.js +452 -0
- package/scripts/lib/audit-cleanup.js +250 -0
- package/scripts/lib/audit-registry.js +304 -0
- package/scripts/lib/configure-features.js +35 -0
- package/scripts/lib/feature-catalog.js +3 -3
- package/scripts/lib/gate-enforcer.js +295 -0
- package/scripts/lib/model-profiles.js +118 -0
- package/scripts/lib/quality-gates.js +163 -0
- package/scripts/lib/signal-detectors.js +44 -1
- package/scripts/lib/skill-catalog.js +557 -0
- package/scripts/lib/skill-recommender.js +311 -0
- package/scripts/lib/status-writer.js +255 -0
- package/scripts/lib/story-claiming.js +128 -45
- package/scripts/lib/task-sync.js +32 -38
- package/scripts/lib/tdd-phase-manager.js +455 -0
- package/scripts/lib/team-events.js +34 -3
- package/scripts/lib/tmux-audit-monitor.js +611 -0
- package/scripts/lib/tmux-group-colors.js +113 -0
- package/scripts/lib/tool-registry.yaml +241 -0
- package/scripts/lib/tool-shed.js +441 -0
- package/scripts/messaging-bridge.js +209 -1
- package/scripts/native-team-observer.js +219 -0
- package/scripts/obtain-context.js +14 -0
- package/scripts/ralph-loop.js +30 -5
- package/scripts/smart-detect.js +21 -0
- package/scripts/spawn-audit-sessions.js +877 -0
- package/scripts/team-manager.js +56 -16
- package/scripts/tmux-close-windows.sh +180 -0
- package/src/core/agents/a11y-analyzer-aria.md +155 -0
- package/src/core/agents/a11y-analyzer-forms.md +162 -0
- package/src/core/agents/a11y-analyzer-keyboard.md +175 -0
- package/src/core/agents/a11y-analyzer-semantic.md +153 -0
- package/src/core/agents/a11y-analyzer-visual.md +158 -0
- package/src/core/agents/a11y-consensus.md +248 -0
- package/src/core/agents/ads-audit-budget.md +181 -0
- package/src/core/agents/ads-audit-compliance.md +169 -0
- package/src/core/agents/ads-audit-creative.md +164 -0
- package/src/core/agents/ads-audit-google.md +226 -0
- package/src/core/agents/ads-audit-meta.md +183 -0
- package/src/core/agents/ads-audit-tracking.md +197 -0
- package/src/core/agents/ads-consensus.md +396 -0
- package/src/core/agents/ads-generate.md +145 -0
- package/src/core/agents/ads-performance-tracker.md +197 -0
- package/src/core/agents/api-quality-analyzer-conventions.md +148 -0
- package/src/core/agents/api-quality-analyzer-docs.md +176 -0
- package/src/core/agents/api-quality-analyzer-errors.md +183 -0
- package/src/core/agents/api-quality-analyzer-pagination.md +171 -0
- package/src/core/agents/api-quality-analyzer-versioning.md +143 -0
- package/src/core/agents/api-quality-consensus.md +214 -0
- package/src/core/agents/arch-analyzer-circular.md +148 -0
- package/src/core/agents/arch-analyzer-complexity.md +171 -0
- package/src/core/agents/arch-analyzer-coupling.md +146 -0
- package/src/core/agents/arch-analyzer-layering.md +151 -0
- package/src/core/agents/arch-analyzer-patterns.md +162 -0
- package/src/core/agents/arch-consensus.md +227 -0
- package/src/core/agents/brainstorm-analyzer-features.md +169 -0
- package/src/core/agents/brainstorm-analyzer-growth.md +161 -0
- package/src/core/agents/brainstorm-analyzer-integration.md +172 -0
- package/src/core/agents/brainstorm-analyzer-market.md +147 -0
- package/src/core/agents/brainstorm-analyzer-ux.md +167 -0
- package/src/core/agents/brainstorm-consensus.md +237 -0
- package/src/core/agents/completeness-consensus.md +5 -5
- package/src/core/agents/perf-consensus.md +2 -2
- package/src/core/agents/security-consensus.md +2 -2
- package/src/core/agents/seo-analyzer-content.md +167 -0
- package/src/core/agents/seo-analyzer-images.md +187 -0
- package/src/core/agents/seo-analyzer-performance.md +206 -0
- package/src/core/agents/seo-analyzer-schema.md +176 -0
- package/src/core/agents/seo-analyzer-sitemap.md +172 -0
- package/src/core/agents/seo-analyzer-technical.md +144 -0
- package/src/core/agents/seo-consensus.md +289 -0
- package/src/core/agents/test-consensus.md +2 -2
- package/src/core/commands/adr.md +1 -0
- package/src/core/commands/ads/audit.md +375 -0
- package/src/core/commands/ads/budget.md +97 -0
- package/src/core/commands/ads/competitor.md +112 -0
- package/src/core/commands/ads/creative.md +85 -0
- package/src/core/commands/ads/generate.md +238 -0
- package/src/core/commands/ads/google.md +112 -0
- package/src/core/commands/ads/health.md +327 -0
- package/src/core/commands/ads/landing.md +119 -0
- package/src/core/commands/ads/linkedin.md +112 -0
- package/src/core/commands/ads/meta.md +91 -0
- package/src/core/commands/ads/microsoft.md +115 -0
- package/src/core/commands/ads/plan.md +321 -0
- package/src/core/commands/ads/test-plan.md +317 -0
- package/src/core/commands/ads/tiktok.md +129 -0
- package/src/core/commands/ads/track.md +288 -0
- package/src/core/commands/ads/youtube.md +124 -0
- package/src/core/commands/ads.md +140 -0
- package/src/core/commands/assign.md +1 -0
- package/src/core/commands/audit.md +43 -6
- package/src/core/commands/babysit.md +315 -1266
- package/src/core/commands/baseline.md +1 -0
- package/src/core/commands/blockers.md +1 -0
- package/src/core/commands/board.md +1 -0
- package/src/core/commands/changelog.md +1 -0
- package/src/core/commands/choose.md +1 -0
- package/src/core/commands/ci.md +1 -0
- package/src/core/commands/code/accessibility.md +347 -0
- package/src/core/commands/code/api.md +297 -0
- package/src/core/commands/code/architecture.md +297 -0
- package/src/core/commands/{audit → code}/completeness.md +72 -25
- package/src/core/commands/{audit → code}/legal.md +63 -16
- package/src/core/commands/{audit → code}/logic.md +64 -16
- package/src/core/commands/{audit → code}/performance.md +67 -20
- package/src/core/commands/{audit → code}/security.md +69 -19
- package/src/core/commands/{audit → code}/test.md +67 -20
- package/src/core/commands/configure.md +1 -0
- package/src/core/commands/council.md +1 -0
- package/src/core/commands/deploy.md +1 -0
- package/src/core/commands/diagnose.md +1 -0
- package/src/core/commands/docs.md +1 -0
- package/src/core/commands/epic/edit.md +213 -0
- package/src/core/commands/epic.md +1 -0
- package/src/core/commands/export.md +238 -0
- package/src/core/commands/help.md +16 -1
- package/src/core/commands/{discovery → ideate}/brief.md +12 -12
- package/src/core/commands/{discovery/new.md → ideate/discover.md} +20 -16
- package/src/core/commands/ideate/features.md +496 -0
- package/src/core/commands/ideate/new.md +158 -124
- package/src/core/commands/impact.md +1 -0
- package/src/core/commands/learn/explain.md +118 -0
- package/src/core/commands/learn/glossary.md +135 -0
- package/src/core/commands/learn/patterns.md +138 -0
- package/src/core/commands/learn/tour.md +126 -0
- package/src/core/commands/migrate/codemods.md +151 -0
- package/src/core/commands/migrate/plan.md +131 -0
- package/src/core/commands/migrate/scan.md +114 -0
- package/src/core/commands/migrate/validate.md +119 -0
- package/src/core/commands/multi-expert.md +1 -0
- package/src/core/commands/pr.md +1 -0
- package/src/core/commands/review.md +1 -0
- package/src/core/commands/seo/audit.md +373 -0
- package/src/core/commands/seo/competitor.md +174 -0
- package/src/core/commands/seo/content.md +107 -0
- package/src/core/commands/seo/geo.md +229 -0
- package/src/core/commands/seo/hreflang.md +140 -0
- package/src/core/commands/seo/images.md +96 -0
- package/src/core/commands/seo/page.md +198 -0
- package/src/core/commands/seo/plan.md +163 -0
- package/src/core/commands/seo/programmatic.md +131 -0
- package/src/core/commands/seo/references/cwv-thresholds.md +64 -0
- package/src/core/commands/seo/references/eeat-framework.md +110 -0
- package/src/core/commands/seo/references/quality-gates.md +91 -0
- package/src/core/commands/seo/references/schema-types.md +102 -0
- package/src/core/commands/seo/schema.md +183 -0
- package/src/core/commands/seo/sitemap.md +97 -0
- package/src/core/commands/seo/technical.md +100 -0
- package/src/core/commands/seo.md +107 -0
- package/src/core/commands/skill/list.md +68 -212
- package/src/core/commands/skill/recommend.md +216 -0
- package/src/core/commands/sprint.md +1 -0
- package/src/core/commands/status/undo.md +191 -0
- package/src/core/commands/status.md +1 -0
- package/src/core/commands/story/edit.md +204 -0
- package/src/core/commands/story/view.md +29 -7
- package/src/core/commands/story-validate.md +1 -0
- package/src/core/commands/story.md +1 -0
- package/src/core/commands/tdd-next.md +238 -0
- package/src/core/commands/tdd.md +211 -0
- package/src/core/commands/team/start.md +10 -6
- package/src/core/commands/tests.md +1 -0
- package/src/core/commands/verify.md +27 -1
- package/src/core/commands/workflow.md +2 -0
- package/src/core/experts/_core-expertise.yaml +105 -0
- package/src/core/experts/analytics/expertise.yaml +5 -99
- package/src/core/experts/codebase-query/expertise.yaml +3 -72
- package/src/core/experts/compliance/expertise.yaml +6 -72
- package/src/core/experts/database/expertise.yaml +9 -52
- package/src/core/experts/documentation/expertise.yaml +7 -140
- package/src/core/experts/integrations/expertise.yaml +7 -127
- package/src/core/experts/mentor/expertise.yaml +8 -35
- package/src/core/experts/monitoring/expertise.yaml +7 -49
- package/src/core/experts/performance/expertise.yaml +1 -26
- package/src/core/experts/security/expertise.yaml +9 -34
- package/src/core/experts/ui/expertise.yaml +6 -36
- package/src/core/knowledge/ads/ad-audit-checklist-scoring.md +424 -0
- package/src/core/knowledge/ads/ad-optimization-logic.md +590 -0
- package/src/core/knowledge/ads/ad-technical-specifications.md +385 -0
- package/src/core/knowledge/ads/definitive-advertising-reference-2026.md +506 -0
- package/src/core/knowledge/ads/paid-advertising-research-2026.md +445 -0
- package/src/core/teams/backend.json +41 -0
- package/src/core/teams/frontend.json +41 -0
- package/src/core/teams/qa.json +41 -0
- package/src/core/teams/solo.json +35 -0
- package/src/core/templates/agileflow-metadata.json +20 -1
- package/tools/cli/commands/setup.js +85 -3
- package/tools/cli/commands/update.js +42 -0
- package/tools/cli/installers/ide/_base-ide.js +42 -5
- package/tools/cli/installers/ide/claude-code.js +71 -3
- package/tools/cli/lib/content-injector.js +160 -12
- package/tools/cli/lib/docs-setup.js +1 -1
- package/src/core/commands/skill/create.md +0 -698
- package/src/core/commands/skill/delete.md +0 -316
- package/src/core/commands/skill/edit.md +0 -359
- package/src/core/commands/skill/test.md +0 -394
- package/src/core/commands/skill/upgrade.md +0 -552
- package/src/core/templates/skill-template.md +0 -117
|
@@ -28,6 +28,19 @@ const { c } = require('../../lib/colors');
|
|
|
28
28
|
// Default claim expiration: 4 hours
|
|
29
29
|
const DEFAULT_CLAIM_TTL_HOURS = 4;
|
|
30
30
|
|
|
31
|
+
// Canonical status-writer (lazy-loaded)
|
|
32
|
+
let _statusWriter;
|
|
33
|
+
function getStatusWriter() {
|
|
34
|
+
if (_statusWriter === undefined) {
|
|
35
|
+
try {
|
|
36
|
+
_statusWriter = require('./status-writer');
|
|
37
|
+
} catch (e) {
|
|
38
|
+
_statusWriter = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return _statusWriter;
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
// Agent Teams integration (lazy-loaded)
|
|
32
45
|
let _featureFlags, _taskSync;
|
|
33
46
|
function getFeatureFlags() {
|
|
@@ -209,9 +222,57 @@ function isStoryClaimed(story, currentSessionId) {
|
|
|
209
222
|
function claimStory(storyId, options = {}) {
|
|
210
223
|
const { force = false, rootDir } = options;
|
|
211
224
|
const root = rootDir || getProjectRoot();
|
|
212
|
-
const statusPath = getStatusPath(root);
|
|
213
225
|
|
|
214
|
-
//
|
|
226
|
+
// Pre-check: read story and validate claim eligibility
|
|
227
|
+
const statusWriter = getStatusWriter();
|
|
228
|
+
if (statusWriter) {
|
|
229
|
+
const readResult = statusWriter.readStory(root, storyId);
|
|
230
|
+
if (!readResult.ok) {
|
|
231
|
+
return { ok: false, error: readResult.error };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check if already claimed by someone else
|
|
235
|
+
const claimCheck = isStoryClaimed(readResult.story);
|
|
236
|
+
if (claimCheck.claimed && !force) {
|
|
237
|
+
return {
|
|
238
|
+
ok: false,
|
|
239
|
+
claimed: true,
|
|
240
|
+
claimedBy: claimCheck.claimedBy,
|
|
241
|
+
error: `Story ${storyId} is claimed by session ${claimCheck.claimedBy.session_id}`,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Get current session info
|
|
246
|
+
const currentSession = getCurrentSession(root);
|
|
247
|
+
if (!currentSession) {
|
|
248
|
+
return { ok: false, error: 'Could not determine current session' };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Build claim object
|
|
252
|
+
const claimObj = {
|
|
253
|
+
session_id: currentSession.session_id,
|
|
254
|
+
pid: currentSession.pid,
|
|
255
|
+
path: currentSession.path,
|
|
256
|
+
claimed_at: new Date().toISOString(),
|
|
257
|
+
claim_type: isTeamSessionActive(root) ? 'team' : 'worktree',
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// Write via status-writer (atomic, validated)
|
|
261
|
+
const writeResult = statusWriter.updateStory(
|
|
262
|
+
root,
|
|
263
|
+
storyId,
|
|
264
|
+
{ claimed_by: claimObj },
|
|
265
|
+
{ skipValidation: true }
|
|
266
|
+
);
|
|
267
|
+
if (!writeResult.ok) {
|
|
268
|
+
return { ok: false, error: writeResult.error };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return { ok: true, claimed: true };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Fallback: direct safeWriteJSON if status-writer unavailable
|
|
275
|
+
const statusPath = getStatusPath(root);
|
|
215
276
|
const result = safeReadJSON(statusPath, { defaultValue: null });
|
|
216
277
|
if (!result.ok || !result.data) {
|
|
217
278
|
return { ok: false, error: result.error || 'Could not load status.json' };
|
|
@@ -224,7 +285,6 @@ function claimStory(storyId, options = {}) {
|
|
|
224
285
|
return { ok: false, error: `Story ${storyId} not found` };
|
|
225
286
|
}
|
|
226
287
|
|
|
227
|
-
// Check if already claimed by someone else
|
|
228
288
|
const claimCheck = isStoryClaimed(story);
|
|
229
289
|
if (claimCheck.claimed && !force) {
|
|
230
290
|
return {
|
|
@@ -235,13 +295,11 @@ function claimStory(storyId, options = {}) {
|
|
|
235
295
|
};
|
|
236
296
|
}
|
|
237
297
|
|
|
238
|
-
// Get current session info
|
|
239
298
|
const currentSession = getCurrentSession(root);
|
|
240
299
|
if (!currentSession) {
|
|
241
300
|
return { ok: false, error: 'Could not determine current session' };
|
|
242
301
|
}
|
|
243
302
|
|
|
244
|
-
// Set the claim
|
|
245
303
|
story.claimed_by = {
|
|
246
304
|
session_id: currentSession.session_id,
|
|
247
305
|
pid: currentSession.pid,
|
|
@@ -250,23 +308,12 @@ function claimStory(storyId, options = {}) {
|
|
|
250
308
|
claim_type: isTeamSessionActive(root) ? 'team' : 'worktree',
|
|
251
309
|
};
|
|
252
310
|
|
|
253
|
-
// Save status.json
|
|
254
311
|
status.updated = new Date().toISOString();
|
|
255
312
|
const writeResult = safeWriteJSON(statusPath, status);
|
|
256
313
|
if (!writeResult.ok) {
|
|
257
314
|
return { ok: false, error: writeResult.error };
|
|
258
315
|
}
|
|
259
316
|
|
|
260
|
-
// Sync claim to native task list when team session is active
|
|
261
|
-
if (isTeamSessionActive(root)) {
|
|
262
|
-
const taskSync = getTaskSync();
|
|
263
|
-
if (taskSync) {
|
|
264
|
-
taskSync.syncToStatus(root, storyId, {
|
|
265
|
-
claimed_by: story.claimed_by,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
317
|
return { ok: true, claimed: true };
|
|
271
318
|
}
|
|
272
319
|
|
|
@@ -281,9 +328,41 @@ function claimStory(storyId, options = {}) {
|
|
|
281
328
|
function releaseStory(storyId, options = {}) {
|
|
282
329
|
const { rootDir } = options;
|
|
283
330
|
const root = rootDir || getProjectRoot();
|
|
284
|
-
const statusPath = getStatusPath(root);
|
|
285
331
|
|
|
286
|
-
|
|
332
|
+
const statusWriter = getStatusWriter();
|
|
333
|
+
if (statusWriter) {
|
|
334
|
+
// Pre-check: verify ownership
|
|
335
|
+
const readResult = statusWriter.readStory(root, storyId);
|
|
336
|
+
if (!readResult.ok) {
|
|
337
|
+
return { ok: false, error: readResult.error };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const currentSession = getCurrentSession(root);
|
|
341
|
+
const mySessionId = currentSession ? currentSession.session_id : null;
|
|
342
|
+
|
|
343
|
+
if (readResult.story.claimed_by && readResult.story.claimed_by.session_id !== mySessionId) {
|
|
344
|
+
return {
|
|
345
|
+
ok: false,
|
|
346
|
+
error: `Story ${storyId} is claimed by session ${readResult.story.claimed_by.session_id}, not you`,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Remove claim via status-writer (null deletes the field)
|
|
351
|
+
const writeResult = statusWriter.updateStory(
|
|
352
|
+
root,
|
|
353
|
+
storyId,
|
|
354
|
+
{ claimed_by: null },
|
|
355
|
+
{ skipValidation: true }
|
|
356
|
+
);
|
|
357
|
+
if (!writeResult.ok) {
|
|
358
|
+
return { ok: false, error: writeResult.error };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return { ok: true, released: true };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Fallback: direct safeWriteJSON if status-writer unavailable
|
|
365
|
+
const statusPath = getStatusPath(root);
|
|
287
366
|
const result = safeReadJSON(statusPath, { defaultValue: null });
|
|
288
367
|
if (!result.ok || !result.data) {
|
|
289
368
|
return { ok: false, error: result.error || 'Could not load status.json' };
|
|
@@ -296,11 +375,9 @@ function releaseStory(storyId, options = {}) {
|
|
|
296
375
|
return { ok: false, error: `Story ${storyId} not found` };
|
|
297
376
|
}
|
|
298
377
|
|
|
299
|
-
// Get current session info
|
|
300
378
|
const currentSession = getCurrentSession(root);
|
|
301
379
|
const mySessionId = currentSession ? currentSession.session_id : null;
|
|
302
380
|
|
|
303
|
-
// Check if we own this claim
|
|
304
381
|
if (story.claimed_by && story.claimed_by.session_id !== mySessionId) {
|
|
305
382
|
return {
|
|
306
383
|
ok: false,
|
|
@@ -308,26 +385,14 @@ function releaseStory(storyId, options = {}) {
|
|
|
308
385
|
};
|
|
309
386
|
}
|
|
310
387
|
|
|
311
|
-
// Remove the claim
|
|
312
388
|
delete story.claimed_by;
|
|
313
389
|
|
|
314
|
-
// Save status.json
|
|
315
390
|
status.updated = new Date().toISOString();
|
|
316
391
|
const writeResult = safeWriteJSON(statusPath, status);
|
|
317
392
|
if (!writeResult.ok) {
|
|
318
393
|
return { ok: false, error: writeResult.error };
|
|
319
394
|
}
|
|
320
395
|
|
|
321
|
-
// Sync release to native task list when team session is active
|
|
322
|
-
if (isTeamSessionActive(root)) {
|
|
323
|
-
const taskSync = getTaskSync();
|
|
324
|
-
if (taskSync) {
|
|
325
|
-
taskSync.syncToStatus(root, storyId, {
|
|
326
|
-
claimed_by: null,
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
396
|
return { ok: true, released: true };
|
|
332
397
|
}
|
|
333
398
|
|
|
@@ -432,35 +497,53 @@ function cleanupStaleClaims(options = {}) {
|
|
|
432
497
|
const root = rootDir || getProjectRoot();
|
|
433
498
|
const statusPath = getStatusPath(root);
|
|
434
499
|
|
|
435
|
-
// Load status.json
|
|
500
|
+
// Load status.json to find stale claims
|
|
436
501
|
const result = safeReadJSON(statusPath, { defaultValue: null });
|
|
437
502
|
if (!result.ok || !result.data) {
|
|
438
503
|
return { ok: false, error: result.error || 'Could not load status.json' };
|
|
439
504
|
}
|
|
440
505
|
|
|
441
506
|
const status = result.data;
|
|
442
|
-
|
|
507
|
+
const staleStoryIds = [];
|
|
443
508
|
|
|
444
509
|
for (const [id, story] of Object.entries(status.stories || {})) {
|
|
445
510
|
if (!story.claimed_by) continue;
|
|
446
|
-
|
|
447
|
-
// Check if claim is stale
|
|
448
511
|
if (!isClaimValid(story.claimed_by)) {
|
|
449
|
-
|
|
450
|
-
cleanedCount++;
|
|
512
|
+
staleStoryIds.push(id);
|
|
451
513
|
}
|
|
452
514
|
}
|
|
453
515
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
516
|
+
if (staleStoryIds.length === 0) {
|
|
517
|
+
return { ok: true, cleaned: 0 };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Use status-writer for each stale claim if available
|
|
521
|
+
const statusWriter = getStatusWriter();
|
|
522
|
+
if (statusWriter) {
|
|
523
|
+
let cleanedCount = 0;
|
|
524
|
+
for (const id of staleStoryIds) {
|
|
525
|
+
const writeResult = statusWriter.updateStory(
|
|
526
|
+
root,
|
|
527
|
+
id,
|
|
528
|
+
{ claimed_by: null },
|
|
529
|
+
{ skipValidation: true }
|
|
530
|
+
);
|
|
531
|
+
if (writeResult.ok) cleanedCount++;
|
|
460
532
|
}
|
|
533
|
+
return { ok: true, cleaned: cleanedCount };
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Fallback: bulk write via safeWriteJSON
|
|
537
|
+
for (const id of staleStoryIds) {
|
|
538
|
+
delete status.stories[id].claimed_by;
|
|
539
|
+
}
|
|
540
|
+
status.updated = new Date().toISOString();
|
|
541
|
+
const writeResult = safeWriteJSON(statusPath, status);
|
|
542
|
+
if (!writeResult.ok) {
|
|
543
|
+
return { ok: false, error: writeResult.error };
|
|
461
544
|
}
|
|
462
545
|
|
|
463
|
-
return { ok: true, cleaned:
|
|
546
|
+
return { ok: true, cleaned: staleStoryIds.length };
|
|
464
547
|
}
|
|
465
548
|
|
|
466
549
|
/**
|
package/scripts/lib/task-sync.js
CHANGED
|
@@ -28,6 +28,19 @@ function getPaths() {
|
|
|
28
28
|
return _paths;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Lazy-load status-writer for canonical writes
|
|
32
|
+
let _statusWriter;
|
|
33
|
+
function getStatusWriter() {
|
|
34
|
+
if (_statusWriter === undefined) {
|
|
35
|
+
try {
|
|
36
|
+
_statusWriter = require('./status-writer');
|
|
37
|
+
} catch (e) {
|
|
38
|
+
_statusWriter = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return _statusWriter;
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
/**
|
|
32
45
|
* Map AgileFlow story status to native task status.
|
|
33
46
|
*/
|
|
@@ -75,6 +88,7 @@ function readStatusStories(rootDir) {
|
|
|
75
88
|
|
|
76
89
|
/**
|
|
77
90
|
* Write status.json stories back.
|
|
91
|
+
* @deprecated Use status-writer.updateStory() for individual story mutations instead.
|
|
78
92
|
*/
|
|
79
93
|
function writeStatusStories(rootDir, stories) {
|
|
80
94
|
try {
|
|
@@ -105,34 +119,12 @@ function writeStatusStories(rootDir, stories) {
|
|
|
105
119
|
* @returns {{ ok: boolean }}
|
|
106
120
|
*/
|
|
107
121
|
function syncToStatus(rootDir, storyId, updates) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
? paths.getStatusPath(rootDir)
|
|
112
|
-
: path.join(rootDir, 'docs', '09-agents', 'status.json');
|
|
113
|
-
|
|
114
|
-
if (!fs.existsSync(statusPath)) {
|
|
115
|
-
return { ok: false, error: 'status.json not found' };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const data = JSON.parse(fs.readFileSync(statusPath, 'utf8'));
|
|
119
|
-
if (!data.stories) data.stories = {};
|
|
120
|
-
|
|
121
|
-
if (!data.stories[storyId]) {
|
|
122
|
-
return { ok: false, error: `Story ${storyId} not found` };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Apply updates
|
|
126
|
-
Object.assign(data.stories[storyId], updates);
|
|
127
|
-
|
|
128
|
-
// Update timestamp
|
|
129
|
-
data.stories[storyId].updated_at = new Date().toISOString();
|
|
130
|
-
|
|
131
|
-
fs.writeFileSync(statusPath, JSON.stringify(data, null, 2) + '\n');
|
|
132
|
-
return { ok: true };
|
|
133
|
-
} catch (e) {
|
|
134
|
-
return { ok: false, error: e.message };
|
|
122
|
+
const statusWriter = getStatusWriter();
|
|
123
|
+
if (statusWriter) {
|
|
124
|
+
return statusWriter.updateStory(rootDir, storyId, updates, { skipValidation: true });
|
|
135
125
|
}
|
|
126
|
+
|
|
127
|
+
return { ok: false, error: 'status-writer module not available' };
|
|
136
128
|
}
|
|
137
129
|
|
|
138
130
|
/**
|
|
@@ -184,6 +176,7 @@ function syncFromStatus(rootDir, filters = {}) {
|
|
|
184
176
|
*/
|
|
185
177
|
function reconcile(rootDir, nativeTasks) {
|
|
186
178
|
let updated = 0;
|
|
179
|
+
const statusWriter = getStatusWriter();
|
|
187
180
|
|
|
188
181
|
try {
|
|
189
182
|
const paths = getPaths();
|
|
@@ -195,6 +188,7 @@ function reconcile(rootDir, nativeTasks) {
|
|
|
195
188
|
return { ok: false, error: 'status.json not found', updated: 0 };
|
|
196
189
|
}
|
|
197
190
|
|
|
191
|
+
// Pre-read current data to check which stories actually changed
|
|
198
192
|
const data = JSON.parse(fs.readFileSync(statusPath, 'utf8'));
|
|
199
193
|
if (!data.stories) data.stories = {};
|
|
200
194
|
|
|
@@ -203,20 +197,20 @@ function reconcile(rootDir, nativeTasks) {
|
|
|
203
197
|
if (!data.stories[storyId]) continue;
|
|
204
198
|
|
|
205
199
|
const newStatus = taskStatusToStoryStatus(task.status);
|
|
206
|
-
if (data.stories[storyId].status
|
|
207
|
-
data.stories[storyId].status = newStatus;
|
|
208
|
-
data.stories[storyId].updated_at = new Date().toISOString();
|
|
200
|
+
if (data.stories[storyId].status === newStatus) continue;
|
|
209
201
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
updated++;
|
|
202
|
+
const storyUpdates = { status: newStatus };
|
|
203
|
+
if (newStatus === 'completed') {
|
|
204
|
+
storyUpdates.completed_at = new Date().toISOString();
|
|
215
205
|
}
|
|
216
|
-
}
|
|
217
206
|
|
|
218
|
-
|
|
219
|
-
|
|
207
|
+
if (statusWriter) {
|
|
208
|
+
const result = statusWriter.updateStory(rootDir, storyId, storyUpdates, {
|
|
209
|
+
skipValidation: true,
|
|
210
|
+
});
|
|
211
|
+
if (result.ok) updated++;
|
|
212
|
+
}
|
|
213
|
+
// If no status-writer, skip this story (no fallback direct write)
|
|
220
214
|
}
|
|
221
215
|
|
|
222
216
|
return { ok: true, updated };
|