Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 26 additions & 26 deletions .skillsaw-baseline.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
{
"version": "1",
"generated_by": "skillsaw 0.18.0",
"generated_at": "2026-09-10T12:03:32.252028+00:00",
"generated_at": "2026-09-17T15:54:07.663767+00:00",
"violations": [
{
"fingerprint": "34efa8521ae07039",
"fingerprint": "d9a273225a48337f",
"rule_id": "agentskill-name",
"file_path": "skills/github-forge/SKILL.md",
"file_path": "skills/issue-labels/gitlab/SKILL.md",
"line": 2,
"message": "Name 'github' does not match directory name 'github-forge'",
"message": "Name 'issue-labels' does not match directory name 'gitlab'",
"severity": "error"
},
{
"fingerprint": "d9a273225a48337f",
"fingerprint": "a44e1496f6fe3e11",
"rule_id": "agentskill-name",
"file_path": "skills/issue-labels/gitlab/SKILL.md",
"file_path": "skills/issue-labels/github/SKILL.md",
"line": 2,
"message": "Name 'issue-labels' does not match directory name 'gitlab'",
"message": "Name 'issue-labels' does not match directory name 'github'",
"severity": "error"
},
{
Expand All @@ -28,11 +28,11 @@
"severity": "error"
},
{
"fingerprint": "a44e1496f6fe3e11",
"fingerprint": "10342080fcd47b75",
"rule_id": "agentskill-name",
"file_path": "skills/issue-labels/github/SKILL.md",
"file_path": "skills/gitlab-forge/SKILL.md",
"line": 2,
"message": "Name 'issue-labels' does not match directory name 'github'",
"message": "Name 'gitlab' does not match directory name 'gitlab-forge'",
"severity": "error"
},
{
Expand All @@ -44,13 +44,22 @@
"severity": "error"
},
{
"fingerprint": "10342080fcd47b75",
"fingerprint": "34efa8521ae07039",
"rule_id": "agentskill-name",
"file_path": "skills/gitlab-forge/SKILL.md",
"file_path": "skills/github-forge/SKILL.md",
"line": 2,
"message": "Name 'gitlab' does not match directory name 'gitlab-forge'",
"message": "Name 'github' does not match directory name 'github-forge'",
"severity": "error"
},
{
"fingerprint": "8aba1d160bb4c413",
"rule_id": "context-budget",
"file_path": "skills/code-implementation/SKILL.md",
"message": "Estimated 12,317 tokens exceeds skill error limit of 6,000",
"severity": "error",
"value": 12317,
"baseline_mode": "ceiling"
},
{
"fingerprint": "43a3bb24cb22288e",
"rule_id": "context-budget",
Expand All @@ -61,12 +70,12 @@
"baseline_mode": "ceiling"
},
{
"fingerprint": "8aba1d160bb4c413",
"fingerprint": "0285e1a7243ebead",
"rule_id": "context-budget",
"file_path": "skills/code-implementation/SKILL.md",
"message": "Estimated 12,180 tokens exceeds skill error limit of 6,000",
"file_path": "skills/pr-review/SKILL.md",
"message": "Estimated 16,784 tokens exceeds skill error limit of 6,000",
"severity": "error",
"value": 12180,
"value": 16784,
"baseline_mode": "ceiling"
},
{
Expand All @@ -77,15 +86,6 @@
"severity": "warning",
"value": 3778,
"baseline_mode": "ceiling"
},
{
"fingerprint": "0285e1a7243ebead",
"rule_id": "context-budget",
"file_path": "skills/pr-review/SKILL.md",
"message": "Estimated 16,630 tokens exceeds skill error limit of 6,000",
"severity": "error",
"value": 16630,
"baseline_mode": "ceiling"
}
]
}
22 changes: 12 additions & 10 deletions skills/code-implementation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ inside 9b, before each retry iteration (9c), and before commit (10),
check remaining time **only if `TIMEOUT_SECONDS` is set**:

```bash
if [ -n "${TIMEOUT_SECONDS:-}" ]; then
ELAPSED=$(( $(date +%s) - AGENT_START ))
REMAINING=$(( TIMEOUT_SECONDS - ELAPSED ))
if test -n "${TIMEOUT_SECONDS:-}"; then
NOW=$(date +%s)
ELAPSED=$((NOW - AGENT_START))
REMAINING=$((TIMEOUT_SECONDS - ELAPSED))
echo "::notice::Time check: ${ELAPSED}s elapsed, ${REMAINING}s remaining"
fi
```
Expand Down Expand Up @@ -227,7 +228,7 @@ The sandbox's `JIRA_TOKEN` is the `jira-ro` provider's opaque placeholder,
not the real token. Extract the issue key from `ISSUE_URL`:

