refacil-sdd-ai 5.3.0 → 5.3.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/README.md +1 -0
- package/agents/proposer.md +10 -0
- package/lib/commands/sdd.js +173 -5
- package/lib/installer.js +1 -0
- package/lib/kapso.js +71 -4
- package/package.json +2 -2
- package/skills/apply/SKILL.md +11 -1
- package/skills/archive/SKILL.md +11 -3
- package/skills/autopilot/SKILL.md +48 -19
- package/skills/guide/SKILL.md +5 -1
- package/skills/propose/SKILL.md +8 -0
- package/skills/review/SKILL.md +8 -0
- package/skills/stats/SKILL.md +4 -1
- package/skills/status/SKILL.md +116 -0
- package/skills/test/SKILL.md +3 -1
- package/skills/verify/SKILL.md +8 -0
package/README.md
CHANGED
|
@@ -277,6 +277,7 @@ All invoked as `/refacil:<name>` in Claude Code, Cursor, OpenCode, or Codex.
|
|
|
277
277
|
| `/refacil:bug` | Full bugfix flow with regression tests |
|
|
278
278
|
| `/refacil:update` | Detect and apply pending methodology migrations to the current repo |
|
|
279
279
|
| `/refacil:stats` | Show change progress, task status, review gate, and test commands from SDD artifacts |
|
|
280
|
+
| `/refacil:status` | Show which phase of the SDD-AI cycle a change is in and the exact command to resume it |
|
|
280
281
|
| `/refacil:read-spec` | Listen to change specs in the browser with on-device TTS |
|
|
281
282
|
| `/refacil:autopilot` | Autonomous pipeline: chains apply → test → verify → review → archive in one invocation; up-code (push + PR) is optional and configured in pre-flight. Optional WhatsApp notification via `~/.refacil-sdd-ai/kapso.env` |
|
|
282
283
|
|
package/agents/proposer.md
CHANGED
|
@@ -196,6 +196,16 @@ If the change involves a contract with another system (external API, event, queu
|
|
|
196
196
|
|
|
197
197
|
If the input comes from a bus room agreement, still generate all artifacts in full according to the SDD-AI methodology. See `METHODOLOGY-CONTRACT.md` and `BUS-CROSS-REPO.md` (room agreements section).
|
|
198
198
|
|
|
199
|
+
### Step 2.5: Record proposed state
|
|
200
|
+
|
|
201
|
+
After writing all four artifacts, run:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
refacil-sdd-ai sdd set-memory <changeName> --state proposed --actor proposer
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
This records the initial state in `memory.yaml` for cross-skill tracking. If the command fails (e.g. the change directory does not exist yet at that point), skip silently — it must not block the flow.
|
|
208
|
+
|
|
199
209
|
### Step 3: Report + JSON block
|
|
200
210
|
|
|
201
211
|
Your final response MUST have this structure:
|
package/lib/commands/sdd.js
CHANGED
|
@@ -183,6 +183,88 @@ function serializeMemoryYaml(obj) {
|
|
|
183
183
|
return lines.join('\n') + '\n';
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
// --- State tracking ---
|
|
187
|
+
|
|
188
|
+
const VALID_STATES = [
|
|
189
|
+
'proposed',
|
|
190
|
+
'approved',
|
|
191
|
+
'apply-in-progress',
|
|
192
|
+
'applied',
|
|
193
|
+
'tested',
|
|
194
|
+
'verified',
|
|
195
|
+
'reviewed',
|
|
196
|
+
'archived',
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Infers the current state of a change from its artifacts when no explicit
|
|
201
|
+
* currentState is stored in memory. Retrocompatible — never writes to disk.
|
|
202
|
+
*
|
|
203
|
+
* Priority (descending):
|
|
204
|
+
* 1. .review-passed exists → 'reviewed'
|
|
205
|
+
* 2. lastStep === 'verify' or 'verified' → 'verified'
|
|
206
|
+
* 3. lastStep === 'test' → 'tested'
|
|
207
|
+
* 4. lastStep === 'apply' → 'applied'
|
|
208
|
+
* 5. tasks.md has at least one [x] and lastStep is absent → 'apply-in-progress'
|
|
209
|
+
* 6. only proposal.md exists → 'proposed'
|
|
210
|
+
* 7. none of the above → 'unknown'
|
|
211
|
+
*
|
|
212
|
+
* NOTE — 'approved' is intentionally absent from this table.
|
|
213
|
+
* There is no artifact signal that distinguishes an approved proposal from a
|
|
214
|
+
* merely proposed one: both have proposal.md, specs.md, design.md, tasks.md.
|
|
215
|
+
* 'approved' is set exclusively via `set-memory --state approved --actor propose-skill`
|
|
216
|
+
* when the human approves the artifacts in /refacil:propose Step 4.
|
|
217
|
+
* Inference can only detect 'proposed' (artifact exists but no explicit state recorded).
|
|
218
|
+
*
|
|
219
|
+
* @param {string} changeDir Absolute path to the change directory.
|
|
220
|
+
* @param {object} memory Parsed memory.yaml object (may be empty).
|
|
221
|
+
* @returns {{ currentState: string, inferred: boolean }}
|
|
222
|
+
*/
|
|
223
|
+
function inferCurrentState(changeDir, memory) {
|
|
224
|
+
// 1. .review-passed
|
|
225
|
+
if (fs.existsSync(path.join(changeDir, '.review-passed'))) {
|
|
226
|
+
return { currentState: 'reviewed', inferred: true };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const lastStep = memory.lastStep || null;
|
|
230
|
+
|
|
231
|
+
// 2. lastStep verify/verified
|
|
232
|
+
if (lastStep === 'verify' || lastStep === 'verified') {
|
|
233
|
+
return { currentState: 'verified', inferred: true };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 3. lastStep test
|
|
237
|
+
if (lastStep === 'test') {
|
|
238
|
+
return { currentState: 'tested', inferred: true };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 4. lastStep apply
|
|
242
|
+
if (lastStep === 'apply') {
|
|
243
|
+
return { currentState: 'applied', inferred: true };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 5. tasks.md has at least one [x] and lastStep absent
|
|
247
|
+
const tasksPath = path.join(changeDir, 'tasks.md');
|
|
248
|
+
if (!lastStep && fs.existsSync(tasksPath)) {
|
|
249
|
+
try {
|
|
250
|
+
const tasksContent = fs.readFileSync(tasksPath, 'utf8');
|
|
251
|
+
if (/^- \[x\]/m.test(tasksContent)) {
|
|
252
|
+
return { currentState: 'apply-in-progress', inferred: true };
|
|
253
|
+
}
|
|
254
|
+
} catch (_) {
|
|
255
|
+
// Unexpected read error (e.g. permission denied): swallow and fall through
|
|
256
|
+
// to the proposal.md check so inference still produces a useful result.
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 6. only proposal.md exists (no design, tasks, specs)
|
|
261
|
+
if (fs.existsSync(path.join(changeDir, 'proposal.md'))) {
|
|
262
|
+
return { currentState: 'proposed', inferred: true };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return { currentState: 'unknown', inferred: true };
|
|
266
|
+
}
|
|
267
|
+
|
|
186
268
|
// --- Subcomandos ---
|
|
187
269
|
|
|
188
270
|
function cmdValidateName(argv) {
|
|
@@ -320,7 +402,7 @@ function cmdSetMemory(argv, projectRoot) {
|
|
|
320
402
|
const rawName = args._positional[0];
|
|
321
403
|
|
|
322
404
|
if (!rawName) {
|
|
323
|
-
console.error('Uso: refacil-sdd-ai sdd set-memory <nombre-cambio> [--last-step <value>] [--stack-detected <value>] [--touched-files <csv>] [--commands-run <value>] [--criteria-run <csv>]');
|
|
405
|
+
console.error('Uso: refacil-sdd-ai sdd set-memory <nombre-cambio> [--last-step <value>] [--stack-detected <value>] [--touched-files <csv>] [--commands-run <value>] [--criteria-run <csv>] [--state <estado>] [--actor <nombre>]');
|
|
324
406
|
process.exit(1);
|
|
325
407
|
}
|
|
326
408
|
|
|
@@ -340,10 +422,30 @@ function cmdSetMemory(argv, projectRoot) {
|
|
|
340
422
|
process.exit(1);
|
|
341
423
|
}
|
|
342
424
|
|
|
425
|
+
// Validate --state before touching any file (CR-01: exit 1 without modifying file)
|
|
426
|
+
if (args['state'] !== undefined) {
|
|
427
|
+
const stateValue = args['state'];
|
|
428
|
+
// CR-02: empty string or bare flag (parsed as boolean true) → explicit empty-state error
|
|
429
|
+
if (stateValue === '' || stateValue === true) {
|
|
430
|
+
console.error('set-memory: el estado no puede estar vacío');
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
if (!VALID_STATES.includes(stateValue)) {
|
|
434
|
+
console.error(`set-memory: estado inválido '${stateValue}'. Estados válidos: ${VALID_STATES.join(', ')}`);
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Validate --actor: pipe character would corrupt the stateHistory pipe format
|
|
440
|
+
if (args['actor'] !== undefined && String(args['actor']).includes('|')) {
|
|
441
|
+
console.error("set-memory: --actor no puede contener el carácter '|'");
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
|
|
343
445
|
// Require at least one field flag
|
|
344
|
-
const knownFlags = ['last-step', 'stack-detected', 'touched-files', 'commands-run', 'criteria-run'];
|
|
446
|
+
const knownFlags = ['last-step', 'stack-detected', 'touched-files', 'commands-run', 'criteria-run', 'state', 'actor'];
|
|
345
447
|
if (!knownFlags.some((f) => args[f] !== undefined)) {
|
|
346
|
-
console.error('set-memory: debe especificar al menos un campo (--last-step, --stack-detected, --touched-files, --commands-run, --criteria-run)');
|
|
448
|
+
console.error('set-memory: debe especificar al menos un campo (--last-step, --stack-detected, --touched-files, --commands-run, --criteria-run, --state, --actor)');
|
|
347
449
|
process.exit(1);
|
|
348
450
|
}
|
|
349
451
|
|
|
@@ -370,6 +472,18 @@ function cmdSetMemory(argv, projectRoot) {
|
|
|
370
472
|
existing['criteriaRun'] = args['criteria-run'].split(',').map((s) => s.trim()).filter(Boolean);
|
|
371
473
|
}
|
|
372
474
|
|
|
475
|
+
// State tracking (T-01)
|
|
476
|
+
if (args['state'] !== undefined) {
|
|
477
|
+
const actor = args['actor'] !== undefined ? String(args['actor']) : 'cli';
|
|
478
|
+
existing['currentState'] = args['state'];
|
|
479
|
+
const iso = new Date().toISOString();
|
|
480
|
+
const entry = `${args['state']}|${iso}|${actor}`;
|
|
481
|
+
if (!Array.isArray(existing['stateHistory'])) {
|
|
482
|
+
existing['stateHistory'] = [];
|
|
483
|
+
}
|
|
484
|
+
existing['stateHistory'].push(entry);
|
|
485
|
+
}
|
|
486
|
+
|
|
373
487
|
fs.writeFileSync(memoryPath, serializeMemoryYaml(existing), 'utf8');
|
|
374
488
|
console.log(`memory.yaml actualizado para '${name}'`);
|
|
375
489
|
}
|
|
@@ -607,12 +721,42 @@ function cmdStatus(argv, projectRoot) {
|
|
|
607
721
|
forArchive: reviewPassed && taskStats.total > 0 && taskStats.pending === 0,
|
|
608
722
|
};
|
|
609
723
|
|
|
724
|
+
// Read memory for currentState / lastStep / touchedFiles
|
|
725
|
+
const memoryPath = path.join(changeDir, 'memory.yaml');
|
|
726
|
+
let memory = {};
|
|
727
|
+
if (fs.existsSync(memoryPath)) {
|
|
728
|
+
try {
|
|
729
|
+
memory = parseYaml(fs.readFileSync(memoryPath, 'utf8')) || {};
|
|
730
|
+
} catch (_) {
|
|
731
|
+
memory = {};
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Resolve currentState: explicit from memory or inferred
|
|
736
|
+
let currentState = null;
|
|
737
|
+
let stateInferred = false;
|
|
738
|
+
if (memory.currentState) {
|
|
739
|
+
currentState = memory.currentState;
|
|
740
|
+
stateInferred = false;
|
|
741
|
+
} else {
|
|
742
|
+
const inferred = inferCurrentState(changeDir, memory);
|
|
743
|
+
currentState = inferred.currentState;
|
|
744
|
+
stateInferred = inferred.inferred;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const lastStep = memory.lastStep || null;
|
|
748
|
+
const touchedFiles = Array.isArray(memory.touchedFiles) ? memory.touchedFiles : [];
|
|
749
|
+
|
|
610
750
|
const status = {
|
|
611
751
|
name,
|
|
612
752
|
artifacts,
|
|
613
753
|
tasks: taskStats,
|
|
614
754
|
reviewPassed,
|
|
615
755
|
ready,
|
|
756
|
+
currentState,
|
|
757
|
+
stateInferred,
|
|
758
|
+
lastStep,
|
|
759
|
+
touchedFiles,
|
|
616
760
|
};
|
|
617
761
|
|
|
618
762
|
if (wantJson) {
|
|
@@ -632,6 +776,11 @@ function cmdStatus(argv, projectRoot) {
|
|
|
632
776
|
console.log('');
|
|
633
777
|
console.log(` Review aprobado: ${reviewPassed ? 'Si' : 'No'}`);
|
|
634
778
|
console.log('');
|
|
779
|
+
console.log(' Estado:');
|
|
780
|
+
console.log(` currentState: ${currentState}${stateInferred ? ' (inferido)' : ''}`);
|
|
781
|
+
if (lastStep) console.log(` lastStep: ${lastStep}`);
|
|
782
|
+
if (touchedFiles.length > 0) console.log(` touchedFiles: ${touchedFiles.join(', ')}`);
|
|
783
|
+
console.log('');
|
|
635
784
|
console.log(' Listo para:');
|
|
636
785
|
console.log(` apply: ${ready.forApply ? 'Si' : 'No'}`);
|
|
637
786
|
console.log(` archive: ${ready.forArchive ? 'Si' : 'No'}`);
|
|
@@ -948,6 +1097,18 @@ function cmdStats(argv, projectRoot) {
|
|
|
948
1097
|
const lastStep = memory.lastStep || null;
|
|
949
1098
|
const criteriaRun = Array.isArray(memory.criteriaRun) ? memory.criteriaRun : [];
|
|
950
1099
|
|
|
1100
|
+
// Resolve currentState for stats
|
|
1101
|
+
let statsCurrentState = null;
|
|
1102
|
+
let statsStateInferred = false;
|
|
1103
|
+
if (memory.currentState) {
|
|
1104
|
+
statsCurrentState = memory.currentState;
|
|
1105
|
+
statsStateInferred = false;
|
|
1106
|
+
} else {
|
|
1107
|
+
const inferred = inferCurrentState(changeDir, memory);
|
|
1108
|
+
statsCurrentState = inferred.currentState;
|
|
1109
|
+
statsStateInferred = inferred.inferred;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
951
1112
|
// --- Read .review-passed ---
|
|
952
1113
|
const reviewPassedPath = path.join(changeDir, '.review-passed');
|
|
953
1114
|
let reviewDate = null;
|
|
@@ -1026,6 +1187,8 @@ function cmdStats(argv, projectRoot) {
|
|
|
1026
1187
|
testCommand: testCommand || null,
|
|
1027
1188
|
lastStep: lastStep || null,
|
|
1028
1189
|
criteriaRun,
|
|
1190
|
+
currentState: statsCurrentState,
|
|
1191
|
+
stateInferred: statsStateInferred,
|
|
1029
1192
|
},
|
|
1030
1193
|
review: {
|
|
1031
1194
|
passed: reviewDate !== null,
|
|
@@ -1060,6 +1223,7 @@ function cmdStats(argv, projectRoot) {
|
|
|
1060
1223
|
if (startDate) console.log(` Inicio estimado: ${startDate.toISOString().slice(0, 10)}`);
|
|
1061
1224
|
console.log('');
|
|
1062
1225
|
console.log(' Memory');
|
|
1226
|
+
console.log(` ${pad('currentState', 20)} ${statsCurrentState || noDataLabel}${statsStateInferred ? ' (inferido)' : ''}`);
|
|
1063
1227
|
console.log(` ${pad('lastStep', 20)} ${lastStep || noDataLabel}`);
|
|
1064
1228
|
console.log(` ${pad('criteriaRun', 20)} ${criteriaRun.length > 0 ? criteriaRun.join(', ') : noDataLabel}`);
|
|
1065
1229
|
console.log(` ${pad('testCommand', 20)} ${testCommand || noDataLabel}`);
|
|
@@ -1110,6 +1274,8 @@ function sddHelp() {
|
|
|
1110
1274
|
[--touched-files <csv>] Archivos modificados (separados por coma)
|
|
1111
1275
|
[--commands-run <value>] Comando de test ejecutado
|
|
1112
1276
|
[--criteria-run <csv>] Criterios CA/CR ejecutados (separados por coma)
|
|
1277
|
+
[--state <estado>] Estado del ciclo SDD-AI (proposed, approved, apply-in-progress, applied, tested, verified, reviewed, archived)
|
|
1278
|
+
[--actor <nombre>] Actor que origina el cambio de estado (default: cli)
|
|
1113
1279
|
sdd get-memory <nombre> Lee memory.yaml del cambio
|
|
1114
1280
|
[--json] Salida en JSON (por defecto: YAML raw)
|
|
1115
1281
|
sdd set-review-fails <nombre> Escribe .review-last-fails.json con archivos fallidos
|
|
@@ -1124,8 +1290,8 @@ function sddHelp() {
|
|
|
1124
1290
|
[--base-branch <branch>] Rama base para nuevos cambios
|
|
1125
1291
|
[--protected-branches <csv>] Ramas protegidas (separadas por coma)
|
|
1126
1292
|
[--artifact-language <language>] Idioma para los artefactos SDD generados (english, spanish)
|
|
1127
|
-
sdd stats <nombre> [--json] Muestra estadísticas del cambio: memoria
|
|
1128
|
-
compact telemetry y CodeGraph en el periodo del cambio
|
|
1293
|
+
sdd stats <nombre> [--json] Muestra estadísticas del cambio: memoria (incluyendo currentState),
|
|
1294
|
+
review, compact telemetry y CodeGraph en el periodo del cambio
|
|
1129
1295
|
sdd test-scope Resolves scoped test files for the given source files
|
|
1130
1296
|
--files <csv> Comma-separated source file paths to scope tests for
|
|
1131
1297
|
[--stack <name>] Stack hint (node, python, go, rust, java, dotnet)
|
|
@@ -1214,4 +1380,6 @@ module.exports = {
|
|
|
1214
1380
|
cmdWriteConfig,
|
|
1215
1381
|
cmdStats,
|
|
1216
1382
|
cmdTestScope,
|
|
1383
|
+
VALID_STATES,
|
|
1384
|
+
inferCurrentState,
|
|
1217
1385
|
};
|
package/lib/installer.js
CHANGED
package/lib/kapso.js
CHANGED
|
@@ -66,6 +66,51 @@ function postMessage(creds, bodyText) {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// ─── validation ────────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
// Unfilled template token left in the command, e.g. "<changeName>" or "<repoSlug>".
|
|
72
|
+
const PLACEHOLDER_REGEX = /<[^>]+>/;
|
|
73
|
+
|
|
74
|
+
function isBlankOrPlaceholder(value) {
|
|
75
|
+
if (value === null || value === undefined) return true;
|
|
76
|
+
const s = String(value).trim();
|
|
77
|
+
if (s === '') return true;
|
|
78
|
+
if (PLACEHOLDER_REGEX.test(s)) return true;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Returns a list of human-readable problems with the notify payload.
|
|
83
|
+
// Empty list ⇒ the payload is complete enough to send.
|
|
84
|
+
//
|
|
85
|
+
// This is the guard against the autopilot failure mode where the skill fires
|
|
86
|
+
// `kapso notify` once with unfilled <placeholders>/empty flags (sending a junk
|
|
87
|
+
// WhatsApp message) and again with the real values. We reject the incomplete
|
|
88
|
+
// call so it never reaches postMessage().
|
|
89
|
+
function validateNotifyOpts(type, opts) {
|
|
90
|
+
const problems = [];
|
|
91
|
+
const required =
|
|
92
|
+
type === 'success'
|
|
93
|
+
? ['repo', 'change', 'branch', 'tasks']
|
|
94
|
+
: ['repo', 'change', 'branch', 'phase'];
|
|
95
|
+
|
|
96
|
+
for (const field of required) {
|
|
97
|
+
if (isBlankOrPlaceholder(opts[field])) {
|
|
98
|
+
problems.push(`--${field} faltante, vacío o sin rellenar`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// tasks must look like "done/total" (e.g. "5/5"), not a placeholder or prose.
|
|
103
|
+
if (
|
|
104
|
+
type === 'success' &&
|
|
105
|
+
!isBlankOrPlaceholder(opts.tasks) &&
|
|
106
|
+
!/^\d+\/\d+$/.test(String(opts.tasks).trim())
|
|
107
|
+
) {
|
|
108
|
+
problems.push(`--tasks con formato inválido (esperado "done/total"): "${opts.tasks}"`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return problems;
|
|
112
|
+
}
|
|
113
|
+
|
|
69
114
|
// ─── log ─────────────────────────────────────────────────────────────────────
|
|
70
115
|
|
|
71
116
|
function appendLog(msg) {
|
|
@@ -150,6 +195,23 @@ function preflight() {
|
|
|
150
195
|
// kapso notify failure --repo <slug> --change <name> --branch <branch>
|
|
151
196
|
// --phase <phase> --last-commit "<commit>" --error "<summary>"
|
|
152
197
|
async function notify(type, opts) {
|
|
198
|
+
if (type !== 'success' && type !== 'failure') {
|
|
199
|
+
console.error(` Unknown notify type: "${type}". Use "success" or "failure".`);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Reject incomplete/placeholder payloads BEFORE doing anything else so a junk
|
|
204
|
+
// notification is never sent. Throwing surfaces a clear, actionable error to
|
|
205
|
+
// the caller (autopilot) without ever hitting the WhatsApp API.
|
|
206
|
+
const problems = validateNotifyOpts(type, opts);
|
|
207
|
+
if (problems.length > 0) {
|
|
208
|
+
appendLog(`kapso notify ${type} REJECTED (datos inválidos): ${problems.join('; ')}`);
|
|
209
|
+
throw new Error(
|
|
210
|
+
`kapso notify ${type}: datos inválidos o incompletos — ${problems.join('; ')}. ` +
|
|
211
|
+
'No se envió la notificación. Reintenta con todos los flags requeridos rellenados con valores reales.',
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
153
215
|
const creds = readCredentials();
|
|
154
216
|
if (!creds) {
|
|
155
217
|
appendLog('kapso notify skipped: no credentials');
|
|
@@ -218,9 +280,6 @@ async function notify(type, opts) {
|
|
|
218
280
|
'',
|
|
219
281
|
'El árbol de trabajo quedó en su estado actual para revisión.',
|
|
220
282
|
].join('\n');
|
|
221
|
-
} else {
|
|
222
|
-
console.error(` Unknown notify type: "${type}". Use "success" or "failure".`);
|
|
223
|
-
process.exit(1);
|
|
224
283
|
}
|
|
225
284
|
|
|
226
285
|
try {
|
|
@@ -238,4 +297,12 @@ async function notify(type, opts) {
|
|
|
238
297
|
}
|
|
239
298
|
}
|
|
240
299
|
|
|
241
|
-
module.exports = {
|
|
300
|
+
module.exports = {
|
|
301
|
+
setup,
|
|
302
|
+
preflight,
|
|
303
|
+
notify,
|
|
304
|
+
readCredentials,
|
|
305
|
+
validateNotifyOpts,
|
|
306
|
+
isBlankOrPlaceholder,
|
|
307
|
+
PHONE_REGEX,
|
|
308
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "refacil-sdd-ai",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.1",
|
|
4
4
|
"description": "SDD-AI: Specification-Driven Development with AI — development methodology using AI with Claude Code, Cursor, OpenCode and Codex",
|
|
5
5
|
"bin": {
|
|
6
6
|
"refacil-sdd-ai": "./bin/cli.js"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"postinstall": "node ./bin/postinstall.js",
|
|
43
|
-
"test": "node --test --test-concurrency=1 test/global-paths.test.js test/opencode-global-path-windows.test.js test/opencode-migrate.test.js test/opencode-clean-global.test.js test/skills-parity.test.js test/multi-ide-parity-pass2.test.js test/repo-ide-sync.test.js test/global-install.test.js test/hooks.test.js test/installer.test.js test/ignore-files.test.js test/methodology-migration-pending.test.js test/sdd.test.js test/test-scope.test.js test/config.test.js test/refactor-integrar-openspec-nativo.test.js test/refactor-rutas-refacil-sdd.test.js test/refactor-agents-english.test.js test/remove-openspec-legacy.test.js test/find-project-root.test.js test/project-root.test.js test/opencode-installer.test.js test/opencode-plugin.test.js test/check-review.test.js test/toml-converter.test.js test/testing-policy-sync.test.js test/session-repo-sync.test.js test/compact-guidance.test.js test/codegraph.test.js test/codegraph-telemetry.test.js test/compact-codegraph.test.js test/autopilot-skill-install.test.js test/read-spec-md-parser.test.js test/read-spec.test.js test/spec-sync.test.js test/kapso-init-session-removed.test.js test/kapso-preflight.test.js test/autopilot-ready-message.test.js test/stats-observability.test.js test/bus-from-env.test.js test/imp-scoped-test-execution.test.js test/broker-port-env.test.js"
|
|
43
|
+
"test": "node --test --test-concurrency=1 test/global-paths.test.js test/opencode-global-path-windows.test.js test/opencode-migrate.test.js test/opencode-clean-global.test.js test/skills-parity.test.js test/multi-ide-parity-pass2.test.js test/repo-ide-sync.test.js test/global-install.test.js test/hooks.test.js test/installer.test.js test/ignore-files.test.js test/methodology-migration-pending.test.js test/sdd.test.js test/test-scope.test.js test/config.test.js test/refactor-integrar-openspec-nativo.test.js test/refactor-rutas-refacil-sdd.test.js test/refactor-agents-english.test.js test/remove-openspec-legacy.test.js test/find-project-root.test.js test/project-root.test.js test/opencode-installer.test.js test/opencode-plugin.test.js test/check-review.test.js test/toml-converter.test.js test/testing-policy-sync.test.js test/session-repo-sync.test.js test/compact-guidance.test.js test/codegraph.test.js test/codegraph-telemetry.test.js test/compact-codegraph.test.js test/autopilot-skill-install.test.js test/read-spec-md-parser.test.js test/read-spec.test.js test/spec-sync.test.js test/kapso-init-session-removed.test.js test/kapso-preflight.test.js test/autopilot-ready-message.test.js test/stats-observability.test.js test/bus-from-env.test.js test/imp-scoped-test-execution.test.js test/broker-port-env.test.js test/change-state-tracking.test.js"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"smol-toml": "^1.6.1",
|
package/skills/apply/SKILL.md
CHANGED
|
@@ -113,6 +113,14 @@ specsNote: <"specs.md" | "specs/**/*.md" | "both — report contradictions in is
|
|
|
113
113
|
|
|
114
114
|
### Step 2: Delegate to the refacil-implementer sub-agent
|
|
115
115
|
|
|
116
|
+
Before delegating, record that implementation has started:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
refacil-sdd-ai sdd set-memory <changeName> --state apply-in-progress --actor apply-skill
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
If the command fails, continue silently — it must not block the flow.
|
|
123
|
+
|
|
116
124
|
Invoke the `refacil-implementer` sub-agent passing it the BRIEFING from the previous step plus:
|
|
117
125
|
- `changeName` (redundant with the briefing, but the guardrail needs it)
|
|
118
126
|
- If the user requested detailed mode, indicate it. Default: concise.
|
|
@@ -130,7 +138,9 @@ Run:
|
|
|
130
138
|
```bash
|
|
131
139
|
refacil-sdd-ai sdd set-memory <changeName> \
|
|
132
140
|
--last-step apply \
|
|
133
|
-
--touched-files "<comma-separated list of modified files>"
|
|
141
|
+
--touched-files "<comma-separated list of modified files>" \
|
|
142
|
+
--state applied \
|
|
143
|
+
--actor apply-skill
|
|
134
144
|
```
|
|
135
145
|
|
|
136
146
|
This command merges into memory.yaml at the repo root using `findProjectRoot()` — no manual path construction needed.
|
package/skills/archive/SKILL.md
CHANGED
|
@@ -174,10 +174,18 @@ Minimal bug fixes only contain `summary.md` (and optionally `.review-passed`) an
|
|
|
174
174
|
```
|
|
175
175
|
- If `review.yaml` already exists, update only the fields that changed without removing others.
|
|
176
176
|
|
|
177
|
-
3. **
|
|
178
|
-
4. Verify the command completed successfully (exit 0) and the original folder no longer exists.
|
|
177
|
+
3. **Record archived state** — run this command **while the change directory is still at its original path** (`refacil-sdd/changes/<changeName>/`), before the CLI move in step 4. After the move that path no longer exists and `set-memory` would fail to locate the change:
|
|
179
178
|
|
|
180
|
-
|
|
179
|
+
```bash
|
|
180
|
+
refacil-sdd-ai sdd set-memory <changeName> --state archived --actor archive-skill
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
If the command fails, continue silently — it must not block the flow.
|
|
184
|
+
|
|
185
|
+
4. **Run the CLI archive**: `refacil-sdd-ai sdd archive <changeName>` — sync-spec, preserves `memory.yaml` if present, then moves the complete change directory to `refacil-sdd/changes/archive/<date>-<changeName>/`.
|
|
186
|
+
5. Verify the command completed successfully (exit 0) and the original folder no longer exists.
|
|
187
|
+
|
|
188
|
+
6. Continue to **Step 3**.
|
|
181
189
|
|
|
182
190
|
The goal is for `refacil-sdd/specs/` to document how the system works TODAY.
|
|
183
191
|
|
|
@@ -354,9 +354,10 @@ When any phase fails:
|
|
|
354
354
|
- `errorSummary`: 1-3 line summary.
|
|
355
355
|
- `branchAtFailure`: `git branch --show-current`.
|
|
356
356
|
- `lastCommit`: `git log -1 --oneline`.
|
|
357
|
-
4.
|
|
358
|
-
5.
|
|
359
|
-
|
|
357
|
+
4. Send the failure notification via Kapso only if `kapsoEnabled = true` (Step 7, failure template).
|
|
358
|
+
5. Leave the working tree as-is so the user can inspect when they return. Normal recovery must never use destructive reset commands; preserve local edits and report the evidence needed for manual repair.
|
|
359
|
+
|
|
360
|
+
> **Note**: do NOT delete `refacil-sdd/.autopilot-active` here. Step 8 deletes the marker unconditionally at the end of the cycle — this keeps the marker alive during Step 7 in both success and failure paths, so the CLI can compute the exact duration from `startedAt`.
|
|
360
361
|
|
|
361
362
|
### Step 7: Notify via Kapso
|
|
362
363
|
|
|
@@ -364,37 +365,65 @@ When any phase fails:
|
|
|
364
365
|
|
|
365
366
|
All notification logic — credentials, message formatting, HTTP call, error logging — is handled by the CLI. Do NOT construct curl calls or read the env file directly.
|
|
366
367
|
|
|
368
|
+
**Send the notification exactly ONCE, with every flag resolved to a real value — never a placeholder.** Before invoking `kapso notify`, confirm that `repoSlug`, `changeName`, `branchAtStart`/`branchAtFailure`, `tasks.done`/`tasks.total` (and `phase` on failure) are all filled with concrete values captured in Steps 0–6 — there must be no literal `<...>` token left in the command. Do NOT fire a first "probe" call and then a corrected one: a single notification per run. The CLI **rejects** any `notify` whose required flags are missing, empty, or still contain a `<placeholder>` (it exits non-zero without sending, and prints which flags are wrong). If you see that rejection, it means a value was not resolved — fill it in from the captured state and invoke once more; do not let an empty notification go out.
|
|
369
|
+
|
|
367
370
|
**Success notification**:
|
|
368
371
|
|
|
372
|
+
Required flags (validated by `validateNotifyOpts`; the CLI rejects the call if any is missing or contains a placeholder):
|
|
373
|
+
- `--repo` — repository slug (e.g. `refacil-ia`)
|
|
374
|
+
- `--change` — change name (e.g. `imp-session-timeout-redis`)
|
|
375
|
+
- `--branch` — branch at start (e.g. `feature/imp-session-timeout-redis`)
|
|
376
|
+
- `--tasks` — tasks in `done/total` format (e.g. `5/5`). Must match `^\d+/\d+$`.
|
|
377
|
+
|
|
378
|
+
Optional flags (omit if not applicable):
|
|
379
|
+
- `--pr` — PR link, empty string, or `"skipped"` (see rules below)
|
|
380
|
+
- `--apply`, `--test`, `--review` — improvement counters (integer, default `0`)
|
|
381
|
+
- `--warnings` — top 3 warning strings joined by `|` (omit or pass `""` if none)
|
|
382
|
+
|
|
383
|
+
> **Duration**: the CLI computes the run duration automatically from the `startedAt` field written to `.autopilot-active` in Step 0.7. You do **not** need to pass `--duration` — omit it and the CLI reads `startedAt` from the marker. Only pass `--duration <minutes>` as a manual fallback if the marker was unavailable for some reason (this should never happen with the corrected Step 6 that no longer deletes the marker before Step 7).
|
|
384
|
+
|
|
369
385
|
The `--pr` flag value depends on `includeUpCode`:
|
|
370
386
|
- `includeUpCode = true` and push succeeded with PR → `--pr "<prLink>"`
|
|
371
387
|
- `includeUpCode = true` and push succeeded without PR (`createPR = false`) → `--pr ""` (empty string)
|
|
372
388
|
- `includeUpCode = false` → `--pr "skipped"`
|
|
373
389
|
|
|
390
|
+
Example (all required flags filled with real values):
|
|
391
|
+
|
|
374
392
|
```bash
|
|
375
393
|
refacil-sdd-ai kapso notify success \
|
|
376
|
-
--repo "
|
|
377
|
-
--change "
|
|
378
|
-
--branch "
|
|
379
|
-
--tasks "
|
|
380
|
-
--
|
|
381
|
-
--
|
|
382
|
-
--
|
|
383
|
-
--
|
|
384
|
-
--
|
|
385
|
-
--warnings "<top 3 warnings joined by | or empty>"
|
|
394
|
+
--repo "refacil-ia" \
|
|
395
|
+
--change "imp-session-timeout-redis" \
|
|
396
|
+
--branch "feature/imp-session-timeout-redis" \
|
|
397
|
+
--tasks "5/5" \
|
|
398
|
+
--pr "https://github.com/org/refacil-ia/pull/42" \
|
|
399
|
+
--apply "1" \
|
|
400
|
+
--test "0" \
|
|
401
|
+
--review "0" \
|
|
402
|
+
--warnings ""
|
|
386
403
|
```
|
|
387
404
|
|
|
388
405
|
**Failure notification**:
|
|
389
406
|
|
|
407
|
+
Required flags:
|
|
408
|
+
- `--repo` — repository slug
|
|
409
|
+
- `--change` — change name
|
|
410
|
+
- `--branch` — branch at the moment of failure (`branchAtFailure`)
|
|
411
|
+
- `--phase` — which phase failed (e.g. `verify`, `test`, `apply`, `review`, `archive`, `up-code`)
|
|
412
|
+
|
|
413
|
+
Optional flags:
|
|
414
|
+
- `--last-commit` — last commit hash + message from `git log -1 --oneline`
|
|
415
|
+
- `--error` — 1-3 line error summary
|
|
416
|
+
|
|
417
|
+
Example (all required flags filled with real values):
|
|
418
|
+
|
|
390
419
|
```bash
|
|
391
420
|
refacil-sdd-ai kapso notify failure \
|
|
392
|
-
--repo "
|
|
393
|
-
--change "
|
|
394
|
-
--branch "
|
|
395
|
-
--phase "
|
|
396
|
-
--last-commit "
|
|
397
|
-
--error "
|
|
421
|
+
--repo "refacil-ia" \
|
|
422
|
+
--change "imp-session-timeout-redis" \
|
|
423
|
+
--branch "feature/imp-session-timeout-redis" \
|
|
424
|
+
--phase "verify" \
|
|
425
|
+
--last-commit "a1b2c3d feat: add session timeout to Redis store" \
|
|
426
|
+
--error "verify: 2 unresolved CRITICAL findings after 2 autofix rounds"
|
|
398
427
|
```
|
|
399
428
|
|
|
400
429
|
The CLI reads credentials from `~/.refacil-sdd-ai/kapso.env` internally. If Kapso returns an error, it logs to `~/.refacil-sdd-ai/autopilot.log` and exits without crashing — the SDD work is intact.
|
package/skills/guide/SKILL.md
CHANGED
|
@@ -35,10 +35,13 @@ You are a **brief** guide: you choose the next command; the detail of each flow
|
|
|
35
35
|
11. Autonomous pipeline (after approved propose) → `/refacil:autopilot` — same chain as option 1 path B; use when you want autonomous execution ("autopilot", "modo autónomo", "termina solo el flujo"). During pre-flight you define whether up-code (push + PR) is included — the cycle adapts and can end at archive or continue with up-code.
|
|
36
36
|
12. Listen to specs with voice (TTS) → `/refacil:read-spec` — on-device browser playback; post-propose option B in propose, or on-demand for active changes / archived specs (`refacil-sdd-ai read-spec --change <name>`)
|
|
37
37
|
13. Change progress and metrics → `/refacil:stats` — task completion, review gate, test commands from `memory.yaml` (`refacil-sdd-ai sdd stats <changeName>`)
|
|
38
|
+
14. Current phase and resume → `/refacil:status` — which phase of the SDD-AI cycle a change is in and the exact command to resume it (`refacil-sdd-ai sdd status <changeName>`)
|
|
38
39
|
|
|
39
40
|
> **Note**: `up-code` verifies `.review-passed` before push; see `METHODOLOGY-CONTRACT.md §5-6` for details.
|
|
40
41
|
|
|
41
|
-
> **Skill parity**:
|
|
42
|
+
> **Skill parity**: 22 user-invocable skills (`user-invocable: true`, excluding internal `prereqs`) must appear in `SKILLS[]`, this menu, and the README "Available IDE Skills" table — including `read-spec`, `stats`, and `status`.
|
|
43
|
+
|
|
44
|
+
> **stats vs status**: `/refacil:stats` shows **telemetry** (token savings, compact rewrites, CodeGraph calls, review history). `/refacil:status` shows **navigation** (current cycle phase, next skill to run, state history). Use status to navigate the flow; use stats for observability data.
|
|
42
45
|
|
|
43
46
|
### After `/refacil:propose` is approved
|
|
44
47
|
|
|
@@ -91,6 +94,7 @@ Full detail in the refacil-sdd-ai README (section `refacil-bus`).
|
|
|
91
94
|
- Option 11 (Autonomous pipeline) → `/refacil:autopilot`
|
|
92
95
|
- Option 12 (Listen to specs) → `/refacil:read-spec`
|
|
93
96
|
- Option 13 (Change progress) → `/refacil:stats`
|
|
97
|
+
- Option 14 (Current phase and resume) → `/refacil:status`
|
|
94
98
|
- Post-propose context with a single clear next step: if artifacts are approved and the user wants hands-off → `/refacil:autopilot`; if they want to hear specs first → `/refacil:read-spec`; if they want step-by-step → `/refacil:apply`.
|
|
95
99
|
- If the intent does not map exactly to an option, do NOT invoke — list numbered options to the user and ask for explicit selection.
|
|
96
100
|
|
package/skills/propose/SKILL.md
CHANGED
|
@@ -132,6 +132,14 @@ If the user requests limited adjustments (change a criterion, fix a path, adjust
|
|
|
132
132
|
|
|
133
133
|
### Step 4: Next step
|
|
134
134
|
|
|
135
|
+
**Before presenting the menu**, record that the proposal has been approved by running:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
refacil-sdd-ai sdd set-memory <changeName> --state approved --actor propose-skill
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If the command fails, continue silently — it must not block the flow.
|
|
142
|
+
|
|
135
143
|
**Always present this menu as a new message** immediately after Step 3 confirms approval. Never skip it or reuse the "OK" from Step 3 as a selection here — that "OK" means "the artifacts are approved", not "choose option A".
|
|
136
144
|
|
|
137
145
|
Before presenting the menu, run `refacil-sdd-ai kapso preflight` (exits 0, prints JSON) and read `kapsoEnabled` from the output. If `kapsoEnabled = true`, append the WhatsApp note to option B. If `kapsoEnabled = false`, omit it.
|
package/skills/review/SKILL.md
CHANGED
|
@@ -167,6 +167,14 @@ Where the values are extracted from the sub-agent's `refacil-review-result` bloc
|
|
|
167
167
|
- `changeName` is null.
|
|
168
168
|
- The sub-agent returned `SCOPE_ERROR`.
|
|
169
169
|
|
|
170
|
+
After writing `.review-passed` with an approved verdict, record the state:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
refacil-sdd-ai sdd set-memory <changeName> --state reviewed --actor review-skill
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
If the command fails or `changeName` is null, continue silently — it must not block the flow.
|
|
177
|
+
|
|
170
178
|
### Step 3.5: Offer to apply corrections (only if REQUIERE CORRECCIONES)
|
|
171
179
|
|
|
172
180
|
If `verdict` is `REQUIERE CORRECCIONES`:
|
package/skills/stats/SKILL.md
CHANGED
|
@@ -51,7 +51,9 @@ Parse the JSON block returned by the CLI. Expected fields:
|
|
|
51
51
|
"memory": {
|
|
52
52
|
"testCommand": "<command or null>",
|
|
53
53
|
"lastStep": "<step or null>",
|
|
54
|
-
"criteriaRun": ["CA-01", "CR-01", ...]
|
|
54
|
+
"criteriaRun": ["CA-01", "CR-01", ...],
|
|
55
|
+
"currentState": "<state or null>",
|
|
56
|
+
"stateInferred": true | false
|
|
55
57
|
},
|
|
56
58
|
"review": {
|
|
57
59
|
"passed": true | false,
|
|
@@ -86,6 +88,7 @@ Present the data in a readable format. If `isArchived` is `true`, add `[archivad
|
|
|
86
88
|
|
|
87
89
|
Phase progress
|
|
88
90
|
Started: <startDate or "unknown">
|
|
91
|
+
Current state: <currentState or "unknown"> <(inferred) if stateInferred is true>
|
|
89
92
|
Last step: <lastStep or "not recorded">
|
|
90
93
|
Criteria: <criteriaRun list or "none">
|
|
91
94
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: refacil:status
|
|
3
|
+
description: Show which phase of the SDD-AI cycle a change is in and the exact command to resume it — use for "where am I?", "how is it going?", "status of the change", "resume", "where did I leave off". Distinct from /refacil:stats (which shows telemetry, token savings, and review metrics). Use /refacil:status to navigate the flow; use /refacil:stats for observability data.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# refacil:status — Current Phase and Resume
|
|
8
|
+
|
|
9
|
+
This skill answers: **"In which phase of the SDD-AI cycle is this change, and what command do I run next?"**
|
|
10
|
+
|
|
11
|
+
It is distinct from `/refacil:stats`:
|
|
12
|
+
- `/refacil:status` — **navigation**: current phase, next skill to run, resume a paused flow.
|
|
13
|
+
- `/refacil:stats` — **telemetry**: token savings, compact rewrites, CodeGraph calls, review history.
|
|
14
|
+
|
|
15
|
+
**Prerequisites**: `sdd` profile from `refacil-prereqs/SKILL.md`.
|
|
16
|
+
|
|
17
|
+
## Activation phrases
|
|
18
|
+
|
|
19
|
+
Invoke this skill for expressions like: "¿en qué va?", "¿cómo voy?", "status del cambio", "retomar", "dónde quedé", "where did I leave off?", "resume", "what's the current state?", "what's next?".
|
|
20
|
+
|
|
21
|
+
## Next-skill mapping
|
|
22
|
+
|
|
23
|
+
The following table maps each state to the recommended next skill in the **normal flow** and in the **bug fix flow** (`fix-*`):
|
|
24
|
+
|
|
25
|
+
| State | Normal flow next skill | Bug fix (fix-*) next skill |
|
|
26
|
+
|-------------------|--------------------------|---------------------------|
|
|
27
|
+
| `proposed` | `/refacil:propose` (approve or adjust) | — |
|
|
28
|
+
| `approved` | `/refacil:apply` | `/refacil:apply` |
|
|
29
|
+
| `apply-in-progress` | `/refacil:apply` | `/refacil:apply` |
|
|
30
|
+
| `applied` | `/refacil:test` | `/refacil:test` |
|
|
31
|
+
| `tested` | `/refacil:verify` | `/refacil:review` (bugs omit verify) |
|
|
32
|
+
| `verified` | `/refacil:review` | — (n/a: bugs never reach verified) |
|
|
33
|
+
| `reviewed` | `/refacil:archive` | `/refacil:archive` |
|
|
34
|
+
| `archived` | `/refacil:up-code` | `/refacil:up-code` |
|
|
35
|
+
| `unknown` | Run `/refacil:propose` to create artifacts | — |
|
|
36
|
+
|
|
37
|
+
## Flow
|
|
38
|
+
|
|
39
|
+
### Without argument — overview of all active changes
|
|
40
|
+
|
|
41
|
+
1. Run `refacil-sdd-ai sdd list --json` to get all active changes.
|
|
42
|
+
|
|
43
|
+
2. If the list is empty, inform the user:
|
|
44
|
+
```
|
|
45
|
+
No active changes found. Run /refacil:propose to start a new change.
|
|
46
|
+
```
|
|
47
|
+
Stop.
|
|
48
|
+
|
|
49
|
+
3. For each active change, run `refacil-sdd-ai sdd status <changeName> --json` and collect:
|
|
50
|
+
- `currentState` and `stateInferred`
|
|
51
|
+
- `tasks.done` / `tasks.total`
|
|
52
|
+
- `reviewPassed`
|
|
53
|
+
|
|
54
|
+
4. Present a summary table:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
=== Active changes ===
|
|
58
|
+
|
|
59
|
+
Change Phase Next command
|
|
60
|
+
─────────────────────────────────────────────────────────────────
|
|
61
|
+
<changeName> <currentState> (inferred?) <next skill>
|
|
62
|
+
...
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For each row, derive the next skill from the **next-skill mapping** above (use the normal flow unless the change name starts with `fix-`).
|
|
66
|
+
|
|
67
|
+
State history is shown only in the detail view — pass a change name as argument to see the full transition log.
|
|
68
|
+
|
|
69
|
+
### With argument — detail for one change
|
|
70
|
+
|
|
71
|
+
1. Use `$ARGUMENTS` as `changeName`.
|
|
72
|
+
|
|
73
|
+
2. Run `refacil-sdd-ai sdd status <changeName> --json` and parse the output.
|
|
74
|
+
|
|
75
|
+
3. If the command exits 1 (change not found), inform the user and stop.
|
|
76
|
+
|
|
77
|
+
4. Resolve `currentState`:
|
|
78
|
+
- Use `status.currentState` from the JSON directly.
|
|
79
|
+
- If `stateInferred` is `true`, note it as `(inferred from artifacts)`.
|
|
80
|
+
|
|
81
|
+
5. Parse `stateHistory` from memory (if available):
|
|
82
|
+
- Run `refacil-sdd-ai sdd get-memory <changeName> --json`.
|
|
83
|
+
- Read the `stateHistory` array. Each entry is a pipe-separated string `"<state>|<ISO>|<actor>"`.
|
|
84
|
+
- Parse each entry with `entry.split('|')` → `[state, iso, actor]`.
|
|
85
|
+
- Show the last 5 entries (most recent last) as a compact history.
|
|
86
|
+
|
|
87
|
+
6. Determine the next skill from the **next-skill mapping** table above.
|
|
88
|
+
|
|
89
|
+
7. Present:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
=== Status: <changeName> ===
|
|
93
|
+
|
|
94
|
+
Current phase: <currentState> <(inferred from artifacts)?>
|
|
95
|
+
Last step: <lastStep or "not recorded">
|
|
96
|
+
Touched files: <touchedFiles list or "none">
|
|
97
|
+
|
|
98
|
+
State history (last 5):
|
|
99
|
+
<date> — <state> (by <actor>)
|
|
100
|
+
...
|
|
101
|
+
|
|
102
|
+
Next command: <next skill>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If the change is in `archived` state, add:
|
|
106
|
+
```
|
|
107
|
+
This change is fully archived. Run /refacil:up-code to push and create the PR.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Rules
|
|
111
|
+
|
|
112
|
+
- **Read-only**: this skill does not modify any file, branch, or memory.
|
|
113
|
+
- **Graceful degradation**: if `stateHistory` is absent or `get-memory` fails, show the current state without history. Do not error.
|
|
114
|
+
- **Empty list behavior**: if `sdd list --json` returns `[]`, inform the user and stop without error.
|
|
115
|
+
- **Bug fix detection**: if `changeName` starts with `fix-`, use the bug fix flow column in the next-skill table. The bug flow skips `/refacil:verify`: a `tested` fix goes straight to `/refacil:review`, and a fix never reaches the `verified` state.
|
|
116
|
+
- **Flow continuity**: if the user confirms affirmatively ("yes", "ok", "go", "continue", etc.) after the status display and there is a single unambiguous next skill, immediately execute that skill. (See `METHODOLOGY-CONTRACT.md §5`.)
|
package/skills/test/SKILL.md
CHANGED
|
@@ -124,7 +124,9 @@ Run:
|
|
|
124
124
|
refacil-sdd-ai sdd set-memory <changeName> \
|
|
125
125
|
--last-step test \
|
|
126
126
|
--commands-run "<test command used>" \
|
|
127
|
-
--criteria-run "<comma-separated criteria IDs that were run>"
|
|
127
|
+
--criteria-run "<comma-separated criteria IDs that were run>" \
|
|
128
|
+
--state tested \
|
|
129
|
+
--actor test-skill
|
|
128
130
|
```
|
|
129
131
|
|
|
130
132
|
If `stackDetected` is available, add `--stack-detected "<stack>"` to the command as well.
|
package/skills/verify/SKILL.md
CHANGED
|
@@ -148,6 +148,14 @@ Parse the ` ```refacil-verify-result ` block from the sub-agent.
|
|
|
148
148
|
|
|
149
149
|
#### If `result` is APPROVED:
|
|
150
150
|
|
|
151
|
+
Record that verification completed successfully:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
refacil-sdd-ai sdd set-memory <changeName> --state verified --actor verify-skill
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
If the command fails, continue silently — it must not block the flow.
|
|
158
|
+
|
|
151
159
|
- `autopilotMode = false` (normal): ask the user:
|
|
152
160
|
```
|
|
153
161
|
RESULT: APPROVED
|