devflow-cli 8.1.0__py3-none-any.whl → 8.2.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """devflow CLI — workflow de développement adaptatif."""
2
2
 
3
- VERSION = "8.1.0"
3
+ VERSION = "8.2.0"
4
4
 
5
5
  SUPPORTED_AGENTS = ["claude-code", "codex", "cursor"]
6
6
 
@@ -22,6 +22,24 @@ from devflow_cli.adapters.linear import environment_without_linear_key
22
22
  PermissionMode = Literal["auto", "acceptEdits", "manual"]
23
23
  SandboxMode = Literal["read-only", "workspace-write"]
24
24
 
25
+ # L'agent a termine sans erreur (code 0) mais sa sortie structuree manque ou n'est
26
+ # pas le JSON attendu. Claude porte le diagnostic dedie ; Codex et Cursor gardent
27
+ # leurs valeurs detaillees, traitees comme la meme famille par la reprise guidee.
28
+ STRUCTURED_OUTPUT_MISSING = "structured-output-missing"
29
+ MISSING_STRUCTURED_OUTPUT_DIAGNOSTICS = frozenset({
30
+ STRUCTURED_OUTPUT_MISSING,
31
+ "final-output-empty",
32
+ "final-output-absent",
33
+ "final-output-invalid-json",
34
+ })
35
+ STRUCTURED_OUTPUT_MISSING_MESSAGE = (
36
+ "l'agent a termine sans erreur mais sa sortie structuree manque "
37
+ "ou n'est pas le JSON attendu"
38
+ )
39
+ # Impose le premier plan aux sous-agents de l'agent Claude d'une activite ; desactive
40
+ # aussi ses commandes shell en arriere-plan.
41
+ CLAUDE_FOREGROUND_ENV = "CLAUDE_CODE_DISABLE_BACKGROUND_TASKS"
42
+
25
43
  _SAFE_DIAGNOSTIC_MESSAGES = {
26
44
  "final-output-empty": "--output-last-message est vide",
27
45
  "final-output-absent": "--output-last-message est absent",
@@ -165,7 +183,7 @@ class AgentAdapter(ABC):
165
183
  capture_output=True,
166
184
  stdin=subprocess.DEVNULL,
167
185
  shell=False,
168
- env=environment_without_linear_key(),
186
+ env=self.structured_environment(),
169
187
  )
