@groupby/ai-dev 0.5.1 → 0.5.4

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.
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env python3
2
+ """Summarize PR review and CI configuration for a repository.
3
+
4
+ Repo-agnostic: takes the repository root as its argument (defaults to the current
5
+ directory) and works on any project. Uses only the Python standard library so it
6
+ can run in sandboxes without installing PyYAML or other dependencies.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import re
13
+ from pathlib import Path
14
+
15
+
16
+ GUIDANCE_FILES = [
17
+ ".github/copilot-instructions.md",
18
+ "CLAUDE.md",
19
+ "AGENTS.md",
20
+ "README.md",
21
+ "docs/conventions.md",
22
+ "docs/project-rule.md",
23
+ "docs/source-control.md",
24
+ ]
25
+
26
+ REVIEW_FILES = [
27
+ ".github/workflows/claude-pr-review.yml",
28
+ ".github/workflows/claude.yml",
29
+ ".github/workflows/build-pr.yaml",
30
+ ".github/PULL_REQUEST_TEMPLATE.md",
31
+ ".github/CODEOWNERS",
32
+ ]
33
+
34
+ BUILD_FILES = [
35
+ "build.gradle",
36
+ "build.gradle.kts",
37
+ "settings.gradle",
38
+ "gradle.properties",
39
+ "pom.xml",
40
+ "go.mod",
41
+ "Makefile",
42
+ "pyproject.toml",
43
+ "requirements.txt",
44
+ "package.json",
45
+ ]
46
+
47
+ KEYWORDS = [
48
+ "Micronaut",
49
+ "Java 21",
50
+ "Java 17",
51
+ "Spring",
52
+ "Lombok",
53
+ "Spock",
54
+ "JUnit",
55
+ "Mockito",
56
+ "Testcontainers",
57
+ "JOOQ",
58
+ "jOOQ",
59
+ "Flyway",
60
+ "PostgreSQL",
61
+ "Mongo",
62
+ "Redis",
63
+ "Google Retail",
64
+ "Command Center",
65
+ "Pub/Sub",
66
+ "BigQuery",
67
+ "LaunchDarkly",
68
+ "ANTLR",
69
+ "Kafka",
70
+ "JWT",
71
+ "Docker",
72
+ "Jib",
73
+ ]
74
+
75
+
76
+ def read(path: Path) -> str:
77
+ try:
78
+ return path.read_text(errors="replace")
79
+ except FileNotFoundError:
80
+ return ""
81
+
82
+
83
+ def existing(root: Path, paths: list[str]) -> list[Path]:
84
+ return [root / path for path in paths if (root / path).exists()]
85
+
86
+
87
+ def first_match(pattern: str, text: str) -> str | None:
88
+ match = re.search(pattern, text, re.MULTILINE)
89
+ return match.group(1).strip() if match else None
90
+
91
+
92
+ def command_lines(text: str) -> list[str]:
93
+ commands: list[str] = []
94
+ for line in text.splitlines():
95
+ stripped = line.strip()
96
+ candidate = stripped
97
+ if stripped.startswith("run:"):
98
+ candidate = stripped.removeprefix("run:").strip()
99
+ if re.match(r"^(\.?/gradlew|gradle|mvn|make|go\s+test|pytest|npm|pnpm|yarn|docker)\b", candidate):
100
+ commands.append(candidate)
101
+ return commands
102
+
103
+
104
+ def pr_checkboxes(text: str) -> list[str]:
105
+ checks = []
106
+ for line in text.splitlines():
107
+ if re.match(r"\s*-\s+\[[ xX]\]", line):
108
+ checks.append(line.strip())
109
+ return checks
110
+
111
+
112
+ def collect_keywords(text: str) -> list[str]:
113
+ found = []
114
+ lower_text = text.lower()
115
+ for keyword in KEYWORDS:
116
+ if keyword.lower() in lower_text:
117
+ found.append(keyword)
118
+ normalized = []
119
+ seen = set()
120
+ for keyword in found:
121
+ key = keyword.lower()
122
+ if key not in seen:
123
+ seen.add(key)
124
+ normalized.append(keyword)
125
+ return sorted(normalized, key=str.lower)
126
+
127
+
128
+ def main() -> int:
129
+ parser = argparse.ArgumentParser(description=__doc__)
130
+ parser.add_argument("repo", nargs="?", default=".", help="Repository root")
131
+ args = parser.parse_args()
132
+
133
+ root = Path(args.repo).resolve()
134
+ print(f"# PR Review Configuration Summary\n")
135
+ print(f"Repository: `{root}`\n")
136
+
137
+ review_files = existing(root, REVIEW_FILES)
138
+ guidance_files = existing(root, GUIDANCE_FILES)
139
+ build_files = existing(root, BUILD_FILES)
140
+
141
+ print("## Files Found\n")
142
+ for title, files in [
143
+ ("Review/CI", review_files),
144
+ ("Guidance", guidance_files),
145
+ ("Build", build_files),
146
+ ]:
147
+ print(f"### {title}")
148
+ if files:
149
+ for path in files:
150
+ print(f"- `{path.relative_to(root)}`")
151
+ else:
152
+ print("- none")
153
+ print()
154
+
155
+ claude_text = read(root / ".github/workflows/claude-pr-review.yml")
156
+ if not claude_text:
157
+ claude_text = read(root / ".github/workflows/claude.yml")
158
+ build_text = read(root / ".github/workflows/build-pr.yaml")
159
+ pr_template = read(root / ".github/PULL_REQUEST_TEMPLATE.md")
160
+ codeowners = read(root / ".github/CODEOWNERS")
161
+ guidance_text = "\n\n".join(read(path) for path in guidance_files)
162
+ build_texts = "\n\n".join(read(path) for path in build_files)
163
+
164
+ print("## Claude Review\n")
165
+ if claude_text:
166
+ triggers = re.findall(r"types:\s*\[([^\]]+)\]", claude_text)
167
+ model = first_match(r"--model\s+([^\s]+)", claude_text)
168
+ print(f"- Workflow present: yes")
169
+ print(f"- Trigger type lists: {', '.join(triggers) if triggers else 'not parsed'}")
170
+ print(f"- Model: {model or 'not parsed'}")
171
+ print("- Standard focus areas present:")
172
+ for area in ["Code Quality", "Security", "Performance", "Testing", "Documentation"]:
173
+ print(f" - {area}: {'yes' if area in claude_text else 'not found'}")
174
+ else:
175
+ print("- Workflow present: no")
176
+ print()
177
+
178
+ print("## PR CI Commands\n")
179
+ commands = command_lines(build_text)
180
+ if commands:
181
+ for command in commands:
182
+ print(f"- `{command}`")
183
+ else:
184
+ print("- none parsed")
185
+ print()
186
+
187
+ java_version = first_match(r"java-version:\s*['\"]?([^'\"\n]+)", build_text)
188
+ if java_version:
189
+ print(f"Java version from PR workflow: `{java_version}`\n")
190
+
191
+ print("## PR Template Checks\n")
192
+ checks = pr_checkboxes(pr_template)
193
+ if checks:
194
+ for check in checks:
195
+ print(f"- {check}")
196
+ else:
197
+ print("- none")
198
+ print()
199
+
200
+ print("## CODEOWNERS\n")
201
+ owners = [line.strip() for line in codeowners.splitlines() if line.strip() and not line.strip().startswith("#")]
202
+ if owners:
203
+ for owner in owners:
204
+ print(f"- `{owner}`")
205
+ else:
206
+ print("- none")
207
+ print()
208
+
209
+ print("## Detected Keywords\n")
210
+ keywords = collect_keywords("\n\n".join([guidance_text, build_texts, claude_text, build_text]))
211
+ if keywords:
212
+ for keyword in keywords:
213
+ print(f"- {keyword}")
214
+ else:
215
+ print("- none")
216
+ print()
217
+
218
+ print("## Suggested Next Steps\n")
219
+ print("- Review changed files against the discovered keywords and guidance files.")
220
+ print("- Run targeted tests first, then the PR build command if feasible.")
221
+ print("- Run `git diff --check` before final output.")
222
+ return 0
223
+
224
+
225
+ if __name__ == "__main__":
226
+ raise SystemExit(main())
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: docs-init
3
- description: Use when the user wants to initialize or refresh the canonical `/docs` folder, `CLAUDE.md`, and `README.md` based on the current repo state. Creates the five canonical docs if missing; audits them for drift against the code if present. Triggered by `/docs-init` or requests like "update the docs", "create the docs", "refresh project documentation", "init docs", "bring docs up to date".
3
+ description: Use when the user wants to initialize or refresh the canonical `/docs` folder, `CLAUDE.md`, and `README.md` based on the current repo state. Generates the canonical `/docs` structure and verifies every claim against source files before including it. Creates the five canonical docs if missing; audits them for drift against the code if present. Triggered by `/docs-init` or requests like "update the docs", "create the docs", "refresh project documentation", "init docs", "bring docs up to date".
4
4
  ---
5
5
 
6
6
  # docs-init
@@ -37,6 +37,11 @@ Preserve this block on every edit.
37
37
  - Check for `README.md`, `CLAUDE.md`, and each of the five `docs/*.md` files.
38
38
  - If three or more are missing → **init mode**.
39
39
  - Otherwise → **refresh mode**.
40
+ - **Exception:** If canonical files are missing but the repo has substantial
41
+ existing documentation (e.g. `docs/` with other files, detailed README,
42
+ wiki-style markdown), treat as **refresh mode** — bridge existing docs
43
+ into the canonical structure rather than creating from scratch. Mention
44
+ existing docs in the report and propose how to integrate or link them.
40
45
  - Announce the detected mode to the user before proceeding.
41
46
 
42
47
  ### 2. Scan the repo
@@ -44,19 +49,28 @@ Preserve this block on every edit.
44
49
  Gather facts from the actual code, not assumptions:
45
50
 
46
51
  - **Stack & build:** `build.gradle` / `pom.xml` / `package.json` /
47
- `Cargo.toml` / `go.mod` / `pyproject.toml` / equivalent — language
48
- version, framework, key libraries.
52
+ `Cargo.toml` / `go.mod` / `pyproject.toml` / `setup.py` /
53
+ `requirements.txt` / `Makefile` / equivalent — language version,
54
+ framework, key libraries.
49
55
  - **Run commands:** README scripts, build-tool tasks (`./gradlew tasks`,
50
56
  `npm run`, `make`, `cargo`, `go`, `pytest`, etc.), `docker-compose.yml`.
51
- - **Package layout:** top-level dirs under `src/` (or `lib/`, `app/`).
52
- - **Domain types:** entity/model/record classes; group by package.
57
+ - **Package layout:** top-level dirs under `src/` (or `lib/`, `app/`,
58
+ `cmd/`, `internal/`, `pkg/` for Go, `dags/`/`jobs/` for Airflow,
59
+ `packages/`/`apps/` for monorepos, `manifests/` for infra repos).
60
+ - **Domain types:** entity/model/record/struct/schema classes; group by
61
+ package or module.
53
62
  - **Tests:** test framework, conventions, fixture locations.
54
- - **Migrations:** Flyway / Liquibase / equivalent paths and naming.
55
- - **CI/CD:** files under `.github/workflows/` (or equivalent).
56
- - **Deploy:** `helm/`, `k8s/`, `terraform/`, Dockerfile, `Procfile`.
57
- - **Observability:** OpenTelemetry / logging configuration.
58
- - **Secrets:** Vault references, sealed-secrets, etc.
59
- - **API surface:** controller annotations, OpenAPI spec, route definitions.
63
+ - **Migrations:** Flyway / Liquibase / Alembic / Knex / equivalent paths
64
+ and naming.
65
+ - **CI/CD:** files under `.github/workflows/`, `.circleci/`, `Jenkinsfile`,
66
+ `cloudbuild.yaml`, or equivalent. Note the CI system in use.
67
+ - **Deploy:** `helm/`, `k8s/`, `kustomize/`, `terraform/`, Dockerfile,
68
+ `Procfile`, Argo CD manifests.
69
+ - **Observability:** OpenTelemetry / logging / Prometheus / Datadog config.
70
+ - **Secrets:** Vault references, sealed-secrets, workload identity, ADC,
71
+ env var injection.
72
+ - **API surface:** controller annotations, OpenAPI spec, route definitions,
73
+ proto definitions, GraphQL schema.
60
74
 
61
75
  For refresh mode, also collect `git log --since="<doc-mtime>"` per doc to
62
76
  narrow attention to changes the doc may not yet reflect.
@@ -68,6 +82,32 @@ Draft the seven files in memory using the canonical templates below. Every
68
82
  the repo does not yet have the answer (e.g. operations runbooks, dashboard
69
83
  links). Do not fabricate.
70
84
 
85
+ **Verification checklist — apply to every claim before including it:**
86
+
87
+ 1. **Commands** — Before writing any `./gradlew <task>`, `npm run <script>`,
88
+ `make <target>`, `go <cmd>`, or similar, confirm the task exists (check
89
+ `build.gradle` tasks, `package.json` scripts, Makefile targets, workflow
90
+ files). Never guess task names.
91
+ 2. **Config keys** — Quote exact property paths from the actual config file
92
+ (YAML, properties, JSON, TOML, `.env`, Helm values, Kustomize overlays).
93
+ Do not paraphrase or use placeholder syntax like `${...}`.
94
+ 3. **Directory contents** — List (`ls`) the directory before describing what it
95
+ contains. Never say "contains templates only" or "contains X" without
96
+ checking.
97
+ 4. **Credentials** — Never instruct placing credentials in a repo-tracked file.
98
+ Recommend user-home config instead: `~/.gradle/gradle.properties`,
99
+ `~/.m2/settings.xml`, `~/.npmrc`, `~/.pypirc`, `~/.docker/config.json`,
100
+ or environment variables / Application Default Credentials (ADC).
101
+ 5. **Submodule commands** — Use `git submodule update --init --recursive`
102
+ (pinned commit). Never use `--remote` (breaks reproducible builds).
103
+ 6. **CI/CD triggers** — Read the actual trigger/filter syntax for the CI system
104
+ in use (GitHub Actions `on:` block, CircleCI `filters:`, Jenkins pipeline
105
+ triggers, etc.). List all branches/events accurately.
106
+ 7. **Scope qualifiers** — When stating "all X do Y," verify there are no
107
+ exceptions (e.g. health, metrics, actuator endpoints outside the API prefix).
108
+ 8. **File references** — When describing what a file is used for, trace its
109
+ references (grep for the filename) so the description matches actual usage.
110
+
71
111
  Targets: `CLAUDE.md` ≤ 40 lines, `README.md` ≤ 30 lines.
72
112
 
73
113
  **Do not write these files to disk yet.** Continue to step 4, where the
@@ -143,6 +183,15 @@ renamed.
143
183
  Numbered list of concrete edits I would make if you approve. Group by
144
184
  file. Each item is one sentence — no diffs.
145
185
 
186
+ ## Verified against source
187
+
188
+ List what was checked to ensure accuracy:
189
+ - Commands: <which tasks were confirmed and how>
190
+ - Config keys: <which files were read for exact property names>
191
+ - Directories: <which directories were listed>
192
+ - Workflow triggers: <which workflow files were read>
193
+ - Credentials: <confirm no repo-tracked file instructions>
194
+
146
195
  ## Awaiting your approval
147
196
 
148
197
  Reply "apply" to make the changes, "apply <numbers>" to apply a subset
@@ -176,6 +225,16 @@ This makes it obvious that the check ran.
176
225
  report and ask whether to create a new file or fold it elsewhere.
177
226
  - **Do not fabricate.** If the repo has no dashboards, no runbooks, no
178
227
  decisions yet, leave honest placeholders or empty sections.
228
+ - **Verify every factual claim.** Before including any command, config key,
229
+ directory description, or workflow trigger in the docs, confirm it against
230
+ the actual source file. See the verification checklist in step 3a.
231
+ - **Never reference repo-tracked files for credentials.** Always use
232
+ user-home paths (`~/.gradle/gradle.properties`, `~/.m2/settings.xml`,
233
+ `~/.npmrc`, `~/.pypirc`) or environment variables / ADC.
234
+ - **Never use `--remote` in submodule commands.** Use pinned commits for
235
+ reproducible builds.
236
+ - **Qualify universal statements.** If writing "all endpoints do X," verify
237
+ there are no exceptions and add qualifiers if needed.
179
238
  - **Do not delete sections without flagging them.** Surfacing a removal
180
239
  belongs in the report, not as a silent edit.
181
240
  - **Do not edit `.github/PULL_REQUEST_TEMPLATE.md`, CI files, or code.**
@@ -341,3 +400,4 @@ Requires <prereqs>. For detail see [docs/local-setup.md](docs/local-setup.md).
341
400
  - [Operations](docs/operations.md) — deploy, secrets, observability, runbooks
342
401
  - [Decisions](docs/decisions.md) — why things are the way they are
343
402
  ```
403
+