@joshuafolkken/kit 1.1010.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 -56
- 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 +31 -5
- 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 +2 -5
- 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
package/scripts/git/main-sync.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { composite_arguments, USAGE_ERROR_EXIT_CODE } from '#scripts/josh/josh-composite-arguments'
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
import { git_command } from './git-command'
|
|
5
6
|
import { gone_branch, type PruneResult } from './gone-branch'
|
|
6
7
|
|
|
@@ -65,7 +66,7 @@ async function prune_quietly(default_branch: string): Promise<void> {
|
|
|
65
66
|
try {
|
|
66
67
|
report_prune(await gone_branch.prune(default_branch))
|
|
67
68
|
} catch (error) {
|
|
68
|
-
console.error(`branch prune skipped: ${
|
|
69
|
+
console.error(`branch prune skipped: ${error_text.message_of(error)}`)
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
@@ -104,7 +105,7 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
|
104
105
|
try {
|
|
105
106
|
return await dispatch(argv)
|
|
106
107
|
} catch (error) {
|
|
107
|
-
console.error(
|
|
108
|
+
console.error(error_text.message_of(error))
|
|
108
109
|
|
|
109
110
|
return FAILURE_EXIT_CODE
|
|
110
111
|
}
|
package/scripts/git/schemas.ts
CHANGED
|
@@ -111,7 +111,7 @@ const rest_comment_schema = z.looseObject({
|
|
|
111
111
|
// Labels come back as objects, so the name is picked out here rather than at every call site.
|
|
112
112
|
const issue_label_schema = z.object({ name: z.string() })
|
|
113
113
|
|
|
114
|
-
// `state` is optional because most readers ignore it — only `
|
|
114
|
+
// `state` is optional because most readers ignore it — only `epic-add.ts` consults it, to refuse
|
|
115
115
|
// adding a child to a closed epic (joshuafolkken/kit#2337). A reader that never asks the state field
|
|
116
116
|
// still parses cleanly.
|
|
117
117
|
const epic_subject_schema = z.object({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
2
2
|
|
|
3
3
|
// `josh stash:pop` turns its `argv` into a single request: the message of the stash to pop. Parsing
|
|
4
4
|
// lives here, acting on the stack lives in `stash-pop-cli.ts`, and a malformed command line is
|
|
@@ -21,18 +21,12 @@ interface Request {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
return { values: parsed.values, positionals: parsed.positionals }
|
|
33
|
-
} catch {
|
|
34
|
-
return undefined
|
|
35
|
-
}
|
|
24
|
+
return cli_flags.parse_or_undefined({
|
|
25
|
+
args: [...argv],
|
|
26
|
+
options: OPTIONS,
|
|
27
|
+
strict: true,
|
|
28
|
+
allowPositionals: true,
|
|
29
|
+
})
|
|
36
30
|
}
|
|
37
31
|
|
|
38
32
|
// The message is the sole positional and must be a non-empty string: an empty message matches every
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url'
|
|
2
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
2
3
|
import { git_stash } from './git-stash'
|
|
3
4
|
import { stash_pop_args, type Request } from './stash-pop-args'
|
|
4
5
|
|
|
@@ -66,7 +67,7 @@ async function answer(request: Request): Promise<number> {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
function report_unknown(error: unknown): number {
|
|
69
|
-
console.error(
|
|
70
|
+
console.error(error_text.message_of(error))
|
|
70
71
|
|
|
71
72
|
return FAILURE_EXIT_CODE
|
|
72
73
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { cli_body } from '#scripts/josh/cli-body'
|
|
2
|
+
import type { TelegramSendInput, TelegramTaskType } from './telegram-notify'
|
|
3
3
|
|
|
4
4
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
5
5
|
interface CliValues {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { parseArgs } from 'node:util'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
4
|
+
import { josh_environment_file } from '#scripts/josh/josh-environment-file'
|
|
5
|
+
import { git_error } from './git-error'
|
|
6
|
+
import { git_gh_issue_read } from './git-gh-issue-read'
|
|
7
|
+
import { git_gh_repo } from './git-gh-repo'
|
|
8
|
+
import { github_issue_url, type IssueUrlTarget } from './github-issue-url'
|
|
9
|
+
import { telegram_notify } from './telegram-notify'
|
|
10
10
|
import { telegram_test_logic, type CliValues, type ResolvedContext } from './telegram-test-logic'
|
|
11
11
|
|
|
12
12
|
const REPO_NAME_SEPARATOR = '/'
|
|
@@ -97,7 +97,7 @@ async function resolve_context(values: CliValues): Promise<ResolvedContext> {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
async function main(): Promise<void> {
|
|
100
|
-
|
|
100
|
+
josh_environment_file.load_environment_file()
|
|
101
101
|
const values = parse_cli_arguments()
|
|
102
102
|
const context = await resolve_context(values)
|
|
103
103
|
const input = telegram_test_logic.build_input({ values, context })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
2
2
|
|
|
3
3
|
// The rewrite half of the edit hook (joshuafolkken/kit#2314). The format hook runs prettier and
|
|
4
4
|
// `eslint --fix` in place after every edit, so a file the model just wrote can be rewritten out from
|
|
@@ -85,13 +85,7 @@ function build(before: string, after: string): string | undefined {
|
|
|
85
85
|
// handed nothing for, or one gone by the time it reads, compares as an empty string rather than
|
|
86
86
|
// throwing — the hook runs after the write already succeeded and must never turn it into a failure.
|
|
87
87
|
function read_text(file_path: string | undefined): string {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
return readFileSync(file_path, 'utf8')
|
|
92
|
-
} catch {
|
|
93
|
-
return ''
|
|
94
|
-
}
|
|
88
|
+
return file_path === undefined ? '' : (file_reader.read_if_readable(file_path) ?? '')
|
|
95
89
|
}
|
|
96
90
|
|
|
97
91
|
const rewrite_notice = { build, read_text }
|
|
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { is_workflow_destination } from '#scripts/claude/workflow-destination'
|
|
4
4
|
import { gh_spawn } from '#scripts/gh/gh-spawn'
|
|
5
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
5
6
|
import { managed_marker_logic } from '#scripts/managed-marker/managed-marker-logic'
|
|
6
7
|
import {
|
|
7
8
|
classify_path,
|
|
@@ -238,11 +239,7 @@ function did_skip_ai_directory_copy(directory_name: string): boolean {
|
|
|
238
239
|
// moved onto the basic path — only that kit-written line, never the consumer's additions.
|
|
239
240
|
// An unreadable file is left as it is and reported as skipped, as it was before the migration.
|
|
240
241
|
function read_if_readable(destination_path: string): string | undefined {
|
|
241
|
-
|
|
242
|
-
return readFileSync(destination_path, 'utf8')
|
|
243
|
-
} catch {
|
|
244
|
-
return undefined
|
|
245
|
-
}
|
|
242
|
+
return file_reader.read_if_readable(destination_path)
|
|
246
243
|
}
|
|
247
244
|
|
|
248
245
|
function report_existing_claude_md(destination_path: string): void {
|
package/scripts/init/init.ts
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'node:path'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { resolve_local_bin } from '#scripts/build/local-bin'
|
|
6
6
|
import { doctor_io } from '#scripts/doctor/doctor-io'
|
|
7
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
7
8
|
import { package_version_schema, with_package_manager_schema } from '#scripts/lib/schemas'
|
|
8
9
|
import { auto_merge_setting } from '#scripts/repo/auto-merge-setting'
|
|
9
10
|
import { project_config } from '#scripts/safe-chain/project-config'
|
|
@@ -45,8 +46,11 @@ function show_sample(action: FileAction): void {
|
|
|
45
46
|
console.info(action.create().replaceAll(/^/gmu, () => SAMPLE_INDENT))
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
// A file that already holds the sample needs nothing added, so only one that differs gets it shown.
|
|
50
|
+
function report_unchanged(action: FileAction, existing: string): void {
|
|
51
|
+
const is_sample_missing = existing !== action.create()
|
|
52
|
+
|
|
53
|
+
if (is_sample_missing && action.should_show_sample_when_unchanged === true) show_sample(action)
|
|
50
54
|
else console.info(` ✔ unchanged ${action.dest}`)
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -59,7 +63,7 @@ function merge_existing_file(
|
|
|
59
63
|
const merged = merge_function(existing)
|
|
60
64
|
|
|
61
65
|
if (merged === existing) {
|
|
62
|
-
report_unchanged(action)
|
|
66
|
+
report_unchanged(action, existing)
|
|
63
67
|
|
|
64
68
|
return
|
|
65
69
|
}
|
|
@@ -375,7 +379,7 @@ function run_cli(args: ReadonlyArray<string>): void {
|
|
|
375
379
|
try {
|
|
376
380
|
if (main(args)) print_start_hint()
|
|
377
381
|
} catch (error) {
|
|
378
|
-
console.error(`\n✖ ${
|
|
382
|
+
console.error(`\n✖ ${error_text.message_of(error)}\n`)
|
|
379
383
|
process.exitCode = 1
|
|
380
384
|
}
|
|
381
385
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
2
3
|
import { repository_labels } from '#scripts/repo/repository-labels'
|
|
3
4
|
import { self_sync_guard } from '#scripts/self-sync-guard/self-sync-guard-logic'
|
|
4
5
|
import { main as init_main } from './init'
|
|
@@ -119,7 +120,7 @@ async function run_step(
|
|
|
119
120
|
try {
|
|
120
121
|
await STEP_ACTIONS[step](context)
|
|
121
122
|
} catch (error) {
|
|
122
|
-
const cause =
|
|
123
|
+
const cause = error_text.message_of(error)
|
|
123
124
|
|
|
124
125
|
throw new Error(start_plan.progress_report(step, completed, cause), { cause: error })
|
|
125
126
|
}
|
package/scripts/init/start.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
3
4
|
import { project_profile, type ProjectProfile } from './project-profile'
|
|
4
5
|
import {
|
|
5
6
|
start_plan,
|
|
@@ -96,7 +97,7 @@ async function run(args: ReadonlyArray<string>, is_tty: boolean): Promise<number
|
|
|
96
97
|
|
|
97
98
|
return 0
|
|
98
99
|
} catch (error) {
|
|
99
|
-
console.error(
|
|
100
|
+
console.error(error_text.message_of(error))
|
|
100
101
|
|
|
101
102
|
return 1
|
|
102
103
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
4
4
|
import { cli_body } from '#scripts/josh/cli-body'
|
|
5
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
5
6
|
|
|
6
7
|
// `josh issue:comment <N> --body <text> | --body-file <path>` — post one comment to an issue (a PR
|
|
7
8
|
// comment is an issue comment over REST) and print the comment URL (joshuafolkken/kit#2304).
|
|
@@ -66,7 +67,7 @@ function resolve_request(argv: ReadonlyArray<string>): CommentRequest | undefine
|
|
|
66
67
|
|
|
67
68
|
return request
|
|
68
69
|
} catch (error) {
|
|
69
|
-
console.error(
|
|
70
|
+
console.error(error_text.message_of(error))
|
|
70
71
|
|
|
71
72
|
return undefined
|
|
72
73
|
}
|
|
@@ -7,6 +7,7 @@ import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
|
7
7
|
import { git_gh_exec } from '#scripts/git/git-gh-exec'
|
|
8
8
|
import { git_gh_issue_write } from '#scripts/git/git-gh-issue-write'
|
|
9
9
|
import { github_issue_url } from '#scripts/git/github-issue-url'
|
|
10
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
10
11
|
import { issue_file, type FileArguments } from './issue-file'
|
|
11
12
|
import { issue_lint_cli } from './issue-lint-cli'
|
|
12
13
|
import { issue_scout_cli } from './issue-scout-cli'
|
|
@@ -105,7 +106,7 @@ async function create(filing: Filing): Promise<string | undefined> {
|
|
|
105
106
|
try {
|
|
106
107
|
return await git_gh_exec.exec_gh_api(request)
|
|
107
108
|
} catch (error) {
|
|
108
|
-
const reason =
|
|
109
|
+
const reason = error_text.message_of(error)
|
|
109
110
|
|
|
110
111
|
console.error(`✖ the create call failed: ${reason}`)
|
|
111
112
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import { parseArgs } from 'node:util'
|
|
4
3
|
import { git_command } from '#scripts/git/git-command'
|
|
5
4
|
import { path_decision } from '#scripts/josh/path-decision'
|
|
5
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
6
6
|
import { split_assess, type SplitVerdict } from '#scripts/split/split-assess'
|
|
7
7
|
import { issue_fold, type FoldVerdict } from './issue-fold'
|
|
8
8
|
|
|
@@ -33,27 +33,20 @@ interface FoldArguments {
|
|
|
33
33
|
is_json: boolean
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// An unknown flag is `undefined` rather than a throw, so the answer is the usage line rather than a
|
|
37
|
+
// stack trace, on a command whose output a workflow reads.
|
|
38
|
+
function read_arguments(argv: ReadonlyArray<string>): FoldArguments | undefined {
|
|
39
|
+
const parsed = cli_flags.parse_or_undefined({
|
|
38
40
|
args: [...argv],
|
|
39
41
|
options: { json: { type: 'boolean' }, 'not-separable': { type: 'boolean' } },
|
|
40
42
|
allowPositionals: true,
|
|
41
43
|
})
|
|
44
|
+
if (parsed === undefined) return undefined
|
|
42
45
|
|
|
43
46
|
return {
|
|
44
|
-
titles: positionals,
|
|
45
|
-
is_separable: values['not-separable'] !== true,
|
|
46
|
-
is_json: values.json === true,
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// An unknown flag makes `parseArgs` throw. Caught so the answer is the usage line rather than a stack
|
|
51
|
-
// trace, on a command whose output a workflow reads.
|
|
52
|
-
function read_arguments(argv: ReadonlyArray<string>): FoldArguments | undefined {
|
|
53
|
-
try {
|
|
54
|
-
return parse_arguments(argv)
|
|
55
|
-
} catch {
|
|
56
|
-
return undefined
|
|
47
|
+
titles: parsed.positionals,
|
|
48
|
+
is_separable: parsed.values['not-separable'] !== true,
|
|
49
|
+
is_json: parsed.values.json === true,
|
|
57
50
|
}
|
|
58
51
|
}
|
|
59
52
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import { parseArgs } from 'node:util'
|
|
5
4
|
import { epic_audit_logic } from '#scripts/epic/epic-audit'
|
|
6
5
|
import { epic_bundle, type BacklogIssue, type BundleDecision } from '#scripts/epic/epic-bundle'
|
|
7
6
|
import { epic_bundle_cli } from '#scripts/epic/epic-bundle-cli'
|
|
8
7
|
import { epic_bundle_gaps } from '#scripts/epic/epic-bundle-gaps'
|
|
9
8
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
9
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
10
10
|
import { issue_citation } from '#scripts/rules/issue-citation'
|
|
11
11
|
import {
|
|
12
12
|
issue_scout,
|
|
@@ -59,12 +59,17 @@ function has_invalid_arguments(
|
|
|
59
59
|
)
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
// An unknown flag is `undefined` rather than a throw, so the answer is the usage line rather than a
|
|
63
|
+
// stack trace, on a command whose output a workflow reads.
|
|
64
|
+
function read_arguments(argv: ReadonlyArray<string>): ScoutArguments | undefined {
|
|
65
|
+
const parsed = cli_flags.parse_or_undefined({
|
|
64
66
|
args: [...argv],
|
|
65
67
|
options: { body: { type: 'string' }, 'body-file': { type: 'string' } },
|
|
66
68
|
allowPositionals: true,
|
|
67
69
|
})
|
|
70
|
+
if (parsed === undefined) return undefined
|
|
71
|
+
|
|
72
|
+
const { values, positionals } = parsed
|
|
68
73
|
const [title] = positionals
|
|
69
74
|
|
|
70
75
|
if (has_invalid_arguments(title, values.body, values['body-file'])) return undefined
|
|
@@ -83,16 +88,6 @@ async function with_draft_body(args: ScoutArguments): Promise<ScoutArguments | u
|
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
// An unknown flag makes `parseArgs` throw. Caught so the answer is the usage line rather than a stack
|
|
87
|
-
// trace, on a command whose output a workflow reads.
|
|
88
|
-
function read_arguments(argv: ReadonlyArray<string>): ScoutArguments | undefined {
|
|
89
|
-
try {
|
|
90
|
-
return parse_arguments(argv)
|
|
91
|
-
} catch {
|
|
92
|
-
return undefined
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
91
|
// The issue as it would be filed. The summary is carried as the body because that is what the epic
|
|
97
92
|
// half reads: the issue numbers its prose names are the references `decide_bundle` decides from.
|
|
98
93
|
function draft_of(args: ScoutArguments, repo: string): BacklogIssue {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { execSync } from 'node:child_process'
|
|
3
3
|
import { readFile } from 'node:fs/promises'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
5
6
|
import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
|
|
6
7
|
import { baseline_measure, type Baseline } from './baseline-measure'
|
|
7
8
|
|
|
@@ -21,7 +22,7 @@ function measure(command: string): string {
|
|
|
21
22
|
try {
|
|
22
23
|
return execSync(command, { encoding: 'utf8' }).trim()
|
|
23
24
|
} catch (error) {
|
|
24
|
-
return `(command failed: ${
|
|
25
|
+
return `(command failed: ${error_text.message_of(error)})`
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -40,12 +41,12 @@ function rerun_one(baseline: Baseline, date: string): Outcome {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
async function append_ledger(lines: ReadonlyArray<string
|
|
44
|
+
async function append_ledger(lines: ReadonlyArray<string>, now: Date): Promise<void> {
|
|
44
45
|
if (lines.length === 0) return
|
|
45
46
|
|
|
46
|
-
// The
|
|
47
|
-
// (joshuafolkken/kit#
|
|
48
|
-
const ledger_path = observation_ledger_home.
|
|
47
|
+
// The running work tree's file for the checked-out issue, a lane's inside a lane — the run's own
|
|
48
|
+
// commit carries it (joshuafolkken/kit#2919).
|
|
49
|
+
const ledger_path = await observation_ledger_home.writer_path(now)
|
|
49
50
|
|
|
50
51
|
await observation_ledger_home.append(ledger_path, lines)
|
|
51
52
|
console.info(`Recorded ${String(lines.length)} refuted premise(s) in ${ledger_path}.`)
|
|
@@ -70,7 +71,7 @@ async function rerun(body_path: string, now: Date): Promise<number> {
|
|
|
70
71
|
.filter((line): line is string => line !== undefined)
|
|
71
72
|
|
|
72
73
|
console.info(outcomes.map((outcome) => outcome.pair).join('\n\n'))
|
|
73
|
-
await append_ledger(ledger_lines)
|
|
74
|
+
await append_ledger(ledger_lines, now)
|
|
74
75
|
|
|
75
76
|
return 0
|
|
76
77
|
}
|
|
@@ -27,7 +27,7 @@ const AI_COMMANDS: Record<string, CommandEntry> = {
|
|
|
27
27
|
reference: ['<message>', 'automation', ['git']],
|
|
28
28
|
},
|
|
29
29
|
epic: {
|
|
30
|
-
script: 'scripts
|
|
30
|
+
script: 'scripts/epic/epic.ts',
|
|
31
31
|
description: 'Create an epic issue from its child issue numbers',
|
|
32
32
|
category: 'AI tools',
|
|
33
33
|
reference: ['<title> <issue...> [--ordered]', 'automation', ['network']],
|
|
@@ -51,7 +51,7 @@ const AI_COMMANDS: Record<string, CommandEntry> = {
|
|
|
51
51
|
reference: ['<epic>', 'automation', ['network']],
|
|
52
52
|
},
|
|
53
53
|
'epic:check': {
|
|
54
|
-
script: 'scripts
|
|
54
|
+
script: 'scripts/epic/epic-check.ts',
|
|
55
55
|
description: 'Check an epic issue against the tracking requirements',
|
|
56
56
|
category: 'AI tools',
|
|
57
57
|
reference: ['<epic>', 'automation', ['network']],
|
|
@@ -19,7 +19,7 @@ const BACKLOG_COMMANDS: Record<string, CommandEntry> = {
|
|
|
19
19
|
description:
|
|
20
20
|
'Collapse a backlogrun loop-head event into one call: read backlog:next, ask backlog:budget, return the verdict and any issues to start',
|
|
21
21
|
category: 'AI tools',
|
|
22
|
-
reference: ['[options]', 'automation', ['network']],
|
|
22
|
+
reference: ['[options]', 'automation', ['files', 'network', 'processes']],
|
|
23
23
|
},
|
|
24
24
|
'backlog:drive': {
|
|
25
25
|
script: 'scripts/backlog/backlog-drive-cli.ts',
|
|
@@ -57,7 +57,7 @@ const DEV_COMMANDS: Record<string, CommandEntry> = {
|
|
|
57
57
|
description:
|
|
58
58
|
"Print each delivered rule's unaided compliance — runs reached, kept rate, refusals",
|
|
59
59
|
category: 'AI tools',
|
|
60
|
-
reference: ['', 'developer', ['
|
|
60
|
+
reference: ['[--refresh]', 'developer', ['files']],
|
|
61
61
|
},
|
|
62
62
|
format: {
|
|
63
63
|
script: 'scripts/lint/format.ts',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OPTIONAL_ENV_FILE_FLAGS, type CommandEntry } from './josh-command-types'
|
|
2
2
|
|
|
3
|
-
const GIT_WORKFLOW_SCRIPT = 'scripts
|
|
3
|
+
const GIT_WORKFLOW_SCRIPT = 'scripts/git/git-workflow.ts'
|
|
4
4
|
const GIT_MESSAGE_ARGUMENTS = '[-y] <message>'
|
|
5
5
|
|
|
6
6
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
@@ -27,7 +27,7 @@ const WORKFLOW_COMMANDS: Record<string, CommandEntry> = {
|
|
|
27
27
|
// session, and `followup` with them the merge step of every `fullrun` there. `doctor` was moved to
|
|
28
28
|
// this form for the same reason in joshuafolkken/kit#869.
|
|
29
29
|
//
|
|
30
|
-
// Nothing is lost by relaxing it: both scripts already
|
|
30
|
+
// Nothing is lost by relaxing it: both scripts already load `.env` through `josh_environment_file`
|
|
31
31
|
// themselves, so the file is read either way and node's precedence — an existing environment
|
|
32
32
|
// variable wins over the file — is node's own in both spellings.
|
|
33
33
|
//
|
|
@@ -35,7 +35,7 @@ const WORKFLOW_COMMANDS: Record<string, CommandEntry> = {
|
|
|
35
35
|
// than dropped: `telegram_notify.send` now throws on missing credentials and on a failed send, so
|
|
36
36
|
// `josh notify` exits non-zero instead of warning and answering 0.
|
|
37
37
|
followup: {
|
|
38
|
-
script: 'scripts
|
|
38
|
+
script: 'scripts/git/git-followup-workflow.ts',
|
|
39
39
|
description: 'Follow-up git workflow',
|
|
40
40
|
category: 'Workflow',
|
|
41
41
|
reference: [
|
|
@@ -46,7 +46,7 @@ const WORKFLOW_COMMANDS: Record<string, CommandEntry> = {
|
|
|
46
46
|
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
47
47
|
},
|
|
48
48
|
notify: {
|
|
49
|
-
script: 'scripts
|
|
49
|
+
script: 'scripts/git/telegram-test.ts',
|
|
50
50
|
description: 'Send Telegram notification',
|
|
51
51
|
category: 'Workflow',
|
|
52
52
|
reference: ['--task-type <type> --body <text>', 'automation', ['notifications']],
|
package/scripts/lane/lane-cli.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { epic_busy } from '#scripts/epic/epic-busy'
|
|
4
4
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
5
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
5
6
|
import { run_carry } from '#scripts/run/run-carry'
|
|
6
7
|
import { lane_await } from './lane-await'
|
|
7
8
|
import { lane_close, type CloseOutcome, type SweepOutcome } from './lane-close'
|
|
@@ -384,7 +385,7 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
|
384
385
|
try {
|
|
385
386
|
return await dispatch(argv)
|
|
386
387
|
} catch (error) {
|
|
387
|
-
console.error(
|
|
388
|
+
console.error(error_text.message_of(error))
|
|
388
389
|
|
|
389
390
|
return FAILURE_EXIT_CODE
|
|
390
391
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { ENV_FILE_NAME } from '#ports'
|
|
4
4
|
import { git_worktree } from '#scripts/git/git-worktree'
|
|
5
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
5
6
|
import { lane_cache } from './lane-cache'
|
|
6
7
|
import { lane_environment } from './lane-environment'
|
|
7
8
|
import { lane_install, type InstallResult } from './lane-install'
|
|
@@ -52,7 +53,7 @@ const SEAT_LOCK_PREFIX = 'seat-'
|
|
|
52
53
|
function read_root_environment(repository_root: string): string {
|
|
53
54
|
const file = path.join(repository_root, ENV_FILE_NAME)
|
|
54
55
|
|
|
55
|
-
return
|
|
56
|
+
return file_reader.read_file_or_empty(file)
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function build_lane(
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { existsSync
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { ENV_FILE_NAME } from '#ports'
|
|
4
4
|
import type { AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
5
5
|
import { repo_discovery } from '#scripts/discovery/repo-discovery'
|
|
6
6
|
import { git_command } from '#scripts/git/git-command'
|
|
7
7
|
import { git_worktree } from '#scripts/git/git-worktree'
|
|
8
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
8
9
|
import { lane_environment } from './lane-environment'
|
|
9
10
|
import { lane_paths } from './lane-paths'
|
|
10
11
|
|
|
@@ -105,11 +106,7 @@ function held_seat(content: string | undefined): SeatPorts {
|
|
|
105
106
|
* a state the report shows rather than a state anything falls back from.
|
|
106
107
|
*/
|
|
107
108
|
function read_environment(directory: string): string | undefined {
|
|
108
|
-
|
|
109
|
-
return readFileSync(path.join(directory, ENV_FILE_NAME), 'utf8')
|
|
110
|
-
} catch {
|
|
111
|
-
return undefined
|
|
112
|
-
}
|
|
109
|
+
return file_reader.read_if_readable(path.join(directory, ENV_FILE_NAME))
|
|
113
110
|
}
|
|
114
111
|
|
|
115
112
|
// A lane is identified by its branch rather than by where it sits, so nothing here compares two
|
package/scripts/lib/cli-flags.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { parseArgs, type ParseArgsConfig } from 'node:util'
|
|
2
|
+
|
|
3
|
+
// Argument reading for the commands under `scripts/`.
|
|
2
4
|
//
|
|
3
5
|
// Reject anything not on the list rather than ignoring it: a misspelled `--dryrun` that fell through
|
|
4
6
|
// would run the real write path. `josh propagate` and `josh adopt` both write into working trees, so
|
|
@@ -16,4 +18,20 @@ function refuse_unknown_flags(
|
|
|
16
18
|
return `Unknown argument(s): ${unknown.join(' ')}\n${usage}`
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
// `parseArgs`, answering `undefined` where it would throw — an unknown flag, a missing option value,
|
|
22
|
+
// or a positional the config does not allow. Each caller turns that into its usage line and a failure
|
|
23
|
+
// exit rather than a stack trace, on commands whose output a workflow reads; eleven of them had begun
|
|
24
|
+
// to carry a copy of the same try/catch each (joshuafolkken/kit#2902), and this is the one copy.
|
|
25
|
+
function parse_or_undefined<T extends ParseArgsConfig>(
|
|
26
|
+
config: T,
|
|
27
|
+
): ReturnType<typeof parseArgs<T>> | undefined {
|
|
28
|
+
try {
|
|
29
|
+
return parseArgs(config)
|
|
30
|
+
} catch {
|
|
31
|
+
return undefined
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const cli_flags = { refuse_unknown_flags, parse_or_undefined }
|
|
36
|
+
|
|
37
|
+
export { cli_flags }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// The one place a caught value becomes the text a report shows: an `Error` contributes its own message,
|
|
2
|
+
// anything else thrown is stringified as it stands.
|
|
3
|
+
function message_of(error: unknown): string {
|
|
4
|
+
return error instanceof Error ? error.message : String(error)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const error_text = { message_of }
|
|
8
|
+
|
|
9
|
+
export { error_text }
|
package/scripts/lib/read-file.ts
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
2
|
|
|
3
|
+
const ABSENT_CODE = 'ENOENT'
|
|
4
|
+
|
|
5
|
+
function is_absent(error: unknown): boolean {
|
|
6
|
+
return error instanceof Error && 'code' in error && error.code === ABSENT_CODE
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// An absent file is an answer ("nothing declared here"); any other failure — a permission error, a
|
|
10
|
+
// directory where a file was expected — is not, and is thrown so it cannot pass for an empty file.
|
|
11
|
+
function read_optional(path: string): string | undefined {
|
|
12
|
+
try {
|
|
13
|
+
return readFileSync(path, 'utf8')
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (is_absent(error)) return undefined
|
|
16
|
+
|
|
17
|
+
throw error
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
3
21
|
// Several readers treat an absent file as "nothing declared" rather than an error: a project may
|
|
4
22
|
// carry no lockfile yet, and either overrides location may be missing entirely. Empty content is
|
|
5
23
|
// the shared stand-in, so the callers do not each re-implement the try/catch.
|
|
6
24
|
function read_file_or_empty(path: string): string {
|
|
25
|
+
return read_optional(path) ?? ''
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// For a reader whose contract is to never fail — a hook running after a write already succeeded, a
|
|
29
|
+
// report that states "unreadable" itself — every failure to read, not only absence, is `undefined`.
|
|
30
|
+
function read_if_readable(path: string): string | undefined {
|
|
7
31
|
try {
|
|
8
32
|
return readFileSync(path, 'utf8')
|
|
9
33
|
} catch {
|
|
10
|
-
return
|
|
34
|
+
return undefined
|
|
11
35
|
}
|
|
12
36
|
}
|
|
13
37
|
|
|
14
|
-
const file_reader = { read_file_or_empty }
|
|
38
|
+
const file_reader = { read_optional, read_file_or_empty, read_if_readable }
|
|
15
39
|
|
|
16
40
|
export { file_reader }
|