mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
Compare commits
12 Commits
fix/codex-
...
fix/trae-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea450853a8 | ||
|
|
dd675d4258 | ||
|
|
db12d3d838 | ||
|
|
46f37ae4fb | ||
|
|
0c166e14da | ||
|
|
527c79350c | ||
|
|
bec1ebf76d | ||
|
|
be76918850 | ||
|
|
99a154a908 | ||
|
|
ebf0f135bb | ||
|
|
2d27da52e2 | ||
|
|
65c4a0f6ba |
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
@@ -34,10 +34,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
|
||||
- name: Cache npm
|
||||
if: matrix.pm == 'npm'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
with:
|
||||
path: ${{ steps.npm-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ matrix.node }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
|
||||
- name: Cache pnpm
|
||||
if: matrix.pm == 'pnpm'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ matrix.node }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Cache yarn
|
||||
if: matrix.pm == 'yarn'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ matrix.node }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
|
||||
- name: Cache bun
|
||||
if: matrix.pm == 'bun'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
# Upload test artifacts on failure
|
||||
- name: Upload test artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: test-results-${{ matrix.os }}-node${{ matrix.node }}-${{ matrix.pm }}
|
||||
path: |
|
||||
@@ -160,10 +160,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
@@ -205,10 +205,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
@@ -223,10 +223,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
|
||||
10
.github/workflows/maintenance.yml
vendored
10
.github/workflows/maintenance.yml
vendored
@@ -15,8 +15,8 @@ jobs:
|
||||
name: Check Dependencies
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
- name: Check for outdated packages
|
||||
@@ -26,8 +26,8 @@ jobs:
|
||||
name: Security Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
- name: Run security audit
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
name: Stale Issues/PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale due to inactivity.'
|
||||
stale-pr-message: 'This PR is stale due to inactivity.'
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
2
.github/workflows/reusable-release.yml
vendored
2
.github/workflows/reusable-release.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
14
.github/workflows/reusable-test.yml
vendored
14
.github/workflows/reusable-test.yml
vendored
@@ -27,10 +27,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
- name: Cache npm
|
||||
if: inputs.package-manager == 'npm'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
with:
|
||||
path: ${{ steps.npm-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ inputs.node-version }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
|
||||
- name: Cache pnpm
|
||||
if: inputs.package-manager == 'pnpm'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ inputs.node-version }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
|
||||
- name: Cache yarn
|
||||
if: inputs.package-manager == 'yarn'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ inputs.node-version }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Cache bun
|
||||
if: inputs.package-manager == 'bun'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: test-results-${{ inputs.os }}-node${{ inputs.node-version }}-${{ inputs.package-manager }}
|
||||
path: |
|
||||
|
||||
4
.github/workflows/reusable-validate.yml
vendored
4
.github/workflows/reusable-validate.yml
vendored
@@ -17,10 +17,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
|
||||
|
||||
@@ -39,6 +39,40 @@ ensure_manifest_entry() {
|
||||
fi
|
||||
}
|
||||
|
||||
manifest_has_entry() {
|
||||
local manifest="$1"
|
||||
local entry="$2"
|
||||
|
||||
[ -f "$manifest" ] && grep -Fqx "$entry" "$manifest"
|
||||
}
|
||||
|
||||
copy_managed_file() {
|
||||
local source_path="$1"
|
||||
local target_path="$2"
|
||||
local manifest="$3"
|
||||
local manifest_entry="$4"
|
||||
local make_executable="${5:-0}"
|
||||
|
||||
local already_managed=0
|
||||
if manifest_has_entry "$manifest" "$manifest_entry"; then
|
||||
already_managed=1
|
||||
fi
|
||||
|
||||
if [ -f "$target_path" ]; then
|
||||
if [ "$already_managed" -eq 1 ]; then
|
||||
ensure_manifest_entry "$manifest" "$manifest_entry"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
cp "$source_path" "$target_path"
|
||||
if [ "$make_executable" -eq 1 ]; then
|
||||
chmod +x "$target_path"
|
||||
fi
|
||||
ensure_manifest_entry "$manifest" "$manifest_entry"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Install function
|
||||
do_install() {
|
||||
local target_dir="$PWD"
|
||||
@@ -95,12 +129,8 @@ do_install() {
|
||||
[ -f "$f" ] || continue
|
||||
local_name=$(basename "$f")
|
||||
target_path="$trae_full_path/commands/$local_name"
|
||||
if [ ! -f "$target_path" ]; then
|
||||
cp "$f" "$target_path"
|
||||
ensure_manifest_entry "$MANIFEST" "commands/$local_name"
|
||||
if copy_managed_file "$f" "$target_path" "$MANIFEST" "commands/$local_name"; then
|
||||
commands=$((commands + 1))
|
||||
else
|
||||
ensure_manifest_entry "$MANIFEST" "commands/$local_name"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@@ -111,12 +141,8 @@ do_install() {
|
||||
[ -f "$f" ] || continue
|
||||
local_name=$(basename "$f")
|
||||
target_path="$trae_full_path/agents/$local_name"
|
||||
if [ ! -f "$target_path" ]; then
|
||||
cp "$f" "$target_path"
|
||||
ensure_manifest_entry "$MANIFEST" "agents/$local_name"
|
||||
if copy_managed_file "$f" "$target_path" "$MANIFEST" "agents/$local_name"; then
|
||||
agents=$((agents + 1))
|
||||
else
|
||||
ensure_manifest_entry "$MANIFEST" "agents/$local_name"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@@ -134,11 +160,9 @@ do_install() {
|
||||
target_path="$target_skill_dir/$relative_path"
|
||||
|
||||
mkdir -p "$(dirname "$target_path")"
|
||||
if [ ! -f "$target_path" ]; then
|
||||
cp "$source_file" "$target_path"
|
||||
if copy_managed_file "$source_file" "$target_path" "$MANIFEST" "skills/$skill_name/$relative_path"; then
|
||||
skill_copied=1
|
||||
fi
|
||||
ensure_manifest_entry "$MANIFEST" "skills/$skill_name/$relative_path"
|
||||
done < <(find "$d" -type f | sort)
|
||||
|
||||
if [ "$skill_copied" -eq 1 ]; then
|
||||
@@ -154,11 +178,9 @@ do_install() {
|
||||
target_path="$trae_full_path/rules/$relative_path"
|
||||
|
||||
mkdir -p "$(dirname "$target_path")"
|
||||
if [ ! -f "$target_path" ]; then
|
||||
cp "$rule_file" "$target_path"
|
||||
if copy_managed_file "$rule_file" "$target_path" "$MANIFEST" "rules/$relative_path"; then
|
||||
rules=$((rules + 1))
|
||||
fi
|
||||
ensure_manifest_entry "$MANIFEST" "rules/$relative_path"
|
||||
done < <(find "$REPO_ROOT/rules" -type f | sort)
|
||||
fi
|
||||
|
||||
@@ -167,12 +189,8 @@ do_install() {
|
||||
if [ -f "$readme_file" ]; then
|
||||
local_name=$(basename "$readme_file")
|
||||
target_path="$trae_full_path/$local_name"
|
||||
if [ ! -f "$target_path" ]; then
|
||||
cp "$readme_file" "$target_path"
|
||||
ensure_manifest_entry "$MANIFEST" "$local_name"
|
||||
if copy_managed_file "$readme_file" "$target_path" "$MANIFEST" "$local_name"; then
|
||||
other=$((other + 1))
|
||||
else
|
||||
ensure_manifest_entry "$MANIFEST" "$local_name"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -182,13 +200,8 @@ do_install() {
|
||||
if [ -f "$script_file" ]; then
|
||||
local_name=$(basename "$script_file")
|
||||
target_path="$trae_full_path/$local_name"
|
||||
if [ ! -f "$target_path" ]; then
|
||||
cp "$script_file" "$target_path"
|
||||
chmod +x "$target_path"
|
||||
ensure_manifest_entry "$MANIFEST" "$local_name"
|
||||
if copy_managed_file "$script_file" "$target_path" "$MANIFEST" "$local_name" 1; then
|
||||
other=$((other + 1))
|
||||
else
|
||||
ensure_manifest_entry "$MANIFEST" "$local_name"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
11
CLAUDE.md
11
CLAUDE.md
@@ -59,3 +59,14 @@ Follow the formats in CONTRIBUTING.md:
|
||||
- Hooks: JSON with matcher and hooks array
|
||||
|
||||
File naming: lowercase with hyphens (e.g., `python-reviewer.md`, `tdd-workflow.md`)
|
||||
|
||||
## Skills
|
||||
|
||||
Use the following skills when working on related files:
|
||||
|
||||
| File(s) | Skill |
|
||||
|---------|-------|
|
||||
| `README.md` | `/readme` |
|
||||
| `.github/workflows/*.yml` | `/ci-workflow` |
|
||||
|
||||
When spawning subagents, always pass conventions from the respective skill into the agent's prompt.
|
||||
|
||||
@@ -295,7 +295,7 @@ everything-claude-code/
|
||||
| |-- plugin.json # Plugin metadata and component paths
|
||||
| |-- marketplace.json # Marketplace catalog for /plugin marketplace add
|
||||
|
|
||||
|-- agents/ # 29 specialized subagents for delegation
|
||||
|-- agents/ # 30 specialized subagents for delegation
|
||||
| |-- planner.md # Feature implementation planning
|
||||
| |-- architect.md # System design decisions
|
||||
| |-- tdd-guide.md # Test-driven development
|
||||
|
||||
@@ -16,6 +16,20 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Skip checks for branch deletion pushes (e.g., git push origin --delete <branch>).
|
||||
# The pre-push hook receives lines on stdin: <local ref> <local sha> <remote ref> <remote sha>.
|
||||
# For deletions, the local sha is the zero OID.
|
||||
is_delete_only=true
|
||||
while read -r _local_ref local_sha _remote_ref _remote_sha; do
|
||||
if [[ "$local_sha" != "0000000000000000000000000000000000000000" ]]; then
|
||||
is_delete_only=false
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$is_delete_only" == "true" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ran_any_check=0
|
||||
|
||||
log() {
|
||||
|
||||
@@ -36,7 +36,11 @@ PYTHON_CMD="${CLV2_PYTHON_CMD:-}"
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
CONFIG_DIR="${HOME}/.claude/homunculus"
|
||||
CONFIG_FILE="${SKILL_ROOT}/config.json"
|
||||
if [ -n "${CLV2_CONFIG:-}" ]; then
|
||||
CONFIG_FILE="$CLV2_CONFIG"
|
||||
else
|
||||
CONFIG_FILE="${SKILL_ROOT}/config.json"
|
||||
fi
|
||||
# PID file is project-scoped so each project can have its own observer
|
||||
PID_FILE="${PROJECT_DIR}/.observer.pid"
|
||||
LOG_FILE="${PROJECT_DIR}/observer.log"
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
---
|
||||
name: token-budget-advisor
|
||||
description: >-
|
||||
Intercepts the response flow to offer the user an informed choice about
|
||||
how much depth/tokens to consume — BEFORE responding. Use this skill
|
||||
when the user wants to control token consumption, adjust response depth,
|
||||
choose between short/long answers, or optimize their prompt.
|
||||
TRIGGER when: "token budget", "response token budget", "token count",
|
||||
"token usage", "response length", "response depth", "brief answer",
|
||||
"short answer", "detailed answer", "full answer",
|
||||
"respuesta corta vs larga", "cuántos tokens", "ahorrar tokens",
|
||||
"responde al 50%", "dame la versión corta", "quiero controlar cuánto usas",
|
||||
"75%", "100%", "at 25%", "at 50%", "at 75%", "at 100%",
|
||||
"give me the full answer", or any variant where the user wants
|
||||
to control length, depth, or token usage — even without mentioning tokens.
|
||||
Offers the user an informed choice about how much response depth to
|
||||
consume before answering. Use this skill when the user explicitly
|
||||
wants to control response length, depth, or token budget.
|
||||
TRIGGER when: "token budget", "token count", "token usage", "token limit",
|
||||
"response length", "answer depth", "short version", "brief answer",
|
||||
"detailed answer", "exhaustive answer", "respuesta corta vs larga",
|
||||
"cuántos tokens", "ahorrar tokens", "responde al 50%", "dame la versión
|
||||
corta", "quiero controlar cuánto usas", or clear variants where the
|
||||
user is explicitly asking to control answer size or depth.
|
||||
DO NOT TRIGGER when: user has already specified a level in the current
|
||||
session (maintain it) or the request is clearly a one-word answer.
|
||||
session (maintain it), the request is clearly a one-word answer, or
|
||||
"token" refers to auth/session/payment tokens rather than response size.
|
||||
origin: community
|
||||
---
|
||||
|
||||
@@ -35,12 +33,14 @@ Intercept the response flow to offer the user a choice about response depth **be
|
||||
|
||||
### Step 1 — Estimate input tokens
|
||||
|
||||
Use the repository's canonical estimation guidance from `skills/context-budget`.
|
||||
Use the repository's canonical context-budget heuristics to estimate the prompt's token count mentally.
|
||||
|
||||
- Prose-first prompts: `input_tokens ≈ word_count × 1.3`
|
||||
- Code-heavy or mixed prompts: `input_tokens ≈ char_count / 4`
|
||||
Use the same calibration guidance as [context-budget](../context-budget/SKILL.md):
|
||||
|
||||
For mixed content, prefer the code-heavy estimate as the conservative default.
|
||||
- prose: `words × 1.3`
|
||||
- code-heavy or mixed/code blocks: `chars / 4`
|
||||
|
||||
For mixed content, use the dominant content type and keep the estimate heuristic.
|
||||
|
||||
### Step 2 — Estimate response size by complexity
|
||||
|
||||
@@ -72,7 +72,7 @@ Choose your depth level:
|
||||
[3] Detailed (75%) -> ~[tokens] Full answer with alternatives
|
||||
[4] Exhaustive (100%) -> ~[tokens] Everything, no limits
|
||||
|
||||
Which level? (1-4 or say "25%", "50%", "75%", "100%")
|
||||
Which level? (1-4 or say "25% depth", "50% depth", "75% depth", "100% depth")
|
||||
|
||||
Precision: heuristic estimate ~85-90% accuracy (±15%).
|
||||
```
|
||||
@@ -98,10 +98,10 @@ If the user already signals a level, respond at that level immediately without a
|
||||
|
||||
| What they say | Level |
|
||||
|----------------------------------------------------|-------|
|
||||
| "1" / "25%" / "short answer" / "brief" / "tldr" / "one-liner" | 25% |
|
||||
| "2" / "50%" / "moderate detail" / "balanced answer" | 50% |
|
||||
| "3" / "75%" / "detailed answer" / "thorough explanation" | 75% |
|
||||
| "4" / "100%" / "exhaustive" / "everything" / "full answer" | 100% |
|
||||
| "1" / "25% depth" / "short version" / "brief answer" / "tldr" | 25% |
|
||||
| "2" / "50% depth" / "moderate depth" / "balanced answer" | 50% |
|
||||
| "3" / "75% depth" / "detailed answer" / "thorough answer" | 75% |
|
||||
| "4" / "100% depth" / "exhaustive answer" / "full deep dive" | 100% |
|
||||
|
||||
If the user set a level earlier in the session, **maintain it silently** for subsequent responses unless they change it.
|
||||
|
||||
@@ -113,19 +113,21 @@ This skill uses heuristic estimation — no real tokenizer. Accuracy ~85-90%, va
|
||||
|
||||
### Triggers
|
||||
|
||||
- "Give me the brief answer first."
|
||||
- "How many tokens will your response use?"
|
||||
- "Give me the short version first."
|
||||
- "How many tokens will your answer use?"
|
||||
- "Respond at 50% depth."
|
||||
- "I want the full answer."
|
||||
- "Dame la version corta."
|
||||
- "I want the exhaustive answer, not the summary."
|
||||
- "Dame la version corta y luego la detallada."
|
||||
|
||||
### Does Not Trigger
|
||||
|
||||
- "Explain OAuth token refresh flow." (`token` here is domain language, not a budget request)
|
||||
- "Why is this JWT token invalid?" (security/domain usage, not response sizing)
|
||||
- "What is 2 + 2?" (trivially short answer)
|
||||
- "What is a JWT token?"
|
||||
- "The checkout flow uses a payment token."
|
||||
- "Is this normal?"
|
||||
- "Complete the refactor."
|
||||
- Follow-up questions after the user already chose a depth for the session
|
||||
|
||||
## Source
|
||||
|
||||
Standalone skill from [TBA — Token Budget Advisor for Claude Code](https://github.com/Xabilimon1/Token-Budget-Advisor-Claude-Code-).
|
||||
The upstream project includes an optional estimator script, but this ECC skill intentionally stays zero-dependency and heuristic-only.
|
||||
Original project also ships a Python estimator script, but this repository keeps the skill self-contained and heuristic-only.
|
||||
|
||||
179
tests/scripts/trae-install.test.js
Normal file
179
tests/scripts/trae-install.test.js
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Tests for .trae/install.sh and .trae/uninstall.sh
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '..', '..');
|
||||
const INSTALL_SCRIPT = path.join(REPO_ROOT, '.trae', 'install.sh');
|
||||
const UNINSTALL_SCRIPT = path.join(REPO_ROOT, '.trae', 'uninstall.sh');
|
||||
|
||||
function createTempDir(prefix) {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
}
|
||||
|
||||
function cleanup(dirPath) {
|
||||
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function runInstall(options = {}) {
|
||||
return execFileSync('bash', [INSTALL_SCRIPT, ...(options.args || [])], {
|
||||
cwd: options.cwd,
|
||||
env: {
|
||||
...process.env,
|
||||
HOME: options.homeDir || process.env.HOME,
|
||||
},
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 20000,
|
||||
});
|
||||
}
|
||||
|
||||
function runUninstall(options = {}) {
|
||||
return execFileSync('bash', [UNINSTALL_SCRIPT, ...(options.args || [])], {
|
||||
cwd: options.cwd,
|
||||
env: {
|
||||
...process.env,
|
||||
HOME: options.homeDir || process.env.HOME,
|
||||
},
|
||||
encoding: 'utf8',
|
||||
input: options.input || 'y\n',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 20000,
|
||||
});
|
||||
}
|
||||
|
||||
function readManifestLines(projectRoot) {
|
||||
const manifestPath = path.join(projectRoot, '.trae', '.ecc-manifest');
|
||||
return fs.readFileSync(manifestPath, 'utf8')
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` \u2713 ${name}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(` \u2717 ${name}`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing Trae install/uninstall scripts ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - skipped on Windows; Trae shell scripts are Unix-only');
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (test('does not claim ownership of preexisting target files', () => {
|
||||
const homeDir = createTempDir('trae-home-');
|
||||
const projectRoot = createTempDir('trae-project-');
|
||||
|
||||
try {
|
||||
const preexistingCommandPath = path.join(projectRoot, '.trae', 'commands', 'e2e.md');
|
||||
fs.mkdirSync(path.dirname(preexistingCommandPath), { recursive: true });
|
||||
fs.writeFileSync(preexistingCommandPath, 'user owned command\n');
|
||||
|
||||
runInstall({ cwd: projectRoot, homeDir });
|
||||
|
||||
const manifestLines = readManifestLines(projectRoot);
|
||||
assert.ok(!manifestLines.includes('commands/e2e.md'), 'Preexisting file should not be recorded in manifest');
|
||||
|
||||
runUninstall({ cwd: projectRoot, homeDir });
|
||||
|
||||
assert.strictEqual(fs.readFileSync(preexistingCommandPath, 'utf8'), 'user owned command\n');
|
||||
} finally {
|
||||
cleanup(homeDir);
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('records nested skill files and the full rules tree in the manifest', () => {
|
||||
const homeDir = createTempDir('trae-home-');
|
||||
const projectRoot = createTempDir('trae-project-');
|
||||
|
||||
try {
|
||||
runInstall({ cwd: projectRoot, homeDir });
|
||||
|
||||
const manifestLines = readManifestLines(projectRoot);
|
||||
assert.ok(manifestLines.includes('skills/skill-comply/pyproject.toml'));
|
||||
assert.ok(manifestLines.includes('rules/common/code-review.md'));
|
||||
assert.ok(manifestLines.includes('rules/python/coding-style.md'));
|
||||
assert.ok(manifestLines.includes('rules/zh/README.md'));
|
||||
|
||||
assert.ok(fs.existsSync(path.join(projectRoot, '.trae', 'skills', 'skill-comply', 'pyproject.toml')));
|
||||
assert.ok(fs.existsSync(path.join(projectRoot, '.trae', 'rules', 'python', 'coding-style.md')));
|
||||
assert.ok(fs.existsSync(path.join(projectRoot, '.trae', 'rules', 'zh', 'README.md')));
|
||||
} finally {
|
||||
cleanup(homeDir);
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('reinstall preserves managed manifest coverage without duplicate entries', () => {
|
||||
const homeDir = createTempDir('trae-home-');
|
||||
const projectRoot = createTempDir('trae-project-');
|
||||
|
||||
try {
|
||||
runInstall({ cwd: projectRoot, homeDir });
|
||||
|
||||
const managedCommandPath = path.join(projectRoot, '.trae', 'commands', 'e2e.md');
|
||||
fs.rmSync(managedCommandPath);
|
||||
|
||||
runInstall({ cwd: projectRoot, homeDir });
|
||||
|
||||
const manifestLines = readManifestLines(projectRoot);
|
||||
const entryCount = manifestLines.filter((line) => line === 'commands/e2e.md').length;
|
||||
|
||||
assert.strictEqual(entryCount, 1, 'Managed file should appear once in manifest after reinstall');
|
||||
assert.ok(fs.existsSync(managedCommandPath), 'Managed file should be recreated on reinstall');
|
||||
} finally {
|
||||
cleanup(homeDir);
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('uninstall rejects manifest entries that escape the Trae root via symlink traversal', () => {
|
||||
const homeDir = createTempDir('trae-home-');
|
||||
const projectRoot = createTempDir('trae-project-');
|
||||
const externalRoot = createTempDir('trae-outside-');
|
||||
|
||||
try {
|
||||
const traeRoot = path.join(projectRoot, '.trae');
|
||||
fs.mkdirSync(traeRoot, { recursive: true });
|
||||
|
||||
const outsideSecretPath = path.join(externalRoot, 'secret.txt');
|
||||
fs.writeFileSync(outsideSecretPath, 'do not remove\n');
|
||||
fs.symlinkSync(externalRoot, path.join(traeRoot, 'escape-link'));
|
||||
fs.writeFileSync(path.join(traeRoot, '.ecc-manifest'), 'escape-link/secret.txt\n.ecc-manifest\n');
|
||||
|
||||
const stdout = runUninstall({ cwd: projectRoot, homeDir });
|
||||
|
||||
assert.ok(stdout.includes('Skipped: escape-link/secret.txt (invalid manifest entry)'));
|
||||
assert.strictEqual(fs.readFileSync(outsideSecretPath, 'utf8'), 'do not remove\n');
|
||||
} finally {
|
||||
cleanup(homeDir);
|
||||
cleanup(projectRoot);
|
||||
cleanup(externalRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
Reference in New Issue
Block a user