techbro 0.0.0-stage → 0.5.0

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 (122) hide show
  1. package/README.md +453 -2
  2. package/assets/baseline-rules.yml +60 -0
  3. package/assets/caller-workflow.yml +26 -0
  4. package/assets/review-action.yml +27 -0
  5. package/assets/review-config.json +4 -0
  6. package/assets/review-workflow.yml +443 -0
  7. package/assets/review.mjs +721 -0
  8. package/bin/techbro.js +9 -0
  9. package/package.json +23 -4
  10. package/src/cli.js +690 -0
  11. package/src/cluster.js +134 -0
  12. package/src/config.js +35 -0
  13. package/src/doctor.js +125 -0
  14. package/src/github.js +152 -0
  15. package/src/init.js +167 -0
  16. package/src/kube-project.js +468 -0
  17. package/src/kube.js +317 -0
  18. package/src/managed.js +63 -0
  19. package/src/project.js +202 -0
  20. package/src/review.js +165 -0
  21. package/src/secret.js +80 -0
  22. package/src/setup.js +91 -0
  23. package/src/team-workflow.js +106 -0
  24. package/src/team.js +145 -0
  25. package/src/templates.js +44 -0
  26. package/src/upgrade.js +94 -0
  27. package/templates/_default/.agents/skills/README.md +36 -0
  28. package/templates/_default/.agents/skills/database-migrations/SKILL.md +72 -0
  29. package/templates/_default/.agents/skills/frontend-feature/SKILL.md +65 -0
  30. package/templates/_default/.agents/skills/release-safe-change/SKILL.md +75 -0
  31. package/templates/_default/.agents/skills/rust-backend-feature/SKILL.md +68 -0
  32. package/templates/_default/.editorconfig +15 -0
  33. package/templates/_default/.env.example +9 -0
  34. package/templates/_default/.github/workflows/ci.yml +60 -0
  35. package/templates/_default/.nvmrc +1 -0
  36. package/templates/_default/Cargo.lock +1834 -0
  37. package/templates/_default/Cargo.toml +3 -0
  38. package/templates/_default/README.md +257 -0
  39. package/templates/_default/TEMPLATE.md +29 -0
  40. package/templates/_default/_dockerignore +8 -0
  41. package/templates/_default/_gitignore +8 -0
  42. package/templates/_default/apps/backend/Cargo.toml +22 -0
  43. package/templates/_default/apps/backend/Dockerfile +34 -0
  44. package/templates/_default/apps/backend/README.md +13 -0
  45. package/templates/_default/apps/backend/src/config.rs +111 -0
  46. package/templates/_default/apps/backend/src/controllers/health.rs +36 -0
  47. package/templates/_default/apps/backend/src/controllers/items.rs +79 -0
  48. package/templates/_default/apps/backend/src/controllers/mod.rs +2 -0
  49. package/templates/_default/apps/backend/src/error.rs +115 -0
  50. package/templates/_default/apps/backend/src/lib.rs +7 -0
  51. package/templates/_default/apps/backend/src/main.rs +71 -0
  52. package/templates/_default/apps/backend/src/models/item.rs +94 -0
  53. package/templates/_default/apps/backend/src/models/mod.rs +1 -0
  54. package/templates/_default/apps/backend/src/routes.rs +94 -0
  55. package/templates/_default/apps/backend/src/state.rs +12 -0
  56. package/templates/_default/apps/backend/src/views/items.rs +18 -0
  57. package/templates/_default/apps/backend/src/views/mod.rs +1 -0
  58. package/templates/_default/apps/backend/tests/api.rs +234 -0
  59. package/templates/_default/apps/frontend/.env.example +2 -0
  60. package/templates/_default/apps/frontend/.prettierignore +3 -0
  61. package/templates/_default/apps/frontend/.prettierrc +1 -0
  62. package/templates/_default/apps/frontend/Dockerfile +15 -0
  63. package/templates/_default/apps/frontend/README.md +39 -0
  64. package/templates/_default/apps/frontend/_dockerignore +3 -0
  65. package/templates/_default/apps/frontend/_gitignore +24 -0
  66. package/templates/_default/apps/frontend/components.json +25 -0
  67. package/templates/_default/apps/frontend/eslint.config.js +37 -0
  68. package/templates/_default/apps/frontend/index.html +13 -0
  69. package/templates/_default/apps/frontend/jsconfig.json +7 -0
  70. package/templates/_default/apps/frontend/nginx.conf +30 -0
  71. package/templates/_default/apps/frontend/package-lock.json +4338 -0
  72. package/templates/_default/apps/frontend/package.json +49 -0
  73. package/templates/_default/apps/frontend/public/favicon.svg +1 -0
  74. package/templates/_default/apps/frontend/src/App.jsx +11 -0
  75. package/templates/_default/apps/frontend/src/App.test.jsx +40 -0
  76. package/templates/_default/apps/frontend/src/api/client.js +50 -0
  77. package/templates/_default/apps/frontend/src/api/client.test.js +70 -0
  78. package/templates/_default/apps/frontend/src/api/items.js +26 -0
  79. package/templates/_default/apps/frontend/src/api/system.js +11 -0
  80. package/templates/_default/apps/frontend/src/components/ItemList.jsx +20 -0
  81. package/templates/_default/apps/frontend/src/components/ItemList.test.jsx +18 -0
  82. package/templates/_default/apps/frontend/src/components/Layout.jsx +35 -0
  83. package/templates/_default/apps/frontend/src/components/StatusBadge.jsx +22 -0
  84. package/templates/_default/apps/frontend/src/components/StepList.jsx +19 -0
  85. package/templates/_default/apps/frontend/src/components/ui/button.jsx +52 -0
  86. package/templates/_default/apps/frontend/src/components/ui/card.jsx +83 -0
  87. package/templates/_default/apps/frontend/src/components/ui/input.jsx +19 -0
  88. package/templates/_default/apps/frontend/src/hooks/useBackendStatus.js +33 -0
  89. package/templates/_default/apps/frontend/src/hooks/useItems.js +47 -0
  90. package/templates/_default/apps/frontend/src/index.css +130 -0
  91. package/templates/_default/apps/frontend/src/lib/config.js +2 -0
  92. package/templates/_default/apps/frontend/src/lib/utils.js +6 -0
  93. package/templates/_default/apps/frontend/src/main.jsx +11 -0
  94. package/templates/_default/apps/frontend/src/models/items.js +24 -0
  95. package/templates/_default/apps/frontend/src/models/items.test.js +24 -0
  96. package/templates/_default/apps/frontend/src/pages/ItemsPage.jsx +41 -0
  97. package/templates/_default/apps/frontend/src/pages/WelcomePage.jsx +156 -0
  98. package/templates/_default/apps/frontend/src/pages/WelcomePage.test.jsx +55 -0
  99. package/templates/_default/apps/frontend/src/routes.jsx +17 -0
  100. package/templates/_default/apps/frontend/src/shadcn.css +655 -0
  101. package/templates/_default/apps/frontend/src/test/setup.js +1 -0
  102. package/templates/_default/apps/frontend/vite.config.js +25 -0
  103. package/templates/_default/database/Dockerfile +9 -0
  104. package/templates/_default/database/README.md +15 -0
  105. package/templates/_default/database/migrations/postgres/20261008120000_create_items.sql +5 -0
  106. package/templates/_default/database/migrations/sqlite/20261008120000_create_items.sql +5 -0
  107. package/templates/_default/docker-compose.prod.yml +53 -0
  108. package/templates/_default/docker-compose.yml +69 -0
  109. package/templates/_default/docs/api/README.md +41 -0
  110. package/templates/_default/docs/api/openapi.yaml +152 -0
  111. package/templates/_default/docs/decisions/0001-adopt-engineering-convention.md +13 -0
  112. package/templates/_default/docs/decisions/README.md +6 -0
  113. package/templates/_default/docs/deployment.md +46 -0
  114. package/templates/_default/docs/docker.md +63 -0
  115. package/templates/_default/docs/getting-started.md +214 -0
  116. package/templates/_default/package.json +15 -0
  117. package/templates/_default/rust-toolchain.toml +3 -0
  118. package/templates/_default/scripts/check +10 -0
  119. package/templates/_default/scripts/db-migrate +25 -0
  120. package/templates/_default/scripts/install-hooks +5 -0
  121. package/templates/_default/scripts/pre-push +3 -0
  122. package/templates/_default/scripts/setup +26 -0
