project-kit-cli 0.2.0__tar.gz → 0.4.0__tar.gz
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.
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/PKG-INFO +90 -28
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/README.md +89 -27
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/pyproject.toml +2 -2
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/pyproject.toml.orig +2 -2
- project_kit_cli-0.4.0/src/project_kit/__init__.py +14 -0
- project_kit_cli-0.4.0/src/project_kit/agents.py +179 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/hooks/commit-msg +2 -1
- project_kit_cli-0.4.0/src/project_kit/assets/project/skills-catalog.yml +184 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/project/workflow.yml +11 -1
- project_kit_cli-0.4.0/src/project_kit/assets/skills/pk-amend/SKILL.md +52 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/skills/pk-decide/SKILL.md +5 -3
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/skills/pk-discover/SKILL.md +4 -4
- project_kit_cli-0.4.0/src/project_kit/assets/skills/pk-foundations/SKILL.md +64 -0
- project_kit_cli-0.4.0/src/project_kit/assets/skills/pk-grill/SKILL.md +52 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/skills/pk-handoff/SKILL.md +9 -7
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/skills/pk-plan/SKILL.md +6 -5
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/skills/pk-provision/SKILL.md +15 -5
- project_kit_cli-0.4.0/src/project_kit/assets/skills/pk-skills/SKILL.md +54 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/skills/pk-start/SKILL.md +15 -10
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/skills/pk-workflow/SKILL.md +5 -3
- project_kit_cli-0.4.0/src/project_kit/assets/templates/gitlab/issue_templates/adr.md +9 -0
- project_kit_cli-0.4.0/src/project_kit/assets/templates/gitlab/issue_templates/bug.md +11 -0
- project_kit_cli-0.4.0/src/project_kit/assets/templates/gitlab/issue_templates/epic.md +7 -0
- project_kit_cli-0.4.0/src/project_kit/assets/templates/gitlab/issue_templates/feature.md +11 -0
- project_kit_cli-0.4.0/src/project_kit/assets/templates/gitlab/merge_request_templates/Default.md +21 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/interview.md +5 -3
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/cli.py +215 -24
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/config.py +19 -3
- project_kit_cli-0.4.0/src/project_kit/context_files.py +125 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/doctor.py +83 -4
- project_kit_cli-0.4.0/src/project_kit/entry.py +36 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/fsutil.py +17 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/gh.py +9 -5
- project_kit_cli-0.4.0/src/project_kit/glab.py +130 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/handoff.py +71 -13
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/help.py +23 -16
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/installer.py +15 -11
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/providers/github.py +12 -4
- project_kit_cli-0.4.0/src/project_kit/providers/gitlab.py +157 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/provision.py +7 -2
- project_kit_cli-0.4.0/src/project_kit/provision_gitlab.py +194 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/provision_issues.py +1 -1
- project_kit_cli-0.4.0/src/project_kit/skills.py +282 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/state.py +10 -1
- project_kit_cli-0.4.0/src/project_kit/statusline.py +347 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/upgrade.py +14 -8
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/workflow.py +27 -9
- project_kit_cli-0.2.0/src/project_kit/__init__.py +0 -8
- project_kit_cli-0.2.0/src/project_kit/agents.py +0 -81
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/LICENSE +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/__main__.py +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/ai_attribution.py +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/approval.py +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/adr.md +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/backlog.example.json +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/brief.md +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/constitution-input.md +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/development-workflow.md +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/github/ISSUE_TEMPLATE/adr.yml +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/github/ISSUE_TEMPLATE/bug.yml +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/github/ISSUE_TEMPLATE/config.yml +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/github/ISSUE_TEMPLATE/epic.yml +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/github/ISSUE_TEMPLATE/feature.yml +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/assets/templates/github/pull_request_template.md +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/backlog.py +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/labels.py +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/manifest.py +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/project_choice.py +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/providers/__init__.py +0 -0
- {project_kit_cli-0.2.0 → project_kit_cli-0.4.0}/src/project_kit/templating.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: project-kit-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Project definition and dev-workflow kit that hands off to Spec Kit
|
|
5
5
|
Keywords: project-management,spec-kit,claude-code,workflow,github
|
|
6
6
|
Author: Miguel Tomás
|
|
@@ -33,13 +33,15 @@ idea ──▶ brief ──▶ decisions (ADRs) ──▶ workflow ──▶ bac
|
|
|
33
33
|
/pk-discover /pk-decide /pk-workflow /pk-plan /pk-provision /pk-handoff
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
The conversations run in your AI agent (
|
|
36
|
+
The conversations run in your AI agent ([Claude Code](https://claude.com/claude-code), Codex CLI,
|
|
37
|
+
Gemini CLI, GitHub Copilot or Cursor) as
|
|
37
38
|
`/pk-…` skills: one question at a time, a recommended answer for each, and everything written to
|
|
38
39
|
documents in your repository. Every phase ends with your approval, and every document can be
|
|
39
40
|
written by hand instead.
|
|
40
41
|
|
|
41
|
-
> **Status: alpha.** It works end to end on GitHub with Claude Code.
|
|
42
|
-
>
|
|
42
|
+
> **Status: alpha.** It works end to end on GitHub with Claude Code. Other agents get their skills
|
|
43
|
+
> and context files but are started by hand. **GitLab support is new and has only been tried
|
|
44
|
+
> against a simulated API**, not a real GitLab project: read the `--dry-run` before applying.
|
|
43
45
|
|
|
44
46
|
## Requirements
|
|
45
47
|
|
|
@@ -47,24 +49,26 @@ written by hand instead.
|
|
|
47
49
|
- `git`
|
|
48
50
|
- [Claude Code](https://claude.com/claude-code), to run the guided phases
|
|
49
51
|
- [`gh`](https://cli.github.com/), logged in with the `repo` and `project` scopes
|
|
50
|
-
(`gh auth login`, then `gh auth refresh -s project`), to create things on GitHub
|
|
52
|
+
(`gh auth login`, then `gh auth refresh -s project`), to create things on GitHub; or
|
|
53
|
+
[`glab`](https://gitlab.com/gitlab-org/cli) (`glab auth login`) for GitLab; or neither, with
|
|
54
|
+
`--provider none`
|
|
51
55
|
|
|
52
|
-
`
|
|
56
|
+
`pjkit doctor` checks all of this for you. Spec Kit is installed for you at the end, if
|
|
53
57
|
you want it.
|
|
54
58
|
|
|
55
59
|
## Install
|
|
56
60
|
|
|
57
61
|
```bash
|
|
58
|
-
uv tool install project-kit-cli # installs the `
|
|
62
|
+
uv tool install project-kit-cli # installs the `pjkit` command
|
|
59
63
|
uv tool upgrade project-kit-cli # later, to update the program
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
The package is called `project-kit-cli`; the command is `
|
|
63
|
-
installing: `uvx --from project-kit-cli
|
|
66
|
+
The package is called `project-kit-cli`; the command is `pjkit`. To try it without
|
|
67
|
+
installing: `uvx --from project-kit-cli pjkit help`.
|
|
64
68
|
|
|
65
69
|
**Updating a project.** `init` copies the skills and templates into your repository and never
|
|
66
70
|
overwrites them, so a new version of the program does not change a project that already exists.
|
|
67
|
-
Run `
|
|
71
|
+
Run `pjkit upgrade` inside it to see what is out of date, and `pjkit upgrade --apply`
|
|
68
72
|
to bring it up to date. Files you edited are never overwritten; see
|
|
69
73
|
[upgrading](https://github.com/miguelmatg/project-kit/blob/main/docs/upgrade.md).
|
|
70
74
|
|
|
@@ -74,7 +78,7 @@ In your repository, new or existing:
|
|
|
74
78
|
|
|
75
79
|
```bash
|
|
76
80
|
cd my-project
|
|
77
|
-
|
|
81
|
+
pjkit init
|
|
78
82
|
```
|
|
79
83
|
|
|
80
84
|
`init` copies the skills and templates into the repository and then offers to open Claude Code
|
|
@@ -83,8 +87,8 @@ right there, already running `/pk-start` (press Enter to accept). `/pk-start` as
|
|
|
83
87
|
whether to continue or stop. You can leave at any point and come back later:
|
|
84
88
|
|
|
85
89
|
```bash
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
pjkit status # where you are and what comes next
|
|
91
|
+
pjkit help # a visual guide to the flow and the commands
|
|
88
92
|
```
|
|
89
93
|
|
|
90
94
|
## The phases
|
|
@@ -95,11 +99,17 @@ project-kit help # a visual guide to the flow and the commands
|
|
|
95
99
|
| 1 | `/pk-discover` | Interviews you about the project: problem, users, scope, constraints | `docs/project/brief.md` |
|
|
96
100
|
| 2 | `/pk-decide` | Guides the technical decisions: monolith or monorepo, language, package manager, data, auth, hosting | `docs/adr/*.md` |
|
|
97
101
|
| 3 | `/pk-workflow` | Agrees how the team works: branches, reviews, quality gates | `docs/development-workflow.md` |
|
|
98
|
-
| 4 | `/pk-
|
|
99
|
-
| 5 | `/pk-
|
|
100
|
-
| 6 | `/pk-
|
|
102
|
+
| 4 | `/pk-foundations` | Writes the project's own rules from its stack: code standards, testing, security, documentation | `docs/project/standards.md`, `testing.md`, `security.md`, `documentation.md` |
|
|
103
|
+
| 5 | `/pk-plan` | Derives phases, epics and a backlog from all of the above | `docs/project/roadmap.md`, `.project-kit/backlog.json` |
|
|
104
|
+
| 6 | `/pk-provision` | Creates milestones, labels, branches, the GitHub Project and the issues (skipped with `provider: none`) | GitHub |
|
|
105
|
+
| 7 | `/pk-handoff` | Prepares the Spec Kit constitution; each issue becomes a spec; writes `CLAUDE.md` / `AGENTS.md` | `.project-kit/handoff/constitution-input.md` |
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
Outside the sequence, three skills are always available: `/pk-grill` interviews you hard and
|
|
108
|
+
cross-checks the project's documents for contradictions (it is offered before each approval),
|
|
109
|
+
`/pk-skills` recommends third-party skills that fit the project, and `/pk-amend` brings a change
|
|
110
|
+
after the plan (a new epic, a decision that replaces another) into the documents and the backlog.
|
|
111
|
+
|
|
112
|
+
Each phase ends with an explicit approval (`pjkit approve <phase>`), and a phase cannot be
|
|
103
113
|
approved while an earlier one is still open. The phases are listed in `.project-kit/workflow.yml`;
|
|
104
114
|
for a small project, switch one off with `enabled: false`.
|
|
105
115
|
|
|
@@ -119,18 +129,70 @@ It never rewrites issues that already exist, and re-running it is always safe.
|
|
|
119
129
|
|
|
120
130
|
| Command | What it does |
|
|
121
131
|
|---|---|
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
132
|
+
| `pjkit init` | Install the skills, templates and workflow in the current directory |
|
|
133
|
+
| `pjkit status` | Show the phases and what comes next |
|
|
134
|
+
| `pjkit upgrade [--apply] [--adopt] [--diff]` | Update the skills and templates a project already has |
|
|
135
|
+
| `pjkit help` | Show a visual guide (also what plain `pjkit` prints) |
|
|
136
|
+
| `pjkit doctor` | Check that git, gh, uv and Spec Kit are available |
|
|
137
|
+
| `pjkit approve <phase>` | Record your sign-off of a phase |
|
|
138
|
+
| `pjkit backlog validate` | Validate `.project-kit/backlog.json` |
|
|
139
|
+
| `pjkit provision [--dry-run] [--project X]` | Create milestones, branches, the Project and issues on GitHub |
|
|
140
|
+
| `pjkit handoff [--install-speckit]` | Generate the Spec Kit constitution input |
|
|
141
|
+
| `pjkit statusline [--install]` | One line for Claude Code's status bar: model, context, usage limits and the project phase |
|
|
142
|
+
| `pjkit context [--dry-run]` | Write or refresh the project block in `CLAUDE.md` / `AGENTS.md` / `GEMINI.md` |
|
|
143
|
+
| `pjkit skills list\|show\|mark` | Recommended third-party skills, their install command, and what the project decided |
|
|
131
144
|
|
|
132
145
|
Useful `init` options: `--project-name`, `--language` (language of the generated documents),
|
|
133
|
-
`--
|
|
146
|
+
`--agents claude,codex,...` (the AI agents you use; asked when there is a terminal),
|
|
147
|
+
`--agent claude|none` (open the agent or not), `--provider github|gitlab|none` and
|
|
148
|
+
`--ai-coauthor keep|remove`.
|
|
149
|
+
|
|
150
|
+
## AI agents
|
|
151
|
+
|
|
152
|
+
Like Spec Kit, `init` asks which agents you use, and you can pick several: Claude Code, Codex
|
|
153
|
+
CLI, Gemini CLI, GitHub Copilot and Cursor. The skills are installed where each one reads them
|
|
154
|
+
(`.claude/skills` for Claude Code, `.agents/skills` for the rest) and, at the end of the handoff, project-kit
|
|
155
|
+
writes the context file each one reads (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`). Only the block
|
|
156
|
+
between its markers is managed; the rest of the file is yours. Only Claude Code is started by
|
|
157
|
+
`init`; for the others, open the agent and ask it to run the `pk-start` skill.
|
|
158
|
+
|
|
159
|
+
## Status bar
|
|
160
|
+
|
|
161
|
+
Claude Code can show a line of your choosing at the bottom of the screen. `pjkit statusline`
|
|
162
|
+
fills it with the model, the context in use, the 5-hour and 7-day usage limits (Pro and Max
|
|
163
|
+
plans) and where the project is in the flow:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
[Opus · high] · ctx 23% · 5h 41% (↻ 17:00) · 7d 12% · ~$1.23 · fase: decide → /pk-decide
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`init` asks whether you want it, and `pjkit statusline --install` sets it up in
|
|
170
|
+
`.claude/settings.json` (`--remove` undoes it). It is never turned on without a yes. If you
|
|
171
|
+
already have a status bar, yours is kept and runs first. It is one line: when the terminal is
|
|
172
|
+
narrow, the cost goes first, then the 7-day limit, the effort and the context. The cost is an
|
|
173
|
+
estimate at list price. More in
|
|
174
|
+
[docs/statusline.md](https://github.com/miguelmatg/project-kit/blob/main/docs/statusline.md).
|
|
175
|
+
|
|
176
|
+
## GitLab
|
|
177
|
+
|
|
178
|
+
`pjkit init --provider gitlab` makes `provision` use `glab`: phases become milestones, kind,
|
|
179
|
+
priority, size and status become labels, and an issue board named `project-kit` is built from the
|
|
180
|
+
status labels. Details in [provisioning](https://github.com/miguelmatg/project-kit/blob/main/docs/provisioning.md).
|
|
181
|
+
|
|
182
|
+
## Generating a project without GitHub
|
|
183
|
+
|
|
184
|
+
`pjkit init --provider none` generates everything that lives in files (brief, decisions, workflow,
|
|
185
|
+
rules, roadmap, backlog, constitution input, context files) and skips `provision`; `doctor` does
|
|
186
|
+
not ask for `gh` either.
|
|
187
|
+
|
|
188
|
+
## Recommended skills
|
|
189
|
+
|
|
190
|
+
project-kit keeps a catalog of third-party skills (UI/UX, motion, accessibility, SEO, security,
|
|
191
|
+
architecture, browser testing, `grill-me`, `find-skills`...) in `.project-kit/skills-catalog.yml`.
|
|
192
|
+
`/pk-skills` recommends the ones that fit your brief and decisions, warns when a task would
|
|
193
|
+
benefit from one you do not have, and records the answer in `.project-kit/skills.yml`. project-kit
|
|
194
|
+
never installs a skill: it shows the command and you run it; `pjkit skills detect` records the ones you already have. How the entries were chosen is in
|
|
195
|
+
[docs/skills.md](https://github.com/miguelmatg/project-kit/blob/main/docs/skills.md).
|
|
134
196
|
|
|
135
197
|
## AI co-author
|
|
136
198
|
|
|
@@ -153,7 +215,7 @@ From there Spec Kit does the work. Each issue of the backlog is one spec: its te
|
|
|
153
215
|
|
|
154
216
|
The constitution is generated from your own rules: the branch pattern and merge strategy you agreed,
|
|
155
217
|
the decisions on record, and an index of where each kind of rule lives (including the GitHub
|
|
156
|
-
Project used for tasks). Once the constitution is written, `
|
|
218
|
+
Project used for tasks). Once the constitution is written, `pjkit approve handoff` deletes
|
|
157
219
|
the temporary input files; from then on the constitution is the single source.
|
|
158
220
|
|
|
159
221
|
## What it adds to your repository
|
|
@@ -13,13 +13,15 @@ idea ──▶ brief ──▶ decisions (ADRs) ──▶ workflow ──▶ bac
|
|
|
13
13
|
/pk-discover /pk-decide /pk-workflow /pk-plan /pk-provision /pk-handoff
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
The conversations run in your AI agent (
|
|
16
|
+
The conversations run in your AI agent ([Claude Code](https://claude.com/claude-code), Codex CLI,
|
|
17
|
+
Gemini CLI, GitHub Copilot or Cursor) as
|
|
17
18
|
`/pk-…` skills: one question at a time, a recommended answer for each, and everything written to
|
|
18
19
|
documents in your repository. Every phase ends with your approval, and every document can be
|
|
19
20
|
written by hand instead.
|
|
20
21
|
|
|
21
|
-
> **Status: alpha.** It works end to end on GitHub with Claude Code.
|
|
22
|
-
>
|
|
22
|
+
> **Status: alpha.** It works end to end on GitHub with Claude Code. Other agents get their skills
|
|
23
|
+
> and context files but are started by hand. **GitLab support is new and has only been tried
|
|
24
|
+
> against a simulated API**, not a real GitLab project: read the `--dry-run` before applying.
|
|
23
25
|
|
|
24
26
|
## Requirements
|
|
25
27
|
|
|
@@ -27,24 +29,26 @@ written by hand instead.
|
|
|
27
29
|
- `git`
|
|
28
30
|
- [Claude Code](https://claude.com/claude-code), to run the guided phases
|
|
29
31
|
- [`gh`](https://cli.github.com/), logged in with the `repo` and `project` scopes
|
|
30
|
-
(`gh auth login`, then `gh auth refresh -s project`), to create things on GitHub
|
|
32
|
+
(`gh auth login`, then `gh auth refresh -s project`), to create things on GitHub; or
|
|
33
|
+
[`glab`](https://gitlab.com/gitlab-org/cli) (`glab auth login`) for GitLab; or neither, with
|
|
34
|
+
`--provider none`
|
|
31
35
|
|
|
32
|
-
`
|
|
36
|
+
`pjkit doctor` checks all of this for you. Spec Kit is installed for you at the end, if
|
|
33
37
|
you want it.
|
|
34
38
|
|
|
35
39
|
## Install
|
|
36
40
|
|
|
37
41
|
```bash
|
|
38
|
-
uv tool install project-kit-cli # installs the `
|
|
42
|
+
uv tool install project-kit-cli # installs the `pjkit` command
|
|
39
43
|
uv tool upgrade project-kit-cli # later, to update the program
|
|
40
44
|
```
|
|
41
45
|
|
|
42
|
-
The package is called `project-kit-cli`; the command is `
|
|
43
|
-
installing: `uvx --from project-kit-cli
|
|
46
|
+
The package is called `project-kit-cli`; the command is `pjkit`. To try it without
|
|
47
|
+
installing: `uvx --from project-kit-cli pjkit help`.
|
|
44
48
|
|
|
45
49
|
**Updating a project.** `init` copies the skills and templates into your repository and never
|
|
46
50
|
overwrites them, so a new version of the program does not change a project that already exists.
|
|
47
|
-
Run `
|
|
51
|
+
Run `pjkit upgrade` inside it to see what is out of date, and `pjkit upgrade --apply`
|
|
48
52
|
to bring it up to date. Files you edited are never overwritten; see
|
|
49
53
|
[upgrading](https://github.com/miguelmatg/project-kit/blob/main/docs/upgrade.md).
|
|
50
54
|
|
|
@@ -54,7 +58,7 @@ In your repository, new or existing:
|
|
|
54
58
|
|
|
55
59
|
```bash
|
|
56
60
|
cd my-project
|
|
57
|
-
|
|
61
|
+
pjkit init
|
|
58
62
|
```
|
|
59
63
|
|
|
60
64
|
`init` copies the skills and templates into the repository and then offers to open Claude Code
|
|
@@ -63,8 +67,8 @@ right there, already running `/pk-start` (press Enter to accept). `/pk-start` as
|
|
|
63
67
|
whether to continue or stop. You can leave at any point and come back later:
|
|
64
68
|
|
|
65
69
|
```bash
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
pjkit status # where you are and what comes next
|
|
71
|
+
pjkit help # a visual guide to the flow and the commands
|
|
68
72
|
```
|
|
69
73
|
|
|
70
74
|
## The phases
|
|
@@ -75,11 +79,17 @@ project-kit help # a visual guide to the flow and the commands
|
|
|
75
79
|
| 1 | `/pk-discover` | Interviews you about the project: problem, users, scope, constraints | `docs/project/brief.md` |
|
|
76
80
|
| 2 | `/pk-decide` | Guides the technical decisions: monolith or monorepo, language, package manager, data, auth, hosting | `docs/adr/*.md` |
|
|
77
81
|
| 3 | `/pk-workflow` | Agrees how the team works: branches, reviews, quality gates | `docs/development-workflow.md` |
|
|
78
|
-
| 4 | `/pk-
|
|
79
|
-
| 5 | `/pk-
|
|
80
|
-
| 6 | `/pk-
|
|
82
|
+
| 4 | `/pk-foundations` | Writes the project's own rules from its stack: code standards, testing, security, documentation | `docs/project/standards.md`, `testing.md`, `security.md`, `documentation.md` |
|
|
83
|
+
| 5 | `/pk-plan` | Derives phases, epics and a backlog from all of the above | `docs/project/roadmap.md`, `.project-kit/backlog.json` |
|
|
84
|
+
| 6 | `/pk-provision` | Creates milestones, labels, branches, the GitHub Project and the issues (skipped with `provider: none`) | GitHub |
|
|
85
|
+
| 7 | `/pk-handoff` | Prepares the Spec Kit constitution; each issue becomes a spec; writes `CLAUDE.md` / `AGENTS.md` | `.project-kit/handoff/constitution-input.md` |
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
Outside the sequence, three skills are always available: `/pk-grill` interviews you hard and
|
|
88
|
+
cross-checks the project's documents for contradictions (it is offered before each approval),
|
|
89
|
+
`/pk-skills` recommends third-party skills that fit the project, and `/pk-amend` brings a change
|
|
90
|
+
after the plan (a new epic, a decision that replaces another) into the documents and the backlog.
|
|
91
|
+
|
|
92
|
+
Each phase ends with an explicit approval (`pjkit approve <phase>`), and a phase cannot be
|
|
83
93
|
approved while an earlier one is still open. The phases are listed in `.project-kit/workflow.yml`;
|
|
84
94
|
for a small project, switch one off with `enabled: false`.
|
|
85
95
|
|
|
@@ -99,18 +109,70 @@ It never rewrites issues that already exist, and re-running it is always safe.
|
|
|
99
109
|
|
|
100
110
|
| Command | What it does |
|
|
101
111
|
|---|---|
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
112
|
+
| `pjkit init` | Install the skills, templates and workflow in the current directory |
|
|
113
|
+
| `pjkit status` | Show the phases and what comes next |
|
|
114
|
+
| `pjkit upgrade [--apply] [--adopt] [--diff]` | Update the skills and templates a project already has |
|
|
115
|
+
| `pjkit help` | Show a visual guide (also what plain `pjkit` prints) |
|
|
116
|
+
| `pjkit doctor` | Check that git, gh, uv and Spec Kit are available |
|
|
117
|
+
| `pjkit approve <phase>` | Record your sign-off of a phase |
|
|
118
|
+
| `pjkit backlog validate` | Validate `.project-kit/backlog.json` |
|
|
119
|
+
| `pjkit provision [--dry-run] [--project X]` | Create milestones, branches, the Project and issues on GitHub |
|
|
120
|
+
| `pjkit handoff [--install-speckit]` | Generate the Spec Kit constitution input |
|
|
121
|
+
| `pjkit statusline [--install]` | One line for Claude Code's status bar: model, context, usage limits and the project phase |
|
|
122
|
+
| `pjkit context [--dry-run]` | Write or refresh the project block in `CLAUDE.md` / `AGENTS.md` / `GEMINI.md` |
|
|
123
|
+
| `pjkit skills list\|show\|mark` | Recommended third-party skills, their install command, and what the project decided |
|
|
111
124
|
|
|
112
125
|
Useful `init` options: `--project-name`, `--language` (language of the generated documents),
|
|
113
|
-
`--
|
|
126
|
+
`--agents claude,codex,...` (the AI agents you use; asked when there is a terminal),
|
|
127
|
+
`--agent claude|none` (open the agent or not), `--provider github|gitlab|none` and
|
|
128
|
+
`--ai-coauthor keep|remove`.
|
|
129
|
+
|
|
130
|
+
## AI agents
|
|
131
|
+
|
|
132
|
+
Like Spec Kit, `init` asks which agents you use, and you can pick several: Claude Code, Codex
|
|
133
|
+
CLI, Gemini CLI, GitHub Copilot and Cursor. The skills are installed where each one reads them
|
|
134
|
+
(`.claude/skills` for Claude Code, `.agents/skills` for the rest) and, at the end of the handoff, project-kit
|
|
135
|
+
writes the context file each one reads (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`). Only the block
|
|
136
|
+
between its markers is managed; the rest of the file is yours. Only Claude Code is started by
|
|
137
|
+
`init`; for the others, open the agent and ask it to run the `pk-start` skill.
|
|
138
|
+
|
|
139
|
+
## Status bar
|
|
140
|
+
|
|
141
|
+
Claude Code can show a line of your choosing at the bottom of the screen. `pjkit statusline`
|
|
142
|
+
fills it with the model, the context in use, the 5-hour and 7-day usage limits (Pro and Max
|
|
143
|
+
plans) and where the project is in the flow:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
[Opus · high] · ctx 23% · 5h 41% (↻ 17:00) · 7d 12% · ~$1.23 · fase: decide → /pk-decide
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`init` asks whether you want it, and `pjkit statusline --install` sets it up in
|
|
150
|
+
`.claude/settings.json` (`--remove` undoes it). It is never turned on without a yes. If you
|
|
151
|
+
already have a status bar, yours is kept and runs first. It is one line: when the terminal is
|
|
152
|
+
narrow, the cost goes first, then the 7-day limit, the effort and the context. The cost is an
|
|
153
|
+
estimate at list price. More in
|
|
154
|
+
[docs/statusline.md](https://github.com/miguelmatg/project-kit/blob/main/docs/statusline.md).
|
|
155
|
+
|
|
156
|
+
## GitLab
|
|
157
|
+
|
|
158
|
+
`pjkit init --provider gitlab` makes `provision` use `glab`: phases become milestones, kind,
|
|
159
|
+
priority, size and status become labels, and an issue board named `project-kit` is built from the
|
|
160
|
+
status labels. Details in [provisioning](https://github.com/miguelmatg/project-kit/blob/main/docs/provisioning.md).
|
|
161
|
+
|
|
162
|
+
## Generating a project without GitHub
|
|
163
|
+
|
|
164
|
+
`pjkit init --provider none` generates everything that lives in files (brief, decisions, workflow,
|
|
165
|
+
rules, roadmap, backlog, constitution input, context files) and skips `provision`; `doctor` does
|
|
166
|
+
not ask for `gh` either.
|
|
167
|
+
|
|
168
|
+
## Recommended skills
|
|
169
|
+
|
|
170
|
+
project-kit keeps a catalog of third-party skills (UI/UX, motion, accessibility, SEO, security,
|
|
171
|
+
architecture, browser testing, `grill-me`, `find-skills`...) in `.project-kit/skills-catalog.yml`.
|
|
172
|
+
`/pk-skills` recommends the ones that fit your brief and decisions, warns when a task would
|
|
173
|
+
benefit from one you do not have, and records the answer in `.project-kit/skills.yml`. project-kit
|
|
174
|
+
never installs a skill: it shows the command and you run it; `pjkit skills detect` records the ones you already have. How the entries were chosen is in
|
|
175
|
+
[docs/skills.md](https://github.com/miguelmatg/project-kit/blob/main/docs/skills.md).
|
|
114
176
|
|
|
115
177
|
## AI co-author
|
|
116
178
|
|
|
@@ -133,7 +195,7 @@ From there Spec Kit does the work. Each issue of the backlog is one spec: its te
|
|
|
133
195
|
|
|
134
196
|
The constitution is generated from your own rules: the branch pattern and merge strategy you agreed,
|
|
135
197
|
the decisions on record, and an index of where each kind of rule lives (including the GitHub
|
|
136
|
-
Project used for tasks). Once the constitution is written, `
|
|
198
|
+
Project used for tasks). Once the constitution is written, `pjkit approve handoff` deletes
|
|
137
199
|
the temporary input files; from then on the constitution is the single source.
|
|
138
200
|
|
|
139
201
|
## What it adds to your repository
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "project-kit-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Project definition and dev-workflow kit that hands off to Spec Kit"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -31,7 +31,7 @@ Repository = "https://github.com/miguelmatg/project-kit"
|
|
|
31
31
|
Issues = "https://github.com/miguelmatg/project-kit/issues"
|
|
32
32
|
|
|
33
33
|
[project.scripts]
|
|
34
|
-
|
|
34
|
+
pjkit = "project_kit.entry:main"
|
|
35
35
|
|
|
36
36
|
[tool.uv.build-backend]
|
|
37
37
|
module-name = "project_kit"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "project-kit-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Project definition and dev-workflow kit that hands off to Spec Kit"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -25,7 +25,7 @@ Repository = "https://github.com/miguelmatg/project-kit"
|
|
|
25
25
|
Issues = "https://github.com/miguelmatg/project-kit/issues"
|
|
26
26
|
|
|
27
27
|
[project.scripts]
|
|
28
|
-
|
|
28
|
+
pjkit = "project_kit.entry:main"
|
|
29
29
|
|
|
30
30
|
[tool.uv.build-backend]
|
|
31
31
|
# The distribution is project-kit-cli (the name free on PyPI); the package keeps its name.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""project-kit: define, plan and set up a project before handing off to Spec Kit."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def __getattr__(name: str) -> str:
|
|
5
|
+
"""``__version__`` is looked up on first use: reading package metadata costs more than the
|
|
6
|
+
whole status line command, which runs at every update of the agent's status bar."""
|
|
7
|
+
if name == "__version__":
|
|
8
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
return version("project-kit-cli")
|
|
12
|
+
except PackageNotFoundError: # running from a source tree that was never installed
|
|
13
|
+
return "0.0.0"
|
|
14
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""AI coding agents project-kit installs for, and can hand the terminal over to after ``init``.
|
|
2
|
+
|
|
3
|
+
An agent is whatever reads the installed skills. Each one has a directory where it looks for
|
|
4
|
+
skills and a context file it reads at the start of a session; several agents may share both
|
|
5
|
+
(``.agents/skills`` and ``AGENTS.md`` are read by Codex, Cursor, Copilot and Gemini CLI).
|
|
6
|
+
Only an agent with a ``prompt`` can be started by ``init``: the others are opened by the user,
|
|
7
|
+
because how a skill is invoked differs between tools and project-kit does not guess it.
|
|
8
|
+
Supporting another agent is a new entry in ``AGENTS``.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class Agent:
|
|
21
|
+
id: str
|
|
22
|
+
name: str
|
|
23
|
+
executable: str
|
|
24
|
+
install_hint: str
|
|
25
|
+
skills_dir: str # where the agent looks for skills, relative to the repository
|
|
26
|
+
context_file: str # the file it reads for the project's context, relative to the repository
|
|
27
|
+
speckit_key: str = "" # its name in `specify init --integration`
|
|
28
|
+
prompt: str = "" # what it is started with (the entry-point skill); empty: not launchable
|
|
29
|
+
inside_env: str = "" # environment variable it sets in the shells it opens, if any
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
AGENTS = (
|
|
33
|
+
Agent(
|
|
34
|
+
id="claude",
|
|
35
|
+
name="Claude Code",
|
|
36
|
+
executable="claude",
|
|
37
|
+
install_hint="https://claude.com/claude-code",
|
|
38
|
+
skills_dir=".claude/skills",
|
|
39
|
+
context_file="CLAUDE.md",
|
|
40
|
+
speckit_key="claude",
|
|
41
|
+
prompt="/pk-start",
|
|
42
|
+
inside_env="CLAUDECODE",
|
|
43
|
+
),
|
|
44
|
+
Agent(
|
|
45
|
+
id="codex",
|
|
46
|
+
name="Codex CLI",
|
|
47
|
+
executable="codex",
|
|
48
|
+
install_hint="https://developers.openai.com/codex/cli",
|
|
49
|
+
skills_dir=".agents/skills",
|
|
50
|
+
context_file="AGENTS.md",
|
|
51
|
+
speckit_key="codex",
|
|
52
|
+
),
|
|
53
|
+
Agent(
|
|
54
|
+
id="gemini",
|
|
55
|
+
name="Gemini CLI",
|
|
56
|
+
executable="gemini",
|
|
57
|
+
install_hint="https://github.com/google-gemini/gemini-cli",
|
|
58
|
+
skills_dir=".agents/skills", # the documented alias of .gemini/skills
|
|
59
|
+
context_file="GEMINI.md",
|
|
60
|
+
speckit_key="gemini",
|
|
61
|
+
),
|
|
62
|
+
Agent(
|
|
63
|
+
id="copilot",
|
|
64
|
+
name="GitHub Copilot",
|
|
65
|
+
executable="copilot",
|
|
66
|
+
install_hint="https://docs.github.com/copilot",
|
|
67
|
+
skills_dir=".agents/skills",
|
|
68
|
+
context_file="AGENTS.md",
|
|
69
|
+
speckit_key="copilot",
|
|
70
|
+
),
|
|
71
|
+
Agent(
|
|
72
|
+
id="cursor",
|
|
73
|
+
name="Cursor",
|
|
74
|
+
executable="cursor-agent",
|
|
75
|
+
install_hint="https://cursor.com/cli",
|
|
76
|
+
skills_dir=".agents/skills",
|
|
77
|
+
context_file="AGENTS.md",
|
|
78
|
+
speckit_key="cursor-agent",
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
AGENT_IDS = tuple(a.id for a in AGENTS)
|
|
82
|
+
DEFAULT_AGENTS = ("claude",)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_agent(agent_id: str) -> Agent:
|
|
86
|
+
return next(a for a in AGENTS if a.id == agent_id)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def launchable(selected: Sequence[Agent] | None = None) -> list[Agent]:
|
|
90
|
+
"""The agents ``init`` can start, among ``selected`` (default: all)."""
|
|
91
|
+
return [a for a in (AGENTS if selected is None else selected) if a.prompt]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def skills_dirs(agent_ids: Sequence[str]) -> list[str]:
|
|
95
|
+
"""Where the skills go for these agents, without repeats, in order."""
|
|
96
|
+
return list(dict.fromkeys(get_agent(i).skills_dir for i in agent_ids))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def context_files(agent_ids: Sequence[str]) -> list[str]:
|
|
100
|
+
"""The context files of these agents, without repeats, in order."""
|
|
101
|
+
return list(dict.fromkeys(get_agent(i).context_file for i in agent_ids))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def is_inside(agent: Agent, env: Mapping[str, str] | None = None) -> bool:
|
|
105
|
+
"""True when we run from a shell opened by this agent: launching it again would nest."""
|
|
106
|
+
if not agent.inside_env:
|
|
107
|
+
return False
|
|
108
|
+
return bool((os.environ if env is None else env).get(agent.inside_env))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def parse_ids(text: str) -> list[str]:
|
|
112
|
+
"""Agent ids from a comma-separated list, validated; ``ValueError`` names the bad ones."""
|
|
113
|
+
ids = list(dict.fromkeys(p.strip().lower() for p in text.split(",") if p.strip()))
|
|
114
|
+
bad = [i for i in ids if i not in AGENT_IDS]
|
|
115
|
+
if bad:
|
|
116
|
+
raise ValueError(f"unknown agent(s): {', '.join(bad)}; choose from {', '.join(AGENT_IDS)}")
|
|
117
|
+
return ids
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def choose_many(
|
|
121
|
+
ask: Callable[[str], str] | None = None,
|
|
122
|
+
out: Callable[[str], None] = print,
|
|
123
|
+
) -> list[str]:
|
|
124
|
+
"""Ask which agents the project is used with (several allowed). Enter takes the default."""
|
|
125
|
+
out("\nWhich AI agents will you use in this project? Skills are installed for each one.")
|
|
126
|
+
for n, agent in enumerate(AGENTS, 1):
|
|
127
|
+
out(f" {n}) {agent.name}")
|
|
128
|
+
try:
|
|
129
|
+
answer = (ask or input)("Numbers separated by commas [1]: ").strip()
|
|
130
|
+
except EOFError: # no terminal attached: keep the default
|
|
131
|
+
return list(DEFAULT_AGENTS)
|
|
132
|
+
if answer == "":
|
|
133
|
+
return list(DEFAULT_AGENTS)
|
|
134
|
+
picked = [p.strip() for p in answer.split(",") if p.strip()]
|
|
135
|
+
if not all(p.isdigit() and 1 <= int(p) <= len(AGENTS) for p in picked):
|
|
136
|
+
out(f"Not understood; using the default ({', '.join(DEFAULT_AGENTS)}).")
|
|
137
|
+
return list(DEFAULT_AGENTS)
|
|
138
|
+
return list(dict.fromkeys(AGENTS[int(p) - 1].id for p in picked))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def find_executable(agent: Agent) -> str | None:
|
|
142
|
+
return shutil.which(agent.executable)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def choose(
|
|
146
|
+
ask: Callable[[str], str] | None = None,
|
|
147
|
+
out: Callable[[str], None] = print,
|
|
148
|
+
options: Sequence[Agent] | None = None,
|
|
149
|
+
) -> Agent | None:
|
|
150
|
+
"""Ask which agent to open now. Enter takes the first one. ``None``: the user declines."""
|
|
151
|
+
pool = launchable(options)
|
|
152
|
+
if not pool:
|
|
153
|
+
return None
|
|
154
|
+
out("\nOpen an AI agent now to continue with /pk-start?")
|
|
155
|
+
for n, agent in enumerate(pool, 1):
|
|
156
|
+
out(f" {n}) {agent.name}")
|
|
157
|
+
none = len(pool) + 1
|
|
158
|
+
out(f" {none}) No, I will do it myself")
|
|
159
|
+
try:
|
|
160
|
+
answer = (ask or input)("Choice [1]: ").strip()
|
|
161
|
+
except EOFError: # no terminal attached: never launch anything
|
|
162
|
+
return None
|
|
163
|
+
if answer == "":
|
|
164
|
+
return pool[0]
|
|
165
|
+
if answer.isdigit() and 1 <= int(answer) <= len(pool):
|
|
166
|
+
return pool[int(answer) - 1]
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def launch(agent: Agent, root: Path, run: Callable[..., object] = subprocess.run) -> int:
|
|
171
|
+
"""Run the agent in the current terminal, in ``root``, started with its entry skill.
|
|
172
|
+
|
|
173
|
+
Returns the agent's exit code; the terminal is inherited, so it is interactive.
|
|
174
|
+
"""
|
|
175
|
+
exe = find_executable(agent)
|
|
176
|
+
if exe is None:
|
|
177
|
+
raise FileNotFoundError(agent.executable)
|
|
178
|
+
result = run([exe, agent.prompt], cwd=root, check=False)
|
|
179
|
+
return getattr(result, "returncode", 0)
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
file="$1"
|
|
6
6
|
tmp="$file.pk-tmp"
|
|
7
7
|
grep -v -i -E \
|
|
8
|
-
-e '^co-authored-by
|
|
8
|
+
-e '^co-authored-by:.*<[^>]*(@anthropic\.com|@openai\.com|@cursor\.com|@aider\.chat|\[bot\]@users\.noreply\.github\.com|copilot@users\.noreply\.github\.com|copilot@github\.com)>' \
|
|
9
|
+
-e '^co-authored-by: *((github )?copilot|chatgpt|codex|cursor( agent)?|claude( code| (opus|sonnet|haiku)[0-9. ]*)?|aider( \([^)]*\))?) *<' \
|
|
9
10
|
-e '^.*generated with.*(claude|copilot|codex|gemini|cursor)' \
|
|
10
11
|
"$file" > "$tmp"
|
|
11
12
|
# grep exits 1 when nothing is left (an empty message): keep that case valid and silent.
|