@cleocode/cleo 2026.4.154 → 2026.4.157

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/cleo",
3
- "version": "2026.4.154",
3
+ "version": "2026.4.157",
4
4
  "description": "CLEO CLI — the assembled product consuming @cleocode/core",
5
5
  "type": "module",
6
6
  "main": "./dist/cli/index.js",
@@ -29,16 +29,16 @@
29
29
  "tree-sitter-ruby": "^0.23.1",
30
30
  "tree-sitter-rust": "0.23.1",
31
31
  "tree-sitter-typescript": "^0.23.2",
32
- "@cleocode/caamp": "2026.4.154",
33
- "@cleocode/contracts": "2026.4.154",
34
- "@cleocode/cant": "2026.4.154",
35
- "@cleocode/lafs": "2026.4.154",
36
- "@cleocode/nexus": "2026.4.154",
37
- "@cleocode/playbooks": "2026.4.154",
38
- "@cleocode/runtime": "2026.4.154"
32
+ "@cleocode/cant": "2026.4.157",
33
+ "@cleocode/caamp": "2026.4.157",
34
+ "@cleocode/contracts": "2026.4.157",
35
+ "@cleocode/lafs": "2026.4.157",
36
+ "@cleocode/nexus": "2026.4.157",
37
+ "@cleocode/playbooks": "2026.4.157",
38
+ "@cleocode/runtime": "2026.4.157"
39
39
  },
40
40
  "peerDependencies": {
41
- "@cleocode/core": "2026.4.154"
41
+ "@cleocode/core": "2026.4.157"
42
42
  },