170
188
  execution = AgentExecution(
171
189
  result.returncode,
@@ -223,7 +241,9 @@ class AgentAdapter(ABC):
223
241
  "Sortie structuree invalide "
224
242
  f"({self.name}) [{diagnostic}]"
225
243
  + (
226
- f" : {_SAFE_DIAGNOSTIC_MESSAGES[diagnostic]}"
244
+ f" : {self.missing_output_message(execution.stdout)}"
245
+ if diagnostic == STRUCTURED_OUTPUT_MISSING
246
+ else f" : {_SAFE_DIAGNOSTIC_MESSAGES[diagnostic]}"
227
247
  if diagnostic in _SAFE_DIAGNOSTIC_MESSAGES
228
248
  else ""
229
249
  ),
@@ -251,6 +271,27 @@ class AgentAdapter(ABC):
251
271
  except FileNotFoundError:
252
272
  return AgentExecution(127, stderr=f"Executable '{self.executable}' introuvable", duration_seconds=time.monotonic() - started)
253
273
 
274
+ def structured_environment(self) -> dict[str, str]:
275
+ """Environnement de l'agent d'une activite : jamais la cle Linear."""
276
+ return environment_without_linear_key()
277
+
278
+ def subagent_counts(self, stdout: str) -> tuple[int | None, int | None]:
279
+ """Sous-agents lances et lances en arriere-plan ; None si l'agent ne le dit pas."""
280
+ return None, None
281
+
282
+ def missing_output_message(self, stdout: str) -> str:
283
+ """Cause d'une sortie structuree absente : message fixe et entiers seulement."""
284
+ try:
285
+ spawned, background = self.subagent_counts(stdout)
286
+ except Exception: # noqa: BLE001 - format externe non contractuel
287
+ spawned, background = None, None
288
+ message = STRUCTURED_OUTPUT_MISSING_MESSAGE
289
+ if spawned is not None:
290
+ message += f" ; sous-agents lances : {spawned}"
291
+ if background is not None:
292
+ message += f", dont {background} en arriere-plan"
293
+ return message
294
+
254
295
  def session_id(self, stdout: str, stderr: str) -> str | None:
255
296
  """Extrait un identifiant opaque de session, sans conserver la sortie."""
256
297
  return _session_id(stdout, stderr)
@@ -326,8 +367,23 @@ class ClaudeCodeAdapter(AgentAdapter):
326
367
  return envelope["structured_output"]
327
368
  result = envelope.get("result")
328
369
  if isinstance(result, str) and result.strip():
329
- return json.loads(_strip_json_fence(result))
330
- raise ValueError("champ structured_output absent")
370
+ try:
371
+ return json.loads(_strip_json_fence(result))
372
+ except json.JSONDecodeError:
373
+ pass
374
+ raise StructuredOutputError(
375
+ STRUCTURED_OUTPUT_MISSING, "champ structured_output absent"
376
+ )
377
+
378
+ def structured_environment(self) -> dict[str, str]:
379
+ return {**environment_without_linear_key(), CLAUDE_FOREGROUND_ENV: "1"}
380
+
381
+ def subagent_counts(self, stdout: str) -> tuple[int | None, int | None]:
382
+ envelope = json.loads(stdout)
383
+ stats = envelope.get("subagent_stats") if isinstance(envelope, dict) else None
384
+ if not isinstance(stats, dict):
385
+ return None, None
386
+ return _count(stats.get("spawned")), _count(stats.get("started_in_background"))
331
387
 
332
388
  def usage(self, stdout: str, session_id: str | None) -> AgentUsage | None:
333
389
  """Lit l'enveloppe --output-format json : usage cumule de tous les tours."""
@@ -515,6 +571,14 @@ def get_agent_adapter(name: str) -> AgentAdapter:
515
571
  return adapter_type()
516
572
 
517
573
 
574
+ def describe_missing_output(agent: str, stdout: str) -> str:
575
+ """Cause d'une sortie structuree absente, pour la reprise guidee."""
576
+ try:
577
+ return get_agent_adapter(agent).missing_output_message(stdout)
578
+ except ValueError:
579
+ return STRUCTURED_OUTPUT_MISSING_MESSAGE
580
+
581
+
518
582
  def _portable_prompt(prompt: str) -> str:
519
583
  """Traduit une slash-command Claude en instruction portable pour les autres agents."""
520
584
  match = re.fullmatch(r"/devflow\.([a-z0-9-]+)(?:\s+(.*))?", prompt.strip())
@@ -610,6 +674,10 @@ def _int(value: object) -> int:
610
674
  return value if isinstance(value, int) and not isinstance(value, bool) else 0
611
675
 
612
676
 
677
+ def _count(value: object) -> int | None:
678
+ return value if isinstance(value, int) and not isinstance(value, bool) else None
679
+
680
+
613
681
  def _codex_home() -> Path:
614
682
  return Path(os.environ.get("CODEX_HOME") or Path.home() / ".codex")
615
683
 
@@ -70,9 +70,20 @@ from devflow_cli.core.validation_approvals import (
70
70
  )
71
71
  from devflow_cli.core.contract_schemas import LegacySchemaError, diagnose_contract_file
72
72
  from devflow_cli.core.operational import ExitCode, exit_code_for_run_reason
73
- from devflow_cli.adapters.agents import get_agent_adapter
73
+ from devflow_cli.core.result_template import (
74
+ remove_result_template,
75
+ write_result_template,
76
+ )
77
+ from devflow_cli.adapters.agents import (
78
+ MISSING_STRUCTURED_OUTPUT_DIAGNOSTICS,
79
+ STRUCTURED_OUTPUT_MISSING,
80
+ StructuredOutputError,
81
+ describe_missing_output,
82
+ get_agent_adapter,
83
+ )
74
84
  from devflow_cli.utils.console import console, fail, ok, warn
75
85
  from devflow_cli.utils.paths import (
86
+ activity_attempt_file,
76
87
  activity_raw_file,
77
88
  activity_result_file,
78
89
  project_root_for_feature,
@@ -476,6 +487,7 @@ def run_adaptive(
476
487
  json_output=json_output,
477
488
  )
478
489
  if outcome.advanced:
490
+ remove_result_template(current_state.issueId, outcome.activity)
479
491
  if json_output:
480
492
  _print_execution_json(
481
493
  current_state.issueId, outcome.activity, True, outcome.reason,
@@ -493,11 +505,22 @@ def run_adaptive(
493
505
  if outcome.reason != "agent-running": # worklog.md : fichier d'execution (B4)
494
506
  safe_refresh_worklog(feature_dir)
495
507
  exit_code = exit_code_for_run_reason(outcome.reason)
508
+ execution = attempt.get("execution")
509
+ recovery = (
510
+ _missing_output_recovery(
511
+ feature_dir, current_state.issueId, outcome.activity, agent,
512
+ execution.stdout,
513
+ )
514
+ if outcome.reason in {"agent-failed", "agent-failed-after-mutation"}
515
+ and execution is not None
516
+ and execution.final_output_diagnostic in MISSING_STRUCTURED_OUTPUT_DIAGNOSTICS
517
+ else None
518
+ )
496
519
  if json_output:
497
520
  _print_execution_json(
498
521
  current_state.issueId, outcome.activity, False, outcome.reason,
499
522
  outcome.detail, outcome.nextActivity, exit_code,
500
- warnings=outcome.warnings,
523
+ warnings=outcome.warnings, recovery=recovery,
501
524
  )
502
525
  elif outcome.reason in BASE_REFUSALS:
503
526
  fail(outcome.detail)
@@ -509,7 +532,8 @@ def run_adaptive(
509
532
  f" Activite '{outcome.activity}' non validee : {outcome.reason}"
510
533
  )
511
534
  _print_run_recovery(
512
- current_state.issueId, outcome.reason, outcome.activity, agent, feature_dir
535
+ current_state.issueId, outcome.reason, outcome.activity, agent, feature_dir,
536
+ recovery=recovery,
513
537
  )
514
538
  raise typer.Exit(int(exit_code))
515
539
 
@@ -611,8 +635,18 @@ def reconcile_adaptive(
611
635
  fail("--reason ne peut etre utilise qu'avec --result")
612
636
  raise typer.Exit(int(ExitCode.USAGE))
613
637
 
638
+ missing_output: dict[str, str] = {}
639
+
614
640
  def recover(agent: str, raw_output: str) -> object:
615
- return get_agent_adapter(agent).recover_structured_payload(raw_output)
641
+ try:
642
+ return get_agent_adapter(agent).recover_structured_payload(raw_output)
643
+ except (json.JSONDecodeError, StructuredOutputError) as exc:
644
+ if (
645
+ isinstance(exc, json.JSONDecodeError)
646
+ or exc.diagnostic in MISSING_STRUCTURED_OUTPUT_DIAGNOSTICS
647
+ ):
648
+ missing_output.update(agent=agent, raw=raw_output)
649
+ raise
616
650
 
617
651
  outcome = reconcile_current_activity(
618
652
  feature_dir,
@@ -628,9 +662,29 @@ def reconcile_adaptive(
628
662
  )
629
663
  _print_warnings(outcome.warnings)
630
664
  _print_pending_approvals(feature_dir, state.issueId, outcome.activity)
665
+ remove_result_template(state.issueId, outcome.activity)
631
666
  return
632
667
  if outcome.reason != "agent-running": # worklog.md : fichier d'execution (B4)
633
668
  safe_refresh_worklog(feature_dir)
669
+ if (
670
+ result is None
671
+ and outcome.reason == "invalid-result"
672
+ and missing_output
673
+ and _last_run_missed_structured_output(feature_dir, outcome.activity)
674
+ ):
675
+ # Trace sans reponse structuree : message actionnable, pas l'erreur de lecture.
676
+ recovery = _missing_output_recovery(
677
+ feature_dir, state.issueId, outcome.activity,
678
+ missing_output["agent"], missing_output["raw"],
679
+ )
680
+ fail(
681
+ f"Reconciliation de '{outcome.activity}' refusee : {outcome.reason} "
682
+ f"({STRUCTURED_OUTPUT_MISSING})"
683
+ )
684
+ _print_warnings(outcome.warnings)
685
+ console.print(" Reprise guidee :")
686
+ _print_recovery_steps(recovery)
687
+ raise typer.Exit(int(exit_code_for_run_reason(outcome.reason)))
634
688
  if outcome.reason in BASE_REFUSALS:
635
689
  fail(outcome.detail)
636
690
  else:
@@ -1372,8 +1426,99 @@ def _fallback_agents(agent: str) -> list[str]:
1372
1426
  ]
1373
1427
 
1374
1428
 
1429
+ def _last_run_missed_structured_output(feature_dir: Path, activity: str) -> bool:
1430
+ """Vrai si la derniere execution de l'activite a fini sans sortie structuree."""
1431
+ try:
1432
+ audit = json.loads(
1433
+ (feature_dir / "agent-execution.json").read_text(encoding="utf-8")
1434
+ )
1435
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError):
1436
+ return False
1437
+ return (
1438
+ isinstance(audit, dict)
1439
+ and audit.get("activity") == activity
1440
+ and audit.get("finalOutputDiagnostic") in MISSING_STRUCTURED_OUTPUT_DIAGNOSTICS
1441
+ )
1442
+
1443
+
1444
+ def _missing_output_recovery(
1445
+ feature_dir: Path, issue_id: str, activity: str, agent: str, raw_output: str
1446
+ ) -> dict:
1447
+ """Reprise guidee d'une sortie structuree absente apres un agent termine sans erreur.
1448
+
1449
+ Le modele prerempli (contrat courant, fichiers observes depuis la base) est ecrit
1450
+ hors du depot ; ses champs a completer sont vides, donc refuses tels quels.
1451
+ """
1452
+ issue = shlex.quote(issue_id)
1453
+ cause = describe_missing_output(agent, raw_output)
1454
+ recovery: dict = {
1455
+ "kind": STRUCTURED_OUTPUT_MISSING,
1456
+ "summary": f"Sortie structuree absente : {cause}.",
1457
+ "resultTemplate": None,
1458
+ "commands": [],
1459
+ }
1460
+ if activity not in MANUAL_RECONCILIATION_ACTIVITIES:
1461
+ recovery["commands"].append({
1462
+ "command": f"devflow adaptive run {issue} --agent {shlex.quote(agent)}",
1463
+ "purpose": "relancer l'agent : la correction manuelle est interdite pour cette activite",
1464
+ })
1465
+ return recovery
1466
+ try:
1467
+ attempt = json.loads(
1468
+ activity_attempt_file(feature_dir, activity).read_text(encoding="utf-8")
1469
+ )
1470
+ contract = json.loads(
1471
+ (feature_dir / "activity-contract.json").read_text(encoding="utf-8")
1472
+ )
1473
+ if (
1474
+ contract["activity"] != activity
1475
+ or contract["contractId"] != attempt["contractId"]
1476
+ ):
1477
+ raise ValueError("contrat et tentative incoherents")
1478
+ template = write_result_template(
1479
+ issue_id,
1480
+ activity,
1481
+ contract["contractId"],
1482
+ contract["expectedOutput"],
1483
+ sorted(attempt["observedFiles"]),
1484
+ )
1485
+ except (OSError, UnicodeDecodeError, KeyError, TypeError, ValueError):
1486
+ template = None
1487
+ if template is not None:
1488
+ recovery["resultTemplate"] = str(template)
1489
+ recovery["commands"].extend([
1490
+ {
1491
+ "command": "git status --short",
1492
+ "purpose": "inspecter les ecritures deja effectuees",
1493
+ },
1494
+ {
1495
+ "command": (
1496
+ f"devflow adaptive reconcile {issue} --result "
1497
+ f"{shlex.quote(str(template)) if template else 'FICHIER.json'} --reason MOTIF"
1498
+ ),
1499
+ "purpose": (
1500
+ "completer le modele prerempli (status, summary, checks, evidence) "
1501
+ "puis valider sans relancer l'agent"
1502
+ if template
1503
+ else "modele prerempli non ecrit : fournir le resultat du contrat"
1504
+ ),
1505
+ },
1506
+ ])
1507
+ return recovery
1508
+
1509
+
1510
+ def _print_recovery_steps(recovery: dict) -> None:
1511
+ # soft_wrap : la commande a copier reste sur une seule ligne.
1512
+ console.print(f" {recovery['summary']}", markup=False, soft_wrap=True)
1513
+ for step in recovery["commands"]:
1514
+ console.print(
1515
+ f" {step['command']} # {step['purpose']}", markup=False, soft_wrap=True
1516
+ )
1517
+
1518
+
1375
1519
  def _print_run_recovery(
1376
- issue_id: str, reason: str, activity: str, agent: str, feature_dir: Path
1520
+ issue_id: str, reason: str, activity: str, agent: str, feature_dir: Path,
1521
+ *, recovery: dict | None = None,
1377
1522
  ) -> None:
1378
1523
  console.print(" Reprise guidee :")
1379
1524
  console.print(f" devflow adaptive status {issue_id}")
@@ -1382,6 +1527,11 @@ def _print_run_recovery(
1382
1527
  " Trace agent : "
1383
1528
  f"{activity_raw_file(feature_dir, activity).name}"
1384
1529
  )
1530
+ if recovery is not None:
1531
+ if reason == "agent-failed-after-mutation":
1532
+ console.print(" Ne relancez pas automatiquement l'agent : reconciliez d'abord les changements partiels.")
1533
+ _print_recovery_steps(recovery)
1534
+ return
1385
1535
  if reason == "agent-unavailable":
1386
1536
  console.print(" Agent indisponible : l'activite reste courante et rien n'est invalide.")
1387
1537
  console.print(f" devflow check --ai {agent} --live # verifier le retour de l'agent")
@@ -43,6 +43,14 @@ DOCUMENT_ACTIVITIES = set(DOCUMENT_ARTIFACTS)
43
43
  ActivityStatus = Literal["completed", "blocked"]
44
44
 
45
45
  REMOVED_RESULT_FIELDS = ("omissionRecommendations", "reviewedOmissions")
46
+ # Commune a tous les contrats et a tous les agents : un sous-agent en arriere-plan
47
+ # force l'agent a rendre la main avant sa sortie structuree, qui ne s'envoie qu'une fois.
48
+ SUBAGENT_CONSTRAINT = (
49
+ "Si des sous-agents sont utilises, les lancer au premier plan, jamais en "
50
+ "arriere-plan, et attendre qu'ils soient tous termines avant la sortie structuree. "
51
+ "La sortie structuree du contrat est le dernier message de l'agent : ne rien "
52
+ "envoyer apres elle."
53
+ )
46
54
  PROTECTED_VALIDATION_CONSTRAINT = (
47
55
  "Les garde-fous sont actifs : inclure dans validationPlan au moins une validation "
48
56
  "bloquante explicitement consacree a la securite, aux donnees, a l'authentification, "
@@ -244,6 +252,7 @@ def build_activity_contract(
244
252
  "Signaler tout blocage au lieu de contourner une validation.",
245
253
  "Dans checks, status vaut passed si et seulement si exitCode vaut 0. Une commande dont l'absence de resultat constitue le succes attendu doit etre declaree failed, ou remplacee par une commande qui renvoie 0 en cas de succes.",
246
254
  "Donner un id stable a chaque objectif de check. Si un check echoue puis est corrige et relance pour le meme objectif, reutiliser cet id : seule la derniere tentative fait foi ; signaler dans remainingRisks toute instabilite encore pertinente.",
255
+ SUBAGENT_CONSTRAINT,
247
256
  ]
248
257
  if activity in DOCUMENT_ACTIVITIES:
249
258
  artifact_name = document_artifact_name(activity, reopened=reopened)
@@ -0,0 +1,74 @@
1
+ """Modele de resultat prerempli pour ``adaptive reconcile --result``.
2
+
3
+ Ecrit hors du depot, dans ``~/.devflow/reconcile/`` (ou ``DEVFLOW_HOME``), quand un
4
+ agent a termine sans rendre sa sortie structuree. Les champs a completer sont
5
+ laisses vides : le modele brut est refuse par les validateurs de ``reconcile``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ import re
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from devflow_cli.core.telemetry import telemetry_file
17
+
18
+
19
+ def result_template_file(issue_id: str, activity: str) -> Path:
20
+ name = re.sub(r"[^A-Za-z0-9_-]", "_", f"{issue_id}-{activity}")
21
+ return telemetry_file().parent / "reconcile" / f"{name}-result.json"
22
+
23
+
24
+ def result_template_payload(
25
+ contract_id: str, expected_output: dict[str, Any], changed_files: list[str]
26
+ ) -> dict[str, Any]:
27
+ """Champs requis du schema : faits connus de la CLI remplis, le reste vide."""
28
+ properties = expected_output.get("properties", {})
29
+ payload: dict[str, Any] = {}
30
+ for name in expected_output.get("required", []):
31
+ schema = properties.get(name, {})
32
+ if name == "contractId":
33
+ payload[name] = contract_id
34
+ elif name == "changedFiles":
35
+ payload[name] = changed_files
36
+ elif "const" in schema:
37
+ payload[name] = schema["const"]
38
+ elif schema.get("type") == "array":
39
+ payload[name] = []
40
+ elif schema.get("type") == "object":
41
+ payload[name] = {}
42
+ else:
43
+ payload[name] = ""
44
+ return payload
45
+
46
+
47
+ def write_result_template(
48
+ issue_id: str,
49
+ activity: str,
50
+ contract_id: str,
51
+ expected_output: dict[str, Any],
52
+ changed_files: list[str],
53
+ ) -> Path:
54
+ """Ecrit le modele (dossier 0700, fichier 0600) et retourne son chemin."""
55
+ path = result_template_file(issue_id, activity)
56
+ path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
57
+ path.parent.chmod(0o700)
58
+ content = json.dumps(
59
+ result_template_payload(contract_id, expected_output, changed_files),
60
+ indent=2,
61
+ ensure_ascii=False,
62
+ ) + "\n"
63
+ path.unlink(missing_ok=True)
64
+ descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
65
+ with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
66
+ handle.write(content)
67
+ return path
68
+
69
+
70
+ def remove_result_template(issue_id: str, activity: str) -> None:
71
+ try:
72
+ result_template_file(issue_id, activity).unlink(missing_ok=True)
73
+ except OSError:
74
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: devflow-cli
3
- Version: 8.1.0
3
+ Version: 8.2.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
@@ -102,6 +102,16 @@ qu'un agent travaille, les commandes humaines qui modifieraient l'état sont
102
102
  refusées sans effet, et un fichier d'exécution modifié par l'agent est remis
103
103
  dans son état d'avant l'agent.
104
104
 
105
+ L'agent Claude d'une activité est lancé avec
106
+ `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` : il peut déléguer à des sous-agents,
107
+ mais au premier plan seulement. Effet de bord : il ne peut plus lancer de
108
+ commande shell en arrière-plan (serveur de test, par exemple). Tous les contrats
109
+ demandent en plus que la sortie structurée soit le dernier message de l'agent.
110
+ Si un agent termine sans erreur mais sans sortie structurée, `adaptive run`
111
+ nomme la cause (`structured-output-missing`) et propose
112
+ `devflow adaptive reconcile KS-123 --result MODELE --reason MOTIF` avec un
113
+ modèle de résultat prérempli, écrit dans `~/.devflow/reconcile/` et à compléter.
114
+
105
115
  Chaque invocation de `adaptive run` traite une activité. La politique est dans
106
116
  `assessment.json`; la progression et les preuves sont dans
107
117
  `workflow-state.json`. Les résultats agent sont validés par schéma et les
@@ -1,10 +1,10 @@
1
- devflow_cli/__init__.py,sha256=PUspBocDYmXBT3YAsO8WxkWncuo5lyBpxQRxMyBY1mY,925
1
+ devflow_cli/__init__.py,sha256=Y5UdblxgAc6mD_GvD_R7nfzo_uyAi09mJw3AJoB8Stk,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
- devflow_cli/adapters/agents.py,sha256=lq3OjcaT3Nt9-WV9Po4Dj1fag4getb7DEt9Y3Zp1js4,26683
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=xfvjQOlBrUjZvf8NLLlaohyeUT54LhjdDCptMr_s9w8,56390
7
+ devflow_cli/commands/adaptive.py,sha256=kYSpQWZ-xUTw1Kquv5fE2kpFjoVClqNl4Nd7pUj7FX8,62199
8
8
  devflow_cli/commands/assess.py,sha256=lyj6TI5hXUWX55dY589fG4bKJSMztvMPxZ0HZjo0O-w,5230
9
9
  devflow_cli/commands/check.py,sha256=hsfeOg9yDOTAIvNm6XhRmTIFgv7w9wC8zvrwm4ojfZs,6923
10
10
  devflow_cli/commands/context.py,sha256=hwTitOHxIpJMx1X8R1jHHy9CpE4HVZpJKkyeOujxAio,6246
@@ -24,7 +24,7 @@ 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=haEVVgZAi3-iX75oo2-fK9GQZeFgN_xRw5U0Z0bdV5w,16343
27
- devflow_cli/core/activity_contracts.py,sha256=nbPj9dkCIG7HXrT1iwZ1m3oaUZY-V65bfC62ALuYAus,38212
27
+ devflow_cli/core/activity_contracts.py,sha256=2-pjjxMyS7iER2eim118U5QSgOqwrP47X_HcfZ7jZ58,38717
28
28
  devflow_cli/core/adaptive_runner.py,sha256=MLws62PW986dd00nA4m_3Y4BAR-ZmmYT3q8N6KiPU-Q,40375
29
29
  devflow_cli/core/adaptive_workflow.py,sha256=F4uV5okQpFidZmneVZwW52XAO7gAmtuBAqBO9K9q7Uk,12173
30
30
  devflow_cli/core/agent_failure.py,sha256=iX4q249lAHeVdD_ZF1aObmU-FX6sjfYP9dfLSnWGAD0,3771
@@ -42,6 +42,7 @@ devflow_cli/core/manifest.py,sha256=h058_m5-HJrYsNUdgfwnKts2riHiYPXUwMzPImHOs2g,
42
42
  devflow_cli/core/operational.py,sha256=11iLTbyZF0ZjYTQ5HDmVCaw4bpbXpLtH2v9f1WQOrYg,1228
43
43
  devflow_cli/core/redaction.py,sha256=o_1MDtCyvRLrk_dWlV6_7E6_mPVB1B5a0cCfTBj9j0Q,1973
44
44
  devflow_cli/core/remediation.py,sha256=AI5fBs-F8xF3uaxgYJDNJHKxJ7V5obEtnfptMm351AU,8426
45
+ devflow_cli/core/result_template.py,sha256=XfrjUlSUDYJxE2nN8EJjHCXq1l06hn-wW1gDn1c1rBA,2493
45
46
  devflow_cli/core/run_progress.py,sha256=hawaMo5sGcxvugexeryMWanFajdf46SY6b37O7dToZM,1855
46
47
  devflow_cli/core/runtime_backup.py,sha256=dowz6a1vcJsqkl-m9wGhS4F6XGfsU2rwSNJvIZMJjhI,4345
47
48
  devflow_cli/core/scanner.py,sha256=bcoC91Nc0Hw2P7aHpL-9ITxynLZhLTEKpOXo553a-18,4395
@@ -89,8 +90,8 @@ devflow_cli/resources/templates/contracts/api-template.md,sha256=rC5v7JBz71PznCn
89
90
  devflow_cli/resources/templates/contracts/components-template.md,sha256=XIWfEXfC72sNmL5OA3V-7xIywkNDU2DBPLfyiagOWcs,484
90
91
  devflow_cli/resources/templates/contracts/interfaces-template.md,sha256=kf8B7b6HsmN7jyRlLNU7RnZl0rTMlI5s97yVRyI-6yc,336
91
92
  devflow_cli/resources/templates/contracts/services-template.md,sha256=cFafKCyLmq_a7aR7G0LXAaMO2z4Fuflq6iKdxfVg0vg,477
92
- devflow_cli-8.1.0.dist-info/METADATA,sha256=anR27a2QLBDXONlC1oDcaDdTlxzx12qoqEmCBOxCouk,7809
93
- devflow_cli-8.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
94
- devflow_cli-8.1.0.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
95
- devflow_cli-8.1.0.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
96
- devflow_cli-8.1.0.dist-info/RECORD,,
93
+ devflow_cli-8.2.0.dist-info/METADATA,sha256=9zPuMV6XtqesVYitXPdUdkg2KqmqP_zQ7LSRCa9jksQ,8470
94
+ devflow_cli-8.2.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
95
+ devflow_cli-8.2.0.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
96
+ devflow_cli-8.2.0.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
97
+ devflow_cli-8.2.0.dist-info/RECORD,,