@biffo/cli 0.109.1 → 0.109.2
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.
- package/_skeletons/plugin-template/.github/workflows/ci.yml +2 -2
- package/_skeletons/plugin-template/AGENTS.md +71 -0
- package/_skeletons/plugin-template/CLAUDE.md +15 -0
- package/_skeletons/sibling-template/.github/workflows/ci.yml +2 -2
- package/_skeletons/sibling-template/.github/workflows/codeql.yml +2 -2
- package/_skeletons/sibling-template/.github/workflows/deploy.yml +1 -1
- package/_skeletons/sibling-template/AGENTS.md +71 -0
- package/_skeletons/sibling-template/CLAUDE.md +16 -0
- package/dist/index.js +19 -20
- package/package.json +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Rules of Engagement for AI Agents
|
|
2
|
+
|
|
3
|
+
These rules are **binding for every automated coding agent** working in this
|
|
4
|
+
repository — Claude Code, Codex, Cursor, or any other. They are tool-agnostic:
|
|
5
|
+
express everything as plain `git` / `gh` / `pnpm` / `uv` commands. Tool-specific
|
|
6
|
+
guidance belongs in that tool's own file (e.g. `CLAUDE.md`), which imports this
|
|
7
|
+
one rather than restating it.
|
|
8
|
+
|
|
9
|
+
This is a **Biffo satellite repo** (a plugin or sibling app), separate from the
|
|
10
|
+
core project it extends. It follows the same engagement rules as every Biffo
|
|
11
|
+
repo; only its product differs.
|
|
12
|
+
|
|
13
|
+
## 1. One integration branch: `dev`
|
|
14
|
+
|
|
15
|
+
- **`dev` is the integration branch** — you branch from it and open every PR back
|
|
16
|
+
into it. There is one environment and no production, so there is one branch.
|
|
17
|
+
`gh repo view --json defaultBranchRef` confirms it; if it reports anything other
|
|
18
|
+
than `dev`, flag it rather than working around it.
|
|
19
|
+
- **All changes land via PR.** No direct commits to `dev`, no force-pushes to it.
|
|
20
|
+
Branch protection stays on.
|
|
21
|
+
- Never leave the primary checkout parked on a feature branch or let it fall
|
|
22
|
+
behind — keep it on `dev`, no more than a `git fetch` behind, and do the work
|
|
23
|
+
in worktrees (§2).
|
|
24
|
+
|
|
25
|
+
## 2. Work in an isolated worktree
|
|
26
|
+
|
|
27
|
+
- **Always start from a freshly-fetched `dev`:** `git fetch origin`, then
|
|
28
|
+
`git worktree add .worktrees/<name> -b <type>/<slug> origin/dev`. Never branch
|
|
29
|
+
from a stale local ref.
|
|
30
|
+
- **Install dependencies in the new worktree before working** — `uv sync` and/or
|
|
31
|
+
`pnpm install`, so the local gates (pre-push checks, lint-staged) run against
|
|
32
|
+
fresh deps, not a stale `.venv`/`node_modules`.
|
|
33
|
+
- **One worktree per unit of work**, under the git-ignored `.worktrees/`.
|
|
34
|
+
- **Clean up when the PR merges:** `git worktree remove .worktrees/<name>`, then
|
|
35
|
+
let the branch be deleted. Keep `git worktree list` short and every entry live.
|
|
36
|
+
|
|
37
|
+
## 3. Commits
|
|
38
|
+
|
|
39
|
+
- **Conventional Commits** (`feat`, `fix`, `chore`, `docs`, `test`, `infra`,
|
|
40
|
+
`security`, `refactor`, `perf`, `ci`), enforced by commitlint.
|
|
41
|
+
- Tag your commits with your agent's own `Co-Authored-By:` trailer.
|
|
42
|
+
- Keep commits and PRs **scoped to one concern**.
|
|
43
|
+
|
|
44
|
+
## 4. Pull requests
|
|
45
|
+
|
|
46
|
+
- Link the issue the PR resolves (`Closes #N`) and describe what changed and why.
|
|
47
|
+
- Mark a PR **ready** (not draft) when it is meant to merge.
|
|
48
|
+
- Behavior changes ship **with tests**. Don't reduce coverage to make CI pass.
|
|
49
|
+
|
|
50
|
+
## 5. Merging — never merge red
|
|
51
|
+
|
|
52
|
+
- Get CI green and confirm it: `gh pr checks <N>`. A green local run is not
|
|
53
|
+
sufficient — verify the actual PR checks.
|
|
54
|
+
- **Squash-merge, delete the branch, remove the worktree:**
|
|
55
|
+
`gh pr merge <N> --squash --delete-branch`.
|
|
56
|
+
|
|
57
|
+
## 6. Push honestly, and verify the remote has your commit
|
|
58
|
+
|
|
59
|
+
- **Push with the exit status visible:** `git push origin HEAD; echo $?`. A pipe
|
|
60
|
+
(`git push | tail`) reports the pipe's status, not git's — a rejected push then
|
|
61
|
+
reads as success and the commit is silently lost. Never trust a "pushed"
|
|
62
|
+
message printed unconditionally after a pipe.
|
|
63
|
+
- **Confirm the remote actually has the commit before relying on it,** especially
|
|
64
|
+
before merge: `git log origin/<branch> -1`. A green PR page is not proof your
|
|
65
|
+
latest local commit reached it.
|
|
66
|
+
|
|
67
|
+
## 7. Security
|
|
68
|
+
|
|
69
|
+
- **Never commit secrets** (keys, tokens, credentials, `.env` values).
|
|
70
|
+
- **Never silently disable a security gate.** If one must be loosened, do it in
|
|
71
|
+
the open and raise a tracking issue to restore it.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# AI Assistant Context
|
|
2
|
+
|
|
3
|
+
The tool-agnostic rules of engagement for **all** automated agents (integration
|
|
4
|
+
branch, worktrees, commits, PRs, merging, honest pushes, security) live in
|
|
5
|
+
[`AGENTS.md`](AGENTS.md) and are imported below. They are binding for any change
|
|
6
|
+
in this repo; `AGENTS.md` is the single source of truth.
|
|
7
|
+
|
|
8
|
+
@AGENTS.md
|
|
9
|
+
|
|
10
|
+
## What this repo is
|
|
11
|
+
|
|
12
|
+
A **Biffo plugin** — code that extends a core Biffo project, distributed and
|
|
13
|
+
mounted by it. See the core project's ADR-0003 (plugin system) and ADR-0021
|
|
14
|
+
(shared plugin hosting) for how a plugin is installed and run. This repo carries
|
|
15
|
+
the plugin's own manifest (`biffo.plugin.json`), source, and tests.
|
|
@@ -12,9 +12,9 @@ name: CI
|
|
|
12
12
|
|
|
13
13
|
on:
|
|
14
14
|
push:
|
|
15
|
-
branches: [
|
|
15
|
+
branches: [dev, staging]
|
|
16
16
|
pull_request:
|
|
17
|
-
branches: [
|
|
17
|
+
branches: [dev, staging]
|
|
18
18
|
# Manual re-run, matching the core project's CI. Without this there is no way
|
|
19
19
|
# to trigger CI on a protected branch without pushing to it — and a sibling
|
|
20
20
|
# repo has the same branch protection, so the same recovery gap. See #293.
|
|
@@ -2,9 +2,9 @@ name: 'CodeQL'
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches: [
|
|
5
|
+
branches: [dev, staging]
|
|
6
6
|
pull_request:
|
|
7
|
-
branches: [
|
|
7
|
+
branches: [dev, staging]
|
|
8
8
|
schedule:
|
|
9
9
|
# Weekly catch-up scan (Tuesday 05:17 UTC) for issues that land outside
|
|
10
10
|
# PR review (e.g. direct merges onto a branch).
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Rules of Engagement for AI Agents
|
|
2
|
+
|
|
3
|
+
These rules are **binding for every automated coding agent** working in this
|
|
4
|
+
repository — Claude Code, Codex, Cursor, or any other. They are tool-agnostic:
|
|
5
|
+
express everything as plain `git` / `gh` / `pnpm` / `uv` commands. Tool-specific
|
|
6
|
+
guidance belongs in that tool's own file (e.g. `CLAUDE.md`), which imports this
|
|
7
|
+
one rather than restating it.
|
|
8
|
+
|
|
9
|
+
This is a **Biffo satellite repo** (a plugin or sibling app), separate from the
|
|
10
|
+
core project it extends. It follows the same engagement rules as every Biffo
|
|
11
|
+
repo; only its product differs.
|
|
12
|
+
|
|
13
|
+
## 1. One integration branch: `dev`
|
|
14
|
+
|
|
15
|
+
- **`dev` is the integration branch** — you branch from it and open every PR back
|
|
16
|
+
into it. There is one environment and no production, so there is one branch.
|
|
17
|
+
`gh repo view --json defaultBranchRef` confirms it; if it reports anything other
|
|
18
|
+
than `dev`, flag it rather than working around it.
|
|
19
|
+
- **All changes land via PR.** No direct commits to `dev`, no force-pushes to it.
|
|
20
|
+
Branch protection stays on.
|
|
21
|
+
- Never leave the primary checkout parked on a feature branch or let it fall
|
|
22
|
+
behind — keep it on `dev`, no more than a `git fetch` behind, and do the work
|
|
23
|
+
in worktrees (§2).
|
|
24
|
+
|
|
25
|
+
## 2. Work in an isolated worktree
|
|
26
|
+
|
|
27
|
+
- **Always start from a freshly-fetched `dev`:** `git fetch origin`, then
|
|
28
|
+
`git worktree add .worktrees/<name> -b <type>/<slug> origin/dev`. Never branch
|
|
29
|
+
from a stale local ref.
|
|
30
|
+
- **Install dependencies in the new worktree before working** — `uv sync` and/or
|
|
31
|
+
`pnpm install`, so the local gates (pre-push checks, lint-staged) run against
|
|
32
|
+
fresh deps, not a stale `.venv`/`node_modules`.
|
|
33
|
+
- **One worktree per unit of work**, under the git-ignored `.worktrees/`.
|
|
34
|
+
- **Clean up when the PR merges:** `git worktree remove .worktrees/<name>`, then
|
|
35
|
+
let the branch be deleted. Keep `git worktree list` short and every entry live.
|
|
36
|
+
|
|
37
|
+
## 3. Commits
|
|
38
|
+
|
|
39
|
+
- **Conventional Commits** (`feat`, `fix`, `chore`, `docs`, `test`, `infra`,
|
|
40
|
+
`security`, `refactor`, `perf`, `ci`), enforced by commitlint.
|
|
41
|
+
- Tag your commits with your agent's own `Co-Authored-By:` trailer.
|
|
42
|
+
- Keep commits and PRs **scoped to one concern**.
|
|
43
|
+
|
|
44
|
+
## 4. Pull requests
|
|
45
|
+
|
|
46
|
+
- Link the issue the PR resolves (`Closes #N`) and describe what changed and why.
|
|
47
|
+
- Mark a PR **ready** (not draft) when it is meant to merge.
|
|
48
|
+
- Behavior changes ship **with tests**. Don't reduce coverage to make CI pass.
|
|
49
|
+
|
|
50
|
+
## 5. Merging — never merge red
|
|
51
|
+
|
|
52
|
+
- Get CI green and confirm it: `gh pr checks <N>`. A green local run is not
|
|
53
|
+
sufficient — verify the actual PR checks.
|
|
54
|
+
- **Squash-merge, delete the branch, remove the worktree:**
|
|
55
|
+
`gh pr merge <N> --squash --delete-branch`.
|
|
56
|
+
|
|
57
|
+
## 6. Push honestly, and verify the remote has your commit
|
|
58
|
+
|
|
59
|
+
- **Push with the exit status visible:** `git push origin HEAD; echo $?`. A pipe
|
|
60
|
+
(`git push | tail`) reports the pipe's status, not git's — a rejected push then
|
|
61
|
+
reads as success and the commit is silently lost. Never trust a "pushed"
|
|
62
|
+
message printed unconditionally after a pipe.
|
|
63
|
+
- **Confirm the remote actually has the commit before relying on it,** especially
|
|
64
|
+
before merge: `git log origin/<branch> -1`. A green PR page is not proof your
|
|
65
|
+
latest local commit reached it.
|
|
66
|
+
|
|
67
|
+
## 7. Security
|
|
68
|
+
|
|
69
|
+
- **Never commit secrets** (keys, tokens, credentials, `.env` values).
|
|
70
|
+
- **Never silently disable a security gate.** If one must be loosened, do it in
|
|
71
|
+
the open and raise a tracking issue to restore it.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# AI Assistant Context
|
|
2
|
+
|
|
3
|
+
The tool-agnostic rules of engagement for **all** automated agents (integration
|
|
4
|
+
branch, worktrees, commits, PRs, merging, honest pushes, security) live in
|
|
5
|
+
[`AGENTS.md`](AGENTS.md) and are imported below. They are binding for any change
|
|
6
|
+
in this repo; `AGENTS.md` is the single source of truth.
|
|
7
|
+
|
|
8
|
+
@AGENTS.md
|
|
9
|
+
|
|
10
|
+
## What this repo is
|
|
11
|
+
|
|
12
|
+
A **Biffo sibling app** (ADR-0007) — an independently-deployed microservice that
|
|
13
|
+
shares the core project's login and domain (`baseurl.com/<name>`) but has its own
|
|
14
|
+
repo, CI/CD, and AWS resources. It never touches the core database directly — it
|
|
15
|
+
only ever calls the core project's own API. Its own identity lives in
|
|
16
|
+
`biffo.sibling.json`.
|
package/dist/index.js
CHANGED
|
@@ -515,10 +515,11 @@ var GitAdapter = class {
|
|
|
515
515
|
*
|
|
516
516
|
* Sets the initial branch name explicitly (`-b`) rather than relying on
|
|
517
517
|
* the running machine's `init.defaultBranch` git config, which isn't
|
|
518
|
-
* guaranteed to be "
|
|
519
|
-
* a CI runner).
|
|
518
|
+
* guaranteed to be "dev" (or even consistent between a dev's laptop and
|
|
519
|
+
* a CI runner). Defaults to `dev`, the single integration branch every Biffo
|
|
520
|
+
* repo uses (#559).
|
|
520
521
|
*/
|
|
521
|
-
async init(cwd, initialBranch = "
|
|
522
|
+
async init(cwd, initialBranch = "dev") {
|
|
522
523
|
await execa("git", ["init", "-b", initialBranch], { cwd });
|
|
523
524
|
}
|
|
524
525
|
/** Adds a remote. Fails if a remote with this name already exists. */
|
|
@@ -531,7 +532,7 @@ var GitAdapter = class {
|
|
|
531
532
|
async commit(cwd, message) {
|
|
532
533
|
await execa("git", ["commit", "-m", message], { cwd });
|
|
533
534
|
}
|
|
534
|
-
/** The current branch name (e.g. "
|
|
535
|
+
/** The current branch name (e.g. "dev"). */
|
|
535
536
|
async currentBranch(cwd) {
|
|
536
537
|
const { stdout } = await execa("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd });
|
|
537
538
|
return stdout.trim();
|
|
@@ -644,7 +645,7 @@ var GitHubAdapter = class {
|
|
|
644
645
|
* The repo's default branch — the integration branch a PR should target.
|
|
645
646
|
*
|
|
646
647
|
* Asked of GitHub rather than inferred from the local checkout: it differs per
|
|
647
|
-
* repo (
|
|
648
|
+
* repo (every Biffo repo uses `dev` (#559)), and the local
|
|
648
649
|
* current branch is whatever the caller happens to be on, which under the
|
|
649
650
|
* worktree-per-change workflow is never it.
|
|
650
651
|
*/
|
|
@@ -840,7 +841,7 @@ var GitHubAdapter = class {
|
|
|
840
841
|
`Ref "${ref}" not found in ${org}/${repo} after ${timeoutMs / 1e3}s \u2014 GitHub template generation may have stalled. Check the repository and re-run biffo init.`
|
|
841
842
|
);
|
|
842
843
|
}
|
|
843
|
-
async createBranch(org, repo, branch, from = "
|
|
844
|
+
async createBranch(org, repo, branch, from = "dev", waitTimeoutMs = 12e4, waitIntervalMs = 3e3) {
|
|
844
845
|
try {
|
|
845
846
|
await this.octokit.repos.getBranch({ owner: org, repo, branch });
|
|
846
847
|
log.info(`Branch ${branch} already exists \u2014 skipping`);
|
|
@@ -1003,7 +1004,7 @@ var GitHubAdapter = class {
|
|
|
1003
1004
|
}
|
|
1004
1005
|
async configureBranchProtection(config, protectionIntervalMs = 3e3, statusChecks = DEFAULT_STATUS_CHECKS) {
|
|
1005
1006
|
const { org, repo } = config.source_control.config;
|
|
1006
|
-
const branches = ["dev", "staging"
|
|
1007
|
+
const branches = ["dev", "staging"];
|
|
1007
1008
|
for (const branch of branches) {
|
|
1008
1009
|
log.info(`Waiting for ${branch} branch to be ready...`);
|
|
1009
1010
|
await this.waitForBranch(org, repo, branch);
|
|
@@ -1043,7 +1044,7 @@ var GitHubAdapter = class {
|
|
|
1043
1044
|
}
|
|
1044
1045
|
}
|
|
1045
1046
|
}
|
|
1046
|
-
log.success("Branch protection configured on dev
|
|
1047
|
+
log.success("Branch protection configured on dev and staging");
|
|
1047
1048
|
}
|
|
1048
1049
|
async createEnvironments(config) {
|
|
1049
1050
|
const { org, repo } = config.source_control.config;
|
|
@@ -1269,7 +1270,7 @@ var GitHubAdapter = class {
|
|
|
1269
1270
|
}
|
|
1270
1271
|
}
|
|
1271
1272
|
}
|
|
1272
|
-
async triggerWorkflow(org, repo, workflowId, inputs = {}, ref = "
|
|
1273
|
+
async triggerWorkflow(org, repo, workflowId, inputs = {}, ref = "dev", timeoutMs = 6e4, intervalMs = 5e3) {
|
|
1273
1274
|
const deadline = Date.now() + timeoutMs;
|
|
1274
1275
|
while (true) {
|
|
1275
1276
|
try {
|
|
@@ -1288,7 +1289,7 @@ var GitHubAdapter = class {
|
|
|
1288
1289
|
}
|
|
1289
1290
|
}
|
|
1290
1291
|
}
|
|
1291
|
-
async waitForWorkflowRun(org, repo, workflowId, baselineRunId, timeoutMs = 36e5, intervalMs = 3e4, branch = "
|
|
1292
|
+
async waitForWorkflowRun(org, repo, workflowId, baselineRunId, timeoutMs = 36e5, intervalMs = 3e4, branch = "dev") {
|
|
1292
1293
|
const deadline = Date.now() + timeoutMs;
|
|
1293
1294
|
while (Date.now() < deadline) {
|
|
1294
1295
|
const { data } = await this.octokit.actions.listWorkflowRuns({
|
|
@@ -1977,7 +1978,7 @@ function breakingChangesBetween(from, to, entries) {
|
|
|
1977
1978
|
}
|
|
1978
1979
|
|
|
1979
1980
|
// src/lib/global-workflows.ts
|
|
1980
|
-
var GLOBAL_DISPATCH_REF = "
|
|
1981
|
+
var GLOBAL_DISPATCH_REF = "dev";
|
|
1981
1982
|
var GLOBAL_DISPATCH_WORKFLOW_PATHS = [
|
|
1982
1983
|
".github/workflows/deploy-global.yml"
|
|
1983
1984
|
];
|
|
@@ -5300,11 +5301,11 @@ async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, git
|
|
|
5300
5301
|
// `template_version` field doc on `SiblingMarker` (lib/sibling-teardown.ts).
|
|
5301
5302
|
templateVersion: getLatestCoreVersion()
|
|
5302
5303
|
});
|
|
5303
|
-
await git.init(workDir, "
|
|
5304
|
+
await git.init(workDir, "dev");
|
|
5304
5305
|
await git.addRemote(workDir, "origin", cloneUrl);
|
|
5305
5306
|
await git.add(workDir, ["."]);
|
|
5306
5307
|
await git.commit(workDir, `feat: scaffold ${config.project.name} sibling app (ADR-0007)`);
|
|
5307
|
-
await git.push(workDir, "
|
|
5308
|
+
await git.push(workDir, "dev", { token: githubToken });
|
|
5308
5309
|
} finally {
|
|
5309
5310
|
git.cleanup(workDir);
|
|
5310
5311
|
}
|
|
@@ -5345,8 +5346,7 @@ function writeSiblingTemplate(templateRoot, targetDir, config, context) {
|
|
|
5345
5346
|
}
|
|
5346
5347
|
async function configureSiblingGithub(github, config, coreConfig, session, coreIdentity) {
|
|
5347
5348
|
const { org, repo } = githubRepo(config);
|
|
5348
|
-
await github.createBranch(org, repo, "
|
|
5349
|
-
await github.createBranch(org, repo, "staging", "main");
|
|
5349
|
+
await github.createBranch(org, repo, "staging", "dev");
|
|
5350
5350
|
await github.setDefaultBranch(org, repo, "dev");
|
|
5351
5351
|
await github.configureBranchProtection(config);
|
|
5352
5352
|
await github.createEnvironments(config);
|
|
@@ -5694,9 +5694,8 @@ async function runInit(github, aws, config, session, appSibling) {
|
|
|
5694
5694
|
}
|
|
5695
5695
|
const { org, repo } = config.source_control.config;
|
|
5696
5696
|
if (!hasCompleted(session, "github_branches")) {
|
|
5697
|
-
log.step(5, totalSteps, "Creating
|
|
5698
|
-
await github.createBranch(org, repo, "
|
|
5699
|
-
await github.createBranch(org, repo, "staging", "main");
|
|
5697
|
+
log.step(5, totalSteps, "Creating the staging branch...");
|
|
5698
|
+
await github.createBranch(org, repo, "staging", "dev");
|
|
5700
5699
|
markStepComplete(session, "github_branches");
|
|
5701
5700
|
} else {
|
|
5702
5701
|
log.step(5, totalSteps, "Branches already created \u2014 skipping");
|
|
@@ -5833,8 +5832,8 @@ function appSiblingRegistryFiles(config) {
|
|
|
5833
5832
|
}));
|
|
5834
5833
|
}
|
|
5835
5834
|
var INSTANCE_CONFIG_FILE = "biffo.config.json";
|
|
5836
|
-
var INSTANCE_FILE_BASE_BRANCH = "
|
|
5837
|
-
var INSTANCE_FILE_FOLLOWER_BRANCHES = ["
|
|
5835
|
+
var INSTANCE_FILE_BASE_BRANCH = "dev";
|
|
5836
|
+
var INSTANCE_FILE_FOLLOWER_BRANCHES = ["staging"];
|
|
5838
5837
|
var INSTANCE_FILE_BRANCHES = [
|
|
5839
5838
|
INSTANCE_FILE_BASE_BRANCH,
|
|
5840
5839
|
...INSTANCE_FILE_FOLLOWER_BRANCHES
|