project-kit-cli 0.1.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.
Files changed (49) hide show
  1. project_kit/__init__.py +8 -0
  2. project_kit/__main__.py +5 -0
  3. project_kit/agents.py +81 -0
  4. project_kit/ai_attribution.py +69 -0
  5. project_kit/approval.py +36 -0
  6. project_kit/assets/hooks/commit-msg +14 -0
  7. project_kit/assets/project/workflow.yml +63 -0
  8. project_kit/assets/skills/pk-decide/SKILL.md +63 -0
  9. project_kit/assets/skills/pk-discover/SKILL.md +57 -0
  10. project_kit/assets/skills/pk-handoff/SKILL.md +43 -0
  11. project_kit/assets/skills/pk-plan/SKILL.md +55 -0
  12. project_kit/assets/skills/pk-provision/SKILL.md +45 -0
  13. project_kit/assets/skills/pk-start/SKILL.md +47 -0
  14. project_kit/assets/skills/pk-workflow/SKILL.md +51 -0
  15. project_kit/assets/templates/adr.md +32 -0
  16. project_kit/assets/templates/backlog.example.json +59 -0
  17. project_kit/assets/templates/brief.md +77 -0
  18. project_kit/assets/templates/constitution-input.md +78 -0
  19. project_kit/assets/templates/development-workflow.md +51 -0
  20. project_kit/assets/templates/github/ISSUE_TEMPLATE/adr.yml +21 -0
  21. project_kit/assets/templates/github/ISSUE_TEMPLATE/bug.yml +25 -0
  22. project_kit/assets/templates/github/ISSUE_TEMPLATE/config.yml +1 -0
  23. project_kit/assets/templates/github/ISSUE_TEMPLATE/epic.yml +15 -0
  24. project_kit/assets/templates/github/ISSUE_TEMPLATE/feature.yml +22 -0
  25. project_kit/assets/templates/github/pull_request_template.md +21 -0
  26. project_kit/assets/templates/interview.md +68 -0
  27. project_kit/backlog.py +312 -0
  28. project_kit/cli.py +286 -0
  29. project_kit/config.py +139 -0
  30. project_kit/doctor.py +187 -0
  31. project_kit/fsutil.py +27 -0
  32. project_kit/gh.py +152 -0
  33. project_kit/handoff.py +360 -0
  34. project_kit/help.py +152 -0
  35. project_kit/installer.py +100 -0
  36. project_kit/labels.py +17 -0
  37. project_kit/project_choice.py +127 -0
  38. project_kit/providers/__init__.py +1 -0
  39. project_kit/providers/github.py +329 -0
  40. project_kit/provision.py +260 -0
  41. project_kit/provision_issues.py +360 -0
  42. project_kit/state.py +115 -0
  43. project_kit/templating.py +23 -0
  44. project_kit/workflow.py +168 -0
  45. project_kit_cli-0.1.0.dist-info/METADATA +97 -0
  46. project_kit_cli-0.1.0.dist-info/RECORD +49 -0
  47. project_kit_cli-0.1.0.dist-info/WHEEL +4 -0
  48. project_kit_cli-0.1.0.dist-info/entry_points.txt +3 -0
  49. project_kit_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