```bash
if [ "${FULLSEND_TRACKER:-}" = "jira" ]; then
if test "${FULLSEND_TRACKER:-}" = "jira"; then
ISSUE_KEY=$(echo "${ISSUE_URL}" | sed -E 's|.*/browse/||')
curl --fail-with-body --silent --user "${JIRA_USER_EMAIL}:${JIRA_TOKEN}" \
"${JIRA_BASE_URL}/rest/api/3/issue/${ISSUE_KEY}"
Expand Down Expand Up @@ -319,15 +320,15 @@ these commands in order until one succeeds:
```bash
# Try each discovery method; use the first that returns a non-empty value.
DEFAULT_BRANCH=""
if [ "${FULLSEND_FORGE:-github}" = "github" ]; then
if test "${FULLSEND_FORGE:-github}" = "github"; then
DEFAULT_BRANCH="$(gh repo view --json defaultBranchRef \
--jq '.defaultBranchRef.name' 2>/dev/null)" || true
fi
if [ -z "${DEFAULT_BRANCH}" ]; then
if test -z "${DEFAULT_BRANCH}"; then
DEFAULT_BRANCH="$(git rev-parse --abbrev-ref origin/HEAD 2>/dev/null \
| sed 's|^origin/||')" || true
fi
if [ -z "${DEFAULT_BRANCH}" ] || [ "${DEFAULT_BRANCH}" = "HEAD" ]; then
if test -z "${DEFAULT_BRANCH}" || test "${DEFAULT_BRANCH}" = "HEAD"; then
DEFAULT_BRANCH="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null \
| sed 's|^refs/remotes/origin/||')" || true
fi
Expand Down Expand Up @@ -665,9 +666,10 @@ The first run may be slow (installs hook environments). This is normal.

```bash
RUN_FALLBACK=1
if [ -n "${TIMEOUT_SECONDS:-}" ] && [ -n "${AGENT_START:-}" ]; then
REMAINING=$(( TIMEOUT_SECONDS - ($(date +%s) - AGENT_START) ))
if [ "$REMAINING" -lt 300 ]; then
if test -n "${TIMEOUT_SECONDS:-}" && test -n "${AGENT_START:-}"; then
NOW=$(date +%s)
REMAINING=$((TIMEOUT_SECONDS - (NOW - AGENT_START)))
if test "$REMAINING" -lt 300; then
RUN_FALLBACK=0; echo "::warning::Direct-execution fallback skipped: ${REMAINING}s remaining < 300s floor"
else
echo "::notice::Fallback time check: ${REMAINING}s remaining >= 300s floor — proceeding"
Expand Down
14 changes: 8 additions & 6 deletions skills/fix-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ inside 7b, before each retry iteration (7c), and before commit (8),
check remaining time **only if `TIMEOUT_SECONDS` is set**:

```bash
if [ -n "${TIMEOUT_SECONDS:-}" ]; then
ELAPSED=$(( $(date +%s) - AGENT_START ))
REMAINING=$(( TIMEOUT_SECONDS - ELAPSED ))
if test -n "${TIMEOUT_SECONDS:-}"; then
NOW=$(date +%s)
ELAPSED=$((NOW - AGENT_START))
REMAINING=$((TIMEOUT_SECONDS - ELAPSED))
echo "::notice::Time check: ${ELAPSED}s elapsed, ${REMAINING}s remaining"
fi
```
Expand Down Expand Up @@ -203,9 +204,10 @@ thin margin, timing out with no commit at all. Re-check against a flat

```bash
RUN_FALLBACK=1
if [ -n "${TIMEOUT_SECONDS:-}" ] && [ -n "${AGENT_START:-}" ]; then
REMAINING=$(( TIMEOUT_SECONDS - ($(date +%s) - AGENT_START) ))
if [ "$REMAINING" -lt 300 ]; then
if test -n "${TIMEOUT_SECONDS:-}" && test -n "${AGENT_START:-}"; then
NOW=$(date +%s)
REMAINING=$((TIMEOUT_SECONDS - (NOW - AGENT_START)))
if test "$REMAINING" -lt 300; then
RUN_FALLBACK=0; echo "::warning::Direct-execution fallback skipped: ${REMAINING}s remaining < 300s floor"
else
echo "::notice::Fallback time check: ${REMAINING}s remaining >= 300s floor — proceeding"
Expand Down
42 changes: 37 additions & 5 deletions skills/pr-review/gitlab/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,33 @@ MR_IID=$(basename "${PR_URL}")
## MR data fetching

