@joshuafolkken/kit 1.921.0 → 1.947.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.
- package/.claude/skills/workflow-commands/background-commands.md +6 -0
- package/.claude/skills/workflow-commands/backlogrun-child.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +4 -3
- package/.claude/skills/workflow-commands/backlogrun-progress.md +13 -12
- package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -1
- package/.claude/skills/workflow-commands/observation-filing.md +3 -3
- package/.claude/skills/workflow-commands/prerequisite.md +1 -1
- package/.github/workflows/ci.yml +26 -8
- package/.github/workflows/pr-classification.yml +76 -0
- package/README.md +47 -21
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/{chunk-LC765SUA.js → chunk-3FWV6CFC.js} +119 -20
- package/dist/hooks/{chunk-UULARQ5Q.js → chunk-EYFJ74MJ.js} +13 -15
- package/dist/hooks/{chunk-O2FBZPSW.js → chunk-GIX5SAAB.js} +248 -231
- package/dist/hooks/{chunk-OLTAGGHH.js → chunk-IA7WC4NS.js} +1 -1
- package/dist/hooks/{chunk-UANRLZV7.js → chunk-IBFC25IX.js} +4 -4
- package/dist/hooks/{chunk-XMR3BS35.js → chunk-M4U4YNRE.js} +3 -3
- package/dist/hooks/{chunk-NWD47MW3.js → chunk-NKVLBB65.js} +3 -3
- package/dist/hooks/{chunk-OYU6KPRA.js → chunk-O3PEDF3M.js} +2 -2
- package/dist/hooks/{chunk-3HZ5MJIW.js → chunk-OM5VDM6F.js} +2 -2
- package/dist/hooks/{chunk-SSX23SJ2.js → chunk-RKUAD3I6.js} +1 -0
- package/dist/hooks/{chunk-77RLKZ5V.js → chunk-V7RH56HU.js} +2 -2
- package/dist/hooks/{chunk-FXAKJKTB.js → chunk-WPALEIRX.js} +1 -1
- package/dist/hooks/codex-hook-adapter.js +12 -12
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +3 -3
- package/dist/hooks/investigation-guard.js +7 -7
- package/dist/hooks/pretool-guard.js +11 -11
- package/dist/hooks/session-lang.js +2 -2
- package/dist/hooks/stop-guard.js +11 -5
- package/dist/josh.js +12 -14
- package/eslint/base.js +4 -1
- package/package.json +21 -6
- package/playwright.config.ts +9 -17
- package/ports/index.d.ts +1 -0
- package/ports/index.js +1 -0
- package/ports/web-server.d.ts +5 -0
- package/ports/web-server.js +73 -0
- package/prompts/collaboration-workflow/issue-template.md +1 -1
- package/scripts/agent/agent-role-profile.ts +11 -0
- package/scripts/ci/pr-classification.ts +27 -6
- package/scripts/document/document-byte-budget.ts +1 -1
- package/scripts/gate/hook-gate-reuse.ts +1 -1
- package/scripts/gate/project-checks.ts +60 -6
- package/scripts/gate/type-check-command.ts +43 -0
- package/scripts/git/constants.ts +4 -0
- package/scripts/git/git-command.ts +11 -5
- package/scripts/git/git-gh-pr-rollup.ts +56 -16
- package/scripts/git/git-staging.ts +6 -4
- package/scripts/git/git-stash.ts +73 -1
- package/scripts/git/stash-orphans.ts +24 -2
- package/scripts/git/stash-sweep-lock.ts +141 -0
- package/scripts/hooks/stop-guard.ts +2 -0
- package/scripts/init/init-actions.ts +3 -2
- package/scripts/init/init-ai-copy.ts +8 -5
- package/scripts/init/init-logic-migrate.ts +10 -1
- package/scripts/init/init-logic-templates.ts +27 -7
- package/scripts/init/init-logic-workspace.ts +89 -5
- package/scripts/init/init-logic.ts +7 -1
- package/scripts/init/init-static.ts +4 -1
- package/scripts/init/init.ts +60 -19
- package/scripts/init/project-profile.ts +14 -5
- package/scripts/issue/issue-merged.ts +75 -0
- package/scripts/issue/measure-rerun-cli.ts +2 -2
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +10 -0
- package/scripts/josh/josh-command-map.ts +1 -0
- package/scripts/josh/josh-command-types.ts +0 -3
- package/scripts/josh/josh-commands-ai.ts +6 -0
- package/scripts/josh/josh-commands-development.ts +6 -19
- package/scripts/lint/format.ts +68 -0
- package/scripts/lint/lint-parallel.ts +23 -36
- package/scripts/observations/observation-ledger-home.ts +22 -2
- package/scripts/observations/observation-ledger-line.ts +11 -0
- package/scripts/observations/observation-ledger-migrate.ts +146 -0
- package/scripts/observations/observation-ledger.ts +51 -3
- package/scripts/observations/observations-flush.ts +38 -7
- package/scripts/review/review-finding-ledger.ts +1 -1
- package/scripts/review/review-record-cli.ts +1 -7
- package/scripts/rules/delivered-rules.ts +10 -0
- package/scripts/rules/lane-background.ts +111 -0
- package/scripts/rules/stop-rules.ts +15 -2
- package/scripts/run/run-carry-flush.ts +2 -7
- package/scripts/run/run-hold-cli.ts +10 -2
- package/scripts/run/run-step-cli.ts +11 -1
- package/scripts/run/run-step.ts +36 -8
- package/scripts/run/run-tidy-cli.ts +50 -0
- package/scripts/run/run-tidy-lanes.ts +110 -0
- package/scripts/run/run-tidy-stashes.ts +160 -0
- package/scripts/run/run-tidy.ts +101 -0
- package/scripts/safe-chain/ci-installer-pin.ts +90 -0
- package/scripts/safe-chain/preinstall-command.ts +39 -0
- package/scripts/sync/sync.ts +5 -2
- package/scripts/test/pilot-files.ts +0 -1
- package/scripts/version/preinstall-version-update.ts +2 -0
- package/templates/pnpm-workspace.static.yaml +4 -0
- package/templates/workflows/ci.yml +21 -7
- package/tsconfig/base.json +4 -1
|
@@ -126,5 +126,11 @@ act, so the run continues rather than stalling — the sanctioned boundary disti
|
|
|
126
126
|
turn-end this section forbids. The boundary, its two commands and the resume verification are the
|
|
127
127
|
`pre-gate-cut.md` skill document, its single source.
|
|
128
128
|
|
|
129
|
+
**A headless lane child (`claude -p`) is where "a background command re-invokes the run" does not
|
|
130
|
+
hold: its background Bash tasks are killed when its turn ends** (joshuafolkken/kit#2704). It hands the
|
|
131
|
+
gate-to-merge region to `pnpm josh ship --detach` instead (`chain-rule.md` step 0); `pnpm josh
|
|
132
|
+
rule:guard` refuses a backgrounded `josh gate` / `git` / `followup` there, and the `Stop` hook sends a
|
|
133
|
+
child with a task still running back to wait rather than to notify.
|
|
134
|
+
|
|
129
135
|
The operational section above is the single source of the rule. `followup.md`, `chain-rule.md` and
|
|
130
136
|
`backlogrun-progress.md` → "Progress while the run is quiet" route here for it rather than restating it.
|
|
@@ -125,7 +125,7 @@ brief hands them to the unit.
|
|
|
125
125
|
discretionary observation has**: a child files `route:tier-a` and `route:interrupt` only, and the
|
|
126
126
|
parent files what survives — `SKILL.md` → §2i, the single source. **What the parent does with the
|
|
127
127
|
rest is append it, not drop it**: an observation that cannot cite the depth-0 work it blocked
|
|
128
|
-
becomes one line in
|
|
128
|
+
becomes one line in the observation ledger, and a second line under the same key files it. **The
|
|
129
129
|
child never writes that file** — it cannot tell its observation from the sibling lane's.
|
|
130
130
|
4. **Decisions taken and why**, where the decision was not already logged as an Issue comment.
|
|
131
131
|
5. **What was left undone**, and under whose authority.
|
|
@@ -111,16 +111,17 @@ written.
|
|
|
111
111
|
**Running unattended gets harder, not easier, and that is the honest trade.** Many lanes make the
|
|
112
112
|
overlap between children real, and every overlap that becomes a conflict costs the child that loses the
|
|
113
113
|
race a resolution, a re-run gate and a review ("Conflicts are not predicted" below), and parks it
|
|
114
|
-
outright under that section's four conditions.
|
|
115
|
-
|
|
114
|
+
outright under that section's four conditions. Report the throughput gain only with its merge-race
|
|
115
|
+
cost.
|
|
116
116
|
|
|
117
117
|
### Once per repository, before the first lane opens
|
|
118
118
|
|
|
119
119
|
In the **primary checkout**, in this order, and never again per lane:
|
|
120
120
|
|
|
121
|
-
1. `git switch main && git pull` — every lane
|
|
121
|
+
1. `git switch main && git pull` — every lane branches from this ref.
|
|
122
122
|
2. `pnpm josh latest:scope`, and the update on `required` — "`josh latest` runs once per session" above.
|
|
123
123
|
3. `pnpm josh lane:prune` — closes the lanes an interruption left registered without a work tree.
|
|
124
|
+
4. `pnpm josh run:tidy` — sweeps merged lanes and stashes (as `run:hold` does).
|
|
124
125
|
|
|
125
126
|
**`pnpm josh latest` is never run inside a lane, whatever `latest:scope` answers there** — the command
|
|
126
127
|
enforces it: `latest:scope` answers `skip` in a lane and `pnpm josh latest` refuses outright, fronted
|
|
@@ -213,7 +213,7 @@ single non-numeric line as the verdict.
|
|
|
213
213
|
`lane:await` watches local process presence and exits when any named child confirms-complete,
|
|
214
214
|
waking the parent at the actual completion rather than at the next heartbeat interval. **The
|
|
215
215
|
re-confirm delay and poll interval are the command's, not the agent's** — pass only the issue
|
|
216
|
-
numbers.
|
|
216
|
+
numbers. Without that wake (Codex), hand off instead — "A parent without a completion callback".
|
|
217
217
|
|
|
218
218
|
**A `lane:await` wake is a confirmed-gone process, so classify the ending at once — do not wait out
|
|
219
219
|
the silent-unit window** (joshuafolkken/kit#2277). `lane:await` exits only after the child's process
|
|
@@ -375,6 +375,11 @@ the check could not answer, and take `over`'s branch at that child.
|
|
|
375
375
|
only moment where **this** child's work is all written down: the PR is merged, the working tree is clean
|
|
376
376
|
on the default branch, and the epic's state on GitHub is complete.
|
|
377
377
|
|
|
378
|
+
### A parent without a completion callback hands off at its first dispatch
|
|
379
|
+
|
|
380
|
+
**A finished command never re-invokes a Codex parent, so after a dispatch `run:step` prints the `--cut`
|
|
381
|
+
below the cut cap, `wait` at it** (joshuafolkken/kit#2653): dispatch the wave, run it, end the turn.
|
|
382
|
+
|
|
378
383
|
### The lane child reuses this measurement mid-implementation
|
|
379
384
|
|
|
380
385
|
**The same `pnpm josh cost --over` measurement bounds a lane child's context _during_ implementation, not
|
|
@@ -534,20 +539,16 @@ to untangle**: `epic:next` detects it and exits with an error.
|
|
|
534
539
|
### The parent keeps no clock of its own — the watcher's exit is the wake
|
|
535
540
|
|
|
536
541
|
**After hand-off, the supervisor's driver owns the mechanical clock.** `backlog:drive` polls lane
|
|
537
|
-
completion and the backlog inside the detached process, without waking an AI parent
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
heartbeat lines, and `run:report` remains the final report source.
|
|
542
|
+
completion and the backlog inside the detached process, without waking an AI parent; the parent-turn
|
|
543
|
+
instructions below apply only to a returned judgment branch. `run:progress --wait` stays the heartbeat
|
|
544
|
+
source and `run:report` the final report.
|
|
541
545
|
|
|
542
|
-
**The numbers above are floors between asks, not a timer the parent sets
|
|
543
|
-
|
|
544
|
-
The watcher took the *reporting* out of the parent; the parent must not go on keeping the clock anyway,
|
|
545
|
-
or the two run side by side and the run pays for both.
|
|
546
|
+
**The numbers above are floors between asks, not a timer the parent sets** — one tick per turn, at the
|
|
547
|
+
largest context, is the cost `run:progress` removed.
|
|
546
548
|
|
|
547
549
|
The driver checks lane completion every five seconds and makes a new backlog offer no sooner than one
|
|
548
|
-
minute after the previous offer, except after collecting a child
|
|
549
|
-
|
|
550
|
-
exit as a prompt to act. The bounds above continue to apply to both processes.
|
|
550
|
+
minute after the previous offer, except after collecting a child — waits that cost no AI turns. The
|
|
551
|
+
watcher reports on its own interval, and a judgment session may take its exit as a prompt to act.
|
|
551
552
|
|
|
552
553
|
#### The wake exists only while something is in flight
|
|
553
554
|
|
|
@@ -535,7 +535,8 @@ ordinary way.
|
|
|
535
535
|
**So every cut is taken at a merge, which is what lets a resumed session state its own `--active`**: a
|
|
536
536
|
woken session picks the run up seconds after the merge the cut was taken at. The one shape that puts a
|
|
537
537
|
cut inside a watch — the backlog answering `exhausted` while this run's own children are still
|
|
538
|
-
merging — is the same, because the woken session has just merged a child.
|
|
538
|
+
merging — is the same, because the woken session has just merged a child. A Codex parent's cut is the one
|
|
539
|
+
taken at a dispatch instead (`backlogrun-progress.md` → "A parent without a completion callback").
|
|
539
540
|
|
|
540
541
|
**The 8-hour whole-run bound is untouched and still outranks all of this** — it is measured from the
|
|
541
542
|
record's `started_at` across every cut, so a run cannot watch its way past it in 30-minute pieces.
|
|
@@ -176,7 +176,7 @@ joshuafolkken/kit#1726 is the worked case: its own body says the depth test woul
|
|
|
176
176
|
and it was filed anyway, because **discarding it was the only alternative on offer**
|
|
177
177
|
(joshuafolkken/kit#1728).
|
|
178
178
|
|
|
179
|
-
- **The destination is `docs/observations.md` in the repository the observation is about** — the same
|
|
179
|
+
- **The destination is `docs/maintainers/observations.md` in the repository the observation is about** — the same
|
|
180
180
|
repository the Issue would have been filed into. **The count and the append are both run in that
|
|
181
181
|
repository's checkout**, resolved the way §2c (`target-repository.md`) resolves any cross-repository target, and the file is
|
|
182
182
|
created on the first append where that repository has none. **The subject decides, never the
|
|
@@ -230,7 +230,7 @@ watched. **A missing file is not a count of zero, though** — there `grep` exit
|
|
|
230
230
|
number at all, so an empty answer means create the ledger, never that this is a first sighting.
|
|
231
231
|
|
|
232
232
|
```bash
|
|
233
|
-
grep -c '^- k:<slug> |' <that repository's checkout>/docs/observations.md || true
|
|
233
|
+
grep -c '^- k:<slug> |' <that repository's checkout>/docs/maintainers/observations.md || true
|
|
234
234
|
```
|
|
235
235
|
|
|
236
236
|
Free-text comparison is what the key exists to replace, so two lines that read alike under different
|
|
@@ -255,7 +255,7 @@ that is empty on every other machine**, and every sighting was a first one, whic
|
|
|
255
255
|
joshuafolkken/kit#1728 created the ledger to end.
|
|
256
256
|
|
|
257
257
|
- **An ordinary run never commits the ledger, and that is enforced rather than remembered.**
|
|
258
|
-
`pnpm josh git` stages with `docs/observations.md` excluded, in the one staging step every entry
|
|
258
|
+
`pnpm josh git` stages with `docs/maintainers/observations.md` excluded, in the one staging step every entry
|
|
259
259
|
point goes through (`scripts/git/git-staging.ts`), so a `fullrun` in the primary checkout **cannot**
|
|
260
260
|
carry a ledger line into an Issue that has nothing to do with it. It is not a rule a run has to
|
|
261
261
|
remember at the commit — a run that had to would be the run that forgets.
|
|
@@ -18,7 +18,7 @@ another is the failure this section exists to prevent:
|
|
|
18
18
|
| A defect originating in **another package** | File the upstream Issue and **stop** — Tier A for a first-party target; a third-party one is Tier C, recorded and drafted rather than filed (`CLAUDE.md` → "Cross-package problems"; `prompts/collaboration-workflow/upstream-interrupt.md`) |
|
|
19
19
|
| This Issue was really **several** (a split) | File the children and the epic and **stop** — except under `backlogrun`, whose authorization already covers a batch, so the children are filed and run through (`split-assessment.md`) |
|
|
20
20
|
| Another Issue in **this** repository has to land first (**a prerequisite**) | This section |
|
|
21
|
-
| Something worth filing that is **none of the three** (**an observation**) | File it **without asking** — Tier A for a first-party target — and **carry the run straight on**: nothing is stashed, nothing is parked (§2i). **A delegated child does not file here**, and a filing at depth 1 or deeper cites the depth-0 work it blocked; one that cannot cite it goes to `docs/observations.md` and is filed on its second sighting — all of them §2i's |
|
|
21
|
+
| Something worth filing that is **none of the three** (**an observation**) | File it **without asking** — Tier A for a first-party target — and **carry the run straight on**: nothing is stashed, nothing is parked (§2i). **A delegated child does not file here**, and a filing at depth 1 or deeper cites the depth-0 work it blocked; one that cannot cite it goes to `docs/maintainers/observations.md` and is filed on its second sighting — all of them §2i's |
|
|
22
22
|
|
|
23
23
|
**File the prerequisite with the `route:tier-a` label**, so a Tier A filing made during implementation
|
|
24
24
|
stays countable by filing route afterwards. **This paragraph belongs to the prerequisite row, not to
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -4,9 +4,13 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches: [main]
|
|
6
6
|
|
|
7
|
+
# No `labeled` / `unlabeled`: a label added right after the pull request opens started a second
|
|
8
|
+
# run on the same commit, the concurrency block below cancelled one of the two, and a cancelled
|
|
9
|
+
# run left in the newer check suite blocked the merge (joshuafolkken/kit#2712). The one check that
|
|
10
|
+
# reads labels runs in `pr-classification.yml` instead.
|
|
7
11
|
pull_request:
|
|
8
12
|
branches: [main]
|
|
9
|
-
types: [opened, synchronize, reopened
|
|
13
|
+
types: [opened, synchronize, reopened]
|
|
10
14
|
|
|
11
15
|
# A push run is grouped by the commit it carries, so a later push to main never cancels an earlier
|
|
12
16
|
# one. Without the sha every push to main shares one group — `github.ref` is `refs/heads/main` for
|
|
@@ -23,6 +27,12 @@ concurrency:
|
|
|
23
27
|
# set the JOSH_PLAYWRIGHT_BROWSERS repository variable to a space-separated list to widen it.
|
|
24
28
|
env:
|
|
25
29
|
JOSH_PLAYWRIGHT_BROWSERS: ${{ vars.JOSH_PLAYWRIGHT_BROWSERS || 'chromium' }}
|
|
30
|
+
# The safe-chain release every "Setup safe-chain" step installs, and the SHA-256 of that release's
|
|
31
|
+
# install-safe-chain.sh. The installer puts the safe-chain binary under ~/.safe-chain/bin and
|
|
32
|
+
# adds it to PATH beside the shims; `pnpm dlx … setup-ci` left only the shims, which then let
|
|
33
|
+
# every install through without a scan (joshuafolkken/kit#2711). `josh latest` moves both together.
|
|
34
|
+
SAFE_CHAIN_INSTALLER_VERSION: 1.5.20
|
|
35
|
+
SAFE_CHAIN_INSTALLER_SHA256: 0ad25efe15d1fa56105157a454d647223e78eb0c53d1f85e3d10afcd722e7bfd
|
|
26
36
|
|
|
27
37
|
jobs:
|
|
28
38
|
node-26-pnpm:
|
|
@@ -308,7 +318,11 @@ jobs:
|
|
|
308
318
|
${{ runner.os }}-cspell-cache-
|
|
309
319
|
|
|
310
320
|
- name: Setup safe-chain
|
|
311
|
-
run:
|
|
321
|
+
run: |
|
|
322
|
+
installer="$RUNNER_TEMP/install-safe-chain.sh"
|
|
323
|
+
curl --proto '=https' -fsSL "https://github.com/AikidoSec/safe-chain/releases/download/$SAFE_CHAIN_INSTALLER_VERSION/install-safe-chain.sh" -o "$installer"
|
|
324
|
+
echo "$SAFE_CHAIN_INSTALLER_SHA256 $installer" | sha256sum -c -
|
|
325
|
+
sh "$installer" --ci
|
|
312
326
|
|
|
313
327
|
- name: Install dependencies
|
|
314
328
|
env:
|
|
@@ -352,10 +366,6 @@ jobs:
|
|
|
352
366
|
- name: Run verification gate
|
|
353
367
|
run: pnpm josh gate --verbose --no-unit
|
|
354
368
|
|
|
355
|
-
- name: Check release classification
|
|
356
|
-
if: github.event_name == 'pull_request'
|
|
357
|
-
run: pnpm josh pr:classification
|
|
358
|
-
|
|
359
369
|
# The other half of the gate, on a runner of its own. It carries the same setup steps as
|
|
360
370
|
# `static-checks` rather than sharing them: a GitHub job is its own machine, so every job that
|
|
361
371
|
# needs Node sets Node up, and this workflow already writes that preamble once per job. The
|
|
@@ -411,7 +421,11 @@ jobs:
|
|
|
411
421
|
${{ runner.os }}-pnpm-store-
|
|
412
422
|
|
|
413
423
|
- name: Setup safe-chain
|
|
414
|
-
run:
|
|
424
|
+
run: |
|
|
425
|
+
installer="$RUNNER_TEMP/install-safe-chain.sh"
|
|
426
|
+
curl --proto '=https' -fsSL "https://github.com/AikidoSec/safe-chain/releases/download/$SAFE_CHAIN_INSTALLER_VERSION/install-safe-chain.sh" -o "$installer"
|
|
427
|
+
echo "$SAFE_CHAIN_INSTALLER_SHA256 $installer" | sha256sum -c -
|
|
428
|
+
sh "$installer" --ci
|
|
415
429
|
|
|
416
430
|
- name: Install dependencies
|
|
417
431
|
env:
|
|
@@ -623,7 +637,11 @@ jobs:
|
|
|
623
637
|
${{ runner.os }}-build-cache-e2e-
|
|
624
638
|
|
|
625
639
|
- name: Setup safe-chain
|
|
626
|
-
run:
|
|
640
|
+
run: |
|
|
641
|
+
installer="$RUNNER_TEMP/install-safe-chain.sh"
|
|
642
|
+
curl --proto '=https' -fsSL "https://github.com/AikidoSec/safe-chain/releases/download/$SAFE_CHAIN_INSTALLER_VERSION/install-safe-chain.sh" -o "$installer"
|
|
643
|
+
echo "$SAFE_CHAIN_INSTALLER_SHA256 $installer" | sha256sum -c -
|
|
644
|
+
sh "$installer" --ci
|
|
627
645
|
|
|
628
646
|
- name: Install dependencies
|
|
629
647
|
env:
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: Release classification
|
|
2
|
+
|
|
3
|
+
# The release-classification check, on its own because it is the one check that reads labels
|
|
4
|
+
# (joshuafolkken/kit#2712). While it lived in CI, CI had to rerun on `labeled` / `unlabeled`; `josh pr`
|
|
5
|
+
# labels a pull request the moment it opens, so every pull request started two CI runs on one commit,
|
|
6
|
+
# and CI's per-ref concurrency cancelled one of them. GitHub judges a check name by its newest check
|
|
7
|
+
# suite, and which of the two runs sat in the newer suite was a race — when it was the cancelled one,
|
|
8
|
+
# the pull request stayed BLOCKED until someone reran it.
|
|
9
|
+
on:
|
|
10
|
+
pull_request:
|
|
11
|
+
branches: [main]
|
|
12
|
+
types: [opened, synchronize, reopened, labeled, unlabeled]
|
|
13
|
+
|
|
14
|
+
# Deliberately no `concurrency` block. Cancelling is exactly what left the blocking suite behind, and
|
|
15
|
+
# nothing here needs it: every run reads the pull request's labels as they stand when it runs, not as
|
|
16
|
+
# its event carried them, so whichever suite GitHub counts as newest holds a current verdict.
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
classification:
|
|
20
|
+
name: Release classification
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 5
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
packages: read
|
|
26
|
+
pull-requests: read
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout code
|
|
29
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
30
|
+
with:
|
|
31
|
+
persist-credentials: false
|
|
32
|
+
|
|
33
|
+
- name: Setup Node.js
|
|
34
|
+
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
35
|
+
with:
|
|
36
|
+
node-version: '24'
|
|
37
|
+
|
|
38
|
+
- name: Configure GitHub Packages authentication for existing projects
|
|
39
|
+
run: printf '//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n' >> "$HOME/.npmrc"
|
|
40
|
+
|
|
41
|
+
- name: Resolve pnpm version
|
|
42
|
+
id: pnpm-version
|
|
43
|
+
run: |
|
|
44
|
+
version=$(node -p "const m = require('./package.json'); m.packageManager?.match(/^pnpm@([^+]+)/)?.[1] ?? (m.devEngines?.packageManager?.name === 'pnpm' ? m.devEngines.packageManager.version?.split('+')[0] : 'latest') ?? 'latest'")
|
|
45
|
+
test -n "$version"
|
|
46
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
47
|
+
|
|
48
|
+
- name: Setup pnpm
|
|
49
|
+
uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
|
|
50
|
+
with:
|
|
51
|
+
version: ${{ steps.pnpm-version.outputs.version }}
|
|
52
|
+
install: false
|
|
53
|
+
|
|
54
|
+
- name: Resolve pnpm store path
|
|
55
|
+
run: echo "PNPM_STORE_PATH=$(pnpm store path)" >> "$GITHUB_ENV"
|
|
56
|
+
|
|
57
|
+
- name: Setup pnpm store cache
|
|
58
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
59
|
+
with:
|
|
60
|
+
path: ${{ env.PNPM_STORE_PATH }}
|
|
61
|
+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
|
|
62
|
+
restore-keys: |
|
|
63
|
+
${{ runner.os }}-pnpm-store-
|
|
64
|
+
|
|
65
|
+
- name: Setup safe-chain
|
|
66
|
+
run: pnpm dlx @aikidosec/safe-chain@1.5.20 setup-ci
|
|
67
|
+
|
|
68
|
+
- name: Install dependencies
|
|
69
|
+
env:
|
|
70
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
71
|
+
run: pnpm install --frozen-lockfile --ignore-scripts
|
|
72
|
+
|
|
73
|
+
- name: Check release classification
|
|
74
|
+
env:
|
|
75
|
+
GH_TOKEN: ${{ github.token }}
|
|
76
|
+
run: pnpm josh pr:classification
|
package/README.md
CHANGED
|
@@ -1,41 +1,67 @@
|
|
|
1
1
|
# @joshuafolkken/kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A development toolkit that sets up a repository for AI-assisted work with one command. `josh init` gives an AI assistant (Claude Code, Codex, Gemini, Cursor) the project's rules, and the `josh` CLI runs the checks and the Git / GitHub Issue workflow the same way in every project. You stop copying config between repositories and re-explaining conventions to the assistant.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- **Project config package** — ESLint / Prettier / tsconfig presets, prompts, and scripts consumed as a devDependency.
|
|
5
|
+
## What your project gets
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
kit does not branch by language. It asks one question — does the project use Node tooling? — and picks a [profile](./docs/init.md#project-profiles):
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
| Your project | Profile | What you get |
|
|
10
|
+
| ---------------------------------------- | -------- | ----------------------------------------------------------------- |
|
|
11
|
+
| Node project (JavaScript / TypeScript) | `node` | The full set: ESLint, type check, unit and E2E tests, Git hooks |
|
|
12
|
+
| HTML / CSS (browser JavaScript included) | `static` | Formatting (Prettier), AI assistant files, Git / VS Code settings |
|
|
13
|
+
| Another language (Python, Rust, Go, …) | `static` | AI assistant files and Git / VS Code settings only |
|
|
14
|
+
|
|
15
|
+
kit adds no linter or test runner for languages other than JavaScript and TypeScript; in a `static` project, `josh` skips each check that has nothing to run and says why.
|
|
12
16
|
|
|
13
17
|
## Quick start
|
|
14
18
|
|
|
19
|
+
In your project directory:
|
|
20
|
+
|
|
15
21
|
```bash
|
|
16
|
-
pnpm add -
|
|
17
|
-
josh
|
|
22
|
+
pnpm add -D --allow-build=esbuild @joshuafolkken/kit
|
|
23
|
+
pnpm exec josh init
|
|
24
|
+
pnpm install
|
|
25
|
+
pnpm josh format
|
|
26
|
+
pnpm josh gate
|
|
18
27
|
```
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
It needs [Node.js](https://nodejs.org/) 22.19.0 or later with [pnpm](https://pnpm.io/), even for a `static` project; [getting-started.md](./docs/getting-started.md) installs both. The global `josh` command ([cli.md](./docs/cli.md)) and the [gh CLI](https://cli.github.com/) for `josh version` and the GitHub Issue workflow are optional.
|
|
21
30
|
|
|
22
31
|
## Documentation
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
**Get started**
|
|
34
|
+
|
|
35
|
+
- [getting-started.md](./docs/getting-started.md) — first install for an `index.html` site or a non-Node project (`static`)
|
|
36
|
+
- [package.md](./docs/package.md) — add kit to a Node project (`node`)
|
|
37
|
+
- [how-to.md](./docs/how-to.md) — step-by-step guides by task: update, release, run Issues, fix a failing gate
|
|
38
|
+
|
|
39
|
+
**Understand kit**
|
|
40
|
+
|
|
41
|
+
- [why.md](./docs/why.md) — the problems kit exists to solve
|
|
42
|
+
- [overview.md](./docs/overview.md) — what kit sets up and how it works
|
|
43
|
+
|
|
44
|
+
**Commands and configuration**
|
|
45
|
+
|
|
46
|
+
- [cli.md](./docs/cli.md) — install the global `josh` command
|
|
47
|
+
- [josh-commands.md](./docs/josh-commands.md) — every `josh` command ([catalog](./docs/josh-command-catalog.md))
|
|
48
|
+
- [init.md](./docs/init.md) — what `josh init` creates, per profile
|
|
49
|
+
- [sync.md](./docs/sync.md) — what `josh sync` updates after an upgrade
|
|
50
|
+
- [manual-config.md](./docs/manual-config.md) — use the presets without `josh init`
|
|
51
|
+
- [package-api.md](./docs/package-api.md) — the config presets and libraries the package exports
|
|
52
|
+
|
|
53
|
+
**AI workflow and operations**
|
|
54
|
+
|
|
55
|
+
- [scripts-ai.md](./docs/scripts-ai.md) — Telegram notifications and the Issue workflow commands
|
|
56
|
+
- [cloud-session.md](./docs/cloud-session.md) — running in an agent container
|
|
57
|
+
- [eval.md](./docs/eval.md) — measuring whether the agent rules are followed
|
|
58
|
+
- [troubleshooting.md](./docs/troubleshooting.md) — install and auth errors
|
|
59
|
+
- [authentication.md](./docs/authentication.md) — existing GitHub Packages installs only
|
|
34
60
|
|
|
35
61
|
## Contributing
|
|
36
62
|
|
|
37
|
-
Community standards live in [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md); security reports go through [SECURITY.md](./SECURITY.md). Development conventions are
|
|
63
|
+
Community standards live in [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md); security reports go through [SECURITY.md](./SECURITY.md). Development conventions are in [CLAUDE.md](./CLAUDE.md); releases are in [publishing.md](./docs/publishing.md), and maintainer records in [docs/maintainers/](./docs/maintainers/).
|
|
38
64
|
|
|
39
65
|
## License
|
|
40
66
|
|
|
41
|
-
MIT
|
|
67
|
+
[MIT](./LICENSE)
|
|
@@ -9,12 +9,12 @@ import {
|
|
|
9
9
|
load_environment_file,
|
|
10
10
|
notice_refusal_path,
|
|
11
11
|
refusal_path
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-V7RH56HU.js";
|
|
13
|
+
import "./chunk-OM5VDM6F.js";
|
|
14
|
+
import "./chunk-O3PEDF3M.js";
|
|
15
|
+
import "./chunk-EYFJ74MJ.js";
|
|
16
|
+
import "./chunk-WPALEIRX.js";
|
|
17
|
+
import "./chunk-RKUAD3I6.js";
|
|
18
18
|
export {
|
|
19
19
|
DISABLED_VALUES,
|
|
20
20
|
SWITCH_ENV_KEY,
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lane_child_marker,
|
|
3
3
|
run_issue_number
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-O3PEDF3M.js";
|
|
5
5
|
import {
|
|
6
6
|
PLATFORM_TEMP_ROOT,
|
|
7
7
|
cost_blocks,
|
|
8
8
|
cost_usage,
|
|
9
|
+
json_value,
|
|
9
10
|
lane_paths,
|
|
10
11
|
stamp_file,
|
|
11
12
|
time_density_hook,
|
|
12
13
|
time_shell,
|
|
13
14
|
time_transcript_line
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-EYFJ74MJ.js";
|
|
15
16
|
import {
|
|
16
17
|
ENV_FILE_NAME,
|
|
17
18
|
LANE_SEAT_KEY,
|
|
18
19
|
PORT_SEED_KEY,
|
|
19
20
|
ports
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-RKUAD3I6.js";
|
|
21
22
|
|
|
22
23
|
// scripts/rules/bash-triggers.ts
|
|
23
24
|
function on_bash_command(is_match) {
|
|
@@ -111,6 +112,7 @@ var filing_cap = {
|
|
|
111
112
|
import { platform } from "node:os";
|
|
112
113
|
var GIT_COMMAND_UNIX = "/usr/bin/git";
|
|
113
114
|
var PORCELAIN_FLAG = "--porcelain";
|
|
115
|
+
var UNTRACKED_FILES_FLAG = "--untracked-files=all";
|
|
114
116
|
function get_git_command() {
|
|
115
117
|
if (platform() === "win32") {
|
|
116
118
|
return String.raw`"C:\Program Files\Git\cmd\git.exe"`;
|
|
@@ -242,7 +244,7 @@ async function branch() {
|
|
|
242
244
|
return await git_spawn.read(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
243
245
|
}
|
|
244
246
|
async function status() {
|
|
245
|
-
return await git_spawn.read(["status", PORCELAIN_FLAG,
|
|
247
|
+
return await git_spawn.read(["status", PORCELAIN_FLAG, UNTRACKED_FILES_FLAG]);
|
|
246
248
|
}
|
|
247
249
|
async function repository_root() {
|
|
248
250
|
return await git_spawn.read(["rev-parse", "--show-toplevel"]);
|
|
@@ -852,6 +854,10 @@ function detected_provider(environment) {
|
|
|
852
854
|
function resolve_provider(environment = process.env) {
|
|
853
855
|
return detected_provider(environment) ?? handed_provider(environment);
|
|
854
856
|
}
|
|
857
|
+
function has_completion_callback(environment = process.env) {
|
|
858
|
+
const selected = resolve_provider(environment);
|
|
859
|
+
return selected.kind !== "provider" || selected.provider !== OPENAI_PROVIDER;
|
|
860
|
+
}
|
|
855
861
|
function handoff_environment(environment = process.env) {
|
|
856
862
|
const selected = resolve_provider(environment);
|
|
857
863
|
return selected.kind === "provider" ? { [HANDED_PROVIDER_KEY]: selected.provider } : {};
|
|
@@ -916,6 +922,7 @@ var agent_role_profile = {
|
|
|
916
922
|
WORKER,
|
|
917
923
|
describe,
|
|
918
924
|
handoff_environment,
|
|
925
|
+
has_completion_callback,
|
|
919
926
|
is_safe_value,
|
|
920
927
|
parse,
|
|
921
928
|
phase_effort,
|
|
@@ -1511,6 +1518,112 @@ var run_cut = {
|
|
|
1511
1518
|
worktree_git_directory_sync
|
|
1512
1519
|
};
|
|
1513
1520
|
|
|
1521
|
+
// scripts/rules/shell-segments.ts
|
|
1522
|
+
var SEGMENT_SEPARATOR = /&&|\|\||;|\n/u;
|
|
1523
|
+
function segments_of(command) {
|
|
1524
|
+
return command.split(SEGMENT_SEPARATOR).map((segment) => segment.trim());
|
|
1525
|
+
}
|
|
1526
|
+
function is_josh_command(segment, names) {
|
|
1527
|
+
const named = time_shell.josh_command_of(segment);
|
|
1528
|
+
if (!named.startsWith(time_shell.JOSH_PREFIX)) return false;
|
|
1529
|
+
return names.has(named.slice(time_shell.JOSH_PREFIX.length));
|
|
1530
|
+
}
|
|
1531
|
+
var shell_segments = {
|
|
1532
|
+
SEGMENT_SEPARATOR,
|
|
1533
|
+
is_josh_command,
|
|
1534
|
+
segments_of
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1537
|
+
// scripts/rules/run-tail.ts
|
|
1538
|
+
var SEGMENT_PREFIX = String.raw`(?:\(\s*)?(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*(?:npx\s+|pnpm\s+(?:exec\s+|run\s+)?)?`;
|
|
1539
|
+
var WORD_END = String.raw`(?=\s|$|\))`;
|
|
1540
|
+
var PUSH_STEP_SEGMENT = new RegExp(String.raw`^${SEGMENT_PREFIX}josh\s+(?:git|g)${WORD_END}`, "u");
|
|
1541
|
+
var CONFIRMED_FLAG = new RegExp(String.raw`(?:^|\s)(?:-y|--yes)${WORD_END}`, "u");
|
|
1542
|
+
var SKIPS_THE_PUSH = new RegExp(String.raw`(?:^|\s)--skip-push${WORD_END}`, "u");
|
|
1543
|
+
var BACKGROUND_KEY = "run_in_background";
|
|
1544
|
+
var QUOTED_SPAN = /"[^"]*"|'[^']*'/gu;
|
|
1545
|
+
function flags_of(segment) {
|
|
1546
|
+
return segment.replaceAll(QUOTED_SPAN, " ");
|
|
1547
|
+
}
|
|
1548
|
+
function is_push_step_segment(segment) {
|
|
1549
|
+
if (!PUSH_STEP_SEGMENT.test(segment)) return false;
|
|
1550
|
+
const flags = flags_of(segment);
|
|
1551
|
+
if (SKIPS_THE_PUSH.test(flags)) return false;
|
|
1552
|
+
return CONFIRMED_FLAG.test(flags);
|
|
1553
|
+
}
|
|
1554
|
+
function is_push_step(command) {
|
|
1555
|
+
return shell_segments.segments_of(command).some((segment) => is_push_step_segment(segment));
|
|
1556
|
+
}
|
|
1557
|
+
function is_backgrounded(input) {
|
|
1558
|
+
if (!json_value.is_record(input)) return false;
|
|
1559
|
+
return input[BACKGROUND_KEY] === true;
|
|
1560
|
+
}
|
|
1561
|
+
function is_push_step_call(call) {
|
|
1562
|
+
if (call.name !== cost_blocks.BASH_TOOL) return false;
|
|
1563
|
+
return is_push_step(time_shell.bash_command(call.input));
|
|
1564
|
+
}
|
|
1565
|
+
function is_backgrounded_push_step(call) {
|
|
1566
|
+
if (!is_backgrounded(call.input)) return false;
|
|
1567
|
+
return is_push_step_call(call);
|
|
1568
|
+
}
|
|
1569
|
+
function is_foreground_push_step(call) {
|
|
1570
|
+
if (is_backgrounded(call.input)) return false;
|
|
1571
|
+
return is_push_step_call(call);
|
|
1572
|
+
}
|
|
1573
|
+
function decide3() {
|
|
1574
|
+
return true;
|
|
1575
|
+
}
|
|
1576
|
+
var RUN_TAIL_REASON = '\u26D4 run tail: this issues the commit-push-PR step in the foreground, where the harness decides when to stop waiting. Reissue it with `run_in_background` set. In `fullrun #1501` this call was given a 900-second timeout, the harness detached it at its own 600-second cap, and nothing read the output for a further 6m28s \u2014 45m03s of wall clock on about 17 minutes of work (joshuafolkken/kit#1510). **Never give a foreground call a timeout above the harness cap**: the cap decides, not the number. A backgrounded call re-invokes you when it exits, so the completion notification is what resumes the run. **When it arrives, the turn that reads it goes straight through any branch-2 filing and `pnpm josh epic:bundle` to `pnpm josh followup` \u2014 the turn never ends at the push.** CI went green 6m06s before the merge started in that run, and it started only because the person asked; joshuafolkken/kit#1333 had already settled that the merge is issued in the turn the review closes. While the push runs, do the work that writes nothing \u2014 drafting the completion body to a file, deciding the branch-2 disposition. `pnpm josh followup` itself stays in the **foreground** \u2014 nearly every step after it reads its result. But a tail does follow the merge, measured at 3.0 min and 5.9% of a run (joshuafolkken/kit#1462), so empty it beforehand: the steps that read the merge result stay after `followup`, and the rest are composed in the turn that issues it, bar the one exception background-commands.md names. The procedure is `.claude/skills/workflow-commands/background-commands.md`, "A command that can take minutes is issued in the background". **This rule fires on every foreground push, not once per run**, so reissuing the same call in the foreground will be refused again.';
|
|
1577
|
+
var run_tail = {
|
|
1578
|
+
RUN_TAIL_REASON,
|
|
1579
|
+
decide: decide3,
|
|
1580
|
+
is_backgrounded_push_step,
|
|
1581
|
+
is_foreground_push_step,
|
|
1582
|
+
is_push_step,
|
|
1583
|
+
is_push_step_call
|
|
1584
|
+
};
|
|
1585
|
+
|
|
1586
|
+
// scripts/rules/lane-background.ts
|
|
1587
|
+
var LONG_RUNNING_COMMANDS = new Set(
|
|
1588
|
+
["gate", "git", "followup"].map((name) => `${time_shell.JOSH_PREFIX}${name}`)
|
|
1589
|
+
);
|
|
1590
|
+
var BACKGROUND_KEY2 = "run_in_background";
|
|
1591
|
+
function is_backgrounded2(input) {
|
|
1592
|
+
return json_value.is_record(input) && input[BACKGROUND_KEY2] === true;
|
|
1593
|
+
}
|
|
1594
|
+
function runs_long_command(command) {
|
|
1595
|
+
return time_shell.josh_commands_of(command).some((name) => LONG_RUNNING_COMMANDS.has(name));
|
|
1596
|
+
}
|
|
1597
|
+
function is_candidate(call) {
|
|
1598
|
+
if (run_tail.is_push_step_call(call)) return true;
|
|
1599
|
+
if (call.name !== cost_blocks.BASH_TOOL || !is_backgrounded2(call.input)) return false;
|
|
1600
|
+
return runs_long_command(time_shell.bash_command(call.input));
|
|
1601
|
+
}
|
|
1602
|
+
function is_background_long_run(call, is_lane_child = () => lane_child_marker.is_child_of(process.cwd())) {
|
|
1603
|
+
return is_candidate(call) && is_lane_child();
|
|
1604
|
+
}
|
|
1605
|
+
function pending_background_ids(tail) {
|
|
1606
|
+
const lines = tail.split("\n").map((line) => time_transcript_line.parse_line(line));
|
|
1607
|
+
const finished = new Set(lines.map((line) => line?.finished_background ?? ""));
|
|
1608
|
+
const launched = lines.flatMap((line) => line?.blocks.map((block) => block.background_id) ?? []);
|
|
1609
|
+
return launched.filter((id) => id !== "" && !finished.has(id));
|
|
1610
|
+
}
|
|
1611
|
+
var LANE_BACKGROUND_REASON = '\u26D4 lane background: this session is a dispatched lane child (`JOSH_LANE_CHILD`), a headless `claude -p` process whose background Bash tasks are killed when the turn ends \u2014 so a backgrounded `josh gate` / `josh git` / `josh followup` never re-invokes you; it dies at `exit code 143` (joshuafolkken/kit#2704, measured on the #2606 child: implementation, review and live verification done, the run parked instead of merged). Hand the gate-to-merge region to the detached supervisor instead: `pnpm josh ship --detach --review "<title> #<N>"` (add `--cite <N>`), then end the turn on `launched` / `busy` \u2014 `.claude/skills/workflow-commands/chain-rule.md` step 0 is the single source. A single check you must read before continuing runs in the foreground within the tool timeout; the commit-push step does not \u2014 in a child it too belongs to the detached ship, in either spelling. This rule fires on every occurrence, not once per run.';
|
|
1612
|
+
var LANE_BACKGROUND_STOP_REASON = "\u26D4 lane background stop: this is a dispatched lane child (`JOSH_LANE_CHILD`) and a background task it launched has not finished \u2014 ending this headless turn kills it, and nothing re-invokes you afterwards (joshuafolkken/kit#2704). Do not send a notification and do not stop: wait for the task in the foreground and act on its result, or re-issue the work through `pnpm josh ship --detach`, whose supervisor outlives the turn (`.claude/skills/workflow-commands/chain-rule.md` step 0). Do not repeat your previous reply.";
|
|
1613
|
+
var ROW2 = {
|
|
1614
|
+
id: "lane-background",
|
|
1615
|
+
is_trigger: (call) => is_background_long_run(call),
|
|
1616
|
+
reason: LANE_BACKGROUND_REASON,
|
|
1617
|
+
decide: () => true
|
|
1618
|
+
};
|
|
1619
|
+
var lane_background = {
|
|
1620
|
+
LANE_BACKGROUND_REASON,
|
|
1621
|
+
LANE_BACKGROUND_STOP_REASON,
|
|
1622
|
+
ROW: ROW2,
|
|
1623
|
+
is_background_long_run,
|
|
1624
|
+
pending_background_ids
|
|
1625
|
+
};
|
|
1626
|
+
|
|
1514
1627
|
// scripts/git/issue-labels.ts
|
|
1515
1628
|
var BUG_LABEL = "bug";
|
|
1516
1629
|
var ENHANCEMENT_LABEL = "enhancement";
|
|
@@ -1530,22 +1643,6 @@ function has_label_name(labels, wanted) {
|
|
|
1530
1643
|
return label_name_of(labels, wanted) !== void 0;
|
|
1531
1644
|
}
|
|
1532
1645
|
|
|
1533
|
-
// scripts/rules/shell-segments.ts
|
|
1534
|
-
var SEGMENT_SEPARATOR = /&&|\|\||;|\n/u;
|
|
1535
|
-
function segments_of(command) {
|
|
1536
|
-
return command.split(SEGMENT_SEPARATOR).map((segment) => segment.trim());
|
|
1537
|
-
}
|
|
1538
|
-
function is_josh_command(segment, names) {
|
|
1539
|
-
const named = time_shell.josh_command_of(segment);
|
|
1540
|
-
if (!named.startsWith(time_shell.JOSH_PREFIX)) return false;
|
|
1541
|
-
return names.has(named.slice(time_shell.JOSH_PREFIX.length));
|
|
1542
|
-
}
|
|
1543
|
-
var shell_segments = {
|
|
1544
|
-
SEGMENT_SEPARATOR,
|
|
1545
|
-
is_josh_command,
|
|
1546
|
-
segments_of
|
|
1547
|
-
};
|
|
1548
|
-
|
|
1549
1646
|
// scripts/rules/lane-park.ts
|
|
1550
1647
|
var NOTIFY_COMMANDS = /* @__PURE__ */ new Set(["notify"]);
|
|
1551
1648
|
var CONFIRMATION_TASK_TYPE = /(?:^|\s)--task-type[=\s]+["']?confirmation\b/u;
|
|
@@ -3295,6 +3392,8 @@ export {
|
|
|
3295
3392
|
ALREADY_DONE_LABEL,
|
|
3296
3393
|
has_any_label,
|
|
3297
3394
|
has_label_name,
|
|
3395
|
+
run_tail,
|
|
3396
|
+
lane_background,
|
|
3298
3397
|
lane_park,
|
|
3299
3398
|
repo_party,
|
|
3300
3399
|
FULL_PAGE_QUERY,
|