@joshuafolkken/kit 1.887.0 → 1.929.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 (127) hide show
  1. package/.claude/skills/workflow-commands/backlogrun-progress.md +13 -12
  2. package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -1
  3. package/.claude/skills/workflow-commands/fullrun-steps.md +4 -2
  4. package/.claude/skills/workflow-commands/halfrun.md +3 -1
  5. package/.claude/skills/workflow-commands/kickoff.md +5 -3
  6. package/.claude/skills/workflow-commands/observation-filing.md +3 -1
  7. package/.claude/skills/workflow-commands/prerequisite.md +3 -1
  8. package/.claude/skills/workflow-commands/target-repository.md +1 -1
  9. package/.github/pull_request_template.md +12 -0
  10. package/.github/release.yml +3 -0
  11. package/.github/workflows/ci.yml +8 -0
  12. package/.github/workflows/production.yml +10 -1
  13. package/.github/workflows/publish.yml +72 -2
  14. package/CLAUDE.md +2 -0
  15. package/README.md +2 -1
  16. package/dist/CLAUDE.md +2 -0
  17. package/dist/CLAUDE.static.md +8 -0
  18. package/dist/hooks/batch-guard.js +6 -6
  19. package/dist/hooks/{chunk-264QY6KS.js → chunk-2PGRQ62K.js} +25 -10
  20. package/dist/hooks/chunk-3V37IID6.js +82 -0
  21. package/dist/hooks/{chunk-36UE53LS.js → chunk-7XQ6TK3R.js} +2 -2
  22. package/dist/hooks/{chunk-PRDCNGA6.js → chunk-BNFG2IFK.js} +4 -4
  23. package/dist/hooks/{chunk-VGYSA2NT.js → chunk-GQ3EVWHY.js} +22 -3
  24. package/dist/hooks/{chunk-Y5OYCU2F.js → chunk-I3JP3D4A.js} +3 -3
  25. package/dist/hooks/{chunk-6MQCXEEA.js → chunk-LCJZE42I.js} +25 -88
  26. package/dist/hooks/{chunk-ZBK4XRAS.js → chunk-NJZSO7BK.js} +3 -3
  27. package/dist/hooks/{chunk-SSX23SJ2.js → chunk-RKUAD3I6.js} +1 -0
  28. package/dist/hooks/{chunk-FRJKVSK3.js → chunk-ROULPSQC.js} +2 -2
  29. package/dist/hooks/{chunk-FNFOLFFV.js → chunk-SXPTRFPG.js} +2 -2
  30. package/dist/hooks/{chunk-FXAKJKTB.js → chunk-WPALEIRX.js} +1 -1
  31. package/dist/hooks/{chunk-4OXFU3MK.js → chunk-YO43HEPY.js} +634 -156
  32. package/dist/hooks/codex-hook-adapter.js +13 -12
  33. package/dist/hooks/duplicate-read-guard.js +7 -7
  34. package/dist/hooks/format-edited.js +4 -3
  35. package/dist/hooks/investigation-guard.js +7 -7
  36. package/dist/hooks/pretool-guard.js +12 -11
  37. package/dist/hooks/session-lang.js +2 -2
  38. package/dist/hooks/stop-guard.js +5 -5
  39. package/dist/josh.js +156 -20
  40. package/eslint/base.js +4 -1
  41. package/package.json +72 -18
  42. package/playwright.config.ts +9 -17
  43. package/ports/index.d.ts +1 -0
  44. package/ports/index.js +1 -0
  45. package/ports/web-server.d.ts +5 -0
  46. package/ports/web-server.js +73 -0
  47. package/prettier/index.js +3 -8
  48. package/prettier/static.d.ts +3 -0
  49. package/prettier/static.js +10 -0
  50. package/prompts/collaboration-workflow/issue-template.md +11 -4
  51. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  52. package/prompts/collaboration-workflow/wip-cap.md +2 -2
  53. package/prompts/review.md +2 -2
  54. package/prompts/testing-guide.md +1 -0
  55. package/scripts/agent/agent-role-profile.ts +11 -0
  56. package/scripts/build/build-claude-md.ts +6 -1
  57. package/scripts/ci/pr-classification.ts +114 -0
  58. package/scripts/document/document-byte-budget.ts +3 -2
  59. package/scripts/gate/gate-report.ts +2 -0
  60. package/scripts/gate/gate-steps.ts +50 -0
  61. package/scripts/gate/project-checks.ts +145 -0
  62. package/scripts/gate/verification-gate.ts +14 -43
  63. package/scripts/git/git-common-directory.ts +9 -1
  64. package/scripts/git/git-gh-pr-rollup.ts +29 -1
  65. package/scripts/git/git-gh-pr.ts +61 -1
  66. package/scripts/git/git-pr.ts +62 -13
  67. package/scripts/git/issue-labels.ts +9 -0
  68. package/scripts/init/distributed-paths.ts +9 -5
  69. package/scripts/init/hook-command-bootstrap.ts +40 -8
  70. package/scripts/init/hook-command-rewrite.ts +7 -3
  71. package/scripts/init/init-actions.ts +94 -7
  72. package/scripts/init/init-ai-copy.ts +107 -19
  73. package/scripts/init/init-logic-workspace.ts +38 -5
  74. package/scripts/init/init-logic.ts +1 -0
  75. package/scripts/init/init-static.ts +55 -0
  76. package/scripts/init/init.ts +97 -30
  77. package/scripts/init/project-profile-cli.ts +14 -0
  78. package/scripts/init/project-profile.ts +158 -0
  79. package/scripts/init/start.ts +30 -0
  80. package/scripts/issue/issue-bug-label.ts +13 -0
  81. package/scripts/issue/issue-classification.ts +74 -0
  82. package/scripts/issue/issue-lint-cli.ts +10 -3
  83. package/scripts/issue/markdown-section.ts +38 -4
  84. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +27 -1
  85. package/scripts/josh/josh-command-map.ts +3 -0
  86. package/scripts/josh/josh-commands-development.ts +8 -15
  87. package/scripts/josh/josh-commands-project.ts +14 -1
  88. package/scripts/josh/josh-logic.ts +13 -0
  89. package/scripts/josh/run-github-prerequisite.ts +32 -0
  90. package/scripts/lane/lane-await.ts +8 -1
  91. package/scripts/lane/openai-lane-ship.ts +30 -0
  92. package/scripts/lane/openai-lane-supervisor.ts +35 -8
  93. package/scripts/lane/openai-review-broker.ts +260 -0
  94. package/scripts/lint/cspell-cached.ts +21 -0
  95. package/scripts/lint/format.ts +68 -0
  96. package/scripts/lint/lint-parallel.ts +30 -1
  97. package/scripts/observations/observations-flush.ts +1 -1
  98. package/scripts/package/production-dependency-graph.ts +68 -0
  99. package/scripts/package/verify-optional-eslint-install.ts +62 -0
  100. package/scripts/release/github-release-cli.ts +11 -0
  101. package/scripts/release/github-release.ts +220 -0
  102. package/scripts/release/publish-tag-cli.ts +8 -0
  103. package/scripts/release/publish-tag.ts +11 -0
  104. package/scripts/release/release-publish.ts +7 -2
  105. package/scripts/rules/delivered-rules.ts +2 -0
  106. package/scripts/rules/issue-bug-label.ts +85 -0
  107. package/scripts/rules/issue-filing-args.ts +50 -0
  108. package/scripts/run/run-merge-cli.ts +19 -0
  109. package/scripts/run/run-merge.ts +9 -1
  110. package/scripts/run/run-ship-cli.ts +20 -0
  111. package/scripts/run/run-ship-detach.ts +180 -14
  112. package/scripts/run/run-ship-review-steps.ts +45 -25
  113. package/scripts/run/run-step-cli.ts +11 -1
  114. package/scripts/run/run-step.ts +36 -8
  115. package/scripts/run/run-wake-session.ts +3 -1
  116. package/scripts/test/pilot-files.ts +0 -2
  117. package/scripts/test/skip-marker.ts +3 -0
  118. package/scripts/test/test-declared-changed.ts +2 -1
  119. package/scripts/test/test-declared-logic.ts +15 -10
  120. package/scripts/test/test-declared.ts +20 -8
  121. package/scripts/test/test-red-logic.ts +3 -3
  122. package/scripts/test/test-unit-guard.ts +24 -12
  123. package/templates/CLAUDE.static.md +8 -0
  124. package/templates/cursorrules.static +1 -0
  125. package/templates/pnpm-workspace.static.yaml +4 -0
  126. package/templates/prettierignore.static +12 -0
  127. package/templates/workflows/ci.yml +5 -0
