chore(tooling): piso de calidad — ESLint, Prettier, lefthook, commitlint y CI - #2
Conversation
Commit solo de formato, sin cambios de comportamiento. Se aísla acá para que el diff del tooling se pueda leer, y queda en .git-blame-ignore-revs para que no ensucie los blame. Arrastra el cambio de dominio custom en wrangler.jsonc que estaba sin commitear en el working tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El repo no tenía ningún gate de calidad. Se replica el piso que ya usa exactamente-frontend, adaptado a este stack. - ESLint (flat config) + Prettier, con eslint-config-prettier para que no se peleen por formato - lefthook: pre-commit formatea y lintea lo staged y corre los tests afectados; pre-push corre typecheck y la suite completa; commit-msg valida Conventional Commits - CI en PRs a main: typecheck, lint, format:check, test y build:cf - Deploy a Cloudflare encadenado a que CI pase en main Se unifica en pnpm: se elimina package-lock.json (convivía con pnpm-lock.yaml) y se declara packageManager. El lockfile de pnpm estaba desactualizado porque wrangler se había instalado con npm. typescript queda en ^6 y no ^7: typescript-eslint 8.x todavía no soporta TS 7 y el lint no arranca. eslint.config va como .mjs en vez de .js porque el paquete no declara "type": "module" y agregarlo cambiaría cómo se resuelve el bin publicado. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds CI and deployment workflows, repository formatting and hook configuration, agent-surface test coverage, documentation updates, and broad Prettier-style normalization across source files. ChangesRepository automation and agent-surface updates
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
package.json (1)
11-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDo not silently ignore hook installation failures.
|| truemakespnpm installsucceed even when Lefthook is misconfigured or cannot install hooks, silently disabling the local quality gate. Let installation fail, or explicitly skip it only in environments where hooks are intentionally unavailable.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@package.json` at line 11, Update the package.json prepare script to remove the unconditional “|| true” fallback from lefthook installation, allowing prepare to fail when hook installation fails; only add an explicit environment-based skip if the project already defines a supported mechanism for environments where hooks are unavailable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Line 18: Update the actions/checkout steps in .github/workflows/ci.yml lines
18-18 and .github/workflows/deploy.yml lines 19-21 to disable persisted
credentials and ensure repository contents permissions are read-only for
subsequent steps. Apply the same security configuration to both workflow
checkout steps; do not include the pinned checkout reference as part of the
comment text.
In @.github/workflows/deploy.yml:
- Around line 25-30: Remove the cache configuration from the
actions/setup-node@v7 step in the deploy job, specifically delete the cache:
pnpm setting while preserving node-version: 22 and the existing frozen-lockfile
installation.
- Around line 4-8: Restrict the privileged deployment workflow’s workflow_run
trigger to successful CI runs originating from a push to main, and ensure its
job condition validates the run event before proceeding. Do not deploy
PR-triggered runs or check out and rebuild github.event.workflow_run.head_sha;
instead consume the artifact produced by the trusted CI run.
In `@CHATGPT_SUBMISSION.md`:
- Around line 53-63: Update the download-resource row in the tool metadata table
so its readOnlyHint is false, while preserving its existing destructiveHint and
openWorldHint values.
In `@lefthook.yml`:
- Around line 1-19: Update the pre-commit configuration to run the format
command before lint and test rather than in parallel. Preserve the existing
format, lint, test, glob, skip, and stage_fixed settings, but use Lefthook’s
sequential/piped command grouping so lint and tests consume Prettier’s formatted
and restaged output.
---
Nitpick comments:
In `@package.json`:
- Line 11: Update the package.json prepare script to remove the unconditional
“|| true” fallback from lefthook installation, allowing prepare to fail when
hook installation fails; only add an explicit environment-based skip if the
project already defines a supported mechanism for environments where hooks are
unavailable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 0d7219b9-2d83-425a-8cba-7f60172ebdbd
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsonpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (38)
.git-blame-ignore-revs.github/workflows/ci.yml.github/workflows/deploy.yml.prettierignore.prettierrcCHATGPT_SUBMISSION.mdREADME.mdcommitlint.config.tseslint.config.mjslefthook.ymlpackage.jsonsrc/__tests__/agentSurface.spec.tssrc/__tests__/downloadResource.spec.tssrc/client/exactamenteApi.spec.tssrc/client/exactamenteApi.tssrc/config.spec.tssrc/config.tssrc/lib/toolShared.tssrc/middleware.tssrc/prompts/buscar-materiales-de-materia.tssrc/prompts/descargar-parcial.tssrc/prompts/diagnosticar-conexion.tssrc/prompts/explorar-carrera.tssrc/resources/(exactamente)/meta.tssrc/resources/(subjects)/[subjectId]/details.tssrc/tools/download-resource.tssrc/tools/find-subject-materials.tssrc/tools/get-subject.tssrc/tools/health-check.tssrc/tools/list-careers.tssrc/tools/list-faculties.tssrc/tools/list-resources.tssrc/tools/list-universities.tssrc/tools/search-subjects.tssrc/types/worker.d.tssrc/worker.tswrangler.jsoncxmcp.config.ts
| quality: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v7 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow files =="
git ls-files '.github/workflows/*' || true
echo
echo "== ci.yml relevant lines =="
if [ -f .github/workflows/ci.yml ]; then
nl -ba .github/workflows/ci.yml | sed -n '1,120p'
fi
echo
echo "== deploy.yml relevant lines =="
if [ -f .github/workflows/deploy.yml ]; then
nl -ba .github/workflows/deploy.yml | sed -n '1,140p'
fi
echo
echo "== occurrences of action/checkout and persist-credentials / permissions =="
rg -n "actions/checkout|persist-credentials|permissions:|GITHUB_TOKEN|GitHub Token|github-token" .github/workflows || trueRepository: exactamente-ar/exactamente-mcp
Length of output: 313
🌐 Web query:
actions/checkout persist-credentials default true README
💡 Result:
The persist-credentials input for the actions/checkout GitHub Action controls whether the repository's authentication token or SSH key is configured in the local git environment [1][2][3]. Key details include: - Default Value: The default is true [3][4]. - Functionality: When set to true, the action configures the token or SSH key with the local git config (or, in more recent versions, a secure file location managed by the runner) [1][5][6]. This allows subsequent steps in your workflow to execute authenticated git commands, such as git fetch or git push, without needing to manually provide credentials [1][5]. - Security: The token is automatically removed by the action during the post-job cleanup phase [1][7]. - Opt-out: You can disable this behavior by explicitly setting persist-credentials: false in your workflow file if you do not need git commands to be authenticated or prefer to manage credentials manually [1][7]. Beginning with version v6.0.0, the action was updated to store these credentials in a secure location under $RUNNER_TEMP rather than directly in the local.git/config file, improving security for the persisted credentials [5][6].
Citations:
- 1: https://github.com/actions/checkout?tab=readme-ov-file
- 2: https://github.com/actions/checkout/blob/main/README.md
- 3: https://github.com/actions/checkout/blob/de0fac2e4500dabe0009e67214ff5f5447ce83dd/action.yml
- 4: https://github.com/marketplace/actions/checkout?version=v3.6.0
- 5: https://github.com/actions/checkout/tree/v6.0.2?tab=readme-ov-file
- 6: https://github.com/actions/checkout/tree/v6.0.0
- 7: https://github.com/actions/checkout
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow files =="
git ls-files '.github/workflows/*'
echo
echo "== ci.yml relevant lines =="
if [ -f .github/workflows/ci.yml ]; then
awk '{printf "%6d: %s\n", NR, $0}' .github/workflows/ci.yml | sed -n '1,160p'
fi
echo
echo "== deploy.yml relevant lines =="
if [ -f .github/workflows/deploy.yml ]; then
awk '{printf "%6d: %s\n", NR, $0}' .github/workflows/deploy.yml | sed -n '1,160p'
fi
echo
echo "== occurrences of action/checkout and permissions / persist-credentials =="
if command -v rg >/dev/null 2>&1; then
rg -n "actions/checkout@|persist-credentials|permissions:|GITHUB_TOKEN|github-token|ref:" .github/workflows || true
else
grep -RIn "actions/checkout@\\|persist-credentials\\|permissions:\\|GITHUB_TOKEN\\|github-token\\|ref:" .github/workflows || true
fiRepository: exactamente-ar/exactamente-mcp
Length of output: 2733
Drop the checkout command from this comment.
The issue is valid: both workflows use actions/checkout without persist-credentials: false and without read-only contents permissions, leaving GITHUB_TOKEN available to later repository-controlled steps. However, the pinned actions/checkout@v7 reference does not need to be part of the command here.
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 18-18: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
📍 Affects 2 files
.github/workflows/ci.yml#L18-L18(this comment).github/workflows/deploy.yml#L19-L21
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci.yml at line 18, Update the actions/checkout steps in
.github/workflows/ci.yml lines 18-18 and .github/workflows/deploy.yml lines
19-21 to disable persisted credentials and ensure repository contents
permissions are read-only for subsequent steps. Apply the same security
configuration to both workflow checkout steps; do not include the pinned
checkout reference as part of the comment text.
Sources: MCP tools, Linters/SAST tools
| on: | ||
| workflow_run: | ||
| workflows: [CI] | ||
| types: [completed] | ||
| branches: [main] |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflows =="
git ls-files .github/workflows || true
echo
echo "== deploy.yml =="
if [ -f .github/workflows/deploy.yml ]; then
cat -n .github/workflows/deploy.yml
else
fd -a 'deploy.yml' .github/workflows || true
fi
echo
echo "== references to workflow_run/deploy =="
rg -n "workflow_run|workflow_run\.(event|head_branch|head_sha|conclusion)|deployment|CLOUDFLARE|cloudflare|pages" .github/workflows || trueRepository: exactamente-ar/exactamente-mcp
Length of output: 1880
🌐 Web query:
GitHub Actions workflow_run security checkout github.event.workflow_run.head_sha head_branch push trusted artifact
💡 Result:
Using workflow_run to checkout code using github.event.workflow_run.head_sha creates a critical security vulnerability known as a pwn request [1][2]. Because workflow_run workflows run in the context of the base repository—with access to secrets, write permissions, and the ability to influence the environment—checking out and executing untrusted code from a fork allows an attacker to execute arbitrary commands with those elevated privileges [3][4][5]. Security Risks and Mechanics 1. Privilege Escalation: Even if the triggering workflow is unprivileged, the workflow_run event operates with the base repository's permissions [3][6]. Attackers can submit a pull request that triggers a workflow, which then triggers the workflow_run, effectively using the second workflow as a confused deputy to perform malicious actions [3][6]. 2. Code Execution: If you check out the head_sha of an untrusted pull request using actions/checkout and subsequently run build scripts (e.g., npm install, pnpm build) or use local actions (uses:./), those commands execute attacker-controlled code within your trusted environment [1][2]. 3. Artifact Poisoning: Artifacts from untrusted workflows are a major attack vector [3][6]. If a workflow_run downloads and processes these artifacts without rigorous validation, an attacker can exfiltrate secrets or execute malicious code [3][6]. Mitigation and Best Practices As of June 2026, actions/checkout v7 introduced safety defaults that refuse to fetch fork pull request code in workflow_run workflows triggered by pull requests [7][8]. 1. Use Separation of Concerns: The recommended pattern is to perform untrusted work in an unprivileged workflow (e.g., triggered by pull_request) and store results in artifacts [9][2]. A separate, privileged workflow (triggered by workflow_run) should then download, verify, and process those artifacts [9][2]. 2. Treat Artifacts as Untrusted: Never assume artifacts are safe. Always implement strict validation and sandboxing when handling them in a privileged context [3]. 3. Avoid Unsafe Checkouts: Do not use the allow-unsafe-pr-checkout: true flag unless absolutely necessary and after a thorough security review [10][7][5]. 4. Pin Actions: Use full commit SHAs for all action references to protect against supply-chain attacks where action tags are hijacked [8]. In summary, you should never check out or execute code from an untrusted PR source within a workflow_run context [4]. If you must process data from an untrusted PR, ensure the privileged workflow only interacts with verified artifacts, never raw source code [9][2].
Citations:
- 1: https://sisaku-security.github.io/lint/docs/ghsl/ghsl-2025-084/
- 2: https://codeql.github.com/codeql-query-help/actions/actions-untrusted-checkout-critical/
- 3: https://securitylab.github.com/resources/github-actions-new-patterns-and-mitigations/
- 4: https://docs.github.com/en/actions/reference/security/secure-use
- 5: https://github.com/actions/checkout?tab=readme-ov-file
- 6: https://github.blog/security/application-security/how-to-secure-your-github-actions-workflows-with-codeql/
- 7: https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/
- 8: https://devops-daily.com/posts/pwn-request-github-actions-checkout-v7
- 9: https://codeql.github.com/codeql-query-help/actions/actions-untrusted-checkout-medium/
- 10: https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== ci.yml =="
cat -n .github/workflows/ci.yml
echo
echo "== current workflow_run trigger configuration =="
python3 - <<'PY'
from pathlib import Path
text = Path(".github/workflows/deploy.yml").read_text()
lines = text.splitlines()
in_event = False
for i, line in enumerate(lines[1:], start=2):
if line.startswith("on:") or "workflow_run:" in line:
in_event = True
if in_event:
print(f"{i}: {line}")
if line and not line.startswith(" ") and line.rstrip(":") != "on" and line.lstrip().isdigit():
break
if in_event and i > 10:
break
PYRepository: exactamente-ar/exactamente-mcp
Length of output: 1339
Do not checkout CI’s head_sha in a privileged deploy workflow.
CI runs on both pull_request and push, but this workflow checks out github.event.workflow_run.head_sha, rebuilds it, and then applies Cloudflare deployment secrets. A PR that triggers a successful CI run can therefore cause execution under these privileged secrets; gate deployment on a successful push CI run (head_branch == main has been checked through this repo’s workflow configuration). Prefer deploying an artifact produced by CI rather than rebuilding checked-out source.
Minimum event guard
- if: github.event.workflow_run.conclusion == 'success'
+ if: >-
+ github.event.workflow_run.conclusion == 'success' &&
+ github.event.workflow_run.event == 'push' &&
+ github.event.workflow_run.head_branch == 'main'🧰 Tools
🪛 zizmor (1.26.1)
[error] 4-8: use of fundamentally insecure workflow trigger (dangerous-triggers): workflow_run is almost always used insecurely
(dangerous-triggers)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy.yml around lines 4 - 8, Restrict the privileged
deployment workflow’s workflow_run trigger to successful CI runs originating
from a push to main, and ensure its job condition validates the run event before
proceeding. Do not deploy PR-triggered runs or check out and rebuild
github.event.workflow_run.head_sha; instead consume the artifact produced by the
trusted CI run.
Sources: MCP tools, Linters/SAST tools
| - uses: actions/setup-node@v7 | ||
| with: | ||
| node-version: 22 | ||
| cache: pnpm | ||
|
|
||
| - run: pnpm install --frozen-lockfile |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files .github/workflows/deploy.yml || true
echo "== workflow excerpt =="
if [ -f .github/workflows/deploy.yml ]; then
nl -ba .github/workflows/deploy.yml | sed -n '1,80p'
fi
echo "== workflow names/triggers =="
python3 - <<'PY'
from pathlib import Path
p=Path(".github/workflows/deploy.yml")
if p.exists():
print(p.read_text(errors="replace")[:3000])
PY
echo "== similar cache usages =="
rg -n "setup-node|cache:\s*pnpm|permissions:|contents:|deployment|deploy" .github/workflows || trueRepository: exactamente-ar/exactamente-mcp
Length of output: 272
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
awk '{printf "%4d\t%s\n", NR, $0}' .github/workflows/deploy.yml | sed -n '1,80p'
echo "== cache/action usages and permissions =="
awk '{printf "%4d\t%s\n", NR, $0}' .github/workflows/deploy.yml | grep -nE 'uses: actions/setup-node|cache:|permissions:|contents:|deploy|npm|pnpm' || true
echo "== workflow files search for cache setup-node =="
while IFS= read -r f; do
echo "-- $f"
grep -nE 'uses: actions/setup-node|cache:\s*pnpm|permissions:|contents:|deployment|deploy' "$f" || true
done < <(find .github/workflows -maxdepth 1 -type f -print)Repository: exactamente-ar/exactamente-mcp
Length of output: 1621
🌐 Web query:
GitHub Actions dependency caching cache poisoning setup-node cache unsigned unauthorized
💡 Result:
Cache poisoning in GitHub Actions, particularly when using actions/setup-node, is a security risk where an attacker injects malicious content into the dependency cache [1][2][3]. Because GitHub Actions caches are shared based on the branch or tag—not the job's identity or privilege level—any workflow run that can read a cache may extract its contents [4][5]. If a subsequent, more privileged workflow (such as a release or publishing job) restores this poisoned cache, it may execute malicious code or expose sensitive secrets [1][2][3][5]. Key security considerations and mitigations include: 1. Disable Automatic Caching in Privileged Workflows: actions/setup-node enables npm caching by default if packageManager is detected in package.json [6][7]. For workflows with elevated privileges or access to secrets (e.g., publishing workflows), you should explicitly disable this by setting package-manager-cache: false [8][6][9]. 2. Use Read-Only Cache Modes: To allow untrusted triggers (like pull requests) to benefit from cached dependencies without risk of them overwriting or poisoning the cache, use the cache-write input [10]. Setting cache-write: ${{ github.event_name != 'pull_request' }} ensures that pull request workflows only read from the cache and cannot modify it [10]. 3. Understand Cache Scope and Restrictions: GitHub enforces security boundaries by limiting which triggers can write to the cache [4][5]. Low-trust triggers, such as pull_request from a fork, are typically granted read-only access to caches in the default branch's scope [5]. However, these protections do not eliminate all risks, especially if a workflow is misconfigured [4]. 4. Never Store Secrets in Caches: Caches are not signed or verified [4]. Never store secrets, tokens, or credentials in a cached path [4]. If an attacker can open a pull request, they may be able to read the contents of the cache [4]. 5. General Best Practices: Always use npm ci rather than npm install to ensure your build strictly follows the committed package-lock.json, preventing the use of arbitrary dependencies that might have been injected into a local cache [2]. Ensure you are not pairing dependency caching with privileged pull_request_target workflows or untrusted forks [2]. Technical note: A 403 error during caching often relates to branch-specific or event-based access restrictions enforced by the GitHub Actions cache service, rather than a failure of the action itself [11]. Always check official documentation for the latest guidance on cache scopes and security configurations [4][5].
Citations:
- 1: Don't cache automatically in privileged workflows actions/setup-node#1358
- 2: https://safeguard.sh/resources/blog/actions-setup-node
- 3: https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/GitHub_Actions_Security_Cheat_Sheet.md
- 4: https://docs.github.com/en/actions/reference/workflows-and-actions/dependency-caching
- 5: https://docs.github.com/en/actions/concepts/workflows-and-actions/dependency-caching
- 6: https://github.com/actions/setup-node/blob/v6/README.md
- 7: https://github.com/actions/setup-node
- 8: docs: Update caching recommendations to mitigate cache poisoning risks actions/setup-node#1567
- 9: https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md
- 10: Add cache-write input for read-only cache mode actions/setup-node#1508
- 11: Error: Cache service responded with 403 when running a job triggered by
deployment_statusactions/setup-node#341
Remove dependency caching from the privileged deploy job.
setup-node with cache: pnpm restores dependencies from the shared Actions cache, and GitHub caches are not signed/verified. Since this job uses workflow_run: CI, also checks out head_sha and then passes Cloudflare secrets to an external action, disabling caching here removes an avoidable cache-poison path.
Safer deployment setup
- uses: actions/setup-node@v7
with:
node-version: 22
- cache: pnpm📝 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.
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| - run: pnpm install --frozen-lockfile |
🧰 Tools
🪛 zizmor (1.26.1)
[error] 25-25: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): this step
(cache-poisoning)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy.yml around lines 25 - 30, Remove the cache
configuration from the actions/setup-node@v7 step in the deploy job,
specifically delete the cache: pnpm setting while preserving node-version: 22
and the existing frozen-lockfile installation.
Sources: MCP tools, Linters/SAST tools
| | Tool | Description | readOnlyHint | destructiveHint | openWorldHint | | ||
| | ------------------------ | ---------------------------------------- | ------------ | --------------- | ------------- | | ||
| | `health-check` | Check backend health status | `true` | `false` | `false` | | ||
| | `list-universities` | List available universities | `true` | `false` | `false` | | ||
| | `list-faculties` | List faculties, optionally by university | `true` | `false` | `false` | | ||
| | `list-careers` | List careers, optionally by faculty | `true` | `false` | `false` | | ||
| | `search-subjects` | Search and filter subjects | `true` | `false` | `false` | | ||
| | `get-subject` | Get detailed subject information | `true` | `false` | `false` | | ||
| | `list-resources` | List published study resources | `true` | `false` | `false` | | ||
| | `find-subject-materials` | Combined subject + resource search | `true` | `false` | `false` | | ||
| | `download-resource` | Get download URL for a resource file | `true` | `false` | `false` | |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Mark download-resource as effectful.
The agent-surface contract describes download-resource as writing, but this table sets readOnlyHint to true. Set it to false so clients do not auto-run a write as a read-only action.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CHATGPT_SUBMISSION.md` around lines 53 - 63, Update the download-resource row
in the tool metadata table so its readOnlyHint is false, while preserving its
existing destructiveHint and openWorldHint values.
| pre-commit: | ||
| parallel: true | ||
| commands: | ||
| format: | ||
| glob: '*.{ts,js,mjs,json,jsonc,md}' | ||
| run: pnpm exec prettier --write {staged_files} | ||
| stage_fixed: true | ||
| skip: | ||
| - merge | ||
| - rebase | ||
| lint: | ||
| glob: '*.ts' | ||
| run: pnpm exec eslint {staged_files} | ||
| skip: | ||
| - merge | ||
| - rebase | ||
| test: | ||
| glob: '*.ts' | ||
| run: pnpm exec vitest run --changed |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
lefthook configuration pre-commit parallel commands stage_fixed stage_fixed true documentation
💡 Result:
In Lefthook, you can combine parallel execution and automatic re-staging of modified files using the parallel and stage_fixed options within your configuration [1][2][3]. Parallel Execution: To run commands or scripts concurrently within a hook (such as pre-commit), set parallel to true at the hook level [2][4]. By default, Lefthook executes tasks sequentially [1][2]. Example of parallel configuration: pre-commit: parallel: true commands: lint: run: npm run lint --fix {staged_files} stage_fixed: true test: run: npm test stage_fixed: true stage_fixed: When stage_fixed is set to true, Lefthook automatically executes git add on the files modified by the command or script after it finishes running [1][3]. This feature is specifically designed for the pre-commit hook [3]. Key details regarding stage_fixed: - If a files option is specified for the command, Lefthook uses those files for git add [1][3]. - If no files option is provided, the {staged_files} template is used automatically [1][3]. - Any glob or exclude filters applied to the command are respected during the re-staging process [1][3]. You can also group jobs to control execution flow more granularly (e.g., mixing parallel and piped execution) using the jobs key [5][6]. Grouped jobs can have their own parallel or piped settings, allowing for complex workflows within a single hook [5][6]. Sources: [1] https://github.com/evilmartians/lefthook/blob/v1.7.15/docs/configuration.md [2] https://lefthook.dev/configuration/parallel/ [5] https://lefthook.dev/configuration/jobs/ [3] https://lefthook.dev/configuration/stage_fixed/ [6] https://lefthook.dev/configuration/group/ [4] https://github.com/evilmartians/lefthook/blob/master/README.md
Citations:
- 1: https://github.com/evilmartians/lefthook/blob/v1.7.15/docs/configuration.md
- 2: https://lefthook.dev/configuration/parallel/
- 3: https://lefthook.dev/configuration/stage_fixed/
- 4: https://github.com/evilmartians/lefthook/blob/master/README.md
- 5: https://lefthook.dev/configuration/jobs/
- 6: https://lefthook.dev/configuration/group/
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== lefthook.yml =="
if [ -f lefthook.yml ]; then
cat -n lefthook.yml
else
fd -a 'lefthook\.(ya?ml|yml)' . || true
fi
echo
echo "== pnpm prettier/eslint/vitest versions/dependencies =="
if [ -f package.json ]; then
node -e 'const p=require("./package.json"); console.log(JSON.stringify({packageManager:p.packageManager, scripts:p.scripts, dependencies:p.dependencies, devDependencies:p.devDependencies}, null, 2))'
fi
echo
echo "== lockfile entries =="
for f in pnpm-lock.yaml package-lock.json yarn.lock; do
[ -f "$f" ] && { echo "--- $f ---"; rg -n '^( +(prettier|eslint|vitest|lefthook)|[^ ]+ *):|lefthook|prettier|eslint|vitest' "$f" | head -80; }
done
echo
echo "== git staged files relevant =="
git status --short --short || true
git diff --cached --name-only -- *.{ts,js,mjs,json,jsonc,md} || trueRepository: exactamente-ar/exactamente-mcp
Length of output: 5419
🌐 Web query:
lefthook git add {staged_files} implementation stage_fixed pre-commit parallel
💡 Result:
In Lefthook, the stage_fixed option is designed specifically for the pre-commit hook to automatically stage files modified by a command or script [1][2]. When set to true, Lefthook executes git add on the files affected by your command after it completes [1][3]. Configuration and Behavior When stage_fixed: true is enabled: - Lefthook automatically calls git add on the files [1][4]. - If you have specified a files option for the command, those files are used [1][3]. - If no files option is specified, the {staged_files} template is used as the default target for git add [1][3]. - Filters such as glob and exclude are respected and applied during the staging process [1][3]. Example Configuration: pre-commit: commands: lint: run: npm run lint --fix {staged_files} stage_fixed: true Parallel Execution Risks While Lefthook supports parallel: true at the hook level to increase performance, combining this with stage_fixed: true can lead to race conditions [5][6][7]. Because multiple commands may attempt to write to the git index simultaneously, users have reported intermittent hangs or errors during the commit process [7]. If you experience these issues, it is recommended to set parallel: false for the affected hook group to ensure sequential, stable execution of the staging process [7].
Citations:
- 1: https://lefthook.dev/configuration/stage_fixed/
- 2: https://lefthook.dev/examples/stage_fixed/
- 3: https://github.com/evilmartians/lefthook/blob/v1.7.15/docs/configuration.md
- 4: feat: add stage_fixed option evilmartians/lefthook#445
- 5: https://github.com/evilmartians/lefthook/
- 6: https://evilmartians.com/chronicles/5-cool-and-surprising-ways-to-configure-lefthook-for-automation-joy
- 7:
pre-commithangs on Windows withparallel: trueevilmartians/lefthook#1256
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== package manager availability =="
command -v pnpm || true
echo "pnpm version: $(pnpm --version 2>/dev/null || true)"
echo
echo "== repository package manager files =="
cat package.json | sed -n '1,80p'
[ -f package.json ] && [ -f pnpm-lock.yaml ] && echo "repository uses pnpm via packageManager and pnpm-lock.yaml"Repository: exactamente-ar/exactamente-mcp
Length of output: 1814
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read-only probe: parse lefthook.yml and inspect file patterns used by each pre-commit command.
python3 - <<'PY'
from pathlib import Path
import re
cfg = Path("lefthook.yml").read_text()
commands = re.findall(r'^( +)([A-Za-z0-9_][A-Za-z0-9_\- ]*[A-Za-z0-9_]*)[ ]*:[ \t\n]', cfg, re.M)[-20:]
print("commands:", commands)
glob_re = re.compile(r"glob:\s*'(.*?)'|glob:\s*\"(.*?)\"")
for idx, line in enumerate(cfg.splitlines(), 1):
m = glob_re.match(line.strip())
if m:
print(f"{idx}: {m.group(1) or m.group(2)}")
PY
echo
echo "== deterministic subset overlap from lefthook.yml =="
python3 - <<'PY'
python3 - <<'PY'
import yaml
from pathlib import Path
cfg = yaml.safe_load(Path("lefthook.yml").read_text())
pre = cfg.get("pre-commit", {})
print("parallel:", pre.get("parallel"))
for name, cmd in pre.get("commands", {}).items():
print(name, "glob:", cmd.get("glob"), "stage_fixed:", cmd.get("stage_fixed"))
PY
filesets = []
for name, cmd in pre.get("commands", {}).items():
s = cmd.get("glob", "")
sets = {line: (bool("ts" in line) and line.endswith(".ts")) for line in s.split()}
filesets.append((name, sets))
for _ in filesets:
pass
PYRepository: exactamente-ar/exactamente-mcp
Length of output: 768
🌐 Web query:
lefthook source run_hook parallel stage_fixed gitAdd
💡 Result:
In Lefthook, stage_fixed and parallel are configuration options that serve distinct purposes within hook definitions, such as pre-commit [1][2][3]. stage_fixed This is a boolean option (default: false) that applies only to the pre-commit hook [1][2]. When set to true, Lefthook automatically executes git add on the files modified by the command or script after it finishes [1]. - If a files option is specified for the command, Lefthook uses that command to identify which files to stage [1][2]. - If no files option is used, Lefthook defaults to using the {staged_files} template to identify files [1][2]. - All defined filters (such as glob or exclude) are applied to the files being staged [1][2]. parallel This is a boolean option (default: false) that allows commands and scripts within a hook to run concurrently rather than sequentially [2][3]. Interaction between stage_fixed and parallel When both options are used together, Lefthook executes the commands concurrently [4]. If multiple commands are configured with stage_fixed: true and they modify the same files, they will run in parallel, and the file state will be determined by whichever command completes last [4]. Crucially, even when running concurrently, Lefthook will stage the files for all commands as they finish, regardless of the parallel execution [4]. Example usage: pre-commit: parallel: true commands: lint: run: npm run lint --fix {staged_files} stage_fixed: true format: run: npx prettier --write {staged_files} stage_fixed: true
Citations:
- 1: https://lefthook.dev/configuration/stage_fixed/
- 2: https://github.com/evilmartians/lefthook/blob/v1.7.15/docs/configuration.md
- 3: https://lefthook.dev/configuration/parallel/
- 4: What is the behavior when there are multiple `stage_fixed` commands and `parrallel: true` is used? evilmartians/lefthook#779
Serialize formatting before lint and tests.
pre-commit.parallel = true runs format, lint, and test concurrently while format.stage_fixed rewrites and restages files. Use a piped/sequential group so Prettier formats the staged files first, then lint/test run on the output; otherwise formatting can change after lint/test have already read the unformatted source.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lefthook.yml` around lines 1 - 19, Update the pre-commit configuration to run
the format command before lint and test rather than in parallel. Preserve the
existing format, lint, test, glob, skip, and stage_fixed settings, but use
Lefthook’s sequential/piped command grouping so lint and tests consume
Prettier’s formatted and restaged output.
Source: MCP tools
Por qué
Este repo no tenía ningún gate: sin linter, sin formateo, sin hooks, sin CI. Se replica el piso que ya usa
exactamente-frontend, adaptado al stack de xmcp/Workers.Qué entra
typescript-eslint, coneslint-config-prettierpara que no se peleen por formatomain: typecheck, lint, format:check, test,build:cfwrangler deployencadenado a que CI pase enmainUnificación en pnpm
package-lock.jsonypnpm-lock.yamlconvivían — instalación no determinística. Se elimina el de npm y se declarapackageManager: pnpm@10.28.0.El lockfile de pnpm estaba desactualizado:
wranglery@cloudflare/workers-typesse habían agregado con npm, así quepnpm install --frozen-lockfilefallaba. Regenerado.Dos decisiones que vale explicar
typescriptqueda en^6, no^7. typescript-eslint 8.x no soporta TS 7 todavía — con TS 7 el linter directamente no arranca.eslint.config.mjs, no.js. El paquete no declara"type": "module"; agregarlo cambiaría cómo se resuelve elbinpublicado (dist/stdio.js). La extensión.mjsresuelve lo mismo sin tocar el paquete.Commits separados
style:— reformateo con Prettier, sin cambios de comportamiento. Anotado en.git-blame-ignore-revs.chore(tooling):— el setup en sí.wrangler.jsonc(el custom domainmcp.exactamente.com.ar). No había forma de separarlo: Prettier tenía que reformatear ese archivo igual para queformat:checkpase en CI.Antes de mergear
El workflow de deploy necesita dos secrets en el repo:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_IDSin ellos el CI pasa igual, pero el deploy falla.
Verificación
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
mcp.exactamente.com.ar.Documentation
Improvements