claude-dev-env 1.72.0 → 1.74.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.md +2 -0
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +2 -2
- package/bin/install.mjs +73 -5
- package/bin/install.test.mjs +360 -4
- package/hooks/blocking/CLAUDE.md +6 -1
- package/hooks/blocking/block_main_commit.py +14 -0
- package/hooks/blocking/bot_mention_comment_blocker.py +7 -0
- package/hooks/blocking/claude_md_orphan_file_blocker.py +19 -48
- package/hooks/blocking/code_rules_dead_config_field.py +69 -56
- package/hooks/blocking/code_rules_docstrings.py +839 -0
- package/hooks/blocking/code_rules_enforcer.py +38 -0
- package/hooks/blocking/code_rules_shared.py +19 -0
- package/hooks/blocking/code_verifier_spawn_preflight_gate.py +426 -0
- package/hooks/blocking/convergence_gate_blocker.py +17 -3
- package/hooks/blocking/destructive_command_blocker.py +7 -0
- package/hooks/blocking/docstring_rule_gate_count_blocker.py +321 -0
- package/hooks/blocking/gh_body_arg_blocker.py +8 -0
- package/hooks/blocking/gh_pr_author_enforcer.py +7 -0
- package/hooks/blocking/hedging_language_blocker.py +16 -10
- package/hooks/blocking/hook_prose_detector_consistency.py +7 -0
- package/hooks/blocking/intent_only_ending_blocker.py +17 -11
- package/hooks/blocking/md_to_html_blocker.py +17 -10
- package/hooks/blocking/open_questions_in_plans_blocker.py +15 -8
- package/hooks/blocking/package_inventory_stale_blocker.py +398 -0
- package/hooks/blocking/plain_language_blocker.py +57 -16
- package/hooks/blocking/pr_converge_bugteam_enforcer.py +11 -5
- package/hooks/blocking/pr_description_enforcer.py +6 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +545 -0
- package/hooks/blocking/precommit_code_rules_gate.py +10 -1
- package/hooks/blocking/pytest_testpaths_orphan_blocker.py +366 -0
- package/hooks/blocking/question_to_user_enforcer.py +18 -12
- package/hooks/blocking/send_user_file_open_locally_blocker.py +70 -0
- package/hooks/blocking/sensitive_file_protector.py +15 -1
- package/hooks/blocking/session_handoff_blocker.py +14 -8
- package/hooks/blocking/state_description_blocker.py +81 -36
- package/hooks/blocking/subprocess_budget_completeness.py +9 -3
- package/hooks/blocking/tdd_enforcer.py +6 -0
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +81 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_inline_literal_claim.py +93 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_returns_plural_cardinality.py +207 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_step_dispatch.py +262 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_undefined_constant.py +253 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_unguarded_payload.py +188 -0
- package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +279 -0
- package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +501 -0
- package/hooks/blocking/test_docstring_rule_gate_count_blocker.py +203 -0
- package/hooks/blocking/test_hook_block_logger_coverage.py +53 -0
- package/hooks/blocking/test_package_inventory_stale_blocker.py +329 -0
- package/hooks/blocking/test_plain_language_blocker.py +36 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +816 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +341 -0
- package/hooks/blocking/test_pytest_testpaths_orphan_blocker.py +247 -0
- package/hooks/blocking/test_send_user_file_open_locally_blocker.py +114 -0
- package/hooks/blocking/test_shared_stdin_adoption.py +208 -0
- package/hooks/blocking/test_state_description_blocker.py +41 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +49 -0
- package/hooks/blocking/test_workflow_substitution_slot_blocker.py +4 -19
- package/hooks/blocking/verdict_directory_write_blocker.py +21 -7
- package/hooks/blocking/verified_commit_gate.py +11 -0
- package/hooks/blocking/verified_commit_message_accuracy_blocker.py +16 -1
- package/hooks/blocking/windows_rmtree_blocker.py +7 -0
- package/hooks/blocking/workflow_substitution_slot_blocker.py +10 -5
- package/hooks/blocking/write_existing_file_blocker.py +16 -1
- package/hooks/hooks.json +19 -79
- package/hooks/hooks_constants/CLAUDE.md +7 -1
- package/hooks/hooks_constants/blocking_check_limits.py +74 -0
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +9 -0
- package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +45 -0
- package/hooks/hooks_constants/dead_config_field_constants.py +5 -5
- package/hooks/hooks_constants/docstring_rule_gate_count_blocker_constants.py +90 -0
- package/hooks/hooks_constants/hook_block_logger.py +59 -0
- package/hooks/hooks_constants/multi_edit_reconstruction.py +56 -0
- package/hooks/hooks_constants/mypy_validator_cache_constants.py +36 -0
- package/hooks/hooks_constants/package_inventory_stale_blocker_constants.py +111 -0
- package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +68 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +143 -0
- package/hooks/hooks_constants/pytest_testpaths_orphan_blocker_constants.py +79 -0
- package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +18 -0
- package/hooks/hooks_constants/test_dispatcher_constants_docstrings.py +44 -0
- package/hooks/hooks_constants/test_hook_block_logger.py +159 -0
- package/hooks/lifecycle/config_change_guard.py +12 -0
- package/hooks/lifecycle/test_config_change_guard.py +23 -0
- package/hooks/validation/hook_format_validator.py +13 -0
- package/hooks/validation/mypy_validator.py +245 -18
- package/hooks/validation/post_tool_use_dispatcher.py +344 -0
- package/hooks/validation/test_hook_format_validator.py +64 -0
- package/hooks/validation/test_mypy_validator.py +206 -1
- package/hooks/validation/test_post_tool_use_dispatcher.py +610 -0
- package/hooks/workflow/test_auto_formatter.py +10 -9
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/docstring-prose-matches-implementation.md +4 -2
- package/rules/package-inventory-stale-entry.md +24 -0
- package/skills/autoconverge/SKILL.md +111 -1
- package/skills/autoconverge/workflow/converge.contract.test.mjs +106 -0
- package/skills/autoconverge/workflow/converge.mjs +29 -3
- package/skills/autoconverge/workflow/converge.path-aware.test.mjs +47 -0
- package/skills/autoconverge/workflow/converge_multi.mjs +161 -0
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +100 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import { strict as assert } from 'node:assert';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const workflowDirectory = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const multiSource = readFileSync(join(workflowDirectory, 'converge_multi.mjs'), 'utf8');
|
|
9
|
+
|
|
10
|
+
function sourceSliceBetween(startNeedle, endNeedle) {
|
|
11
|
+
const sliceStart = multiSource.indexOf(startNeedle);
|
|
12
|
+
assert.notEqual(sliceStart, -1, `expected ${startNeedle} to exist`);
|
|
13
|
+
const sliceEnd = multiSource.indexOf(endNeedle, sliceStart + startNeedle.length);
|
|
14
|
+
assert.notEqual(sliceEnd, -1, `expected ${endNeedle} to exist after ${startNeedle}`);
|
|
15
|
+
return multiSource.slice(sliceStart, sliceEnd);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const productionModule = new Function(
|
|
19
|
+
`${sourceSliceBetween('function normalizeMultiInput(', '\nconst multiInput =')}\n` +
|
|
20
|
+
'return { normalizeMultiInput, isUsablePrEntry, classifyMultiInput };',
|
|
21
|
+
)();
|
|
22
|
+
const { normalizeMultiInput, classifyMultiInput } = productionModule;
|
|
23
|
+
|
|
24
|
+
const SCRIPT_PATH = '/abs/skills/autoconverge/workflow/converge.mjs';
|
|
25
|
+
|
|
26
|
+
function validEntry(prNumber) {
|
|
27
|
+
return {
|
|
28
|
+
owner: 'JonEcho',
|
|
29
|
+
repo: 'python-automation',
|
|
30
|
+
prNumber,
|
|
31
|
+
repoPath: `/worktrees/pr-${prNumber}`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function validArgs() {
|
|
36
|
+
return { convergeScriptPath: SCRIPT_PATH, prs: [validEntry(398), validEntry(402)] };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test('an object payload passes through unchanged', () => {
|
|
40
|
+
const parsed = validArgs();
|
|
41
|
+
assert.deepEqual(normalizeMultiInput(parsed), parsed);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('a JSON-encoded string payload is parsed into coordinates', () => {
|
|
45
|
+
assert.deepEqual(normalizeMultiInput(JSON.stringify(validArgs())), validArgs());
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('a non-JSON string returns null rather than throwing', () => {
|
|
49
|
+
assert.equal(normalizeMultiInput('not json at all'), null);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('valid coordinates classify with no blocker and keep every PR entry', () => {
|
|
53
|
+
const classified = classifyMultiInput(validArgs());
|
|
54
|
+
assert.equal(classified.blocker, null);
|
|
55
|
+
assert.equal(classified.input.prs.length, 2);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('a missing convergeScriptPath is blocked', () => {
|
|
59
|
+
const classified = classifyMultiInput({ prs: [validEntry(398)] });
|
|
60
|
+
assert.equal(classified.input, null);
|
|
61
|
+
assert.match(classified.blocker, /convergeScriptPath/);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('an empty prs array is blocked', () => {
|
|
65
|
+
const classified = classifyMultiInput({ convergeScriptPath: SCRIPT_PATH, prs: [] });
|
|
66
|
+
assert.equal(classified.input, null);
|
|
67
|
+
assert.match(classified.blocker, /non-empty array/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('a missing prs list is blocked', () => {
|
|
71
|
+
const classified = classifyMultiInput({ convergeScriptPath: SCRIPT_PATH });
|
|
72
|
+
assert.equal(classified.input, null);
|
|
73
|
+
assert.match(classified.blocker, /non-empty array/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('a non-array prs value is blocked', () => {
|
|
77
|
+
const classified = classifyMultiInput({ convergeScriptPath: SCRIPT_PATH, prs: 'nope' });
|
|
78
|
+
assert.equal(classified.input, null);
|
|
79
|
+
assert.match(classified.blocker, /non-empty array/);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('an entry missing prNumber is blocked', () => {
|
|
83
|
+
const badEntry = { owner: 'JonEcho', repo: 'python-automation', repoPath: '/w/x' };
|
|
84
|
+
const classified = classifyMultiInput({ convergeScriptPath: SCRIPT_PATH, prs: [badEntry] });
|
|
85
|
+
assert.equal(classified.input, null);
|
|
86
|
+
assert.match(classified.blocker, /missing/);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('an entry missing repoPath is blocked', () => {
|
|
90
|
+
const badEntry = { owner: 'JonEcho', repo: 'python-automation', prNumber: 398 };
|
|
91
|
+
const classified = classifyMultiInput({ convergeScriptPath: SCRIPT_PATH, prs: [badEntry] });
|
|
92
|
+
assert.equal(classified.input, null);
|
|
93
|
+
assert.match(classified.blocker, /missing/);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('a null payload is blocked', () => {
|
|
97
|
+
const classified = classifyMultiInput('not json at all');
|
|
98
|
+
assert.equal(classified.input, null);
|
|
99
|
+
assert.match(classified.blocker, /did not parse/);
|
|
100
|
+
});
|