Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
dc6a6a9
feat(holmes): surface sub-score breakdowns and versioned bundles
flyingrobots Oct 13, 2025
51ebfcb
docs(go-public): link outstanding tasks to tracking issues
flyingrobots Oct 13, 2025
f9545bd
ci: run quick CLI check on all PRs
flyingrobots Oct 13, 2025
3e6f1ca
Updates to pre-commit logic to help detect package-lock drift
flyingrobots Oct 26, 2025
79cc863
WIP planning some moriarty enhancements
flyingrobots Oct 26, 2025
6b6cf1f
Add Moriarty scenario docs, test helpers, scenario tests
flyingrobots Oct 26, 2025
d0858b2
Merge branch 'main' into feat/website
flyingrobots Oct 26, 2025
fd5a861
Moriarty tests
flyingrobots Oct 26, 2025
59d2e86
feat(holmes): MP-01..03 — MergePlanner stubs, --project-merge flag, p…
flyingrobots Oct 27, 2025
cec48de
feat(holmes): MP-04 — implement merge-tree clean-merge computation (t…
flyingrobots Oct 27, 2025
ed31a11
feat(holmes): MP-05 — worktree fallback with conflict detection and m…
flyingrobots Oct 27, 2025
a65124d
feat(holmes): MP-06 (partial) — penalize readiness confidence when pr…
flyingrobots Oct 27, 2025
3d36925
docs(policy): add HOLMES/Moriarty policy spec and ready-to-use templa…
flyingrobots Oct 27, 2025
bfb9f5d
docs(policy): add policies README (overview + links to spec and templ…
flyingrobots Oct 27, 2025
0f39f02
Merge pull request #286 from flyingrobots/feat/merge-projection-mp05
flyingrobots Oct 27, 2025
871d096
Merge pull request #321 from flyingrobots/moriarty-tests
flyingrobots Oct 27, 2025
d99dda0
ci(holmes): detect schema and expose bundle_dir/schema for downstream…
flyingrobots Oct 27, 2025
64fbca8
Merge remote-tracking branch 'origin/ci/holmes-detect-schema-2025-10-…
flyingrobots Oct 27, 2025
ff32c0d
Merge remote-tracking branch 'origin/feat/website' into integration/h…
flyingrobots Oct 27, 2025
084f2b1
Merge remote-tracking branch 'origin/feat/merge-projection-mp01-03' i…
flyingrobots Oct 27, 2025
1a31216
Merge branch 'feat/holmes-score-breakdown' into integration/holmes-20…
flyingrobots Oct 27, 2025
4ee4134
Merge remote-tracking branch 'origin/feat/moriarty-enhancements' into…
flyingrobots Oct 27, 2025
c995e7a
fix: dedupe BUNDLE_VERSION and bundleVersion in GenerationPipeline af…
flyingrobots Oct 27, 2025
794e205
merge(holmes): resolve checkout version conflict; standardize to acti…
flyingrobots Oct 29, 2025
0cb3c34
fix(host-node): writer.writeFiles respects provided baseDir fallback …
flyingrobots Oct 29, 2025
3c995bb
docs(chronicles): append correction linking line 42 literal timestamp…
flyingrobots Oct 29, 2025
c089837
fix(core/scoring): normalize fallback UID to 'col:Table.field' everyw…
flyingrobots Oct 29, 2025
50b0db0
chore(uid): unify table UIDs to 'tbl:Table' in JS and core Zod genera…
flyingrobots Oct 29, 2025
5a34be0
docs(contributing): add UID conventions for EvidenceMap (tbl:/col:) a…
flyingrobots Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/bin/env bash
set -euo pipefail

# Auto-sync pnpm-lock.yaml when dependency resolution manifests change.
# - Triggers only when staged changes affect fields that impact resolution:
# dependencies/devDependencies/optionalDependencies/peerDependencies,
# overrides (npm or pnpm.overrides), pnpm.peerDependencyRules,
# pnpm.packageExtensions, workspaces, or pnpm-workspace.yaml.
# - Also triggers when the root package.json `packageManager` field changes
# (e.g., pnpm version bump or switching package managers).
# - Runs `pnpm install --lockfile-only` and stages pnpm-lock.yaml.
# - Blocks commit only if pnpm is missing or wrong version AND an update is required.
# Skip entirely with: WESLEY_SKIP_LOCKFILE_HOOK=1

if [[ "${WESLEY_SKIP_LOCKFILE_HOOK:-0}" == "1" ]]; then
echo "⚠️ Skipping lockfile guard (WESLEY_SKIP_LOCKFILE_HOOK=1)."
exit 0
fi

# Only act if package manifests are staged
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
if ! echo "$STAGED_FILES" | grep -E -q '(^|/)package.json$|(^|/)pnpm-workspace.yaml$'; then
exit 0
fi
Comment on lines +21 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Filename robustness: prefer NUL-delimited plumbing.

Newlines in paths are rare but possible. Use -z and mapfile.

-STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
+mapfile -d '' -t _STAGED_FILES < <(git diff --cached --name-only -z --diff-filter=ACMR)
+STAGED_FILES=$(printf '%s\n' "${_STAGED_FILES[@]}")
 
-while IFS= read -r f; do
+while IFS= read -r f; do
   [[ -z "$f" ]] && continue
   if [[ "$f" =~ (^|/)package.json$ ]]; then
     if check_pkg_change "$f"; then
       NEEDS_UPDATE=1
       break
     fi
   fi
 done < <(echo "$STAGED_FILES")

Also applies to: 83-93

🤖 Prompt for AI Agents
.githooks/pre-commit around lines 21-24 (and similarly update lines 83-93): the
script reads staged filenames as newline-delimited which breaks on filenames
containing newlines; switch to NUL-delimited plumbing by calling git diff
--cached --name-only --diff-filter=ACMR -z, then consume the NUL-separated list
into a bash array using mapfile -d '' or a while read -r -d '' loop; replace the
grep check with a loop or an array check that inspects each filename (or use
printf '%s\0' "${STAGED_FILES[@]}" | grep -z -E
'(^|/)package.json$|(^|/)pnpm-workspace.yaml$') so all filename handling is
NUL-safe, and apply the same changes to the block at lines 83-93.


echo "🔎 Checking if staged manifest changes affect the lockfile..."

NEEDS_UPDATE=0

# If pnpm-workspace.yaml changed, lockfile may need update (importers layout)
if echo "$STAGED_FILES" | grep -Eq '(^|/)pnpm-workspace.yaml$'; then
NEEDS_UPDATE=1
fi

Comment on lines +26 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Node.js may be missing; you call it unguarded.

If only package.json is staged and Node isn’t installed, the hook dies mid-flight. Fail early with a clear message (but don’t require Node for pure workspace changes).

 echo "🔎 Checking if staged manifest changes affect the lockfile..."
 
 NEEDS_UPDATE=0
 
+# If any package.json is staged, ensure Node is available for JSON diffs
+if echo "$STAGED_FILES" | grep -Eq '(^|/)package.json$'; then
+  if ! command -v node >/dev/null 2>&1; then
+    echo "❌ Node.js not found; required to analyze staged package.json diffs." >&2
+    echo "   Install Node.js (or set WESLEY_SKIP_LOCKFILE_HOOK=1 to skip this guard)." >&2
+    exit 1
+  fi
+fi
+
 # If pnpm-workspace.yaml changed, lockfile may need update (importers layout)
 if echo "$STAGED_FILES" | grep -Eq '(^|/)pnpm-workspace.yaml$'; then
   NEEDS_UPDATE=1
 fi

Also applies to: 83-93

# Helper: determine if a package.json change affects resolution using Node (no jq dependency)
check_pkg_change() {
local path="$1"
# Read staged version of the file
local staged_json
if ! staged_json=$(git show ":$path" 2>/dev/null); then
# Deleted or unreadable in index -> treat as affecting
return 0
fi
# Read HEAD version of the file (may not exist)
local head_json
head_json=$(git show "HEAD:$path" 2>/dev/null || echo '{}')
# Use node to compare only resolution-impacting fields
node -e '
function pick(x){
if(!x||typeof x!="object") return {};
const out={
dependencies:x.dependencies||{},
devDependencies:x.devDependencies||{},
optionalDependencies:x.optionalDependencies||{},
peerDependencies:x.peerDependencies||{},
overrides:x.overrides||{},
pnpmOverrides:(x.pnpm&&x.pnpm.overrides)||{},
pnpmPeerRules:(x.pnpm&&x.pnpm.peerDependencyRules)||{},
pnpmPackageExtensions:(x.pnpm&&x.pnpm.packageExtensions)||{},
workspaces:x.workspaces||null
};
// stable stringify with sorted keys
function sort(obj){
if(Array.isArray(obj)) return obj.map(sort);
if(obj&&typeof obj==="object"){ return Object.keys(obj).sort().reduce((a,k)=>{a[k]=sort(obj[k]);return a;},{}); }
return obj;
}
return JSON.stringify(sort(out));
}
Comment on lines +61 to +69

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Optional: treat workspaces array as an unordered set.

Re-ordering workspaces shouldn’t force a lockfile update. Normalize string arrays by sorting.

-      function sort(obj){
-        if(Array.isArray(obj)) return obj.map(sort);
+      function sort(obj){
+        if(Array.isArray(obj)) {
+          // Sort arrays of strings for stability; deep-sort objects within arrays otherwise.
+          if (obj.every(v => typeof v === "string")) return [...obj].sort();
+          return obj.map(sort);
+        }
         if(obj&&typeof obj==="object"){ return Object.keys(obj).sort().reduce((a,k)=>{a[k]=sort(obj[k]);return a;},{}); }
         return obj;
       }
🤖 Prompt for AI Agents
.githooks/pre-commit around lines 61-69: the current sort function preserves
array order which causes re-ordering of workspace entries to trigger unnecessary
lockfile changes; update the sorter so that when an array consists solely of
primitive strings (or when the key name is "workspaces") it returns a new array
sorted lexicographically before recursing, otherwise keep existing behavior for
arrays of objects. Ensure sorting is stable and does not mutate inputs (return
new arrays/objects) so JSON.stringify(sort(out)) produces a canonical
representation that treats workspaces as an unordered set.

const fs=require("node:fs");
const staged=JSON.parse(fs.readFileSync(0,"utf8"));
const head=JSON.parse(process.argv[1]||"{}");
const s=pick(staged), h=pick(head);
process.exit(s===h? 10: 0);
' "${head_json}" <<<"${staged_json}"
Comment on lines +47 to +75

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Feeding full JSON via argv risks ARG_MAX limits; use file descriptors.

Large manifests can exceed shell arg limits. Pass HEAD via FD and staged via STDIN.

-  node -e '
+  node - <<'NODE'
     function pick(x){
       if(!x||typeof x!="object") return {};
       const out={
         dependencies:x.dependencies||{},
         devDependencies:x.devDependencies||{},
         optionalDependencies:x.optionalDependencies||{},
         peerDependencies:x.peerDependencies||{},
         overrides:x.overrides||{},
         pnpmOverrides:(x.pnpm&&x.pnpm.overrides)||{},
         pnpmPeerRules:(x.pnpm&&x.pnpm.peerDependencyRules)||{},
         pnpmPackageExtensions:(x.pnpm&&x.pnpm.packageExtensions)||{},
         workspaces:x.workspaces||null
       };
       // stable stringify with sorted keys
       function sort(obj){
-        if(Array.isArray(obj)) return obj.map(sort);
+        if(Array.isArray(obj)) return obj.map(sort);
         if(obj&&typeof obj==="object"){ return Object.keys(obj).sort().reduce((a,k)=>{a[k]=sort(obj[k]);return a;},{}); }
         return obj;
       }
       return JSON.stringify(sort(out));
     }
-    const fs=require("node:fs");
-    const staged=JSON.parse(fs.readFileSync(0,"utf8"));
-    const head=JSON.parse(process.argv[1]||"{}");
+    const fs=require("node:fs");
+    const staged=JSON.parse(fs.readFileSync(0,"utf8"));
+    const head=JSON.parse(fs.readFileSync(3,"utf8"));
     const s=pick(staged), h=pick(head);
     process.exit(s===h? 10: 0);
-  ' "${head_json}" <<<"${staged_json}"
+NODE
+  3<<<"${head_json}" <<<"${staged_json}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Use node to compare only resolution-impacting fields
node -e '
function pick(x){
if(!x||typeof x!="object") return {};
const out={
dependencies:x.dependencies||{},
devDependencies:x.devDependencies||{},
optionalDependencies:x.optionalDependencies||{},
peerDependencies:x.peerDependencies||{},
overrides:x.overrides||{},
pnpmOverrides:(x.pnpm&&x.pnpm.overrides)||{},
pnpmPeerRules:(x.pnpm&&x.pnpm.peerDependencyRules)||{},
pnpmPackageExtensions:(x.pnpm&&x.pnpm.packageExtensions)||{},
workspaces:x.workspaces||null
};
// stable stringify with sorted keys
function sort(obj){
if(Array.isArray(obj)) return obj.map(sort);
if(obj&&typeof obj==="object"){ return Object.keys(obj).sort().reduce((a,k)=>{a[k]=sort(obj[k]);return a;},{}); }
return obj;
}
return JSON.stringify(sort(out));
}
const fs=require("node:fs");
const staged=JSON.parse(fs.readFileSync(0,"utf8"));
const head=JSON.parse(process.argv[1]||"{}");
const s=pick(staged), h=pick(head);
process.exit(s===h? 10: 0);
' "${head_json}" <<<"${staged_json}"
# Use node to compare only resolution-impacting fields
node - <<'NODE'
function pick(x){
if(!x||typeof x!="object") return {};
const out={
dependencies:x.dependencies||{},
devDependencies:x.devDependencies||{},
optionalDependencies:x.optionalDependencies||{},
peerDependencies:x.peerDependencies||{},
overrides:x.overrides||{},
pnpmOverrides:(x.pnpm&&x.pnpm.overrides)||{},
pnpmPeerRules:(x.pnpm&&x.pnpm.peerDependencyRules)||{},
pnpmPackageExtensions:(x.pnpm&&x.pnpm.packageExtensions)||{},
workspaces:x.workspaces||null
};
// stable stringify with sorted keys
function sort(obj){
if(Array.isArray(obj)) return obj.map(sort);
if(obj&&typeof obj==="object"){ return Object.keys(obj).sort().reduce((a,k)=>{a[k]=sort(obj[k]);return a;},{}); }
return obj;
}
return JSON.stringify(sort(out));
}
const fs=require("node:fs");
const staged=JSON.parse(fs.readFileSync(0,"utf8"));
const head=JSON.parse(fs.readFileSync(3,"utf8"));
const s=pick(staged), h=pick(head);
process.exit(s===h? 10: 0);
NODE
3<<<"${head_json}" <<<"${staged_json}"
🤖 Prompt for AI Agents
.githooks/pre-commit around lines 47 to 75: the script currently reads the HEAD
JSON from process.argv which risks exceeding shell ARG_MAX for large manifests;
change it to read HEAD from a file descriptor instead of argv and continue
reading staged JSON from STDIN so both large payloads avoid arg limits — open
the extra FD (e.g. fd 3) in the hook invocation and replace process.argv[1]
usage with reading that FD via fs.readFileSync(3, "utf8") (falling back to "{}"
if empty), leaving staged read from STDIN unchanged, and ensure exit codes/logic
remain the same.

case $? in
0) return 0 ;; # changed
10) return 1 ;; # no change
*) return 0 ;; # on parser error, be safe and assume change
esac
}

