@joshuafolkken/kit 1.1094.0 → 1.1136.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/agents/investigator.md +17 -0
- package/.claude/settings.json +1 -0
- package/.claude/skills/workflow-commands/SKILL.md +1 -1
- package/.claude/skills/workflow-commands/background-commands.md +36 -85
- package/.claude/skills/workflow-commands/backlogrun-progress.md +31 -34
- package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -2
- package/.claude/skills/workflow-commands/delegation.md +5 -1
- package/.claude/skills/workflow-commands/fullrun.md +5 -4
- package/.claude/skills/workflow-commands/halfrun.md +5 -4
- package/.claude/skills/workflow-commands/latest-gate.md +7 -15
- package/.claude/skills/workflow-commands/pre-gate-cut.md +4 -8
- package/.claude/skills/workflow-commands/split-assessment.md +4 -18
- package/.github/actions/setup-node/action.yml +33 -0
- package/.github/actions/setup-pnpm/action.yml +4 -4
- package/.github/workflows/auto-tag.yml +2 -3
- package/.github/workflows/ci.yml +7 -8
- package/.github/workflows/dependabot-auto-merge.yml +1 -0
- package/.github/workflows/pr-classification.yml +1 -3
- package/.github/workflows/production.yml +1 -0
- package/.github/workflows/publish.yml +10 -9
- package/.github/workflows/sonar-qube.yml +17 -13
- package/.pnpmfile.mjs +29 -0
- package/AGENTS.md +4 -11
- package/CLAUDE.md +24 -17
- package/GEMINI.md +4 -13
- package/README.md +8 -3
- package/SECURITY.md +2 -0
- package/cspell/index.yaml +4 -0
- package/dist/CLAUDE.basic.md +6 -6
- package/dist/CLAUDE.md +24 -17
- package/dist/CLAUDE.static.md +6 -6
- package/dist/config-merge/index.js +33 -9
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-OIZUHPJI.js → chunk-FO2I7UT7.js} +1 -1
- package/dist/hooks/{chunk-NKOG3XAV.js → chunk-GDU26K3J.js} +11 -3
- package/dist/hooks/{chunk-O4LL4HTI.js → chunk-HANSKHES.js} +2 -2
- package/dist/hooks/{chunk-3C4YCCEF.js → chunk-HBKZLITF.js} +4 -4
- package/dist/hooks/{chunk-UD24EAIL.js → chunk-JKKTVDAG.js} +1984 -146
- package/dist/hooks/{chunk-M2XDCUKD.js → chunk-KBXZLMRH.js} +89 -1803
- package/dist/hooks/{chunk-IL27YTPH.js → chunk-KOKOAV6D.js} +2 -2
- package/dist/hooks/{chunk-7ZPFAVR7.js → chunk-LJBLGHEH.js} +59 -19
- package/dist/hooks/{chunk-E54KGFEQ.js → chunk-NFZGFGOR.js} +1 -1
- package/dist/hooks/{chunk-KHWHKJOM.js → chunk-RFWWUI5F.js} +4 -4
- package/dist/hooks/codex-hook-adapter.js +12 -47
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/inputs.json +175 -173
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +9 -9
- package/dist/hooks/stop-guard.js +17 -12
- package/dist/josh.js +87 -31
- package/dist/version/index.js +36 -20
- package/eslint/rules/unicorn.js +3 -2
- package/package.json +23 -5
- package/pnpm-workspace.yaml +5 -7
- package/prompts/collaboration-workflow/glossary.md +17 -0
- package/prompts/collaboration-workflow/issue-template.md +1 -1
- package/prompts/collaboration-workflow/operating-rules.md +1 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow.md +1 -0
- package/scripts/backlog/backlog-arrival.ts +30 -10
- package/scripts/backlog/backlog-budget-cli.ts +15 -16
- package/scripts/backlog/backlog-offer-cli.ts +6 -14
- package/scripts/backlog/backlog-plan.ts +29 -18
- package/scripts/backlog/backlog-ready.ts +20 -7
- package/scripts/config-merge/parse-jsonc.ts +33 -3
- package/scripts/config-merge/patch-json-key.ts +13 -6
- package/scripts/cost-runtime/cost-cli.ts +3 -7
- package/scripts/delegation/delegation-policy.ts +9 -1
- package/scripts/delegation/investigation-guard.ts +1 -1
- package/scripts/delegation/investigation-reads.ts +105 -19
- package/scripts/doctor/doctor-consumer.ts +3 -5
- package/scripts/doctor/doctor-io.ts +9 -13
- package/scripts/document/backlogrun-parent-read-set.ts +12 -1
- package/scripts/document/capped-print.ts +74 -0
- package/scripts/document/document-byte-budget.ts +1 -0
- package/scripts/document/document-section-cli.ts +4 -3
- package/scripts/document/entry-read-budget.ts +13 -7
- package/scripts/document/entry-read-set.ts +32 -8
- package/scripts/document/lane-child-read-set.ts +7 -0
- package/scripts/document/read-set-point-of-use.ts +8 -3
- package/scripts/document/read-set-trim.ts +3 -1
- package/scripts/epic/epic-audit.ts +3 -18
- package/scripts/gate/scoped-green.ts +25 -14
- package/scripts/gh/git-gh-issue-list.ts +12 -11
- package/scripts/gh/git-gh-issue-write.ts +4 -1
- package/scripts/gh/git-gh-pr-auto-merge.ts +4 -1
- package/scripts/git/git-command.ts +6 -1
- package/scripts/git/git-spawn-sync.ts +65 -0
- package/scripts/git/git-spawn.ts +5 -4
- package/scripts/git/stash/stash-pop-args.ts +1 -6
- package/scripts/init/claude-kit-only-settings.ts +29 -0
- package/scripts/init/init-copy-content.ts +8 -2
- package/scripts/init/init-file-action.ts +54 -0
- package/scripts/init/init-logic-migrate.ts +35 -17
- package/scripts/init/init-logic-secretlint.ts +1 -1
- package/scripts/init/init-logic.ts +6 -22
- package/scripts/init/init.ts +11 -68
- package/scripts/init/initial-commit.ts +24 -0
- package/scripts/init/kit-written-paths.ts +2 -0
- package/scripts/init/project-profile.ts +3 -6
- package/scripts/init/start-exec.ts +16 -3
- package/scripts/init/start-steps.ts +10 -13
- package/scripts/issue/defect-rate-cli.ts +61 -20
- package/scripts/issue/issue-backlinks.ts +7 -14
- package/scripts/issue/issue-cite.ts +12 -6
- package/scripts/issue/issue-file.ts +5 -12
- package/scripts/issue/issue-read-cli.ts +1 -0
- package/scripts/issue/issue-report-failures.ts +6 -2
- package/scripts/issue/issue-state-cli.ts +1 -1
- package/scripts/issue/markdown-section.ts +21 -6
- package/scripts/issue/measure-rerun-cli.ts +73 -14
- package/scripts/josh/josh-commands-lane.ts +1 -1
- package/scripts/josh/josh-commands-project.ts +8 -1
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/run-github-prerequisite.ts +3 -6
- package/scripts/lane/lane-await.ts +89 -15
- package/scripts/lane/lane-cli.ts +11 -13
- package/scripts/lane/lane-close.ts +4 -1
- package/scripts/lane/lane-launch-cli.ts +10 -16
- package/scripts/lane/openai-review-broker.ts +4 -1
- package/scripts/lib/cli-flags.ts +31 -2
- package/scripts/lib/error-message.ts +12 -1
- package/scripts/lines/line-targets.ts +0 -11
- package/scripts/lines/lines-command.ts +5 -3
- package/scripts/propagate/propagate-git.ts +3 -8
- package/scripts/refactor/refactor-scan.ts +2 -1
- package/scripts/registry-migration/migrate-logic.ts +28 -19
- package/scripts/registry-migration/migrate.ts +6 -5
- package/scripts/release/github-release-cli.ts +14 -3
- package/scripts/release/github-release-environment.ts +1 -0
- package/scripts/release/github-release.ts +103 -17
- package/scripts/release/release-publish.ts +36 -2
- package/scripts/release/release-worktree.ts +97 -2
- package/scripts/review/review-checkout.ts +3 -2
- package/scripts/review/review-record-cli.ts +9 -16
- package/scripts/rules/early-heartbeat.ts +4 -3
- package/scripts/rules/lane-park.ts +9 -0
- package/scripts/rules/stop-rules.ts +7 -4
- package/scripts/run/run-carry-args.ts +8 -12
- package/scripts/run/run-carry-cli.ts +3 -1
- package/scripts/run/run-carry-conversation.ts +94 -0
- package/scripts/run/run-carry-reclaim.ts +37 -0
- package/scripts/run/run-carry.ts +21 -5
- package/scripts/run/run-cut-args.ts +1 -6
- package/scripts/run/run-ending-cli.ts +1 -5
- package/scripts/run/run-entry-cli.ts +40 -9
- package/scripts/run/run-entry-stop.ts +104 -0
- package/scripts/run/run-event-stream-emit.ts +7 -2
- package/scripts/run/run-hold.ts +4 -1
- package/scripts/run/run-liveness-cli.ts +1 -5
- package/scripts/run/run-merge-cli.ts +2 -10
- package/scripts/run/run-merge-steps.ts +4 -1
- package/scripts/run/run-parent-cut-hook.ts +4 -1
- package/scripts/run/run-prep-cli.ts +4 -1
- package/scripts/run/run-progress-args.ts +20 -4
- package/scripts/run/run-progress-cli.ts +31 -29
- package/scripts/run/run-progress-clock.ts +4 -1
- package/scripts/run/run-ship-cli.ts +8 -16
- package/scripts/run/run-ship-probe.ts +4 -1
- package/scripts/run/run-status-cli.ts +2 -15
- package/scripts/run/run-tidy-lanes.ts +4 -1
- package/scripts/run/run-wake-cli.ts +2 -10
- package/scripts/run/run-watcher-guard.ts +9 -5
- package/scripts/safe-chain/pack-hook.ts +92 -0
- package/scripts/sync/sync-ai-files.ts +4 -0
- package/scripts/test/test-declared-changed.ts +13 -16
- package/scripts/test/test-declared.ts +3 -7
- package/scripts/test/test-related.ts +1 -6
- package/scripts/test/test-unit-guard.ts +21 -7
- package/scripts/time-runtime/time-spans.ts +4 -0
- package/scripts/version/version-commands.ts +11 -1
- package/templates/CLAUDE.basic.md +6 -6
- package/templates/workflows/ci.yml +16 -82
- package/templates/workflows/dependabot-auto-merge.yml +1 -0
- package/templates/workflows/github-release.yml +14 -28
- package/tsconfig.sonar.json +4 -22
|
@@ -2,6 +2,7 @@ import { file_map_stamp, type FileMapStampAccess } from '#scripts/josh/file-map-
|
|
|
2
2
|
import { hook_decision } from '#scripts/josh/hook-decision'
|
|
3
3
|
import { review_brief } from '#scripts/review/review-brief'
|
|
4
4
|
import { review_stamps } from '#scripts/review/review-stamps'
|
|
5
|
+
import { test_unit_guard } from '#scripts/test/test-unit-guard'
|
|
5
6
|
import { gate_tree, type GateTree } from './gate-tree'
|
|
6
7
|
|
|
7
8
|
// Whether the two scoped checks have been green on *this exact tree* by the time the review is
|
|
@@ -60,26 +61,36 @@ interface ScopedCheck {
|
|
|
60
61
|
|
|
61
62
|
// The record paths, overridable so a test can plant one without overwriting the record a real run in
|
|
62
63
|
// this checkout relies on — the same reason `gate_skip.reusable_green_gate` takes a `source`.
|
|
64
|
+
// `is_unit_skipped` is overridable for the same reason, so a suite running in a checkout with vitest
|
|
65
|
+
// can stand in for a project without one.
|
|
63
66
|
interface ScopedSources {
|
|
64
67
|
lint?: string
|
|
65
68
|
test?: string
|
|
69
|
+
is_unit_skipped?: boolean
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
// **`test:related` is required only where it can ever answer** (joshuafolkken/kit#3136). In a project
|
|
73
|
+
// whose unit guard skips — vitest absent, or a basic project with no test file — `test:related` runs
|
|
74
|
+
// nothing and so writes no record, and requiring one refused the gate forever with no way through.
|
|
75
|
+
// The guard's own decision is asked rather than re-derived, so the writer and this reader agree; a
|
|
76
|
+
// project with a unit suite still needs the record, and a run that executed nothing still writes none.
|
|
68
77
|
function checks_of(sources: ScopedSources): ReadonlyArray<ScopedCheck> {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
const lint = {
|
|
79
|
+
script: LINT_SCRIPT,
|
|
80
|
+
command: LINT_COMMAND,
|
|
81
|
+
stamp: review_stamps.lint_related_stamp,
|
|
82
|
+
source: sources.lint,
|
|
83
|
+
}
|
|
84
|
+
const test = {
|
|
85
|
+
script: TEST_SCRIPT,
|
|
86
|
+
command: TEST_COMMAND,
|
|
87
|
+
stamp: review_stamps.test_related_stamp,
|
|
88
|
+
source: sources.test,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return (sources.is_unit_skipped ?? test_unit_guard.is_skipping(process.cwd()))
|
|
92
|
+
? [lint]
|
|
93
|
+
: [lint, test]
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
// **An unreadable record reads as an absent one, and that is the safe direction.** `read` swallows a
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bounded_pool } from '#scripts/lib/bounded-pool'
|
|
1
2
|
import { git_gh_api_path } from './git-gh-api-path'
|
|
2
3
|
import { git_gh_exec } from './git-gh-exec'
|
|
3
4
|
import { read_blocked_by } from './git-gh-issue-read'
|
|
@@ -198,21 +199,21 @@ async function fetch_selected(request: IssueListRequest): Promise<SelectedRows>
|
|
|
198
199
|
// issue GitHub itself reports as unblocked costs no request. A backlog with no declared blockers
|
|
199
200
|
// therefore costs exactly what `gh` cost — one listing — rather than one request per row.
|
|
200
201
|
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
202
|
+
// Bounded rather than sequential (joshuafolkken/kit#3103): the pass was paced one request at a time,
|
|
203
|
+
// which made every `backlog:plan` wait about 0.7 s per opted-in issue declaring a blocker. The width
|
|
204
|
+
// is the one `epic:bundle` already reads the same endpoint at, so the burst stays bounded while a
|
|
205
|
+
// rate-limited read still fails the whole listing rather than a row (`bounded-pool.ts`).
|
|
206
|
+
const RELATION_CONCURRENCY = 8
|
|
207
|
+
|
|
204
208
|
async function read_relations(
|
|
205
209
|
rows: ReadonlyArray<RestIssue>,
|
|
206
210
|
repo?: string,
|
|
207
211
|
): Promise<Array<BlockedBy>> {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return relations
|
|
212
|
+
return await bounded_pool.bounded_map(
|
|
213
|
+
rows,
|
|
214
|
+
RELATION_CONCURRENCY,
|
|
215
|
+
async (row) => await read_blocked_by(String(row.number), row, repo),
|
|
216
|
+
)
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
// The listing, plus what it could not cover. `json` is `undefined` — never `'[]'` — when the listing
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
1
2
|
import { git_gh_api_path } from './git-gh-api-path'
|
|
2
3
|
import { git_gh_exec, type GhApiRequest } from './git-gh-exec'
|
|
3
4
|
|
|
@@ -43,7 +44,9 @@ async function did_write_succeed(write: () => Promise<unknown>): Promise<boolean
|
|
|
43
44
|
await write()
|
|
44
45
|
|
|
45
46
|
return true
|
|
46
|
-
} catch {
|
|
47
|
+
} catch (error) {
|
|
48
|
+
error_text.trace_swallowed('git_gh_issue_write.did_write_succeed', error)
|
|
49
|
+
|
|
47
50
|
return false
|
|
48
51
|
}
|
|
49
52
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
1
2
|
import { git_gh_api_path } from './git-gh-api-path'
|
|
2
3
|
import { git_gh_exec } from './git-gh-exec'
|
|
3
4
|
import { read_pull, require_pr_number } from './git-gh-pr-read'
|
|
@@ -40,7 +41,9 @@ async function pr_enable_auto_merge(branch_name: string): Promise<void> {
|
|
|
40
41
|
async function pr_is_merged(branch_name: string): Promise<boolean> {
|
|
41
42
|
try {
|
|
42
43
|
return git_gh_pr_rest.is_merged(await read_pull(await require_pr_number(branch_name)))
|
|
43
|
-
} catch {
|
|
44
|
+
} catch (error) {
|
|
45
|
+
error_text.trace_swallowed('git_gh_pr_auto_merge.pr_is_merged', error)
|
|
46
|
+
|
|
44
47
|
return false
|
|
45
48
|
}
|
|
46
49
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { PORCELAIN_FLAG, UNTRACKED_FILES_FLAG } from './constants'
|
|
2
|
+
import { create_spawn_error } from './git-execa-error'
|
|
2
3
|
import { git_push_transport } from './git-push-transport'
|
|
3
4
|
import { git_spawn } from './git-spawn'
|
|
5
|
+
import { git_spawn_sync } from './git-spawn-sync'
|
|
4
6
|
|
|
5
7
|
async function branch(): Promise<string> {
|
|
6
8
|
return await git_spawn.read(['rev-parse', '--abbrev-ref', 'HEAD'])
|
|
@@ -31,7 +33,10 @@ async function status(directory?: string): Promise<string> {
|
|
|
31
33
|
// onto the working directory resolves to nothing, and a digest map built that way collapses to
|
|
32
34
|
// "every file absent" — which compares *equal* to another such map (joshuafolkken/kit#1241).
|
|
33
35
|
async function repository_root(): Promise<string> {
|
|
34
|
-
|
|
36
|
+
const result = git_spawn_sync.toplevel()
|
|
37
|
+
if (result.exit_code !== 0) throw create_spawn_error('rev-parse', result.exit_code)
|
|
38
|
+
|
|
39
|
+
return result.stdout.trim()
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
// The commit this checkout is sitting on, as opposed to `change_base_commit`'s commit a change is
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { read_spawn_stderr, read_spawn_stdout } from '#scripts/lib/spawn-exit'
|
|
2
|
+
import { execaSync } from 'execa'
|
|
3
|
+
import { git_utilities } from './constants'
|
|
4
|
+
|
|
5
|
+
// The one place a synchronous git command is spawned from (joshuafolkken/kit#3065) — the sync
|
|
6
|
+
// counterpart of `git-spawn.ts`. Every caller here wants an answer rather than an exception, so a
|
|
7
|
+
// non-zero exit is returned as a result, and every call is bounded: a git that never answers is an
|
|
8
|
+
// unknown the caller already handles, never a hang.
|
|
9
|
+
|
|
10
|
+
const GIT_TIMEOUT_MS = 10_000
|
|
11
|
+
const SUCCESS_EXIT_CODE = 0
|
|
12
|
+
// git translates its messages, so a caller matching stderr would fail under any non-English locale.
|
|
13
|
+
const C_LOCALE: Record<string, string> = { LC_ALL: 'C', LANGUAGE: 'C' }
|
|
14
|
+
|
|
15
|
+
interface GitSyncOptions {
|
|
16
|
+
cwd?: string
|
|
17
|
+
timeout?: number
|
|
18
|
+
env?: Record<string, string | undefined>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface GitSyncResult {
|
|
22
|
+
exit_code: number | undefined
|
|
23
|
+
stdout: string
|
|
24
|
+
stderr: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function run(args: ReadonlyArray<string>, options: GitSyncOptions = {}): GitSyncResult {
|
|
28
|
+
// execa runs the binary directly with an argument array and no `shell` option; the git command and
|
|
29
|
+
// args are internally controlled, never untrusted input. tssecurity:S8705 is a false positive here.
|
|
30
|
+
const result = execaSync(git_utilities.get_git_command_for_spawn(), args, {
|
|
31
|
+
...options,
|
|
32
|
+
extendEnv: true,
|
|
33
|
+
reject: false,
|
|
34
|
+
timeout: options.timeout ?? GIT_TIMEOUT_MS,
|
|
35
|
+
}) // NOSONAR
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
exit_code: result.exitCode,
|
|
39
|
+
stdout: read_spawn_stdout(result),
|
|
40
|
+
stderr: read_spawn_stderr(result),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// The trimmed output of a command that succeeded, or undefined for any failure.
|
|
45
|
+
function read(args: ReadonlyArray<string>, options: GitSyncOptions = {}): string | undefined {
|
|
46
|
+
const result = run(args, options)
|
|
47
|
+
|
|
48
|
+
return result.exit_code === SUCCESS_EXIT_CODE ? result.stdout.trim() : undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// The configured `origin` URL, or undefined when the repository has none.
|
|
52
|
+
function origin_url(cwd: string): string | undefined {
|
|
53
|
+
return read(['config', '--get', 'remote.origin.url'], { cwd })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The absolute repository (or worktree) root, asked under the C locale so a caller can tell "not a
|
|
57
|
+
// repository" apart from every other failure by its stderr.
|
|
58
|
+
function toplevel(timeout: number = GIT_TIMEOUT_MS): GitSyncResult {
|
|
59
|
+
return run(['rev-parse', '--show-toplevel'], { env: C_LOCALE, timeout })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const git_spawn_sync = { GIT_TIMEOUT_MS, C_LOCALE, run, read, origin_url, toplevel }
|
|
63
|
+
|
|
64
|
+
export { git_spawn_sync }
|
|
65
|
+
export type { GitSyncResult }
|
package/scripts/git/git-spawn.ts
CHANGED
|
@@ -18,10 +18,11 @@ import { git_ssh_keepalive } from './git-ssh-keepalive'
|
|
|
18
18
|
// **`git-push-transport.ts` is the one deliberate exception among the command modules** and is not a
|
|
19
19
|
// second helper of this kind: a push needs a transport-fault retry that no other command has, so it
|
|
20
20
|
// spawns git itself. The timeout it carries is shared here by `read_remote` / `with_output_remote`. Those two files are the spawn sites of the ordinary command
|
|
21
|
-
// modules — the claim stops there.
|
|
22
|
-
// (
|
|
23
|
-
//
|
|
24
|
-
// binary is resolved has to read
|
|
21
|
+
// modules — the claim stops there. A synchronous caller goes through `git-spawn-sync.ts`, the same
|
|
22
|
+
// binary resolution with a result instead of an exception (joshuafolkken/kit#3065). Other parts of
|
|
23
|
+
// this package spawn git for their own purposes (`scripts/run/run-progress-clock.ts`,
|
|
24
|
+
// `scripts/git/git-fixture-workspace.ts`), and an audit of how the git binary is resolved has to read
|
|
25
|
+
// those too.
|
|
25
26
|
|
|
26
27
|
async function read(arguments_: Array<string>): Promise<string> {
|
|
27
28
|
const git_cmd = git_utilities.get_git_command_for_spawn()
|
|
@@ -21,12 +21,7 @@ interface Request {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
24
|
-
return cli_flags.
|
|
25
|
-
args: [...argv],
|
|
26
|
-
options: OPTIONS,
|
|
27
|
-
strict: true,
|
|
28
|
-
allowPositionals: true,
|
|
29
|
-
})
|
|
24
|
+
return cli_flags.arguments_of(argv, OPTIONS)
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
// The message is the sole positional and must be a non-empty string: an empty message matches every
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { patch_json_key } from '#scripts/config-merge/patch-json-key'
|
|
2
|
+
import { claude_plugin_config } from './claude-plugin-config'
|
|
3
|
+
|
|
4
|
+
// Keys kit's own `.claude/settings.json` sets for its sessions but never ships. `enableArtifact:
|
|
5
|
+
// false` keeps the Artifact tool's ~12k tokens out of every call of a kit run
|
|
6
|
+
// (joshuafolkken/kit#3140), but Claude Code lets any layer that sets it to `false` win, so a
|
|
7
|
+
// consumer given it could not turn the tool back on from `.claude/settings.local.json`.
|
|
8
|
+
const KIT_ONLY_SETTING_KEYS: ReadonlyArray<string> = ['enableArtifact']
|
|
9
|
+
|
|
10
|
+
function strip_kit_only_settings(content: string): string {
|
|
11
|
+
let stripped = content
|
|
12
|
+
for (const key of KIT_ONLY_SETTING_KEYS) stripped = patch_json_key.remove_json_key(stripped, key)
|
|
13
|
+
|
|
14
|
+
return stripped
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function apply_kit_only_strip_for_destination(destination_path: string, content: string): string {
|
|
18
|
+
return destination_path.endsWith(claude_plugin_config.CLAUDE_SETTINGS_DESTINATION)
|
|
19
|
+
? strip_kit_only_settings(content)
|
|
20
|
+
: content
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const claude_kit_only_settings = {
|
|
24
|
+
KIT_ONLY_SETTING_KEYS,
|
|
25
|
+
strip_kit_only_settings,
|
|
26
|
+
apply_kit_only_strip_for_destination,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { claude_kit_only_settings }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { managed_marker_logic } from '#scripts/managed-marker/managed-marker-logic'
|
|
2
2
|
import { workflow_pin_logic } from '#scripts/sync/workflow-pin-logic'
|
|
3
3
|
import { KIT_PACKAGE_NAME } from '#scripts/version/kit-descriptor'
|
|
4
|
+
import { claude_kit_only_settings } from './claude-kit-only-settings'
|
|
4
5
|
import { claude_plugin_config } from './claude-plugin-config'
|
|
5
6
|
import { hook_command_rewrite } from './hook-command-rewrite'
|
|
6
7
|
import { init_logic } from './init-logic'
|
|
@@ -19,7 +20,8 @@ import { init_logic } from './init-logic'
|
|
|
19
20
|
* workflow-pin-logic.ts for why the template refs are not authoritative, and
|
|
20
21
|
* managed-marker-logic.ts for why the stamp is on the file rather than in a list.
|
|
21
22
|
*
|
|
22
|
-
* The consumer's `.claude/settings.json` gets
|
|
23
|
+
* The consumer's `.claude/settings.json` gets three further passes. The keys kit sets only for its
|
|
24
|
+
* own sessions are removed (joshuafolkken/kit#3140, see claude-kit-only-settings.ts). The kit plugin's marketplace and
|
|
23
25
|
* `enabledPlugins` declaration is injected, because the skills that used to be copied now ship as the
|
|
24
26
|
* `kit` plugin and load from that declaration (joshuafolkken/kit#1879). And its hook commands are
|
|
25
27
|
* rewritten from `pnpm josh` to the published bundle invoked directly with node
|
|
@@ -36,10 +38,14 @@ function transform_copied_content(destination_path: string, content: string): st
|
|
|
36
38
|
with_pins,
|
|
37
39
|
KIT_PACKAGE_NAME,
|
|
38
40
|
)
|
|
39
|
-
const
|
|
41
|
+
const with_kit_only_stripped = claude_kit_only_settings.apply_kit_only_strip_for_destination(
|
|
40
42
|
destination_path,
|
|
41
43
|
with_marker,
|
|
42
44
|
)
|
|
45
|
+
const with_plugin = claude_plugin_config.apply_plugin_config_for_destination(
|
|
46
|
+
destination_path,
|
|
47
|
+
with_kit_only_stripped,
|
|
48
|
+
)
|
|
43
49
|
|
|
44
50
|
return hook_command_rewrite.apply_hook_command_rewrite_for_destination(
|
|
45
51
|
destination_path,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import type { FileAction } from './init-actions'
|
|
4
|
+
|
|
5
|
+
const SAMPLE_INDENT_WIDTH = 4
|
|
6
|
+
const SAMPLE_INDENT = ' '.repeat(SAMPLE_INDENT_WIDTH)
|
|
7
|
+
|
|
8
|
+
function write_new_file(action: FileAction, destination_path: string): void {
|
|
9
|
+
mkdirSync(path.dirname(destination_path), { recursive: true })
|
|
10
|
+
writeFileSync(destination_path, action.create())
|
|
11
|
+
console.info(` ✔ created ${action.dest}`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function show_sample(action: FileAction): void {
|
|
15
|
+
console.info(` ⚠ exists ${action.dest} — add manually:`)
|
|
16
|
+
console.info('')
|
|
17
|
+
console.info(action.create().replaceAll(/^/gmu, () => SAMPLE_INDENT))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// A file that already holds the sample needs nothing added, so only one that differs gets it shown —
|
|
21
|
+
// for a create-only file (no merge) as for a merge that only migrates a kit-written line (#3069).
|
|
22
|
+
function report_unchanged(action: FileAction, existing: string): void {
|
|
23
|
+
const is_sample_missing = existing !== action.create()
|
|
24
|
+
const should_show_sample =
|
|
25
|
+
action.merge === undefined || action.should_show_sample_when_unchanged === true
|
|
26
|
+
|
|
27
|
+
if (is_sample_missing && should_show_sample) show_sample(action)
|
|
28
|
+
else console.info(` ✔ unchanged ${action.dest}`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function merge_existing_file(action: FileAction, destination_path: string): void {
|
|
32
|
+
const existing = readFileSync(destination_path, 'utf8')
|
|
33
|
+
const merged = action.merge === undefined ? existing : action.merge(existing)
|
|
34
|
+
|
|
35
|
+
if (merged === existing) {
|
|
36
|
+
report_unchanged(action, existing)
|
|
37
|
+
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
writeFileSync(destination_path, merged)
|
|
42
|
+
console.info(` ✔ updated ${action.dest}`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function execute_file_action(action: FileAction, project_root: string): void {
|
|
46
|
+
const destination_path = path.join(project_root, action.dest)
|
|
47
|
+
|
|
48
|
+
if (existsSync(destination_path)) merge_existing_file(action, destination_path)
|
|
49
|
+
else write_new_file(action, destination_path)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const init_file_action = { execute_file_action }
|
|
53
|
+
|
|
54
|
+
export { init_file_action }
|
|
@@ -4,6 +4,27 @@ const PREINSTALL_KEY = 'preinstall'
|
|
|
4
4
|
const JF_PREFIX = 'jf-'
|
|
5
5
|
const JOSH_PREFIX = 'josh '
|
|
6
6
|
|
|
7
|
+
// The plain values kit wrote before its scripts became josh subcommands (#80). `check` is left out:
|
|
8
|
+
// its value is the one `sv create` writes itself, so it cannot be told apart from the project's own.
|
|
9
|
+
const PRE_JOSH_SCRIPT_VALUES: ReadonlyMap<string, string> = new Map([
|
|
10
|
+
['lint', 'pnpm lint:prettier && pnpm lint:eslint'],
|
|
11
|
+
['lint:prettier', 'prettier --check .'],
|
|
12
|
+
['lint:eslint', 'eslint . --cache --cache-strategy content'],
|
|
13
|
+
['format', 'pnpm format:prettier && pnpm format:eslint'],
|
|
14
|
+
['format:prettier', 'prettier --write .'],
|
|
15
|
+
['format:eslint', 'eslint . --fix --cache --cache-strategy content'],
|
|
16
|
+
['cspell', 'cspell lint --no-must-find-files --no-progress "**/*.{ts,js,md,yaml,yml,json}"'],
|
|
17
|
+
['cspell:dot', 'pnpm cspell . --dot'],
|
|
18
|
+
['test:unit', 'vitest run'],
|
|
19
|
+
['lefthook:install', 'lefthook install'],
|
|
20
|
+
['lefthook:uninstall', 'lefthook uninstall'],
|
|
21
|
+
['lefthook:commit', 'lefthook run pre-commit'],
|
|
22
|
+
['lefthook:push', 'lefthook run pre-push'],
|
|
23
|
+
['main:sync', 'git checkout main && git pull'],
|
|
24
|
+
['main:merge', 'git pull origin main'],
|
|
25
|
+
['check:ci', 'svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --threshold error'],
|
|
26
|
+
])
|
|
27
|
+
|
|
7
28
|
const RETIRED_MANAGED_SCRIPTS = new Set<string>([
|
|
8
29
|
'git',
|
|
9
30
|
'git:followup',
|
|
@@ -18,30 +39,15 @@ const RETIRED_MANAGED_SCRIPTS = new Set<string>([
|
|
|
18
39
|
'version:patch',
|
|
19
40
|
'version:current',
|
|
20
41
|
'overrides:check',
|
|
21
|
-
'lint',
|
|
22
|
-
'lint:prettier',
|
|
23
|
-
'lint:eslint',
|
|
24
|
-
'format',
|
|
25
|
-
'format:prettier',
|
|
26
|
-
'format:eslint',
|
|
27
|
-
'cspell',
|
|
28
|
-
'cspell:dot',
|
|
29
|
-
'test:unit',
|
|
30
42
|
'test:e2e',
|
|
31
43
|
'test',
|
|
32
|
-
'lefthook:install',
|
|
33
|
-
'lefthook:uninstall',
|
|
34
|
-
'lefthook:commit',
|
|
35
|
-
'lefthook:push',
|
|
36
|
-
'main:sync',
|
|
37
|
-
'main:merge',
|
|
38
44
|
'latest',
|
|
39
45
|
'latest:corepack',
|
|
40
46
|
'latest:update',
|
|
41
47
|
'check',
|
|
42
|
-
'check:ci',
|
|
43
48
|
'check:svelte',
|
|
44
49
|
'check:svelte:ci',
|
|
50
|
+
...PRE_JOSH_SCRIPT_VALUES.keys(),
|
|
45
51
|
])
|
|
46
52
|
|
|
47
53
|
function migrate_jf_value(value: string): string {
|
|
@@ -62,9 +68,21 @@ function apply_jf_migrations(scripts: Record<string, string>): Record<string, st
|
|
|
62
68
|
)
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
function is_kit_written(key: string, value: string): boolean {
|
|
72
|
+
if (value.startsWith(JOSH_PREFIX) || value.startsWith(JF_PREFIX)) return true
|
|
73
|
+
|
|
74
|
+
return PRE_JOSH_SCRIPT_VALUES.get(key) === value
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// A retired name alone does not make a script kit's: `sv create` writes its own `check`, which must
|
|
78
|
+
// survive `josh init` (joshuafolkken/kit#3069). Only a value kit itself wrote is retired.
|
|
79
|
+
function is_retired_script(key: string, value: string): boolean {
|
|
80
|
+
return RETIRED_MANAGED_SCRIPTS.has(key) && is_kit_written(key, value)
|
|
81
|
+
}
|
|
82
|
+
|
|
65
83
|
function remove_retired_scripts(scripts: Record<string, string>): Record<string, string> {
|
|
66
84
|
return Object.fromEntries(
|
|
67
|
-
Object.entries(scripts).filter(([key]) => !
|
|
85
|
+
Object.entries(scripts).filter(([key, value]) => !is_retired_script(key, value)),
|
|
68
86
|
)
|
|
69
87
|
}
|
|
70
88
|
|
|
@@ -5,7 +5,7 @@ const SECRETLINT_RULE_PRESET = '@secretlint/secretlint-rule-preset-recommend'
|
|
|
5
5
|
|
|
6
6
|
// secretlint resolves both its CLI and every rule package from the project it runs in, not
|
|
7
7
|
// transitively through the kit — the same constraint that forces the prettier plugins into
|
|
8
|
-
// consumer devDependencies (
|
|
8
|
+
// consumer devDependencies (as kit peers, see init.ts). Omitting either
|
|
9
9
|
// entry makes the pre-commit hook fail with "Cannot find module". Versions mirror the kit's
|
|
10
10
|
// own devDependencies.
|
|
11
11
|
const SECRETLINT_DEV_DEPS: Record<string, string> = {
|
|
@@ -92,10 +92,13 @@ const AI_COPY_FILES: ReadonlyArray<string> = [
|
|
|
92
92
|
'.github/workflows/production.yml',
|
|
93
93
|
'.github/workflows/sonar-qube.yml',
|
|
94
94
|
'.github/workflows/pr-classification.yml',
|
|
95
|
-
// Called by `pr-classification.yml` as `uses: ./.github/actions/setup-pnpm`; without
|
|
96
|
-
// consumer's
|
|
97
|
-
//
|
|
95
|
+
// Called by `ci.yml` and `pr-classification.yml` as `uses: ./.github/actions/setup-pnpm`; without
|
|
96
|
+
// it the consumer's jobs cannot resolve the action and their checks fail (joshuafolkken/kit#3013,
|
|
97
|
+
// joshuafolkken/kit#3095).
|
|
98
98
|
'.github/actions/setup-pnpm/action.yml',
|
|
99
|
+
// The one place the workflows' Node.js version is written, called by every distributed workflow
|
|
100
|
+
// that sets Node.js up (joshuafolkken/kit#3084).
|
|
101
|
+
'.github/actions/setup-node/action.yml',
|
|
99
102
|
'.github/pull_request_template.md',
|
|
100
103
|
'.github/release.yml',
|
|
101
104
|
'.github/dependabot.yml',
|
|
@@ -220,20 +223,6 @@ const SUGGESTED_SCRIPTS_COMMON: Record<string, string> = {
|
|
|
220
223
|
josh: 'josh',
|
|
221
224
|
}
|
|
222
225
|
|
|
223
|
-
// prettier resolves its `plugins[]` from the consumer project, not transitively through the kit,
|
|
224
|
-
// so every package that uses the kit prettier preset must declare these as devDependencies. The
|
|
225
|
-
// preset references all three unconditionally (prettier/index.js → plugins), hence all three are
|
|
226
|
-
// added regardless of project — omitting any breaks `prettier`/`josh lint` with a
|
|
227
|
-
// "Cannot find package" error. Versions mirror the kit's own devDependencies.
|
|
228
|
-
const SORT_IMPORTS_PLUGIN_KEY = '@ianvs/prettier-plugin-sort-imports'
|
|
229
|
-
const PRETTIER_SVELTE_PLUGIN_KEY = 'prettier-plugin-svelte'
|
|
230
|
-
const PRETTIER_TAILWIND_PLUGIN_KEY = 'prettier-plugin-tailwindcss'
|
|
231
|
-
const PRETTIER_PLUGIN_DEV_DEPS: Record<string, string> = {
|
|
232
|
-
[SORT_IMPORTS_PLUGIN_KEY]: '^4.7.1',
|
|
233
|
-
[PRETTIER_SVELTE_PLUGIN_KEY]: '^4.1.1',
|
|
234
|
-
[PRETTIER_TAILWIND_PLUGIN_KEY]: '^0.8.0',
|
|
235
|
-
}
|
|
236
|
-
|
|
237
226
|
// format_json rather than JSON.stringify: it lays arrays out the way prettier does — inline while
|
|
238
227
|
// they fit within printWidth, one element per line once they do not — so the file kit writes is
|
|
239
228
|
// `prettier --check`-clean in the consumer whatever the entry count happens to be (#660).
|
|
@@ -433,10 +422,6 @@ function strip_managed_postinstall(content: string): string {
|
|
|
433
422
|
)
|
|
434
423
|
}
|
|
435
424
|
|
|
436
|
-
function merge_prettier_plugin_development_deps(content: string): string {
|
|
437
|
-
return init_logic_json_merge.merge_development_dependencies(content, PRETTIER_PLUGIN_DEV_DEPS)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
425
|
const init_logic = {
|
|
441
426
|
...init_logic_templates,
|
|
442
427
|
...init_logic_workspace,
|
|
@@ -454,7 +439,6 @@ const init_logic = {
|
|
|
454
439
|
generate_npmrc,
|
|
455
440
|
merge_npmrc,
|
|
456
441
|
merge_gitignore,
|
|
457
|
-
merge_prettier_plugin_development_deps,
|
|
458
442
|
get_tsconfig_extends_entry,
|
|
459
443
|
get_tsconfig_preset_filename,
|
|
460
444
|
get_lefthook_extends_value,
|
package/scripts/init/init.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
-
import { existsSync,
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { resolve_local_bin } from '#scripts/build/local-bin'
|
|
@@ -7,6 +7,7 @@ import { doctor_io } from '#scripts/doctor/doctor-io'
|
|
|
7
7
|
import { error_text } from '#scripts/lib/error-message'
|
|
8
8
|
import { package_version_schema, with_package_manager_schema } from '#scripts/lib/schemas'
|
|
9
9
|
import { auto_merge_setting } from '#scripts/repo/auto-merge-setting'
|
|
10
|
+
import { pack_hook } from '#scripts/safe-chain/pack-hook'
|
|
10
11
|
import { project_config } from '#scripts/safe-chain/project-config'
|
|
11
12
|
import { security_updates } from '#scripts/security/security-updates'
|
|
12
13
|
import { did_refuse_self_run } from '#scripts/self-sync-guard/self-sync-refusal'
|
|
@@ -14,10 +15,11 @@ import { sync } from '#scripts/sync/sync'
|
|
|
14
15
|
import { package_manager_version } from '#scripts/version/package-manager-version'
|
|
15
16
|
import { execaSync } from 'execa'
|
|
16
17
|
import { z } from 'zod'
|
|
17
|
-
import { init_actions, PRETTIER_CONFIG_JS
|
|
18
|
+
import { init_actions, PRETTIER_CONFIG_JS } from './init-actions'
|
|
18
19
|
import { init_ai_copy } from './init-ai-copy'
|
|
19
20
|
import { init_basic } from './init-basic'
|
|
20
21
|
import { init_bootstrap } from './init-bootstrap'
|
|
22
|
+
import { init_file_action } from './init-file-action'
|
|
21
23
|
import { init_install } from './init-install'
|
|
22
24
|
import { init_logic } from './init-logic'
|
|
23
25
|
import { PACKAGE_DIR, PROJECT_ROOT } from './init-paths'
|
|
@@ -27,69 +29,10 @@ import { project_profile, type ProjectProfile, type ProjectShape } from './proje
|
|
|
27
29
|
const PACKAGE_JSON = 'package.json'
|
|
28
30
|
const KIT_PACKAGE_NAME = '@joshuafolkken/kit'
|
|
29
31
|
const LEFTHOOK_BIN = 'lefthook'
|
|
30
|
-
const SAMPLE_INDENT_WIDTH = 4
|
|
31
32
|
const ARGUMENT_START_INDEX = 2
|
|
32
|
-
const SAMPLE_INDENT = ' '.repeat(SAMPLE_INDENT_WIDTH)
|
|
33
33
|
const INSTALL_HINT = '→ run `pnpm install` to install what package.json lists'
|
|
34
34
|
const INIT_USAGE = 'josh init [--profile basic|full] [--no-install]'
|
|
35
35
|
|
|
36
|
-
function write_new_file(action: FileAction, destination_path: string): void {
|
|
37
|
-
mkdirSync(path.dirname(destination_path), { recursive: true })
|
|
38
|
-
writeFileSync(destination_path, action.create())
|
|
39
|
-
console.info(` ✔ created ${action.dest}`)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function show_sample(action: FileAction): void {
|
|
43
|
-
console.info(` ⚠ exists ${action.dest} — add manually:`)
|
|
44
|
-
console.info('')
|
|
45
|
-
console.info(action.create().replaceAll(/^/gmu, () => SAMPLE_INDENT))
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// A file that already holds the sample needs nothing added, so only one that differs gets it shown.
|
|
49
|
-
function report_unchanged(action: FileAction, existing: string): void {
|
|
50
|
-
const is_sample_missing = existing !== action.create()
|
|
51
|
-
|
|
52
|
-
if (is_sample_missing && action.should_show_sample_when_unchanged === true) show_sample(action)
|
|
53
|
-
else console.info(` ✔ unchanged ${action.dest}`)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function merge_existing_file(
|
|
57
|
-
merge_function: (existing: string) => string,
|
|
58
|
-
destination_path: string,
|
|
59
|
-
action: FileAction,
|
|
60
|
-
): void {
|
|
61
|
-
const existing = readFileSync(destination_path, 'utf8')
|
|
62
|
-
const merged = merge_function(existing)
|
|
63
|
-
|
|
64
|
-
if (merged === existing) {
|
|
65
|
-
report_unchanged(action, existing)
|
|
66
|
-
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
writeFileSync(destination_path, merged)
|
|
71
|
-
console.info(` ✔ updated ${action.dest}`)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function execute_file_action(action: FileAction): void {
|
|
75
|
-
const destination_path = path.join(PROJECT_ROOT, action.dest)
|
|
76
|
-
const is_existing = existsSync(destination_path)
|
|
77
|
-
|
|
78
|
-
if (!is_existing) {
|
|
79
|
-
write_new_file(action, destination_path)
|
|
80
|
-
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (action.merge === undefined) {
|
|
85
|
-
show_sample(action)
|
|
86
|
-
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
merge_existing_file(action.merge, destination_path, action)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
36
|
const engine_pin_schema = z.looseObject({ name: z.string(), version: z.string() })
|
|
94
37
|
|
|
95
38
|
const published_development_engines_schema = z.looseObject({
|
|
@@ -124,10 +67,10 @@ function get_kit_self_dependency(): Record<string, string> {
|
|
|
124
67
|
}
|
|
125
68
|
|
|
126
69
|
// Each config `init` generates runs a CLI or imports types the consumer has to resolve itself:
|
|
127
|
-
// `
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
const TOOL_DEVELOPMENT_DEPENDENCIES = ['
|
|
70
|
+
// `cspell.config.yaml` → cspell, `playwright.config.ts` → @types/node, `lefthook.yml` → lefthook.
|
|
71
|
+
// Prettier, its preset plugins and @playwright/test come in as peers. Without them the first
|
|
72
|
+
// `josh gate` after `pnpm install` fails (joshuafolkken/kit#2710).
|
|
73
|
+
const TOOL_DEVELOPMENT_DEPENDENCIES = ['cspell', '@types/node']
|
|
131
74
|
const LEFTHOOK_DEVELOPMENT_DEPENDENCY = 'lefthook'
|
|
132
75
|
|
|
133
76
|
function tool_dependency_names(has_git: boolean): ReadonlyArray<string> {
|
|
@@ -164,9 +107,8 @@ function apply_dependency_merges(content: string, has_git = true): string {
|
|
|
164
107
|
? suggested
|
|
165
108
|
: Object.fromEntries(Object.entries(suggested).filter(([key]) => key !== 'prepare')),
|
|
166
109
|
)
|
|
167
|
-
const with_prettier = init_logic.merge_prettier_plugin_development_deps(merged)
|
|
168
110
|
const with_toolchain = init_logic.merge_development_dependencies(
|
|
169
|
-
|
|
111
|
+
merged,
|
|
170
112
|
get_toolchain_development_dependencies(has_git),
|
|
171
113
|
)
|
|
172
114
|
const with_secretlint = init_logic.merge_secretlint_development_deps(with_toolchain)
|
|
@@ -262,7 +204,7 @@ function run_config_file_actions(shape: ProjectShape): void {
|
|
|
262
204
|
}
|
|
263
205
|
|
|
264
206
|
for (const action of init_actions.build_file_actions(shape)) {
|
|
265
|
-
execute_file_action(action)
|
|
207
|
+
init_file_action.execute_file_action(action, PROJECT_ROOT)
|
|
266
208
|
}
|
|
267
209
|
}
|
|
268
210
|
|
|
@@ -305,6 +247,7 @@ function initialize_project(shape: ProjectShape): void {
|
|
|
305
247
|
merge_project_package_json(shape)
|
|
306
248
|
|
|
307
249
|
run_ai_file_actions(shape)
|
|
250
|
+
pack_hook.sync_pack_hook(PROJECT_ROOT, PACKAGE_DIR)
|
|
308
251
|
if (shape.profile === 'full') project_config.sync_project_config(PROJECT_ROOT)
|
|
309
252
|
}
|
|
310
253
|
|