devflow-cli 2.0.0__py3-none-any.whl → 2.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 +12 -1
- devflow_cli/cli.py +2 -0
- devflow_cli/commands/migrate_speckit.py +219 -0
- devflow_cli/core/state.py +69 -1
- {devflow_cli-2.0.0.dist-info → devflow_cli-2.2.0.dist-info}/METADATA +2 -2
- {devflow_cli-2.0.0.dist-info → devflow_cli-2.2.0.dist-info}/RECORD +9 -8
- {devflow_cli-2.0.0.dist-info → devflow_cli-2.2.0.dist-info}/WHEEL +0 -0
- {devflow_cli-2.0.0.dist-info → devflow_cli-2.2.0.dist-info}/entry_points.txt +0 -0
- {devflow_cli-2.0.0.dist-info → devflow_cli-2.2.0.dist-info}/licenses/LICENSE +0 -0
devflow_cli/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""devflow CLI — Spec-Driven Development workflow."""
|
|
2
2
|
|
|
3
|
-
VERSION = "
|
|
3
|
+
VERSION = "2.2.0"
|
|
4
4
|
|
|
5
5
|
STEPS = [
|
|
6
6
|
"constitution", # Pré-requis projet (1/projet). Auto-complétée si .specify/memory/constitution.md existe.
|
|
@@ -51,6 +51,17 @@ STEP_LINEAR_MAPPING = {
|
|
|
51
51
|
|
|
52
52
|
SUPPORTED_AGENTS = ["claude-code", "cursor"]
|
|
53
53
|
|
|
54
|
+
# Mapping speckit → devflow pour la migration
|
|
55
|
+
SPECKIT_STEP_MAP = {
|
|
56
|
+
"specify": "spec",
|
|
57
|
+
"clarify": "clarify",
|
|
58
|
+
"plan": "plan",
|
|
59
|
+
"tasks": "tasks",
|
|
60
|
+
"implement": "implement",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
SPECKIT_IGNORED_STEPS = {"checklist", "analyze"}
|
|
64
|
+
|
|
54
65
|
ARTIFACTS = [
|
|
55
66
|
"spec.md",
|
|
56
67
|
"clarify-log.md",
|
devflow_cli/cli.py
CHANGED
|
@@ -15,6 +15,7 @@ from devflow_cli.commands.regen import regen
|
|
|
15
15
|
from devflow_cli.commands.migrate_cmd import migrate
|
|
16
16
|
from devflow_cli.commands.upgrade_cmd import upgrade
|
|
17
17
|
from devflow_cli.commands.rollback import rollback
|
|
18
|
+
from devflow_cli.commands.migrate_speckit import migrate_speckit
|
|
18
19
|
from devflow_cli.commands import extension
|
|
19
20
|
|
|
20
21
|
app = typer.Typer(
|
|
@@ -47,6 +48,7 @@ app.command()(regen)
|
|
|
47
48
|
app.command()(migrate)
|
|
48
49
|
app.command()(upgrade)
|
|
49
50
|
app.command()(rollback)
|
|
51
|
+
app.command(name="migrate-speckit")(migrate_speckit)
|
|
50
52
|
app.add_typer(extension.app, name="extension")
|
|
51
53
|
|
|
52
54
|
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""devflow migrate-speckit — Migration des projets speckit vers devflow."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from devflow_cli.core.state import (
|
|
12
|
+
is_speckit_state, convert_speckit_state, write_state,
|
|
13
|
+
)
|
|
14
|
+
from devflow_cli.core.manifest import compute_file_hash
|
|
15
|
+
from devflow_cli.utils.console import console, ok, info, warn, header
|
|
16
|
+
from devflow_cli.utils.paths import get_devflow_root, get_claude_dir, get_specs_root
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def migrate_speckit(
|
|
20
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Affiche le plan de migration sans modifier de fichiers"),
|
|
21
|
+
clean_speckit: bool = typer.Option(False, "--clean-speckit", help="Supprime les commandes speckit et templates non personnalises"),
|
|
22
|
+
) -> None:
|
|
23
|
+
"""Migration des projets speckit vers le workflow devflow."""
|
|
24
|
+
specs_root = get_specs_root()
|
|
25
|
+
|
|
26
|
+
if not specs_root.is_dir():
|
|
27
|
+
info("Rien a migrer : .specify/specs/ n'existe pas.")
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
# Scanner les features
|
|
31
|
+
features = sorted(
|
|
32
|
+
d for d in specs_root.iterdir()
|
|
33
|
+
if d.is_dir() and (d / "state.json").is_file()
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if not features:
|
|
37
|
+
info("Aucune feature speckit detectee.")
|
|
38
|
+
if clean_speckit:
|
|
39
|
+
_clean_speckit(dry_run)
|
|
40
|
+
else:
|
|
41
|
+
_suggest_clean_speckit()
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
# Filtrer : seulement les speckit states
|
|
45
|
+
speckit_features = []
|
|
46
|
+
devflow_features = []
|
|
47
|
+
error_features = []
|
|
48
|
+
|
|
49
|
+
for feat_dir in features:
|
|
50
|
+
state_file = feat_dir / "state.json"
|
|
51
|
+
if is_speckit_state(state_file):
|
|
52
|
+
speckit_features.append(feat_dir)
|
|
53
|
+
else:
|
|
54
|
+
# Verifier si c'est un devflow valide ou corrompu
|
|
55
|
+
try:
|
|
56
|
+
data = json.loads(state_file.read_text(encoding="utf-8"))
|
|
57
|
+
if "reviewIterations" in data:
|
|
58
|
+
devflow_features.append(feat_dir)
|
|
59
|
+
else:
|
|
60
|
+
error_features.append((feat_dir, "Format non reconnu"))
|
|
61
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
62
|
+
error_features.append((feat_dir, str(e)))
|
|
63
|
+
|
|
64
|
+
if not speckit_features and not error_features:
|
|
65
|
+
info("Aucune feature speckit detectee.")
|
|
66
|
+
if devflow_features:
|
|
67
|
+
info(f" {len(devflow_features)} feature(s) deja au format devflow.")
|
|
68
|
+
if clean_speckit:
|
|
69
|
+
_clean_speckit(dry_run)
|
|
70
|
+
else:
|
|
71
|
+
_suggest_clean_speckit()
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
header(f"devflow migrate-speckit — {len(speckit_features)} feature(s) speckit")
|
|
75
|
+
console.print()
|
|
76
|
+
|
|
77
|
+
if dry_run:
|
|
78
|
+
console.print("[dim]Mode dry-run : aucun fichier ne sera modifie.[/dim]")
|
|
79
|
+
console.print()
|
|
80
|
+
|
|
81
|
+
migrated = 0
|
|
82
|
+
|
|
83
|
+
for feat_dir in speckit_features:
|
|
84
|
+
state_file = feat_dir / "state.json"
|
|
85
|
+
try:
|
|
86
|
+
raw_data = json.loads(state_file.read_text(encoding="utf-8"))
|
|
87
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
88
|
+
warn(f" {feat_dir.name} : erreur lecture — {e}")
|
|
89
|
+
error_features.append((feat_dir, str(e)))
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
issue_id = raw_data.get("issueId", feat_dir.name)
|
|
93
|
+
from_step = raw_data.get("currentStep", "?")
|
|
94
|
+
converted = convert_speckit_state(raw_data)
|
|
95
|
+
to_step = converted.currentStep
|
|
96
|
+
|
|
97
|
+
if dry_run:
|
|
98
|
+
console.print(f" [bold]{feat_dir.name}[/bold] ({issue_id})")
|
|
99
|
+
console.print(f" Etape : {from_step} → {to_step}")
|
|
100
|
+
console.print(f" Champs ajoutes : reviewIterations, mode, skippedSteps")
|
|
101
|
+
else:
|
|
102
|
+
# Backup
|
|
103
|
+
shutil.copy2(state_file, state_file.with_suffix(".json.bak"))
|
|
104
|
+
# Ecrire le state converti
|
|
105
|
+
write_state(state_file, converted)
|
|
106
|
+
ok(f" {feat_dir.name} ({issue_id}) : {from_step} → {to_step}")
|
|
107
|
+
|
|
108
|
+
migrated += 1
|
|
109
|
+
|
|
110
|
+
# Constitution
|
|
111
|
+
if not dry_run:
|
|
112
|
+
_ensure_constitution()
|
|
113
|
+
|
|
114
|
+
# Rapport
|
|
115
|
+
console.print()
|
|
116
|
+
console.rule("Recapitulatif")
|
|
117
|
+
console.print(f" Migrees : {migrated}")
|
|
118
|
+
if devflow_features:
|
|
119
|
+
console.print(f" Ignorees : {len(devflow_features)} (deja devflow)")
|
|
120
|
+
if error_features:
|
|
121
|
+
console.print(f" Erreurs : {len(error_features)}")
|
|
122
|
+
for feat_dir, msg in error_features:
|
|
123
|
+
warn(f" {feat_dir.name} : {msg}")
|
|
124
|
+
|
|
125
|
+
if dry_run:
|
|
126
|
+
console.print()
|
|
127
|
+
console.print("[dim]Mode dry-run : aucun fichier modifie.[/dim]")
|
|
128
|
+
|
|
129
|
+
# Nettoyage speckit
|
|
130
|
+
if clean_speckit:
|
|
131
|
+
_clean_speckit(dry_run)
|
|
132
|
+
elif not dry_run:
|
|
133
|
+
_suggest_clean_speckit()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _ensure_constitution() -> None:
|
|
137
|
+
"""Cree une constitution starter si absente."""
|
|
138
|
+
const_dest = Path.cwd() / ".specify" / "memory" / "constitution.md"
|
|
139
|
+
if const_dest.is_file():
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
const_src = get_devflow_root() / "templates" / "constitution-template.md"
|
|
144
|
+
if const_src.is_file():
|
|
145
|
+
const_dest.parent.mkdir(parents=True, exist_ok=True)
|
|
146
|
+
shutil.copy2(const_src, const_dest)
|
|
147
|
+
ok("Constitution starter creee dans .specify/memory/constitution.md")
|
|
148
|
+
info(" Pensez a la personnaliser pour votre projet.")
|
|
149
|
+
else:
|
|
150
|
+
warn("Template constitution non trouve — creation ignoree.")
|
|
151
|
+
except Exception:
|
|
152
|
+
warn("Impossible de creer la constitution starter.")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _clean_speckit(dry_run: bool) -> None:
|
|
156
|
+
"""Supprime les commandes speckit et templates non personnalises."""
|
|
157
|
+
console.print()
|
|
158
|
+
header("Nettoyage speckit")
|
|
159
|
+
console.print()
|
|
160
|
+
|
|
161
|
+
# 1. Commandes speckit dans ~/.claude/commands/
|
|
162
|
+
claude_dir = get_claude_dir()
|
|
163
|
+
cmd_dir = claude_dir / "commands"
|
|
164
|
+
removed_cmds = 0
|
|
165
|
+
if cmd_dir.is_dir():
|
|
166
|
+
for f in sorted(cmd_dir.glob("speckit.*.md")):
|
|
167
|
+
if dry_run:
|
|
168
|
+
console.print(f" [dim](dry-run) Supprimerait {f.name}[/dim]")
|
|
169
|
+
else:
|
|
170
|
+
f.unlink()
|
|
171
|
+
console.print(f" Supprime : {f.name}")
|
|
172
|
+
removed_cmds += 1
|
|
173
|
+
|
|
174
|
+
# 2. Templates dans .specify/templates/
|
|
175
|
+
tpl_dir = Path.cwd() / ".specify" / "templates"
|
|
176
|
+
removed_tpls = 0
|
|
177
|
+
preserved_tpls = 0
|
|
178
|
+
if tpl_dir.is_dir():
|
|
179
|
+
try:
|
|
180
|
+
devflow_tpl_dir = get_devflow_root() / "templates"
|
|
181
|
+
except Exception:
|
|
182
|
+
devflow_tpl_dir = None
|
|
183
|
+
|
|
184
|
+
for f in sorted(tpl_dir.glob("*")):
|
|
185
|
+
if not f.is_file():
|
|
186
|
+
continue
|
|
187
|
+
# Comparer avec le template devflow source
|
|
188
|
+
if devflow_tpl_dir and (devflow_tpl_dir / f.name).is_file():
|
|
189
|
+
src_hash = compute_file_hash(devflow_tpl_dir / f.name)
|
|
190
|
+
installed_hash = compute_file_hash(f)
|
|
191
|
+
if src_hash == installed_hash:
|
|
192
|
+
if dry_run:
|
|
193
|
+
console.print(f" [dim](dry-run) Supprimerait {f.name}[/dim]")
|
|
194
|
+
else:
|
|
195
|
+
f.unlink()
|
|
196
|
+
console.print(f" Supprime : {f.name}")
|
|
197
|
+
removed_tpls += 1
|
|
198
|
+
else:
|
|
199
|
+
warn(f" Conserve (personnalise) : {f.name}")
|
|
200
|
+
preserved_tpls += 1
|
|
201
|
+
else:
|
|
202
|
+
# Pas de reference → conserver par securite
|
|
203
|
+
warn(f" Conserve (pas de reference) : {f.name}")
|
|
204
|
+
preserved_tpls += 1
|
|
205
|
+
|
|
206
|
+
console.print()
|
|
207
|
+
console.print(f" Commandes supprimees : {removed_cmds}")
|
|
208
|
+
console.print(f" Templates supprimes : {removed_tpls}")
|
|
209
|
+
if preserved_tpls:
|
|
210
|
+
console.print(f" Templates conserves : {preserved_tpls}")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _suggest_clean_speckit() -> None:
|
|
214
|
+
"""Suggere --clean-speckit si des commandes speckit sont detectees."""
|
|
215
|
+
claude_dir = get_claude_dir()
|
|
216
|
+
cmd_dir = claude_dir / "commands"
|
|
217
|
+
if cmd_dir.is_dir() and list(cmd_dir.glob("speckit.*.md")):
|
|
218
|
+
info("Des commandes speckit sont encore presentes dans ~/.claude/commands/.")
|
|
219
|
+
info("Utilisez --clean-speckit pour les supprimer.")
|
devflow_cli/core/state.py
CHANGED
|
@@ -7,7 +7,7 @@ from dataclasses import dataclass, field, asdict
|
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
-
from devflow_cli import STEPS, OPTIONAL_STEPS, STEP_ARTIFACTS
|
|
10
|
+
from devflow_cli import STEPS, OPTIONAL_STEPS, STEP_ARTIFACTS, SPECKIT_STEP_MAP, SPECKIT_IGNORED_STEPS
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@dataclass
|
|
@@ -262,6 +262,74 @@ def rollback_step(state: FeatureState) -> FeatureState | None:
|
|
|
262
262
|
return state
|
|
263
263
|
|
|
264
264
|
|
|
265
|
+
def is_speckit_state(state_file: Path) -> bool:
|
|
266
|
+
"""Detecte si un state.json est au format speckit (pas devflow).
|
|
267
|
+
|
|
268
|
+
Lit le JSON brut et verifie l'absence du champ 'reviewIterations'.
|
|
269
|
+
"""
|
|
270
|
+
try:
|
|
271
|
+
data = json.loads(state_file.read_text(encoding="utf-8"))
|
|
272
|
+
return isinstance(data, dict) and "reviewIterations" not in data
|
|
273
|
+
except (json.JSONDecodeError, OSError):
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def convert_speckit_state(raw_data: dict) -> FeatureState:
|
|
278
|
+
"""Convertit un state.json speckit brut en FeatureState devflow.
|
|
279
|
+
|
|
280
|
+
- Mappe les etapes via SPECKIT_STEP_MAP (specify→spec, etc.)
|
|
281
|
+
- Filtre les etapes ignorees (checklist, analyze)
|
|
282
|
+
- Injecte les etapes devflow intermediaires comme auto-validees
|
|
283
|
+
si l'utilisateur les a deja depassees
|
|
284
|
+
"""
|
|
285
|
+
issue_id = raw_data.get("issueId", "")
|
|
286
|
+
|
|
287
|
+
# Mapper currentStep
|
|
288
|
+
raw_current = raw_data.get("currentStep", "spec")
|
|
289
|
+
current_step = SPECKIT_STEP_MAP.get(raw_current, raw_current)
|
|
290
|
+
|
|
291
|
+
# Mapper completedSteps : convertir et filtrer
|
|
292
|
+
raw_completed = raw_data.get("completedSteps", [])
|
|
293
|
+
mapped_completed: list[str] = []
|
|
294
|
+
for step in raw_completed:
|
|
295
|
+
if step in SPECKIT_IGNORED_STEPS:
|
|
296
|
+
continue
|
|
297
|
+
mapped = SPECKIT_STEP_MAP.get(step, step)
|
|
298
|
+
if mapped in STEPS and mapped not in mapped_completed:
|
|
299
|
+
mapped_completed.append(mapped)
|
|
300
|
+
|
|
301
|
+
# Injecter les etapes devflow intermediaires auto-validees
|
|
302
|
+
# Pour chaque etape devflow, si elle precede une etape deja completee
|
|
303
|
+
# et n'est pas encore dans completed, l'injecter
|
|
304
|
+
review_iterations: dict[str, int] = {}
|
|
305
|
+
if current_step in STEPS:
|
|
306
|
+
current_idx = STEPS.index(current_step)
|
|
307
|
+
final_completed: list[str] = []
|
|
308
|
+
for i, step in enumerate(STEPS):
|
|
309
|
+
if step == current_step:
|
|
310
|
+
break
|
|
311
|
+
if step in mapped_completed:
|
|
312
|
+
final_completed.append(step)
|
|
313
|
+
elif i < current_idx:
|
|
314
|
+
# Etape intermediaire manquante → auto-valider
|
|
315
|
+
final_completed.append(step)
|
|
316
|
+
if step in ("review-spec", "review-tasks", "review-impl"):
|
|
317
|
+
review_iterations[step] = 0
|
|
318
|
+
mapped_completed = final_completed
|
|
319
|
+
|
|
320
|
+
return FeatureState(
|
|
321
|
+
issueId=issue_id,
|
|
322
|
+
currentStep=current_step,
|
|
323
|
+
completedSteps=mapped_completed,
|
|
324
|
+
stepTimestamps={},
|
|
325
|
+
reviewIterations=review_iterations,
|
|
326
|
+
mode="full",
|
|
327
|
+
skippedSteps=[],
|
|
328
|
+
createdAt=raw_data.get("createdAt", ""),
|
|
329
|
+
updatedAt=raw_data.get("updatedAt", ""),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
265
333
|
def validate_and_advance(
|
|
266
334
|
state: FeatureState,
|
|
267
335
|
completed_step: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devflow-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.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
|
|
@@ -117,7 +117,7 @@ devflow status
|
|
|
117
117
|
Consultez la [documentation complète](docs/index.md) pour :
|
|
118
118
|
|
|
119
119
|
- [Pipeline détaillé](docs/pipeline.md) — les 13 étapes expliquées
|
|
120
|
-
- [Commandes CLI](docs/cli-reference.md) — référence des
|
|
120
|
+
- [Commandes CLI](docs/cli-reference.md) — référence des 12 commandes terminal
|
|
121
121
|
- [Commandes slash](docs/commands-reference.md) — référence des 23 commandes Claude Code
|
|
122
122
|
- [Guides par type de projet](docs/index.md#guides-par-type-de-projet) — brownfield, greenfield, microservices
|
|
123
123
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
devflow_cli/__init__.py,sha256=
|
|
2
|
-
devflow_cli/cli.py,sha256=
|
|
1
|
+
devflow_cli/__init__.py,sha256=VCGzz5uVlYBpsURHQZexi_70jMAIuYAHQWhxTfVjDjs,1643
|
|
2
|
+
devflow_cli/cli.py,sha256=HxxFDISXAYvv5dMvI_xNx1mhUdhQSsa_fqvFtzsN_bM,1576
|
|
3
3
|
devflow_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
devflow_cli/commands/check.py,sha256=sb5zkSXXwl91o8yn1-TRL5wH89MWX-kY2CAHYYjMxq4,4170
|
|
5
5
|
devflow_cli/commands/context.py,sha256=hwTitOHxIpJMx1X8R1jHHy9CpE4HVZpJKkyeOujxAio,6246
|
|
@@ -8,6 +8,7 @@ devflow_cli/commands/extension.py,sha256=gL33YS8EuBTADmqKGDqw_0gJXFwccwg1uJmCNtv
|
|
|
8
8
|
devflow_cli/commands/feature.py,sha256=EceMCxYEif4P6Ir-1XZml4txLH9iqm3jEAeS6dJ8LpA,4098
|
|
9
9
|
devflow_cli/commands/init_cmd.py,sha256=LLUJx_Kdl-YCEuy1corvaYe65pZ6R5-VnVBal6wZSa4,4513
|
|
10
10
|
devflow_cli/commands/migrate_cmd.py,sha256=y2_yWyBabMgDZ0e2CsX2nYtb33yQRyr4zcrj858Ieoo,3274
|
|
11
|
+
devflow_cli/commands/migrate_speckit.py,sha256=NbHgTbrWi9vtvD1OQz0VLSSY5hv68KlU_Fsh-ovlHx8,7870
|
|
11
12
|
devflow_cli/commands/regen.py,sha256=UyB7_0oBxDs_CqaAQ6yE2Yoz4IcpUr2bqkIEmm0e1iM,3198
|
|
12
13
|
devflow_cli/commands/rollback.py,sha256=B-Qnmem_sAwmSwyFdzpzglbeX4uLd0t4ohsLYyHngl4,1789
|
|
13
14
|
devflow_cli/commands/status.py,sha256=nPuudZPqCFWE2JqiCFOMKlN_CM_uDwD-gq9MbHBhg20,4775
|
|
@@ -20,14 +21,14 @@ devflow_cli/core/installer.py,sha256=XGuugkWAV7McTnpdNZ2JX4NtEAE8CI73mfrYnKlb-JQ
|
|
|
20
21
|
devflow_cli/core/manifest.py,sha256=5hu2w6PPGo4raGbgKzx6qpAtt653SrzpRkbWngHM9qw,5770
|
|
21
22
|
devflow_cli/core/scanner.py,sha256=bcoC91Nc0Hw2P7aHpL-9ITxynLZhLTEKpOXo553a-18,4395
|
|
22
23
|
devflow_cli/core/staleness.py,sha256=ASe-rlYN17AtpI_S4QlidaHwbtv7E7L4fhRGYtyTlPE,5586
|
|
23
|
-
devflow_cli/core/state.py,sha256=
|
|
24
|
+
devflow_cli/core/state.py,sha256=EvfX-s3mBLIaOI6OzpRkeQQfdb2mB7S8UZ2EGJ0ZSYk,12038
|
|
24
25
|
devflow_cli/core/validators.py,sha256=3PtqC-R2kqNbh4HK_l_Yl8RylQsFdtCXhopt0yT065k,6194
|
|
25
26
|
devflow_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
27
|
devflow_cli/utils/console.py,sha256=t7cpyeEluWj4v_O_KFSI59iqXK4GiSXPuxlsr8q5zoQ,771
|
|
27
28
|
devflow_cli/utils/paths.py,sha256=9J2Qhq2Ot6p7HQwRAfdK3V5bc5sme6FnnnAg9dhXcP4,4212
|
|
28
29
|
devflow_cli/utils/strings.py,sha256=bfFXCwBDnFxshgZOYfy6mtysnjiaWO1wFnqBf7HLg4o,351
|
|
29
|
-
devflow_cli-2.
|
|
30
|
-
devflow_cli-2.
|
|
31
|
-
devflow_cli-2.
|
|
32
|
-
devflow_cli-2.
|
|
33
|
-
devflow_cli-2.
|
|
30
|
+
devflow_cli-2.2.0.dist-info/METADATA,sha256=I2tCqZ3vTDs_DjcIcOwGbgG3LHlze_x1BtxmIm8uoQg,4896
|
|
31
|
+
devflow_cli-2.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
32
|
+
devflow_cli-2.2.0.dist-info/entry_points.txt,sha256=GVJ6bMvFpqyO5589iC4nFKeGTpGIW8aKn_QPPpSZ6kU,48
|
|
33
|
+
devflow_cli-2.2.0.dist-info/licenses/LICENSE,sha256=v79PvnmqOdKhQiOa_QcWxng4q8GuGscLOwi7ygUpwpo,1070
|
|
34
|
+
devflow_cli-2.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|