# Evaluate all staged package.json files
while IFS= read -r f; do
[[ -z "$f" ]] && continue
if [[ "$f" =~ (^|/)package.json$ ]]; then
if check_pkg_change "$f"; then
NEEDS_UPDATE=1
break
fi
fi
done < <(echo "$STAGED_FILES")

# Additionally, if root package.json's packageManager changed, require action
if echo "$STAGED_FILES" | grep -qx "package.json"; then
ROOT_STAGED=$(git show ":package.json" 2>/dev/null || echo '{}')
ROOT_HEAD=$(git show "HEAD:package.json" 2>/dev/null || echo '{}')
PM_CHANGE_OUTPUT=$(node -e '
try{
const fs=require("node:fs");
const staged=JSON.parse(fs.readFileSync(0,"utf8"));
const head=JSON.parse(process.argv[1]||"{}");
const s=staged.packageManager||""; const h=head.packageManager||"";
if(s!==h){ console.log(`${h}=>${s}`); process.exit(0);} else process.exit(10);
}catch{ process.exit(0) }
' "${ROOT_HEAD}" <<<"${ROOT_STAGED}" || true)
PM_CHANGED=$?
if [[ "$PM_CHANGED" -eq 0 ]]; then
PM_NEW="${PM_CHANGE_OUTPUT#*=>}"
PM_OLD="${PM_CHANGE_OUTPUT%=>*}"
# If switching away from pnpm, block with guidance (this repo standardizes on pnpm)
if [[ ! "$PM_NEW" =~ ^pnpm@ ]]; then
echo "❌ packageManager changed from '${PM_OLD:-<none>}' to '${PM_NEW:-<none>}', which is not pnpm." >&2
echo " This repository is configured for pnpm; update CI/workflows and lockfiles in a dedicated PR if switching managers." >&2
echo " To proceed with pnpm, set packageManager to 'pnpm@<version>' and re-run the commit." >&2
exit 1
fi
# Switching pnpm version: ensure lockfile is regenerated
NEEDS_UPDATE=1
fi
fi
Comment on lines +98 to +121

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

packageManager change detection is broken (always triggers).

You throw away Node’s exit code with command substitution and || true, then read $? (which is 0 from the assignment). Result: you always think packageManager changed and may block every commit that stages the root package.json.

Apply this minimal, correct logic keyed on output presence:

-  PM_CHANGE_OUTPUT=$(node -e '
+  PM_CHANGE_OUTPUT=$(node -e '
     try{
       const fs=require("node:fs");
       const staged=JSON.parse(fs.readFileSync(0,"utf8"));
       const head=JSON.parse(process.argv[1]||"{}");
       const s=staged.packageManager||""; const h=head.packageManager||"";
       if(s!==h){ console.log(`${h}=>${s}`); process.exit(0);} else process.exit(10);
     }catch{ process.exit(0) }
-  ' "${ROOT_HEAD}" <<<"${ROOT_STAGED}" || true)
-  PM_CHANGED=$?
-  if [[ "$PM_CHANGED" -eq 0 ]]; then
+  ' "${ROOT_HEAD}" <<<"${ROOT_STAGED}" 2>/dev/null || true)
+  if [[ -n "$PM_CHANGE_OUTPUT" ]]; then
     PM_NEW="${PM_CHANGE_OUTPUT#*=>}"
     PM_OLD="${PM_CHANGE_OUTPUT%=>*}"
     # If switching away from pnpm, block with guidance (this repo standardizes on pnpm)
     if [[ ! "$PM_NEW" =~ ^pnpm@ ]]; then
       echo "❌ packageManager changed from '${PM_OLD:-<none>}' to '${PM_NEW:-<none>}', which is not pnpm." >&2
       echo "   This repository is configured for pnpm; update CI/workflows and lockfiles in a dedicated PR if switching managers." >&2
       echo "   To proceed with pnpm, set packageManager to 'pnpm@<version>' and re-run the commit." >&2
       exit 1
     fi
     # Switching pnpm version: ensure lockfile is regenerated
     NEEDS_UPDATE=1
   fi
🤖 Prompt for AI Agents
In .githooks/pre-commit around lines 98 to 121, the current command substitution
swallows Node’s exit code with "|| true" and then reads $? (always 0), so the
script always thinks packageManager changed; fix by not discarding output/exit
and instead test the presence of the output string: assign PM_CHANGE_OUTPUT from
the node invocation without "|| true", remove the PM_CHANGED numeric check, and
replace it with a conditional like "if [[ -n \"$PM_CHANGE_OUTPUT\" ]]; then" (or
set PM_CHANGED based on -n "$PM_CHANGE_OUTPUT"); keep the existing parsing of
PM_NEW/PM_OLD and remaining logic the same.


if [[ "$NEEDS_UPDATE" != "1" ]]; then
echo "✅ Manifest changes do not impact lockfile; skipping pnpm install"
exit 0
fi

echo "🧩 Manifest changes affect dependency resolution. Updating pnpm-lock.yaml..."

# Ensure pnpm exists and version matches only when we need to update
if ! command -v pnpm >/dev/null 2>&1; then
echo "❌ pnpm not found on PATH." >&2
echo " Tip: corepack enable && corepack prepare pnpm@<version> --activate" >&2
exit 1
fi

REQ=$(node -e "try{const pm=require('./package.json').packageManager||'';if(pm.startsWith('pnpm@')){console.log(pm.split('@')[1])}}catch{process.exit(0)}" || true)
HAVE=$(pnpm --version || true)
if [[ -n "${REQ:-}" && "${HAVE:-}" != "$REQ" ]]; then
echo "❌ pnpm version mismatch: required $REQ (from packageManager), found $HAVE." >&2
echo " Run: corepack prepare pnpm@$REQ --activate" >&2
exit 1
fi
Comment on lines +137 to +143

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

pnpm version check reads working tree, not staged manifest.

You may enforce the wrong version if the staged package.json differs from the working tree. Read from the index first.

-REQ=$(node -e "try{const pm=require('./package.json').packageManager||'';if(pm.startsWith('pnpm@')){console.log(pm.split('@')[1])}}catch{process.exit(0)}" || true)
+REQ=$(node -e '
+  const fs=require("node:fs");
+  let data="{}";
+  try{ data=fs.readFileSync(0,"utf8"); }catch{}
+  try{
+    const pm=(JSON.parse(data).packageManager||"");
+    if(pm.startsWith("pnpm@")) console.log(pm.split("@")[1]);
+  }catch{}
+' <<<"$(git show ':package.json' 2>/dev/null || cat package.json 2>/dev/null || echo '{}')" || true)
🤖 Prompt for AI Agents
.githooks/pre-commit around lines 137-143: the hook currently reads package.json
from the working tree which can differ from the staged manifest; change the node
command so it reads the staged package.json from the index (e.g., via `git show
:package.json`) and falls back to the working-tree file if not staged, then
extract packageManager as before; keep error handling/silent exit semantics and
ensure the rest of the pnpm-version comparison logic is unchanged.


pnpm install --lockfile-only

if ! git diff --quiet -- pnpm-lock.yaml; then
git add pnpm-lock.yaml
echo "✅ Updated and staged pnpm-lock.yaml"
else
echo "✅ pnpm-lock.yaml already up to date"
fi
Comment on lines +147 to +152

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Untracked pnpm-lock.yaml won’t be staged (git diff ignores untracked).

If the lockfile is newly created, your check stays quiet and you skip git add, leaving the commit inconsistent.

Use porcelain status to detect both modified and untracked:

-if ! git diff --quiet -- pnpm-lock.yaml; then
-  git add pnpm-lock.yaml
-  echo "✅ Updated and staged pnpm-lock.yaml"
-else
-  echo "✅ pnpm-lock.yaml already up to date"
-fi
+if test -n "$(git status --porcelain=v1 -- pnpm-lock.yaml)"; then
+  git add pnpm-lock.yaml
+  echo "✅ Updated and staged pnpm-lock.yaml"
+else
+  echo "✅ pnpm-lock.yaml already up to date"
+fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ! git diff --quiet -- pnpm-lock.yaml; then
git add pnpm-lock.yaml
echo "✅ Updated and staged pnpm-lock.yaml"
else
echo "✅ pnpm-lock.yaml already up to date"
fi
if test -n "$(git status --porcelain=v1 -- pnpm-lock.yaml)"; then
git add pnpm-lock.yaml
echo "✅ Updated and staged pnpm-lock.yaml"
else
echo "✅ pnpm-lock.yaml already up to date"
fi
🤖 Prompt for AI Agents
In .githooks/pre-commit around lines 147 to 152, the script uses `git diff
--quiet -- pnpm-lock.yaml` which ignores untracked files so a newly created
pnpm-lock.yaml won’t be detected or staged; replace that check with a porcelain
status check that detects both modified and untracked states (for example run
`git status --porcelain -- pnpm-lock.yaml` and test for non-empty output) and if
non-empty run `git add pnpm-lock.yaml` and echo the staged message.


exit 0
29 changes: 25 additions & 4 deletions .github/workflows/cli-quick.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ name: Wesley CLI Quick Check

on:
push:
paths:
- 'packages/wesley-cli/**'
branches:
- main
pull_request:
paths:
- 'packages/wesley-cli/**'
types: [opened, synchronize, reopened]

env:
FORCE_COLOR: 1
Expand All @@ -25,33 +24,52 @@ jobs:
with:
submodules: recursive # For Bats plugins

- name: Detect CLI changes
id: changes
uses: dorny/paths-filter@v3
with:
filters: |
cli:
- 'packages/wesley-cli/**'

- name: Skip check (no CLI changes)
if: steps.changes.outputs.cli != 'true'
run: |
echo "No changes under packages/wesley-cli; skipping quick CLI tests."
echo "::notice title=Quick Check Skipped::No CLI changes detected"
Comment on lines +27 to +39

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Change detection is correct but notification could be clearer.

Using dorny/paths-filter to skip CLI tests when no CLI changes exist is smart and saves CI time. The skip notification on lines 38-39 is visible.

BUT: The workflow still shows as "success" even when everything was skipped. Users might not notice the notice. Consider:

  1. Renaming the job from "Quick CLI Test" to "Quick CLI Test (conditional)"
  2. Adding a final summary step that explicitly states whether tests ran or were skipped
  3. Using a neutral exit instead of success when skipped (though that's noisier)

Current approach is acceptable but could be more obvious.

🤖 Prompt for AI Agents
.github/workflows/cli-quick.yml lines 27 to 39: The workflow currently skips the
CLI tests when no CLI changes are detected but the job still appears as a
successful run and the skip notice may be missed; rename the job display name to
"Quick CLI Test (conditional)" and add a final step that prints an explicit
one-line summary like "Quick CLI Test: RUN" or "Quick CLI Test: SKIPPED"
depending on steps.changes.outputs.cli, ensuring the summary step runs
unconditionally so users see whether tests ran or were skipped; optionally, if
you prefer more visibility, set the skip branch to exit with a
neutral/non-success status or annotate the run with ::warning or ::notice in
that summary step.


- name: Setup pnpm
if: steps.changes.outputs.cli == 'true'
uses: pnpm/action-setup@v4
- name: Verify pnpm version
run: |
echo "pnpm: $(pnpm --version)"
node -v

Comment on lines 44 to 48

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Why verify pnpm version when skipping all CLI tests?

Lines 42-43 conditionally setup pnpm only if CLI changes exist. But lines 44-48 ALWAYS verify pnpm version, even when steps.changes.outputs.cli != 'true'.

This means:

  1. pnpm won't be installed (step skipped)
  2. Verification runs anyway (no condition)
  3. Fails or prints misleading output

Either add the conditional guard to this step too, or remove it entirely (setup-pnpm already validates the version).

Apply this diff:

     - name: Verify pnpm version
+      if: steps.changes.outputs.cli == 'true'
       run: |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Verify pnpm version
run: |
echo "pnpm: $(pnpm --version)"
node -v
- name: Verify pnpm version
if: steps.changes.outputs.cli == 'true'
run: |
echo "pnpm: $(pnpm --version)"
node -v
🤖 Prompt for AI Agents
In .github/workflows/cli-quick.yml around lines 44 to 48, the pnpm version
verification step runs unconditionally even when the pnpm setup step is skipped,
causing failures or misleading output; add the same conditional guard (if:
steps.changes.outputs.cli == 'true') to this step so it only runs when CLI
changes exist (alternatively remove the step entirely if you prefer relying on
setup-pnpm's validation).

- name: Setup Node.js (LTS)
if: steps.changes.outputs.cli == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'

- name: Install Bats
if: steps.changes.outputs.cli == 'true'
run: |
curl -LO https://github.com/bats-core/bats-core/archive/v1.12.0.tar.gz
tar -xzf v1.12.0.tar.gz
sudo ./bats-core-1.12.0/install.sh /usr/local
echo "/usr/local/bin" >> $GITHUB_PATH

- name: Install dependencies
if: steps.changes.outputs.cli == 'true'
run: |
pnpm install --frozen-lockfile
- name: Verify lockfile unchanged
run: git diff --exit-code -- pnpm-lock.yaml

Comment on lines 68 to 70

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Lockfile check runs unconditionally but dependencies were conditionally installed.

Line 65-67 installs dependencies ONLY when CLI changes exist. But line 68-70 verifies lockfile ALWAYS.

If no CLI changes → no pnpm install → git diff checks nothing meaningful. This step will pass vacuously, giving false confidence.

Move lockfile verification inside the conditional block OR make dependency installation unconditional (if you want to enforce lockfile hygiene on every push).

Apply this diff:

     - name: Verify lockfile unchanged
+      if: steps.changes.outputs.cli == 'true'
       run: git diff --exit-code -- pnpm-lock.yaml
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Verify lockfile unchanged
run: git diff --exit-code -- pnpm-lock.yaml
- name: Verify lockfile unchanged
if: steps.changes.outputs.cli == 'true'
run: git diff --exit-code -- pnpm-lock.yaml
🤖 Prompt for AI Agents
In .github/workflows/cli-quick.yml around lines 68 to 70, the "Verify lockfile
unchanged" step runs unconditionally while the pnpm install step (lines ~65-67)
runs only when CLI changes are detected, so the lockfile check can pass
vacuously; fix this by either moving the lockfile verification step inside the
same conditional block that runs pnpm install (so it only runs when dependencies
were installed), or make dependency installation unconditional (run pnpm install
for every workflow run) so the lockfile check is valid; ensure the YAML step
ordering and indentation are updated accordingly so the verification step
executes only after the install when appropriate.

- name: Initialize git for CLI tests
if: steps.changes.outputs.cli == 'true'
run: |
cd packages/wesley-cli
git init .
Expand All @@ -61,12 +79,14 @@ jobs:
git commit -m "CI test commit" || echo "Nothing to commit"

- name: Run CLI tests
if: steps.changes.outputs.cli == 'true'
run: |
cd packages/wesley-cli
echo "🧪 Running Wesley CLI Tests..."
pnpm test

- name: Smoke test CLI functionality
if: steps.changes.outputs.cli == 'true'
run: |
cd packages/wesley-cli
echo ""
Expand All @@ -81,6 +101,7 @@ jobs:
echo 'type Query { hello: String }' | node ../wesley-host-node/bin/wesley.mjs generate --schema - --json --quiet || echo "✓ Expected parser failure" || true

- name: Success notification
if: steps.changes.outputs.cli == 'true'
run: |
echo "✅ Wesley CLI quick check passed!"
echo "All 12 end-to-end tests successful"
Expand Down
Loading
Loading