agileflow 2.90.7 → 2.92.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 (144) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/lib/README.md +178 -0
  4. package/lib/codebase-indexer.js +818 -0
  5. package/lib/colors.js +190 -12
  6. package/lib/consent.js +232 -0
  7. package/lib/correlation.js +277 -0
  8. package/lib/error-codes.js +46 -0
  9. package/lib/errors.js +48 -6
  10. package/lib/file-cache.js +182 -0
  11. package/lib/format-error.js +156 -0
  12. package/lib/path-resolver.js +155 -7
  13. package/lib/paths.js +212 -20
  14. package/lib/placeholder-registry.js +205 -0
  15. package/lib/registry-di.js +358 -0
  16. package/lib/result-schema.js +363 -0
  17. package/lib/result.js +210 -0
  18. package/lib/session-registry.js +13 -0
  19. package/lib/session-state-machine.js +465 -0
  20. package/lib/validate-commands.js +308 -0
  21. package/lib/validate-names.js +3 -3
  22. package/lib/validate.js +116 -52
  23. package/package.json +4 -1
  24. package/scripts/af +34 -0
  25. package/scripts/agent-loop.js +63 -9
  26. package/scripts/agileflow-configure.js +2 -2
  27. package/scripts/agileflow-welcome.js +435 -23
  28. package/scripts/archive-completed-stories.sh +57 -11
  29. package/scripts/claude-tmux.sh +102 -0
  30. package/scripts/damage-control-bash.js +3 -70
  31. package/scripts/damage-control-edit.js +3 -20
  32. package/scripts/damage-control-write.js +3 -20
  33. package/scripts/dependency-check.js +310 -0
  34. package/scripts/get-env.js +11 -4
  35. package/scripts/lib/configure-detect.js +23 -1
  36. package/scripts/lib/configure-features.js +43 -2
  37. package/scripts/lib/context-formatter.js +771 -0
  38. package/scripts/lib/context-loader.js +699 -0
  39. package/scripts/lib/damage-control-utils.js +107 -0
  40. package/scripts/lib/json-utils.sh +162 -0
  41. package/scripts/lib/state-migrator.js +353 -0
  42. package/scripts/lib/story-state-machine.js +437 -0
  43. package/scripts/obtain-context.js +118 -1048
  44. package/scripts/pre-push-check.sh +46 -0
  45. package/scripts/precompact-context.sh +36 -11
  46. package/scripts/query-codebase.js +538 -0
  47. package/scripts/ralph-loop.js +5 -5
  48. package/scripts/session-manager.js +220 -42
  49. package/scripts/spawn-parallel.js +651 -0
  50. package/scripts/tui/blessed/data/watcher.js +180 -0
  51. package/scripts/tui/blessed/index.js +244 -0
  52. package/scripts/tui/blessed/panels/output.js +101 -0
  53. package/scripts/tui/blessed/panels/sessions.js +150 -0
  54. package/scripts/tui/blessed/panels/trace.js +97 -0
  55. package/scripts/tui/blessed/ui/help.js +77 -0
  56. package/scripts/tui/blessed/ui/screen.js +52 -0
  57. package/scripts/tui/blessed/ui/statusbar.js +47 -0
  58. package/scripts/tui/blessed/ui/tabbar.js +99 -0
  59. package/scripts/tui/index.js +38 -30
  60. package/scripts/validators/README.md +143 -0
  61. package/scripts/validators/component-validator.js +239 -0
  62. package/scripts/validators/json-schema-validator.js +186 -0
  63. package/scripts/validators/markdown-validator.js +152 -0
  64. package/scripts/validators/migration-validator.js +129 -0
  65. package/scripts/validators/security-validator.js +380 -0
  66. package/scripts/validators/story-format-validator.js +197 -0
  67. package/scripts/validators/test-result-validator.js +114 -0
  68. package/scripts/validators/workflow-validator.js +247 -0
  69. package/src/core/agents/accessibility.md +6 -0
  70. package/src/core/agents/adr-writer.md +6 -0
  71. package/src/core/agents/analytics.md +6 -0
  72. package/src/core/agents/api.md +6 -0
  73. package/src/core/agents/ci.md +6 -0
  74. package/src/core/agents/codebase-query.md +261 -0
  75. package/src/core/agents/compliance.md +6 -0
  76. package/src/core/agents/configuration-damage-control.md +6 -0
  77. package/src/core/agents/configuration-visual-e2e.md +6 -0
  78. package/src/core/agents/database.md +10 -0
  79. package/src/core/agents/datamigration.md +6 -0
  80. package/src/core/agents/design.md +6 -0
  81. package/src/core/agents/devops.md +6 -0
  82. package/src/core/agents/documentation.md +6 -0
  83. package/src/core/agents/epic-planner.md +6 -0
  84. package/src/core/agents/integrations.md +6 -0
  85. package/src/core/agents/mentor.md +6 -0
  86. package/src/core/agents/mobile.md +6 -0
  87. package/src/core/agents/monitoring.md +6 -0
  88. package/src/core/agents/multi-expert.md +6 -0
  89. package/src/core/agents/performance.md +6 -0
  90. package/src/core/agents/product.md +6 -0
  91. package/src/core/agents/qa.md +6 -0
  92. package/src/core/agents/readme-updater.md +6 -0
  93. package/src/core/agents/refactor.md +6 -0
  94. package/src/core/agents/research.md +6 -0
  95. package/src/core/agents/security.md +6 -0
  96. package/src/core/agents/testing.md +10 -0
  97. package/src/core/agents/ui.md +6 -0
  98. package/src/core/commands/adr.md +114 -0
  99. package/src/core/commands/agent.md +120 -0
  100. package/src/core/commands/assign.md +145 -0
  101. package/src/core/commands/audit.md +401 -0
  102. package/src/core/commands/babysit.md +32 -5
  103. package/src/core/commands/board.md +1 -0
  104. package/src/core/commands/changelog.md +118 -0
  105. package/src/core/commands/configure.md +42 -6
  106. package/src/core/commands/diagnose.md +114 -0
  107. package/src/core/commands/epic.md +205 -1
  108. package/src/core/commands/handoff.md +128 -0
  109. package/src/core/commands/help.md +76 -0
  110. package/src/core/commands/metrics.md +1 -0
  111. package/src/core/commands/pr.md +96 -0
  112. package/src/core/commands/research/analyze.md +1 -0
  113. package/src/core/commands/research/ask.md +2 -0
  114. package/src/core/commands/research/import.md +1 -0
  115. package/src/core/commands/research/list.md +2 -0
  116. package/src/core/commands/research/synthesize.md +584 -0
  117. package/src/core/commands/research/view.md +2 -0
  118. package/src/core/commands/roadmap/analyze.md +400 -0
  119. package/src/core/commands/session/new.md +113 -6
  120. package/src/core/commands/session/spawn.md +197 -0
  121. package/src/core/commands/sprint.md +22 -0
  122. package/src/core/commands/status.md +200 -1
  123. package/src/core/commands/story/list.md +9 -9
  124. package/src/core/commands/story/view.md +1 -0
  125. package/src/core/commands/story.md +143 -4
  126. package/src/core/experts/codebase-query/expertise.yaml +190 -0
  127. package/src/core/experts/codebase-query/question.md +73 -0
  128. package/src/core/experts/codebase-query/self-improve.md +105 -0
  129. package/src/core/templates/agileflow-metadata.json +55 -2
  130. package/src/core/templates/plan-template.md +125 -0
  131. package/src/core/templates/story-lifecycle.md +213 -0
  132. package/src/core/templates/story-template.md +4 -0
  133. package/src/core/templates/tdd-test-template.js +241 -0
  134. package/tools/cli/commands/setup.js +86 -0
  135. package/tools/cli/installers/core/installer.js +94 -0
  136. package/tools/cli/installers/ide/_base-ide.js +20 -11
  137. package/tools/cli/installers/ide/codex.js +29 -47
  138. package/tools/cli/lib/config-manager.js +17 -2
  139. package/tools/cli/lib/content-transformer.js +271 -0
  140. package/tools/cli/lib/error-handler.js +14 -22
  141. package/tools/cli/lib/ide-error-factory.js +421 -0
  142. package/tools/cli/lib/ide-health-monitor.js +364 -0
  143. package/tools/cli/lib/ide-registry.js +114 -1
  144. package/tools/cli/lib/ui.js +14 -25
