devflow-cli 7.1.1__py3-none-any.whl → 7.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,18 +1,18 @@
1
1
  """devflow CLI — workflow de développement adaptatif."""
2
2
 
3
- VERSION = "7.1.1"
3
+ VERSION = "7.2.0"
4
4
 
5
5
  SUPPORTED_AGENTS = ["claude-code", "codex", "cursor"]
6
6
 
7
7
  ACTIVITY_LINEAR_MAPPING = {
8
8
  "assess": "Backlog",
9
- "prepare": "Plan",
10
- "risk-assessment": "Research",
11
- "spec": "Spec",
12
- "contracts": "Plan",
13
- "plan": "Plan",
14
- "tasks": "Tasks",
15
- "preflight-evidence": "Plan",
9
+ "risk-assessment": "Design",
10
+ "prepare": "Design",
11
+ "spec": "Design",
12
+ "contracts": "Design",
13
+ "plan": "Design",
14
+ "tasks": "Design",
15
+ "preflight-evidence": "Design",
16
16
  "implement": "In Progress",
17
17
  "verify": "Review",
18
18
  "review": "Review",
@@ -22,10 +22,7 @@ ACTIVITY_LINEAR_MAPPING = {
22
22
 
23
23
  LINEAR_STATUS_ORDER = [
24
24
  "Backlog",
25
- "Spec",
26
- "Research",
27
- "Plan",
28
- "Tasks",
25
+ "Design",
29
26
  "In Progress",
30
27
  "Review",
31
28
  "Done",
@@ -16,6 +16,8 @@ from typing import Literal
16
16
  from jsonschema.exceptions import SchemaError, ValidationError
17
17
  from jsonschema.validators import validator_for
18
18
 
19
+ from devflow_cli.adapters.linear import environment_without_linear_key
20
+
19
21
 
20
22
  PermissionMode = Literal["auto", "acceptEdits", "manual"]
21
23
  SandboxMode = Literal["read-only", "workspace-write"]
@@ -123,6 +125,7 @@ class AgentAdapter(ABC):
123
125
  capture_output=capture_output,
124
126
  stdin=subprocess.DEVNULL,
125
127
  shell=False,
128
+ env=environment_without_linear_key(),
126
129
  )
127
130
  return AgentExecution(
128
131
  result.returncode,
@@ -162,6 +165,7 @@ class AgentAdapter(ABC):
162
165
  capture_output=True,
163
166
  stdin=subprocess.DEVNULL,
164
167
  shell=False,
168
+ env=environment_without_linear_key(),
165
169
  )
166
170
  execution = AgentExecution(
167
171
  result.returncode,
@@ -1,11 +1,32 @@
1
- """Politique de synchronisation Linear, indépendante du transport MCP/API."""
1
+ """Politique de synchronisation Linear et transport GraphQL."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
6
+ import os
7
+ import urllib.error
8
+ import urllib.request
5
9
  from dataclasses import dataclass
6
- from typing import Literal, Protocol
10
+ from typing import Literal, Protocol, TextIO
7
11
 
8
- from devflow_cli import ACTIVITY_LINEAR_MAPPING, LINEAR_STATUS_ORDER
12
+ from devflow_cli import ACTIVITY_LINEAR_MAPPING, LINEAR_STATUS_ORDER, VERSION
13
+ from devflow_cli.core.run_progress import emit_progress
14
+
15
+
16
+ LINEAR_API_KEY_ENV = "LINEAR_API_KEY"
17
+ LINEAR_API_URL = "https://api.linear.app/graphql"
18
+ LINEAR_TIMEOUT_SECONDS = 5
19
+
20
+ _ISSUE_QUERY = (
21
+ "query($id: String!) { issue(id: $id) { id state { name } "
22
+ "team { states { nodes { id name } } } } }"
23
+ )
24
+ _UPDATE_MUTATION = (
25
+ "mutation($id: String!, $stateId: String!) { "
26
+ "issueUpdate(id: $id, input: { stateId: $stateId }) { success } }"
27
+ )
28
+
29
+ _missing_key_warned = False
9
30
 
10
31
 
11
32
  LinearAction = Literal["update", "keep", "skip"]
@@ -81,3 +102,142 @@ def sync_linear_status(
81
102
  if decision.should_update and decision.target_status is not None:
82
103
  adapter.update_status(issue_id, decision.target_status)
83
104
  return decision
105
+
106
+
107
+ def environment_without_linear_key() -> dict[str, str]:
108
+ """Environnement transmis aux processus lancés par la CLI, sans la clé Linear."""
109
+ return {key: value for key, value in os.environ.items() if key != LINEAR_API_KEY_ENV}
110
+
111
+
112
+ class _NoRedirectHandler(urllib.request.HTTPRedirectHandler):
113
+ """Refuse de suivre une redirection : l'en-tête Authorization ne doit pas la suivre."""
114
+
115
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
116
+ return None
117
+
118
+
119
+ class LinearSyncError(Exception):
120
+ """Échec de synchronisation ; le libellé ne contient aucune donnée externe."""
121
+
122
+ def __init__(self, label: str) -> None:
123
+ super().__init__(label)
124
+ self.label = label
125
+
126
+
127
+ class LinearGraphQLAdapter:
128
+ """Transport par l'API GraphQL de Linear, en bibliothèque standard."""
129
+
130
+ def __init__(self, api_key: str, timeout: float = LINEAR_TIMEOUT_SECONDS) -> None:
131
+ self._api_key = api_key
132
+ self._timeout = timeout
133
+ self._issue_uuid: str | None = None
134
+ self._state_ids: dict[str, str] = {}
135
+
136
+ def get_status(self, issue_id: str) -> str | None:
137
+ data = self._request(_ISSUE_QUERY, {"id": issue_id})
138
+ issue = data.get("issue")
139
+ if issue is None:
140
+ raise LinearSyncError("issue inconnue de Linear")
141
+ try:
142
+ self._issue_uuid = str(issue["id"])
143
+ self._state_ids = {
144
+ str(node["name"]): str(node["id"])
145
+ for node in issue["team"]["states"]["nodes"]
146
+ }
147
+ return str(issue["state"]["name"])
148
+ except (KeyError, TypeError):
149
+ raise LinearSyncError("reponse Linear invalide") from None
150
+
151
+ def update_status(self, issue_id: str, status: str) -> None:
152
+ state_id = self._state_ids.get(status)
153
+ if self._issue_uuid is None or state_id is None:
154
+ raise LinearSyncError(f"statut '{status}' absent de l'equipe Linear")
155
+ data = self._request(
156
+ _UPDATE_MUTATION, {"id": self._issue_uuid, "stateId": state_id}
157
+ )
158
+ result = data.get("issueUpdate")
159
+ if not isinstance(result, dict) or result.get("success") is not True:
160
+ raise LinearSyncError("mise a jour refusee par Linear")
161
+
162
+ def _request(self, query: str, variables: dict[str, str]) -> dict:
163
+ request = urllib.request.Request(
164
+ LINEAR_API_URL,
165
+ data=json.dumps({"query": query, "variables": variables}).encode("utf-8"),
166
+ headers={
167
+ "Content-Type": "application/json",
168
+ "Authorization": self._api_key,
169
+ "User-Agent": f"devflow-cli/{VERSION}",
170
+ },
171
+ method="POST",
172
+ )
173
+ opener = urllib.request.build_opener(_NoRedirectHandler())
174
+ try:
175
+ with opener.open(request, timeout=self._timeout) as response:
176
+ payload = json.loads(response.read().decode("utf-8"))
177
+ except urllib.error.HTTPError as exc:
178
+ raise LinearSyncError(f"erreur HTTP {exc.code}") from None
179
+ except TimeoutError:
180
+ raise LinearSyncError("delai depasse") from None
181
+ except urllib.error.URLError as exc:
182
+ if isinstance(exc.reason, TimeoutError):
183
+ raise LinearSyncError("delai depasse") from None
184
+ raise LinearSyncError("erreur reseau") from None
185
+ except OSError:
186
+ raise LinearSyncError("erreur reseau") from None
187
+ except ValueError:
188
+ raise LinearSyncError("reponse Linear invalide") from None
189
+ if not isinstance(payload, dict):
190
+ raise LinearSyncError("reponse Linear invalide")
191
+ if payload.get("errors"):
192
+ raise LinearSyncError("erreur GraphQL")
193
+ data = payload.get("data")
194
+ if not isinstance(data, dict):
195
+ raise LinearSyncError("reponse Linear invalide")
196
+ return data
197
+
198
+
199
+ def sync_activity_status(
200
+ issue_id: str, activity: str, stream: TextIO
201
+ ) -> LinearDecision | None:
202
+ """Synchronise le statut au démarrage d'une activité, sans jamais bloquer."""
203
+ global _missing_key_warned
204
+ api_key = os.environ.get(LINEAR_API_KEY_ENV, "").strip()
205
+ if not api_key:
206
+ if not _missing_key_warned:
207
+ _missing_key_warned = True
208
+ emit_progress(
209
+ f"Linear : avertissement, {LINEAR_API_KEY_ENV} absente ; "
210
+ "statuts non synchronises",
211
+ stream,
212
+ )
213
+ return None
214
+ try:
215
+ decision = sync_linear_status(LinearGraphQLAdapter(api_key), issue_id, activity)
216
+ except LinearSyncError as exc:
217
+ label = exc.label
218
+ except Exception:
219
+ label = "erreur inattendue"
220
+ else:
221
+ emit_progress(_describe_decision(issue_id, decision), stream)
222
+ return decision
223
+ emit_progress(
224
+ f"Linear : avertissement, {issue_id} non synchronisee ({label})", stream
225
+ )
226
+ return None
227
+
228
+
229
+ def _describe_decision(issue_id: str, decision: LinearDecision) -> str:
230
+ if decision.action == "update":
231
+ return (
232
+ f"Linear : {issue_id} {decision.current_status} -> "
233
+ f"{decision.target_status} (activite {decision.activity})"
234
+ )
235
+ if decision.action == "keep":
236
+ return (
237
+ f"Linear : {issue_id} reste en {decision.current_status} "
238
+ f"(activite {decision.activity}, {decision.reason})"
239
+ )
240
+ return (
241
+ f"Linear : {issue_id} ignoree, statut {decision.current_status or 'inconnu'} "
242
+ f"({decision.reason})"
243
+ )
@@ -14,6 +14,7 @@ from typing import Literal, Optional
14
14
  import typer
15
15
 
16
16
  from devflow_cli import VERSION
17
+ from devflow_cli.adapters.linear import sync_activity_status
17
18
  from devflow_cli.core.adaptive_workflow import (
18
19
  AdaptiveWorkflowState,
19
20
  complete_activity,
@@ -226,6 +227,8 @@ def complete(
226
227
  raise typer.Exit(int(ExitCode.BLOCKED)) from exc
227
228
  write_adaptive_state(feature_dir / "workflow-state.json", state)
228
229
  safe_refresh_worklog(feature_dir)
230
+ if activity == "done":
231
+ sync_activity_status(state.issueId, "done", run_progress.progress_stream(False))
229
232
  if state.status == "completed":
230
233
  ok(f"Workflow {state.profile} termine.")
231
234
  _print_completion_summary(feature_dir, state)
@@ -388,6 +391,9 @@ def run_adaptive(
388
391
  f"agent {agent}, delai {timeout} s",
389
392
  stream,
390
393
  )
394
+ sync_activity_status(
395
+ current_state.issueId, activity_contract.activity, stream
396
+ )
391
397
  with run_progress.heartbeat(
392
398
  activity_contract.activity,
393
399
  agent,
@@ -809,10 +815,12 @@ def _execute_local_verification(
809
815
  feature_dir: Path, project_root: Path, issue_id: str, *,
810
816
  refresh: bool = False, json_output: bool = False,
811
817
  ) -> None:
818
+ stream = run_progress.progress_stream(json_output)
812
819
  run_progress.emit_progress(
813
- "Activite 'verify' demarree : verification locale",
814
- run_progress.progress_stream(json_output),
820
+ "Activite 'verify' demarree : verification locale", stream
815
821
  )
822
+ if not refresh:
823
+ sync_activity_status(issue_id, "verify", stream)
816
824
  outcome = run_automatic_verification(feature_dir, project_root, refresh=refresh)
817
825
  if json_output:
818
826
  _print_execution_json(
@@ -8,6 +8,7 @@ from pathlib import Path
8
8
 
9
9
  import typer
10
10
  from devflow_cli.adapters.agents import get_agent_adapter
11
+ from devflow_cli.adapters.linear import environment_without_linear_key
11
12
  from devflow_cli.core.agent_failure import diagnose_agent_failure
12
13
  from devflow_cli.utils.console import console, ok, warn, fail
13
14
  from devflow_cli.utils.logging import get_logger
@@ -78,7 +79,12 @@ def check(
78
79
  # git
79
80
  git_path = _cmd_exists("git")
80
81
  if git_path:
81
- result = subprocess.run(["git", "--version"], capture_output=True, text=True)
82
+ result = subprocess.run(
83
+ ["git", "--version"],
84
+ capture_output=True,
85
+ text=True,
86
+ env=environment_without_linear_key(),
87
+ )
82
88
  version = result.stdout.strip().replace("git version ", "")
83
89
  ok(f"git -- {git_path} (version {version})")
84
90
  ok_count += 1
@@ -90,7 +96,10 @@ def check(
90
96
  codex_path = _cmd_exists("codex")
91
97
  if codex_path:
92
98
  result = subprocess.run(
93
- ["codex", "--version"], capture_output=True, text=True
99
+ ["codex", "--version"],
100
+ capture_output=True,
101
+ text=True,
102
+ env=environment_without_linear_key(),
94
103
  )
95
104
  version = (result.stdout or result.stderr).strip()
96
105
  if result.returncode == 0:
@@ -8,6 +8,7 @@ from pathlib import Path
8
8
 
9
9
  import typer
10
10
 
11
+ from devflow_cli.adapters.linear import environment_without_linear_key
11
12
  from devflow_cli.core.git import is_git_repo
12
13
  from devflow_cli.core.hooks import (
13
14
  DEFAULT_HOOK_TIMEOUT,
@@ -28,6 +29,7 @@ def _configure_hooks_path(repo: Path) -> None:
28
29
  ["git", "-C", str(repo), "config", "core.hooksPath", HOOKS_DIR_NAME],
29
30
  check=True,
30
31
  capture_output=True,
32
+ env=environment_without_linear_key(),
31
33
  )
32
34
 
33
35
 
@@ -36,6 +38,7 @@ def _current_hooks_path(repo: Path) -> str | None:
36
38
  ["git", "-C", str(repo), "config", "--get", "core.hooksPath"],
37
39
  capture_output=True,
38
40
  text=True,
41
+ env=environment_without_linear_key(),
39
42
  )
40
43
  if result.returncode != 0:
41
44
  return None
@@ -46,6 +49,7 @@ def _unset_hooks_path(repo: Path) -> None:
46
49
  subprocess.run(
47
50
  ["git", "-C", str(repo), "config", "--unset", "core.hooksPath"],
48
51
  capture_output=True,
52
+ env=environment_without_linear_key(),
49
53
  )
50
54
 
51
55
 
@@ -21,6 +21,7 @@ from pathlib import Path
21
21
  from typing import Any
22
22
 
23
23
  from devflow_cli import VERSION
24
+ from devflow_cli.adapters.linear import environment_without_linear_key
24
25
  from devflow_cli.core.contract_schemas import LegacySchemaError, check_schema_version
25
26
  from devflow_cli.core.redaction import redact_text
26
27
  from devflow_cli.core.telemetry import (
@@ -167,6 +168,7 @@ def _git_root(cwd: Path) -> str:
167
168
  text=True,
168
169
  timeout=5,
169
170
  check=False,
171
+ env=environment_without_linear_key(),
170
172
  )
171
173
  except (OSError, subprocess.SubprocessError):
172
174
  return str(cwd.resolve())
devflow_cli/core/git.py CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import subprocess
6
6
  from pathlib import Path
7
7
 
8
+ from devflow_cli.adapters.linear import environment_without_linear_key
8
9
  from devflow_cli.utils.logging import get_logger
9
10
 
10
11
  logger = get_logger(__name__)
@@ -31,6 +32,7 @@ def ensure_git_available() -> None:
31
32
  capture_output=True,
32
33
  text=True,
33
34
  timeout=5,
35
+ env=environment_without_linear_key(),
34
36
  )
35
37
  _git_checked = True
36
38
  except FileNotFoundError:
@@ -45,7 +47,12 @@ def _run(args: list[str], cwd: Path | None = None) -> subprocess.CompletedProces
45
47
  ensure_git_available()
46
48
  try:
47
49
  return subprocess.run(
48
- args, capture_output=True, text=True, cwd=cwd, timeout=_GIT_TIMEOUT,
50
+ args,
51
+ capture_output=True,
52
+ text=True,
53
+ cwd=cwd,
54
+ timeout=_GIT_TIMEOUT,
55
+ env=environment_without_linear_key(),
49
56
  )
50
57
  except subprocess.TimeoutExpired:
51
58
  logger.warning("git command timed out: %s", args, exc_info=True)
devflow_cli/core/hooks.py CHANGED
@@ -11,6 +11,7 @@ from typing import Any
11
11
  import yaml
12
12
 
13
13
  from devflow_cli.adapters.agents import ClaudeCodeAdapter
14
+ from devflow_cli.adapters.linear import environment_without_linear_key
14
15
 
15
16
  from devflow_cli.utils.logging import get_logger
16
17
 
@@ -187,6 +188,7 @@ def run_hook_point(
187
188
  text=True,
188
189
  timeout=timeout,
189
190
  shell=False,
191
+ env=environment_without_linear_key(),
190
192
  )
191
193
  executions.append(HookExecution(
192
194
  hook_point=hook_point,
@@ -9,6 +9,7 @@ from dataclasses import dataclass
9
9
  from pathlib import Path
10
10
  from typing import Collection, Mapping
11
11
 
12
+ from devflow_cli.adapters.linear import environment_without_linear_key
12
13
  from devflow_cli.core.redaction import redact_command, redact_text
13
14
 
14
15
 
@@ -237,6 +238,7 @@ def run_verifications(
237
238
  text=True,
238
239
  timeout=validation.timeout,
239
240
  shell=False,
241
+ env=environment_without_linear_key(),
240
242
  )
241
243
  results.append(VerificationResult(
242
244
  id=validation.id,
@@ -9,6 +9,8 @@ import subprocess
9
9
  from dataclasses import dataclass
10
10
  from pathlib import Path
11
11
 
12
+ from devflow_cli.adapters.linear import environment_without_linear_key
13
+
12
14
 
13
15
  @dataclass(frozen=True)
14
16
  class WorktreeSnapshot:
@@ -99,6 +101,7 @@ def _git_text(project_root: Path, args: list[str], *, allow_failure: bool = Fals
99
101
  capture_output=True,
100
102
  text=True,
101
103
  timeout=30,
104
+ env=environment_without_linear_key(),
102
105
  )
103
106
  if result.returncode != 0:
104
107
  if allow_failure:
@@ -115,6 +118,7 @@ def _git_paths(
115
118
  cwd=project_root,
116
119
  capture_output=True,
117
120
  timeout=30,
121
+ env=environment_without_linear_key(),
118
122
  )
119
123
  if result.returncode != 0:
120
124
  if allow_failure:
@@ -8,6 +8,7 @@ import re
8
8
  import subprocess
9
9
  from pathlib import Path
10
10
 
11
+ from devflow_cli.adapters.linear import environment_without_linear_key
11
12
  from devflow_cli.utils.logging import get_logger
12
13
 
13
14
  logger = get_logger(__name__)
@@ -206,6 +207,7 @@ def next_feature_number(project_dir: Path | None = None) -> int:
206
207
  text=True,
207
208
  cwd=str(base),
208
209
  timeout=5,
210
+ env=environment_without_linear_key(),
209
211
  )
210
212
  if result.returncode == 0:
211
213
  for line in result.stdout.splitlines():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: devflow-cli
3
- Version: 7.1.1
3
+ Version: 7.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
@@ -129,6 +129,22 @@ mise à jour. Voir [ADR-0002](docs/adr/0002-v5-rupture-and-provenance.md).
129
129
  Claude connues vers `~/.claude/.devflow-legacy-backup/` sans toucher aux
130
130
  commandes personnalisées.
131
131
 
132
+ ## Synchronisation Linear
133
+
134
+ Si `LINEAR_API_KEY` contient une clé d'API personnelle Linear, la CLI met à jour
135
+ le statut de l'issue du parcours au démarrage de chaque activité (`adaptive
136
+ run`, y compris avec `--until`, et clôture `done`). L'équipe Linear doit avoir
137
+ les statuts `Backlog`, `Design`, `In Progress`, `Review` et `Done`. Un statut
138
+ n'est jamais ramené en arrière ; un statut hors de cette liste (`Canceled`,
139
+ `Duplicate`) n'est jamais modifié.
140
+
141
+ Sans clé, ou si Linear échoue (issue inconnue, statut absent, erreur réseau ou
142
+ délai de 5 s dépassé), la CLI affiche un avertissement et le parcours continue
143
+ à l'identique. La clé est retirée de l'environnement de tout processus lancé
144
+ par Devflow (agents, commandes de validation, hooks, commandes git) ; elle reste
145
+ lisible par un agent si elle est définie dans un fichier que son shell charge (`~/.zshrc`). Détails :
146
+ [Référence CLI](docs/cli-reference.md#synchronisation-linear).
147
+
132
148
  ## Documentation
133
149
 
134
150
  - [Workflow adaptatif](docs/pipeline.md)
@@ -1,19 +1,19 @@
1
- devflow_cli/__init__.py,sha256=YRmOwDvGZfXzLf-ljn7cLGXMx2uLhcN-6E6OWUsRmsg,955
1
+ devflow_cli/__init__.py,sha256=6uOANM6Vfa9M0S7zoErecm-UQvyc77gqsxxEdBRDmHw,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=8_y70AIhmtuR8kyPN3Wpj0-i76k9wd_uaycVKTqC96g,26503
5
- devflow_cli/adapters/linear.py,sha256=MyqI2tHx7NNHY1KRfeYZ_Zjc-6_xhZPWD4oS-j7wnNY,2659
4
+ devflow_cli/adapters/agents.py,sha256=lq3OjcaT3Nt9-WV9Po4Dj1fag4getb7DEt9Y3Zp1js4,26683
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=NwJNXjJu8mDtn0epKjKrdBYsY_z0adNc4r5yHsXhdms,46241
7
+ devflow_cli/commands/adaptive.py,sha256=6wNnVgMklIpiFgxqzQfCFMrmwcRZE6EzkrxpDvkWtB8,46640
8
8
  devflow_cli/commands/assess.py,sha256=lyj6TI5hXUWX55dY589fG4bKJSMztvMPxZ0HZjo0O-w,5230
9
- devflow_cli/commands/check.py,sha256=M_sbcH1L3FEa4DF9gm46LEADlJndRKlaEDoysIBHv8k,6668
9
+ devflow_cli/commands/check.py,sha256=hsfeOg9yDOTAIvNm6XhRmTIFgv7w9wC8zvrwm4ojfZs,6923
10
10
  devflow_cli/commands/context.py,sha256=hwTitOHxIpJMx1X8R1jHHy9CpE4HVZpJKkyeOujxAio,6246
11
11
  devflow_cli/commands/docs_sync.py,sha256=Xc0mulpNTjDyeUDumKL7VNG0eumAf3016wEM_dqXpSU,1021
12
12
  devflow_cli/commands/export_cmd.py,sha256=_DVgQru8z7UM9br-ntzMkNPcDL9OpY1G6S3cMPJ4Gqo,3259
13
13
  devflow_cli/commands/extension.py,sha256=W2bc1xntJmDUrvLt8GFgg3lgTcJSwmGGTDPUkrxonBo,4494
14
14
  devflow_cli/commands/feature.py,sha256=gomtMUUJl-0YfZZIx0PHNHBX-Rq5GhmMZE-XkP55SwE,4200
15
15
  devflow_cli/commands/feedback.py,sha256=pKG4jCsyVcfTA7Df_qe8tYF1VzveLJ-WfR9BcV9ZtMM,10268
16
- devflow_cli/commands/hooks_cmd.py,sha256=440K9AmXt6nF99JV2v6ActvG7-Pp1u6rYScEHRNoibI,5924
16
+ devflow_cli/commands/hooks_cmd.py,sha256=BMzNBKHIpQkMj5y-2bxtCNqAa8cncGfGTWMgx6SUeeQ,6133
17
17
  devflow_cli/commands/init_cmd.py,sha256=VXR52ocqkO23J18pgij0Mhc_BTBl2PolxArjoCOuNow,5383
18
18
  devflow_cli/commands/linear.py,sha256=i066d5ryyBzZVfrSudbD0VWg9fSLivnB8yNe4vAMMW0,972
19
19
  devflow_cli/commands/stale.py,sha256=UiaUb48lrXmPyjfvbFkChfLE-rs58KhgKu-zgL_NBIo,3182
@@ -32,9 +32,9 @@ devflow_cli/core/automatic_evidence.py,sha256=Pjjy8yi_72Qo0-joKeWeZx6Zt0gutkFNaF
32
32
  devflow_cli/core/catalog.py,sha256=nLPc78JkbISaSxJnHlWiMi_lvepaumbfwcjgLuVy7Zc,3102
33
33
  devflow_cli/core/contract_schemas.py,sha256=4e29MPBpjKTeLm_SWd8LkjP_fWn7DHJQwQ86ujwUmos,5326
34
34
  devflow_cli/core/docgen.py,sha256=47JoVDWetkcctgHwJZC67AVebOKgl10VB9hZ3Xr63mo,3908
35
- devflow_cli/core/feedback.py,sha256=Nn-83BdzyZtAxiVn9eeLscuzoX-OBhRj6zSQIOnwxy8,18459
36
- devflow_cli/core/git.py,sha256=o7Vk_n8b3-cAQa3CR1FblTZUvMDez7Ik4D9lrYI0njY,2561
37
- devflow_cli/core/hooks.py,sha256=p_NC0mR0Qa0a-LvfWhDQqqcAwxC5qRRk-lPHKUHGpiA,7942
35
+ devflow_cli/core/feedback.py,sha256=5eePFprl51THrhFyE4uM4lPq630Z6_v9fODZPFTxqhk,18580
36
+ devflow_cli/core/git.py,sha256=gsDfT-hDNtMQFRUl1WzZSqltrdwZQ2BJBcCIh4TwDkA,2780
37
+ devflow_cli/core/hooks.py,sha256=nFPaziFJgeZgAPKPxbTibrB_0MJAST3cAmg3gtcvDBQ,8067
38
38
  devflow_cli/core/installer.py,sha256=MfcGZKoOcQc2qviNnGShoD0P_MqOgRuVHX7YwCrG2vw,6259
39
39
  devflow_cli/core/manifest.py,sha256=h058_m5-HJrYsNUdgfwnKts2riHiYPXUwMzPImHOs2g,7023
40
40
  devflow_cli/core/operational.py,sha256=ZEWh6DKJfHYLpV9r3vZwaDFREQwlJF0wTtgB1VleXog,1204
@@ -47,10 +47,10 @@ devflow_cli/core/telemetry.py,sha256=emcYo6Qikc86TYKT5Mc_9-3Thkt3nztkPZrpTy9VlvA
47
47
  devflow_cli/core/traceability.py,sha256=jfNE9MZzGkmaPer9Wa6Y18yA9Y3sqdeR-nzH9h_TB-8,2968
48
48
  devflow_cli/core/validation_approvals.py,sha256=_uwUYGclqSWfhKoQ0GVkf-iXnkeU2houD14uyn96teg,8409
49
49
  devflow_cli/core/validators.py,sha256=fREjEx2fBW36q3vB6WJNqoGUM9ejYlQUXie5A9lNuPs,7440
50
- devflow_cli/core/verification.py,sha256=YotT8eZFGQRdRrNFwiPGnFNmA2XQ-IjGIyaVi7shA0g,19858
50
+ devflow_cli/core/verification.py,sha256=gVPCkYfzG_v0MKFnvvpEhnpv5OHftrM3v5R9eEiQGA8,19983
51
51
  devflow_cli/core/waivers.py,sha256=xpfSiC4K51ZVlBOZTRNJ5ftS05-bsLyMW-W9CdQHqH4,11595
52
52
  devflow_cli/core/worklog.py,sha256=72neQL33LPRouj4R6ZJwZpidyYVgQk3r3hdaBWujAek,12090
53
- devflow_cli/core/worktree_snapshot.py,sha256=5ycGW5sYSeoht-ofUqoNhrscZuhTJlynfObzisJal90,4669
53
+ devflow_cli/core/worktree_snapshot.py,sha256=FTmpSn-O42PP1vOlnwG0nb9SUkzSCuVygb8edB2uKiw,4833
54
54
  devflow_cli/schemas/activity-contract-v4.schema.json,sha256=IeduWDiJ5IV6EL7FDZWnHBjzyUt1uwig2rB-SMbcpOU,2006
55
55
  devflow_cli/schemas/activity-result-v1.schema.json,sha256=xQuyD5fwHGIdeoJoDJxJn26qBGJX74lD6Dc5gi5CLQo,2772
56
56
  devflow_cli/schemas/assessment-v4.schema.json,sha256=byFBaoG_Vu-qS7vERREKThtpQa3dzNtPiFCMyEpWFVQ,2254
@@ -58,7 +58,7 @@ devflow_cli/schemas/workflow-state-v5.schema.json,sha256=I18nTSi52XDTJhMIDO_AZJB
58
58
  devflow_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
59
  devflow_cli/utils/console.py,sha256=t7cpyeEluWj4v_O_KFSI59iqXK4GiSXPuxlsr8q5zoQ,771
60
60
  devflow_cli/utils/logging.py,sha256=I7U9_IVvYAFnUAWi7xs5F_WGR0uSz6DmRvZ_B3xLFbs,1769
61
- devflow_cli/utils/paths.py,sha256=fzw86Y_xzqXosRCL76c-m__-LWfYoNOhk-p2ESnwJDk,8051
61
+ devflow_cli/utils/paths.py,sha256=CH81dZZTI2vjgl8mck6QcAChxoBpujmKZ0vVKhKmXEY,8176
62
62
  devflow_cli/utils/strings.py,sha256=bfFXCwBDnFxshgZOYfy6mtysnjiaWO1wFnqBf7HLg4o,351
63
63
  devflow_cli/resources/commands/devflow.context.md,sha256=SdvZ286cJKkgQIKYwnjd30v5zDeEXTFBhpEEsEOInoc,2048
64
64
  devflow_cli/resources/commands/devflow.extension.md,sha256=w7hoFfxRS0y-3rsvUmQDx1fvZhtE9KhANmRgFDwYVaA,2373
@@ -86,8 +86,8 @@ devflow_cli/resources/templates/contracts/api-template.md,sha256=rC5v7JBz71PznCn
86
86
  devflow_cli/resources/templates/contracts/components-template.md,sha256=XIWfEXfC72sNmL5OA3V-7xIywkNDU2DBPLfyiagOWcs,484
87
87
  devflow_cli/resources/templates/contracts/interfaces-template.md,sha256=kf8B7b6HsmN7jyRlLNU7RnZl0rTMlI5s97yVRyI-6yc,336
88
88
  devflow_cli/resources/templates/contracts/services-template.md,sha256=cFafKCyLmq_a7aR7G0LXAaMO2z4Fuflq6iKdxfVg0vg,477
89
- devflow_cli-7.1.1.dist-info/METADATA,sha256=eVG11bf7WSfH7l5IOP8PHN_l5B4Mu4uaE5qZs36bClA,5742
90
- devflow_cli-7.1.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
91
- devflow_cli-7.1.1.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
92
- devflow_cli-7.1.1.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
93
- devflow_cli-7.1.1.dist-info/RECORD,,
89
+ devflow_cli-7.2.0.dist-info/METADATA,sha256=KbyPQIqAxLJO8bWSrJBVcJhYKKp1aaxWnzFPZvFxyCs,6700
90
+ devflow_cli-7.2.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
91
+ devflow_cli-7.2.0.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
92
+ devflow_cli-7.2.0.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
93
+ devflow_cli-7.2.0.dist-info/RECORD,,