@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,6 +1,6 @@
|
|
|
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
4
|
import { z } from 'zod'
|
|
5
5
|
import {
|
|
6
6
|
package_scout,
|
|
@@ -120,27 +120,19 @@ function parse_size(raw: string | undefined): number {
|
|
|
120
120
|
return raw === undefined || !Number.isSafeInteger(parsed) || parsed <= 0 ? DEFAULT_SIZE : parsed
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
// An unknown flag is `undefined` rather than a throw, so the answer is the usage line rather than a
|
|
124
|
+
// stack trace, on a command whose output a workflow reads.
|
|
125
|
+
function read_arguments(argv: ReadonlyArray<string>): ScoutArguments | undefined {
|
|
126
|
+
const parsed = cli_flags.parse_or_undefined({
|
|
125
127
|
args: [...argv],
|
|
126
128
|
options: { size: { type: 'string' } },
|
|
127
129
|
allowPositionals: true,
|
|
128
130
|
})
|
|
129
|
-
const keywords = positionals.join(' ').trim()
|
|
130
|
-
|
|
131
|
-
if (keywords === '') return undefined
|
|
131
|
+
const keywords = parsed?.positionals.join(' ').trim() ?? ''
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
}
|
|
133
|
+
if (parsed === undefined || keywords === '') return undefined
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
// trace, on a command whose output a workflow reads.
|
|
138
|
-
function read_arguments(argv: ReadonlyArray<string>): ScoutArguments | undefined {
|
|
139
|
-
try {
|
|
140
|
-
return parse_arguments(argv)
|
|
141
|
-
} catch {
|
|
142
|
-
return undefined
|
|
143
|
-
}
|
|
135
|
+
return { keywords, size: parse_size(parsed.values.size) }
|
|
144
136
|
}
|
|
145
137
|
|
|
146
138
|
async function report(args: ScoutArguments): Promise<number> {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { ports, type PortEnvironment } from '#ports'
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
|
|
5
6
|
const SUCCESS_EXIT_CODE = 0
|
|
6
7
|
const USAGE_EXIT_CODE = 1
|
|
@@ -49,7 +50,7 @@ function run(argv: ReadonlyArray<string>, environment?: PortEnvironment): PortRe
|
|
|
49
50
|
return { text: String(RESOLVERS[name](environment)), exit_code: SUCCESS_EXIT_CODE }
|
|
50
51
|
} catch (error) {
|
|
51
52
|
return {
|
|
52
|
-
text:
|
|
53
|
+
text: error_text.message_of(error),
|
|
53
54
|
exit_code: FAILURE_EXIT_CODE,
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { git_gh_exec } from '#scripts/git/git-gh-exec'
|
|
2
2
|
import { poll } from '#scripts/lib/poll'
|
|
3
3
|
import { with_page_size } from '#scripts/version/version-remote'
|
|
4
|
-
import { execaSync } from 'execa'
|
|
5
4
|
|
|
6
5
|
// Waiting for a specific version of this package to actually exist in the registry.
|
|
7
6
|
//
|
|
@@ -58,15 +57,15 @@ function finish(state: PublishWaitState, version: string, attempts: number): Pub
|
|
|
58
57
|
// reported apart, because one means wait longer and the other means something is broken.
|
|
59
58
|
function fetch_published_versions(versions_endpoint: string): Array<string> | undefined {
|
|
60
59
|
const endpoint = with_page_size(versions_endpoint, VERSIONS_PAGE_SIZE)
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
const stdout = git_gh_exec.read_gh_api_sync({
|
|
61
|
+
path: endpoint,
|
|
62
|
+
jq_filter: NAMES_JQ,
|
|
63
|
+
timeout_ms: GH_TIMEOUT_MS,
|
|
65
64
|
})
|
|
66
|
-
if (
|
|
65
|
+
if (stdout === undefined) return undefined
|
|
67
66
|
|
|
68
67
|
try {
|
|
69
|
-
const parsed: unknown = JSON.parse(
|
|
68
|
+
const parsed: unknown = JSON.parse(stdout)
|
|
70
69
|
|
|
71
70
|
return Array.isArray(parsed) ? parsed.filter((name) => typeof name === 'string') : undefined
|
|
72
71
|
} catch {
|
|
@@ -3,6 +3,7 @@ import { git_gh_exec } from '#scripts/git/git-gh-exec'
|
|
|
3
3
|
import { git_gh_issue_write } from '#scripts/git/git-gh-issue-write'
|
|
4
4
|
import { OTHER_CHANGE_LABEL } from '#scripts/git/issue-labels'
|
|
5
5
|
import { GATE_COMMAND } from '#scripts/josh/josh-command-types'
|
|
6
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
6
7
|
import { build_upgrade_shell_command } from '#scripts/version/upgrade-shell-command'
|
|
7
8
|
import { create_version_command_config } from '#scripts/version/version-command-config'
|
|
8
9
|
import { execaSync } from 'execa'
|
|
@@ -235,7 +236,7 @@ function precheck_step(target: PropagateTarget, step: string): StepResult {
|
|
|
235
236
|
// multi-line detail would print raw JSON as an unindented second line and glue that warning to the
|
|
236
237
|
// end of it, where nobody reading the report would see it.
|
|
237
238
|
function to_issue_detail(error: unknown): string {
|
|
238
|
-
const message =
|
|
239
|
+
const message = error_text.message_of(error)
|
|
239
240
|
|
|
240
241
|
return message.replaceAll('\n', ' ').trim()
|
|
241
242
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { repo_discovery } from '#scripts/discovery/repo-discovery'
|
|
4
4
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
5
|
-
import {
|
|
5
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
6
6
|
import { status_icons } from '#scripts/lib/status-icons'
|
|
7
7
|
import { self_sync_guard } from '#scripts/self-sync-guard/self-sync-guard-logic'
|
|
8
8
|
import { KIT_PACKAGE_NAME } from '#scripts/version/kit-descriptor'
|
|
@@ -72,7 +72,8 @@ function parse_options(argv: ReadonlyArray<string>): RunOptions {
|
|
|
72
72
|
...(target !== undefined && { target }),
|
|
73
73
|
}
|
|
74
74
|
const usage =
|
|
75
|
-
target_usage ??
|
|
75
|
+
target_usage ??
|
|
76
|
+
cli_flags.refuse_unknown_flags(rest, [...KNOWN_FLAGS, TARGET_USAGE], 'propagate')
|
|
76
77
|
|
|
77
78
|
return usage === undefined ? options : { ...options, usage }
|
|
78
79
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
1
|
import path from 'node:path'
|
|
2
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
3
3
|
|
|
4
4
|
// The scope expansion `prompts/refactoring.md` §4.3 used to describe in prose (joshuafolkken/kit#2180):
|
|
5
5
|
// from the seed files, follow imports both ways — the files a seed imports and the files that import a
|
|
@@ -67,11 +67,7 @@ function resolve_spec(
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function read_source(file: string): string {
|
|
70
|
-
|
|
71
|
-
return readFileSync(file, 'utf8')
|
|
72
|
-
} catch {
|
|
73
|
-
return ''
|
|
74
|
-
}
|
|
70
|
+
return file_reader.read_if_readable(file) ?? ''
|
|
75
71
|
}
|
|
76
72
|
|
|
77
73
|
function file_imports(file: string, root: string, known: ReadonlySet<string>): ReadonlySet<string> {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
4
5
|
import { execa } from 'execa'
|
|
5
6
|
import { z } from 'zod'
|
|
6
7
|
import { migrate_logic, type MigrationPlan } from './migrate-logic'
|
|
@@ -70,7 +71,7 @@ interface MigrationContext {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
function read_file(file_path: string): string {
|
|
73
|
-
return
|
|
74
|
+
return file_reader.read_file_or_empty(file_path)
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
function has_registry_override(environment: NodeJS.ProcessEnv): boolean {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
1
|
import os from 'node:os'
|
|
3
2
|
import path from 'node:path'
|
|
3
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
4
4
|
|
|
5
5
|
const NPMRC = '.npmrc'
|
|
6
6
|
|
|
7
7
|
function read(home: string = os.homedir()): string {
|
|
8
8
|
const file = path.join(home, NPMRC)
|
|
9
9
|
|
|
10
|
-
return
|
|
10
|
+
return file_reader.read_file_or_empty(file)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const user_npmrc = { read }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { git_command } from '#scripts/git/git-command'
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
import { release_history } from './release-history'
|
|
5
6
|
import { release_plan, type ReleasePlan } from './release-plan'
|
|
6
7
|
import { release_publish } from './release-publish'
|
|
@@ -76,7 +77,7 @@ async function main(): Promise<void> {
|
|
|
76
77
|
try {
|
|
77
78
|
process.exit(await run(is_dry_run_requested(process.argv)))
|
|
78
79
|
} catch (error) {
|
|
79
|
-
console.error(
|
|
80
|
+
console.error(error_text.message_of(error))
|
|
80
81
|
process.exit(FAILURE_EXIT_CODE)
|
|
81
82
|
}
|
|
82
83
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { git_gh_exec } from '#scripts/git/git-gh-exec'
|
|
2
2
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
3
|
-
import { read_spawn_stdout } from '#scripts/lib/spawn-exit'
|
|
4
|
-
import { execaSync } from 'execa'
|
|
5
3
|
|
|
6
4
|
// The shared half of every "report a GitHub repository setting kit cannot write" check.
|
|
7
5
|
//
|
|
@@ -27,17 +25,21 @@ interface RepoApiResult {
|
|
|
27
25
|
stdout: string
|
|
28
26
|
}
|
|
29
27
|
|
|
28
|
+
const FAILED_EXIT_CODE = 1
|
|
29
|
+
|
|
30
30
|
// One `gh api` call. Never throws and never propagates a non-zero exit: an unreadable answer is a
|
|
31
|
-
// reported status, not a command failure.
|
|
31
|
+
// reported status, not a command failure. Every failure reads as `FAILED_EXIT_CODE` with no output,
|
|
32
|
+
// which `parse_payload` treats as unreadable whatever gh's own exit code was.
|
|
32
33
|
function query_repo_api(api_path: string): RepoApiResult {
|
|
33
|
-
const
|
|
34
|
-
|
|
34
|
+
const stdout = git_gh_exec.read_gh_api_sync({
|
|
35
|
+
path: api_path,
|
|
35
36
|
cwd: PROJECT_ROOT,
|
|
36
|
-
|
|
37
|
-
timeout: GH_TIMEOUT_MS,
|
|
37
|
+
timeout_ms: GH_TIMEOUT_MS,
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
return
|
|
40
|
+
return stdout === undefined
|
|
41
|
+
? { exit_code: FAILED_EXIT_CODE, stdout: '' }
|
|
42
|
+
: { exit_code: 0, stdout }
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
// The response body as an object, or nothing. A non-zero exit covers every case where the answer
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// The review-finding line's grammar, its category vocabulary and the aggregation over it — the single
|
|
2
2
|
// place all three are defined (joshuafolkken/kit#2325). A `/code-review` round's findings evaporated
|
|
3
3
|
// the moment they were fixed, so no one could say which category recurred. They now land in the same
|
|
4
|
-
// append-only ledger the observation lines use (`docs/maintainers/observations
|
|
5
|
-
// prefix so `observation-ledger-line.ts`'s `- k:` grammar never treats them as its own — one ledger
|
|
6
|
-
//
|
|
4
|
+
// append-only ledger the observation lines use (`docs/maintainers/observations/`), under a distinct `- rf:`
|
|
5
|
+
// prefix so `observation-ledger-line.ts`'s `- k:` grammar never treats them as its own — one ledger,
|
|
6
|
+
// one commit path, a second line *type* rather than a second ledger.
|
|
7
7
|
|
|
8
8
|
const FINDING_PREFIX = '- rf:'
|
|
9
9
|
const FIELD_SEPARATOR = ' | '
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
-
import { readFile } from 'node:fs/promises'
|
|
3
2
|
import { fileURLToPath } from 'node:url'
|
|
4
3
|
import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
|
|
5
4
|
import { review_finding_ledger, type CategoryCount } from './review-finding-ledger'
|
|
@@ -29,16 +28,10 @@ function format_report(counts: ReadonlyArray<CategoryCount>, zero_rounds: number
|
|
|
29
28
|
return `findings by category:\n${body}\n${zero_line(zero_rounds)}`
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return ''
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function run(ledger_path: string = observation_ledger_home.ledger_path()): Promise<number> {
|
|
41
|
-
const content = await read_ledger(ledger_path)
|
|
31
|
+
// Every issue's file of the ledger directory is counted (joshuafolkken/kit#2919): a recurring category
|
|
32
|
+
// is one whichever issue recorded each sighting.
|
|
33
|
+
async function run(root: string = observation_ledger_home.ledger_root()): Promise<number> {
|
|
34
|
+
const content = (await observation_ledger_home.read(root)) ?? ''
|
|
42
35
|
const counts = review_finding_ledger.category_counts(content)
|
|
43
36
|
|
|
44
37
|
console.info(format_report(counts, review_finding_ledger.zero_round_count(content)))
|
|
@@ -7,8 +7,8 @@ import { review_record, type RecordVerdict } from './review-record'
|
|
|
7
7
|
|
|
8
8
|
// `josh review:record --issue <N> [<category>:<severity>:<file> ...]` — the one write path for a
|
|
9
9
|
// `/code-review` round's findings (joshuafolkken/kit#2325). It appends a `- rf:` line per finding to
|
|
10
|
-
// the observation ledger, which
|
|
11
|
-
// change. **A call with no findings is a zero-finding round, and it still writes one line** — so the
|
|
10
|
+
// the issue's own file of the observation ledger, which the run's commit carries like any other
|
|
11
|
+
// ledger change. **A call with no findings is a zero-finding round, and it still writes one line** — so the
|
|
12
12
|
// round that found nothing is recorded rather than mistaken for a round nobody reviewed.
|
|
13
13
|
//
|
|
14
14
|
// `josh review:record --check --issue <N>` is the read half `pnpm josh followup` runs before it
|
|
@@ -121,14 +121,14 @@ function check_line(verdict: RecordVerdict): string {
|
|
|
121
121
|
return verdict.status === 'ok' ? RECORDED_LINE : NOT_REQUIRED_LINE
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
async function run_check(issue: string | undefined,
|
|
124
|
+
async function run_check(issue: string | undefined, root: string): Promise<number> {
|
|
125
125
|
if (issue === undefined || !ISSUE_PATTERN.test(issue)) {
|
|
126
126
|
console.error(CHECK_USAGE)
|
|
127
127
|
|
|
128
128
|
return FAILURE_EXIT_CODE
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
const verdict = await review_record.check(Number(issue),
|
|
131
|
+
const verdict = await review_record.check(Number(issue), root)
|
|
132
132
|
|
|
133
133
|
if (verdict.status === 'ok' || verdict.status === 'not-required') {
|
|
134
134
|
console.info(check_line(verdict))
|
|
@@ -141,7 +141,9 @@ async function run_check(issue: string | undefined, ledger_path: string): Promis
|
|
|
141
141
|
return FAILURE_EXIT_CODE
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
// The round lands in its own issue's file (joshuafolkken/kit#2919), so two lanes recording at once
|
|
145
|
+
// write two files, and their pull requests never conflict on the ledger.
|
|
146
|
+
async function run_record(parsed: Parsed, now: Date, root: string): Promise<number> {
|
|
145
147
|
const request = to_request(parsed)
|
|
146
148
|
|
|
147
149
|
if (request === undefined) {
|
|
@@ -151,6 +153,7 @@ async function run_record(parsed: Parsed, now: Date, ledger_path: string): Promi
|
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
const lines = build_lines(request, today(now))
|
|
156
|
+
const ledger_path = observation_ledger_home.issue_path(request.issue, root)
|
|
154
157
|
|
|
155
158
|
await observation_ledger_home.append(ledger_path, lines)
|
|
156
159
|
console.info(confirmation(lines.length, ledger_path))
|
|
@@ -158,13 +161,13 @@ async function run_record(parsed: Parsed, now: Date, ledger_path: string): Promi
|
|
|
158
161
|
return 0
|
|
159
162
|
}
|
|
160
163
|
|
|
161
|
-
// **The default is the
|
|
162
|
-
//
|
|
163
|
-
// `
|
|
164
|
+
// **The default is the work tree the command runs in, a lane's inside a lane** (joshuafolkken/kit#2919).
|
|
165
|
+
// The line rides that tree's own commit or `pnpm josh followup`'s pre-merge ledger commit, and
|
|
166
|
+
// `--check` reads the same tree, so the two halves can never disagree about where the round is.
|
|
164
167
|
async function run(
|
|
165
168
|
argv: ReadonlyArray<string>,
|
|
166
169
|
now: Date,
|
|
167
|
-
|
|
170
|
+
root: string = observation_ledger_home.ledger_root(),
|
|
168
171
|
): Promise<number> {
|
|
169
172
|
const parsed = parse_argv(argv)
|
|
170
173
|
|
|
@@ -174,9 +177,9 @@ async function run(
|
|
|
174
177
|
return FAILURE_EXIT_CODE
|
|
175
178
|
}
|
|
176
179
|
|
|
177
|
-
if (parsed.check) return await run_check(parsed.issue,
|
|
180
|
+
if (parsed.check) return await run_check(parsed.issue, root)
|
|
178
181
|
|
|
179
|
-
return await run_record(parsed, now,
|
|
182
|
+
return await run_record(parsed, now, root)
|
|
180
183
|
}
|
|
181
184
|
|
|
182
185
|
async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
2
1
|
import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
|
|
3
2
|
import { review_finding_ledger } from './review-finding-ledger'
|
|
4
3
|
|
|
@@ -29,24 +28,15 @@ interface RecordVerdict {
|
|
|
29
28
|
issue?: number
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return await readFile(ledger_path, 'utf8')
|
|
37
|
-
} catch {
|
|
38
|
-
return undefined
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// The default is the primary checkout's ledger, the one `review:record` writes from a lane
|
|
43
|
-
// (joshuafolkken/kit#2419) — a cwd-relative default read the lane's copy, so the followup gate refused
|
|
44
|
-
// every round recorded from a lane (joshuafolkken/kit#2431).
|
|
31
|
+
// The default is the work tree the check runs in — the one `review:record` wrote to, a lane's in a
|
|
32
|
+
// lane (joshuafolkken/kit#2919). Every file of the directory is read, so the round is found whichever
|
|
33
|
+
// file holds it; a directory that is not there answers `undefined`, a ledger this checkout does not
|
|
34
|
+
// keep, which becomes `not-required`.
|
|
45
35
|
async function check(
|
|
46
36
|
issue: number,
|
|
47
|
-
|
|
37
|
+
root: string = observation_ledger_home.ledger_root(),
|
|
48
38
|
): Promise<RecordVerdict> {
|
|
49
|
-
const content = await
|
|
39
|
+
const content = await observation_ledger_home.read(root)
|
|
50
40
|
|
|
51
41
|
if (content === undefined) return { status: 'not-required' }
|
|
52
42
|
if (review_finding_ledger.has_issue_record(content, issue)) return { status: 'ok', issue }
|
|
@@ -47,7 +47,7 @@ const HUMAN_REVIEW = 'human-review'
|
|
|
47
47
|
// test. `LIVE` is the silent norm; `STOPPED` and `UNKNOWN` lead the difference lines.
|
|
48
48
|
const LIVE = 'live'
|
|
49
49
|
const STOPPED = 'stopped'
|
|
50
|
-
// The verdicts `epic --reconcile` prints; kept in step with the `
|
|
50
|
+
// The verdicts `epic --reconcile` prints; kept in step with the `epic_reconcile` constants in its
|
|
51
51
|
// test.
|
|
52
52
|
const RECONCILED = 'reconciled'
|
|
53
53
|
const NOTHING_TO_RECONCILE = 'nothing to reconcile'
|
|
@@ -302,7 +302,7 @@ const DECISION_ORACLES: ReadonlyArray<DecisionOracle> = [
|
|
|
302
302
|
decision: 'Whether cross-file or cross-repository code duplication exists',
|
|
303
303
|
args: '',
|
|
304
304
|
vocabulary: ['clean', 'clones:'],
|
|
305
|
-
single_source: 'prompts/collaboration-workflow/
|
|
305
|
+
single_source: 'prompts/collaboration-workflow/principles.md → クローン禁止',
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
name: 'epic:reconcile',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
1
|
import { json_value } from '#scripts/lib/json-value'
|
|
2
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
3
3
|
import type { GuardedCall } from '#scripts/time-runtime/time-batch-guard'
|
|
4
4
|
import { bash_triggers } from './bash-triggers'
|
|
5
5
|
import { shell_segments } from './shell-segments'
|
|
@@ -81,7 +81,7 @@ function string_field(input: Record<string, unknown>, key: string): string {
|
|
|
81
81
|
// measure against. Injected so the suite controls it without touching the real filesystem, the way
|
|
82
82
|
// `file-body.ts` injects its existence check. A new file reads as empty, so its whole body is added.
|
|
83
83
|
function read_existing(file_path: string): string {
|
|
84
|
-
return
|
|
84
|
+
return file_reader.read_file_or_empty(file_path)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function texts_of(
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import { mkdirSync, renameSync, statSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
5
|
+
|
|
6
|
+
// The `rule:value` reading at the `backlogrun` loop head, without the loop waiting on it
|
|
7
|
+
// (joshuafolkken/kit#2881). Measuring re-reads every transcript and takes minutes, and `backlog:offer`
|
|
8
|
+
// ran it synchronously on every iteration, so each dispatch waited on it. The loop head now prints the
|
|
9
|
+
// last reading from a cache and, when the last refresh is old enough, starts one in a detached process
|
|
10
|
+
// — `josh rule:value --refresh` — so the next iteration prints the newer reading.
|
|
11
|
+
//
|
|
12
|
+
// **It reports and never fails**, as `rule:value` itself does: a missing cache, an unwritable directory
|
|
13
|
+
// or a refresh that could not start prints at most nothing, and never changes the offer's answer.
|
|
14
|
+
|
|
15
|
+
const CACHE_DIRECTORY = ['node_modules', '.cache', 'josh']
|
|
16
|
+
const CACHE_FILE = 'rule-value.txt'
|
|
17
|
+
// Touched when a refresh starts, so the loop head starts at most one per interval however often it runs.
|
|
18
|
+
const STAMP_FILE = 'rule-value.stamp'
|
|
19
|
+
const MS_PER_MINUTE = 60_000
|
|
20
|
+
const REFRESH_MINUTES = 15
|
|
21
|
+
const REFRESH_MS = REFRESH_MINUTES * MS_PER_MINUTE
|
|
22
|
+
const REFRESH_ARGV = ['josh', 'rule:value', '--refresh']
|
|
23
|
+
const PNPM = 'pnpm'
|
|
24
|
+
const TEMPORARY_SUFFIX = '.tmp'
|
|
25
|
+
|
|
26
|
+
interface CachePorts {
|
|
27
|
+
now: () => number
|
|
28
|
+
start_refresh: (cwd: string) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function cache_path(cwd: string, file: string): string {
|
|
32
|
+
return path.join(cwd, ...CACHE_DIRECTORY, file)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function read_cached(cwd: string): string | undefined {
|
|
36
|
+
return file_reader.read_if_readable(cache_path(cwd, CACHE_FILE))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function is_refresh_due(cwd: string, now_ms: number): boolean {
|
|
40
|
+
try {
|
|
41
|
+
return now_ms - statSync(cache_path(cwd, STAMP_FILE)).mtimeMs >= REFRESH_MS
|
|
42
|
+
} catch {
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function write_cache_file(cwd: string, file: string, text: string): void {
|
|
48
|
+
const target = cache_path(cwd, file)
|
|
49
|
+
const temporary = `${target}${TEMPORARY_SUFFIX}`
|
|
50
|
+
|
|
51
|
+
mkdirSync(path.dirname(target), { recursive: true })
|
|
52
|
+
writeFileSync(temporary, text)
|
|
53
|
+
renameSync(temporary, target)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ignore_spawn_error(): void {
|
|
57
|
+
/* a refresh that could not start leaves the last reading in place */
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Detached with no descriptor shared, so the loop head exits — and a backgrounded caller completes —
|
|
61
|
+
// without waiting on the measurement.
|
|
62
|
+
function start_refresh_default(cwd: string): void {
|
|
63
|
+
const child = spawn(PNPM, REFRESH_ARGV, { cwd, detached: true, stdio: 'ignore' })
|
|
64
|
+
|
|
65
|
+
child.on('error', ignore_spawn_error)
|
|
66
|
+
child.unref()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const DEFAULT_PORTS: CachePorts = { now: Date.now, start_refresh: start_refresh_default }
|
|
70
|
+
|
|
71
|
+
function refresh_if_due(cwd: string, ports: CachePorts): void {
|
|
72
|
+
if (!is_refresh_due(cwd, ports.now())) return
|
|
73
|
+
|
|
74
|
+
write_cache_file(cwd, STAMP_FILE, new Date(ports.now()).toISOString())
|
|
75
|
+
ports.start_refresh(cwd)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The loop-head call: the last reading to stderr, then a refresh when one is due.
|
|
79
|
+
function emit(cwd: string = process.cwd(), ports: CachePorts = DEFAULT_PORTS): void {
|
|
80
|
+
const cached = read_cached(cwd)
|
|
81
|
+
|
|
82
|
+
if (cached !== undefined) process.stderr.write(cached)
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
refresh_if_due(cwd, ports)
|
|
86
|
+
} catch {
|
|
87
|
+
// A refresh that could not be recorded or started leaves the last reading in place.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The detached refresh's body: the reading written whole, so a reader never sees half of one.
|
|
92
|
+
function store(text: string, cwd: string = process.cwd()): void {
|
|
93
|
+
write_cache_file(cwd, CACHE_FILE, `${text}\n`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const rule_value_cache = { CACHE_FILE, REFRESH_MS, STAMP_FILE, cache_path, emit, store }
|
|
97
|
+
|
|
98
|
+
export type { CachePorts }
|
|
99
|
+
export { rule_value_cache }
|
|
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
3
3
|
import { cost_transcript, type SessionFile } from '#scripts/cost-runtime/cost-transcript'
|
|
4
4
|
import { delivered_rules } from './delivered-rules'
|
|
5
5
|
import { rule_value, type RuleReading } from './rule-value'
|
|
6
|
+
import { rule_value_cache } from './rule-value-cache'
|
|
6
7
|
|
|
7
8
|
// `josh rule:value` — what each delivered rule earns on the channel that carries it, one row per rule
|
|
8
9
|
// (joshuafolkken/kit#2271). `rule_value.measure` was public but had no caller outside its own test, so
|
|
@@ -14,6 +15,8 @@ import { rule_value, type RuleReading } from './rule-value'
|
|
|
14
15
|
// no measurable transcript prints `NO_TARGETS` and exits zero rather than erroring, so a fresh
|
|
15
16
|
// checkout reads differently from a broken command.
|
|
16
17
|
|
|
18
|
+
const ARGV_OFFSET = 2
|
|
19
|
+
const REFRESH_FLAG = '--refresh'
|
|
17
20
|
const PERCENT_SIGN = '%'
|
|
18
21
|
const COLUMN_GAP = ' '
|
|
19
22
|
const NO_TARGETS = 'no measurement targets'
|
|
@@ -98,24 +101,22 @@ function render(runs: ReadonlyArray<ReadonlyArray<string>>): string {
|
|
|
98
101
|
.join('\n')
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
}
|
|
104
|
+
// `--refresh` is the detached measurement the `backlogrun` loop head starts: the reading goes to the
|
|
105
|
+
// cache `backlog:offer` prints from, never to stdout (`rule-value-cache.ts`, joshuafolkken/kit#2881).
|
|
106
|
+
function run_rule_value(argv: ReadonlyArray<string> = [], cwd: string = process.cwd()): number {
|
|
107
|
+
const reading = render(gather_runs(cwd))
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
process.stdout.write(`${
|
|
109
|
+
if (argv.includes(REFRESH_FLAG)) rule_value_cache.store(reading, cwd)
|
|
110
|
+
else process.stdout.write(`${reading}\n`)
|
|
109
111
|
|
|
110
112
|
return 0
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
114
|
-
process.exitCode = run_rule_value()
|
|
116
|
+
process.exitCode = run_rule_value(process.argv.slice(ARGV_OFFSET))
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
const rule_value_cli = {
|
|
118
|
-
emit,
|
|
119
120
|
gather_runs,
|
|
120
121
|
group_by_run,
|
|
121
122
|
render,
|
|
@@ -9,7 +9,7 @@ import { delivered_rules, type MeasuredRule } from './delivered-rules'
|
|
|
9
9
|
// (joshuafolkken/kit#1525).
|
|
10
10
|
//
|
|
11
11
|
// **It exists because the eviction order was decided by protection rather than by value.**
|
|
12
|
-
//
|
|
12
|
+
// `prompts/collaboration-workflow/residency.md` records that when a rule is trimmed to keep a
|
|
13
13
|
// byte count, the sentence that goes is the one no marker pinned (joshuafolkken/kit#951) — so the
|
|
14
14
|
// least-defended text leaves, never the least-useful. Nothing in the repository could tell the two
|
|
15
15
|
// apart: `scripts/josh/hook-decision.ts` keeps a once-per-run stamp that answers "has this fired
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
2
2
|
import { run_carry, type CarryChange, type CarryClaimRequest, type CarryOwner } from './run-carry'
|
|
3
3
|
import { run_issue_number } from './run-issue-number'
|
|
4
4
|
|
|
@@ -76,11 +76,7 @@ type Request =
|
|
|
76
76
|
const READ_REQUEST: Request = { kind: 'read' }
|
|
77
77
|
|
|
78
78
|
function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
79
|
-
|
|
80
|
-
return parseArgs({ args: [...argv], options: OPTIONS, strict: true }).values
|
|
81
|
-
} catch {
|
|
82
|
-
return undefined
|
|
83
|
-
}
|
|
79
|
+
return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, strict: true })?.values
|
|
84
80
|
}
|
|
85
81
|
|
|
86
82
|
function text_of(value: OptionValue): string | undefined {
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
type RunCarry,
|
|
11
11
|
} from './run-carry'
|
|
12
12
|
import { run_carry_args, type CountRequest, type Request } from './run-carry-args'
|
|
13
|
-
import { run_carry_flush } from './run-carry-flush'
|
|
14
13
|
import { run_carry_stash } from './run-carry-stash'
|
|
15
14
|
import { run_event_stream } from './run-event-stream'
|
|
16
15
|
import { run_event_stream_emit } from './run-event-stream-emit'
|
|
@@ -344,16 +343,15 @@ async function count(
|
|
|
344
343
|
// **A stop over a record that is still there pushes one ⏸️ confirmation before the record goes**
|
|
345
344
|
// (joshuafolkken/kit#2136). `plan` is read from the record `--end` is about to remove, so the second
|
|
346
345
|
// `--end` reads `none` and plans nothing — one stop never notifies twice. A clean `--end` passes no
|
|
347
|
-
// reason and stays silent, because a completed run has its own notification. The
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
346
|
+
// reason and stays silent, because a completed run has its own notification. The closed-issue stash
|
|
347
|
+
// report (joshuafolkken/kit#2505, `run-carry-stash.ts`) is once per invocation on the same read.
|
|
348
|
+
//
|
|
349
|
+
// **No ledger flush rides it any more** (joshuafolkken/kit#2919): each lane merges its own ledger
|
|
350
|
+
// lines with its own pull request, so there is nothing left in the primary checkout to flush.
|
|
352
351
|
async function report_end(read: CarryRead): Promise<void> {
|
|
353
352
|
if (read.kind === 'none') return
|
|
354
353
|
|
|
355
354
|
await run_carry_stash.report_orphans()
|
|
356
|
-
await run_carry_flush.flush_ledger()
|
|
357
355
|
}
|
|
358
356
|
|
|
359
357
|
async function finish(
|