@@ -0,0 +1,465 @@
1
+ /**
2
+ * Session State Machine
3
+ *
4
+ * Provides type-safe state transitions for AgileFlow sessions.
5
+ * Enforces valid state changes and emits events on transitions.
6
+ *
7
+ * States:
8
+ * - idle: Session created but not started
9
+ * - active: Session is running
10
+ * - paused: Session temporarily suspended
11
+ * - terminated: Session has ended
12
+ *
13
+ * Valid Transitions:
14
+ * - idle → active (start)
15
+ * - active → paused (pause)
16
+ * - active → terminated (stop)
17
+ * - paused → active (resume)
18
+ * - paused → terminated (stop)
19
+ *
20
+ * Usage:
21
+ * const { SessionStateMachine, SessionState } = require('./session-state-machine');
22
+ *
23
+ * const sm = new SessionStateMachine('idle');
24
+ * sm.on('transition', ({ from, to, action }) => {
25
+ * console.log(`Transition: ${from} -[${action}]-> ${to}`);
26
+ * });
27
+ *
28
+ * sm.canTransition('start'); // true
29
+ * sm.transition('start'); // State is now 'active'
30
+ */
31
+
32
+ 'use strict';
33
+
34
+ const EventEmitter = require('events');
35
+
36
+ /**
37
+ * Valid session states
38
+ * @enum {string}
39
+ */
40
+ const SessionState = Object.freeze({
41
+ IDLE: 'idle',
42
+ ACTIVE: 'active',
43
+ PAUSED: 'paused',
44
+ TERMINATED: 'terminated',
45
+ });
46
+
47
+ /**
48
+ * Valid state transition actions
49
+ * @enum {string}
50
+ */
51
+ const SessionAction = Object.freeze({
52
+ START: 'start',
53
+ PAUSE: 'pause',
54
+ RESUME: 'resume',
55
+ STOP: 'stop',
56
+ RESTART: 'restart',
57
+ });
58
+
59
+ /**
60
+ * State transition table
61
+ * Maps current state → action → next state
62
+ */
63
+ const TRANSITIONS = Object.freeze({
64
+ [SessionState.IDLE]: {
65
+ [SessionAction.START]: SessionState.ACTIVE,
66
+ },
67
+ [SessionState.ACTIVE]: {
68
+ [SessionAction.PAUSE]: SessionState.PAUSED,
69
+ [SessionAction.STOP]: SessionState.TERMINATED,
70
+ [SessionAction.RESTART]: SessionState.ACTIVE, // Self-transition for restart
71
+ },
72
+ [SessionState.PAUSED]: {
73
+ [SessionAction.RESUME]: SessionState.ACTIVE,
74
+ [SessionAction.STOP]: SessionState.TERMINATED,
75
+ },
76
+ [SessionState.TERMINATED]: {
77
+ // No transitions out of terminated (final state)
78
+ },
79
+ });
80
+
81
+ /**
82
+ * Get all valid states
83
+ * @returns {string[]}
84
+ */
85
+ function getValidStates() {
86
+ return Object.values(SessionState);
87
+ }
88
+
89
+ /**
90
+ * Get all valid actions
91
+ * @returns {string[]}
92
+ */
93
+ function getValidActions() {
94
+ return Object.values(SessionAction);
95
+ }
96
+
97
+ /**
98
+ * Check if a state is valid
99
+ * @param {string} state
100
+ * @returns {boolean}
101
+ */
102
+ function isValidState(state) {
103
+ return getValidStates().includes(state);
104
+ }
105
+
106
+ /**
107
+ * Check if an action is valid
108
+ * @param {string} action
109
+ * @returns {boolean}
110
+ */
111
+ function isValidAction(action) {
112
+ return getValidActions().includes(action);
113
+ }
114
+
115
+ /**
116
+ * Get valid transitions from a state
117
+ * @param {string} state - Current state
118
+ * @returns {Object} Map of action → next state
119
+ */
120
+ function getTransitionsFromState(state) {
121
+ return TRANSITIONS[state] || {};
122
+ }
123
+
124
+ /**
125
+ * Get available actions from a state
126
+ * @param {string} state - Current state
127
+ * @returns {string[]} Available actions
128
+ */
129
+ function getAvailableActions(state) {
130
+ return Object.keys(getTransitionsFromState(state));
131
+ }
132
+
133
+ /**
134
+ * Check if a transition is valid
135
+ * @param {string} currentState - Current state
136
+ * @param {string} action - Action to perform
137
+ * @returns {boolean}
138
+ */
139
+ function canTransition(currentState, action) {
140
+ const transitions = TRANSITIONS[currentState];
141
+ return !!(transitions && transitions[action]);
142
+ }
143
+
144
+ /**
145
+ * Get the next state for a transition
146
+ * @param {string} currentState - Current state
147
+ * @param {string} action - Action to perform
148
+ * @returns {string|null} Next state or null if invalid
149
+ */
150
+ function getNextState(currentState, action) {
151
+ const transitions = TRANSITIONS[currentState];
152
+ return (transitions && transitions[action]) || null;
153
+ }
154
+
155
+ /**
156
+ * Session state machine error
157
+ */
158
+ class StateMachineError extends Error {
159
+ constructor(message, details = {}) {
160
+ super(message);
161
+ this.name = 'StateMachineError';
162
+ this.currentState = details.currentState;
163
+ this.action = details.action;
164
+ this.code = details.code || 'INVALID_TRANSITION';
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Session State Machine
170
+ * @extends EventEmitter
171
+ *
172
+ * Events:
173
+ * - 'transition': { from, to, action, timestamp }
174
+ * - 'invalidTransition': { currentState, action, error }
175
+ * - 'stateChange': { state }
176
+ */
177
+ class SessionStateMachine extends EventEmitter {
178
+ /**
179
+ * @param {string} [initialState='idle'] - Initial state
180
+ * @param {Object} [options={}] - Options
181
+ * @param {boolean} [options.strict=true] - Throw on invalid transitions
182
+ * @param {boolean} [options.historyEnabled=false] - Track transition history
183
+ */
184
+ constructor(initialState = SessionState.IDLE, options = {}) {
185
+ super();
186
+
187
+ // Validate initial state
188
+ if (!isValidState(initialState)) {
189
+ throw new StateMachineError(`Invalid initial state: ${initialState}`, {
190
+ code: 'INVALID_STATE',
191
+ });
192
+ }
193
+
194
+ this._state = initialState;
195
+ this._strict = options.strict !== false;
196
+ this._historyEnabled = options.historyEnabled || false;
197
+ this._history = this._historyEnabled ? [{ state: initialState, timestamp: new Date() }] : [];
198
+ this._metadata = {};
199
+ }
200
+
201
+ /**
202
+ * Get current state
203
+ * @returns {string}
204
+ */
205
+ get state() {
206
+ return this._state;
207
+ }
208
+
209
+ /**
210
+ * Get transition history (if enabled)
211
+ * @returns {Array<{state: string, action?: string, timestamp: Date}>}
212
+ */
213
+ get history() {
214
+ return [...this._history];
215
+ }
216
+
217
+ /**
218
+ * Get metadata
219
+ * @returns {Object}
220
+ */
221
+ get metadata() {
222
+ return { ...this._metadata };
223
+ }
224
+
225
+ /**
226
+ * Set metadata
227
+ * @param {Object} meta - Metadata to merge
228
+ */
229
+ setMetadata(meta) {
230
+ this._metadata = { ...this._metadata, ...meta };
231
+ }
232
+
233
+ /**
234
+ * Check if state machine is in a final state
235
+ * @returns {boolean}
236
+ */
237
+ isFinal() {
238
+ return this._state === SessionState.TERMINATED;
239
+ }
240
+
241
+ /**
242
+ * Check if a transition can be performed
243
+ * @param {string} action - Action to check
244
+ * @returns {boolean}
245
+ */
246
+ canTransition(action) {
247
+ return canTransition(this._state, action);
248
+ }
249
+
250
+ /**
251
+ * Get available actions from current state
252
+ * @returns {string[]}
253
+ */
254
+ getAvailableActions() {
255
+ return getAvailableActions(this._state);
256
+ }
257
+
258
+ /**
259
+ * Perform a transition
260
+ * @param {string} action - Action to perform
261
+ * @returns {{ok: boolean, from?: string, to?: string, error?: Error}}
262
+ */
263
+ transition(action) {
264
+ const from = this._state;
265
+
266
+ // Check if action is valid
267
+ if (!isValidAction(action)) {
268
+ const error = new StateMachineError(`Invalid action: ${action}`, {
269
+ currentState: from,
270
+ action,
271
+ code: 'INVALID_ACTION',
272
+ });
273
+
274
+ this.emit('invalidTransition', { currentState: from, action, error });
275
+
276
+ if (this._strict) {
277
+ throw error;
278
+ }
279
+ return { ok: false, error };
280
+ }
281
+
282
+ // Check if transition is valid
283
+ const to = getNextState(from, action);
284
+
285
+ if (!to) {
286
+ const error = new StateMachineError(
287
+ `Cannot perform '${action}' from state '${from}'. Available actions: ${this.getAvailableActions().join(', ') || 'none'}`,
288
+ {
289
+ currentState: from,
290
+ action,
291
+ code: 'INVALID_TRANSITION',
292
+ }
293
+ );
294
+
295
+ this.emit('invalidTransition', { currentState: from, action, error });
296
+
297
+ if (this._strict) {
298
+ throw error;
299
+ }
300
+ return { ok: false, error };
301
+ }
302
+
303
+ // Perform transition
304
+ this._state = to;
305
+
306
+ const timestamp = new Date();
307
+
308
+ if (this._historyEnabled) {
309
+ this._history.push({ state: to, action, from, timestamp });
310
+ }
311
+
312
+ // Emit events
313
+ this.emit('transition', { from, to, action, timestamp });
314
+ this.emit('stateChange', { state: to });
315
+
316
+ return { ok: true, from, to };
317
+ }
318
+
319
+ /**
320
+ * Attempt transition without throwing on failure
321
+ * @param {string} action - Action to perform
322
+ * @returns {boolean} True if transition succeeded
323
+ */
324
+ tryTransition(action) {
325
+ try {
326
+ const result = this.transition(action);
327
+ return result.ok;
328
+ } catch {
329
+ return false;
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Reset to initial state
335
+ * @param {string} [state='idle'] - State to reset to
336
+ */
337
+ reset(state = SessionState.IDLE) {
338
+ if (!isValidState(state)) {
339
+ throw new StateMachineError(`Invalid state: ${state}`, {
340
+ code: 'INVALID_STATE',
341
+ });
342
+ }
343
+
344
+ const from = this._state;
345
+ this._state = state;
346
+
347
+ if (this._historyEnabled) {
348
+ this._history.push({ state, action: 'reset', from, timestamp: new Date() });
349
+ }
350
+
351
+ this.emit('transition', { from, to: state, action: 'reset', timestamp: new Date() });
352
+ this.emit('stateChange', { state });
353
+ }
354
+
355
+ /**
356
+ * Check if state matches
357
+ * @param {string} state - State to check
358
+ * @returns {boolean}
359
+ */
360
+ is(state) {
361
+ return this._state === state;
362
+ }
363
+
364
+ /**
365
+ * Check if state is one of given states
366
+ * @param {...string} states - States to check
367
+ * @returns {boolean}
368
+ */
369
+ isOneOf(...states) {
370
+ return states.includes(this._state);
371
+ }
372
+
373
+ /**
374
+ * Serialize state machine
375
+ * @returns {Object}
376
+ */
377
+ serialize() {
378
+ return {
379
+ state: this._state,
380
+ history: this._historyEnabled ? this._history : undefined,
381
+ metadata: Object.keys(this._metadata).length > 0 ? this._metadata : undefined,
382
+ };
383
+ }
384
+
385
+ /**
386
+ * Deserialize state machine
387
+ * @param {Object} data - Serialized data
388
+ * @param {Object} [options={}] - Options
389
+ * @returns {SessionStateMachine}
390
+ */
391
+ static deserialize(data, options = {}) {
392
+ const sm = new SessionStateMachine(data.state, {
393
+ ...options,
394
+ historyEnabled: !!data.history,
395
+ });
396
+
397
+ if (data.history) {
398
+ sm._history = data.history.map(h => ({
399
+ ...h,
400
+ timestamp: new Date(h.timestamp),
401
+ }));
402
+ }
403
+
404
+ if (data.metadata) {
405
+ sm._metadata = data.metadata;
406
+ }
407
+
408
+ return sm;
409
+ }
410
+
411
+ /**
412
+ * Create state machine diagram (Mermaid format)
413
+ * @returns {string}
414
+ */
415
+ static toMermaid() {
416
+ const lines = ['stateDiagram-v2'];
417
+
418
+ for (const [state, transitions] of Object.entries(TRANSITIONS)) {
419
+ for (const [action, nextState] of Object.entries(transitions)) {
420
+ lines.push(` ${state} --> ${nextState}: ${action}`);
421
+ }
422
+ }
423
+
424
+ // Mark initial and final states
425
+ lines.push(' [*] --> idle');
426
+ lines.push(' terminated --> [*]');
427
+
428
+ return lines.join('\n');
429
+ }
430
+ }
431
+
432
+ /**
433
+ * Create a pre-configured state machine for sessions
434
+ * @param {Object} [options={}] - Options
435
+ * @returns {SessionStateMachine}
436
+ */
437
+ function createSessionStateMachine(options = {}) {
438
+ return new SessionStateMachine(SessionState.IDLE, options);
439
+ }
440
+
441
+ module.exports = {
442
+ // Enums
443
+ SessionState,
444
+ SessionAction,
445
+
446
+ // Constants
447
+ TRANSITIONS,
448
+
449
+ // Utility functions
450
+ getValidStates,
451
+ getValidActions,
452
+ isValidState,
453
+ isValidAction,
454
+ getTransitionsFromState,
455
+ getAvailableActions,
456
+ canTransition,
457
+ getNextState,
458
+
459
+ // Classes
460
+ StateMachineError,
461
+ SessionStateMachine,
462
+
463
+ // Factory
464
+ createSessionStateMachine,
465
+ };