```bash
# Write token into a stable curl config file so the header never
# appears on a command line (avoids the tirith sensitive-upload rule).
# Use a hardcoded path, not a mktemp path stored in a shell variable:
# each fenced block in this file runs as an independent Bash call, so
# shell variables do not survive between them — files (at a fixed,
# predictable path) do. Written and scrubbed within this same block so
# the token never outlives the calls that need it.
(umask 077; printf 'header = "PRIVATE-TOKEN: %s"\n' "${GITLAB_TOKEN}" > /tmp/gitlab-api.curlrc)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] secret-exposure

GITLAB_TOKEN is written once at Environment setup to the predictable path /tmp/gitlab-api.curlrc and reused by later, independent fenced blocks (MR metadata/changes at lines 38/46, issue context at 112/118, prior-review compare at 127). The only scrub instruction for this file is prose after the last optional section (lines 132-137), not a fenced bash call and not an in-block truncate. This is weaker than the sibling /tmp/pr-head.curlrc pattern in the same file, which truncates inside its own fenced script (line 99) in addition to a follow-up prose scrub (line 105). If a reviewing agent skips the issue-context or prior-review-compare sections, or stops before the trailing prose instruction runs, /tmp/gitlab-api.curlrc keeps the token at a well-known path for the rest of the sandbox lifetime. Impact is bounded (token already present in the sandbox environment; described as read-only), but this introduces a new persistent on-disk copy the previous --header pattern did not create.

Suggested fix: Make each consuming fenced block self-contained: recreate the curlrc at the start of the block, use it, then truncate it (: > /tmp/gitlab-api.curlrc) at the end of that same block, matching the /tmp/pr-head.curlrc patterns in-block truncate plus follow-up scrub.


# MR metadata: title, description, author, labels, draft status, head SHA
MR_DATA=$(curl --fail --silent --show-error \
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
-K /tmp/gitlab-api.curlrc \
"https://${GITLAB_HOST}/api/v4/projects/${REPO_ENCODED}/merge_requests/${MR_IID}")
HEAD_SHA=$(echo "$MR_DATA" | jq -r '.sha')
IS_DRAFT=$(echo "$MR_DATA" | jq -r '.draft')

# MR changes (includes diff per file), saved for later Bash calls
# (shell variables do not survive between calls; files do)
curl --fail --silent --show-error \
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
-K /tmp/gitlab-api.curlrc \
"https://${GITLAB_HOST}/api/v4/projects/${REPO_ENCODED}/merge_requests/${MR_IID}/changes" \
> /sandbox/workspace/mr-changes.json

# Changed file paths
jq -r '.changes[].new_path' /sandbox/workspace/mr-changes.json

: > /tmp/gitlab-api.curlrc
```

## Unified diff (small and large MRs)
Expand Down Expand Up @@ -99,28 +110,49 @@ timed out — scrub the token: `: > /tmp/pr-head.curlrc`.
## Issue context

```bash
# Recreate the curl config for this block (see MR data fetching above
# for why a stable path, not a mktemp variable, is used).
(umask 077; printf 'header = "PRIVATE-TOKEN: %s"\n' "${GITLAB_TOKEN}" > /tmp/gitlab-api.curlrc)

# Fetch linked issue metadata
curl --fail --silent --show-error \
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
-K /tmp/gitlab-api.curlrc \
"https://${GITLAB_HOST}/api/v4/projects/${REPO_ENCODED}/issues/<issue-iid>" \
| jq '{title, description}'

# Fetch issue notes (comments)
curl --fail --silent --show-error \
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
-K /tmp/gitlab-api.curlrc \
"https://${GITLAB_HOST}/api/v4/projects/${REPO_ENCODED}/issues/<issue-iid>/notes"

: > /tmp/gitlab-api.curlrc
```

## Prior review comparison

```bash
# Recreate the curl config for this block (see MR data fetching above
# for why a stable path, not a mktemp variable, is used).
(umask 077; printf 'header = "PRIVATE-TOKEN: %s"\n' "${GITLAB_TOKEN}" > /tmp/gitlab-api.curlrc)

# Compare commits between prior review and current HEAD
COMPARE=$(curl --fail --silent --show-error \
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
-K /tmp/gitlab-api.curlrc \
"https://${GITLAB_HOST}/api/v4/projects/${REPO_ENCODED}/repository/compare?from=${PRIOR_REVIEW_SHA}&to=${HEAD_SHA}")
CHANGED_FILES=$(echo "$COMPARE" | jq -r '.diffs[].new_path')

: > /tmp/gitlab-api.curlrc
```

Each block above writes and scrubs `/tmp/gitlab-api.curlrc` within its
own Bash call. As a fallback in case a block is interrupted before its
own scrub line runs, once every GitLab API call in this skill is done
for the review, scrub the token again as its own explicit Bash call:
`: > /tmp/gitlab-api.curlrc` — not `rm`, and not an `EXIT` trap (a trap
set in one Bash call does not fire for commands run in a later,
independent call, and would instead delete the file before the later
consumers ever read it).

## Notes

- The sandbox policy allows `curl` but not `gh` for GitLab forges.
Expand Down
Loading