@@ -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. The historical
538
- parent-turn instructions below apply only while an AI session is handling a returned judgment branch;
539
- they do not schedule the supervisor's normal loop. `run:progress --wait` remains the source of human
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.** A parent that sets one spends
543
- a turn per tick at the point its context is largest — the exact cost `run:progress` was built to remove.
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. These waits cost no AI turns.
549
- The progress watcher keeps reporting on its own interval; a judgment session may still receive its
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.
@@ -7,6 +7,8 @@ the run is at, and the sections below carry that step in full.
7
7
 
8
8
  ## The `fullrun #N` step list
9
9
 
10
+ Use `pnpm josh issue:lint` `labels:`; see `prompts/collaboration-workflow/issue-template.md`.
11
+
10
12
  **Add `in-progress` the moment `run:hold` answered `hold`** (create if missing:
11
13
  `gh api repos/{owner}/{repo}/labels -f name=in-progress -f color=0075ca -f description="Work is
12
14
  actively in progress" --silent 2>/dev/null || true`, then `gh api
@@ -49,8 +51,8 @@ completion notification, then merges; if blockers are found it exits non-zero
49
51
  `kickoff new` + `fullrun #N` in one run. Steps: (1) Derive an English title, or use the provided one.
50
52
  **(1a) Run `pnpm josh issue:scout "<title>" [--body "<summary>"]` before creating the Issue** — a
51
53
  candidate that covers the same work stops the run rather than filing a second Issue (`SKILL.md` → §2e).
52
- (2) Create Issue: `gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=depth:<n>' -f
53
- body="<body>"` (body per `prompts/collaboration-workflow/issue-template.md`). Capture `<N>`. (3) Add
54
+ (2) Create Issue: `gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=depth:<n>' -F
55
+ body=@<body-file>` (per `prompts/collaboration-workflow/issue-template.md`). Capture `<N>`. (3) Add
54
56
  `in-progress` (as above). (4) Post the agreed plan in the session language: fill the body if blank,
55
57
  otherwise add a comment. (5) If the working tree already has staged or modified files,
56
58
  `git stash push -m "fullrun new: pre-existing changes"` first. (6) `git switch main && git pull`. (7)
@@ -1,5 +1,7 @@
1
1
  # `halfrun` — the manifest (implement + verify, stop before commit)
2
2
 
3
+ When this run files a new Issue, run `pnpm josh issue:lint <body-file>` and apply every label in its `labels:` answer at creation; `prompts/collaboration-workflow/issue-template.md` is the single source for this classification. Keep the existing `depth` and `route` labels.
4
+
3
5
  `halfrun` sits between `kickoff` (plan only) and `fullrun` (full execution with auto-merge). It
4
6
  implements the change and runs the full verification gate, then **stops before commit** — nothing is
5
7
  committed, pushed, or opened as a PR — so the user manually verifies the change (typically by exercising
@@ -52,7 +54,7 @@ back with fixes, treat each as a new round: implement, re-run `pnpm josh gate`,
52
54
  `halfrun new` or `halfrun new "<title>"`: `kickoff new` + `halfrun #<N>` (no Issue exists yet). Steps
53
55
  mirror `fullrun new` (1)–(8): derive an English title (or use the provided one) → **run `pnpm josh
54
56
  issue:scout "<title>" [--body "<summary>"]` before creating the Issue** (`SKILL.md` → §2e) → create the
55
- Issue (`gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=depth:<n>' -f body="<body>"`,
57
+ Issue (`gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=depth:<n>' -F body=@<body-file>`,
56
58
  body per `prompts/collaboration-workflow/issue-template.md`) → add `in-progress` → post the agreed plan → stash
57
59
  any pre-existing changes with `git stash push -m "halfrun new: pre-existing changes"`, popped by
58
60
  message with `pnpm josh stash:pop "halfrun new: pre-existing changes"`, never a positional `git stash pop` →
@@ -47,13 +47,13 @@ split is found.
47
47
  title. **(1a) Run `pnpm josh issue:scout "<title>" [--body "<summary>"]` before creating the Issue** —
48
48
  a candidate that covers the same work stops the run rather than filing a second Issue (`SKILL.md` →
49
49
  §2e). (2) Create Issue: `gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=depth:<n>'
50
- -f body="<body>"` (body per `prompts/collaboration-workflow/issue-template.md`). Capture `<N>`. (3)
50
+ -F body=@<body-file>` (body per `prompts/collaboration-workflow/issue-template.md`). Capture `<N>`. (3)
51
51
  Post the plan in the session language, using the same body/comment logic as `kickoff #<N>`. (4) Send
52
52
  Telegram notification. (5) **Stop** — do not implement. **Multi-issue split path**: (1) For each
53
53
  independent deliverable, derive a focused English title, **run `pnpm josh issue:scout "<sub-title>"`
54
54
  on it** (`SKILL.md` → §2e), and create a separate Issue with the `route:split` label: `gh api
55
55
  repos/{owner}/{repo}/issues -f title="<sub-title>" -f 'labels[]=route:split' -f 'labels[]=depth:<n>'
56
- -f body="<body>"`. Capture each Issue number. **When the split is filed into a repository other than
56
+ -F body=@<body-file>`. Capture each Issue number. **When the split is filed into a repository other than
57
57
  the one this session is running in**, every child body gets the `## Origin` backlink described in the
58
58
  cross-package rule the AI documents keep resident, the epic body carries the same link as prose or a
59
59
  plain bullet (never as a checkbox row, which would disable its auto-close), and the originating Issue
@@ -66,7 +66,7 @@ split is found.
66
66
  josh epic:check <E>`. Only where `josh` is unavailable, fall back to the manual procedure: ensure the
67
67
  label exists (`gh api repos/{owner}/{repo}/labels -f name=epic -f color=5319e7 -f description="Tracks
68
68
  a batch of child issues from one split" --silent 2>/dev/null || true`), then create the epic with `gh
69
- api repos/{owner}/{repo}/issues -f title="<epic-title>" -f 'labels[]=epic' -f body="<body>"`. Its body
69
+ api repos/{owner}/{repo}/issues -f title="<epic-title>" -f 'labels[]=epic' -F body=@<body-file>`. Its body
70
70
  follows the epic format in `prompts/collaboration-workflow/issue-template.md` — split rationale,
71
71
  dependencies, the `backlogrun` command, and a child task list in task-list syntax (`- [ ] #N`). The epic
72
72
  exists as the **non-closing home for the split rationale**. When the children have no required order,
@@ -80,3 +80,5 @@ split is found.
80
80
  **database id**, not its issue number; a failure here is non-fatal). Never fold the relation into the
81
81
  creation call. (3) Send Telegram notification listing all created issues. (4) Present the command
82
82
  `backlogrun #<E> --only`. (5) **Stop** — do not implement.
83
+
84
+ Before filing, run `pnpm josh issue:lint <body-file>` and apply every label in its `labels:` answer at creation, following `prompts/collaboration-workflow/issue-template.md` for classification.
@@ -29,6 +29,8 @@ backlog pool (`backlogrun-steps.md` → "What one invocation approves").
29
29
 
30
30
  The detail below is read at the filing decision, and the marker suite pins the rules here.
31
31
 
32
+ Before filing an observation, run `pnpm josh issue:lint <body-file>` and apply every label in its `labels:` answer at creation; `prompts/collaboration-workflow/issue-template.md` is the single source. Keep the depth label.
33
+
32
34
  ### The depth test — a discretionary filing cites the product work it blocked
33
35
 
34
36
  **A run that has just spent an hour inside the workflow tooling files findings about the workflow
@@ -55,7 +57,7 @@ cap's branch-2 filing all pass through a `gh api … issues` call, and the depth
55
57
  beside whatever `route:` label that call already carries.
56
58
 
57
59
  ```bash
58
- gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=depth:1' -f body="<body>"
60
+ gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=depth:1' -F body=@<body-file>
59
61
  gh api repos/{owner}/{repo}/issues/<N>/labels -f 'labels[]=depth:1' # an Issue already filed
60
62
  ```
61
63
 
@@ -25,8 +25,10 @@ stays countable by filing route afterwards. **This paragraph belongs to the prer
25
25
  the table** — the label means a filing the run is *blocked by*, so the observation row carries no
26
26
  `route:` label of its own (§2i):
27
27
 
28
+ Before that call, run `pnpm josh issue:lint <body-file>` and apply every label in its `labels:` answer at creation; `prompts/collaboration-workflow/issue-template.md` is the single source. Keep `route:tier-a` and `depth`.
29
+
28
30
  ```bash
29
- gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=route:tier-a' -f 'labels[]=depth:<n>' -f body="<body>"
31
+ gh api repos/{owner}/{repo}/issues -f title="<title>" -f 'labels[]=route:tier-a' -f 'labels[]=depth:<n>' -F body=@<body-file>
30
32
  ```
31
33
 
32
34
  Every "file the prerequisite" below means that labelled filing, and it always happens **first**: the
@@ -41,7 +41,7 @@ backlogrun joshuafolkken/kit#858 --only
41
41
  never clone. The one exception is the split path's epic, since `pnpm josh epic` only writes the
42
42
  repository it runs in — run it in that repository's checkout, or fall back to `gh api
43
43
  repos/<owner/repo>/labels …` followed by `gh api repos/<owner/repo>/issues -f title="<epic-title>" -f
44
- 'labels[]=epic' -f body="<body>"`, and report that `epic:check` could not be run. The promote arm has
44
+ 'labels[]=epic' -F body=@<body-file>`, and report that `epic:check` could not be run. The promote arm has
45
45
  no such fallback: with no checkout there, file the children and stop.
46
46
  - **The implementing entries require a checkout and never create one — when the target is another
47
47
  repository.** A prefix naming the session's own repository changes nothing (`fullrun kit#412` in the
@@ -1 +1,13 @@
1
1
  closes #
2
+
3
+ ## Release classification
4
+
5
+ Add exactly one label before merging:
6
+
7
+ - `breaking-change` — any incompatible change, even when it also adds a feature or fixes a bug
8
+ - `enhancement` — a compatible new feature or improvement
9
+ - `bugfix` — a compatible fix
10
+ - `other-change` — documentation, dependencies, or maintenance included in Other Changes
11
+ - `ignore-for-release` — omit this pull request from the changelog
12
+
13
+ Automated pull requests without a classification label are treated as Other Changes. If an automated pull request needs another classification or exclusion, add that one label.
@@ -13,6 +13,9 @@ changelog:
13
13
  labels:
14
14
  - Semver-Minor
15
15
  - enhancement
16
+ - title: Bug Fixes 🐛
17
+ labels:
18
+ - bugfix
16
19
  - title: Other Changes
17
20
  labels:
18
21
  - '*'
@@ -6,6 +6,7 @@ on:
6
6
 
7
7
  pull_request:
8
8
  branches: [main]
9
+ types: [opened, synchronize, reopened, labeled, unlabeled]
9
10
 
10
11
  # A push run is grouped by the commit it carries, so a later push to main never cancels an earlier
11
12
  # one. Without the sha every push to main shares one group — `github.ref` is `refs/heads/main` for
@@ -351,6 +352,10 @@ jobs:
351
352
  - name: Run verification gate
352
353
  run: pnpm josh gate --verbose --no-unit
353
354
 
355
+ - name: Check release classification
356
+ if: github.event_name == 'pull_request'
357
+ run: pnpm josh pr:classification
358
+
354
359
  # The other half of the gate, on a runner of its own. It carries the same setup steps as
355
360
  # `static-checks` rather than sharing them: a GitHub job is its own machine, so every job that
356
361
  # needs Node sets Node up, and this workflow already writes that preamble once per job. The
@@ -425,6 +430,9 @@ jobs:
425
430
  - name: Run unit tests
426
431
  run: pnpm josh test:unit
427
432
 
433
+ - name: Verify optional ESLint consumer install
434
+ run: pnpm exec tsx scripts/package/verify-optional-eslint-install.ts
435
+
428
436
  # **The required status check. `Checks` is a name the branch ruleset already requires, and this job
429
437
  # is what keeps that name meaning what it meant** (joshuafolkken/kit#1226) — every check the single
430
438
  # `Checks` job used to run has passed. It runs no checks itself; it reads the two halves' results.
@@ -2,9 +2,18 @@ name: Release to Production
2
2
  on:
3
3
  repository_dispatch:
4
4
  types: [new-tag-created]
5
+ workflow_call:
6
+ inputs:
7
+ tag:
8
+ required: true
9
+ type: string
5
10
 
6
11
  jobs:
7
12
  update-branch:
13
+ if: github.repository != 'joshuafolkken/kit' || inputs.tag != ''
14
+ concurrency:
15
+ group: production-update
16
+ queue: max
8
17
  runs-on: ubuntu-latest
9
18
  permissions:
10
19
  contents: write
@@ -24,7 +33,7 @@ jobs:
24
33
  # repository_dispatch always runs on the default branch, so the event ref
25
34
  # resolves to that branch, not the released tag. Use the tag carried in the
26
35
  # dispatch payload (set by auto-tag.yml) as the merge source instead.
27
- REF_NAME: ${{ github.event.client_payload.tag }}
36
+ REF_NAME: ${{ inputs.tag || github.event.client_payload.tag }}
28
37
  run: |
29
38
  if [ -z "$REF_NAME" ]; then
30
39
  echo "::error::new-tag-created dispatch carried no tag payload; aborting." >&2
@@ -1,4 +1,5 @@
1
1
  name: Publish
2
+ run-name: Publish ${{ github.event.client_payload.tag }}
2
3
 
3
4
  on:
4
5
  repository_dispatch:
@@ -6,6 +7,9 @@ on:
6
7
 
7
8
  jobs:
8
9
  publish-github:
10
+ concurrency:
11
+ group: package-publish
12
+ queue: max
9
13
  runs-on: ubuntu-latest
10
14
  timeout-minutes: 10
11
15
  permissions:
@@ -36,11 +40,24 @@ jobs:
36
40
 
37
41
  - run: pnpm install --frozen-lockfile --ignore-scripts
38
42
 
39
- - run: pnpm publish --no-git-checks --tag latest --registry https://npm.pkg.github.com
43
+ - name: Select GitHub Packages tag
44
+ id: publish-tag
45
+ run: |
46
+ version=$(node -p "require('./package.json').version")
47
+ latest=$(cd "$RUNNER_TEMP" && npm view @joshuafolkken/kit dist-tags.latest --registry https://npm.pkg.github.com)
48
+ tag=$(pnpm exec tsx scripts/release/publish-tag-cli.ts "$version" "$latest")
49
+ echo "tag=$tag" >> "$GITHUB_OUTPUT"
50
+ env:
51
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52
+
53
+ - run: pnpm publish --no-git-checks --tag ${{ steps.publish-tag.outputs.tag }} --registry https://npm.pkg.github.com
40
54
  env:
41
55
  NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42
56
 
43
57
  publish-npm:
58
+ concurrency:
59
+ group: package-publish
60
+ queue: max
44
61
  runs-on: ubuntu-latest
45
62
  timeout-minutes: 10
46
63
  permissions:
@@ -72,5 +89,58 @@ jobs:
72
89
 
73
90
  - run: pnpm pack --out "$RUNNER_TEMP/kit.tgz"
74
91
 
92
+ - name: Select npm tag
93
+ id: publish-tag
94
+ run: |
95
+ version=$(node -p "require('./package.json').version")
96
+ latest=$(cd "$RUNNER_TEMP" && npm view @joshuafolkken/kit dist-tags.latest --registry https://registry.npmjs.org)
97
+ tag=$(pnpm exec tsx scripts/release/publish-tag-cli.ts "$version" "$latest")
98
+ echo "tag=$tag" >> "$GITHUB_OUTPUT"
99
+
75
100
  - name: Publish to npm
76
- run: cd "$RUNNER_TEMP" && npm publish kit.tgz --access public --tag latest --registry https://registry.npmjs.org --userconfig /dev/null
101
+ run: cd "$RUNNER_TEMP" && npm publish kit.tgz --access public --tag ${{ steps.publish-tag.outputs.tag }} --registry https://registry.npmjs.org --userconfig /dev/null
102
+
103
+ update-production:
104
+ needs: [publish-github, publish-npm]
105
+ permissions:
106
+ contents: write
107
+ uses: ./.github/workflows/production.yml
108
+ with:
109
+ tag: ${{ github.event.client_payload.tag }}
110
+
111
+ create-release:
112
+ needs: [publish-github, publish-npm, update-production]
113
+ runs-on: ubuntu-latest
114
+ timeout-minutes: 65
115
+ permissions:
116
+ actions: read
117
+ contents: write
118
+ steps:
119
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
120
+ with:
121
+ ref: ${{ github.event.client_payload.tag }}
122
+ fetch-depth: 0
123
+
124
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
125
+ with:
126
+ node-version: 24
127
+
128
+ - name: Resolve pnpm version
129
+ id: pnpm-version
130
+ run: |
131
+ 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'")
132
+ test -n "$version"
133
+ echo "version=$version" >> "$GITHUB_OUTPUT"
134
+
135
+ - uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
136
+ with:
137
+ version: ${{ steps.pnpm-version.outputs.version }}
138
+ install: false
139
+
140
+ - run: pnpm install --frozen-lockfile --ignore-scripts
141
+
142
+ - name: Publish GitHub Release
143
+ run: pnpm exec tsx scripts/release/github-release-cli.ts
144
+ env:
145
+ GH_TOKEN: ${{ github.token }}
146
+ RELEASE_TAG: ${{ github.event.client_payload.tag }}
package/CLAUDE.md CHANGED
@@ -108,6 +108,7 @@ For every code modification, in order:
108
108
 
109
109
  - **Tests are required for ALL code changes** (bug fixes, timing/animation fixes and refactors included): bug fix → regression test; UI/animation/timing → E2E for the observable change; logic/utility → unit; refactor → tests pinning existing behavior BEFORE the change (`prompts/refactoring.md`).
110
110
  - **Non-runtime updates (pre-approved manual-only exception)**: a change touching no executable runtime code path may proceed with manual verification — declare it in Step 0 and state why. It covers docs, non-executable config, editor/IDE files, and cosmetic asset swaps with no selector/path change; the mechanically exempt (`*.md`, `.editorconfig`, `.idea/**`, `.vscode/**`, `prompts/**`) are recognized by the command below, and the rest stay the exemption a person declares. A test infeasible for a runtime change needs its reason stated and user approval.
111
+ - **static の HTML/CSS 例外:** `package.json` に `josh.profile: static` が記録され、変更が HTML/CSS だけなら自動テストの代わりにブラウザーで表示を確認する。対象ページを開き、想定する画面幅でレイアウト・リンク・HTML 内の操作を確認し、観察結果を報告する。HTML/CSS と同時に変更した場合も、JavaScript と TypeScript のファイルには自動テストが必要。`pnpm josh test:declared` はこの場合だけ理由を添えて `exempt` と表示する。
111
112
  - **The commit stage answers this mechanically**: `pnpm josh test:declared` prints `required` / `exempt` / `satisfied` from the changed paths, and a runtime change with no test is refused at `pnpm josh git -y` on `required` — add the declared test or declare the non-runtime exception, then reissue (`prompts/collaboration-workflow/rule-delivery.md`).
112
113
  - **Read a target's line headroom before editing: `pnpm josh lines <path>`** — near the limit, `Target` carries the splitting plan.
113
114
 
@@ -121,6 +122,7 @@ For every code modification, in order:
121
122
  Run the full verification set **in order**; do not skip, reorder, or report completion on a failed or skipped step without the user agreeing. Inside a `fullrun` / `backlogrun` the gate runs beside the review and the merge chain (`workflow-commands` skill → §2, `prompts/review.md`).
122
123
 
123
124
  0. **Test gate** — no tests added? Continue only under the non-runtime exception (Step 0) or explicit approval; otherwise stop and add tests.
125
+ static プロファイルの HTML/CSS だけの変更には、Step 0 の手動ブラウザー確認を適用できる。
124
126
  1. **Refactor** — `prompts/refactoring.md`, until no high/medium items remain.
125
127
  2. **`pnpm josh gate`** — lint, type check, spell check and unit tests concurrently. One gate per run; while implementing re-run a single check by name (`pnpm josh lint:related` / `pnpm josh cspell:dot` / `pnpm josh test:related`).
126
128
  3. **Self-review** — a subagent runs `/code-review` per `prompts/review.md` (never a main-line `Skill` load); level from the changed paths, at most two rounds, high/medium findings resolved. `prompts/review.md` → "Review round cap".
package/README.md CHANGED
@@ -7,7 +7,7 @@ Shared toolchain config and CLI for TypeScript projects — ESLint, Prettier, Ty
7
7
 
8
8
  ## Prerequisites
9
9
 
10
- - [Node.js](https://nodejs.org/) with [pnpm](https://pnpm.io/)
10
+ - [Node.js](https://nodejs.org/) 22.19.0 or later with [pnpm](https://pnpm.io/) — [getting-started.md](./docs/getting-started.md) covers installing both
11
11
  - [gh CLI](https://cli.github.com/) for `josh version` and existing GitHub Packages consumers. It is not required to install kit from public npm; see [authentication.md](./docs/authentication.md) for setup when needed.
12
12
 
13
13
  ## Quick start
@@ -24,6 +24,7 @@ Using the kit inside a project? See [docs/package.md](./docs/package.md).
24
24
  | Guide | What it covers |
25
25
  | ----------------------------------------------- | -------------------------------------------------------------------------------- |
26
26
  | [why.md](./docs/why.md) | Why kit exists — the AI-development problems it solves, for practitioners |
27
+ | [getting-started.md](./docs/getting-started.md) | First install for an `index.html` site or a non-Node project, without Git |
27
28
  | [authentication.md](./docs/authentication.md) | GitHub Packages auth for existing projects |
28
29
  | [cli.md](./docs/cli.md) | Install and use the global `josh` CLI |
29
30
  | [package.md](./docs/package.md) | Use the kit as a project devDependency — configs, prompts, scripts, `josh init` |
package/dist/CLAUDE.md CHANGED
@@ -108,6 +108,7 @@ For every code modification, in order:
108
108
 
109
109
  - **Tests are required for ALL code changes** (bug fixes, timing/animation fixes and refactors included): bug fix → regression test; UI/animation/timing → E2E for the observable change; logic/utility → unit; refactor → tests pinning existing behavior BEFORE the change (`node_modules/@joshuafolkken/kit/prompts/refactoring.md`).
110
110
  - **Non-runtime updates (pre-approved manual-only exception)**: a change touching no executable runtime code path may proceed with manual verification — declare it in Step 0 and state why. It covers docs, non-executable config, editor/IDE files, and cosmetic asset swaps with no selector/path change; the mechanically exempt (`*.md`, `.editorconfig`, `.idea/**`, `.vscode/**`, `prompts/**`) are recognized by the command below, and the rest stay the exemption a person declares. A test infeasible for a runtime change needs its reason stated and user approval.
111
+ - **static の HTML/CSS 例外:** `package.json` に `josh.profile: static` が記録され、変更が HTML/CSS だけなら自動テストの代わりにブラウザーで表示を確認する。対象ページを開き、想定する画面幅でレイアウト・リンク・HTML 内の操作を確認し、観察結果を報告する。HTML/CSS と同時に変更した場合も、JavaScript と TypeScript のファイルには自動テストが必要。`pnpm josh test:declared` はこの場合だけ理由を添えて `exempt` と表示する。
111
112
  - **The commit stage answers this mechanically**: `pnpm josh test:declared` prints `required` / `exempt` / `satisfied` from the changed paths, and a runtime change with no test is refused at `pnpm josh git -y` on `required` — add the declared test or declare the non-runtime exception, then reissue (`node_modules/@joshuafolkken/kit/prompts/collaboration-workflow/rule-delivery.md`).
112
113
  - **Read a target's line headroom before editing: `pnpm josh lines <path>`** — near the limit, `Target` carries the splitting plan.
113
114
 
@@ -121,6 +122,7 @@ For every code modification, in order:
121
122
  Run the full verification set **in order**; do not skip, reorder, or report completion on a failed or skipped step without the user agreeing. Inside a `fullrun` / `backlogrun` the gate runs beside the review and the merge chain (`workflow-commands` skill → §2, `node_modules/@joshuafolkken/kit/prompts/review.md`).
122
123
 
123
124
  0. **Test gate** — no tests added? Continue only under the non-runtime exception (Step 0) or explicit approval; otherwise stop and add tests.
125
+ static プロファイルの HTML/CSS だけの変更には、Step 0 の手動ブラウザー確認を適用できる。
124
126
  1. **Refactor** — `node_modules/@joshuafolkken/kit/prompts/refactoring.md`, until no high/medium items remain.
125
127
  2. **`pnpm josh gate`** — lint, type check, spell check and unit tests concurrently. One gate per run; while implementing re-run a single check by name (`pnpm josh lint:related` / `pnpm josh cspell:dot` / `pnpm josh test:related`).
126
128
  3. **Self-review** — a subagent runs `/code-review` per `node_modules/@joshuafolkken/kit/prompts/review.md` (never a main-line `Skill` load); level from the changed paths, at most two rounds, high/medium findings resolved. `node_modules/@joshuafolkken/kit/prompts/review.md` → "Review round cap".
@@ -0,0 +1,8 @@
1
+ # Project instructions
2
+
3
+ - 既存のファイルと利用者の設定を尊重し、変更する前に内容を確認する。
4
+ - 変更には検証を付け、実行した結果を報告する。
5
+ - HTML、CSS、JavaScript のファイルがある場合は Prettier で整形する。
6
+ - 画面に見える変更をした場合はブラウザーで表示と操作を確認する。
7
+ - JavaScript または TypeScript の動作を変更した場合は、その動作を確認する自動テストを追加する。
8
+ - Git や GitHub を使う場合だけ、それぞれのワークフローを利用する。
@@ -9,12 +9,12 @@ import {
9
9
  load_environment_file,
10
10
  notice_refusal_path,
11
11
  refusal_path
12
- } from "./chunk-FRJKVSK3.js";
13
- import "./chunk-36UE53LS.js";
14
- import "./chunk-FNFOLFFV.js";
15
- import "./chunk-264QY6KS.js";
16
- import "./chunk-FXAKJKTB.js";
17
- import "./chunk-SSX23SJ2.js";
12
+ } from "./chunk-ROULPSQC.js";
13
+ import "./chunk-7XQ6TK3R.js";
14
+ import "./chunk-SXPTRFPG.js";
15
+ import "./chunk-2PGRQ62K.js";
16
+ import "./chunk-WPALEIRX.js";
17
+ import "./chunk-RKUAD3I6.js";
18
18
  export {
19
19
  DISABLED_VALUES,
20
20
  SWITCH_ENV_KEY,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ENV_FILE_NAME
3
- } from "./chunk-SSX23SJ2.js";
3
+ } from "./chunk-RKUAD3I6.js";
4
4
 
5
5
  // scripts/lib/json-value.ts
6
6
  function parse_or_undefined(text) {
@@ -684,6 +684,12 @@ var FILTER_ARGUMENTS = "[filters...]";
684
684
  var MATCH_ARGUMENTS = "[--match]";
685
685
  var PATH_ARGUMENTS = "<path...>";
686
686
  var DEV_COMMANDS = {
687
+ "pr:classification": {
688
+ script: "scripts/ci/pr-classification.ts",
689
+ description: "Require one release classification on a pull request",
690
+ category: "Development",
691
+ reference: ["", "automation", ["none"]]
692
+ },
687
693
  [GATE_COMMAND]: {
688
694
  script: "scripts/gate/verification-gate.ts",
689
695
  description: "Run lint, type check, spell check and unit tests concurrently",
@@ -727,14 +733,7 @@ var DEV_COMMANDS = {
727
733
  reference: ["", "developer", ["none"]]
728
734
  },
729
735
  format: {
730
- // prettier first here, unlike `format:edited`, and deliberately: `eslint --fix` exits 1
731
- // whenever a non-autofixable error remains, so putting it first behind `&&` would mean one
732
- // unused variable anywhere in the tree stops prettier from running at all.
733
- shell: [
734
- "sh",
735
- "-c",
736
- `pnpm exec prettier --write . && pnpm exec eslint . --fix ${ESLINT_CACHE_FLAGS.join(" ")}`
737
- ],
736
+ script: "scripts/lint/format.ts",
738
737
  description: "Format code with prettier and eslint",
739
738
  category: "Development",
740
739
  reference: ["", "developer", ["files", "processes"]]
@@ -1003,12 +1002,25 @@ var MAINTENANCE_COMMANDS = {
1003
1002
  };
1004
1003
 
1005
1004
  // scripts/josh/josh-commands-project.ts
1005
+ var PROFILE_ARGUMENTS = "[--profile static|node]";
1006
1006
  var PROJECT_COMMANDS = {
1007
+ profile: {
1008
+ script: "scripts/init/project-profile-cli.ts",
1009
+ description: "Show the project profile and the reason for it",
1010
+ category: "Project",
1011
+ reference: [PROFILE_ARGUMENTS, "developer", ["files"]]
1012
+ },
1013
+ start: {
1014
+ script: "scripts/init/start.ts",
1015
+ description: "Initialize a project for the GitHub Issue workflow",
1016
+ category: "Project",
1017
+ reference: [PROFILE_ARGUMENTS, "developer", ["files", "git", "network"]]
1018
+ },
1007
1019
  init: {
1008
1020
  script: "scripts/init/init.ts",
1009
1021
  description: "Initialize config in a new project",
1010
1022
  category: "Project",
1011
- reference: ["", "developer", ["files", "processes"]]
1023
+ reference: [PROFILE_ARGUMENTS, "developer", ["files", "processes"]]
1012
1024
  },
1013
1025
  sync: {
1014
1026
  script: "scripts/sync/sync.ts",
@@ -1242,11 +1254,14 @@ var ALIASES = {
1242
1254
  c: "check",
1243
1255
  pt: "port",
1244
1256
  i: "init",
1257
+ pf: "profile",
1258
+ st: "start",
1245
1259
  sy: "sync",
1246
1260
  rmi: "registry:migrate",
1247
1261
  sys: "sync:scope",
1248
1262
  g: "git",
1249
1263
  gp: "pr",
1264
+ prc: "pr:classification",
1250
1265
  fu: "followup",
1251
1266
  nf: "notify",
1252
1267
  obf: "observations:flush",
@@ -0,0 +1,82 @@
1
+ // scripts/build/local-bin.ts
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+
6
+ // scripts/lib/schemas.ts
7
+ import { z } from "zod";
8
+ var overrides_snapshot_schema = z.record(z.string(), z.string());
9
+ var package_version_schema = z.object({ version: z.string() });
10
+ var package_named_version_schema = z.looseObject({ name: z.string(), version: z.string() });
11
+ var package_with_version_schema = z.looseObject({ version: z.string() });
12
+ var ls_global_dependency_schema = z.looseObject({ version: z.string() });
13
+ var ls_global_dependencies_schema = z.record(z.string(), ls_global_dependency_schema).optional();
14
+ var pnpm_ls_global_schema = z.array(
15
+ z.looseObject({
16
+ dependencies: ls_global_dependencies_schema
17
+ })
18
+ );
19
+ var vscode_settings_schema = z.record(z.string(), z.unknown());
20
+ var with_scripts_schema = z.looseObject({
21
+ scripts: z.record(z.string(), z.string()).optional()
22
+ });
23
+ var with_development_deps_schema = z.looseObject({
24
+ devDependencies: z.record(z.string(), z.string()).optional()
25
+ });
26
+ var with_package_manager_schema = z.looseObject({
27
+ packageManager: z.string().optional()
28
+ });
29
+ var with_development_engine_schema = z.looseObject({
30
+ devEngines: z.record(z.string(), z.unknown()).optional()
31
+ });
32
+ var bin_entry_schema = z.union([z.string(), z.record(z.string(), z.string())]).optional();
33
+ var package_bin_schema = z.looseObject({ bin: bin_entry_schema });
34
+ var json_object_schema = z.record(z.string(), z.unknown());
35
+ var string_array_schema = z.array(z.string());
36
+ var string_record_schema = z.record(z.string(), z.string());
37
+
38
+ // scripts/build/local-bin.ts
39
+ var NODE_MODULES = "node_modules";
40
+ var BIN_DIR = ".bin";
41
+ var WINDOWS_PLATFORM = "win32";
42
+ var WINDOWS_SHIM_SUFFIX = ".cmd";
43
+ var PACKAGE_JSON = "package.json";
44
+ function resolve_local_bin(project_root, bin_name) {
45
+ const shim = process.platform === WINDOWS_PLATFORM ? `${bin_name}${WINDOWS_SHIM_SUFFIX}` : bin_name;
46
+ return path.join(project_root, NODE_MODULES, BIN_DIR, shim);
47
+ }
48
+ function ancestor_directories(start_directory) {
49
+ const directories = [];
50
+ let current = start_directory;
51
+ while (!directories.includes(current)) {
52
+ directories.push(current);
53
+ current = path.dirname(current);
54
+ }
55
+ return directories;
56
+ }
57
+ function find_local_bin_upwards(start_directory, bin_name) {
58
+ return ancestor_directories(start_directory).map((directory) => resolve_local_bin(directory, bin_name)).find((candidate) => existsSync(candidate));
59
+ }
60
+ function read_bin_entry(manifest_path, package_name, bin_name) {
61
+ const { bin } = package_bin_schema.parse(JSON.parse(readFileSync(manifest_path, "utf8")));
62
+ if (typeof bin === "string") return bin_name === path.basename(package_name) ? bin : void 0;
63
+ return bin?.[bin_name];
64
+ }
65
+ function resolve_package_bin(base_directory, package_name, bin_name) {
66
+ try {
67
+ const resolve_from = createRequire(path.join(base_directory, PACKAGE_JSON));
68
+ const manifest_path = resolve_from.resolve(`${package_name}/${PACKAGE_JSON}`);
69
+ const bin_entry = read_bin_entry(manifest_path, package_name, bin_name);
70
+ if (bin_entry === void 0) return void 0;
71
+ const cli_entry = path.join(path.dirname(manifest_path), bin_entry);
72
+ return existsSync(cli_entry) ? cli_entry : void 0;
73
+ } catch {
74
+ return void 0;
75
+ }
76
+ }
77
+
78
+ export {
79
+ resolve_local_bin,
80
+ find_local_bin_upwards,
81
+ resolve_package_bin
82
+ };
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  lane_child_marker
3
- } from "./chunk-FNFOLFFV.js";
3
+ } from "./chunk-SXPTRFPG.js";
4
4
  import {
5
5
  time_bundle_call,
6
6
  time_format,
7
7
  time_round_trips,
8
8
  time_spans,
9
9
  time_transcript_line
10
- } from "./chunk-264QY6KS.js";
10
+ } from "./chunk-2PGRQ62K.js";
11
11
 
12
12
  // scripts/lane/lane-guard-policy.ts
13
13
  var DEFAULT_MODE = "refuse";