@joshuafolkken/kit 1.983.0 → 1.998.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/skills/workflow-commands/backlogrun-child.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-steps.md +1 -1
- package/.github/workflows/sonar-qube.yml +12 -2
- package/CLAUDE.md +2 -2
- package/README.md +30 -21
- package/dist/CLAUDE.md +2 -2
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-ZT65LR3U.js → chunk-4GDR2YTV.js} +4 -4
- package/dist/hooks/{chunk-ASUZGZFT.js → chunk-56LGMJJV.js} +2 -2
- package/dist/hooks/{chunk-LBXQHXMC.js → chunk-762BDXDU.js} +220 -68
- package/dist/hooks/{chunk-IM3KM25Y.js → chunk-7WAOP42A.js} +5 -3
- package/dist/hooks/{chunk-P3UX7SFN.js → chunk-A2H5I2VD.js} +2 -2
- package/dist/hooks/{chunk-ERXMJJOV.js → chunk-AWTFKKFW.js} +1 -1
- package/dist/hooks/{chunk-APVOACVT.js → chunk-GBJUERYK.js} +1 -1
- package/dist/hooks/{chunk-5JX6HXDX.js → chunk-KPZN42RT.js} +3 -3
- package/dist/hooks/{chunk-47SPMGOD.js → chunk-PAXMLGSS.js} +26 -7
- package/dist/hooks/{chunk-RFPHKU7C.js → chunk-WMIR5SNX.js} +3 -3
- 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 +98 -26
- package/dist/josh.js +5 -5
- package/package.json +5 -1
- package/prettier/basic.d.ts +3 -0
- package/prettier/basic.js +10 -0
- package/prettier/index.js +2 -2
- package/prettier/static.js +3 -10
- package/prompts/sonar-hotspot-handling.md +6 -0
- package/prompts/testing-guide.md +1 -1
- package/scripts/agent/agent-diagnostics.ts +1 -0
- package/scripts/agent/agent-role-profile.ts +1 -1
- package/scripts/behavior/behavior-rules.ts +8 -1
- package/scripts/build/build-claude-md.ts +13 -4
- package/scripts/document/document-byte-budget.ts +1 -1
- package/scripts/gate/gate-steps.ts +1 -1
- package/scripts/gate/project-checks.ts +12 -11
- package/scripts/gate/type-check-command.ts +1 -1
- package/scripts/git/git-branch.ts +14 -3
- package/scripts/git/git-issue.ts +13 -5
- package/scripts/git/git-pr.ts +16 -10
- package/scripts/git/git-preflight.ts +138 -0
- package/scripts/git/telegram-notify.ts +21 -0
- package/scripts/hooks/stop-guard.ts +2 -0
- package/scripts/init/basic-path-migration.ts +24 -0
- package/scripts/init/distributed-paths.ts +22 -0
- package/scripts/init/init-actions.ts +27 -19
- package/scripts/init/init-ai-copy.ts +86 -28
- package/scripts/init/{init-static.ts → init-basic.ts} +6 -6
- package/scripts/init/init-bootstrap.ts +4 -3
- package/scripts/init/init-install.ts +1 -1
- package/scripts/init/init-logic-templates.ts +1 -1
- package/scripts/init/init-logic.ts +1 -1
- package/scripts/init/init.ts +41 -19
- package/scripts/init/kit-setup-state.ts +30 -0
- package/scripts/init/kit-written-paths.ts +33 -0
- package/scripts/init/project-profile-cli.ts +15 -1
- package/scripts/init/project-profile.ts +41 -12
- package/scripts/init/session-language-environment.ts +83 -0
- package/scripts/init/start-exec.ts +28 -0
- package/scripts/init/start-plan.ts +35 -13
- package/scripts/init/start-prompt.ts +22 -8
- package/scripts/init/start-setup-pr.ts +138 -0
- package/scripts/init/start-steps.ts +30 -30
- package/scripts/init/start.ts +7 -3
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +3 -3
- package/scripts/josh/josh-commands-development.ts +3 -3
- package/scripts/josh/josh-commands-project.ts +2 -2
- package/scripts/lint/cspell-cached.ts +3 -3
- package/scripts/lint/format.ts +1 -1
- package/scripts/lint/lint-parallel.ts +2 -2
- package/scripts/rules/filing-cap.ts +1 -0
- package/scripts/rules/issue-citation.ts +30 -1
- package/scripts/rules/last-prompt.ts +79 -0
- package/scripts/rules/stop-rules.ts +5 -3
- package/scripts/safe-chain/ci-installer-pin.ts +19 -12
- package/scripts/safe-chain/project-config.ts +1 -1
- package/scripts/sync/sync-ai-files.ts +204 -0
- package/scripts/sync/sync.ts +48 -184
- package/scripts/test/josh-harness-environment.ts +1 -1
- package/scripts/test/pilot-files.ts +0 -1
- package/scripts/test/test-declared-changed.ts +1 -1
- package/scripts/test/test-declared-logic.ts +55 -14
- package/scripts/test/test-declared.ts +23 -11
- package/scripts/test/test-unit-guard.ts +6 -6
- package/scripts/time-runtime/time-shell.ts +36 -2
- package/scripts-ai/git-workflow.ts +10 -0
- package/templates/CLAUDE.basic.md +8 -0
- package/templates/workflows/ci.yml +27 -0
- /package/{templates/CLAUDE.static.md → dist/CLAUDE.basic.md} +0 -0
- /package/templates/{cursorrules.static → cursorrules.basic} +0 -0
- /package/templates/{pnpm-workspace.static.yaml → pnpm-workspace.basic.yaml} +0 -0
- /package/templates/{prettierignore.static → prettierignore.basic} +0 -0
|
@@ -712,7 +712,7 @@ var DEV_COMMANDS = {
|
|
|
712
712
|
},
|
|
713
713
|
lint: {
|
|
714
714
|
script: "scripts/lint/lint-parallel.ts",
|
|
715
|
-
description: "Check code with prettier and eslint (skips a tool a
|
|
715
|
+
description: "Check code with prettier and eslint (skips a tool a basic project lacks)",
|
|
716
716
|
category: "Development",
|
|
717
717
|
reference: ["", "developer", ["processes"]]
|
|
718
718
|
},
|
|
@@ -748,7 +748,7 @@ var DEV_COMMANDS = {
|
|
|
748
748
|
},
|
|
749
749
|
format: {
|
|
750
750
|
script: "scripts/lint/format.ts",
|
|
751
|
-
description: "Format code with prettier and eslint (skips a tool a
|
|
751
|
+
description: "Format code with prettier and eslint (skips a tool a basic project lacks)",
|
|
752
752
|
category: "Development",
|
|
753
753
|
reference: ["", "developer", ["files", "processes"]]
|
|
754
754
|
},
|
|
@@ -861,7 +861,7 @@ var DEV_COMMANDS = {
|
|
|
861
861
|
},
|
|
862
862
|
check: {
|
|
863
863
|
script: "scripts/gate/type-check-command.ts",
|
|
864
|
-
description: "Type-check with tsc (skips a
|
|
864
|
+
description: "Type-check with tsc (skips a basic project with no TypeScript to check)",
|
|
865
865
|
category: "Development",
|
|
866
866
|
reference: ["[arguments...]", "developer", ["processes"]]
|
|
867
867
|
},
|
|
@@ -1016,7 +1016,7 @@ var MAINTENANCE_COMMANDS = {
|
|
|
1016
1016
|
};
|
|
1017
1017
|
|
|
1018
1018
|
// scripts/josh/josh-commands-project.ts
|
|
1019
|
-
var PROFILE_ARGUMENTS = "[--profile
|
|
1019
|
+
var PROFILE_ARGUMENTS = "[--profile basic|full]";
|
|
1020
1020
|
var PROJECT_COMMANDS = {
|
|
1021
1021
|
profile: {
|
|
1022
1022
|
script: "scripts/init/project-profile-cli.ts",
|
|
@@ -1026,7 +1026,7 @@ var PROJECT_COMMANDS = {
|
|
|
1026
1026
|
},
|
|
1027
1027
|
start: {
|
|
1028
1028
|
script: "scripts/init/start.ts",
|
|
1029
|
-
description: "
|
|
1029
|
+
description: "Set a project up for the GitHub Issue workflow, from git init to the setup PR",
|
|
1030
1030
|
category: "Project",
|
|
1031
1031
|
reference: [
|
|
1032
1032
|
`${PROFILE_ARGUMENTS} [--yes] [--github] [--public]`,
|
|
@@ -1417,7 +1417,10 @@ var WHITESPACE_PATTERN = /\s+/u;
|
|
|
1417
1417
|
var SEGMENT_PATTERN = /&&|\|\||;|\|/u;
|
|
1418
1418
|
var PIPE_PATTERN = /(?<!\|)\|(?!\|)/u;
|
|
1419
1419
|
var CHAIN_PATTERN = /&&|\|\||;/u;
|
|
1420
|
-
var QUOTED_SPAN_PATTERN =
|
|
1420
|
+
var QUOTED_SPAN_PATTERN = /\\[\s\S]|'[^']*'|"(?:\\[\s\S]|[^"\\])*"/gu;
|
|
1421
|
+
var HEREDOC_MARKER = /(?<!<)<<-?\s*(['"]?)(\w+)\1/u;
|
|
1422
|
+
var HEREDOC_DELIMITER_GROUP = 2;
|
|
1423
|
+
var LINE_BREAK = "\n";
|
|
1421
1424
|
var PIPEFAIL_PATTERN = /\bset\s+-[a-z]*o\s+pipefail\b/u;
|
|
1422
1425
|
var COMMAND_WORD_PATTERN = /^[\w./@:+][\w./@:+-]*$/u;
|
|
1423
1426
|
var ASSIGNMENT_PATTERN = /^\w+=/u;
|
|
@@ -1453,8 +1456,23 @@ function command_segment(command) {
|
|
|
1453
1456
|
function leading_word(command) {
|
|
1454
1457
|
return segment_command(command_segment(command));
|
|
1455
1458
|
}
|
|
1459
|
+
function open_heredoc(line, kept) {
|
|
1460
|
+
kept.push(line.replace(HEREDOC_MARKER, " "));
|
|
1461
|
+
return HEREDOC_MARKER.exec(line)?.[HEREDOC_DELIMITER_GROUP];
|
|
1462
|
+
}
|
|
1463
|
+
function close_heredoc(line, delimiter) {
|
|
1464
|
+
return line.trim() === delimiter ? void 0 : delimiter;
|
|
1465
|
+
}
|
|
1466
|
+
function without_heredoc_bodies(command) {
|
|
1467
|
+
const kept = [];
|
|
1468
|
+
const body = {};
|
|
1469
|
+
for (const line of command.split(LINE_BREAK)) {
|
|
1470
|
+
body.delimiter = body.delimiter === void 0 ? open_heredoc(line, kept) : close_heredoc(line, body.delimiter);
|
|
1471
|
+
}
|
|
1472
|
+
return kept.join(LINE_BREAK);
|
|
1473
|
+
}
|
|
1456
1474
|
function unquoted(command) {
|
|
1457
|
-
return command.replaceAll(QUOTED_SPAN_PATTERN, " ");
|
|
1475
|
+
return without_heredoc_bodies(command).replaceAll(QUOTED_SPAN_PATTERN, " ");
|
|
1458
1476
|
}
|
|
1459
1477
|
function discarded_commands(command) {
|
|
1460
1478
|
const plain = unquoted(command);
|
|
@@ -3505,6 +3523,7 @@ export {
|
|
|
3505
3523
|
time_bundle_call,
|
|
3506
3524
|
time_format,
|
|
3507
3525
|
cost_attribute,
|
|
3526
|
+
time_reported_failure,
|
|
3508
3527
|
time_transcript_line,
|
|
3509
3528
|
time_spans,
|
|
3510
3529
|
time_round_trips,
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
time_batch_guard
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-56LGMJJV.js";
|
|
4
4
|
import {
|
|
5
5
|
hook_decision
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GBJUERYK.js";
|
|
7
7
|
import {
|
|
8
8
|
cost_blocks,
|
|
9
9
|
cost_transcript,
|
|
10
10
|
time_bundle_call,
|
|
11
11
|
time_shell,
|
|
12
12
|
time_spans
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-PAXMLGSS.js";
|
|
14
14
|
|
|
15
15
|
// scripts/delegation/investigation-reads.ts
|
|
16
16
|
import path from "node:path";
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import {
|
|
3
3
|
pretool_guard
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-762BDXDU.js";
|
|
5
|
+
import "./chunk-7WAOP42A.js";
|
|
6
6
|
import {
|
|
7
7
|
build_envelope,
|
|
8
8
|
compose_context,
|
|
9
9
|
format_edited_payload
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-AWTFKKFW.js";
|
|
11
11
|
import "./chunk-3V37IID6.js";
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-A2H5I2VD.js";
|
|
13
|
+
import "./chunk-KPZN42RT.js";
|
|
14
|
+
import "./chunk-4GDR2YTV.js";
|
|
15
|
+
import "./chunk-WMIR5SNX.js";
|
|
16
|
+
import "./chunk-56LGMJJV.js";
|
|
17
17
|
import {
|
|
18
18
|
hook_decision
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-GBJUERYK.js";
|
|
20
20
|
import {
|
|
21
21
|
time_density_hook
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-PAXMLGSS.js";
|
|
23
23
|
import "./chunk-WPALEIRX.js";
|
|
24
24
|
import "./chunk-RKUAD3I6.js";
|
|
25
25
|
|
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
is_enabled,
|
|
10
10
|
notice_refusal_path,
|
|
11
11
|
refusal_path
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-4GDR2YTV.js";
|
|
13
|
+
import "./chunk-WMIR5SNX.js";
|
|
14
|
+
import "./chunk-56LGMJJV.js";
|
|
15
|
+
import "./chunk-GBJUERYK.js";
|
|
16
|
+
import "./chunk-PAXMLGSS.js";
|
|
17
17
|
import "./chunk-WPALEIRX.js";
|
|
18
18
|
import "./chunk-RKUAD3I6.js";
|
|
19
19
|
export {
|
|
@@ -15,9 +15,9 @@ import {
|
|
|
15
15
|
relative_to_root,
|
|
16
16
|
resolve_invocations,
|
|
17
17
|
select_invocation
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-AWTFKKFW.js";
|
|
19
19
|
import "./chunk-3V37IID6.js";
|
|
20
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-PAXMLGSS.js";
|
|
21
21
|
import "./chunk-RKUAD3I6.js";
|
|
22
22
|
export {
|
|
23
23
|
ESLINT_DAEMON,
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
is_enabled,
|
|
11
11
|
load_environment_file,
|
|
12
12
|
refusal_path
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-KPZN42RT.js";
|
|
14
|
+
import "./chunk-WMIR5SNX.js";
|
|
15
|
+
import "./chunk-56LGMJJV.js";
|
|
16
|
+
import "./chunk-GBJUERYK.js";
|
|
17
|
+
import "./chunk-PAXMLGSS.js";
|
|
18
18
|
import "./chunk-WPALEIRX.js";
|
|
19
19
|
import "./chunk-RKUAD3I6.js";
|
|
20
20
|
export {
|
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
pretool_guard,
|
|
4
4
|
pretool_outcome,
|
|
5
5
|
pretool_outcome_async
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-762BDXDU.js";
|
|
7
|
+
import "./chunk-7WAOP42A.js";
|
|
8
8
|
import "./chunk-3V37IID6.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-A2H5I2VD.js";
|
|
10
|
+
import "./chunk-KPZN42RT.js";
|
|
11
|
+
import "./chunk-4GDR2YTV.js";
|
|
12
|
+
import "./chunk-WMIR5SNX.js";
|
|
13
|
+
import "./chunk-56LGMJJV.js";
|
|
14
|
+
import "./chunk-GBJUERYK.js";
|
|
15
|
+
import "./chunk-PAXMLGSS.js";
|
|
16
16
|
import "./chunk-WPALEIRX.js";
|
|
17
17
|
import "./chunk-RKUAD3I6.js";
|
|
18
18
|
export {
|
package/dist/hooks/stop-guard.js
CHANGED
|
@@ -19,20 +19,22 @@ import {
|
|
|
19
19
|
run_hold,
|
|
20
20
|
run_invocation,
|
|
21
21
|
run_watcher_guard
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-7WAOP42A.js";
|
|
23
23
|
import {
|
|
24
24
|
session_language
|
|
25
25
|
} from "./chunk-6V5OPIUU.js";
|
|
26
26
|
import {
|
|
27
27
|
hook_decision,
|
|
28
28
|
lane_child_marker
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-GBJUERYK.js";
|
|
30
30
|
import {
|
|
31
31
|
PROJECT_ROOT,
|
|
32
|
+
json_value,
|
|
32
33
|
stamp_file,
|
|
33
34
|
time_density_hook,
|
|
34
|
-
time_hook_transcript
|
|
35
|
-
|
|
35
|
+
time_hook_transcript,
|
|
36
|
+
time_reported_failure
|
|
37
|
+
} from "./chunk-PAXMLGSS.js";
|
|
36
38
|
import "./chunk-WPALEIRX.js";
|
|
37
39
|
import "./chunk-RKUAD3I6.js";
|
|
38
40
|
|
|
@@ -329,6 +331,17 @@ function bare_references(message2) {
|
|
|
329
331
|
for (const line of prose_lines(message2)) collect_line_references(line, found);
|
|
330
332
|
return [...new Set(found)];
|
|
331
333
|
}
|
|
334
|
+
function prompt_references(prompt) {
|
|
335
|
+
const found = /* @__PURE__ */ new Set();
|
|
336
|
+
for (const match of prompt.matchAll(ISSUE_NUMBER)) {
|
|
337
|
+
found.add(repo_prefix(prompt, match.index) + match[0]);
|
|
338
|
+
}
|
|
339
|
+
return found;
|
|
340
|
+
}
|
|
341
|
+
function unquoted_references(message2, prompt) {
|
|
342
|
+
const quoted = prompt_references(prompt);
|
|
343
|
+
return bare_references(message2).filter((reference) => !quoted.has(reference));
|
|
344
|
+
}
|
|
332
345
|
function has_bare_reference(message2) {
|
|
333
346
|
return bare_references(message2).length > 0;
|
|
334
347
|
}
|
|
@@ -366,7 +379,14 @@ function linkify(message2, slug) {
|
|
|
366
379
|
}
|
|
367
380
|
return out.join("\n");
|
|
368
381
|
}
|
|
369
|
-
var issue_citation = {
|
|
382
|
+
var issue_citation = {
|
|
383
|
+
bare_references,
|
|
384
|
+
cite_arguments,
|
|
385
|
+
has_bare_reference,
|
|
386
|
+
linkify,
|
|
387
|
+
prose_lines,
|
|
388
|
+
unquoted_references
|
|
389
|
+
};
|
|
370
390
|
|
|
371
391
|
// scripts/epic/epic-triage.ts
|
|
372
392
|
var TRIAGE_VERDICT = "triage";
|
|
@@ -684,6 +704,8 @@ var TASK_DEFINITIONS = {
|
|
|
684
704
|
stalled: { icon: "\u23F3", label: "Ready work is sitting undispatched" },
|
|
685
705
|
stranded: { icon: "\u{1F6A8}", label: "Run stranded \u2014 nobody is driving it" }
|
|
686
706
|
};
|
|
707
|
+
var NOTIFY_SWITCH_KEY = "JOSH_NOTIFY";
|
|
708
|
+
var NOTIFY_OFF_VALUE = "off";
|
|
687
709
|
var NOT_CONFIGURED_PREFIX = "Telegram is not configured";
|
|
688
710
|
var SEND_FAILED_PREFIX = "Telegram notification failed";
|
|
689
711
|
var REDACTED = "<redacted>";
|
|
@@ -720,6 +742,9 @@ function load_config() {
|
|
|
720
742
|
}
|
|
721
743
|
return { bot_token: result.data.telegram_bot_token, chat_id: result.data.telegram_chat_id };
|
|
722
744
|
}
|
|
745
|
+
function is_notify_disabled() {
|
|
746
|
+
return parse_environment_string(process.env[NOTIFY_SWITCH_KEY]).toLowerCase() === NOTIFY_OFF_VALUE;
|
|
747
|
+
}
|
|
723
748
|
function build_header(task_type, repo_name) {
|
|
724
749
|
const { icon, label: label2 } = TASK_DEFINITIONS[task_type];
|
|
725
750
|
if (repo_name === void 0 || repo_name.length === 0) return `${icon} ${label2}`;
|
|
@@ -767,6 +792,12 @@ function build_send_failure(error, config) {
|
|
|
767
792
|
return new Error(`${SEND_FAILED_PREFIX}: ${reason}`, { cause: new Error(reason) });
|
|
768
793
|
}
|
|
769
794
|
async function send(input) {
|
|
795
|
+
if (is_notify_disabled()) {
|
|
796
|
+
console.info(
|
|
797
|
+
`\u{1F515} Telegram notifications are disabled (${NOTIFY_SWITCH_KEY}=${NOTIFY_OFF_VALUE}).`
|
|
798
|
+
);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
770
801
|
const config = load_config();
|
|
771
802
|
const text2 = build_text(input);
|
|
772
803
|
try {
|
|
@@ -1038,8 +1069,48 @@ async function run_stall_check(ready = backlog_ready.DEFAULT_PORTS) {
|
|
|
1038
1069
|
}
|
|
1039
1070
|
var backlog_stalled_detect = { DEFAULT_PORTS: DEFAULT_PORTS2, detect_and_report, gather, report, run_stall_check };
|
|
1040
1071
|
|
|
1041
|
-
// scripts/rules/
|
|
1072
|
+
// scripts/rules/last-prompt.ts
|
|
1042
1073
|
import { z as z3 } from "zod";
|
|
1074
|
+
var WORKFLOW_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1075
|
+
"kickoff",
|
|
1076
|
+
"fullrun",
|
|
1077
|
+
"halfrun",
|
|
1078
|
+
"backlogrun"
|
|
1079
|
+
]);
|
|
1080
|
+
var NO_PROMPT = "";
|
|
1081
|
+
var HUMAN_ORIGIN = "human";
|
|
1082
|
+
var KEYWORD_BEFORE_ARGUMENT = -2;
|
|
1083
|
+
var PROMPT_LINE_SCHEMA = z3.object({
|
|
1084
|
+
isMeta: z3.boolean().nullish(),
|
|
1085
|
+
origin: z3.object({ kind: z3.string().nullish() }).nullish(),
|
|
1086
|
+
message: z3.object({ content: z3.unknown() })
|
|
1087
|
+
});
|
|
1088
|
+
function is_person(line) {
|
|
1089
|
+
if (line.isMeta === true) return false;
|
|
1090
|
+
return line.origin === null || line.origin === void 0 || line.origin.kind === HUMAN_ORIGIN;
|
|
1091
|
+
}
|
|
1092
|
+
function person_text(line) {
|
|
1093
|
+
const parsed = PROMPT_LINE_SCHEMA.safeParse(json_value.parse_or_undefined(line));
|
|
1094
|
+
if (!parsed.success || !is_person(parsed.data)) return void 0;
|
|
1095
|
+
return time_reported_failure.result_text(parsed.data.message.content);
|
|
1096
|
+
}
|
|
1097
|
+
function is_invocation(prompt) {
|
|
1098
|
+
const words = prompt.trim().split(/\s+/u);
|
|
1099
|
+
return [words.at(0), words.at(KEYWORD_BEFORE_ARGUMENT)].some(
|
|
1100
|
+
(word) => WORKFLOW_KEYWORDS.has(word ?? NO_PROMPT)
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
function prompt_line_text(line) {
|
|
1104
|
+
return filing_cap.is_prompt_line(line) ? person_text(line) : void 0;
|
|
1105
|
+
}
|
|
1106
|
+
function prompt_text(tail) {
|
|
1107
|
+
const text2 = tail.split("\n").map((line) => prompt_line_text(line)).findLast((one) => one !== void 0);
|
|
1108
|
+
return text2 === void 0 || is_invocation(text2) ? NO_PROMPT : text2;
|
|
1109
|
+
}
|
|
1110
|
+
var last_prompt = { prompt_text };
|
|
1111
|
+
|
|
1112
|
+
// scripts/rules/stop-rules.ts
|
|
1113
|
+
import { z as z4 } from "zod";
|
|
1043
1114
|
|
|
1044
1115
|
// scripts/rules/filing-offer.ts
|
|
1045
1116
|
var OFFER_PATTERNS = [
|
|
@@ -1147,10 +1218,10 @@ var reply_language = { MIN_PROSE_LETTERS, is_mismatch, script_of };
|
|
|
1147
1218
|
// scripts/rules/stop-rules.ts
|
|
1148
1219
|
var SWITCH_ENV_KEY = "JOSH_STOP_GUARD";
|
|
1149
1220
|
var BLOCK_DECISION = "block";
|
|
1150
|
-
var stop_payload_schema =
|
|
1151
|
-
transcript_path:
|
|
1152
|
-
stop_hook_active:
|
|
1153
|
-
last_assistant_message:
|
|
1221
|
+
var stop_payload_schema = z4.object({
|
|
1222
|
+
transcript_path: z4.string().min(1),
|
|
1223
|
+
stop_hook_active: z4.boolean().nullish(),
|
|
1224
|
+
last_assistant_message: z4.string().nullish()
|
|
1154
1225
|
});
|
|
1155
1226
|
var NO_OUTCOME = { reason: void 0 };
|
|
1156
1227
|
var STOP_NOTIFY_REASON = '\u26D4 mid-workflow stop notification: this working tree is still held by a run and no `confirmation` Telegram was sent this turn, so a person is being left to wait off-screen without knowing the run paused. `CLAUDE.md` \u2192 "Mid-workflow stop notification" requires `pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\'` before any mid-workflow pause. Send it, then end with a one-line confirmation that it was sent \u2014 do not repeat your previous reply. This fires only while the hold is held and no notify is on the transcript, and `stop_hook_active` lets a second stop through so a run is never wedged.';
|
|
@@ -1199,14 +1270,14 @@ function needs_notify(context) {
|
|
|
1199
1270
|
function needs_release(context) {
|
|
1200
1271
|
return is_held_idle(context) && context.tree_clean;
|
|
1201
1272
|
}
|
|
1202
|
-
function citation_reason(
|
|
1203
|
-
const references = issue_citation.
|
|
1273
|
+
function citation_reason(context) {
|
|
1274
|
+
const references = issue_citation.unquoted_references(context.message, context.prompt);
|
|
1204
1275
|
if (references.length === 0) return void 0;
|
|
1205
1276
|
return build_citation_reason(references);
|
|
1206
1277
|
}
|
|
1207
1278
|
function reply_reason(context) {
|
|
1208
1279
|
if (needs_filing(context)) return FILING_OFFER_REASON;
|
|
1209
|
-
return citation_reason(context
|
|
1280
|
+
return citation_reason(context) ?? language_reason(context);
|
|
1210
1281
|
}
|
|
1211
1282
|
function is_headless_held(context) {
|
|
1212
1283
|
if (!context.headless_waiting) return false;
|
|
@@ -1302,7 +1373,7 @@ var run_stranded = {
|
|
|
1302
1373
|
};
|
|
1303
1374
|
|
|
1304
1375
|
// scripts/run/run-wake.ts
|
|
1305
|
-
import { z as
|
|
1376
|
+
import { z as z5 } from "zod";
|
|
1306
1377
|
var WAKE_PREFIX = "josh-run-wake-";
|
|
1307
1378
|
var WAKE_LOG_PREFIX = "josh-run-wake-log-";
|
|
1308
1379
|
var WAKE_LOG_SUFFIX = ".log";
|
|
@@ -1322,21 +1393,21 @@ var STOP_REASONS = {
|
|
|
1322
1393
|
expired: "expired",
|
|
1323
1394
|
unreadable: "unreadable"
|
|
1324
1395
|
};
|
|
1325
|
-
var run_wake_schema =
|
|
1326
|
-
invocation:
|
|
1327
|
-
started_at:
|
|
1328
|
-
pid:
|
|
1329
|
-
process_start:
|
|
1330
|
-
woke:
|
|
1331
|
-
woke_at:
|
|
1396
|
+
var run_wake_schema = z5.object({
|
|
1397
|
+
invocation: z5.string(),
|
|
1398
|
+
started_at: z5.string(),
|
|
1399
|
+
pid: z5.number(),
|
|
1400
|
+
process_start: z5.string().optional(),
|
|
1401
|
+
woke: z5.number(),
|
|
1402
|
+
woke_at: z5.string().optional(),
|
|
1332
1403
|
// **Every optional field of `RunWake` has to be listed here.** `z.object` strips what it does not
|
|
1333
1404
|
// declare, so a field added to the interface alone round-trips through disk as `undefined` — which
|
|
1334
1405
|
// reads as "no attempt has been made yet" and would restart the retry count on every pass.
|
|
1335
|
-
attempts:
|
|
1336
|
-
woke_pid:
|
|
1337
|
-
held_at:
|
|
1338
|
-
spawned:
|
|
1339
|
-
idle_since:
|
|
1406
|
+
attempts: z5.number().optional(),
|
|
1407
|
+
woke_pid: z5.number().optional(),
|
|
1408
|
+
held_at: z5.string().optional(),
|
|
1409
|
+
spawned: z5.array(z5.string()).optional(),
|
|
1410
|
+
idle_since: z5.string().optional(),
|
|
1340
1411
|
profile: agent_role_profile.PROFILE_SCHEMA.optional()
|
|
1341
1412
|
});
|
|
1342
1413
|
function is_overdue(marked_at, now, limit_ms = WAKE_GRACE_MS) {
|
|
@@ -1585,6 +1656,7 @@ async function build_context(transcript_path, payload_tail) {
|
|
|
1585
1656
|
tree_clean: !await run_hold.is_tree_dirty(),
|
|
1586
1657
|
notified: lane_park.mentions_confirmation_notify(tail),
|
|
1587
1658
|
message: payload_tail.message,
|
|
1659
|
+
prompt: last_prompt.prompt_text(tail),
|
|
1588
1660
|
stop_hook_active: payload_tail.stop_hook_active,
|
|
1589
1661
|
cut_pending: run_cut.carried_cut_sync() !== void 0,
|
|
1590
1662
|
filed: was_filed(tail),
|
package/dist/josh.js
CHANGED
|
@@ -869,7 +869,7 @@ var DEV_COMMANDS = {
|
|
|
869
869
|
},
|
|
870
870
|
lint: {
|
|
871
871
|
script: "scripts/lint/lint-parallel.ts",
|
|
872
|
-
description: "Check code with prettier and eslint (skips a tool a
|
|
872
|
+
description: "Check code with prettier and eslint (skips a tool a basic project lacks)",
|
|
873
873
|
category: "Development",
|
|
874
874
|
reference: ["", "developer", ["processes"]]
|
|
875
875
|
},
|
|
@@ -905,7 +905,7 @@ var DEV_COMMANDS = {
|
|
|
905
905
|
},
|
|
906
906
|
format: {
|
|
907
907
|
script: "scripts/lint/format.ts",
|
|
908
|
-
description: "Format code with prettier and eslint (skips a tool a
|
|
908
|
+
description: "Format code with prettier and eslint (skips a tool a basic project lacks)",
|
|
909
909
|
category: "Development",
|
|
910
910
|
reference: ["", "developer", ["files", "processes"]]
|
|
911
911
|
},
|
|
@@ -1018,7 +1018,7 @@ var DEV_COMMANDS = {
|
|
|
1018
1018
|
},
|
|
1019
1019
|
check: {
|
|
1020
1020
|
script: "scripts/gate/type-check-command.ts",
|
|
1021
|
-
description: "Type-check with tsc (skips a
|
|
1021
|
+
description: "Type-check with tsc (skips a basic project with no TypeScript to check)",
|
|
1022
1022
|
category: "Development",
|
|
1023
1023
|
reference: ["[arguments...]", "developer", ["processes"]]
|
|
1024
1024
|
},
|
|
@@ -1173,7 +1173,7 @@ var MAINTENANCE_COMMANDS = {
|
|
|
1173
1173
|
};
|
|
1174
1174
|
|
|
1175
1175
|
// scripts/josh/josh-commands-project.ts
|
|
1176
|
-
var PROFILE_ARGUMENTS = "[--profile
|
|
1176
|
+
var PROFILE_ARGUMENTS = "[--profile basic|full]";
|
|
1177
1177
|
var PROJECT_COMMANDS = {
|
|
1178
1178
|
profile: {
|
|
1179
1179
|
script: "scripts/init/project-profile-cli.ts",
|
|
@@ -1183,7 +1183,7 @@ var PROJECT_COMMANDS = {
|
|
|
1183
1183
|
},
|
|
1184
1184
|
start: {
|
|
1185
1185
|
script: "scripts/init/start.ts",
|
|
1186
|
-
description: "
|
|
1186
|
+
description: "Set a project up for the GitHub Issue workflow, from git init to the setup PR",
|
|
1187
1187
|
category: "Project",
|
|
1188
1188
|
reference: [
|
|
1189
1189
|
`${PROFILE_ARGUMENTS} [--yes] [--github] [--public]`,
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.998.0",
|
|
3
3
|
"name": "@joshuafolkken/kit",
|
|
4
4
|
"description": "Development toolkit for AI-assisted projects: formatting, AI assistant rules and a Git/GitHub Issue workflow for any repository, plus lint, type check, tests and hooks for Node projects",
|
|
5
5
|
"keywords": [
|
|
@@ -97,6 +97,10 @@
|
|
|
97
97
|
"types": "./prettier/index.d.ts",
|
|
98
98
|
"default": "./prettier/index.js"
|
|
99
99
|
},
|
|
100
|
+
"./prettier/basic": {
|
|
101
|
+
"types": "./prettier/basic.d.ts",
|
|
102
|
+
"default": "./prettier/basic.js"
|
|
103
|
+
},
|
|
100
104
|
"./prettier/static": {
|
|
101
105
|
"types": "./prettier/static.d.ts",
|
|
102
106
|
"default": "./prettier/static.js"
|
package/prettier/index.js
CHANGED
package/prettier/static.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
trailingComma: 'all',
|
|
5
|
-
printWidth: 100,
|
|
6
|
-
semi: false,
|
|
7
|
-
arrowParens: 'always',
|
|
8
|
-
bracketSpacing: true,
|
|
9
|
-
endOfLine: 'lf',
|
|
10
|
-
}
|
|
1
|
+
// The basic profile's preset under the name it had before joshuafolkken/kit#2829. A project
|
|
2
|
+
// initialized earlier imports this path from its `prettier.config.mjs`, so it stays exported.
|
|
3
|
+
export { config } from './basic.js'
|
|
@@ -17,6 +17,12 @@ comments), but it **does** wait on the required `SonarQube` CI check, so a red g
|
|
|
17
17
|
`followup` merge. Consumers pick the workflow up verbatim via `josh sync`; do not re-implement the
|
|
18
18
|
gate per consumer.
|
|
19
19
|
|
|
20
|
+
**A run that cannot see `SONAR_TOKEN` skips the scan and passes**, printing one line that names the
|
|
21
|
+
missing token (joshuafolkken/kit#2815) — a consumer that does not use SonarCloud is never blocked by
|
|
22
|
+
it. The same holds for any run GitHub withholds secrets from (a fork pull request, or a Dependabot
|
|
23
|
+
run without the secret in the Dependabot store), so a repository that relies on this gate keeps
|
|
24
|
+
`SONAR_TOKEN` in both secret stores.
|
|
25
|
+
|
|
20
26
|
### New Code vs Overall Code semantics
|
|
21
27
|
|
|
22
28
|
SonarCloud's default **Sonar way** Quality Gate evaluates **New Code only** — conditions
|
package/prompts/testing-guide.md
CHANGED
|
@@ -23,7 +23,7 @@ Req N: ...
|
|
|
23
23
|
|
|
24
24
|
- **Every user-facing behavior change** (UI interaction, keyboard shortcut, visible state change) must have a corresponding E2E test.
|
|
25
25
|
- **Every logic/utility change** (pure functions, filters, transforms) must have a unit test.
|
|
26
|
-
- **
|
|
26
|
+
- **basic で kit がテストできない言語だけの変更:** `package.json` に `josh.profile: basic`(改名前の `static` も同じ)が記録され、変更が HTML/CSS や Lua などのソースだけなら自動テストは不要。HTML/CSS はブラウザーで対象ページを開き、想定する画面幅でレイアウト・リンク・HTML 内の操作を確認する。そのほかのソースは変更した処理を実際に動かす。どちらも結果を記録する。同じ変更に JavaScript または TypeScript のファイルが含まれれば自動テストを行う。
|
|
27
27
|
- If a test is genuinely infeasible (e.g., native OS date-picker popup cannot be driven by Playwright), write a comment in the test file explaining why and test the closest observable behavior instead (e.g., that the editor remains open after the `change` event fires).
|
|
28
28
|
- Do **not** report a requirement as done if its test is missing.
|
|
29
29
|
|
|
@@ -37,6 +37,7 @@ const PROVIDER_CLI_TABLE: Readonly<Record<AgentProvider, ProviderCli>> = {
|
|
|
37
37
|
const MODEL_CLI_FLOORS: ReadonlyMap<string, string> = new Map([
|
|
38
38
|
['claude-opus-5-5', '2.1.280'],
|
|
39
39
|
['gpt-6-sol', '0.155.0'],
|
|
40
|
+
['gpt-6.1-sol', '0.159.1'],
|
|
40
41
|
])
|
|
41
42
|
|
|
42
43
|
const default_ports: DiagnosticPorts = {
|
|
@@ -44,7 +44,7 @@ const OPENAI_PROVIDER: AgentProvider = 'openai'
|
|
|
44
44
|
// whenever the CLI moves it, so a run log could not say which model produced it and a model migration
|
|
45
45
|
// could not be measured apart from everything else. A new lane records the id it resolved; a lane
|
|
46
46
|
// created before a migration keeps the model it recorded (`with_phase_effort` leaves it untouched).
|
|
47
|
-
const OPENAI_MODEL = 'gpt-6-sol'
|
|
47
|
+
const OPENAI_MODEL = 'gpt-6.1-sol'
|
|
48
48
|
const ANTHROPIC_MODEL = 'claude-opus-5-5'
|
|
49
49
|
const CODEX_SESSION_KEY = 'CODEX_THREAD_ID'
|
|
50
50
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { json_value } from '#scripts/lib/json-value'
|
|
2
|
+
import { time_shell } from '#scripts/time-runtime/time-shell'
|
|
2
3
|
import type { Block, TranscriptLine } from '#scripts/time-runtime/time-transcript-line'
|
|
3
4
|
import type { Assertion, Finding } from './behavior-assertion'
|
|
4
5
|
|
|
@@ -57,8 +58,14 @@ function command_of(block: Block): string | undefined {
|
|
|
57
58
|
// Whether any segment of a command directly mutates the index: it begins with a staging or commit
|
|
58
59
|
// subcommand and is not a dry run. Each `;`/`&&`/`|`-separated segment is judged on its own, so a
|
|
59
60
|
// mutation anywhere in a chain is caught while a mention inside another command's argument is not.
|
|
61
|
+
//
|
|
62
|
+
// Quoted spans are removed before the split (joshuafolkken/kit#2841): a `|` inside a quoted pattern —
|
|
63
|
+
// `grep -E "commit|git add" file` — is not a separator, and cutting there synthesized a segment that
|
|
64
|
+
// began with `git add` out of a read-only search. `unquoted` honors escapes and heredoc bodies, so a
|
|
65
|
+
// stray `it's` cannot pair with a later quote and erase the real `git add` between them.
|
|
60
66
|
function is_index_mutation(command: string): boolean {
|
|
61
|
-
return
|
|
67
|
+
return time_shell
|
|
68
|
+
.unquoted(command)
|
|
62
69
|
.split(SEGMENT_SEPARATOR)
|
|
63
70
|
.map((segment) => segment.trim())
|
|
64
71
|
.some((segment) => INDEX_MUTATION_HEAD.test(segment) && !is_dry_run(segment))
|
|
@@ -7,11 +7,17 @@ import { init_logic } from '#scripts/init/init-logic'
|
|
|
7
7
|
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url))
|
|
8
8
|
const REPO_ROOT = path.resolve(SCRIPT_DIR, '..', '..')
|
|
9
9
|
const DIST_ROOT = path.join(REPO_ROOT, 'dist')
|
|
10
|
-
const
|
|
10
|
+
const BASIC_CLAUDE_FILENAME = 'CLAUDE.basic.md'
|
|
11
11
|
const SOURCE_CLAUDE_MD = path.join(REPO_ROOT, 'CLAUDE.md')
|
|
12
12
|
const DIST_CLAUDE_MD = path.join(DIST_ROOT, 'CLAUDE.md')
|
|
13
|
-
const
|
|
14
|
-
|
|
13
|
+
const SOURCE_BASIC_CLAUDE_MD = path.join(REPO_ROOT, 'templates', BASIC_CLAUDE_FILENAME)
|
|
14
|
+
// A basic project's `CLAUDE.md` imports the rules from dist/. One initialized before
|
|
15
|
+
// joshuafolkken/kit#2829 imports them under the profile's old name until `josh init` is re-run
|
|
16
|
+
// and migrates that import, so the same rules ship under both names.
|
|
17
|
+
const DIST_BASIC_CLAUDE_MDS: ReadonlyArray<string> = [
|
|
18
|
+
path.join(DIST_ROOT, BASIC_CLAUDE_FILENAME),
|
|
19
|
+
path.join(DIST_ROOT, 'CLAUDE.static.md'),
|
|
20
|
+
]
|
|
15
21
|
|
|
16
22
|
// Read kit's own CLAUDE.md and apply the distribution path transform. kit's source keeps relative
|
|
17
23
|
// paths so it resolves inside the kit repository; the published copy rewrites them so every backtick
|
|
@@ -27,7 +33,9 @@ function generate_distributed_claude_md(): string {
|
|
|
27
33
|
function build_claude_md(): string {
|
|
28
34
|
mkdirSync(path.dirname(DIST_CLAUDE_MD), { recursive: true })
|
|
29
35
|
writeFileSync(DIST_CLAUDE_MD, generate_distributed_claude_md())
|
|
30
|
-
|
|
36
|
+
const basic_rules = readFileSync(SOURCE_BASIC_CLAUDE_MD, 'utf8')
|
|
37
|
+
|
|
38
|
+
for (const destination of DIST_BASIC_CLAUDE_MDS) writeFileSync(destination, basic_rules)
|
|
31
39
|
|
|
32
40
|
return DIST_CLAUDE_MD
|
|
33
41
|
}
|
|
@@ -40,6 +48,7 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) main()
|
|
|
40
48
|
|
|
41
49
|
export {
|
|
42
50
|
build_claude_md,
|
|
51
|
+
DIST_BASIC_CLAUDE_MDS,
|
|
43
52
|
generate_distributed_claude_md,
|
|
44
53
|
SOURCE_CLAUDE_MD,
|
|
45
54
|
DIST_CLAUDE_MD,
|