devflow-cli 8.4.1__py3-none-any.whl → 8.5.0__py3-none-any.whl
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.
- devflow_cli/__init__.py +1 -1
- devflow_cli/commands/adaptive.py +52 -32
- devflow_cli/core/activity_contracts.py +12 -5
- devflow_cli/core/automatic_evidence.py +2 -1
- devflow_cli/core/contract_schemas.py +12 -4
- devflow_cli/core/feedback.py +1 -1
- devflow_cli/core/installer.py +1 -0
- devflow_cli/core/worklog.py +21 -9
- devflow_cli/resources/commands/devflow.md +10 -6
- devflow_cli/resources/templates/constitution-template.md +5 -5
- devflow_cli/resources/templates/plan-template.md +3 -3
- {devflow_cli-8.4.1.dist-info → devflow_cli-8.5.0.dist-info}/METADATA +3 -2
- {devflow_cli-8.4.1.dist-info → devflow_cli-8.5.0.dist-info}/RECORD +16 -16
- {devflow_cli-8.4.1.dist-info → devflow_cli-8.5.0.dist-info}/WHEEL +0 -0
- {devflow_cli-8.4.1.dist-info → devflow_cli-8.5.0.dist-info}/entry_points.txt +0 -0
- {devflow_cli-8.4.1.dist-info → devflow_cli-8.5.0.dist-info}/licenses/LICENSE +0 -0
devflow_cli/__init__.py
CHANGED
devflow_cli/commands/adaptive.py
CHANGED
|
@@ -57,7 +57,11 @@ from devflow_cli.core.automatic_evidence import (
|
|
|
57
57
|
stale_devflow_evidence,
|
|
58
58
|
stale_review_evidence,
|
|
59
59
|
)
|
|
60
|
-
from devflow_cli.core.worklog import
|
|
60
|
+
from devflow_cli.core.worklog import (
|
|
61
|
+
latest_remaining_risks,
|
|
62
|
+
refresh_worklog,
|
|
63
|
+
safe_refresh_worklog,
|
|
64
|
+
)
|
|
61
65
|
from devflow_cli.core.telemetry import append_record, telemetry_enabled, usage_payload
|
|
62
66
|
from devflow_cli.core.waivers import create_validation_waiver
|
|
63
67
|
from devflow_cli.core.remediation import (
|
|
@@ -755,8 +759,8 @@ def review_adaptive(
|
|
|
755
759
|
)
|
|
756
760
|
if result.advanced:
|
|
757
761
|
ok(
|
|
758
|
-
f"Revue humaine de {by.strip()} validee pour '{result.activity}'
|
|
759
|
-
f"Prochaine : {result.nextActivity}"
|
|
762
|
+
f"Revue humaine de {by.strip()} validee pour '{result.activity}' : "
|
|
763
|
+
f"activite completee. Prochaine : {result.nextActivity}"
|
|
760
764
|
)
|
|
761
765
|
return
|
|
762
766
|
safe_refresh_worklog(feature_dir)
|
|
@@ -928,18 +932,19 @@ def discard_attempt_command(
|
|
|
928
932
|
fail(str(exc))
|
|
929
933
|
console.print(f" Suite : devflow adaptive status {state.issueId}")
|
|
930
934
|
raise typer.Exit(int(ExitCode.BLOCKED)) from exc
|
|
935
|
+
remove_result_template(state.issueId, entry["activity"])
|
|
931
936
|
safe_refresh_worklog(feature_dir)
|
|
932
937
|
ok(
|
|
933
938
|
f"Tentative '{entry['activity']}' abandonnee (audit #{entry['sequence']}, "
|
|
934
939
|
f"archive : {entry['archivePath']})."
|
|
935
940
|
)
|
|
936
941
|
discarded = list(entry["discardedFiles"])
|
|
942
|
+
feature_path = feature_path_for(project_root_for_feature(feature_dir), feature_dir)
|
|
943
|
+
imputed = _imputed_inputs_of_discarded_attempt(feature_dir, entry, feature_path)
|
|
937
944
|
inputs = (
|
|
938
945
|
[
|
|
939
946
|
path for path in discarded
|
|
940
|
-
if _is_need_input(path,
|
|
941
|
-
project_root_for_feature(feature_dir), feature_dir
|
|
942
|
-
))
|
|
947
|
+
if _is_need_input(path, feature_path) and path in imputed
|
|
943
948
|
]
|
|
944
949
|
if entry["activity"] in DOCUMENT_ARTIFACTS
|
|
945
950
|
else []
|
|
@@ -962,6 +967,27 @@ def discard_attempt_command(
|
|
|
962
967
|
warn(f"Fichiers depuis la base non calcules : {entry['observationError']}")
|
|
963
968
|
|
|
964
969
|
|
|
970
|
+
def _imputed_inputs_of_discarded_attempt(
|
|
971
|
+
feature_dir: Path, entry: dict, feature_path: str | None
|
|
972
|
+
) -> set[str]:
|
|
973
|
+
"""Entrees du besoin que la fiche abandonnee imputait a un agent (lecture seule)."""
|
|
974
|
+
if feature_path is None:
|
|
975
|
+
return set()
|
|
976
|
+
card = (
|
|
977
|
+
feature_dir
|
|
978
|
+
/ entry["archivePath"]
|
|
979
|
+
/ activity_attempt_file(feature_dir, entry["activity"]).name
|
|
980
|
+
)
|
|
981
|
+
try:
|
|
982
|
+
payload = json.loads(card.read_text(encoding="utf-8"))
|
|
983
|
+
except (OSError, UnicodeDecodeError, json.JSONDecodeError):
|
|
984
|
+
return set()
|
|
985
|
+
names = payload.get("changedInputs") if isinstance(payload, dict) else None
|
|
986
|
+
if not isinstance(names, list):
|
|
987
|
+
return set()
|
|
988
|
+
return {f"{feature_path}/{name}" for name in names if isinstance(name, str)}
|
|
989
|
+
|
|
990
|
+
|
|
965
991
|
def _is_need_input(path: str, feature_path: str | None) -> bool:
|
|
966
992
|
"""Vrai pour need.md ou issue-*.md directement sous le dossier de la feature."""
|
|
967
993
|
if feature_path is None:
|
|
@@ -1305,7 +1331,7 @@ def _operational_status(
|
|
|
1305
1331
|
) -> dict:
|
|
1306
1332
|
result_file, result = _latest_activity_result(feature_dir, state.currentActivity)
|
|
1307
1333
|
blockers = result.get("blockers", []) if isinstance(result, dict) else []
|
|
1308
|
-
risks =
|
|
1334
|
+
risks = latest_remaining_risks(feature_dir, state.activities)
|
|
1309
1335
|
if not isinstance(blockers, list):
|
|
1310
1336
|
blockers = []
|
|
1311
1337
|
stale: set[str] = set()
|
|
@@ -1456,28 +1482,6 @@ def _latest_activity_result(
|
|
|
1456
1482
|
return None, {}
|
|
1457
1483
|
|
|
1458
1484
|
|
|
1459
|
-
def _remaining_risks(feature_dir: Path) -> list[str]:
|
|
1460
|
-
risks: list[str] = []
|
|
1461
|
-
for path in sorted(feature_dir.glob("*-result.json")):
|
|
1462
|
-
if not path.is_file():
|
|
1463
|
-
continue
|
|
1464
|
-
try:
|
|
1465
|
-
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
1466
|
-
except (OSError, UnicodeDecodeError, json.JSONDecodeError):
|
|
1467
|
-
continue
|
|
1468
|
-
values = (
|
|
1469
|
-
payload.get("remainingRisks", [])
|
|
1470
|
-
if isinstance(payload, dict)
|
|
1471
|
-
else []
|
|
1472
|
-
)
|
|
1473
|
-
if not isinstance(values, list):
|
|
1474
|
-
continue
|
|
1475
|
-
for value in values:
|
|
1476
|
-
if isinstance(value, str) and value not in risks:
|
|
1477
|
-
risks.append(value)
|
|
1478
|
-
return risks
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
1485
|
def _fallback_agents(agent: str) -> list[str]:
|
|
1482
1486
|
"""Autres agents pris en charge, pour reprendre pendant une indisponibilite."""
|
|
1483
1487
|
current = get_agent_adapter(agent).name
|
|
@@ -1724,12 +1728,28 @@ def _print_recovery_steps(recovery: dict) -> None:
|
|
|
1724
1728
|
)
|
|
1725
1729
|
|
|
1726
1730
|
|
|
1731
|
+
# Noms de `run --agent` traduits en noms que `devflow check --ai` accepte.
|
|
1732
|
+
CHECK_AGENT_NAMES = {"claude": "claude-code", "claude-code": "claude-code", "codex": "codex"}
|
|
1733
|
+
|
|
1734
|
+
|
|
1735
|
+
def _print_check_command(agent: str, comment: str) -> None:
|
|
1736
|
+
"""Propose `devflow check --ai` avec un nom que cette commande accepte."""
|
|
1737
|
+
name = CHECK_AGENT_NAMES.get(agent.casefold())
|
|
1738
|
+
if name is not None:
|
|
1739
|
+
console.print(f" devflow check --ai {name} --live{comment}")
|
|
1740
|
+
|
|
1741
|
+
|
|
1727
1742
|
def _print_run_recovery(
|
|
1728
1743
|
issue_id: str, reason: str, activity: str, agent: str, feature_dir: Path,
|
|
1729
1744
|
*, recovery: dict | None = None,
|
|
1730
1745
|
) -> None:
|
|
1731
1746
|
console.print(" Reprise guidee :")
|
|
1732
|
-
|
|
1747
|
+
status_command = f"devflow adaptive status {issue_id}"
|
|
1748
|
+
status_commands = {status_command, f"devflow adaptive status {shlex.quote(issue_id)}"}
|
|
1749
|
+
if recovery is None or not any(
|
|
1750
|
+
step["command"] in status_commands for step in recovery["commands"]
|
|
1751
|
+
):
|
|
1752
|
+
console.print(f" {status_command}")
|
|
1733
1753
|
if reason in {"agent-failed", "agent-failed-after-mutation", "agent-unavailable"}:
|
|
1734
1754
|
console.print(
|
|
1735
1755
|
" Trace agent : "
|
|
@@ -1742,7 +1762,7 @@ def _print_run_recovery(
|
|
|
1742
1762
|
return
|
|
1743
1763
|
if reason == "agent-unavailable":
|
|
1744
1764
|
console.print(" Agent indisponible : l'activite reste courante et rien n'est invalide.")
|
|
1745
|
-
|
|
1765
|
+
_print_check_command(agent, " # verifier le retour de l'agent")
|
|
1746
1766
|
console.print(f" devflow adaptive reconcile {issue_id} # si l'agent avait deja produit son travail")
|
|
1747
1767
|
for fallback in _fallback_agents(agent):
|
|
1748
1768
|
console.print(f" devflow adaptive run {issue_id} --agent {fallback}")
|
|
@@ -1787,7 +1807,7 @@ def _print_run_recovery(
|
|
|
1787
1807
|
)
|
|
1788
1808
|
if reason == "agent-failed":
|
|
1789
1809
|
console.print(f" devflow adaptive reconcile {issue_id} # si la trace contient une reponse finale")
|
|
1790
|
-
|
|
1810
|
+
_print_check_command(agent, " # verifier l'agent")
|
|
1791
1811
|
elif reason == "state-changed":
|
|
1792
1812
|
console.print(" Rechargez le statut avant toute nouvelle execution.")
|
|
1793
1813
|
return
|
|
@@ -555,8 +555,8 @@ def parse_activity_result(payload: dict[str, Any], contract: ActivityContract) -
|
|
|
555
555
|
for removed in REMOVED_RESULT_FIELDS:
|
|
556
556
|
if removed in payload:
|
|
557
557
|
raise ValueError(
|
|
558
|
-
f"champ {removed} supprime en devflow 7.0 ;
|
|
559
|
-
"
|
|
558
|
+
f"champ {removed} supprime en devflow 7.0 ; relancer l'activite, qui "
|
|
559
|
+
"produit un nouveau contrat sans ce champ"
|
|
560
560
|
)
|
|
561
561
|
expected_fields = {
|
|
562
562
|
"schemaVersion",
|
|
@@ -847,6 +847,13 @@ def _parse_checks(value: Any) -> list[ActivityCheck]:
|
|
|
847
847
|
return checks
|
|
848
848
|
|
|
849
849
|
|
|
850
|
+
_EVIDENCE_SHAPE_MESSAGE = (
|
|
851
|
+
"preuve invalide : chaque element de evidence doit etre un objet aux cles "
|
|
852
|
+
"id (texte non vide, unique), outcome (passed ou failed) et report (texte non vide), "
|
|
853
|
+
"sans autre cle"
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
|
|
850
857
|
def _parse_evidence(value: Any) -> list[ActivityEvidence]:
|
|
851
858
|
if not isinstance(value, list):
|
|
852
859
|
raise ValueError("evidence doit etre une liste")
|
|
@@ -854,9 +861,9 @@ def _parse_evidence(value: Any) -> list[ActivityEvidence]:
|
|
|
854
861
|
seen: set[str] = set()
|
|
855
862
|
for item in value:
|
|
856
863
|
if not isinstance(item, dict):
|
|
857
|
-
raise ValueError(
|
|
864
|
+
raise ValueError(_EVIDENCE_SHAPE_MESSAGE)
|
|
858
865
|
if set(item) != {"id", "outcome", "report"}:
|
|
859
|
-
raise ValueError(
|
|
866
|
+
raise ValueError(_EVIDENCE_SHAPE_MESSAGE)
|
|
860
867
|
evidence_id, outcome, report = item.get("id"), item.get("outcome"), item.get("report")
|
|
861
868
|
if (
|
|
862
869
|
not isinstance(evidence_id, str)
|
|
@@ -866,7 +873,7 @@ def _parse_evidence(value: Any) -> list[ActivityEvidence]:
|
|
|
866
873
|
or not isinstance(report, str)
|
|
867
874
|
or not report.strip()
|
|
868
875
|
):
|
|
869
|
-
raise ValueError(
|
|
876
|
+
raise ValueError(_EVIDENCE_SHAPE_MESSAGE)
|
|
870
877
|
seen.add(evidence_id)
|
|
871
878
|
evidence.append(ActivityEvidence(evidence_id, outcome, report.strip()))
|
|
872
879
|
return evidence
|
|
@@ -545,7 +545,7 @@ def run_automatic_verification(
|
|
|
545
545
|
preparation_payload, prepare_contract
|
|
546
546
|
)
|
|
547
547
|
if preparation.status != "completed":
|
|
548
|
-
raise ValueError("
|
|
548
|
+
raise ValueError(f"{plan_activity}-result n'est pas complete")
|
|
549
549
|
planned_prefix = "PREP" if state.profile == "standard" else "PLAN"
|
|
550
550
|
planned_commands = []
|
|
551
551
|
for item in preparation.validationPlan:
|
|
@@ -1011,6 +1011,7 @@ def _inspect_diff(project_root: Path, changed_files: list[str]) -> dict:
|
|
|
1011
1011
|
if missing:
|
|
1012
1012
|
findings.append(
|
|
1013
1013
|
"fichiers implementes absents du diff courant : " + ", ".join(missing)
|
|
1014
|
+
+ " ; cause possible : lot commite entre implement et verify"
|
|
1014
1015
|
)
|
|
1015
1016
|
|
|
1016
1017
|
diff_result = run_verifications(
|
|
@@ -38,7 +38,13 @@ class LegacySchemaError(ValueError):
|
|
|
38
38
|
) -> None:
|
|
39
39
|
self.path = path
|
|
40
40
|
self.version = version
|
|
41
|
-
if producer == "
|
|
41
|
+
if producer == "no-base":
|
|
42
|
+
message = (
|
|
43
|
+
f"{path} : schemaVersion {version} : l'etat n'a pas de base d'activite "
|
|
44
|
+
"valide (activityBase absente ou nulle) ; aucune base n'est reconstruite ; "
|
|
45
|
+
"recreez le parcours"
|
|
46
|
+
)
|
|
47
|
+
elif producer == "7.x":
|
|
42
48
|
message = (
|
|
43
49
|
f"{path} : schemaVersion {version} : parcours cree avec devflow 7.x ; "
|
|
44
50
|
f"terminez-le avec devflow 7.x ({install}) ou recreez-le ; aucune migration"
|
|
@@ -46,8 +52,8 @@ class LegacySchemaError(ValueError):
|
|
|
46
52
|
else:
|
|
47
53
|
message = (
|
|
48
54
|
f"{path} : schemaVersion {version} produit par devflow {producer}, non "
|
|
49
|
-
f"lisible par devflow 8 ; terminer
|
|
50
|
-
f"({install})"
|
|
55
|
+
f"lisible par devflow 8 ; le terminer ou le consulter avec "
|
|
56
|
+
f"devflow {producer} ({install})"
|
|
51
57
|
)
|
|
52
58
|
super().__init__(message)
|
|
53
59
|
|
|
@@ -58,7 +64,9 @@ WORKFLOW_STATE_7X_VERSION = 5
|
|
|
58
64
|
|
|
59
65
|
|
|
60
66
|
def legacy_workflow_state_error(path: Path, version: int) -> LegacySchemaError:
|
|
61
|
-
"""Refus de rupture d'un etat adaptatif
|
|
67
|
+
"""Refus de rupture d'un etat adaptatif sans base (schema 5 de 7.x, ou schema 6 sans base)."""
|
|
68
|
+
if version != WORKFLOW_STATE_7X_VERSION:
|
|
69
|
+
return LegacySchemaError(path, version, producer="no-base")
|
|
62
70
|
return LegacySchemaError(path, version, producer="7.x", install=LEGACY_7X_INSTALL)
|
|
63
71
|
|
|
64
72
|
|
devflow_cli/core/feedback.py
CHANGED
|
@@ -442,7 +442,7 @@ def render_export(
|
|
|
442
442
|
treated: dict[str, dict[str, Any]],
|
|
443
443
|
home: Path,
|
|
444
444
|
) -> str:
|
|
445
|
-
"""Markdown
|
|
445
|
+
"""Markdown de l'export (format de l'ancien FEEDBACK.md, fichier supprime) ; chemins normalises, structure neutralisee."""
|
|
446
446
|
out = ["# Retours d'usage devflow — export", ""]
|
|
447
447
|
if not entries:
|
|
448
448
|
out += ["Aucun retour.", ""]
|
devflow_cli/core/installer.py
CHANGED
devflow_cli/core/worklog.py
CHANGED
|
@@ -31,6 +31,7 @@ def refresh_worklog(feature_dir: Path) -> Path:
|
|
|
31
31
|
|
|
32
32
|
result = _read_json(activity_result_file(feature_dir, "implement"), required=False)
|
|
33
33
|
review = _read_json(activity_result_file(feature_dir, "review"), required=False)
|
|
34
|
+
risks = latest_remaining_risks(feature_dir, state.activities)
|
|
34
35
|
rollback = _read_json(
|
|
35
36
|
activity_result_file(feature_dir, "rollback-check"), required=False
|
|
36
37
|
)
|
|
@@ -71,11 +72,28 @@ def refresh_worklog(feature_dir: Path) -> Path:
|
|
|
71
72
|
execution,
|
|
72
73
|
observation_warnings,
|
|
73
74
|
approvals,
|
|
75
|
+
risks,
|
|
74
76
|
)
|
|
75
77
|
_write_atomic(worklog_file, managed + manual)
|
|
76
78
|
return worklog_file
|
|
77
79
|
|
|
78
80
|
|
|
81
|
+
def latest_remaining_risks(feature_dir: Path, activities: list[str]) -> list[str]:
|
|
82
|
+
"""Risques du resultat le plus recent de la route (dernier lisible), sans doublon."""
|
|
83
|
+
for activity in reversed(activities):
|
|
84
|
+
payload = _read_json(activity_result_file(feature_dir, activity), required=False)
|
|
85
|
+
if not payload:
|
|
86
|
+
continue
|
|
87
|
+
values = payload.get("remainingRisks", [])
|
|
88
|
+
risks: list[str] = []
|
|
89
|
+
if isinstance(values, list):
|
|
90
|
+
for value in values:
|
|
91
|
+
if isinstance(value, str) and value not in risks:
|
|
92
|
+
risks.append(value)
|
|
93
|
+
return risks
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
|
|
79
97
|
def safe_refresh_worklog(feature_dir: Path) -> Path | None:
|
|
80
98
|
"""Actualise la vue derivee sans compromettre une transition deja validee."""
|
|
81
99
|
try:
|
|
@@ -99,6 +117,7 @@ def _render_managed(
|
|
|
99
117
|
execution: dict[str, Any],
|
|
100
118
|
observation_warnings: list[str] | None = None,
|
|
101
119
|
approvals: list[str] | None = None,
|
|
120
|
+
risks: list[str] | None = None,
|
|
102
121
|
) -> str:
|
|
103
122
|
issue_id = state["issueId"]
|
|
104
123
|
summary = assessment.get("summary")
|
|
@@ -117,14 +136,7 @@ def _render_managed(
|
|
|
117
136
|
checks.extend(_verification_lines(security, {}))
|
|
118
137
|
checks.extend(_activity_evidence_lines(review))
|
|
119
138
|
checks.extend(_activity_evidence_lines(rollback))
|
|
120
|
-
|
|
121
|
-
risks = (
|
|
122
|
-
risk_source.get("remainingRisks", [])
|
|
123
|
-
if isinstance(risk_source, dict)
|
|
124
|
-
else []
|
|
125
|
-
)
|
|
126
|
-
if not isinstance(risks, list) or not all(isinstance(item, str) for item in risks):
|
|
127
|
-
risks = []
|
|
139
|
+
risks = risks or []
|
|
128
140
|
|
|
129
141
|
lines = [
|
|
130
142
|
f"# Worklog — {issue_id}",
|
|
@@ -343,7 +355,7 @@ def _manual_suffix(path: Path) -> str:
|
|
|
343
355
|
def _read_json(path: Path, *, required: bool = True) -> dict[str, Any]:
|
|
344
356
|
try:
|
|
345
357
|
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
346
|
-
except (OSError, json.JSONDecodeError):
|
|
358
|
+
except (OSError, UnicodeDecodeError, json.JSONDecodeError):
|
|
347
359
|
if required:
|
|
348
360
|
raise ValueError(f"artefact JSON invalide ou absent : {path.name}")
|
|
349
361
|
return {}
|
|
@@ -12,13 +12,17 @@ interpréter de `state.json`.
|
|
|
12
12
|
1. Lire `$ARGUMENTS` et extraire l'identifiant de feature, l'agent demandé
|
|
13
13
|
(`claude` par défaut) et la cible `--until` (`done` par défaut).
|
|
14
14
|
2. Exécuter `devflow status ISSUE_ID`.
|
|
15
|
-
3. Si la feature n'existe pas,
|
|
16
|
-
`devflow feature ISSUE_ID
|
|
17
|
-
|
|
15
|
+
3. Si la feature n'existe pas, exécuter
|
|
16
|
+
`devflow feature ISSUE_ID --short-name NOM` (nom court tiré du titre de
|
|
17
|
+
l'issue) et annoncer la branche et le dossier créés.
|
|
18
|
+
4. Si la feature n'est pas encore évaluée et que `need.md` n'existe pas dans
|
|
19
|
+
son dossier, le rédiger à partir de l'issue et de la demande. En cas de
|
|
20
|
+
doute sur le besoin, poser la question avant de l'écrire.
|
|
21
|
+
5. Si elle n'est pas évaluée, demander les signaux nécessaires puis lancer
|
|
18
22
|
`devflow assess ISSUE_ID` avec des options explicites.
|
|
19
|
-
|
|
23
|
+
6. Si elle est évaluée mais non démarrée, lancer
|
|
20
24
|
`devflow adaptive start ISSUE_ID`.
|
|
21
|
-
|
|
25
|
+
7. Enchaîner les activités avec
|
|
22
26
|
`devflow adaptive run ISSUE_ID --agent AGENT --until CIBLE`.
|
|
23
27
|
`--until` inclut l'activité ciblée.
|
|
24
28
|
La CLI s'arrête au premier refus. Distinguer alors deux cas :
|
|
@@ -31,7 +35,7 @@ interpréter de `state.json`.
|
|
|
31
35
|
CLI plante (traceback, commande inconnue), sortir du parcours : terminer
|
|
32
36
|
la feature hors Devflow, clôturer par un `bilan` dont `--what` indique
|
|
33
37
|
l'abandon et sa cause, et indiquer cette cause dans le compte rendu.
|
|
34
|
-
|
|
38
|
+
8. Afficher ensuite `devflow adaptive status ISSUE_ID`.
|
|
35
39
|
|
|
36
40
|
## Retours d'usage
|
|
37
41
|
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
|
|
47
47
|
## Gates obligatoires
|
|
48
48
|
|
|
49
|
-
Les gates sont vérifiées
|
|
49
|
+
Les gates sont vérifiées avant l'implémentation. Une violation non justifiée est BLOQUANTE.
|
|
50
50
|
|
|
51
51
|
### Gate 1 — Simplicité
|
|
52
52
|
|
|
@@ -64,7 +64,7 @@ Les gates sont vérifiées à chaque `/devflow.plan`. Une violation non justifi
|
|
|
64
64
|
| Conventions | Suivre celles du codebase existant |
|
|
65
65
|
| Modèle de données | Représentation unique, pas de duplication |
|
|
66
66
|
|
|
67
|
-
### Gate 3 — Tests
|
|
67
|
+
### Gate 3 — Tests
|
|
68
68
|
|
|
69
69
|
| Critère | Règle |
|
|
70
70
|
|---------|-------|
|
|
@@ -80,6 +80,6 @@ Les gates sont vérifiées à chaque `/devflow.plan`. Une violation non justifi
|
|
|
80
80
|
|
|
81
81
|
## Suivi des dérogations
|
|
82
82
|
|
|
83
|
-
| Date | Article | Dérogation | Justification |
|
|
84
|
-
|
|
85
|
-
| | | | |
|
|
83
|
+
| Date | Article | Dérogation | Justification | Échéance |
|
|
84
|
+
|------|---------|------------|---------------|----------|
|
|
85
|
+
| | | | | |
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
|------|--------|-------------|
|
|
15
15
|
| Simplicité | | |
|
|
16
16
|
| Patterns existants | | |
|
|
17
|
-
| Tests
|
|
17
|
+
| Tests | | |
|
|
18
18
|
|
|
19
19
|
### Dérogations
|
|
20
20
|
|
|
21
21
|
<!-- Si une gate est violée, justifier ici. Sans justification = BLOQUANT -->
|
|
22
22
|
|
|
23
|
-
| Article | Dérogation | Justification |
|
|
24
|
-
|
|
23
|
+
| Article | Dérogation | Justification | Échéance |
|
|
24
|
+
|---------|------------|---------------|----------|
|
|
25
25
|
|
|
26
26
|
### Complexity Tracking
|
|
27
27
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: devflow-cli
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.5.0
|
|
4
4
|
Summary: Spec-Driven Development workflow CLI
|
|
5
5
|
Project-URL: Homepage, https://github.com/sopequenoteck/devflow
|
|
6
6
|
Project-URL: Repository, https://github.com/sopequenoteck/devflow
|
|
@@ -65,7 +65,8 @@ devflow init --ai codex
|
|
|
65
65
|
Après chaque mise à jour de la CLI, lancer `devflow upgrade` : il met à jour
|
|
66
66
|
les commandes `/devflow` installées dans `~/.claude`, que l'installation de la
|
|
67
67
|
CLI ne touche pas. `--refresh` évite qu'`uv` ignore une version tout juste
|
|
68
|
-
publiée.
|
|
68
|
+
publiée. Juste après une publication, l'installation peut échouer pendant une
|
|
69
|
+
à quatre minutes, le temps que l'index se propage : réessayer. Tant que les commandes installées diffèrent de la CLI,
|
|
69
70
|
`devflow status` affiche un avertissement avec cette commande ; rien n'est
|
|
70
71
|
modifié sans vous.
|
|
71
72
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
devflow_cli/__init__.py,sha256=
|
|
1
|
+
devflow_cli/__init__.py,sha256=gN1yusVBmKEDSmEtb_g_BPIHNRFR3EDm3BA7mMbPXtQ,925
|
|
2
2
|
devflow_cli/cli.py,sha256=0_SmhJX_k7OQfp-jNV_jq9EOgWY4rTjStGIoc9pH6Ec,2686
|
|
3
3
|
devflow_cli/adapters/__init__.py,sha256=RkjHPU2Lp4hSVIq2iOmyTrv2zYU9NuiIF9wEMWzLdMI,377
|
|
4
4
|
devflow_cli/adapters/agents.py,sha256=01ZQb96koWMKH-mma3AtttdEjNEbu3mM89vFetD9iR8,29689
|
|
5
5
|
devflow_cli/adapters/linear.py,sha256=HE1YzjBxwoj2jJ_3bmh9Pn_7Mt5u3EOLk891vMXBEj4,8700
|
|
6
6
|
devflow_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
devflow_cli/commands/adaptive.py,sha256=
|
|
7
|
+
devflow_cli/commands/adaptive.py,sha256=efDFrh9GOfnISzJwc5utP4KchhjyjxagytIwBgQ7N2Q,72537
|
|
8
8
|
devflow_cli/commands/assess.py,sha256=V-E3ft41ljCrqp2WLjR1Dqw-JtswcqsK2nD1-euNbNE,5655
|
|
9
9
|
devflow_cli/commands/check.py,sha256=hsfeOg9yDOTAIvNm6XhRmTIFgv7w9wC8zvrwm4ojfZs,6923
|
|
10
10
|
devflow_cli/commands/context.py,sha256=hwTitOHxIpJMx1X8R1jHHy9CpE4HVZpJKkyeOujxAio,6246
|
|
@@ -24,20 +24,20 @@ devflow_cli/commands/usage.py,sha256=ehH1AH5J-XfkebFx6qLBwpKN-MOoX2uUbq4LejBpNsk
|
|
|
24
24
|
devflow_cli/commands/verify.py,sha256=GBFCfsN-xWyROCcngOANz-3ftNVeNZ4Y_JeWFo2muwE,4139
|
|
25
25
|
devflow_cli/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
devflow_cli/core/activity_base.py,sha256=YId_tUxiL6TYmnVpG2VYGkA31nPjIrskx6Dof27XR6E,16696
|
|
27
|
-
devflow_cli/core/activity_contracts.py,sha256=
|
|
27
|
+
devflow_cli/core/activity_contracts.py,sha256=ui-9JYwtZYcjEVppgcD5YNW_O7gkxxCkfXlIevLrJ-c,39295
|
|
28
28
|
devflow_cli/core/adaptive_runner.py,sha256=BU3LZzM2f7dNvgKm_ul0CZYIs8z7WN1hM--TrF9o07s,48852
|
|
29
29
|
devflow_cli/core/adaptive_workflow.py,sha256=F4uV5okQpFidZmneVZwW52XAO7gAmtuBAqBO9K9q7Uk,12173
|
|
30
30
|
devflow_cli/core/agent_failure.py,sha256=iX4q249lAHeVdD_ZF1aObmU-FX6sjfYP9dfLSnWGAD0,3771
|
|
31
31
|
devflow_cli/core/agent_run_marker.py,sha256=QW3ScA2ZpqfQdUo5ANTbenDVyY2iHpB_wTaN6VUKlzc,3451
|
|
32
32
|
devflow_cli/core/assessment.py,sha256=ACtJbXq1qZjVfeERjy8Qg6-ioLfSMUBVQm2l-mtGwgI,11522
|
|
33
|
-
devflow_cli/core/automatic_evidence.py,sha256=
|
|
33
|
+
devflow_cli/core/automatic_evidence.py,sha256=m84Pa8DL4tO-Jzcvv6BNCX7g4zPbczFruGlg5Y2lrUs,42420
|
|
34
34
|
devflow_cli/core/catalog.py,sha256=nLPc78JkbISaSxJnHlWiMi_lvepaumbfwcjgLuVy7Zc,3102
|
|
35
|
-
devflow_cli/core/contract_schemas.py,sha256
|
|
35
|
+
devflow_cli/core/contract_schemas.py,sha256=TOGXwpNmm_MEzDkxR1wYvcCqASv1j4C1vxT6RjDFMiM,6744
|
|
36
36
|
devflow_cli/core/docgen.py,sha256=47JoVDWetkcctgHwJZC67AVebOKgl10VB9hZ3Xr63mo,3908
|
|
37
|
-
devflow_cli/core/feedback.py,sha256=
|
|
37
|
+
devflow_cli/core/feedback.py,sha256=4LJk-stAz_1toVVK4kGlWs6yUIQEEjjXVLglDXc37JU,18965
|
|
38
38
|
devflow_cli/core/git.py,sha256=gsDfT-hDNtMQFRUl1WzZSqltrdwZQ2BJBcCIh4TwDkA,2780
|
|
39
39
|
devflow_cli/core/hooks.py,sha256=nFPaziFJgeZgAPKPxbTibrB_0MJAST3cAmg3gtcvDBQ,8067
|
|
40
|
-
devflow_cli/core/installer.py,sha256=
|
|
40
|
+
devflow_cli/core/installer.py,sha256=fmfaJs5ApkhRmQi1B4vz52iAHBtqgeyBoAnE8CXAzJI,6303
|
|
41
41
|
devflow_cli/core/manifest.py,sha256=h058_m5-HJrYsNUdgfwnKts2riHiYPXUwMzPImHOs2g,7023
|
|
42
42
|
devflow_cli/core/operational.py,sha256=11iLTbyZF0ZjYTQ5HDmVCaw4bpbXpLtH2v9f1WQOrYg,1228
|
|
43
43
|
devflow_cli/core/redaction.py,sha256=o_1MDtCyvRLrk_dWlV6_7E6_mPVB1B5a0cCfTBj9j0Q,1973
|
|
@@ -53,7 +53,7 @@ devflow_cli/core/validation_approvals.py,sha256=3AHOpnW3BtY3BQrRZr38K7VNYRvbxGNh
|
|
|
53
53
|
devflow_cli/core/validators.py,sha256=fREjEx2fBW36q3vB6WJNqoGUM9ejYlQUXie5A9lNuPs,7440
|
|
54
54
|
devflow_cli/core/verification.py,sha256=rvbdAdLyrZYb20oauti7D8nV5TDFBIf_8-hqLahfRag,23576
|
|
55
55
|
devflow_cli/core/waivers.py,sha256=9XXzMmoWCn-2Dc3486WhZwtGJQLQPXjP0qAaLuL_3qU,13299
|
|
56
|
-
devflow_cli/core/worklog.py,sha256=
|
|
56
|
+
devflow_cli/core/worklog.py,sha256=i4zgJIlS1OSIBMrhT1AKU97bh7IHAhNYbnr4eJVGv_Y,14692
|
|
57
57
|
devflow_cli/core/worktree_snapshot.py,sha256=Q2_KTkhJTGvdM6Ay4RK9OHkyPEMrHQRTb-f-Eptek-8,5255
|
|
58
58
|
devflow_cli/schemas/activity-contract-v4.schema.json,sha256=IeduWDiJ5IV6EL7FDZWnHBjzyUt1uwig2rB-SMbcpOU,2006
|
|
59
59
|
devflow_cli/schemas/activity-result-v1.schema.json,sha256=xQuyD5fwHGIdeoJoDJxJn26qBGJX74lD6Dc5gi5CLQo,2772
|
|
@@ -67,7 +67,7 @@ devflow_cli/utils/strings.py,sha256=bfFXCwBDnFxshgZOYfy6mtysnjiaWO1wFnqBf7HLg4o,
|
|
|
67
67
|
devflow_cli/resources/commands/devflow.context.md,sha256=SdvZ286cJKkgQIKYwnjd30v5zDeEXTFBhpEEsEOInoc,2048
|
|
68
68
|
devflow_cli/resources/commands/devflow.extension.md,sha256=w7hoFfxRS0y-3rsvUmQDx1fvZhtE9KhANmRgFDwYVaA,2373
|
|
69
69
|
devflow_cli/resources/commands/devflow.init.md,sha256=wplv3IiJrNkUoPXGZtOw9k6m3dzXBQDYAcPPN8d3EF8,2011
|
|
70
|
-
devflow_cli/resources/commands/devflow.md,sha256=
|
|
70
|
+
devflow_cli/resources/commands/devflow.md,sha256=EZ1yo5Hx7lnHX8G1EB0U6V4wNJ4kzb8nSmXI2PneY7s,2984
|
|
71
71
|
devflow_cli/resources/commands/devflow.regen.md,sha256=yjbMNAt6k0y6M9Y5HZDhCRgj_tb33Om6_-5OijmD5Bw,1169
|
|
72
72
|
devflow_cli/resources/commands/devflow.status.md,sha256=tqjQ1IboU99ztG6C4nunBHGJCvMn3qDqx3AoF4SPjCk,338
|
|
73
73
|
devflow_cli/resources/extensions/EXTENSION-API-REFERENCE.md,sha256=1wuAV-K8WI0BJ6SJ0HViieSHe3zazlfHemkKDT7bbKM,4478
|
|
@@ -76,12 +76,12 @@ devflow_cli/resources/extensions/catalog.json,sha256=O2Uqgkvudf6JXIMQFKEk8d1MuA1
|
|
|
76
76
|
devflow_cli/resources/extensions/template/example-command.md,sha256=3qlpEOzbYQdL79iucaZ5wU5zDX2Q_pxz0Fg9jSF-B84,1020
|
|
77
77
|
devflow_cli/resources/extensions/template/example-extension.json,sha256=Az9Ihq3ANlWmUzWZDfttaUCUmfesOFH4IbYq23CmWC4,416
|
|
78
78
|
devflow_cli/resources/templates/agent-context-template.md,sha256=D5wd2hcxGrVR0sIE2JEDUPcsFHoZWJHf5m8LuD12J9Y,520
|
|
79
|
-
devflow_cli/resources/templates/constitution-template.md,sha256=
|
|
79
|
+
devflow_cli/resources/templates/constitution-template.md,sha256=uSzNuwF-xIRx8cHt9gloynRjNgDGmnN5z2Xck6fCOsI,2102
|
|
80
80
|
devflow_cli/resources/templates/contracts-template.md,sha256=dx7slFV_5mG6BJ-OfQ8rfRXSid-ZY_NKmVjllmN2fAA,1768
|
|
81
81
|
devflow_cli/resources/templates/data-model-template.md,sha256=RliumtOY20L-Z0grx7tfvg9pbulGDKR6EN7k-7zKNUU,1015
|
|
82
82
|
devflow_cli/resources/templates/devflow-hooks-template.yml,sha256=dGjsh4PLEkCjwNWWhMOI0F7Md-Edu3oYkHtmaPLlg3o,1205
|
|
83
83
|
devflow_cli/resources/templates/extension-manifest-template.json,sha256=GVCE2ZFObN93dlMRyf7UUawpmy5GSu11Yj11UPfqTBc,226
|
|
84
|
-
devflow_cli/resources/templates/plan-template.md,sha256=
|
|
84
|
+
devflow_cli/resources/templates/plan-template.md,sha256=0mZ0hBCbP8mYdAjzDzrJY4bZDhAXvJNHynm_mKu6Vmk,2625
|
|
85
85
|
devflow_cli/resources/templates/quickstart-template.md,sha256=LkB7FfO7ie0-wD5NltR7BGDudNVOW5_huDvha6NcOsg,1226
|
|
86
86
|
devflow_cli/resources/templates/spec-template.md,sha256=XiUSkj-GkzhN-kHO4p5i1u1thbSCgK1x-CIAHSso3Go,1918
|
|
87
87
|
devflow_cli/resources/templates/tasks-template.md,sha256=PnWRAeok9MGTj1F30UeYu0njru7RsAOdj16Cgyd45Ww,2525
|
|
@@ -90,8 +90,8 @@ devflow_cli/resources/templates/contracts/api-template.md,sha256=rC5v7JBz71PznCn
|
|
|
90
90
|
devflow_cli/resources/templates/contracts/components-template.md,sha256=XIWfEXfC72sNmL5OA3V-7xIywkNDU2DBPLfyiagOWcs,484
|
|
91
91
|
devflow_cli/resources/templates/contracts/interfaces-template.md,sha256=kf8B7b6HsmN7jyRlLNU7RnZl0rTMlI5s97yVRyI-6yc,336
|
|
92
92
|
devflow_cli/resources/templates/contracts/services-template.md,sha256=cFafKCyLmq_a7aR7G0LXAaMO2z4Fuflq6iKdxfVg0vg,477
|
|
93
|
-
devflow_cli-8.
|
|
94
|
-
devflow_cli-8.
|
|
95
|
-
devflow_cli-8.
|
|
96
|
-
devflow_cli-8.
|
|
97
|
-
devflow_cli-8.
|
|
93
|
+
devflow_cli-8.5.0.dist-info/METADATA,sha256=pdYiU-6bFeQyZOYpdXA3GrR9Ha99afor2pZcjB9__oU,9982
|
|
94
|
+
devflow_cli-8.5.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
95
|
+
devflow_cli-8.5.0.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
|
|
96
|
+
devflow_cli-8.5.0.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
|
|
97
|
+
devflow_cli-8.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|