43
43
  "peerDependenciesMeta": {
44
44
  "@cleocode/core": {
@@ -0,0 +1,73 @@
1
+ #!/bin/sh
2
+ # CLEO_MANAGED_HOOK v1
3
+ # T1588 — project-agnostic T-ID enforcement for every commit subject.
4
+ #
5
+ # Rule: subject MUST contain `T<digits>` somewhere, OR be a merge/revert
6
+ # (which preserves the git merge --no-ff path established in T1587 and the
7
+ # stock `git revert` flow).
8
+ #
9
+ # Override: `git commit --no-verify` bypasses (standard git behaviour).
10
+ # A best-effort audit of `--no-verify` lives in the git shim (see T1591) —
11
+ # hooks themselves cannot observe `--no-verify`.
12
+ #
13
+ # This script is POSIX `/bin/sh` only (no bash/zsh-isms). It MUST work in
14
+ # any environment cleo init runs in: node-less projects, Rust, Python,
15
+ # bare repos, etc. Do not introduce node/pnpm dependencies here.
16
+ set -e
17
+
18
+ MSG_FILE="$1"
19
+ if [ -z "$MSG_FILE" ] || [ ! -f "$MSG_FILE" ]; then
20
+ echo "cleo commit-msg hook: missing message file argument" >&2
21
+ exit 1
22
+ fi
23
+
24
+ # First non-empty, non-comment line = the subject.
25
+ SUBJECT=""
26
+ while IFS= read -r line || [ -n "$line" ]; do
27
+ case "$line" in
28
+ '#'*) continue ;;
29
+ esac
30
+ trimmed=$(printf '%s' "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
31
+ if [ -n "$trimmed" ]; then
32
+ SUBJECT="$trimmed"
33
+ break
34
+ fi
35
+ done < "$MSG_FILE"
36
+
37
+ if [ -z "$SUBJECT" ]; then
38
+ echo "cleo commit-msg hook: empty commit subject — refusing." >&2
39
+ exit 1
40
+ fi
41
+
42
+ # Bypass merge / revert / fixup / squash / amend-only metadata commits.
43
+ case "$SUBJECT" in
44
+ 'Merge '*) exit 0 ;;
45
+ 'Revert '*) exit 0 ;;
46
+ 'fixup! '*) exit 0 ;;
47
+ 'squash! '*) exit 0 ;;
48
+ 'amend! '*) exit 0 ;;
49
+ esac
50
+
51
+ # Match `T` followed by 1+ digits anywhere in the subject.
52
+ # POSIX BRE — `[0-9][0-9]*` is `\d+` equivalent.
53
+ if printf '%s' "$SUBJECT" | grep -Eq 'T[0-9]+'; then
54
+ exit 0
55
+ fi
56
+
57
+ cat >&2 <<EOF
58
+ cleo commit-msg hook: commit subject is missing a task ID.
59
+
60
+ subject: $SUBJECT
61
+
62
+ Every commit MUST reference at least one CLEO task. Examples:
63
+
64
+ feat(T1588): ship POSIX commit-msg hook
65
+ T1588 — wire hooks-install into cleo init
66
+ fix: T1588 typo
67
+
68
+ Override (audited via git shim — see T1591):
69
+
70
+ git commit --no-verify
71
+
72
+ EOF
73
+ exit 1
@@ -0,0 +1,87 @@
1
+ #!/bin/sh
2
+ # CLEO_MANAGED_HOOK v1
3
+ # T1588 — project-agnostic T-ID enforcement for every commit being pushed.
4
+ #
5
+ # git invokes this with `<remote> <url>` as args and feeds ref updates on
6
+ # stdin: `<local-ref> <local-sha> <remote-ref> <remote-sha>` (one per line).
7
+ #
8
+ # For each new commit being pushed (commits in `local-sha` that are NOT
9
+ # already in any remote ref), require a T-ID in the subject. Same allow-
10
+ # list as commit-msg: merge / revert / fixup / squash / amend.
11
+ #
12
+ # Override: `git push --no-verify` (standard git override).
13
+ #
14
+ # T1595:reconcile-extension-point
15
+ # Pre-push reconcile gate hooks here (see T1595 worker).
16
+ # Reserved range below — DO NOT remove these markers; T1595 extends here.
17
+ # T1595:reconcile-extension-point-end
18
+ #
19
+ # POSIX `/bin/sh` only. No node/pnpm dependency.
20
+ set -e
21
+
22
+ ZERO_SHA="0000000000000000000000000000000000000000"
23
+ EMPTY_TREE_SHA=""
24
+ FAIL=0
25
+ FAIL_LOG=""
26
+
27
+ check_subject() {
28
+ subject="$1"
29
+ case "$subject" in
30
+ 'Merge '*) return 0 ;;
31
+ 'Revert '*) return 0 ;;
32
+ 'fixup! '*) return 0 ;;
33
+ 'squash! '*) return 0 ;;
34
+ 'amend! '*) return 0 ;;
35
+ esac
36
+ if printf '%s' "$subject" | grep -Eq 'T[0-9]+'; then
37
+ return 0
38
+ fi
39
+ return 1
40
+ }
41
+
42
+ # Read each ref-update line from stdin.
43
+ while read -r local_ref local_sha remote_ref remote_sha; do
44
+ # Branch deletion (push :branch) — nothing to validate.
45
+ if [ "$local_sha" = "$ZERO_SHA" ]; then
46
+ continue
47
+ fi
48
+
49
+ if [ "$remote_sha" = "$ZERO_SHA" ]; then
50
+ # New branch: validate every commit reachable from local_sha that is
51
+ # NOT reachable from any other remote ref.
52
+ range="$local_sha --not --remotes"
53
+ else
54
+ range="$remote_sha..$local_sha"
55
+ fi
56
+
57
+ # Subject extraction: %s = subject. NUL-delimit for safety.
58
+ # shellcheck disable=SC2086
59
+ commits=$(git rev-list $range 2>/dev/null || true)
60
+ for sha in $commits; do
61
+ subject=$(git log -1 --pretty=%s "$sha" 2>/dev/null || true)
62
+ if [ -z "$subject" ]; then
63
+ continue
64
+ fi
65
+ if ! check_subject "$subject"; then
66
+ FAIL=1
67
+ FAIL_LOG="${FAIL_LOG} ${sha} ${subject}
68
+ "
69
+ fi
70
+ done
71
+ done
72
+
73
+ if [ "$FAIL" -ne 0 ]; then
74
+ cat >&2 <<EOF
75
+ cleo pre-push hook: refusing push — commits are missing task IDs.
76
+
77
+ ${FAIL_LOG}
78
+ Every commit MUST reference at least one CLEO task (e.g. \`T1588\`).
79
+ Fix the subjects (\`git rebase -i\` + reword), or override with:
80
+
81
+ git push --no-verify
82
+
83
+ EOF
84
+ exit 1
85
+ fi
86
+
87
+ exit 0
@@ -0,0 +1,140 @@
1
+ #!/bin/sh
2
+ # T1595 — pre-push reconcile gate (extension)
3
+ #
4
+ # This file is the reconcile-gate extension for the project's pre-push
5
+ # hook. T1588 will produce a unified pre-push hook that contains a
6
+ # sentinel block:
7
+ #
8
+ # # T1595:reconcile-extension-point
9
+ # # Pre-push reconcile gate hooks here (see T1595 worker)
10
+ #
11
+ # When T1588 lands, the contents of `reconcile_gate()` below MUST be
12
+ # inlined at that sentinel. Until then, this file is sourced as-is by
13
+ # any pre-push hook that wants the reconcile gate (see installer in
14
+ # `packages/core/src/hooks/install-pre-push.ts`, future).
15
+ #
16
+ # CONTRACT
17
+ # - POSIX shell (`/bin/sh`); no bashisms.
18
+ # - Reads pending tag from `git tag --sort=-v:refname | head -1`
19
+ # (tag-shape agnostic — works for CalVer or SemVer).
20
+ # - Calls `cleo reconcile release --tag <pending> --dry-run --json`
21
+ # and parses the aggregate `reconciled` count.
22
+ # - Drift > 0 → exit 1 with task-ID list.
23
+ # - Drift == 0 → return 0.
24
+ # - Override: env `CLEO_ALLOW_DRIFT_PUSH=1` bypasses the gate AND
25
+ # appends an audit entry to
26
+ # `${XDG_DATA_HOME:-$HOME/.local/share}/cleo/audit/drift-push-bypass.jsonl`.
27
+ # - Project-agnostic: no hardcoded branch name; default branch is
28
+ # resolved via `git symbolic-ref refs/remotes/origin/HEAD` when
29
+ # needed.
30
+ #
31
+ # EXIT CODES
32
+ # 0 — no drift, push allowed
33
+ # 1 — drift detected, push refused (or cleo CLI unavailable in
34
+ # strict mode; see CLEO_RECONCILE_STRICT below)
35
+ #
36
+ # CONFIGURATION (env)
37
+ # CLEO_ALLOW_DRIFT_PUSH=1 bypass the gate (audited)
38
+ # CLEO_RECONCILE_STRICT=1 if `cleo` CLI is missing, refuse push
39
+ # (default: warn-and-allow so first-time
40
+ # clones without cleo installed still work)
41
+ # CLEO_RECONCILE_BIN=<path> override the cleo binary path (testing)
42
+
43
+ set -eu
44
+
45
+ reconcile_gate() {
46
+ # Locate cleo CLI ------------------------------------------------------
47
+ cleo_bin="${CLEO_RECONCILE_BIN:-cleo}"
48
+ if ! command -v "$cleo_bin" >/dev/null 2>&1; then
49
+ if [ "${CLEO_RECONCILE_STRICT:-0}" = "1" ]; then
50
+ echo "ERROR: cleo CLI not found on PATH (strict mode)" >&2
51
+ echo " install cleo or unset CLEO_RECONCILE_STRICT" >&2
52
+ return 1
53
+ fi
54
+ # Soft-fail: warn but allow push. Avoids blocking fresh clones.
55
+ echo "warn: cleo CLI not found; skipping reconcile gate" >&2
56
+ return 0
57
+ fi
58
+
59
+ # Resolve the pending release tag -------------------------------------
60
+ # We use the most recent tag as the "pending" anchor. Reconcile is
61
+ # project-agnostic — it walks tasks released_in this tag's range
62
+ # regardless of CalVer vs SemVer shape.
63
+ pending_tag="$(git tag --sort=-v:refname 2>/dev/null | head -n 1 || true)"
64
+ if [ -z "$pending_tag" ]; then
65
+ # No tags yet → no release to reconcile.
66
+ return 0
67
+ fi
68
+
69
+ # Override path -------------------------------------------------------
70
+ if [ "${CLEO_ALLOW_DRIFT_PUSH:-0}" = "1" ]; then
71
+ audit_dir="${XDG_DATA_HOME:-$HOME/.local/share}/cleo/audit"
72
+ mkdir -p "$audit_dir"
73
+ audit_log="$audit_dir/drift-push-bypass.jsonl"
74
+ ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
75
+ user="${USER:-${LOGNAME:-unknown}}"
76
+ repo="$(git rev-parse --show-toplevel 2>/dev/null || echo unknown)"
77
+ head_sha="$(git rev-parse HEAD 2>/dev/null || echo unknown)"
78
+ # Write a JSONL line. Stay project-agnostic: no jq dependency.
79
+ printf '{"ts":"%s","user":"%s","repo":"%s","head":"%s","tag":"%s","reason":"CLEO_ALLOW_DRIFT_PUSH=1"}\n' \
80
+ "$ts" "$user" "$repo" "$head_sha" "$pending_tag" >> "$audit_log"
81
+ echo "warn: pre-push reconcile gate bypassed (CLEO_ALLOW_DRIFT_PUSH=1)" >&2
82
+ echo " audit: $audit_log" >&2
83
+ return 0
84
+ fi
85
+
86
+ # Run reconcile in dry-run JSON mode ----------------------------------
87
+ # We swallow non-zero exit codes from the CLI here because reconcile
88
+ # exits 2 when drift exists; we want to read the JSON regardless.
89
+ json_out="$("$cleo_bin" reconcile release --tag "$pending_tag" --dry-run --json 2>/dev/null || true)"
90
+ if [ -z "$json_out" ]; then
91
+ if [ "${CLEO_RECONCILE_STRICT:-0}" = "1" ]; then
92
+ echo "ERROR: cleo reconcile release returned empty output (strict mode)" >&2
93
+ return 1
94
+ fi
95
+ echo "warn: cleo reconcile release returned empty output; skipping gate" >&2
96
+ return 0
97
+ fi
98
+
99
+ # Parse the aggregate `reconciled` count without jq.
100
+ # The InvariantReport JSON has a top-level `"reconciled": N` integer.
101
+ # We grep the first such key (top-level always emitted before per-result
102
+ # entries). Per-result entries are nested under `results[*].details`,
103
+ # so the first match is the aggregate.
104
+ drift_count="$(printf '%s' "$json_out" \
105
+ | grep -o '"reconciled"[[:space:]]*:[[:space:]]*[0-9]\+' \
106
+ | head -n 1 \
107
+ | grep -o '[0-9]\+' \
108
+ || true)"
109
+ drift_count="${drift_count:-0}"
110
+
111
+ if [ "$drift_count" -gt 0 ] 2>/dev/null; then
112
+ echo "ERROR: pre-push reconcile gate detected drift" >&2
113
+ echo " tag: $pending_tag" >&2
114
+ echo " drift count: $drift_count shipped-but-pending task(s)" >&2
115
+ # Best-effort: extract reconciled task IDs from results[*].details.reconciled
116
+ # arrays. Strings look like "T1411" (project-agnostic prefix).
117
+ drifted_ids="$(printf '%s' "$json_out" \
118
+ | tr -d '\n' \
119
+ | grep -o '"reconciled"[[:space:]]*:[[:space:]]*\[[^]]*\]' \
120
+ | grep -o '"[A-Za-z][A-Za-z0-9_-]*"' \
121
+ | sort -u \
122
+ | tr '\n' ' ' \
123
+ || true)"
124
+ if [ -n "$drifted_ids" ]; then
125
+ echo " drifted tasks: $drifted_ids" >&2
126
+ fi
127
+ echo "" >&2
128
+ echo "Refuse push: run 'cleo reconcile release --tag $pending_tag' to" >&2
129
+ echo "reconcile, or set CLEO_ALLOW_DRIFT_PUSH=1 (audited bypass)." >&2
130
+ return 1
131
+ fi
132
+
133
+ return 0
134
+ }
135
+
136
+ # When sourced from the unified pre-push hook the function is invoked
137
+ # at the sentinel point. When run standalone (testing), invoke directly.
138
+ if [ "${T1595_SOURCED:-0}" != "1" ]; then
139
+ reconcile_gate
140
+ fi