package/README.md CHANGED
@@ -1,3 +1,454 @@
1
- # Temporary Holding Version
1
+ # techbro
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ techbro is a CLI companion for programmers who build with AI assistance ("vibe
4
+ coding"). It helps AI-assisted code meet the team's standards, much like a
5
+ programmer's review would, and keeps the team's conventions in one shared place.
6
+ Zero runtime dependencies; plain ESM JavaScript.
7
+
8
+ ## Vision and current scope
9
+
10
+ The goal is broader than reviews: help a programmer start projects the team's
11
+ way, align coding style with the tech team, review work against team standards,
12
+ and eventually push and deploy code safely.
13
+
14
+ Implemented today:
15
+
16
+ - Scaffold projects from the company template library (`.project_templates/` in
17
+ this repository): `project init`, `project list`.
18
+ - Keep shared team state (members and linked projects) in a GitHub repository:
19
+ `team create|join|list|use|upgrade`, `project link`.
20
+ - Dispatch a team-hosted review workflow for a registered project:
21
+ `project review`.
22
+
23
+ - Generate manifests, build in-cluster, deploy, and operate Kubernetes apps with
24
+ explicit cluster/namespace targets: `kube cluster list|add|use`,
25
+ `kube check|status|logs|rollout-status|restart|rollback`,
26
+ `kube generate|build|deploy`. Build and deploy are separate, need `--yes`, and
27
+ were validated end to end on one internal cluster only; there is no built-in
28
+ cluster, so set one up first. See the limits in
29
+ [docs/kubernetes.md](docs/kubernetes.md).
30
+ - Share a cluster with the team: the owner runs `cluster add` once and the
31
+ credential is stored encrypted (with `TECHBRO_PASSPHRASE`) in the team repo;
32
+ members run `cluster use`. Validated live once.
33
+ - Check local setup and missing config or secrets: `doctor [--online]`.
34
+
35
+ Planned, not implemented (no commands exist yet): pushing code through techbro
36
+ and automatically aligning code style with team conventions.
37
+ Do not rely on these until they appear in the command reference below.
38
+
39
+ For a step-by-step walkthrough from install to first review, see
40
+ [docs/onboarding.md](docs/onboarding.md). Upgrading from an older version: see
41
+ [docs/upgrading.md](docs/upgrading.md).
42
+
43
+ ## Requirements
44
+
45
+ - Node 20 or newer and npm.
46
+ - git (for `project init` repository setup and `project link` origin detection).
47
+ - Rust toolchain (`cargo`, https://rustup.rs) for the setup step of `project init`.
48
+ - `gh` (logged in) or a `GITHUB_TOKEN` for `team` and `project link` commands.
49
+
50
+ ## Install
51
+
52
+ The repository is private, so the installer must be fetched with a GitHub token.
53
+ Run `gh auth login` once, then:
54
+
55
+ ```bash
56
+ curl -fsSL -H "Authorization: Bearer $(gh auth token)" \
57
+ https://raw.githubusercontent.com/mtel-thailand/techbro/main/install.sh | bash
58
+ techbro --version
59
+ ```
60
+
61
+ Without `gh`, use `-H "Authorization: Bearer $GITHUB_TOKEN"` with a token that has
62
+ `repo` scope. An anonymous `curl` returns 404.
63
+
64
+ The installer downloads this repository from GitHub, bundles the templates, and runs
65
+ `npm install -g`. Running it again reinstalls the latest version and prints
66
+ `updated <old> -> <new>`.
67
+
68
+ | Variable | Default | Effect |
69
+ | --- | --- | --- |
70
+ | `TECHBRO_SOURCE` | `github` | `github` or `npm` (install `techbro@latest` from the registry) |
71
+ | `TECHBRO_REF` | `main` | git ref to download (github source) |
72
+ | `TECHBRO_REPO` | `mtel-thailand/techbro` | `owner/name` to download |
73
+ | `GITHUB_TOKEN` | `gh auth token` | token for the download |
74
+
75
+ Uninstall: `npm uninstall -g techbro`. Once published to npm,
76
+ `npx techbro@latest project init my-app` works without installing.
77
+
78
+ ## Quick start
79
+
80
+ ```bash
81
+ # 1. create a team (once per team; needs push access to the GitHub repo)
82
+ techbro team create core --url https://github.com/<owner>/<team-repo>
83
+
84
+ # 2. scaffold a project
85
+ techbro project init my-app
86
+ cd my-app
87
+
88
+ # 3. push my-app to its own GitHub repo, then register it with the team
89
+ git remote add origin https://github.com/<owner>/my-app.git
90
+ techbro project link
91
+
92
+ # 4. run it (two terminals)
93
+ cargo watch -x 'run -p backend' # http://localhost:8080
94
+ npm run dev:frontend # http://localhost:5173
95
+ ```
96
+
97
+ Teammates run `techbro team join core --url https://github.com/<owner>/<team-repo>`.
98
+
99
+ ## Command reference
100
+
101
+ Exit codes: `0` success; `2` no arguments, unknown command, or usage printed
102
+ without a subcommand; `1` any error (message on stderr as `techbro: <message>`).
103
+
104
+ ### `techbro project init [dir]`
105
+
106
+ Scaffolds a template into `dir` (default: current directory) and makes it runnable.
107
+
108
+ | Flag | Default | Effect |
109
+ | --- | --- | --- |
110
+ | `-t, --template <name>` | `_default` | template to scaffold (see `project list`) |
111
+ | `-n, --name <app-name>` | slug of the directory name | app name for `package.json`, `.env.example`, `apps/frontend/.env.example` |
112
+ | `-f, --force` | off | allow a non-empty target directory (files overwritten) |
113
+ | `--no-git` | git on | skip `git init -b main` |
114
+ | `--skip-setup` | setup on | scaffold only; install nothing |
115
+ | `--skip-build` | build on | run setup but skip `cargo build -p backend` |
116
+
117
+ Reads: the template folder. Writes: the copied template, `.techbro.json`, and
118
+ `git init -b main` unless disabled. If `dir` is already inside a git repository,
119
+ git init is skipped and the repository is left as is. Never copied:
120
+ `node_modules`, `target`, `dist`, `.env`, `*.db`, `*.sqlite`, `coverage`,
121
+ `.DS_Store`. Errors: non-empty directory without `--force`, unknown template,
122
+ invalid existing `.techbro.json` (checked before copying).
123
+
124
+ What setup does (7 steps, 6 with `--skip-build`):
125
+
126
+ 1. Checks `node`, `npm` and `cargo` are on PATH.
127
+ 2. Creates `.env` and `apps/frontend/.env` from the `.env.example` files.
128
+ 3. `npm ci` in `apps/frontend` (`npm install` if there is no lockfile).
129
+ 4. `cargo fetch --locked`.
130
+ 5. Installs `sqlx-cli` and `cargo-watch` with `cargo install` if missing.
131
+ 6. `bash scripts/db-migrate sqlite` to create `local.db`.
132
+ 7. `cargo build -p backend`.
133
+
134
+ The first run can take several minutes. If a step fails, the scaffold is kept and
135
+ the error shows the command to rerun.
136
+
137
+ ### `techbro project link`
138
+
139
+ Registers the current project repo in the current team's `projects.json`.
140
+
141
+ | Flag | Default | Effect |
142
+ | --- | --- | --- |
143
+ | `-u, --url <repo>` | `git remote get-url origin` in the current directory | project repository |
144
+ | `-n, --name <name>` | `.techbro.json` `name`, else repo name | project name |
145
+
146
+ Checks, in order: a current team exists; the project URL parses; you have read
147
+ access to the project repo; you are listed in the team's `members.json`; the team
148
+ repo has `projects.json`. An entry with the same URL is left alone
149
+ (`already linked`). Otherwise the entry is added by direct write or by issue (see
150
+ Permissions). If `.techbro.json` exists in the current directory, `team` and
151
+ `repo` are set in it (other keys kept); it is never created.
152
+
153
+ Output: `already linked` | `linked <name> (<url>) to team <team>` |
154
+ `link pending: <issueUrl> (the team workflow will add it)`.
155
+
156
+ Review config seeding: when you have push access to the team repo, link also adds
157
+ a `projects.<name>` entry to `.github/techbro-review.json` if (and only if) that
158
+ project has none. Existing entries and all other keys are preserved, never
159
+ overwritten; the write uses the file's current sha, so a concurrent change is
160
+ reported as a warning instead of clobbering it. Recognized projects:
161
+
162
+ - the `_default` template (`.techbro.json` `template` is `_default`, plus
163
+ `package.json` and `Cargo.toml`): `test` = `{ runtime: "rust-node", install:
164
+ "npm --prefix apps/frontend ci", command: "npm run check", workdir: "." }`;
165
+ - otherwise, only if `package.json` defines a `test` script: `npm test` on the
166
+ `node` runtime (`npm ci` install when `package-lock.json` exists).
167
+
168
+ Both get `security: { failOn: "ERROR" }`, `sonar: { mode: "optional" }` and `duplication: { mode: "required", maxPercent: 10 }`. Existing entries are never rewritten; entries without `duplication` inherit the same default remotely and in `--dry-run`. Anything
169
+ else is not guessed: link warns and you must add the entry by hand. Without push
170
+ access (issue flow) link never changes the config; it prints the proposed entry
171
+ and a team admin must add it. Check readiness with `project review --dry-run`.
172
+
173
+ What a review runs: Opengrep security rules and jscpd duplication (required by
174
+ default, 10% max; set `duplication.mode: "off"` to opt out, noted in the report, or
175
+ raise `duplication.maxPercent` for legacy projects, which techbro never does
176
+ automatically); Sonar is optional, not required on the server. A scanner that
177
+ errors fails closed. For the `_default` template the configured test command
178
+ (`npm run check`) covers rustfmt, Clippy, ESLint, Prettier and tests. A non-template
179
+ project with only `npm test` runs just that test script; it is not linted.
180
+
181
+ Review caller: after the registry and config steps, link installs a tiny
182
+ `.github/workflows/techbro-review.yml` in the project repo that calls the team's
183
+ shared workflow (`<team-owner>/<team-repo>/.github/workflows/techbro-review.yml@main`;
184
+ `@main` is intentional so the team updates reviews centrally). It is always
185
+ delivered through a pull request from branch `techbro/review-caller-v3` on the
186
+ project's default branch, never a direct push. Missing or unmodified-old callers
187
+ are added/updated; a customized caller is kept with a warning; an existing
188
+ branch or PR is reused and manual edits on the branch are not overwritten. Without
189
+ push access to the project repo link reports that a project writer must run it.
190
+ Projects outside the team's organization are unsupported (no caller installed).
191
+ The read-only issue flow installs no caller; rerun link after registration.
192
+
193
+ ### `techbro project review`
194
+
195
+ Dispatches the team's `techbro-review.yml` workflow for a registered project.
196
+
197
+ | Flag | Default | Effect |
198
+ | --- | --- | --- |
199
+ | `-u, --url <repo>` | origin of the current directory | registered project |
200
+ | `-r, --ref <ref>` | local `HEAD`, else remote default branch | ref resolved to an immutable SHA |
201
+ | `-t, --team <name>` | current team | team to use |
202
+ | `-p, --project <name>` | none | pick a registered project by name |
203
+ | `-w, --wait` | off | wait (30 min max); exit 1 unless the run succeeds |
204
+ | `--dry-run` | off | read-only preflight; dispatches nothing, writes nothing |
205
+
206
+ `--dry-run` verifies team membership, registration, project access and the
207
+ workflow file, then shows the effective checks from `.github/techbro-review.json`
208
+ (test command, `security.failOn` ERROR|WARNING|INFO|none, `sonar.mode`
209
+ optional|required|off) and lists missing setup; exit 1 if anything is missing.
210
+ Sonar token/host cannot be inspected, so they are not validated; the remote run is the authoritative gate. A normal review prints the
211
+ same configuration warnings but still dispatches. With `sonar.mode: optional` an
212
+ unavailable Sonar is skipped with a note; an attempted Sonar run that fails is a
213
+ failure. Example: `techbro project review --dry-run`.
214
+
215
+ Requires team membership, read access to the team repo, write access to the
216
+ project repo, a project registered exactly once in the same organization as the
217
+ team, and the merged caller on the project's default branch. Uncommitted files
218
+ are excluded (warning); the commit must exist on the remote. The project's
219
+ `techbro-review.yml` is dispatched on its default branch with only inputs `sha`
220
+ and `request_id`, matched by run title; the run and the report artifact live in
221
+ the project repo. If the run is not found in time the workflow URL is printed and
222
+ nothing is redispatched. `--dry-run` also checks the caller and the team's
223
+ reusable workflow/action, and warns if the team repo's Actions sharing cannot be
224
+ verified (a team admin must allow organization repositories to use its workflows
225
+ and actions).
226
+
227
+ Setup (no secrets are provisioned by the CLI; no GitHub App or browser login is
228
+ needed): the review token only reads the project's own repository. Optionally set
229
+ `SONAR_TOKEN` (secret) and `SONAR_HOST_URL` (variable) in the project repo; they
230
+ are passed through the caller. Never put secrets in team repo files. A private
231
+ team repo shares its workflow and action with organization repositories only
232
+ after an admin enables that in its Actions settings.
233
+
234
+ Coverage figures are project-controlled (the project's own test commands produce them).
235
+ `--wait` exits non-zero when the run fails or is incomplete, tests are not
236
+ configured, the scanner errors, or the quality gate fails; the report artifact is
237
+ still linked. If the 30 minute `--wait` limit passes, the remote run keeps going;
238
+ the run link is printed so you can follow it.
239
+
240
+ Be aware: the review runs the project's own test commands, i.e. arbitrary source
241
+ code, and anyone who can read the project repo's Actions runs can read the report artifacts. Persistent Sonar is planned; limitations are listed in each
242
+ report, and test commands come from the team review config.
243
+
244
+ ### `techbro team upgrade`
245
+
246
+ `techbro team upgrade [--team <name>] [--dry-run]` compares the team repo's
247
+ managed review files with this CLI's versions. Missing files are added, unmodified
248
+ old ones updated, current ones skipped; customized files are kept and a
249
+ `<path>.techbro-new` candidate is added for manual merge. `members.json`,
250
+ `projects.json` and `techbro-team.yml` are never touched. Changes go through a PR
251
+ from branch `techbro/upgrade-v5` against the default branch; an open PR and
252
+ existing branch are reused (never reset). `--dry-run` writes nothing. Seed files (config) that exist are never flagged. If the review workflow, action or
253
+ helper is customized, all three are kept and both get `.techbro-new` candidates.
254
+ Pushing workflow files needs the `workflow` token scope. `team create` also adds missing
255
+ managed files.
256
+
257
+ ### `techbro project list`
258
+
259
+ Prints each template name with a description taken from the first `# ` heading of
260
+ its `TEMPLATE.md` (or `README.md`).
261
+
262
+ ### `techbro team create <name> --url <repo>`
263
+
264
+ Initializes shared state in a GitHub repo and makes the team current.
265
+
266
+ | Flag | Default | Effect |
267
+ | --- | --- | --- |
268
+ | `-u, --url <repo>` | required | team repository (`https://github.com/o/r`, with `.git` or trailing slash, or `git@github.com:o/r.git`) |
269
+ | `-f, --force` | off | overwrite an existing local team of the same name |
270
+
271
+ `<name>` must match `[a-z0-9-]+`. Requires push access to the repo; otherwise it
272
+ stops with `no write access to <o/r> as <login>` before writing anything. Writes
273
+ to `main`, skipping files that exist:
274
+
275
+ - `members.json` (commit `techbro: initialize team members`). If it already
276
+ exists, owner and members are kept and your login is added if missing
277
+ (`techbro: add <login> to team members`).
278
+ - `projects.json` (`techbro: initialize team projects`); never modified if present.
279
+ - `.github/workflows/techbro-team.yml` (`techbro: add team workflow`).
280
+
281
+ Output: `Repository`, `Owner`, `Members`, `Created`, `Updated`, `Current team`.
282
+ Then the team is saved in the global config as current.
283
+
284
+ ### `techbro team join <name> --url <repo>`
285
+
286
+ Same flags and name rules as `team create`. Needs read access (`no read access to
287
+ <o/r> as <login>` otherwise) and an initialized team repo (`team repo is not
288
+ initialized; run team create`). Output: `Repository: <o/r>`, then one of
289
+ `already a member`, `added <login> to members.json`, or
290
+ `membership pending: <issueUrl> (the team workflow will add you)`, then
291
+ `Current team: <name>`.
292
+
293
+ ### `techbro team list`
294
+
295
+ One line per saved team: `*` marks the current one, then name and URL.
296
+
297
+ ### `techbro team use <name>`
298
+
299
+ Makes a saved team current and prints `Current team: <name>`. Errors on an unknown
300
+ name.
301
+
302
+ ### `techbro cluster add|list|use|remove`
303
+
304
+ Team-shared clusters. The team owner runs `cluster add <name> --kubeconfig <file>
305
+ --context <ctx> --yes` once; techbro encrypts only that context with
306
+ `TECHBRO_PASSPHRASE` and commits it under `clusters/` in the team repo. Members
307
+ run `cluster use <name>` to decrypt it into a private techbro-owned kubeconfig.
308
+ See [docs/kubernetes.md](docs/kubernetes.md#team-shared-clusters-techbro-cluster).
309
+
310
+ ### `techbro doctor [--online] [--json]`
311
+
312
+ Read-only check of tools, GitHub token, `TECHBRO_PASSPHRASE`, config, team,
313
+ shared cluster kubeconfigs and secret-like tracked files, with a fix per problem.
314
+ Never prints secret values; exits 1 if any check fails.
315
+
316
+ ### `techbro --help`, `--version`
317
+
318
+ `techbro --help` (also `-h`, `help`) prints the usage; `techbro project --help` and
319
+ `techbro team --help` print per-group usage. `--version` (`-v`) prints the package
320
+ version. With no arguments the usage is printed and the exit code is `2`.
321
+
322
+ ## Files and formats
323
+
324
+ ### `.techbro.json` (project root)
325
+
326
+ Written by `project init` unless one already exists (an existing JSON object is
327
+ preserved as is):
328
+
329
+ ```json
330
+ {
331
+ "schemaVersion": 1,
332
+ "name": "my-app",
333
+ "template": "_default",
334
+ "techbroVersion": "0.3.0",
335
+ "config": {}
336
+ }
337
+ ```
338
+
339
+ `project link` adds `"team": "<team>"` and `"repo": "https://github.com/<o>/<r>"`.
340
+ `config` is reserved and does not affect commands.
341
+
342
+ ### `~/.config/techbro/config.json` (global)
343
+
344
+ Set `TECHBRO_CONFIG_DIR` to use `$TECHBRO_CONFIG_DIR/config.json` instead. Missing
345
+ file means an empty config; invalid JSON or a wrong shape is an error naming the
346
+ file. Written atomically (temp file plus rename).
347
+
348
+ ```json
349
+ {
350
+ "schemaVersion": 1,
351
+ "currentTeam": "core",
352
+ "teams": {
353
+ "core": { "url": "https://github.com/<o>/<r>", "owner": "<login>", "addedAt": "<ISO time>" }
354
+ }
355
+ }
356
+ ```
357
+
358
+ ### `members.json` (team repo, `main`)
359
+
360
+ ```json
361
+ { "schemaVersion": 1, "team": "core", "owner": "<login>", "members": ["<login>"] }
362
+ ```
363
+
364
+ ### `projects.json` (team repo, `main`)
365
+
366
+ ```json
367
+ {
368
+ "schemaVersion": 1,
369
+ "projects": [
370
+ {
371
+ "name": "my-app",
372
+ "url": "https://github.com/<o>/my-app",
373
+ "template": "_default",
374
+ "linkedBy": "<login>",
375
+ "linkedAt": "<ISO time>"
376
+ }
377
+ ]
378
+ }
379
+ ```
380
+
381
+ `template` is `null` when the project has no `.techbro.json`.
382
+
383
+ ### `.github/workflows/techbro-team.yml` (team repo)
384
+
385
+ Created by `team create`. Triggers on issues `opened` and `reopened`; permissions
386
+ `contents: write` and `issues: write`; each job has a 5 minute timeout. Issue
387
+ fields reach the scripts only through environment variables.
388
+
389
+ - Job `join` (label `techbro-join`, body `{"schemaVersion":1,"action":"join","login":"<you>"}`).
390
+ - Job `link` (label `techbro-link`, body `{"schemaVersion":1,"action":"link","login":"<you>","project":{...}}`).
391
+
392
+ Each job: verifies the issue author is a collaborator (read, triage, write,
393
+ maintain or admin; otherwise comments and closes), checks the body `login` equals
394
+ the issue author, updates the JSON file, commits as `github-actions[bot]`, pushes
395
+ to `main`, comments, and closes the issue. If the push fails (protected `main`) it
396
+ comments that the change must be made manually and the job fails. The result is
397
+ asynchronous: the CLI returns `pending` and the change lands when the workflow
398
+ runs. Issues must be enabled on the team repo for this path.
399
+
400
+ ## Permissions model
401
+
402
+ Token: `GITHUB_TOKEN` if set, else `gh auth token`; otherwise the command fails
403
+ with `set GITHUB_TOKEN or run gh auth login`. The token is never printed.
404
+
405
+ | Command | GitHub permission needed | Path |
406
+ | --- | --- | --- |
407
+ | `team create` | push on team repo | direct write to `main` only (shared workflow written last) |
408
+ | `team join` | read on team repo | push: direct write to `members.json`; read only: issue `techbro-join` + workflow |
409
+ | `project link` | read on project repo, membership in team, read on team repo | push on team repo: direct write to `projects.json`; read only: issue `techbro-link` + workflow |
410
+ | `project link` (caller) | push on project repo | PR on the project repo, never a direct push |
411
+ | `project review` | membership, read on team repo, write on project repo | dispatch on the project repo |
412
+ | `team list`, `team use` | none (local config only) | none |
413
+
414
+ An open request issue created by you is reused instead of opening a duplicate.
415
+
416
+ ## Templates
417
+
418
+ Templates are tracked in `.project_templates/` at the repository root. On `npm pack` /
419
+ `npm publish`, `prepack` (`npm run sync`) copies them into `templates/`
420
+ (gitignored). `.gitignore` and `.dockerignore` are stored as `_gitignore` /
421
+ `_dockerignore` because npm renames `.gitignore` in packages; `init` restores the
422
+ real names. In a repository checkout (no `templates/` folder) the CLI reads
423
+ `./.project_templates` directly, so no sync is needed.
424
+
425
+ To add a template, create `.project_templates/<name>/` with a `TEMPLATE.md` whose
426
+ first line is a `# ` heading. It shows up in `project list` and works with
427
+ `project init --template <name>`.
428
+
429
+ ## Troubleshooting
430
+
431
+ - `curl` returns 404 on install: the repo is private; send a token (see Install).
432
+ - `missing on PATH: cargo`: install Rust from https://rustup.rs, or use
433
+ `--skip-setup`.
434
+ - `main may be protected`: the push to `main` was rejected. Allow the token to
435
+ bypass protection, or make the change by hand in a pull request.
436
+ - `issues are disabled on <o/r>`: enable Issues on the team repo, or ask the owner
437
+ to add you.
438
+ - `<login> is not a member of team <name>; run techbro team join`: join first.
439
+ - `no origin remote; pass --url`: add a remote (`git remote add origin <url>`) or
440
+ pass `--url`.
441
+ - `no current team`: run `team create`, `team join` or `team use`.
442
+ - `project init` inside an existing git repository prints "existing repository,
443
+ left as is" and does not run `git init`.
444
+
445
+ ## Development
446
+
447
+ ```bash
448
+ npm test # node --test, no network
449
+ npm pack --dry-run # check what gets published
450
+ npm publish
451
+ ```
452
+
453
+ Bump `version` in `package.json` for every release; `techbroVersion` in new
454
+ `.techbro.json` files and `techbro --version` come from it.
@@ -0,0 +1,60 @@
1
+ rules:
2
+ - id: techbro.js.eval
3
+ languages: [javascript, typescript]
4
+ severity: ERROR
5
+ message: eval of dynamic code
6
+ pattern: eval(...)
7
+ - id: techbro.js.new-function
8
+ languages: [javascript, typescript]
9
+ severity: WARNING
10
+ message: dynamic Function constructor
11
+ pattern: new Function(...)
12
+ - id: techbro.py.eval
13
+ languages: [python]
14
+ severity: ERROR
15
+ message: eval of dynamic code
16
+ pattern: eval(...)
17
+ - id: techbro.py.subprocess-shell
18
+ languages: [python]
19
+ severity: WARNING
20
+ message: subprocess with shell=True
21
+ pattern: subprocess.$F(..., shell=True, ...)
22
+ - id: techbro.any.private-key
23
+ languages: [regex]
24
+ severity: ERROR
25
+ message: private key material in source
26
+ pattern-regex: '-----BEGIN (RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----'
27
+ - id: techbro.any.aws-access-key
28
+ languages: [regex]
29
+ severity: ERROR
30
+ message: AWS access key id in source
31
+ pattern-regex: '\b(AKIA|ASIA)[0-9A-Z]{16}\b'
32
+ - id: techbro.any.github-token
33
+ languages: [regex]
34
+ severity: ERROR
35
+ message: GitHub token in source
36
+ pattern-regex: '\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\b|\bgithub_pat_[A-Za-z0-9_]{50,255}\b'
37
+ - id: techbro.any.slack-token
38
+ languages: [regex]
39
+ severity: ERROR
40
+ message: Slack token in source
41
+ pattern-regex: '\bxox[abprs]-[A-Za-z0-9-]{10,100}\b'
42
+ - id: techbro.rust.shell-command
43
+ languages: [rust]
44
+ severity: ERROR
45
+ message: shell invocation via sh -c
46
+ pattern-either:
47
+ - pattern: Command::new("sh").arg("-c")
48
+ - pattern: Command::new("bash").arg("-c")
49
+ - pattern: Command::new("sh").args(["-c", ...])
50
+ - pattern: Command::new("bash").args(["-c", ...])
51
+ - id: techbro.rust.unsafe-block
52
+ languages: [rust]
53
+ severity: WARNING
54
+ message: unsafe block
55
+ pattern: unsafe { ... }
56
+ - id: techbro.rust.unwrap
57
+ languages: [rust]
58
+ severity: INFO
59
+ message: unwrap on Option/Result
60
+ pattern: $X.unwrap()
@@ -0,0 +1,26 @@
1
+ name: techbro review
2
+ run-name: review ${{ inputs.request_id }}
3
+
4
+ on:
5
+ workflow_dispatch:
6
+ inputs:
7
+ sha:
8
+ description: Commit sha to review (40 hex)
9
+ required: true
10
+ type: string
11
+ request_id:
12
+ description: Request UUID
13
+ required: true
14
+ type: string
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ review:
21
+ uses: __TEAM__/.github/workflows/techbro-review.yml@main
22
+ with:
23
+ sha: ${{ inputs.sha }}
24
+ request_id: ${{ inputs.request_id }}
25
+ secrets:
26
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -0,0 +1,27 @@
1
+ name: techbro review helper
2
+ description: Stage the trusted techbro helper, rules and team data from this team repository.
3
+ inputs:
4
+ expect-version:
5
+ description: Managed version the calling workflow was rendered for
6
+ required: true
7
+ runs:
8
+ using: composite
9
+ steps:
10
+ - name: Stage trusted files
11
+ shell: bash
12
+ env:
13
+ EXPECT: ${{ inputs.expect-version }}
14
+ run: |
15
+ set -euo pipefail
16
+ a="$GITHUB_ACTION_PATH"
17
+ t="$a/../.."
18
+ v() { head -n1 "$1" | sed -n 's/^[#/]* techbro-managed v\([0-9][0-9]*\) .*/\1/p'; }
19
+ for f in "$a/action.yml" "$t/techbro/review.mjs" "$t/techbro/rules/baseline.yml" "$t/workflows/techbro-review.yml"; do
20
+ [ -f "$f" ] && [ ! -L "$f" ] || { echo "missing or unsafe trusted file" >&2; exit 1; }
21
+ [ "$(v "$f")" = "$EXPECT" ] || { echo "managed version mismatch (expected v$EXPECT); run techbro team upgrade" >&2; exit 1; }
22
+ done
23
+ mkdir -p "$GITHUB_WORKSPACE/ctl" "$GITHUB_WORKSPACE/team"
24
+ cp "$t/techbro/review.mjs" "$GITHUB_WORKSPACE/ctl/review.mjs"
25
+ cp -R "$t/techbro/rules" "$GITHUB_WORKSPACE/ctl/rules"
26
+ cp "$t/../projects.json" "$t/../members.json" "$GITHUB_WORKSPACE/team/"
27
+ if [ -f "$t/techbro-review.json" ]; then cp "$t/techbro-review.json" "$GITHUB_WORKSPACE/team/"; fi
@@ -0,0 +1,4 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "projects": {}
4
+ }