@sylad/cadence 1.41.0 → 1.43.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/dist/archive.js +103 -6
- package/dist/audit.js +2 -2
- package/dist/clean.js +0 -4
- package/dist/cli/context.js +129 -0
- package/dist/cli/deliver-commands.js +114 -0
- package/dist/cli/gates.js +234 -0
- package/dist/cli/lead-commands.js +161 -0
- package/dist/cli/lot-commands.js +257 -0
- package/dist/cli/read-commands.js +227 -0
- package/dist/cli/session-commands.js +152 -0
- package/dist/cli/setup-commands.js +72 -0
- package/dist/cli/spec-commands.js +298 -0
- package/dist/cli/stats-commands.js +138 -0
- package/dist/cli-add.js +37 -0
- package/dist/cli.js +82 -1651
- package/dist/config.js +16 -103
- package/dist/corrections.js +28 -42
- package/dist/docsync.js +15 -3
- package/dist/lead.js +6 -24
- package/dist/orchestrate/attente.js +82 -0
- package/dist/orchestrate/command-args.js +138 -0
- package/dist/orchestrate/command.js +22 -1618
- package/dist/orchestrate/continue.js +2 -12
- package/dist/orchestrate/cycle.js +8 -12
- package/dist/orchestrate/draw.js +62 -0
- package/dist/orchestrate/dry-run.js +223 -0
- package/dist/orchestrate/execute.js +421 -0
- package/dist/orchestrate/integrate.js +58 -4
- package/dist/orchestrate/integration-vivante.js +138 -0
- package/dist/orchestrate/launch.js +6 -18
- package/dist/orchestrate/lock.js +3 -20
- package/dist/orchestrate/pool.js +1 -1
- package/dist/orchestrate/preflight.js +190 -0
- package/dist/orchestrate/registry.js +8 -1
- package/dist/orchestrate/resume.js +384 -0
- package/dist/orchestrate/schemas.js +5 -1
- package/dist/orchestrate/snapshot.js +19 -4
- package/dist/orchestrate/state.js +17 -0
- package/dist/orchestrate/status.js +264 -0
- package/dist/plan-extras.js +3 -2
- package/dist/plan-ids.js +95 -0
- package/dist/plan-read.js +2 -0
- package/dist/plan.js +29 -34
- package/dist/ref/command.js +2 -3
- package/dist/resources.js +3 -9
- package/dist/state.js +8 -8
- package/dist/verify-gate.js +33 -2
- package/dist/yaml-keys.js +19 -0
- package/docs/cadence-hud.md +1 -1
- package/docs/deliver.md +1 -1
- package/docs/lead.md +2 -2
- package/docs/news.md +2 -0
- package/docs/orchestrate.md +20 -4
- package/docs/qualite.md +6 -1
- package/docs/raf.md +22 -4
- package/docs/whats-new.md +9 -0
- package/package.json +3 -1
- package/scripts/qualite.d.mts +15 -0
- package/scripts/qualite.mjs +352 -0
- package/skills/lead/SKILL.md +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// corrections et perf : les mesures du workflow, sans modèle ni écriture (L488, sorti de cli.ts).
|
|
2
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { readPerfConfig } from '../config.js';
|
|
5
|
+
import { RafError } from '../plan.js';
|
|
6
|
+
import { renderTestsSansCode, testsSansCode } from '../corrections-sans-code.js';
|
|
7
|
+
import { bugCodeOf, bugCodePasses, classifyAll, classifySous, measure, ollamaClassifier, ollamaSousClassifier, readCache, readJournals, readRuns, readSousCache, renderReport, sampleKeys, serializeCache, serializeSousCache } from '../corrections.js';
|
|
8
|
+
import { computePerf, perfWindows, readPlans, readProjectDeliveries, readProjectFailedEntries, renderPerf } from '../perf.js';
|
|
9
|
+
/** `cadence corrections` (L165) : la mesure de référence des passes de correction d'une vague, lue dans <dossier>/.cadence/runs. */
|
|
10
|
+
export async function corrections(rest, values, io) {
|
|
11
|
+
if (rest.length > 1)
|
|
12
|
+
throw new RafError('usage : cadence corrections [dossier] [--classify [--model m]] [--cache fichier] [--cache-sous-causes fichier] [--sample n] [--until date] [--since date] [--json]');
|
|
13
|
+
const parent = resolve(io.cwd, rest[0] ?? '.');
|
|
14
|
+
const runsDir = join(parent, '.cadence', 'runs');
|
|
15
|
+
if (!existsSync(runsDir))
|
|
16
|
+
throw new RafError(`corrections : aucun journal de vague dans ${runsDir}`);
|
|
17
|
+
const sample = values.sample === undefined ? undefined : Number(values.sample);
|
|
18
|
+
if (sample !== undefined && !(Number.isInteger(sample) && sample > 0))
|
|
19
|
+
throw new RafError(`--sample invalide : ${values.sample}`);
|
|
20
|
+
if (values.until !== undefined && Number.isNaN(Date.parse(values.until)))
|
|
21
|
+
throw new RafError(`--until invalide : ${values.until} (une date ISO, ex. 2026-10-10T18:31Z)`);
|
|
22
|
+
if (values.since !== undefined && Number.isNaN(Date.parse(values.since)))
|
|
23
|
+
throw new RafError(`--since invalide : ${values.since} (une date ISO, ex. 2026-10-10T18:31Z)`);
|
|
24
|
+
const model = values.model ?? 'mistral-small3.2:24b';
|
|
25
|
+
const cacheFile = resolve(io.cwd, values.cache ?? join(parent, '.cadence', 'corrections.json'));
|
|
26
|
+
const data = readRuns(runsDir, values.until, values.since);
|
|
27
|
+
const sousFile = resolve(io.cwd, values['cache-sous-causes'] ?? join(parent, '.cadence', 'corrections-sous-causes.json'));
|
|
28
|
+
const cache = readCache(cacheFile);
|
|
29
|
+
// R5 (L447) : le cache des sous-causes illisible sort avec le code 1 de la fiche (corrections.json illisible sort avec 2, par `failure`).
|
|
30
|
+
let sousCache;
|
|
31
|
+
try {
|
|
32
|
+
sousCache = readSousCache(sousFile);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
if (!(e instanceof RafError))
|
|
36
|
+
throw e;
|
|
37
|
+
io.err(`raf: ${e.message}`);
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
if (values.classify) {
|
|
41
|
+
const url = io.env.OLLAMA_HOST ?? 'http://localhost:11434';
|
|
42
|
+
await classifyAll(data.passes, cache, ollamaClassifier({ url, model }), model, (done, total) => {
|
|
43
|
+
if (done % 25 === 0 || done === total)
|
|
44
|
+
io.err(`classement : ${done}/${total}`);
|
|
45
|
+
}, { every: 10, save: () => writeFileSync(cacheFile, serializeCache(cache)) });
|
|
46
|
+
writeFileSync(cacheFile, serializeCache(cache));
|
|
47
|
+
const saveSous = () => writeFileSync(sousFile, serializeSousCache(sousCache));
|
|
48
|
+
const added = await classifySous(data.passes, cache, sousCache, ollamaSousClassifier({ url, model }), model, (done, total) => {
|
|
49
|
+
if (done % 25 === 0 || done === total)
|
|
50
|
+
io.err(`sous-causes : ${done}/${total}`);
|
|
51
|
+
}, { every: 10, save: saveSous });
|
|
52
|
+
if (added > 0)
|
|
53
|
+
saveSous();
|
|
54
|
+
}
|
|
55
|
+
if (sample !== undefined) {
|
|
56
|
+
const byKey = new Map(data.passes.map((p) => [p.key, p]));
|
|
57
|
+
// Une passe sans constat (tests rouges) n'a rien à relire : elle ne fait pas partie de l'échantillon.
|
|
58
|
+
for (const key of sampleKeys(data.passes.filter((p) => p.findings.length > 0).map((p) => p.key), sample)) {
|
|
59
|
+
const p = byKey.get(key);
|
|
60
|
+
io.out(`## ${key} (${p.project} ${p.lot}) · modèle : ${cache.get(key)?.cause ?? 'non classé'}${cache.get(key)?.manual ? ` · relu : ${cache.get(key).manual}` : ''}`);
|
|
61
|
+
for (const f of p.findings)
|
|
62
|
+
io.out(`- ${f}`);
|
|
63
|
+
for (const c of p.commits)
|
|
64
|
+
io.out(` commit : ${c}`);
|
|
65
|
+
io.out('');
|
|
66
|
+
}
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
const m = measure(data, cache);
|
|
70
|
+
const bugCode = bugCodeOf(bugCodePasses(data.passes, cache), sousCache);
|
|
71
|
+
const sansCode = testsSansCode(data.passes);
|
|
72
|
+
io.out(values.json ? JSON.stringify({ ...m, testsSansCode: sansCode, ...(bugCode ? { bugCode } : {}) }, null, 2) : [renderReport(m, bugCode), ...renderTestsSansCode(sansCode)].join('\n'));
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
/** `cadence perf` sur un dossier : lecture seule, une RafError dit pourquoi le dossier n'est pas lisible ; `warn` reçoit les avertissements (stderr de perf). */
|
|
76
|
+
export function readPerf(parent, today, since, warn) {
|
|
77
|
+
// La grille se lit avant tout journal : une grille mal formée refuse la commande sans sortie partielle (R7).
|
|
78
|
+
const prices = readPerfConfig(join(parent, 'cadence.yaml'));
|
|
79
|
+
const runsDir = join(parent, '.cadence', 'runs');
|
|
80
|
+
if (!existsSync(runsDir))
|
|
81
|
+
throw new RafError(`perf : aucun journal de vague dans ${runsDir}`);
|
|
82
|
+
const cache = readCache(join(parent, '.cadence', 'corrections.json'));
|
|
83
|
+
const journals = readJournals(runsDir);
|
|
84
|
+
const warnings = [];
|
|
85
|
+
// Les sous-causes (L447) n'arrêtent pas perf : un cache illisible avertit et la ligne est omise.
|
|
86
|
+
const sousFile = join(parent, '.cadence', 'corrections-sous-causes.json');
|
|
87
|
+
let sousCache;
|
|
88
|
+
let sousWarning;
|
|
89
|
+
try {
|
|
90
|
+
sousCache = readSousCache(sousFile);
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
if (!(e instanceof RafError))
|
|
94
|
+
throw e;
|
|
95
|
+
sousWarning = `perf : ${sousFile} illisible, sous-causes n/d`;
|
|
96
|
+
}
|
|
97
|
+
const plans = readPlans(parent, (l) => warnings.push(l));
|
|
98
|
+
if (plans.size === 0 && warnings.length === 0)
|
|
99
|
+
warnings.push(`perf : aucun plan sous ${parent}, indicateurs du plan n/d`);
|
|
100
|
+
const deliveries = readProjectDeliveries(parent, [...plans.keys()]);
|
|
101
|
+
const failures = readProjectFailedEntries(parent, [...plans.keys()]);
|
|
102
|
+
const failedDeliveries = new Map([...failures].map(([name, entries]) => [name, entries.map((e) => e.day)]));
|
|
103
|
+
const result = computePerf({ journals, plans, cache, ...(sousCache ? { sousCache } : {}), deliveries, failedDeliveries, failures, prices, windows: perfWindows(today, since), today, since });
|
|
104
|
+
if (!prices)
|
|
105
|
+
warnings.push('perf : aucune grille de prix (cadence.yaml perf.prices), coût en euros n/d');
|
|
106
|
+
for (const [model, lots] of Object.entries(result.cost.withoutPrice))
|
|
107
|
+
warnings.push(`perf : modèle sans prix dans perf.prices : ${model}, coût n/d pour ${lots} lot(s)`);
|
|
108
|
+
if (sousWarning)
|
|
109
|
+
warnings.push(sousWarning);
|
|
110
|
+
warnings.forEach(warn);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* `cadence perf` (L225) : le tableau de bord du workflow, sans modèle ni écriture, lu dans <dossier>/.cadence/runs et les plans des projets du dossier.
|
|
115
|
+
* Une erreur de saisie ou de lecture sort avec le code 1 (un ⚠ imprimé, 0). `--since` y est un jour qui tronque les fenêtres, non l'instant de `corrections --since`.
|
|
116
|
+
*/
|
|
117
|
+
export function perf(rest, values, today, io) {
|
|
118
|
+
try {
|
|
119
|
+
if (rest.length > 1)
|
|
120
|
+
throw new RafError('usage : cadence perf [dossier] [--since date] [--json]');
|
|
121
|
+
if (values.since !== undefined && Number.isNaN(Date.parse(values.since)))
|
|
122
|
+
throw new RafError(`--since invalide : ${values.since} (une date ISO, ex. 2026-10-10T18:31Z)`);
|
|
123
|
+
const parent = resolve(io.cwd, rest[0] ?? '.');
|
|
124
|
+
const result = readPerf(parent, today, values.since, io.err);
|
|
125
|
+
if (values.json)
|
|
126
|
+
io.out(JSON.stringify(result, null, 2));
|
|
127
|
+
else
|
|
128
|
+
for (const l of renderPerf(result))
|
|
129
|
+
io.out(l);
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
if (!(e instanceof RafError))
|
|
134
|
+
throw e;
|
|
135
|
+
io.err(`raf: ${e.message}`);
|
|
136
|
+
return 1;
|
|
137
|
+
}
|
|
138
|
+
}
|
package/dist/cli-add.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// `raf add` : les contrôles des options et l'ajout du lot ou de la sous-tâche (sorti de cli.ts).
|
|
2
|
+
import { invalidResource, RafError } from './plan.js';
|
|
3
|
+
import { parseSet } from './plan-extras.js';
|
|
4
|
+
/** `raf add --parent <lot>` : une sous-tâche, qui ne porte aucune des options d'un lot. */
|
|
5
|
+
function addSubTask(plan, parent, values, title) {
|
|
6
|
+
if (values.id !== undefined)
|
|
7
|
+
throw new RafError('--id se pose sur un lot, pas sur une sous-tâche');
|
|
8
|
+
if (values.under !== undefined)
|
|
9
|
+
throw new RafError('--under se pose sur un lot, pas sur une sous-tâche : --parent crée une sous-tâche, --under un sous-lot');
|
|
10
|
+
for (const [flag, given] of [['screen', values.screen !== undefined], ['spec', values.spec], ['local', values.local], ['public', values.public !== undefined], ['every', values.every !== undefined]]) {
|
|
11
|
+
if (given)
|
|
12
|
+
throw new RafError(`--${flag} se pose sur un lot, pas sur une sous-tâche`);
|
|
13
|
+
}
|
|
14
|
+
if (values.uses !== undefined)
|
|
15
|
+
throw new RafError(`uses se pose sur un lot, pas sur une sous-tâche : ${parent}`);
|
|
16
|
+
if (values.set !== undefined)
|
|
17
|
+
throw new RafError(`--set se pose sur un lot, pas sur une sous-tâche : ${parent}`);
|
|
18
|
+
return plan.addTask(parent, title);
|
|
19
|
+
}
|
|
20
|
+
/** Ajoute un lot (ou, avec `--parent`, une sous-tâche) et rend son identifiant ; le plan n'est pas encore écrit. */
|
|
21
|
+
export function addLot(plan, values, title, today, refuseLongTitle) {
|
|
22
|
+
if (values.parent)
|
|
23
|
+
return addSubTask(plan, values.parent, values, title);
|
|
24
|
+
const estimate = values.estimate === undefined ? undefined : Number(values.estimate);
|
|
25
|
+
if (estimate !== undefined && !(estimate > 0))
|
|
26
|
+
throw new RafError(`estimation invalide : ${values.estimate}`);
|
|
27
|
+
const after = values.after ? values.after.split(',').map((s) => s.trim()).filter(Boolean) : undefined;
|
|
28
|
+
const every = values.every === undefined ? undefined : Number(values.every);
|
|
29
|
+
if (every !== undefined && !(Number.isInteger(every) && every > 0))
|
|
30
|
+
throw new RafError(`périodicité invalide : ${values.every} (un nombre entier de jours)`);
|
|
31
|
+
if (values.public !== undefined)
|
|
32
|
+
refuseLongTitle(values.public);
|
|
33
|
+
const uses = values.uses?.split(',').map((n) => n.trim()).filter(Boolean);
|
|
34
|
+
if (uses?.length === 0)
|
|
35
|
+
throw new RafError(invalidResource(''));
|
|
36
|
+
return plan.add(title, today, { estimate, quickwin: values.quickwin, visible: values.visible, screen: values.screen, spec: values.spec, local: values.local, public: values.public, every, after, uses, extras: parseSet(values.set), id: values.id, under: values.under });
|
|
37
|
+
}
|