project_kit/state.py ADDED
@@ -0,0 +1,115 @@
1
+ """.project-kit/state.json: where the project is and what exists in the provider.
2
+
3
+ The ``provider_ids`` map (``kind -> local_id -> provider_id``) is what makes provisioning
4
+ resumable: after a partial failure, items already created are looked up here instead of
5
+ being created again.
6
+ """
7
+
8
+ import json
9
+ from dataclasses import dataclass, field
10
+ from datetime import UTC, datetime
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from project_kit.config import PROJECT_DIR
15
+ from project_kit.fsutil import atomic_write_text
16
+
17
+ STATE_VERSION = 1
18
+ PHASES = ("init", "discover", "decide", "workflow", "plan", "provision", "handoff")
19
+
20
+
21
+ class StateError(ValueError):
22
+ """The state file is unreadable or invalid."""
23
+
24
+
25
+ def state_path(root: Path) -> Path:
26
+ return root / PROJECT_DIR / "state.json"
27
+
28
+
29
+ @dataclass
30
+ class State:
31
+ phase: str = "init"
32
+ approvals: dict[str, str] = field(default_factory=dict)
33
+ provider_ids: dict[str, dict[str, str]] = field(default_factory=dict)
34
+
35
+ def validate(self) -> None:
36
+ problems = []
37
+ if self.phase not in PHASES:
38
+ problems.append(f"phase must be one of {', '.join(PHASES)}, got {self.phase!r}")
39
+ for phase, when in self.approvals.items():
40
+ if phase not in PHASES:
41
+ problems.append(f"approvals has unknown phase {phase!r}")
42
+ if not isinstance(when, str):
43
+ problems.append(f"approvals.{phase} must be a timestamp string")
44
+ for kind, ids in self.provider_ids.items():
45
+ if not isinstance(ids, dict) or not all(
46
+ isinstance(k, str) and isinstance(v, str) for k, v in ids.items()
47
+ ):
48
+ problems.append(f"provider_ids.{kind} must map local ids to provider ids (strings)")
49
+ if problems:
50
+ raise StateError("Invalid state: " + "; ".join(problems))
51
+
52
+ def approve(self, phase: str, when: datetime | None = None) -> None:
53
+ if phase not in PHASES:
54
+ raise StateError(f"Unknown phase {phase!r}")
55
+ self.approvals[phase] = (when or datetime.now(UTC)).isoformat(timespec="seconds")
56
+
57
+ def is_approved(self, phase: str) -> bool:
58
+ return phase in self.approvals
59
+
60
+ def set_id(self, kind: str, local_id: str, provider_id: str) -> None:
61
+ self.provider_ids.setdefault(kind, {})[local_id] = provider_id
62
+
63
+ def get_id(self, kind: str, local_id: str) -> str | None:
64
+ return self.provider_ids.get(kind, {}).get(local_id)
65
+
66
+ def to_dict(self) -> dict[str, Any]:
67
+ return {
68
+ "version": STATE_VERSION,
69
+ "phase": self.phase,
70
+ "approvals": self.approvals,
71
+ "provider_ids": self.provider_ids,
72
+ }
73
+
74
+
75
+ def parse_state(text: str) -> State:
76
+ try:
77
+ data = json.loads(text)
78
+ except json.JSONDecodeError as exc:
79
+ raise StateError(
80
+ f"state.json is not valid JSON ({exc}). It is written atomically, so it was "
81
+ "probably edited by hand; fix or delete it."
82
+ ) from exc
83
+ if not isinstance(data, dict):
84
+ raise StateError("state.json must contain an object")
85
+
86
+ version = data.get("version")
87
+ if version != STATE_VERSION:
88
+ hint = (
89
+ " Upgrade project-kit." if isinstance(version, int) and version > STATE_VERSION else ""
90
+ )
91
+ raise StateError(f"Unsupported state.json version {version!r}.{hint}")
92
+
93
+ state = State(
94
+ phase=data.get("phase", "init"),
95
+ approvals=data.get("approvals", {}),
96
+ provider_ids=data.get("provider_ids", {}),
97
+ )
98
+ state.validate()
99
+ return state
100
+
101
+
102
+ def load_state(root: Path) -> State:
103
+ """Load the state, or a fresh one if the project has none yet."""
104
+ try:
105
+ text = state_path(root).read_text(encoding="utf-8")
106
+ except FileNotFoundError:
107
+ return State()
108
+ return parse_state(text)
109
+
110
+
111
+ def save_state(root: Path, state: State) -> None:
112
+ state.validate()
113
+ atomic_write_text(
114
+ state_path(root), json.dumps(state.to_dict(), indent=2, sort_keys=True) + "\n"
115
+ )
@@ -0,0 +1,23 @@
1
+ """Minimal ``{{variable}}`` substitution for the templates project-kit ships.
2
+
3
+ Deliberately strict: an unknown variable is an error, so a typo in a template that the
4
+ project has overridden is reported instead of leaking ``{{...}}`` into generated files.
5
+ """
6
+
7
+ import re
8
+
9
+ _VARIABLE = re.compile(r"\{\{\s*([A-Za-z_][\w.]*)\s*\}\}")
10
+
11
+
12
+ class TemplateError(ValueError):
13
+ """The template uses a variable that was not provided."""
14
+
15
+
16
+ def render(text: str, variables: dict[str, str]) -> str:
17
+ unknown = sorted({name for name in _VARIABLE.findall(text) if name not in variables})
18
+ if unknown:
19
+ known = ", ".join(sorted(variables))
20
+ raise TemplateError(
21
+ f"Unknown template variable(s): {', '.join(unknown)}. Available: {known}."
22
+ )
23
+ return _VARIABLE.sub(lambda m: variables[m.group(1)], text)
@@ -0,0 +1,168 @@
1
+ """.project-kit/workflow.yml: the ordered phases of the project-definition flow.
2
+
3
+ Modelled on Spec Kit's ``workflow.yml``: a list of steps, each one with an optional review
4
+ gate. The skills and ``project-kit status`` read it to know what comes next, and
5
+ ``project-kit approve`` uses it to refuse a gate while an earlier enabled gate is still open.
6
+ """
7
+
8
+ from dataclasses import dataclass, field
9
+ from importlib.resources import files
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ import yaml
14
+
15
+ from project_kit.config import PROJECT_DIR
16
+ from project_kit.state import PHASES, State
17
+
18
+ SCHEMA_VERSION = "1.0"
19
+ _STEP_KEYS = {"id", "title", "skill", "gate", "enabled", "produces"}
20
+
21
+
22
+ class WorkflowError(ValueError):
23
+ """The workflow file is missing, unreadable or invalid."""
24
+
25
+
26
+ def workflow_path(root: Path) -> Path:
27
+ return root / PROJECT_DIR / "workflow.yml"
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class Step:
32
+ id: str
33
+ title: str
34
+ skill: str
35
+ gate: bool = True
36
+ enabled: bool = True
37
+ produces: tuple[str, ...] = ()
38
+
39
+
40
+ @dataclass
41
+ class Workflow:
42
+ steps: list[Step] = field(default_factory=list)
43
+
44
+ def enabled_steps(self) -> list[Step]:
45
+ return [s for s in self.steps if s.enabled]
46
+
47
+ def open_gates_before(self, phase: str, state: State) -> list[Step]:
48
+ """Enabled steps with a gate, earlier than ``phase``, that nobody has approved yet."""
49
+ later = PHASES.index(phase)
50
+ return [
51
+ s
52
+ for s in self.enabled_steps()
53
+ if s.gate and PHASES.index(s.id) < later and not state.is_approved(s.id)
54
+ ]
55
+
56
+ def next_step(self, state: State) -> Step | None:
57
+ """First enabled step that is not done. ``init`` is done once the project exists."""
58
+ for step in self.enabled_steps():
59
+ if step.id != "init" and not state.is_approved(step.id):
60
+ return step
61
+ return None
62
+
63
+
64
+ def _flag(raw: dict[str, Any], key: str, where: str, problems: list[str]) -> bool:
65
+ value = raw.get(key, True)
66
+ if not isinstance(value, bool):
67
+ problems.append(f"{where}.{key} must be true or false")
68
+ return True
69
+ return value
70
+
71
+
72
+ def parse_workflow(text: str) -> Workflow:
73
+ try:
74
+ data = yaml.safe_load(text)
75
+ except yaml.YAMLError as exc:
76
+ raise WorkflowError(f"workflow.yml is not valid YAML: {exc}") from exc
77
+ if not isinstance(data, dict):
78
+ raise WorkflowError("workflow.yml must contain a mapping")
79
+ if str(data.get("schema_version")) != SCHEMA_VERSION:
80
+ raise WorkflowError(f"Unsupported workflow.yml schema_version; expected {SCHEMA_VERSION!r}")
81
+ raw_steps = data.get("steps")
82
+ if not isinstance(raw_steps, list) or not raw_steps:
83
+ raise WorkflowError("workflow.yml needs a non-empty `steps` list")
84
+
85
+ problems: list[str] = []
86
+ steps: list[Step] = []
87
+ for n, raw in enumerate(raw_steps, 1):
88
+ where = f"steps[{n}]"
89
+ if not isinstance(raw, dict):
90
+ problems.append(f"{where} must be a mapping")
91
+ continue
92
+ unknown = set(raw) - _STEP_KEYS
93
+ if unknown:
94
+ problems.append(f"{where} has unknown key(s): {', '.join(sorted(map(str, unknown)))}")
95
+ step_id = raw.get("id")
96
+ if step_id not in PHASES:
97
+ problems.append(f"{where}.id must be one of {', '.join(PHASES)}, got {step_id!r}")
98
+ continue
99
+ produces = raw.get("produces") or []
100
+ if not isinstance(produces, list) or not all(isinstance(p, str) for p in produces):
101
+ problems.append(f"{where}.produces must be a list of paths")
102
+ produces = []
103
+ title, skill = raw.get("title"), raw.get("skill")
104
+ for key, value in (("title", title), ("skill", skill)):
105
+ if not isinstance(value, str) or not value.strip():
106
+ problems.append(f"{where}.{key} must be a non-empty string")
107
+ steps.append(
108
+ Step(
109
+ id=step_id,
110
+ title=title if isinstance(title, str) else "",
111
+ skill=skill if isinstance(skill, str) else "",
112
+ gate=_flag(raw, "gate", where, problems),
113
+ enabled=_flag(raw, "enabled", where, problems),
114
+ produces=tuple(produces),
115
+ )
116
+ )
117
+
118
+ ids = [s.id for s in steps]
119
+ if len(set(ids)) != len(ids):
120
+ problems.append("steps must not repeat an id")
121
+ if [i for i in PHASES if i in ids] != ids:
122
+ problems.append(f"steps must follow this order: {', '.join(PHASES)}")
123
+ if problems:
124
+ raise WorkflowError("Invalid workflow.yml: " + "; ".join(problems))
125
+ return Workflow(steps)
126
+
127
+
128
+ def default_workflow() -> Workflow:
129
+ text = (files("project_kit") / "assets" / "project" / "workflow.yml").read_text("utf-8")
130
+ return parse_workflow(text)
131
+
132
+
133
+ def load_workflow(root: Path) -> Workflow:
134
+ """The project's workflow, or the shipped one for projects created before it existed."""
135
+ path = workflow_path(root)
136
+ if not path.exists():
137
+ return default_workflow()
138
+ return parse_workflow(path.read_text(encoding="utf-8"))
139
+
140
+
141
+ def step_marks(workflow: Workflow, state: State) -> list[tuple[Step, str, str]]:
142
+ """Each step with its mark (``x`` done, ``>`` next, ``-`` skipped, blank pending) and note."""
143
+ next_step = workflow.next_step(state)
144
+ marks = []
145
+ for step in workflow.steps:
146
+ if not step.enabled:
147
+ mark, note = "-", "skipped (disabled in workflow.yml)"
148
+ elif step.id == "init" or state.is_approved(step.id):
149
+ mark, note = "x", "done"
150
+ elif step is next_step:
151
+ mark, note = ">", "next"
152
+ else:
153
+ mark, note = " ", "pending"
154
+ marks.append((step, mark, note))
155
+ return marks
156
+
157
+
158
+ def render_status(workflow: Workflow, state: State, project_name: str) -> str:
159
+ next_step = workflow.next_step(state)
160
+ lines = [f"Project: {project_name}", ""]
161
+ for step, mark, note in step_marks(workflow, state):
162
+ lines.append(f" [{mark}] {step.id:<10} /{step.skill:<12} {note}")
163
+ lines.append("")
164
+ if next_step is None:
165
+ lines.append("Every enabled phase is done.")
166
+ else:
167
+ lines.append(f"Next: /{next_step.skill}: {next_step.title}")
168
+ return "\n".join(lines)
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.4
2
+ Name: project-kit-cli
3
+ Version: 0.1.0
4
+ Summary: Project definition and dev-workflow kit that hands off to Spec Kit
5
+ Keywords: project-management,spec-kit,claude-code,workflow,github
6
+ Author: Miguel Tomás
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development
15
+ Requires-Dist: pyyaml>=6.0.3
16
+ Requires-Python: >=3.12
17
+ Project-URL: Repository, https://github.com/miguelmatg/project-kit
18
+ Project-URL: Issues, https://github.com/miguelmatg/project-kit/issues
19
+ Description-Content-Type: text/markdown
20
+
21
+ # project-kit
22
+
23
+ Herramienta para el **antes** de un proyecto: definirlo, planificarlo, decidir su
24
+ arquitectura y montar su flujo de desarrollo y gestión (GitHub primero, GitLab después).
25
+ Al terminar entrega el relevo a [Spec Kit](https://github.com/github/spec-kit), que define e
26
+ implementa cada spec.
27
+
28
+ > Estado: diseño y esqueleto del paquete (v0.1 en desarrollo). Ver [`docs/design.md`](docs/design.md).
29
+
30
+ ## Instalación y uso
31
+
32
+ Requisitos: [uv](https://docs.astral.sh/uv/), `git`, y [`gh`](https://cli.github.com/) con
33
+ sesión iniciada y los scopes `repo` y `project` (`gh auth refresh -s project`) para publicar en
34
+ GitHub. [Claude Code](https://claude.com/claude-code) es el agente que guía las fases.
35
+
36
+ ```
37
+ # Instalar (ahora, desde un clon; cuando se publique será `uv tool install project-kit-cli`; el comando es `project-kit`)
38
+ uv tool install --editable <ruta-a-este-clon>
39
+
40
+ # En el repositorio del proyecto (puede estar vacío)
41
+ project-kit doctor # comprueba las herramientas
42
+ project-kit init # instala skills y plantillas; pregunta si la IA firma commits y si abrir Claude
43
+ project-kit help # guía visual del flujo y los comandos (también: `project-kit` a secas)
44
+ ```
45
+
46
+ `init` copia los ficheros y, al terminar, muestra los agentes de IA soportados (hoy solo Claude
47
+ Code) y ofrece abrirlo en esa misma terminal ya con **`/pk-start`** (Intro acepta). Se puede
48
+ evitar con `--agent none`; sin terminal no pregunta ni lanza nada, y dentro de Claude Code no
49
+ abre otro. Si lo prefieres, ábrelo tú y ejecuta `/pk-start`. Pregunta si quieres **solo
50
+ inicializar** o **empezar a definir el proyecto**, y al terminar cada fase ofrece continuar o
51
+ parar. `project-kit status` muestra en qué fase estás y cuál toca.
52
+
53
+ | Fase | Skill | Qué hace | Salida |
54
+ |---|---|---|---|
55
+ | 0 | `/pk-start` | Comprueba el entorno y elige: solo inicializar o empezar | `.project-kit/` |
56
+ | 1 | `/pk-discover` | Entrevista sobre qué es el proyecto | `docs/project/brief.md` |
57
+ | 2 | `/pk-decide` | Guía las decisiones de stack y arquitectura | `docs/adr/*.md` |
58
+ | 3 | `/pk-workflow` | Acuerda el flujo de trabajo del equipo | `docs/development-workflow.md` |
59
+ | 4 | `/pk-plan` | Deriva fases, épicas y backlog | `roadmap.md`, `backlog.json` |
60
+ | 5 | `/pk-provision` | Crea milestones, ramas, Project e issues | GitHub |
61
+ | 6 | `/pk-handoff` | Relevo a Spec Kit (una spec por issue) | `constitution-input.md` |
62
+
63
+ Cada fase termina con una aprobación (`project-kit approve <fase>`) y puede rellenarse a mano
64
+ a partir de las plantillas de `.project-kit/templates/`. Las fases se listan en
65
+ `.project-kit/workflow.yml`, con el mismo estilo que el de Spec Kit; para un proyecto pequeño se
66
+ desactiva una con `enabled: false`.
67
+
68
+ ## Documentación
69
+
70
+ - [Diseño](docs/design.md): alcance, fases, proveedor, relevo a Spec Kit, decisiones abiertas.
71
+ - [Publicación](docs/publishing.md): qué hace falta para `uv tool install project-kit-cli` público.
72
+ - [Esquema de `backlog.json`](docs/backlog-schema.md): formato del backlog y reglas de validación.
73
+ - [Relevo a Spec Kit](docs/handoff.md): qué genera `handoff`, cómo instala Spec Kit y qué viene después.
74
+ - [Provisionado en GitHub](docs/provisioning.md): qué crea `provision`, idempotencia y protección de ramas.
75
+ - [Referencia: playbook del flujo](docs/references/template-repo-playbook.md): receta de origen
76
+ (extraída de otro proyecto).
77
+
78
+ ## Licencia y contribuciones
79
+
80
+ MIT ([`LICENSE`](LICENSE)). Las issues con ideas y mejoras son bienvenidas; el código lo
81
+ desarrolla solo su autor y las pull requests están desactivadas ([`CONTRIBUTING.md`](CONTRIBUTING.md)).
82
+
83
+ ## Ramas
84
+
85
+ `main` (estable), `release` (pruebas) y `develop` (desarrollo). Todo entra por PR.
86
+
87
+ ## Desarrollo
88
+
89
+ Requiere [uv](https://docs.astral.sh/uv/).
90
+
91
+ ```
92
+ uv sync # entorno y dependencias
93
+ uv run project-kit --version
94
+ uv run pytest # tests
95
+ uv run ruff check . # lint
96
+ uv run ruff format . # formato
97
+ ```
@@ -0,0 +1,49 @@
1
+ project_kit/__init__.py,sha256=rfqHzTyFKfQC37a8Drkf3RPB3g_MFIdwPohAC_bEIN8,308
2
+ project_kit/__main__.py,sha256=30-4ifzqERnbwzERDfR8OIvPTAnCa1p4cRLKqJnXdR0,63
3
+ project_kit/agents.py,sha256=LSbperuz7qfNRPeQfu7e3DhjQ2KEJ4f7D5s6s8xvDkw,2575
4
+ project_kit/ai_attribution.py,sha256=SYmYUxNdwEFj0jL2ZIXvLCRfNM0Y6dBzT5N0yZn_BZU,2848
5
+ project_kit/approval.py,sha256=FNC40ZH_CslKSiGnoakPkkQH3YuQg2smny_YDnr6vW4,1490
6
+ project_kit/assets/hooks/commit-msg,sha256=hXK6LP8jC4bxq8AkfktQ6Ff65OEnAs_94fL9r1BZ8aU,573
7
+ project_kit/assets/project/workflow.yml,sha256=XVxUIuLAyVqs170lbseAXX6YiMdCao_udJmXx1F_tLM,1892
8
+ project_kit/assets/skills/pk-decide/SKILL.md,sha256=bXE7_fdKxs4pHC5Nsf_HelwGXKh6hlyG4-6HZwZt5TU,3572
9
+ project_kit/assets/skills/pk-discover/SKILL.md,sha256=ziwlpu5S_o1UG8wajV4BhNfrJi1scN_H1EFSkQvR_2o,3454
10
+ project_kit/assets/skills/pk-handoff/SKILL.md,sha256=uoxd_mST1OGDMRchTzQ7U9eEcF3O3ntsVuRk0k8CnrA,2613
11
+ project_kit/assets/skills/pk-plan/SKILL.md,sha256=9lRB97dEMtOnlxXjBrY-CrgaKUcRASpdvs-5rlP2qws,3174
12
+ project_kit/assets/skills/pk-provision/SKILL.md,sha256=HQgJusTvK41lhhtmk2tLDcvb7szPWzvKbw0CDA72IlU,2457
13
+ project_kit/assets/skills/pk-start/SKILL.md,sha256=5PrjdfdxlWwkjqV4LQS65bhDJiRiF5UPEz3LeaNKgd8,2852
14
+ project_kit/assets/skills/pk-workflow/SKILL.md,sha256=PO8IYLCcvzwlfGBobCKCbj7P7-wvMau0eF8QbkEyIzE,2654
15
+ project_kit/assets/templates/adr.md,sha256=akh47VtHesUwu04xqWuF0lMxtFqX_la2FUNiJdOdF5A,760
16
+ project_kit/assets/templates/backlog.example.json,sha256=J5ccoKNq5oiTkn6BwELOUDybsbvtUnhVrTy-N4Ti8IE,1745
17
+ project_kit/assets/templates/brief.md,sha256=acTirEJx_PzuYiXjscG3dh_Ca27KlL3UFxHj6X6I5_o,1502
18
+ project_kit/assets/templates/constitution-input.md,sha256=UzrkT4YvnA-tIfOZycwIp085ferK-pw47GZQOI8ooPk,3968
19
+ project_kit/assets/templates/development-workflow.md,sha256=7Y7mknEvGQ2RZMTBdkd2-m24veP3yTBArXCzttwYIhA,1397
20
+ project_kit/assets/templates/github/ISSUE_TEMPLATE/adr.yml,sha256=QZkRGgOR84jwH1oEtqf01J97Im97Hft9GOVUNG0Tzwk,459
21
+ project_kit/assets/templates/github/ISSUE_TEMPLATE/bug.yml,sha256=V4wW0mPYh7Nck9zVe1cg7I8l0iKK8hJTk_aTc6tRoyI,530
22
+ project_kit/assets/templates/github/ISSUE_TEMPLATE/config.yml,sha256=HxA8ap3QfNE6mm8XrOa4E_R3R-uct-AEiMsgc8qvkbs,28
23
+ project_kit/assets/templates/github/ISSUE_TEMPLATE/epic.yml,sha256=CV_bMgSimkG0CyD4IAwf7VTJpC_vyJrX3yhnp7jW0cw,309
24
+ project_kit/assets/templates/github/ISSUE_TEMPLATE/feature.yml,sha256=m-IT6NG7AESFuBr9B51mFmERS20Kd_jZG0Jl19JuH_s,475
25
+ project_kit/assets/templates/github/pull_request_template.md,sha256=VFVxbuFFVkYxfXi6R8j_v1fRhEbKEYUBjPmE20q3QAc,394
26
+ project_kit/assets/templates/interview.md,sha256=iQ74Ml5NkwAoFYsjiaFSuua16kxeqQN_s091-gXDm4E,3612
27
+ project_kit/backlog.py,sha256=sLY1TWynro9q9Nb3G1i2_KIwwJm4-T6PkpGRcw8vGFE,11247
28
+ project_kit/cli.py,sha256=NbB3WOO1IjDjuR-nzDkTVgMOnNJputi8SPWqFwzFkQI,9933
29
+ project_kit/config.py,sha256=PP222aKodd8Di1ShDxygJDRQ-JWH9BcN2hOzQHkTwxQ,4817
30
+ project_kit/doctor.py,sha256=qZ5GB_NJ_2BHvLwZmzr94XRh-tAC2W6b6vVv1qlyiUY,5790
31
+ project_kit/fsutil.py,sha256=MoocEm8qAvGw4HfAkQzWmwxEp6fId55dkQmi62j9n7c,951
32
+ project_kit/gh.py,sha256=u-PjjpCrgbHitr0tU-s2f7ntRAMk2YKldNDz-vhennw,5491
33
+ project_kit/handoff.py,sha256=289o1NUpRtCssvFU9KqnUmHCazwlnQ6zO0-xyjL4xPg,12490
34
+ project_kit/help.py,sha256=Y8e126puyP69St0t_7jz1gLYTC8_FLREti5ZgAnSpzw,5793
35
+ project_kit/installer.py,sha256=gYu-naIWu7X-PSBXl38m4RB4HMMmy_GsZLCnQU7wAao,3767
36
+ project_kit/labels.py,sha256=I5l_g7QRJWlzrZdoQWDuBM4hsn7HOeVn0fX1P8GfI_Y,764
37
+ project_kit/project_choice.py,sha256=D7yBF3VLQfQLc2KUQg4ubXZyeS06ygjgCelkOWu3_EM,4663
38
+ project_kit/providers/__init__.py,sha256=iV97GHRPiMZJq0w6j0lRV4FcYToM9m_5ccMioagdAT0,85
39
+ project_kit/providers/github.py,sha256=0ZBDqxWkWF1JTMnQ9qV6ivvEpMDmxwhW56CGP-NS7ck,12793
40
+ project_kit/provision.py,sha256=qecVdcgAGIJVNw7QuOfVXXzIqmaQmzMj19-aOpy6nWI,9651
41
+ project_kit/provision_issues.py,sha256=rr2wq5n-rFPNTo2okppW1L-bzRV8dbhQjyxEPX8OGEQ,14449
42
+ project_kit/state.py,sha256=S9d_2iPQuSi0hi2gdOwHT8zOSOlyNejhOjafJwbyFHU,4003
43
+ project_kit/templating.py,sha256=BV3e51DktlUxOtIcahp2iq8X0f63MfxZktFjh6Pwnpw,826
44
+ project_kit/workflow.py,sha256=C6PD-nTNIrzznn2aJ0L1qlGTnqu9ZC5dz0jFfKGNL00,6204
45
+ project_kit_cli-0.1.0.dist-info/licenses/LICENSE,sha256=dFazNRGw47ZuUFnKCZRvdgbjpEH2aO--gWslO1Ck3Us,1070
46
+ project_kit_cli-0.1.0.dist-info/WHEEL,sha256=9IUYslYuG_asT4WlWvfk_K76nveuLvSTKDoLeL0cV-Q,80
47
+ project_kit_cli-0.1.0.dist-info/entry_points.txt,sha256=HtNObc7MmuDfwtN4c9mktht-Sri29HVzEg3DSjtj9cE,54
48
+ project_kit_cli-0.1.0.dist-info/METADATA,sha256=uLIQ20pbse9sKYHzpcH5Vt33M0UFsFeHerk4d1YiiuQ,4708
49
+ project_kit_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.13.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ project-kit = project_kit.cli:main
3
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Miguel Tomás
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.