Commit 7889fed
authored
* feat(audit): --format sarif on `codemap audit` (Slice 1a of #73 plan)
First half of Slice 1 from the GitHub Marketplace Action plan. `audit`
today only emits `--json`; this adds SARIF 2.1.0 output directly, no
JSON→SARIF transform step needed in CI workflows.
CLI:
- New `--format <text|json|sarif>` flag (default `text`).
- `--json` stays as backward-compat shortcut for `--format json`.
- `--json` + `--format <other>` rejected as a contradiction with a
helpful error message.
- `--summary` is a no-op with `--format sarif` (SARIF results are
per-row, not counts) and surfaces a stderr warning.
- `runAuditCmd` signature updated: `json: boolean` → `format:
AuditOutputFormat` (`"text" | "json" | "sarif"`).
SARIF shape:
- One rule per delta key (`codemap.audit.files-added`,
`codemap.audit.dependencies-added`,
`codemap.audit.deprecated-added`).
- One result per `added` row (severity = `warning`; audit deltas are
more actionable than per-recipe `note`).
- Locations auto-detected via the existing
`file_path`/`path`/`to_path`/`from_path` priority list (same as
`query --format sarif`); `line_start`/`line_end` populate the SARIF
`region`.
- `removed` rows intentionally excluded — SARIF surfaces findings to
act on, not cleanups.
- Location-only rows (e.g. files-added has only `path`) get a "new
files: src/foo.ts" message, not the generic "(no message)" fallback.
Tests:
- 4 new SARIF-formatter unit tests (rules/results shape; empty deltas;
missing location column; line range).
- 4 new audit-CLI parser tests (--format sarif/json/=json, unknown
format value, --json + --format contradiction, --json + --format
json reconcilable).
- All existing audit / output-formatters tests updated for the
`json: bool` → `format: AuditOutputFormat` field rename.
Lockstep updates:
- `.agents/rules/codemap.md` + `templates/agents/rules/codemap.md`
audit-vs-git-ref row gains `--format sarif` mention.
- New changeset (.changeset/audit-format-sarif.md, minor).
Slice 1b (`--ci` aggregate flag on `query` + `audit`) lands in the
follow-up PR.
* feat(cli): --ci aggregate flag on `query` + `audit` (Slice 1b of #73 plan)
Second half of Slice 1. `--ci` is the GitHub-Action-shaped CI flag:
- Aliases `--format sarif`
- Sets process.exitCode = 1 when ≥1 finding/addition surfaced
- Suppresses the no-locatable-rows stderr warning (CI templates would
surface it as red noise; the row-set itself is the gating signal)
Lands on both `query` and `audit` (the two finding-producing verbs).
Same parser / resolver semantics on both:
- Mutually exclusive with `--json` (different format aliases)
- Mutually exclusive with `--format <other>` (contradicts the alias)
- `--ci --format sarif` redundant but accepted (consumers may set both
for clarity in CI templates)
Wiring:
- `parseQueryRest` / `parseAuditRest` gain `--ci` token + `ci: boolean`
in the run-shape union.
- `runQueryCmd` / `runAuditCmd` gain `ci?: boolean` opt; threaded
through to `printFormattedQuery` (query) and the post-render
exit-code branch (audit).
- `query`: exit 1 if `rows.length > 0` after SARIF emit.
- `audit`: exit 1 if any delta has `added.length > 0` after SARIF emit.
Tests:
- 4 new `cmd-query` parser tests (--ci alias; --ci+--json reject;
--ci+--format json reject; --ci+--format sarif accept).
- 4 new `cmd-audit` parser tests (same matrix).
- All existing toEqual tests updated for the `ci: false` field default.
Smoke verified end-to-end:
- `query --ci` with results → SARIF stdout, exit 1.
- `audit --baseline X --ci` against identical baseline → 0 additions,
exit 0. Diff with adds → exit 1.
- Contradiction tests (`--ci --json`) emit clear error + exit 1.
* feat(action): action.yml + scripts/detect-pm.mjs (Slice 2 of #73 plan)
Composite GitHub Action wrapping codemap CLI for the Marketplace.
~16 declarative inputs per Q1 resolution; package-manager detection +
codemap CLI invocation resolution via package-manager-detector
(antfu/userquin, MIT, 0 transitive deps, 23 kB).
action.yml shape:
- Skip-on-non-PR-events for the headline α default (audit --base
${{ github.base_ref }} --ci). Other events (push, schedule, …)
no-op + log "no PR context, skipping" + exit 0 unless an explicit
command: input is passed.
- 16 declarative inputs across 3 categories:
- WHERE TO RUN: working-directory, package-manager (override),
version (CLI pin), state-dir
- WHAT TO RUN: mode (audit | recipe | aggregate | command), recipe,
params, baseline, audit-base, changed-since, group-by, command
(escape hatch)
- WHAT TO DO WITH OUTPUT: format (default sarif), output-path,
upload-sarif, pr-comment (Slice 3 stub for v1.0), fail-on, token
- Validation precedence: command > mode > defaults; mode='aggregate'
rejected (reserved for v1.x post-Q6 SARIF rule.id de-dup work).
- 4 outputs: agent / exec / install_method (debug breadcrumbs) +
output-file (echoes inputs.output-path).
- Composite steps: gate → setup-node → detect-pm → validate → run →
upload-sarif (if Code Scanning enabled) → pr-comment-stub.
scripts/detect-pm.mjs:
- Wraps `package-manager-detector`'s `detect()` + `resolveCommand()`.
- Implements the Q3 invocation logic:
- VERSION env var set → 'execute' intent (dlx-pinned)
- codemap in devDependencies → 'execute-local'
- else → 'execute' intent (dlx-latest)
- Outputs to $GITHUB_OUTPUT per current Actions convention (set-output
deprecated 2022-10).
- Validates PACKAGE_MANAGER override against known agents.
- 8 unit tests covering: pnpm/bun/npm autodetect, no-lockfile fallback,
execute-local for project-installed, dlx-pinned override, manual PM
override, unknown PM rejection, packageManager-field priority over
lockfile.
New runtime dep: package-manager-detector@1.6.0 (MIT, antfu/userquin,
0 transitive deps).
* feat(cli): codemap pr-comment + action.yml integration (Slice 3 of #73 plan)
Slice 3 — markdown PR-summary writer for the cases SARIF→Code-Scanning
doesn't cover well: private repos without GHAS, repos that haven't
enabled Code Scanning, aggregate audit deltas without a single
file:line anchor, and bot-context seeding (review bots read PR
conversation, not workflow artifacts).
Architecture (engine + CLI separation, mirrors show / impact / audit):
- src/application/pr-comment-engine.ts — pure transport-agnostic
renderer. Auto-detects input shape (audit envelope vs SARIF doc) +
renders markdown grouped by delta key (audit) or rule id (SARIF).
Lists >50 entries collapse to `… and N more`. Removed rows surface
in their own collapsed section (audit only).
- src/cli/cmd-pr-comment.ts — CLI wrapper. Reads JSON from a file or
stdin (`-`). `--shape audit|sarif` overrides autodetection;
`--json` emits structured envelope `{ markdown, findings_count,
kind }` for action.yml steps.
- src/cli/main.ts + src/cli/bootstrap.ts wire the new `pr-comment`
verb (whitelist + dispatch).
action.yml integration (Slice 2 stub replaced):
- pr-comment toggle now actually invokes `codemap pr-comment` against
the SARIF / JSON output file produced by the run step, then posts
via `gh pr comment <PR> -F -`. Same binary that produced the output
renders the comment — version stream stays coherent.
Tests:
- 12 new pr-comment-engine unit tests (input shape detection,
no-drift / no-findings ✅ rendering, audit summary line + per-delta
sections, SARIF rule grouping, location formatting, >50 collapse).
- Smoke verified: real audit envelope produces markdown with file:
links + delta sections; SARIF doc groups findings by rule id.
Lockstep updates (per docs/README.md Rule 10):
- .agents/rules/codemap.md + templates/agents/rules/codemap.md
gain rows for `--ci` aggregate flag (Slice 1b) and `pr-comment`
renderer (Slice 3).
* ci(action): dogfood action-smoke job + bundle changeset (Slice 4 of #73 plan)
Adds an `action-smoke` job to .github/workflows/ci.yml that runs
`uses: ./` from this repo on every PR. Validates:
- action.yml YAML syntax + composite-step flow
- gate step (skip-on-non-PR; command-set-overrides-skip)
- setup-node + npm install of package-manager-detector
- scripts/detect-pm.mjs execution + GITHUB_OUTPUT writes
- detect-pm command resolution (`<pm> dlx codemap@latest --version`)
Smoke uses `command: --version` to avoid the real-audit dependency
chain (audit baselines etc.) — codemap audit logic is covered by the
unit-test suite. The action-smoke validates the wrapper, not the
underlying CLI.
Non-blocking (`continue-on-error: true`) until v1.0.0 of codemap is
published — today the action pulls codemap@latest from npm (0.4.0),
which works for `--version` but doesn't validate v1.x flags. Promote
to a hard gate when v1.0.0 ships.
Bundles the v1.0 changeset:
- .changeset/marketplace-action.md describes the full Slice 1b-4
surface (--ci flag, action.yml, pr-comment, dogfood) as one minor
release. Slice 5 (Marketplace publish + listing metadata) is
post-merge once a v1.0.0 tag exists.
* chore: slim new comments + sweep docs staleness
Per .agents/rules/concise-comments.md — re-read every comment authored
in this PR and slimmed the ones the code itself already conveys. Net:
+68 / -163 lines of comments / docstrings; same information density,
fewer words.
Cuts/slims (in order of file):
- output-formatters.ts: AuditSarifDelta + formatAuditSarif JSDoc; the
message-fallback inline comment.
- cmd-audit.ts: AUDIT_OUTPUT_FORMATS doc-block; jsonShortcut /
ci / Reconcile / --ci-exit / emitAuditError comments.
- cmd-query.ts: --ci variable + resolveFormat doc; printFormattedQuery's
ci option doc; "no-locatable-rows warning" inline; runQueryCmd's
ci option JSDoc; parseQueryRest's ci field JSDoc.
- pr-comment-engine.ts: file header; RenderedComment JSDoc; "kind"
field doc; detectCommentInputShape doc; renderAuditComment +
renderSarifComment JSDocs; "Header summary line" / "Group by ruleId"
/ "Prefer the most-specific identity columns first." / "Unknown
shape — fall through to JSON." inline comments.
- cmd-pr-comment.ts: PrCommentOpts.shape doc; printPrCommentCmdHelp
JSDoc; runPrCommentCmd JSDoc; "detected here is …" inline;
readStdinSync gotcha doc.
- scripts/detect-pm.mjs: file header; "Step 1/2/3:" labels; "Local /
non-Actions invocation —" comment; codemapInDevDependencies JSDoc.
- action.yml: "Build args based on inputs" inline.
Docs staleness sweep — fact-checked against the codebase:
- docs/architecture.md § "Audit wiring" listed audit's flags but
pre-dated --format sarif + --ci. Added both + the formatAuditSarif
shape note.
- docs/architecture.md § "Output formatters" missed formatAuditSarif.
Added (one rule per delta key, severity warning, removed-rows
excluded, location-only fallback).
- docs/architecture.md § "Query wiring" missed --ci. Added.
- docs/architecture.md added a new "PR-comment wiring" section
(mirrors the cmd ↔ engine seam pattern).
- docs/glossary.md added `--ci` (under C) and `pr-comment` (under P)
entries per Rule 9.
- README.md CLI examples updated with `audit --format sarif`,
`audit --ci`, `query --ci`, and the `pr-comment` pipe-to-`gh pr
comment` idiom.
Pre-existing comments (preserve-comments rule) untouched.
* fix(action): empty defaults for github-context inputs (composite actions reject expressions in input defaults)
CI failure on PR #74's Action smoke job:
TemplateValidationException: Unrecognized named-value: 'github'.
Located at position 1 within expression: github.base_ref / github.token
GitHub composite actions do NOT allow ${{ github.* }} expressions in
input defaults. Only `runs:` step expressions can reference the github
context. Two inputs were affected:
- `audit-base`: now defaults to "". The existing run step already does
`BASE="${AUDIT_BASE:-$BASE_REF}"` where `BASE_REF: ${{ github.base_ref }}`
is set as an env var (legal in step env blocks), so empty-input → PR
base_ref behavior is preserved.
- `token`: now defaults to "". Two call sites (`upload-sarif` step's
`token:` arg + `pr-comment` step's `GH_TOKEN`) now use
`${{ inputs.token != '' && inputs.token || github.token }}` to fall
back to `github.token` when unset.
Both inputs' descriptions updated to document the empty-falls-back
behavior so consumers know what to expect.
* fix: address PR #74 CodeRabbit review (6 findings, all real)
Fact-checked each finding against the codebase; all valid. Two were
critical correctness bugs (T2, T3, T5) that would have shipped silently
broken behavior.
T2 (Major) — `scripts/detect-pm.mjs` wrong package-name keys
`codemapInDevDependencies` checked `manifest?.dependencies?.codemap` but
the published package is `@stainless-code/codemap` — the project-installed
branch was dead code for all real consumers; `dlx-pinned` / `dlx-latest`
also pulled the unscoped `codemap` package (a different npm namespace
entirely). Fixed:
- Lookup checks both scoped (`@stainless-code/codemap`) and bare
(`codemap`) keys — the latter for monorepos that alias via
`"codemap": "workspace:*"`.
- `'execute'` (dlx) commandArgs now use the scoped published name so
npm/pnpm/yarn/bun resolve the right registry entry.
- `'execute-local'` keeps `["codemap"]` because that's the bin alias
per `package.json#bin`, regardless of the scoped name.
- Tests updated: scoped-dev-dep / bare-dev-dep / scoped-dlx-pinned
cases. Old tests that asserted dlx-with-unscoped-`codemap@<v>` were
themselves testing a bug.
T3 (Critical) — no-op `expect()` in formatAuditSarif test
`expect(run.results.every(...))` without a chained matcher creates and
discards the expectation. If formatAuditSarif reverted to severity
`note`, the test would still pass. Added `.toBe(true)`.
T5 (Major) — `require()` in ESM module
`readStdinSync` in `cmd-pr-comment.ts` used `require("node:fs").readSync`
but the file loads via `await import()` (ESM); `require` is undefined at
runtime. `codemap pr-comment -` would have crashed for every user. My
unit tests passed file paths, not stdin — caught nothing. Imported
`readSync` from `node:fs` at the top, used directly.
T4 (Major) — pr-comment SARIF renderer dropped runs[1+]
`renderSarifComment` only read `doc.runs?.[0]?.results`. Valid SARIF
allows multiple runs (merged / multi-tool docs). Now flattens via
`(doc.runs ?? []).flatMap(run => run.results ?? [])`. New test
`aggregates results across multi-run SARIF docs (not just runs[0])`.
T1 (Minor) — `mode: command` without `command:` input falls through
The validate step accepted `mode=command` but didn't guard against
empty `command:`. Run step's if/elif only handled `audit` + `recipe`,
so `$EXEC` would invoke with empty `$ARGS`. Added an explicit guard
mirroring the `mode=recipe` pattern.
T6 (Minor) — `--ci` doc said "no-findings warning"; actual is "no-locatable-rows"
Both `.agents/rules/codemap.md` and `templates/agents/rules/codemap.md`
described the suppressed warning incorrectly. Aligned wording with the
implementation in `printFormattedQuery`.
* docs(research): mark § 1.5 boundary-violations as shipped (PR #72)
`non-goals-reassessment-2026-05.md` § 1 Shipped table + § 7 Lifted-to
table contradicted each other on § 1.5 — § 1 didn't list it (lifted
table was last edited before PR #72), § 7 still said "(pending)".
Boundary-violations shipped 2026-05 as PR #72; both tables now reflect
that, plus the "Pending picks" enumeration drops § 1.5.
Per Rule 8, research notes get closed/lifted but factual state may
still be corrected when the codebase moves under them. This is
hygiene, not extension of the analysis.
* docs(plan): consolidate Slice 5 runbook for later pickup
Slice 5 (publish + Marketplace listing) is post-merge work — anyone
should be able to pick it up without triangulating across the plan's
Q7 release-workflow + Q10 listing-metadata + Slice-5 one-liner.
Added a "Slice 5 runbook (post-merge)" subsection that:
- Surfaces the CLI / Action version stream decoupling explicitly:
Action ships at v1 against CLI 0.5.0 (the version PR #74's
changeset bumps to). CLI v1.0.0 is not required.
- Lists 7 sequenced executable steps from "merge PR #74" through to
"delete this plan per Rule 3" (the canonical close-out per
docs-governance).
- Calls out which docs already hold the durable design decisions
(architecture.md / glossary.md / agent rules / MARKETPLACE.md) so
the deletion in step 7 is safe.
- Documents subsequent-release cadence (changesets-driven; force-push
the floating v<major> tag) and the major-bump policy.
1 parent f1a28c1 commit 7889fed
26 files changed
Lines changed: 2000 additions & 100 deletions
File tree
- .agents/rules
- .changeset
- .github/workflows
- docs
- plans
- research
- scripts
- src
- application
- cli
- templates/agents/rules
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
162 | 191 | | |
163 | 192 | | |
164 | 193 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
112 | 112 | | |
113 | 113 | | |
114 | 114 | | |
| 115 | + | |
| 116 | + | |
115 | 117 | | |
116 | 118 | | |
117 | 119 | | |
| |||
127 | 129 | | |
128 | 130 | | |
129 | 131 | | |
| 132 | + | |
130 | 133 | | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
131 | 137 | | |
132 | 138 | | |
133 | 139 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
0 commit comments