@joshuafolkken/kit 1.1005.0 → 1.1035.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/settings.json +1 -5
- package/.claude/skills/dependency-update/SKILL.md +6 -3
- package/.claude/skills/epic-commands/SKILL.md +46 -17
- package/.claude/skills/workflow-commands/SKILL.md +8 -23
- package/.claude/skills/workflow-commands/backlogrun-child.md +4 -2
- package/.claude/skills/workflow-commands/backlogrun.md +3 -12
- package/.claude/skills/workflow-commands/fullrun-steps.md +4 -3
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +2 -1
- package/.claude/skills/workflow-commands/into-target.md +1 -1
- package/.claude/skills/workflow-commands/observation-filing.md +42 -43
- package/.claude/skills/workflow-commands/prerequisite.md +1 -1
- package/.codex/hooks.json +1 -5
- package/.github/workflows/sonar-qube.yml +1 -1
- package/AGENTS.md +4 -25
- package/CLAUDE.md +24 -38
- package/GEMINI.md +4 -24
- package/README.md +22 -58
- package/dist/CLAUDE.md +24 -38
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-WMIR5SNX.js → chunk-D67KVGGY.js} +3 -3
- package/dist/hooks/{chunk-EZLHXKJ6.js → chunk-EIFCAJRB.js} +70 -27
- package/dist/hooks/{chunk-KPZN42RT.js → chunk-GJT4BNVE.js} +3 -3
- package/dist/hooks/{chunk-PAXMLGSS.js → chunk-GLBDBJOK.js} +44 -24
- package/dist/hooks/{chunk-GBJUERYK.js → chunk-HOYJU5RM.js} +1 -1
- package/dist/hooks/{chunk-6PSTVOLH.js → chunk-HU66PWWL.js} +33 -57
- package/dist/hooks/{chunk-A2H5I2VD.js → chunk-KG6PZD72.js} +2 -2
- package/dist/hooks/{chunk-AWTFKKFW.js → chunk-XPLP27WA.js} +3 -8
- package/dist/hooks/{chunk-4GDR2YTV.js → chunk-Y27M6A7X.js} +4 -4
- package/dist/hooks/{chunk-56LGMJJV.js → chunk-ZXNE6NY3.js} +2 -2
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +9 -9
- package/dist/hooks/stop-guard.js +11 -15
- package/dist/josh.js +38 -19
- package/dist/version/index.js +93 -41
- package/eslint/base.js +2 -11
- package/package.json +32 -6
- package/prompts/coding-standards.md +15 -290
- package/prompts/collaboration-workflow/cross-repo-epic.md +2 -51
- package/prompts/collaboration-workflow/epic-audit.md +2 -53
- package/prompts/collaboration-workflow/epic-bundle.md +2 -106
- package/prompts/collaboration-workflow/issue-template.md +4 -4
- package/prompts/collaboration-workflow/operating-rules.md +18 -75
- package/prompts/collaboration-workflow/principles.md +74 -0
- package/prompts/collaboration-workflow/residency.md +54 -14
- package/prompts/collaboration-workflow/rule-delivery.md +1 -11
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/prompts/collaboration-workflow.md +22 -28
- package/scripts/adopt/adopt-logic.ts +2 -2
- package/scripts/adopt/adopt-toolkits.ts +1 -1
- package/scripts/adopt/adopt.ts +2 -2
- package/scripts/agent/agent-event.ts +2 -9
- package/scripts/agent/agent-exit-record.ts +2 -9
- package/scripts/backlog/backlog-budget-cli.ts +2 -6
- package/scripts/backlog/backlog-drive-cli.ts +20 -10
- package/scripts/backlog/backlog-drive.ts +50 -16
- package/scripts/backlog/backlog-offer-cli.ts +6 -5
- package/scripts/backlog/backlog-plan.ts +1 -1
- package/scripts/build/build-bin.ts +2 -1
- package/scripts/build/build-config-merge.ts +2 -1
- package/scripts/build/build-hooks.ts +24 -4
- package/scripts/build/build-managed-marker.ts +2 -1
- package/scripts/build/build-self-sync-guard.ts +2 -1
- package/scripts/build/build-version.ts +2 -1
- package/scripts/cases/cases-logic.ts +2 -6
- package/scripts/ci/pr-classification.ts +13 -11
- package/scripts/cost-runtime/cost-transcript.ts +4 -11
- package/scripts/doctor/doctor-consumer.ts +5 -12
- package/scripts/doctor/doctor-ports.ts +2 -2
- package/scripts/document/document-byte-budget.ts +8 -16
- package/scripts/document/document-section.ts +2 -6
- package/scripts/document/entry-read-budget.ts +1 -1
- package/scripts/{git/git-epic-add-body.ts → epic/epic-add-body.ts} +23 -23
- package/scripts/{git/git-epic-add-plan.ts → epic/epic-add-plan.ts} +23 -26
- package/scripts/{git/git-epic-add.ts → epic/epic-add.ts} +17 -17
- package/scripts/epic/epic-audit-cli.ts +4 -4
- package/scripts/epic/epic-audit-orphans.ts +2 -2
- package/scripts/epic/epic-audit-rationale.ts +1 -1
- package/scripts/epic/epic-audit.ts +1 -1
- package/scripts/{git/git-epic-body.ts → epic/epic-body.ts} +6 -6
- package/scripts/epic/epic-bundle-cli.ts +1 -1
- package/scripts/epic/epic-bundle.ts +2 -2
- package/scripts/epic/epic-candidate-confirm.ts +2 -1
- package/scripts/{git/git-epic-chains.ts → epic/epic-chains.ts} +4 -4
- package/{scripts-ai → scripts/epic}/epic-check.ts +5 -4
- package/{scripts-ai → scripts/epic}/epic-cli.ts +4 -4
- package/scripts/{git/git-epic-close-comment.ts → epic/epic-close-comment.ts} +9 -9
- package/scripts/{git/git-epic-close.ts → epic/epic-close.ts} +23 -19
- package/scripts/epic/epic-cross-repo.ts +21 -38
- package/scripts/{git/git-epic-decision.ts → epic/epic-decision.ts} +12 -14
- package/scripts/epic/epic-fetch.ts +3 -3
- package/scripts/epic/epic-graph.ts +3 -3
- package/scripts/epic/epic-index.ts +2 -2
- package/scripts/epic/epic-issue.ts +1 -1
- package/scripts/epic/epic-next.ts +3 -3
- package/scripts/{git/git-epic-parse.ts → epic/epic-parse.ts} +3 -3
- package/scripts/{git/git-epic-promote.ts → epic/epic-promote.ts} +10 -10
- package/scripts/{git/git-epic-read.ts → epic/epic-read.ts} +9 -9
- package/scripts/{git/git-epic-reconcile.ts → epic/epic-reconcile.ts} +18 -20
- package/scripts/{git/git-epic-reference.ts → epic/epic-reference.ts} +4 -23
- package/scripts/epic/epic-relation-recheck.ts +4 -3
- package/scripts/{git/git-epic-relations.ts → epic/epic-relations.ts} +5 -5
- package/scripts/{git/git-epic-remove-plan.ts → epic/epic-remove-plan.ts} +17 -19
- package/scripts/{git/git-epic-remove.ts → epic/epic-remove.ts} +13 -13
- package/scripts/{git/git-epic-run.ts → epic/epic-run.ts} +27 -27
- package/scripts/{git/git-epic-sections.ts → epic/epic-sections.ts} +6 -6
- package/scripts/{git/git-epic-shape.ts → epic/epic-shape.ts} +7 -7
- package/scripts/{git/git-epic-validate.ts → epic/epic-validate.ts} +13 -13
- package/{scripts-ai → scripts/epic}/epic.ts +17 -16
- package/scripts/gh/fix-gh-packages.ts +2 -1
- package/scripts/gh/gh-spawn.ts +12 -10
- package/scripts/git/constants.ts +1 -1
- package/scripts/git/git-branch.ts +10 -1
- package/scripts/git/git-command.ts +1 -1
- package/scripts/git/git-error.ts +2 -5
- package/{scripts-ai → scripts/git}/git-followup-finish.ts +7 -7
- package/scripts/git/git-followup-flush.ts +74 -48
- package/scripts/git/git-followup-issue-close.ts +2 -2
- package/scripts/git/git-followup-label.ts +1 -1
- package/scripts/git/git-followup-pending.ts +1 -1
- package/{scripts-ai → scripts/git}/git-followup-workflow.ts +23 -11
- package/scripts/git/git-gh-exec.ts +85 -26
- package/scripts/git/git-gh-issue-read.ts +2 -2
- package/scripts/git/git-gh-issue-rest.ts +2 -2
- package/scripts/git/git-gh-issue-write.ts +6 -6
- package/scripts/git/git-gh-pr-read.ts +0 -21
- package/scripts/git/git-gh-pr.ts +2 -23
- package/scripts/git/git-pr-followup-wrapup.ts +2 -6
- package/scripts/git/git-preflight.ts +3 -6
- package/scripts/git/git-staging.ts +21 -9
- package/{scripts-ai → scripts/git}/git-workflow.ts +11 -11
- package/scripts/git/github-issue-url.ts +1 -1
- package/scripts/git/issue-labels.ts +1 -1
- package/scripts/git/issue-reference.ts +19 -0
- package/scripts/git/main-merge.ts +2 -1
- package/scripts/git/main-sync.ts +3 -2
- package/scripts/git/schemas.ts +1 -1
- package/scripts/git/stash-pop-args.ts +7 -13
- package/scripts/git/stash-pop-cli.ts +2 -1
- package/{scripts-ai → scripts/git}/telegram-test-logic.ts +2 -2
- package/{scripts-ai → scripts/git}/telegram-test.ts +7 -7
- package/scripts/hooks/format-rewrite-notice.ts +2 -8
- package/scripts/init/init-ai-copy.ts +23 -14
- package/scripts/init/init.ts +8 -4
- package/scripts/init/start-steps.ts +2 -1
- package/scripts/init/start.ts +2 -1
- package/scripts/issue/issue-comment-cli.ts +2 -1
- package/scripts/issue/issue-file-cli.ts +2 -1
- package/scripts/issue/issue-fold-cli.ts +9 -16
- package/scripts/issue/issue-scout-cli.ts +8 -13
- package/scripts/issue/measure-rerun-cli.ts +7 -6
- package/scripts/josh/josh-commands-ai.ts +2 -2
- package/scripts/josh/josh-commands-backlog.ts +1 -1
- package/scripts/josh/josh-commands-development.ts +1 -1
- package/scripts/josh/josh-commands-workflow.ts +4 -4
- package/scripts/lane/lane-cli.ts +2 -1
- package/scripts/lane/lane-open.ts +3 -2
- package/scripts/lane/lane-registry.ts +3 -6
- package/scripts/lib/cli-flags.ts +20 -2
- package/scripts/lib/error-message.ts +9 -0
- package/scripts/lib/read-file.ts +26 -2
- package/scripts/observations/observation-ledger-home.ts +98 -26
- package/scripts/observations/observation-ledger-migrate.ts +26 -21
- package/scripts/observations/observation-ledger-prepare.ts +10 -20
- package/scripts/observations/observation-ledger.ts +58 -25
- package/scripts/observations/observations-flush-cli.ts +6 -23
- package/scripts/observations/observations-flush-landing.ts +2 -1
- package/scripts/observations/observations-flush.ts +29 -20
- package/scripts/package/package-scout-cli.ts +8 -16
- package/scripts/ports/port-command.ts +2 -1
- package/scripts/propagate/propagate-publish.ts +6 -7
- package/scripts/propagate/propagate-steps.ts +2 -1
- package/scripts/propagate/propagate.ts +3 -2
- package/scripts/refactor/import-graph.ts +2 -6
- package/scripts/registry-migration/migrate.ts +3 -2
- package/scripts/registry-migration/user-npmrc.ts +2 -2
- package/scripts/release/release-cli.ts +2 -1
- package/scripts/repo/repo-setting.ts +10 -8
- package/scripts/review/review-finding-ledger.ts +3 -3
- package/scripts/review/review-findings-cli.ts +4 -11
- package/scripts/review/review-record-cli.ts +14 -11
- package/scripts/review/review-record.ts +6 -16
- package/scripts/rules/decision-oracle.ts +2 -2
- package/scripts/rules/rule-body-guard.ts +2 -2
- package/scripts/rules/rule-value-cache.ts +99 -0
- package/scripts/rules/rule-value-cli.ts +10 -9
- package/scripts/rules/rule-value.ts +1 -1
- package/scripts/run/run-carry-args.ts +2 -6
- package/scripts/run/run-carry-cli.ts +5 -7
- package/scripts/run/run-cut-args.ts +7 -13
- package/scripts/run/run-ending-cli.ts +4 -7
- package/scripts/run/run-event-stream.ts +2 -5
- package/scripts/run/run-liveness-cli.ts +4 -7
- package/scripts/run/run-progress-args.ts +2 -6
- package/scripts/run/run-progress-cli.ts +2 -1
- package/scripts/run/run-tail-cli.ts +3 -3
- package/scripts/run/run-tidy-stashes.ts +7 -14
- package/scripts/run/run-wake-cli.ts +2 -6
- package/scripts/run/run-wake-driver.ts +15 -2
- package/scripts/run/run-wake-session.ts +14 -4
- package/scripts/safe-chain/project-config.ts +3 -6
- package/scripts/security/security-audit-provision.ts +6 -7
- package/scripts/sonar/sonar-hotspots-cli.ts +2 -1
- package/scripts/sync/directory-copy-guard.ts +2 -1
- package/scripts/sync/sync-ai-files.ts +3 -3
- package/scripts/sync/template-source-logic.ts +3 -4
- package/scripts/test/e2e-retry-check.ts +3 -6
- package/scripts/ui/ui-routes-cli.ts +3 -6
- package/scripts/version/bump-version.ts +2 -1
- package/scripts/version/version-remote.ts +18 -27
- package/scripts/version/version-targets.ts +3 -3
- package/.claude/skills/workflow-commands/rule-residency.md +0 -267
- package/evals/scenarios/consult-not-execute.json +0 -20
- package/evals/scenarios/no-implicit-workflow.json +0 -26
- package/evals/scenarios/overrides-both-locations.json +0 -29
- package/evals/scenarios/upstream-interrupt.json +0 -23
- package/evals/scenarios/workflow-skill-loaded.json +0 -16
- package/prompts/agent-lint-protocol.json +0 -79
- package/prompts/agent-rules.md +0 -63
- package/prompts/collaboration-workflow/consultation-vs-execution.md +0 -8
- package/prompts/collaboration-workflow/distributed-docs.md +0 -7
- package/prompts/collaboration-workflow/durable-rules.md +0 -24
- package/prompts/collaboration-workflow/latest-first.md +0 -14
- package/prompts/collaboration-workflow/no-clones.md +0 -11
- package/prompts/collaboration-workflow/simplicity-first.md +0 -9
- package/prompts/collaboration-workflow/single-source-rules.md +0 -13
- package/scripts/build/shim-shell.ts +0 -25
- package/scripts/claude/skill-meta.ts +0 -37
- package/scripts/document/document-scan.ts +0 -108
- package/scripts/document/generate-catalog.ts +0 -69
- package/scripts/document/retired-phrases.ts +0 -143
- package/scripts/document/review-agent-type.ts +0 -46
- package/scripts/document/single-source.ts +0 -73
- package/scripts/git/git-fixture-workspace.ts +0 -88
- package/scripts/hooks/format-edited-file-fixtures.ts +0 -109
- package/scripts/hooks/prompt-hooks.ts +0 -38
- package/scripts/init/hook-command-bootstrap.ts +0 -83
- package/scripts/issue/issue-logic.ts +0 -50
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +0 -1227
- package/scripts/josh/josh-script-reader.ts +0 -11
- package/scripts/package/production-dependency-graph.ts +0 -68
- package/scripts/retrospective/retrospective-cli.ts +0 -97
- package/scripts/retrospective/retrospective.ts +0 -127
- package/scripts/rules/delivered-rules-harness.ts +0 -72
- package/scripts/rules/top-level-scripts-guard.ts +0 -47
- package/scripts/run/run-carry-flush.ts +0 -58
- package/scripts/run/run-transcript.golden.txt +0 -57
- package/scripts/run/run-wake-work.ts +0 -108
- package/scripts/sync/dependabot-pin-logic.ts +0 -135
- package/scripts/test/classify-isolation.ts +0 -149
- package/scripts/test/http-fault-injection.ts +0 -79
- package/scripts/test/josh-harness-environment.ts +0 -260
- package/scripts/test/josh-harness.ts +0 -96
- package/scripts/test/pilot-files.ts +0 -452
- package/scripts/test/pilot-measure.ts +0 -78
- package/scripts/test/test-network-guard.ts +0 -453
- package/scripts/test/test-repository-guard.ts +0 -317
- package/scripts/test/test-state-guard.ts +0 -85
- package/scripts/test/test-stdout-guard.ts +0 -54
- package/scripts/test/test-telegram-guard.ts +0 -90
- package/scripts/test/unit-guard-environment.ts +0 -12
- package/scripts/test/unit-projects.ts +0 -139
- package/scripts/test/vitest-include-globs.ts +0 -12
- package/scripts/time-runtime/time-batch-guard-fixtures.ts +0 -66
- package/scripts-ai/completion-gate.ts +0 -13
- package/scripts-ai/environment-loader.ts +0 -9
- /package/{scripts-ai → scripts/epic}/epic-cli-argv.ts +0 -0
- /package/{scripts-ai → scripts/epic}/epic-cli-remove.ts +0 -0
- /package/{scripts-ai → scripts/git}/followup-issue-number.ts +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
2
2
|
import { run_issue_number } from './run-issue-number'
|
|
3
3
|
|
|
4
4
|
// `josh run:cut` turns its `argv` into one of four requests. The contract mirrors `run-carry-args.ts`:
|
|
@@ -47,18 +47,12 @@ type Request =
|
|
|
47
47
|
| { kind: 'end' }
|
|
48
48
|
|
|
49
49
|
function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
return { values: parsed.values, positionals: parsed.positionals }
|
|
59
|
-
} catch {
|
|
60
|
-
return undefined
|
|
61
|
-
}
|
|
50
|
+
return cli_flags.parse_or_undefined({
|
|
51
|
+
args: [...argv],
|
|
52
|
+
options: OPTIONS,
|
|
53
|
+
strict: true,
|
|
54
|
+
allowPositionals: true,
|
|
55
|
+
})
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
// The issue is the sole positional, and it must be a real issue number: a cut relaunches
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import {
|
|
3
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
import {
|
|
5
6
|
run_ending,
|
|
6
7
|
UNREADABLE_VERDICT,
|
|
@@ -56,11 +57,7 @@ interface ParsedArguments {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function read_arguments(argv: ReadonlyArray<string>): ParsedArguments | undefined {
|
|
59
|
-
|
|
60
|
-
return parseArgs({ args: [...argv], options: OPTIONS, allowPositionals: true })
|
|
61
|
-
} catch {
|
|
62
|
-
return undefined
|
|
63
|
-
}
|
|
60
|
+
return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, allowPositionals: true })
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
function is_valid(parsed: ParsedArguments): boolean {
|
|
@@ -96,7 +93,7 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
|
96
93
|
try {
|
|
97
94
|
return report(await run_ending.check(request))
|
|
98
95
|
} catch (error) {
|
|
99
|
-
return report_unreadable(
|
|
96
|
+
return report_unreadable(error_text.message_of(error))
|
|
100
97
|
}
|
|
101
98
|
}
|
|
102
99
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
2
|
+
import { json_value } from '#scripts/lib/json-value'
|
|
2
3
|
import { z } from 'zod'
|
|
3
4
|
|
|
4
5
|
// The run's append-only, ordered event stream — the surface a reader who was away rebuilds the run's
|
|
@@ -143,11 +144,7 @@ function is_present(event: RunEvent | undefined): event is RunEvent {
|
|
|
143
144
|
// A malformed line reads as no event rather than throwing, so one truncated append — a write that lost a
|
|
144
145
|
// race — never makes the whole stream unreadable.
|
|
145
146
|
function parse_line(line: string): RunEvent | undefined {
|
|
146
|
-
|
|
147
|
-
return event_schema.safeParse(JSON.parse(line)).data
|
|
148
|
-
} catch {
|
|
149
|
-
return undefined
|
|
150
|
-
}
|
|
147
|
+
return event_schema.safeParse(json_value.parse_or_undefined(line)).data
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
// Every event on the stream, oldest first. An absent or unreadable file is an empty stream, the same
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import {
|
|
3
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
import { run_issue_number } from './run-issue-number'
|
|
5
6
|
import {
|
|
6
7
|
MS_PER_MINUTE,
|
|
@@ -89,11 +90,7 @@ interface ParsedArguments {
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
function read_arguments(argv: ReadonlyArray<string>): ParsedArguments | undefined {
|
|
92
|
-
|
|
93
|
-
return parseArgs({ args: [...argv], options: OPTIONS, allowPositionals: true })
|
|
94
|
-
} catch {
|
|
95
|
-
return undefined
|
|
96
|
-
}
|
|
93
|
+
return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, allowPositionals: true })
|
|
97
94
|
}
|
|
98
95
|
|
|
99
96
|
function is_valid_target(parsed: ParsedArguments): boolean {
|
|
@@ -154,7 +151,7 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
|
154
151
|
try {
|
|
155
152
|
return report(await run_liveness.check(request))
|
|
156
153
|
} catch (error) {
|
|
157
|
-
return report_undetermined(
|
|
154
|
+
return report_undetermined(error_text.message_of(error))
|
|
158
155
|
}
|
|
159
156
|
}
|
|
160
157
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
2
2
|
import { run_progress_config } from './run-progress-config'
|
|
3
3
|
|
|
4
4
|
// The argument half of `josh run:progress`, kept apart from the watcher so the CLI stays inside the file
|
|
@@ -37,11 +37,7 @@ interface ParsedValues {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
40
|
-
|
|
41
|
-
return parseArgs({ args: [...argv], options: OPTIONS }).values
|
|
42
|
-
} catch {
|
|
43
|
-
return undefined
|
|
44
|
-
}
|
|
40
|
+
return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS })?.values
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
// A hand-typed `--interval` outranks the environment, the environment outranks the interval the
|
|
@@ -5,6 +5,7 @@ import { backlog_arrival, type ArrivalProbe } from '#scripts/backlog/backlog-arr
|
|
|
5
5
|
import { backlog_ready } from '#scripts/backlog/backlog-ready'
|
|
6
6
|
import { gh_spawn } from '#scripts/gh/gh-spawn'
|
|
7
7
|
import { lane_child_marker } from '#scripts/lane/lane-child-marker'
|
|
8
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
8
9
|
import { run_event_stream_emit } from './run-event-stream-emit'
|
|
9
10
|
import { run_progress, type ProgressState } from './run-progress'
|
|
10
11
|
import { run_progress_args, type ParsedValues } from './run-progress-args'
|
|
@@ -229,7 +230,7 @@ function decline(loop: WatchLoop, now_ms: number, notice: string): WatchLoop {
|
|
|
229
230
|
// under load — the very load this command exists to report — would otherwise take the reporting down
|
|
230
231
|
// silently for the rest of an unattended run, with the parent not waiting on it and nothing saying why.
|
|
231
232
|
function to_notice(error: unknown): string {
|
|
232
|
-
return `${FAILED_TICK_PREFIX} ${
|
|
233
|
+
return `${FAILED_TICK_PREFIX} ${error_text.message_of(error)}`
|
|
233
234
|
}
|
|
234
235
|
|
|
235
236
|
async function attempt(
|
|
@@ -17,9 +17,9 @@ import { run_tail, type TailSection } from './run-tail'
|
|
|
17
17
|
//
|
|
18
18
|
// **A dispatched lane child skips the ledger step** (joshuafolkken/kit#2492). A flush opens a
|
|
19
19
|
// ledger-only pull request and waits out its whole CI before it merges — minutes per issue, spent on a
|
|
20
|
-
// file only the retrospective and the measurements read
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// file only the retrospective and the measurements read — and a lane has nothing for it: its lines
|
|
21
|
+
// merged with its own pull request (joshuafolkken/kit#2919). A single run outside a lane keeps the
|
|
22
|
+
// step for a line written on the default branch outside any issue's run.
|
|
23
23
|
|
|
24
24
|
const ARGV_OFFSET = 2
|
|
25
25
|
const SUCCESS_EXIT_CODE = 0
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
2
1
|
import { git_stash, type StashEntry } from '#scripts/git/git-stash'
|
|
3
2
|
import { stash_orphans } from '#scripts/git/stash-orphans'
|
|
4
3
|
import { stash_sweep_lock } from '#scripts/git/stash-sweep-lock'
|
|
@@ -18,16 +17,8 @@ const READ_CONCURRENCY = 8
|
|
|
18
17
|
const GONE_REASON = 'already dropped by another run'
|
|
19
18
|
const BUSY_REASON = 'another run held the stash sweep lock'
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return await readFile(ledger_path, 'utf8')
|
|
24
|
-
} catch {
|
|
25
|
-
return ''
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// An entry cut before joshuafolkken/kit#2724 holds its lines at the ledger's old path, so the lines of
|
|
30
|
-
// every ledger path it touches are carried to the current ledger.
|
|
20
|
+
// An entry cut before joshuafolkken/kit#2919 holds its lines at an old single-file path, so the lines
|
|
21
|
+
// of every ledger path it touches are carried to the current ledger.
|
|
31
22
|
async function added_ledger_lines(
|
|
32
23
|
hash: string,
|
|
33
24
|
ledger_paths: ReadonlyArray<string>,
|
|
@@ -43,16 +34,18 @@ async function added_ledger_lines(
|
|
|
43
34
|
return added
|
|
44
35
|
}
|
|
45
36
|
|
|
46
|
-
// How many ledger lines went into the
|
|
37
|
+
// How many ledger lines went into the running work tree's ledger before the drop. The duplicate check
|
|
38
|
+
// reads every issue's file, and the lines land in the file this tree writes (joshuafolkken/kit#2919),
|
|
39
|
+
// so the run's own commit takes them to the default branch.
|
|
47
40
|
async function carry_ledger(hash: string): Promise<number> {
|
|
48
41
|
const changed = await git_stash.changed_paths(hash)
|
|
49
42
|
const ledger_paths = changed.filter((file_path) => observation_ledger.is_ledger_path(file_path))
|
|
50
43
|
|
|
51
44
|
if (ledger_paths.length === 0) return 0
|
|
52
45
|
|
|
53
|
-
const ledger_path = observation_ledger_home.
|
|
46
|
+
const ledger_path = await observation_ledger_home.writer_path(new Date())
|
|
54
47
|
const added = await added_ledger_lines(hash, ledger_paths)
|
|
55
|
-
const carried = run_tidy.ledger_carry(added, await
|
|
48
|
+
const carried = run_tidy.ledger_carry(added, (await observation_ledger_home.read()) ?? '')
|
|
56
49
|
|
|
57
50
|
await observation_ledger_home.append(ledger_path, carried)
|
|
58
51
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { randomUUID } from 'node:crypto'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
|
-
import { parseArgs } from 'node:util'
|
|
6
5
|
import type { AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
7
6
|
import { telegram_notify } from '#scripts/git/telegram-notify'
|
|
7
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
8
8
|
import { run_carry, type CarryRead } from './run-carry'
|
|
9
9
|
import { run_event_stream } from './run-event-stream'
|
|
10
10
|
import { run_headless } from './run-headless'
|
|
@@ -120,11 +120,7 @@ function note_to_stderr(note: string): void {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
123
|
-
|
|
124
|
-
return parseArgs({ args: [...argv], options: OPTIONS, strict: true }).values
|
|
125
|
-
} catch {
|
|
126
|
-
return undefined
|
|
127
|
-
}
|
|
123
|
+
return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, strict: true })?.values
|
|
128
124
|
}
|
|
129
125
|
|
|
130
126
|
function text_of(value: string | boolean | undefined): string | undefined {
|
|
@@ -6,6 +6,8 @@ import type { DriveResult } from './run-wake-loop'
|
|
|
6
6
|
const SUCCESS = 0
|
|
7
7
|
const FIRST_LINE = 0
|
|
8
8
|
const RESUME_PREFIX = 'resume: '
|
|
9
|
+
const DETAILS_TAIL_LENGTH = 2048
|
|
10
|
+
const TRUNCATION_MARK = '... '
|
|
9
11
|
|
|
10
12
|
function driver_args(invocation: string): ReadonlyArray<string> | undefined {
|
|
11
13
|
const rebuilt = run_invocation.rebuild(invocation)
|
|
@@ -24,13 +26,24 @@ function is_final_stop(verdict: string | undefined, read: CarryRead): boolean {
|
|
|
24
26
|
return verdict?.startsWith('stop') === true && read.kind === 'none'
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
// **The details are the driver's whole stderr, so only their tail is handed on, and it goes last**
|
|
30
|
+
// (joshuafolkken/kit#2931). The stderr carries every command the driver ran since it started, which
|
|
31
|
+
// after a long run is tens of kilobytes — far past what `is_safe_value` lets into an argv element. Its
|
|
32
|
+
// tail is where the reason for stopping is; placing it last means the cap `wake_argv` applies to the
|
|
33
|
+
// whole prompt can only ever cut into the details, never the verdict, the resume line or the epic note.
|
|
34
|
+
function details_tail(details: string): string {
|
|
35
|
+
return details.length > DETAILS_TAIL_LENGTH
|
|
36
|
+
? `${TRUNCATION_MARK}${details.slice(-DETAILS_TAIL_LENGTH)}`
|
|
37
|
+
: details
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
function handoff_material(verdict: string, resume: string, details: string): string {
|
|
28
|
-
const context = details === '' ? '' : `\nDetails: ${details}`
|
|
41
|
+
const context = details === '' ? '' : `\nDetails: ${details_tail(details)}`
|
|
29
42
|
const epic = verdict.startsWith('epic #')
|
|
30
43
|
? '\nThe named item is an epic. Follow backlogrun-steps.md named epic procedure; do not launch the epic root as a fullrun child. After all children merge or park, record the root with pnpm josh run:carry --done <epic-number> --owner "$PPID" before continuing to the next named item.'
|
|
31
44
|
: ''
|
|
32
45
|
|
|
33
|
-
return `Driver result: ${verdict}\n${resume}${
|
|
46
|
+
return `Driver result: ${verdict}\n${resume}${epic}${context}`
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
function driver_result(out: string, read: CarryRead, details = ''): DriveResult {
|
|
@@ -37,6 +37,8 @@ const WAKE_FLAGS = claude_agent_argv.AGENT_FLAGS
|
|
|
37
37
|
const LOOP_FLAG = '--loop'
|
|
38
38
|
const INTERVAL_FLAG = '--interval'
|
|
39
39
|
const SUPERVISOR_SESSION = 'run-wake-supervisor'
|
|
40
|
+
// eslint-disable-next-line no-control-regex -- matching control characters is the point
|
|
41
|
+
const CONTROL_CHARACTERS = /[\u{0}-\u{1F}\u{7F}]/gu
|
|
40
42
|
|
|
41
43
|
function scheduler_profile(): AgentProfile | undefined {
|
|
42
44
|
const resolved = agent_role_profile.resolve(agent_role_profile.SCHEDULER)
|
|
@@ -96,6 +98,17 @@ function resolved_argv(invocation: string, cwd?: string): AgentArgvResult {
|
|
|
96
98
|
: agent_argv.resolve_in(invocation, agent_role_profile.SCHEDULER, cwd)
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
// **Handoff material is made safe to launch rather than refused** (joshuafolkken/kit#2931). It is the
|
|
102
|
+
// driver's own output, not the record, so it is flattened — a newline becomes a separator, any other
|
|
103
|
+
// control character a space — and the prompt is cut at the cap `is_safe_value` enforces. The driver puts
|
|
104
|
+
// its verdict and resume line first, so the cut only ever shortens the details. The check itself is
|
|
105
|
+
// untouched: the argv still passes `is_safe_argv` before anything is spawned.
|
|
106
|
+
function handoff_prompt(matched: string, material: string): string {
|
|
107
|
+
const flattened = material.replaceAll('\n', ' | ').replaceAll(CONTROL_CHARACTERS, ' ')
|
|
108
|
+
|
|
109
|
+
return `${matched} | Driver handoff: ${flattened}`.slice(0, agent_role_profile.MAX_VALUE_LENGTH)
|
|
110
|
+
}
|
|
111
|
+
|
|
99
112
|
function wake_argv(
|
|
100
113
|
invocation: string,
|
|
101
114
|
profile?: AgentProfile,
|
|
@@ -104,10 +117,7 @@ function wake_argv(
|
|
|
104
117
|
): AgentArgvResult | undefined {
|
|
105
118
|
const matched = safe_invocation(invocation)
|
|
106
119
|
if (matched === undefined) return undefined
|
|
107
|
-
const prompt =
|
|
108
|
-
typeof session === 'object'
|
|
109
|
-
? `${matched} | Driver handoff: ${session.material.replaceAll('\n', ' | ')}`
|
|
110
|
-
: matched
|
|
120
|
+
const prompt = typeof session === 'object' ? handoff_prompt(matched, session.material) : matched
|
|
111
121
|
const session_id = typeof session === 'object' ? session.id : session
|
|
112
122
|
|
|
113
123
|
return profile === undefined
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
3
4
|
import { release_age } from '#scripts/version/release-age'
|
|
4
5
|
import { yaml_document } from '#scripts/yaml/yaml-document'
|
|
5
6
|
import { dump } from 'js-yaml'
|
|
@@ -126,17 +127,13 @@ function merge_project_config(existing: string, workspace: string, npmrc = ''):
|
|
|
126
127
|
return replace_safe_chain_block(existing, merged)
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
function read_optional(file_path: string): string {
|
|
130
|
-
return existsSync(file_path) ? readFileSync(file_path, 'utf8') : ''
|
|
131
|
-
}
|
|
132
|
-
|
|
133
130
|
function sync_project_config(root: string): boolean {
|
|
134
131
|
const workspace_path = path.join(root, WORKSPACE_FILE)
|
|
135
132
|
if (!existsSync(workspace_path)) return false
|
|
136
133
|
const config_path = path.join(root, PROJECT_CONFIG_FILE)
|
|
137
|
-
const existing =
|
|
134
|
+
const existing = file_reader.read_file_or_empty(config_path)
|
|
138
135
|
const workspace = readFileSync(workspace_path, 'utf8')
|
|
139
|
-
const npmrc =
|
|
136
|
+
const npmrc = file_reader.read_file_or_empty(path.join(root, NPMRC_FILE))
|
|
140
137
|
const merged = merge_project_config(existing, workspace, npmrc)
|
|
141
138
|
if (merged === existing) return false
|
|
142
139
|
writeFileSync(config_path, merged)
|
|
@@ -4,6 +4,7 @@ import path from 'node:path'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
6
6
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
7
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
7
8
|
import { security_audit } from './security-audit'
|
|
8
9
|
import { security_audit_logic } from './security-audit-logic'
|
|
9
10
|
import { security_audit_provision_logic, type ScannerAsset } from './security-audit-provision-logic'
|
|
@@ -14,10 +15,6 @@ const FORCE_FLAG = '--force'
|
|
|
14
15
|
const PATH_LOCATION = 'on PATH'
|
|
15
16
|
const { RETRY_INTERVAL_MS } = security_audit_provision_logic
|
|
16
17
|
|
|
17
|
-
function describe_error(error: unknown): string {
|
|
18
|
-
return error instanceof Error ? error.message : String(error)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
18
|
// `content` absent is the failure, and `reason` says which one. A 404 — what a bumped version whose
|
|
22
19
|
// asset was renamed looks like, and the one failure that never fixes itself — would otherwise read
|
|
23
20
|
// exactly like a transient 503, and a rejected request like neither.
|
|
@@ -41,7 +38,7 @@ async function download(url: string, timeout_ms: number): Promise<DownloadOutcom
|
|
|
41
38
|
|
|
42
39
|
return { content: Buffer.from(await response.arrayBuffer()), reason: '' }
|
|
43
40
|
} catch (error) {
|
|
44
|
-
return { reason:
|
|
41
|
+
return { reason: error_text.message_of(error) }
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
|
|
@@ -128,7 +125,7 @@ async function attempt(
|
|
|
128
125
|
} catch (error) {
|
|
129
126
|
return record_failure(
|
|
130
127
|
target_path,
|
|
131
|
-
security_audit_provision_logic.format_provision_error(
|
|
128
|
+
security_audit_provision_logic.format_provision_error(error_text.message_of(error)),
|
|
132
129
|
)
|
|
133
130
|
}
|
|
134
131
|
}
|
|
@@ -175,7 +172,9 @@ async function main(): Promise<void> {
|
|
|
175
172
|
try {
|
|
176
173
|
console.info(await report(PROJECT_ROOT, process.platform, process.arch, is_forced))
|
|
177
174
|
} catch (error) {
|
|
178
|
-
console.warn(
|
|
175
|
+
console.warn(
|
|
176
|
+
security_audit_provision_logic.format_provision_error(error_text.message_of(error)),
|
|
177
|
+
)
|
|
179
178
|
}
|
|
180
179
|
}
|
|
181
180
|
|
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from 'node:fs'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { git_command } from '#scripts/git/git-command'
|
|
6
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
6
7
|
import { managed_config_scope } from '#scripts/sync/managed-config-scope'
|
|
7
8
|
import { z } from 'zod'
|
|
8
9
|
import {
|
|
@@ -69,7 +70,7 @@ async function fetch_hotspots(url: string): Promise<HotspotFetch> {
|
|
|
69
70
|
|
|
70
71
|
return { hotspots: parsed.hotspots ?? [] }
|
|
71
72
|
} catch (error) {
|
|
72
|
-
return { error:
|
|
73
|
+
return { error: error_text.message_of(error) }
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cpSync, lstatSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { transform_copied_content } from '#scripts/init/init-copy-content'
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
import { file_content } from './file-content'
|
|
5
6
|
|
|
6
7
|
// `josh init` and `josh sync` both copy a distributed directory with `cpSync`, and both have to
|
|
@@ -143,7 +144,7 @@ function copy_directory(source_path: string, destination_path: string): boolean
|
|
|
143
144
|
// throw here would end `josh init` or `josh sync` in the middle of their work. The message is
|
|
144
145
|
// returned for the caller to report, the same way a blocker is.
|
|
145
146
|
function failure_message(prefix: string, error: unknown): string {
|
|
146
|
-
return `${prefix} (${
|
|
147
|
+
return `${prefix} (${error_text.message_of(error)})`
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
// The two failures are reported apart because they leave the destination in opposite states, and
|
|
@@ -5,6 +5,7 @@ import { transform_copied_content } from '#scripts/init/init-copy-content'
|
|
|
5
5
|
import { init_logic } from '#scripts/init/init-logic'
|
|
6
6
|
import { PACKAGE_DIR, PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
7
7
|
import type { ProjectShape } from '#scripts/init/project-profile'
|
|
8
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
8
9
|
import { copy_directory_failure, directory_copy_blocker } from './directory-copy-guard'
|
|
9
10
|
import { file_content } from './file-content'
|
|
10
11
|
import { REMOVED_SKILL_MANIFEST } from './removed-skill-manifest'
|
|
@@ -70,8 +71,7 @@ function sync_workspace_yaml(
|
|
|
70
71
|
is_force = false,
|
|
71
72
|
): boolean {
|
|
72
73
|
const template = readFileSync(template_path, 'utf8')
|
|
73
|
-
const existing =
|
|
74
|
-
!is_force && existsSync(destination_path) ? readFileSync(destination_path, 'utf8') : ''
|
|
74
|
+
const existing = is_force ? '' : file_reader.read_file_or_empty(destination_path)
|
|
75
75
|
const merged = init_logic.merge_workspace_yaml(existing, template)
|
|
76
76
|
|
|
77
77
|
return file_content.write_text_if_changed(destination_path, merged)
|
|
@@ -155,7 +155,7 @@ function ensured_claude_md(existing: string | undefined, shape?: ProjectShape):
|
|
|
155
155
|
// without ever disturbing those additions — so this ignores --force, which would otherwise mean
|
|
156
156
|
// discarding a consumer's content.
|
|
157
157
|
function sync_claude_md(destination_path: string, shape?: ProjectShape): void {
|
|
158
|
-
const existing =
|
|
158
|
+
const existing = file_reader.read_optional(destination_path)
|
|
159
159
|
const ensured = ensured_claude_md(existing, shape)
|
|
160
160
|
|
|
161
161
|
if (ensured === existing) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
|
-
import { copyFileSync,
|
|
2
|
+
import { copyFileSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
3
|
import { package_path } from '#scripts/init/init-paths'
|
|
4
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
4
5
|
import { z } from 'zod'
|
|
5
6
|
|
|
6
7
|
interface TemplateSourcePair {
|
|
@@ -39,9 +40,7 @@ function read_file(relative_path: string): string {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function read_optional_file(relative_path: string): string {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return existsSync(full_path) ? readFileSync(full_path, 'utf8') : ''
|
|
43
|
+
return file_reader.read_file_or_empty(package_path(relative_path))
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
function hash_text(text: string): string {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
-
import { appendFileSync, readdirSync,
|
|
2
|
+
import { appendFileSync, readdirSync, statSync } from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
5
6
|
|
|
6
7
|
// Whether the preview server process died during a failed E2E attempt.
|
|
7
8
|
//
|
|
@@ -59,11 +60,7 @@ function has_crash_signature(log_text: string): boolean {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
function read_file_safe(file_path: string): string {
|
|
62
|
-
|
|
63
|
-
return readFileSync(file_path, 'utf8')
|
|
64
|
-
} catch {
|
|
65
|
-
return ''
|
|
66
|
-
}
|
|
63
|
+
return file_reader.read_if_readable(file_path) ?? ''
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
// Every file under the directory, because wrangler names the debug log after the run and a job that
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
-
import { readdirSync
|
|
2
|
+
import { readdirSync } from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { changed_paths } from '#scripts/git/changed-paths'
|
|
6
6
|
import { git_command } from '#scripts/git/git-command'
|
|
7
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
7
8
|
import { ui_routes } from './ui-routes'
|
|
8
9
|
|
|
9
10
|
// `josh ui:routes [--staged]` — list the screenshot-target routes the current change touches
|
|
@@ -59,11 +60,7 @@ function route_files(root: string): Array<RouteFile> {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
function read_text(file: string): string {
|
|
62
|
-
|
|
63
|
-
return readFileSync(file, 'utf8')
|
|
64
|
-
} catch {
|
|
65
|
-
return ''
|
|
66
|
-
}
|
|
63
|
+
return file_reader.read_if_readable(file) ?? ''
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
// A route file imports the component when its source names the component's file — the import
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
5
6
|
import { json_object_schema, package_with_version_schema } from '#scripts/lib/schemas'
|
|
6
7
|
import semver from 'semver'
|
|
7
8
|
|
|
@@ -61,7 +62,7 @@ function main(): void {
|
|
|
61
62
|
try {
|
|
62
63
|
bump_version(bump_type as BumpType)
|
|
63
64
|
} catch (error) {
|
|
64
|
-
console.error(
|
|
65
|
+
console.error(error_text.message_of(error))
|
|
65
66
|
process.exit(1)
|
|
66
67
|
}
|
|
67
68
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { git_gh_exec } from '#scripts/git/git-gh-exec'
|
|
2
|
-
import {
|
|
2
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
3
3
|
import { safe_json_parse } from './parse-json'
|
|
4
4
|
import { release_age } from './release-age'
|
|
5
5
|
|
|
6
|
-
const GH_FAILURE_FALLBACK = 'gh api failed'
|
|
7
6
|
// Enough history to reach past a quarantine window into the newest release that has aged out of it.
|
|
8
7
|
// The endpoint's default page is a single entry, so it is widened to the API's maximum rather than
|
|
9
8
|
// paged through: a page that cannot span the window resolves no aged release at all, which silently
|
|
@@ -23,32 +22,27 @@ function require_endpoint(versions_endpoint: string | undefined, package_name: s
|
|
|
23
22
|
)
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
// Turn a failed `gh api` result into a concise detail line: prefer gh's own stderr
|
|
27
|
-
// (e.g. "gh: Not Found (HTTP 404)"), falling back to execa's short message.
|
|
28
|
-
function describe_failure(result: { stderr: string; shortMessage?: string | undefined }): string {
|
|
29
|
-
const stderr = result.stderr.trim()
|
|
30
|
-
|
|
31
|
-
if (stderr !== '') return stderr
|
|
32
|
-
|
|
33
|
-
return result.shortMessage ?? GH_FAILURE_FALLBACK
|
|
34
|
-
}
|
|
35
|
-
|
|
36
25
|
// Fetch the latest published version from a GitHub Packages versions endpoint. The endpoint is
|
|
37
26
|
// supplied per package (e.g. `/users/joshuafolkken/packages/npm/kit/versions?per_page=1`) so the
|
|
38
27
|
// same fetcher serves kit, jgame, and app-kit. Guards an undefined/empty endpoint and wraps
|
|
39
|
-
// `gh api` failures with an actionable message instead of a raw ExecaSyncError stack.
|
|
28
|
+
// `gh api` failures with an actionable message instead of a raw ExecaSyncError stack. The detail is
|
|
29
|
+
// the shared layer's failure text: gh's own stderr (e.g. "gh: Not Found (HTTP 404)") when it wrote
|
|
30
|
+
// one, execa's message otherwise, followed by any JSON error body gh wrote to stdout.
|
|
40
31
|
function fetch_latest_version(versions_endpoint: string | undefined, package_name: string): string {
|
|
41
32
|
const endpoint = require_endpoint(versions_endpoint, package_name)
|
|
42
|
-
const result = execaSync('gh', ['api', endpoint, '--jq', '.[0].name'], {
|
|
43
|
-
...git_gh_exec.direct_environment(),
|
|
44
|
-
reject: false,
|
|
45
|
-
})
|
|
46
33
|
|
|
47
|
-
|
|
34
|
+
try {
|
|
35
|
+
return git_gh_exec.exec_gh_api_sync({ path: endpoint, jq_filter: '.[0].name' }).trim()
|
|
36
|
+
} catch (error) {
|
|
37
|
+
const detail = error_text.message_of(error)
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Failed to fetch latest version for ${package_name} via ${endpoint}: ${detail}`,
|
|
41
|
+
{
|
|
42
|
+
cause: error,
|
|
43
|
+
},
|
|
44
|
+
)
|
|
45
|
+
}
|
|
52
46
|
}
|
|
53
47
|
|
|
54
48
|
// Rewrite the endpoint's page size. The endpoint is consumer-overridable, so `per_page` is set
|
|
@@ -73,12 +67,9 @@ function fetch_release_times(
|
|
|
73
67
|
): Record<string, string> | undefined {
|
|
74
68
|
if (versions_endpoint === undefined || versions_endpoint.trim() === '') return undefined
|
|
75
69
|
const endpoint = with_page_size(versions_endpoint, TIMES_PAGE_SIZE)
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
})
|
|
80
|
-
if (result.exitCode !== 0) return undefined
|
|
81
|
-
const parsed = release_age.release_times_schema.safeParse(safe_json_parse(result.stdout))
|
|
70
|
+
const stdout = git_gh_exec.read_gh_api_sync({ path: endpoint, jq_filter: TIMES_JQ })
|
|
71
|
+
if (stdout === undefined) return undefined
|
|
72
|
+
const parsed = release_age.release_times_schema.safeParse(safe_json_parse(stdout))
|
|
82
73
|
|
|
83
74
|
return parsed.success ? parsed.data : undefined
|
|
84
75
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
1
|
import path from 'node:path'
|
|
2
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
3
3
|
import { package_version_schema, pnpm_ls_global_schema } from '#scripts/lib/schemas'
|
|
4
4
|
import { execaSync } from 'execa'
|
|
5
5
|
import { safe_json_parse } from './parse-json'
|
|
@@ -42,7 +42,7 @@ function project_package_path(cwd: string, package_name: string): string {
|
|
|
42
42
|
|
|
43
43
|
function read_project_version(cwd: string, package_name: string): string | undefined {
|
|
44
44
|
const package_path = project_package_path(cwd, package_name)
|
|
45
|
-
const raw =
|
|
45
|
+
const raw = file_reader.read_optional(package_path)
|
|
46
46
|
|
|
47
47
|
return parse_project_version(raw)
|
|
48
48
|
}
|
|
@@ -52,7 +52,7 @@ function read_project_version(cwd: string, package_name: string): string | undef
|
|
|
52
52
|
// package version under node_modules.
|
|
53
53
|
function read_workspace_version(cwd: string): string | undefined {
|
|
54
54
|
const package_path = path.join(cwd, PACKAGE_JSON)
|
|
55
|
-
const raw =
|
|
55
|
+
const raw = file_reader.read_optional(package_path)
|
|
56
56
|
|
|
57
57
|
return parse_project_version(raw)
|
|
58
58
|
}
|