mirror of https://github.com/garrytan/gstack.git
fix(gen): small config scrubs — openclaw blobs to real files, setup host drift, dead artifacts
- The three openclaw markdown blobs hardcoded inside gen-skill-docs.ts (which silently reverted any hand edit to their tracked outputs on regen) move to openclaw/templates/*.md source files; output shasums byte-identical. - setup's --host allowlists gain cursor + slate — both fully registered hosts with generated output, but './setup --host cursor' exited 1 because two hand-rolled lists in setup had drifted from hosts/index.ts. - scripts/proactive-suggestions.json deleted: 31KB regenerated on every run, read by nobody (the catalog-trim design's reader was never built); its emitter and three determinism tests (which guaranteed a file nothing reads didn't churn) retired with stays-retired pins. - claude/SKILL.md.tmpl deleted: a complete 8.9KB skill that never generated output (directory name collides with the host id 'claude'), in no registry. Recoverable from git if ever wanted under a non-colliding name. - openclaw's frozen extraFields.version '0.15.2.0' stamp dropped; includeSkills: [] no-ops omitted (the generator treats [] as absent); llms.txt 55 -> 54 skills. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5586f57c62
commit
b60fe8bd64
|
|
@ -1,341 +0,0 @@
|
|||
---
|
||||
name: claude
|
||||
preamble-tier: 3
|
||||
version: 1.0.0
|
||||
description: |
|
||||
Claude Code CLI wrapper for non-Claude hosts - three modes. Review: independent
|
||||
diff review via claude -p. Challenge: adversarial failure-mode review. Consult:
|
||||
ask Claude about the repo with read-only file tools. Use when asked for "claude
|
||||
review", "claude challenge", "ask claude", "second opinion from claude", or
|
||||
"outside voice". (gstack)
|
||||
triggers:
|
||||
- claude review
|
||||
- claude challenge
|
||||
- ask claude
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- AskUserQuestion
|
||||
---
|
||||
|
||||
{{PREAMBLE}}
|
||||
|
||||
{{BASE_BRANCH_DETECT}}
|
||||
|
||||
# /claude - Claude Outside Voice
|
||||
|
||||
You are running the `/claude` skill from a non-Claude host. This wraps `claude -p`
|
||||
to get an independent Claude Code second opinion without allowing nested Claude to
|
||||
modify files.
|
||||
|
||||
The generated external invocation name is `gstack-claude`.
|
||||
|
||||
---
|
||||
|
||||
## Step 0: Check Claude CLI
|
||||
|
||||
```bash
|
||||
CLAUDE_BIN=$(command -v claude 2>/dev/null || echo "")
|
||||
[ -z "$CLAUDE_BIN" ] && echo "NOT_FOUND" || echo "FOUND: $CLAUDE_BIN"
|
||||
```
|
||||
|
||||
If `NOT_FOUND`, stop and tell the user:
|
||||
"Claude CLI not found. Install Claude Code, then re-run this skill."
|
||||
|
||||
Check auth:
|
||||
|
||||
```bash
|
||||
if [ -f "$HOME/.claude/.credentials.json" ] || [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
||||
echo "AUTH_FOUND"
|
||||
else
|
||||
echo "AUTH_MISSING"
|
||||
fi
|
||||
```
|
||||
|
||||
If `AUTH_MISSING`, stop and tell the user:
|
||||
"No Claude authentication found. Run `claude` interactively to log in, or export `ANTHROPIC_API_KEY`, then re-run this skill."
|
||||
|
||||
---
|
||||
|
||||
## Safety Boundary
|
||||
|
||||
Nested Claude must stay focused on the user's repository and must not run gstack
|
||||
skills from inside this skill.
|
||||
|
||||
All `claude -p` calls MUST include:
|
||||
|
||||
- `--disable-slash-commands`
|
||||
- Review/challenge: `--tools ""`
|
||||
- Consult: `--allowedTools Read,Grep,Glob --disallowedTools Bash,Edit,Write`
|
||||
|
||||
Never pass `Bash`, `Edit`, or `Write` to nested Claude in this skill.
|
||||
|
||||
All prompts MUST be written to a temp file and fed through stdin. Never interpolate
|
||||
user text directly into the shell command.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Detect Mode
|
||||
|
||||
Parse the user's input:
|
||||
|
||||
1. `/claude review` or `/claude review <instructions>` - **Review mode** (Step 2A)
|
||||
2. `/claude challenge` or `/claude challenge <focus>` - **Challenge mode** (Step 2B)
|
||||
3. `/claude` with no arguments, or `/claude <anything else>` - **Consult mode** (Step 2C)
|
||||
|
||||
If no mode is obvious and a diff exists, ask whether to review, challenge, or consult.
|
||||
|
||||
---
|
||||
|
||||
## Shared Helpers
|
||||
|
||||
Use these shell snippets in every mode.
|
||||
|
||||
Create temp files:
|
||||
|
||||
```bash
|
||||
PROMPT_FILE=$(mktemp /tmp/gstack-claude-prompt-XXXXXX)
|
||||
RESP_FILE=$(mktemp /tmp/gstack-claude-response-XXXXXX.json)
|
||||
ERR_FILE=$(mktemp /tmp/gstack-claude-error-XXXXXX.txt)
|
||||
```
|
||||
|
||||
Cleanup at the end of every mode:
|
||||
|
||||
```bash
|
||||
rm -f "$PROMPT_FILE" "$RESP_FILE" "$ERR_FILE"
|
||||
```
|
||||
|
||||
Parse JSON output:
|
||||
|
||||
```bash
|
||||
python3 - "$RESP_FILE" <<'PY'
|
||||
import json, sys
|
||||
path = sys.argv[1]
|
||||
try:
|
||||
obj = json.load(open(path))
|
||||
except Exception as exc:
|
||||
print(f"CLAUDE_JSON_PARSE_ERROR: {exc}")
|
||||
sys.exit(0)
|
||||
|
||||
if obj.get("is_error"):
|
||||
print("CLAUDE_ERROR: true")
|
||||
|
||||
result = obj.get("result") or obj.get("response") or ""
|
||||
if result:
|
||||
print(result)
|
||||
|
||||
usage = obj.get("usage") or {}
|
||||
input_tokens = usage.get("input_tokens", 0) or 0
|
||||
output_tokens = usage.get("output_tokens", 0) or 0
|
||||
cache_read = usage.get("cache_read_input_tokens", 0) or 0
|
||||
model = obj.get("model") or "unknown"
|
||||
session_id = obj.get("session_id") or ""
|
||||
|
||||
print(f"\nTokens: input={input_tokens} output={output_tokens} cache_read={cache_read} | Model: {model}")
|
||||
if session_id:
|
||||
print(f"SESSION_ID:{session_id}")
|
||||
PY
|
||||
```
|
||||
|
||||
If stderr contains `auth`, `login`, or `unauthorized`, tell the user:
|
||||
"Claude authentication failed. Run `claude` interactively to authenticate or export `ANTHROPIC_API_KEY`."
|
||||
|
||||
---
|
||||
|
||||
## Step 2A: Review Mode
|
||||
|
||||
Review the current branch diff with nested Claude in tool-less mode.
|
||||
|
||||
1. Fetch base and capture diff:
|
||||
|
||||
```bash
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
DIFF_FILE=$(mktemp /tmp/gstack-claude-diff-XXXXXX.patch)
|
||||
git fetch origin <base> --quiet 2>/dev/null || true
|
||||
git diff "origin/<base>" > "$DIFF_FILE" 2>/dev/null || git diff "<base>" > "$DIFF_FILE"
|
||||
```
|
||||
|
||||
If the diff file is empty, stop and say:
|
||||
"Nothing to review - no changes against the base branch."
|
||||
|
||||
2. Write the prompt file:
|
||||
|
||||
```bash
|
||||
cat > "$PROMPT_FILE" <<'EOF'
|
||||
You are a brutally honest Claude Code reviewer. Review this git diff for bugs,
|
||||
production failure modes, security issues, missing tests, and maintainability
|
||||
problems. Be direct. No compliments. Reference files and changed code where possible.
|
||||
|
||||
Additional user instructions, if any:
|
||||
<custom review instructions>
|
||||
|
||||
DIFF:
|
||||
EOF
|
||||
cat "$DIFF_FILE" >> "$PROMPT_FILE"
|
||||
```
|
||||
|
||||
3. Run Claude:
|
||||
|
||||
```bash
|
||||
cat "$PROMPT_FILE" | claude -p --output-format json --disable-slash-commands --tools "" > "$RESP_FILE" 2>"$ERR_FILE"
|
||||
```
|
||||
|
||||
4. Present the parsed output:
|
||||
|
||||
```
|
||||
CLAUDE SAYS (code review):
|
||||
============================================================
|
||||
<parsed result from RESP_FILE>
|
||||
============================================================
|
||||
```
|
||||
|
||||
5. Cleanup:
|
||||
|
||||
```bash
|
||||
rm -f "$DIFF_FILE" "$PROMPT_FILE" "$RESP_FILE" "$ERR_FILE"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2B: Challenge Mode
|
||||
|
||||
Run an adversarial failure-mode review with nested Claude in tool-less mode.
|
||||
|
||||
1. Capture the diff using the same diff commands from Review mode.
|
||||
|
||||
2. Write the prompt:
|
||||
|
||||
```bash
|
||||
cat > "$PROMPT_FILE" <<'EOF'
|
||||
You are an adversarial Claude Code reviewer. Try to break this change before users do.
|
||||
Find edge cases, race conditions, security holes, resource leaks, silent data
|
||||
corruption, bad error handling, and operational failure modes. Be thorough. No
|
||||
compliments. If the user provided a focus area, prioritize it.
|
||||
|
||||
Focus area, if any:
|
||||
<focus>
|
||||
|
||||
DIFF:
|
||||
EOF
|
||||
cat "$DIFF_FILE" >> "$PROMPT_FILE"
|
||||
```
|
||||
|
||||
3. Run Claude:
|
||||
|
||||
```bash
|
||||
cat "$PROMPT_FILE" | claude -p --output-format json --disable-slash-commands --tools "" > "$RESP_FILE" 2>"$ERR_FILE"
|
||||
```
|
||||
|
||||
4. Present the parsed output:
|
||||
|
||||
```
|
||||
CLAUDE SAYS (adversarial challenge):
|
||||
============================================================
|
||||
<parsed result from RESP_FILE>
|
||||
============================================================
|
||||
```
|
||||
|
||||
5. Cleanup:
|
||||
|
||||
```bash
|
||||
rm -f "$DIFF_FILE" "$PROMPT_FILE" "$RESP_FILE" "$ERR_FILE"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2C: Consult Mode
|
||||
|
||||
Ask Claude about the repository. Consult mode may inspect files, but only with
|
||||
read-only tools.
|
||||
|
||||
1. Check for an existing Claude session:
|
||||
|
||||
```bash
|
||||
cat .context/claude-session-id 2>/dev/null || echo "NO_SESSION"
|
||||
```
|
||||
|
||||
If a session exists, ask the user whether to continue it or start fresh.
|
||||
|
||||
2. Write the prompt:
|
||||
|
||||
```bash
|
||||
cat > "$PROMPT_FILE" <<'EOF'
|
||||
You are Claude Code acting as an independent outside voice for this repository.
|
||||
Answer the user's question directly. You may inspect repository files with Read,
|
||||
Grep, and Glob only. Do not use Bash. Do not edit or write files. Do not invoke
|
||||
slash commands or gstack skills.
|
||||
|
||||
USER QUESTION:
|
||||
<user prompt>
|
||||
EOF
|
||||
```
|
||||
|
||||
3. Run Claude.
|
||||
|
||||
For a new session:
|
||||
|
||||
```bash
|
||||
cat "$PROMPT_FILE" | claude -p --output-format json --disable-slash-commands --allowedTools Read,Grep,Glob --disallowedTools Bash,Edit,Write > "$RESP_FILE" 2>"$ERR_FILE"
|
||||
```
|
||||
|
||||
For a resumed session:
|
||||
|
||||
```bash
|
||||
cat "$PROMPT_FILE" | claude -p --resume "<session-id>" --output-format json --disable-slash-commands --allowedTools Read,Grep,Glob --disallowedTools Bash,Edit,Write > "$RESP_FILE" 2>"$ERR_FILE"
|
||||
```
|
||||
|
||||
4. Parse and save the session id:
|
||||
|
||||
```bash
|
||||
SESSION_ID=$(python3 - "$RESP_FILE" <<'PY'
|
||||
import json, sys
|
||||
try:
|
||||
obj = json.load(open(sys.argv[1]))
|
||||
print(obj.get("session_id") or "")
|
||||
except Exception:
|
||||
print("")
|
||||
PY
|
||||
)
|
||||
if [ -n "$SESSION_ID" ]; then
|
||||
mkdir -p .context
|
||||
printf "%s\n" "$SESSION_ID" > .context/claude-session-id
|
||||
fi
|
||||
```
|
||||
|
||||
5. Present the parsed output:
|
||||
|
||||
```
|
||||
CLAUDE SAYS (consult):
|
||||
============================================================
|
||||
<parsed result from RESP_FILE>
|
||||
============================================================
|
||||
Session saved - run /claude again to continue this conversation.
|
||||
```
|
||||
|
||||
6. Cleanup:
|
||||
|
||||
```bash
|
||||
rm -f "$PROMPT_FILE" "$RESP_FILE" "$ERR_FILE"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
- **Binary not found:** Stop with install instructions.
|
||||
- **Auth missing:** Stop with login/API key instructions.
|
||||
- **Auth failure from stderr:** Surface the stderr line and ask the user to re-authenticate.
|
||||
- **JSON parse failure:** Show raw stdout from `$RESP_FILE` and stderr from `$ERR_FILE`.
|
||||
- **Empty response:** Tell the user "Claude returned no response. Check stderr for errors."
|
||||
- **Resume failure:** Delete `.context/claude-session-id` and retry with a fresh session.
|
||||
|
||||
---
|
||||
|
||||
## Important Rules
|
||||
|
||||
- Nested Claude is read-only in consult mode and tool-less in review/challenge.
|
||||
- Always include `--disable-slash-commands`.
|
||||
- Never pass nested Claude `Bash`, `Edit`, or `Write`.
|
||||
- Never interpolate user text into a shell command.
|
||||
- Present Claude's response faithfully, then add any host-agent synthesis after it.
|
||||
|
|
@ -16,7 +16,6 @@ Conventions:
|
|||
- [/browse](browse/SKILL.md): Fast headless browser for QA testing and site dogfooding.
|
||||
- [/canary](canary/SKILL.md): Post-deploy canary monitoring.
|
||||
- [/careful](careful/SKILL.md): Safety guardrails for destructive commands.
|
||||
- [/claude](claude/SKILL.md): Claude Code CLI wrapper for non-Claude hosts - three modes.
|
||||
- [/codex](codex/SKILL.md): OpenAI Codex CLI wrapper — three modes.
|
||||
- [/context-restore](context-restore/SKILL.md): Restore working context saved earlier by /context-save.
|
||||
- [/context-save](context-save/SKILL.md): Save working context.
|
||||
|
|
|
|||
|
|
@ -15,12 +15,6 @@ const gbrain = defineHost({
|
|||
descriptionLimit: null,
|
||||
},
|
||||
|
||||
generation: {
|
||||
generateMetadata: false,
|
||||
skipSkills: ['codex'],
|
||||
includeSkills: [],
|
||||
},
|
||||
|
||||
extraPathRewrites: [
|
||||
{ from: 'CLAUDE.md', to: 'AGENTS.md' },
|
||||
],
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@ const hermes = defineHost({
|
|||
name: 'hermes',
|
||||
displayName: 'Hermes',
|
||||
|
||||
generation: {
|
||||
generateMetadata: false,
|
||||
skipSkills: ['codex'],
|
||||
includeSkills: [],
|
||||
},
|
||||
|
||||
extraPathRewrites: [
|
||||
{ from: 'CLAUDE.md', to: 'AGENTS.md' },
|
||||
],
|
||||
|
|
|
|||
|
|
@ -4,21 +4,6 @@ const openclaw = defineHost({
|
|||
name: 'openclaw',
|
||||
displayName: 'OpenClaw',
|
||||
|
||||
frontmatter: {
|
||||
mode: 'allowlist',
|
||||
keepFields: ['name', 'description'],
|
||||
descriptionLimit: null,
|
||||
extraFields: {
|
||||
version: '0.15.2.0',
|
||||
},
|
||||
},
|
||||
|
||||
generation: {
|
||||
generateMetadata: false,
|
||||
skipSkills: ['codex'],
|
||||
includeSkills: [], // native ClawHub skills replaced the generated ones
|
||||
},
|
||||
|
||||
extraPathRewrites: [
|
||||
{ from: 'CLAUDE.md', to: 'AGENTS.md' },
|
||||
],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
# gstack-full Pipeline
|
||||
|
||||
Injected by the orchestrator for complete feature builds. Append to existing CLAUDE.md.
|
||||
|
||||
## Full Pipeline
|
||||
1. Read CLAUDE.md and understand the project context.
|
||||
2. Run /autoplan to review your approach (CEO + eng + design review pipeline).
|
||||
3. Implement the approved plan. Follow the planning discipline above.
|
||||
4. Run /ship to create a PR with tests, changelog, and version bump.
|
||||
5. Report back: PR URL, what shipped, decisions made, anything uncertain.
|
||||
|
||||
Do not ask for human input until the PR is ready for review.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# gstack-lite Planning Discipline
|
||||
|
||||
Injected by the orchestrator into spawned Claude Code sessions. Append to existing CLAUDE.md.
|
||||
|
||||
## Planning Discipline
|
||||
1. Read every file you will modify. Understand existing patterns first.
|
||||
2. Before writing code, state your plan: what, why, which files, test case, risk.
|
||||
3. When ambiguous, prefer: completeness over shortcuts, existing patterns over new ones,
|
||||
reversible choices over irreversible ones, safe defaults over clever ones.
|
||||
4. Self-review your changes before reporting done. Check for: missed files, broken
|
||||
imports, untested paths, style inconsistencies.
|
||||
5. Report when done: what shipped, what decisions you made, anything uncertain.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# gstack-plan: Full Review Gauntlet
|
||||
|
||||
Injected by the orchestrator when the user wants to plan a Claude Code project.
|
||||
Append to existing CLAUDE.md.
|
||||
|
||||
## Planning Pipeline
|
||||
1. Read CLAUDE.md and understand the project context.
|
||||
2. Run /office-hours to produce a design doc (problem statement, premises, alternatives).
|
||||
3. Run /autoplan to review the design (CEO + eng + design + DX reviews + codex adversarial).
|
||||
4. Save the final reviewed plan to a file the orchestrator can reference later.
|
||||
Write it to: plans/<project-slug>-plan-<date>.md in the current repo.
|
||||
Include the design doc, all review decisions, and the implementation sequence.
|
||||
5. Report back to the orchestrator:
|
||||
- Plan file path
|
||||
- One-paragraph summary of what was designed and the key decisions
|
||||
- List of accepted scope expansions (if any)
|
||||
- Recommended next step (usually: spawn a new session with gstack-full to implement)
|
||||
|
||||
Do not implement anything. This is planning only.
|
||||
The orchestrator will persist the plan link to its own memory/knowledge store.
|
||||
|
|
@ -107,9 +107,8 @@ const MODEL_ARG_VAL: Model = (() => {
|
|||
})();
|
||||
|
||||
// ─── Catalog Mode (v1.45.0.0 T4) ────────────────────────────
|
||||
// 'trim' (default): shorten frontmatter description to lead sentence,
|
||||
// move routing/voice prose into a "## When to invoke" body section, and
|
||||
// emit scripts/proactive-suggestions.json (single file across all skills).
|
||||
// 'trim' (default): shorten frontmatter description to lead sentence and
|
||||
// move routing/voice prose into a "## When to invoke" body section.
|
||||
// 'full': legacy v1.44 behavior — full description stays in frontmatter.
|
||||
const CATALOG_MODE_ARG = process.argv.find(a => a.startsWith('--catalog-mode'));
|
||||
const CATALOG_MODE: 'trim' | 'full' = (() => {
|
||||
|
|
@ -296,9 +295,7 @@ export { extractVoiceTriggers, processVoiceTriggers };
|
|||
// session pays for the full text. The catalog trim splits the description
|
||||
// into a one-line catalog entry (lead sentence + "(gstack)") that stays in
|
||||
// the frontmatter, and a "## When to invoke" body section that holds the
|
||||
// routing/voice triggers prose for in-skill discovery. A registry written
|
||||
// to scripts/proactive-suggestions.json (one entry per skill) makes routing
|
||||
// available to agents that need it without paying the always-loaded cost.
|
||||
// routing/voice triggers prose for in-skill discovery.
|
||||
//
|
||||
// Opt-out: `--catalog-mode=full` keeps v1.44 behavior (no trim, full
|
||||
// description in frontmatter). Use when debugging routing regressions or
|
||||
|
|
@ -423,8 +420,7 @@ export function toYamlInlineScalar(s: string): string {
|
|||
* (so it lands near the top of body content, where routing guidance
|
||||
* belongs)
|
||||
*
|
||||
* Returns the rewritten content plus the parts (used for proactive-suggestions
|
||||
* JSON aggregation at the end of the run).
|
||||
* Returns the rewritten content plus the extracted parts.
|
||||
*/
|
||||
export function applyCatalogTrim(content: string, skillName: string): { content: string; parts: CatalogParts } | null {
|
||||
// Locate description block in frontmatter
|
||||
|
|
@ -796,7 +792,7 @@ function processExternalHost(
|
|||
return { content: result, outputPath, outputDir, symlinkLoop };
|
||||
}
|
||||
|
||||
function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath: string; content: string; symlinkLoop?: boolean; catalogParts?: CatalogParts | null } {
|
||||
function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath: string; content: string; symlinkLoop?: boolean } {
|
||||
const tmplContent = fs.readFileSync(tmplPath, 'utf-8');
|
||||
const relTmplPath = path.relative(ROOT, tmplPath);
|
||||
let outputPath = tmplPath.replace(/\.tmpl$/, '');
|
||||
|
|
@ -855,19 +851,15 @@ function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath:
|
|||
}
|
||||
|
||||
// Catalog trim (Claude only — external hosts have their own frontmatter shapes)
|
||||
let catalogParts: CatalogParts | null = null;
|
||||
if (host === 'claude' && CATALOG_MODE === 'trim') {
|
||||
const trimmed = applyCatalogTrim(content, skillName);
|
||||
if (trimmed) {
|
||||
content = trimmed.content;
|
||||
catalogParts = trimmed.parts;
|
||||
}
|
||||
if (trimmed) content = trimmed.content;
|
||||
}
|
||||
|
||||
// --out-dir: repoint section-base paths to the out-dir (no-op otherwise).
|
||||
if (host === 'claude') content = rewriteSectionBase(content);
|
||||
|
||||
return { outputPath, content, symlinkLoop, catalogParts };
|
||||
return { outputPath, content, symlinkLoop };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -943,14 +935,6 @@ for (const currentHost of hostsToRun) {
|
|||
let hasChanges = false;
|
||||
const tokenBudget: Array<{ skill: string; lines: number; tokens: number }> = [];
|
||||
|
||||
// T4 catalog trim: collect routing/voice parts across all Claude skills,
|
||||
// then write scripts/proactive-suggestions.json once per gen-skill-docs run.
|
||||
const proactiveAggregate: Record<string, {
|
||||
lead: string;
|
||||
routing: string;
|
||||
voice_line: string | null;
|
||||
}> = {};
|
||||
|
||||
const currentHostConfig = getHostConfig(currentHost);
|
||||
for (const tmplPath of findTemplates()) {
|
||||
const dir = path.basename(path.dirname(tmplPath));
|
||||
|
|
@ -964,24 +948,7 @@ for (const currentHost of hostsToRun) {
|
|||
if (currentHostConfig.generation.skipSkills.includes(dir)) continue;
|
||||
}
|
||||
|
||||
const { outputPath, content, symlinkLoop, catalogParts } = processTemplate(tmplPath, currentHost);
|
||||
if (catalogParts) {
|
||||
// Root-skill detection: when the template lives at ROOT/SKILL.md.tmpl,
|
||||
// path.basename(path.dirname(tmplPath)) returns the repo's directory
|
||||
// name (e.g. "seville-v3" in a Conductor worktree, "gstack" on CI).
|
||||
// That's non-deterministic across machines and breaks CI freshness
|
||||
// checks. Use the frontmatter `name` field as the registry key — the
|
||||
// root SKILL.md.tmpl declares `name: gstack` explicitly. For all other
|
||||
// skills, `dir` matches the directory name which matches the
|
||||
// frontmatter name by convention.
|
||||
const isRoot = path.dirname(tmplPath) === ROOT;
|
||||
const key = isRoot ? 'gstack' : dir;
|
||||
proactiveAggregate[key] = {
|
||||
lead: catalogParts.lead,
|
||||
routing: catalogParts.routingProse,
|
||||
voice_line: catalogParts.voiceLine,
|
||||
};
|
||||
}
|
||||
const { outputPath, content, symlinkLoop } = processTemplate(tmplPath, currentHost);
|
||||
const relOutput = path.relative(OUT_DIR || ROOT, outputPath);
|
||||
|
||||
if (symlinkLoop) {
|
||||
|
|
@ -1056,66 +1023,19 @@ for (const currentHost of hostsToRun) {
|
|||
});
|
||||
}
|
||||
|
||||
// Generate gstack-lite and gstack-full for OpenClaw host
|
||||
// Generate the OpenClaw orchestrator-injection docs (gstack-lite / gstack-full /
|
||||
// gstack-plan CLAUDE.md snippets). Sources live in openclaw/templates/ —
|
||||
// plain markdown, no placeholder resolution — and are copied byte-for-byte
|
||||
// to openclaw/ at gen time.
|
||||
if (currentHost === 'openclaw' && !DRY_RUN) {
|
||||
const openclawDir = path.join(ROOT, 'openclaw');
|
||||
if (!fs.existsSync(openclawDir)) fs.mkdirSync(openclawDir, { recursive: true });
|
||||
|
||||
const gstackLite = `# gstack-lite Planning Discipline
|
||||
|
||||
Injected by the orchestrator into spawned Claude Code sessions. Append to existing CLAUDE.md.
|
||||
|
||||
## Planning Discipline
|
||||
1. Read every file you will modify. Understand existing patterns first.
|
||||
2. Before writing code, state your plan: what, why, which files, test case, risk.
|
||||
3. When ambiguous, prefer: completeness over shortcuts, existing patterns over new ones,
|
||||
reversible choices over irreversible ones, safe defaults over clever ones.
|
||||
4. Self-review your changes before reporting done. Check for: missed files, broken
|
||||
imports, untested paths, style inconsistencies.
|
||||
5. Report when done: what shipped, what decisions you made, anything uncertain.
|
||||
`;
|
||||
fs.writeFileSync(path.join(openclawDir, 'gstack-lite-CLAUDE.md'), gstackLite);
|
||||
console.log('GENERATED: openclaw/gstack-lite-CLAUDE.md');
|
||||
|
||||
const gstackFull = `# gstack-full Pipeline
|
||||
|
||||
Injected by the orchestrator for complete feature builds. Append to existing CLAUDE.md.
|
||||
|
||||
## Full Pipeline
|
||||
1. Read CLAUDE.md and understand the project context.
|
||||
2. Run /autoplan to review your approach (CEO + eng + design review pipeline).
|
||||
3. Implement the approved plan. Follow the planning discipline above.
|
||||
4. Run /ship to create a PR with tests, changelog, and version bump.
|
||||
5. Report back: PR URL, what shipped, decisions made, anything uncertain.
|
||||
|
||||
Do not ask for human input until the PR is ready for review.
|
||||
`;
|
||||
fs.writeFileSync(path.join(openclawDir, 'gstack-full-CLAUDE.md'), gstackFull);
|
||||
console.log('GENERATED: openclaw/gstack-full-CLAUDE.md');
|
||||
|
||||
const gstackPlan = `# gstack-plan: Full Review Gauntlet
|
||||
|
||||
Injected by the orchestrator when the user wants to plan a Claude Code project.
|
||||
Append to existing CLAUDE.md.
|
||||
|
||||
## Planning Pipeline
|
||||
1. Read CLAUDE.md and understand the project context.
|
||||
2. Run /office-hours to produce a design doc (problem statement, premises, alternatives).
|
||||
3. Run /autoplan to review the design (CEO + eng + design + DX reviews + codex adversarial).
|
||||
4. Save the final reviewed plan to a file the orchestrator can reference later.
|
||||
Write it to: plans/<project-slug>-plan-<date>.md in the current repo.
|
||||
Include the design doc, all review decisions, and the implementation sequence.
|
||||
5. Report back to the orchestrator:
|
||||
- Plan file path
|
||||
- One-paragraph summary of what was designed and the key decisions
|
||||
- List of accepted scope expansions (if any)
|
||||
- Recommended next step (usually: spawn a new session with gstack-full to implement)
|
||||
|
||||
Do not implement anything. This is planning only.
|
||||
The orchestrator will persist the plan link to its own memory/knowledge store.
|
||||
`;
|
||||
fs.writeFileSync(path.join(openclawDir, 'gstack-plan-CLAUDE.md'), gstackPlan);
|
||||
console.log('GENERATED: openclaw/gstack-plan-CLAUDE.md');
|
||||
const openclawTemplatesDir = path.join(openclawDir, 'templates');
|
||||
for (const variant of ['lite', 'full', 'plan'] as const) {
|
||||
const fileName = `gstack-${variant}-CLAUDE.md`;
|
||||
const content = fs.readFileSync(path.join(openclawTemplatesDir, fileName), 'utf-8');
|
||||
fs.writeFileSync(path.join(openclawDir, fileName), content);
|
||||
console.log(`GENERATED: openclaw/${fileName}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (DRY_RUN && hasChanges) {
|
||||
|
|
@ -1124,42 +1044,6 @@ The orchestrator will persist the plan link to its own memory/knowledge store.
|
|||
failures.push({ host: currentHost, error: new Error('Stale files detected') });
|
||||
}
|
||||
|
||||
// T4 catalog trim: write aggregated proactive-suggestions.json (Claude only).
|
||||
// The JSON registry lets agents pull voice triggers / routing prose for any
|
||||
// skill on demand instead of paying for it always-loaded in the catalog.
|
||||
//
|
||||
// No timestamp field — keeps the file content-deterministic across runs so
|
||||
// CI dry-run freshness checks don't flap on regen. If a per-run timestamp
|
||||
// is ever needed for debugging, write it to a separate `.gen-stamp` file.
|
||||
// Skip the global proactive-suggestions.json in --out-dir mode: it lives at
|
||||
// a repo path (scripts/) and the dev workspace render doesn't need it.
|
||||
if (currentHost === 'claude' && CATALOG_MODE === 'trim' && Object.keys(proactiveAggregate).length > 0 && !DRY_RUN && !OUT_DIR) {
|
||||
const proactivePath = path.join(ROOT, 'scripts', 'proactive-suggestions.json');
|
||||
// Sort keys alphabetically so the serialized JSON is identical across
|
||||
// machines regardless of filesystem-iteration order. Without this, CI
|
||||
// freshness checks fail when the local dev machine and CI runner
|
||||
// discover templates in different orders.
|
||||
const sortedSkills: typeof proactiveAggregate = {};
|
||||
for (const key of Object.keys(proactiveAggregate).sort()) {
|
||||
sortedSkills[key] = proactiveAggregate[key];
|
||||
}
|
||||
const payload = {
|
||||
$schema: 'https://gstack.dev/schemas/proactive-suggestions.json',
|
||||
catalog_mode: 'trim',
|
||||
note: 'Routing / voice-trigger prose extracted from SKILL.md frontmatter descriptions during catalog trim. Loaded on demand when routing guidance is needed.',
|
||||
skills: sortedSkills,
|
||||
};
|
||||
const serialized = JSON.stringify(payload, null, 2) + '\n';
|
||||
// Only write if content actually changed — prevents needless touches that
|
||||
// would flap CI freshness checks. Read existing file, compare, skip write
|
||||
// when identical.
|
||||
let existing = '';
|
||||
try { existing = fs.readFileSync(proactivePath, 'utf-8'); } catch { /* first run */ }
|
||||
if (existing !== serialized) {
|
||||
fs.writeFileSync(proactivePath, serialized);
|
||||
}
|
||||
}
|
||||
|
||||
// Print token budget summary
|
||||
if (!DRY_RUN && tokenBudget.length > 0) {
|
||||
tokenBudget.sort((a, b) => b.lines - a.lines);
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
{
|
||||
"$schema": "https://gstack.dev/schemas/proactive-suggestions.json",
|
||||
"catalog_mode": "trim",
|
||||
"note": "Routing / voice-trigger prose extracted from SKILL.md frontmatter descriptions during catalog trim. Loaded on demand when routing guidance is needed.",
|
||||
"skills": {
|
||||
"autoplan": {
|
||||
"lead": "Auto-review pipeline — reads the full CEO, design, eng, and DX review skills from disk and runs them sequentially with auto-decisions using 6 decision principles.",
|
||||
"routing": "Surfaces\ntaste decisions (close approaches, borderline scope, codex disagreements) at a final\napproval gate. One command, fully reviewed plan out.\nUse when asked to \"auto review\", \"autoplan\", \"run all reviews\", \"review this plan\nautomatically\", or \"make the decisions for me\".\nProactively suggest when the user has a plan file and wants to run the full review\ngauntlet without answering 15-30 intermediate questions.",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"auto plan\", \"automatic review\"."
|
||||
},
|
||||
"benchmark": {
|
||||
"lead": "Performance regression detection using the browse daemon.",
|
||||
"routing": "Establishes\nbaselines for page load times, Core Web Vitals, and resource sizes.\nCompares before/after on every PR. Tracks performance trends over time.\nUse when: \"performance\", \"benchmark\", \"page speed\", \"lighthouse\", \"web vitals\",\n\"bundle size\", \"load time\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"speed test\", \"check performance\"."
|
||||
},
|
||||
"benchmark-models": {
|
||||
"lead": "Cross-model benchmark for gstack skills.",
|
||||
"routing": "Runs the same prompt through Claude,\nGPT (via Codex CLI), and Gemini side-by-side — compares latency, tokens, cost,\nand optionally quality via LLM judge. Answers \"which model is actually best\nfor this skill?\" with data instead of vibes. Separate from /benchmark, which\nmeasures web page performance. Use when: \"benchmark models\", \"compare models\",\n\"which model is best for X\", \"cross-model comparison\", \"model shootout\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"compare models\", \"model shootout\", \"which model is best\"."
|
||||
},
|
||||
"browse": {
|
||||
"lead": "Fast headless browser for QA testing and site dogfooding.",
|
||||
"routing": "Navigate any URL, interact with\nelements, verify page state, diff before/after actions, take annotated screenshots, check\nresponsive layouts, test forms and uploads, handle dialogs, and assert element states.\n~100ms per command. Use when you need to test a feature, verify a deployment, dogfood a\nuser flow, or file a bug with evidence. Use when asked to \"open in browser\", \"test the\nsite\", \"take a screenshot\", or \"dogfood this\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"canary": {
|
||||
"lead": "Post-deploy canary monitoring.",
|
||||
"routing": "Watches the live app for console errors,\nperformance regressions, and page failures using the browse daemon. Takes\nperiodic screenshots, compares against pre-deploy baselines, and alerts\non anomalies. Use when: \"monitor deploy\", \"canary\", \"post-deploy check\",\n\"watch production\", \"verify deploy\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"careful": {
|
||||
"lead": "Safety guardrails for destructive commands.",
|
||||
"routing": "Warns before rm -rf, DROP TABLE,\nforce-push, git reset --hard, kubectl delete, and similar destructive operations.\nUser can override each warning. Use when touching prod, debugging live systems,\nor working in a shared environment. Use when asked to \"be careful\", \"safety mode\",\n\"prod mode\", or \"careful mode\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"codex": {
|
||||
"lead": "OpenAI Codex CLI wrapper — three modes.",
|
||||
"routing": "Code review: independent diff review via\ncodex review with pass/fail gate. Challenge: adversarial mode that tries to break\nyour code. Consult: ask codex anything with session continuity for follow-ups.\nThe \"200 IQ autistic developer\" second opinion. Use when asked to \"codex review\",\n\"codex challenge\", \"ask codex\", \"second opinion\", or \"consult codex\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"code x\", \"code ex\", \"get another opinion\"."
|
||||
},
|
||||
"context-restore": {
|
||||
"lead": "Restore working context saved earlier by /context-save.",
|
||||
"routing": "Loads the most recent\nsaved state (preferring the current branch, falling back across branches) so\nyou can pick up where you left off — even across Conductor workspace handoffs.\nUse when asked to \"resume\", \"restore context\", \"where was I\", or\n\"pick up where I left off\". Pair with /context-save.\nFormerly /checkpoint resume — renamed because Claude Code treats /checkpoint\nas a native rewind alias in current environments.",
|
||||
"voice_line": null
|
||||
},
|
||||
"context-save": {
|
||||
"lead": "Save working context.",
|
||||
"routing": "Captures git state, decisions made, and remaining work\nso any future session can pick up without losing a beat.\nUse when asked to \"save progress\", \"save state\", \"context save\", or\n\"save my work\". Pair with /context-restore to resume later.\nFormerly /checkpoint — renamed because Claude Code treats /checkpoint as a\nnative rewind alias in current environments, which was shadowing this skill.",
|
||||
"voice_line": null
|
||||
},
|
||||
"cso": {
|
||||
"lead": "Chief Security Officer mode.",
|
||||
"routing": "Infrastructure-first security audit: secrets archaeology,\ndependency supply chain, CI/CD pipeline security, LLM/AI security, skill supply chain\nscanning, plus OWASP Top 10, STRIDE threat modeling, and active verification.\nTwo modes: daily (zero-noise, 8/10 confidence gate) and comprehensive (monthly deep\nscan, 2/10 bar). Trend tracking across audit runs.\nUse when: \"security audit\", \"threat model\", \"pentest review\", \"OWASP\", \"CSO review\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"see-so\", \"see so\", \"security review\", \"security check\", \"vulnerability scan\", \"run security\"."
|
||||
},
|
||||
"design-consultation": {
|
||||
"lead": "Design consultation: understands your product, researches the landscape, proposes a complete design system (aesthetic, typography, color, layout, spacing, motion), and generates font+color preview...",
|
||||
"routing": "Creates DESIGN.md as your project's design source\nof truth. For existing sites, use /plan-design-review to infer the system instead.\nUse when asked to \"design system\", \"brand guidelines\", or \"create DESIGN.md\".\nProactively suggest when starting a new project's UI with no existing\ndesign system or DESIGN.md.",
|
||||
"voice_line": null
|
||||
},
|
||||
"design-html": {
|
||||
"lead": "Design finalization: generates production-quality Pretext-native HTML/CSS.",
|
||||
"routing": "Works with approved mockups from /design-shotgun, CEO plans from /plan-ceo-review,\ndesign review context from /plan-design-review, or from scratch with a user\ndescription. Text actually reflows, heights are computed, layouts are dynamic.\n30KB overhead, zero deps. Smart API routing: picks the right Pretext patterns\nfor each design type. Use when: \"finalize this design\", \"turn this into HTML\",\n\"build me a page\", \"implement this design\", or after any planning skill.\nProactively suggest when user has approved a design or has a plan ready.",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"build the design\", \"code the mockup\", \"make it real\"."
|
||||
},
|
||||
"design-review": {
|
||||
"lead": "Designer's eye QA: finds visual inconsistency, spacing issues, hierarchy problems, AI slop patterns, and slow interactions — then fixes them.",
|
||||
"routing": "Iteratively fixes issues\nin source code, committing each fix atomically and re-verifying with before/after\nscreenshots. For plan-mode design review (before implementation), use /plan-design-review.\nUse when asked to \"audit the design\", \"visual QA\", \"check if it looks good\", or \"design polish\".\nProactively suggest when the user mentions visual inconsistencies or\nwants to polish the look of a live site.",
|
||||
"voice_line": null
|
||||
},
|
||||
"design-shotgun": {
|
||||
"lead": "Design shotgun: generate multiple AI design variants, open a comparison board, collect structured feedback, and iterate.",
|
||||
"routing": "Standalone design exploration you can\nrun anytime. Use when: \"explore designs\", \"show me options\", \"design variants\",\n\"visual brainstorm\", or \"I don't like how this looks\".\nProactively suggest when the user describes a UI feature but hasn't seen\nwhat it could look like.",
|
||||
"voice_line": null
|
||||
},
|
||||
"devex-review": {
|
||||
"lead": "Live developer experience audit.",
|
||||
"routing": "Uses the browse tool to actually TEST the\ndeveloper experience: navigates docs, tries the getting started flow, times\nTTHW, screenshots error messages, evaluates CLI help text. Produces a DX\nscorecard with evidence. Compares against /plan-devex-review scores if they\nexist (the boomerang: plan said 3 minutes, reality says 8). Use when asked to\n\"test the DX\", \"DX audit\", \"developer experience test\", or \"try the\nonboarding\". Proactively suggest after shipping a developer-facing feature.",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"dx audit\", \"test the developer experience\", \"try the onboarding\", \"developer experience test\"."
|
||||
},
|
||||
"diagram": {
|
||||
"lead": "Turn an English description (or mermaid source) into a diagram triplet: the source, an editable .excalidraw file you can open",
|
||||
"routing": "on excalidraw.com,\nand rendered SVG + PNG (clean mermaid style; the .excalidraw carries the\nhand-drawn aesthetic). Fully offline.\nUse when asked to \"make a diagram\", \"draw the architecture\", \"create a\nflowchart\", \"diagram this\", or \"visualize this flow\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"document-generate": {
|
||||
"lead": "Generate missing documentation from scratch for a feature, module, or entire project.",
|
||||
"routing": "Uses the Diataxis framework (tutorial / how-to / reference / explanation) to produce\ncomplete, structured documentation. Can be invoked standalone or called by\n/document-release when it finds coverage gaps. Use when asked to \"write docs\",\n\"generate documentation\", \"document this feature\", \"create a tutorial\", or\n\"explain this module\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"document-release": {
|
||||
"lead": "Post-ship documentation update.",
|
||||
"routing": "Reads all project docs, cross-references the\ndiff, builds a Diataxis coverage map (reference/how-to/tutorial/explanation),\nupdates README/ARCHITECTURE/CONTRIBUTING/CLAUDE.md to match what shipped,\ndetects architecture diagram drift, polishes CHANGELOG voice with a sell-test\nrubric, cleans up TODOS, and optionally bumps VERSION. Surfaces documentation\ndebt in the PR body. Use when asked to \"update the docs\", \"sync documentation\",\nor \"post-ship docs\". Proactively suggest after a PR is merged or code is shipped.",
|
||||
"voice_line": null
|
||||
},
|
||||
"freeze": {
|
||||
"lead": "Restrict file edits to a specific directory for the session.",
|
||||
"routing": "Blocks Edit and\nWrite outside the allowed path. Use when debugging to prevent accidentally\n\"fixing\" unrelated code, or when you want to scope changes to one module.\nUse when asked to \"freeze\", \"restrict edits\", \"only edit this folder\",\nor \"lock down edits\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"gstack": {
|
||||
"lead": "Router for the gstack skill suite.",
|
||||
"routing": "Sends any gstack request to the right skill\n(planning, review, QA, shipping, debugging, docs, security, design). For browser/QA\nand dogfooding it points you at /browse. Use when you invoke gstack without a specific\nskill, or ask \"which gstack skill fits this?\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"gstack-upgrade": {
|
||||
"lead": "Upgrade gstack to the latest version.",
|
||||
"routing": "Detects global vs vendored install,\nruns the upgrade, and shows what's new. Use when asked to \"upgrade gstack\",\n\"update gstack\", or \"get latest version\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"upgrade the tools\", \"update the tools\", \"gee stack upgrade\", \"g stack upgrade\"."
|
||||
},
|
||||
"guard": {
|
||||
"lead": "Full safety mode: destructive command warnings + directory-scoped edits.",
|
||||
"routing": "Combines /careful (warns before rm -rf, DROP TABLE, force-push, etc.) with\n/freeze (blocks edits outside a specified directory). Use for maximum safety\nwhen touching prod or debugging live systems. Use when asked to \"guard mode\",\n\"full safety\", \"lock it down\", or \"maximum safety\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"health": {
|
||||
"lead": "Code quality dashboard.",
|
||||
"routing": "Wraps existing project tools (type checker, linter,\ntest runner, dead code detector, shell linter), computes a weighted composite\n0-10 score, and tracks trends over time. Use when: \"health check\",\n\"code quality\", \"how healthy is the codebase\", \"run all checks\",\n\"quality score\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"investigate": {
|
||||
"lead": "Systematic debugging with root cause investigation.",
|
||||
"routing": "Four phases: investigate,\nanalyze, hypothesize, implement. Iron Law: no fixes without root cause.\nUse when asked to \"debug this\", \"fix this bug\", \"why is this broken\",\n\"investigate this error\", or \"root cause analysis\".\nProactively invoke this skill (do NOT debug directly) when the user reports\nerrors, 500 errors, stack traces, unexpected behavior, \"it was working\nyesterday\", or is troubleshooting why something stopped working.",
|
||||
"voice_line": null
|
||||
},
|
||||
"ios-clean": {
|
||||
"lead": "Remove the DebugBridge SPM package and all #if DEBUG wiring from an iOS app.",
|
||||
"routing": "Cleans up StateServer, DebugOverlay, accessor codegen output, and\napp-side hooks installed by /ios-qa. This is a convenience wrapper —\nthe structural Release-build guard (Package.swift conditional + CI\nswift build -c release check) is the safety-critical path.\nUse when asked to \"clean the iOS debug bridge\", \"remove DebugBridge\",\nor \"strip the gstack iOS instrumentation\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"clean the iOS debug bridge\", \"remove DebugBridge\", \"strip the gstack iOS instrumentation\"."
|
||||
},
|
||||
"ios-design-review": {
|
||||
"lead": "Visual design audit for iOS apps on real hardware.",
|
||||
"routing": "Connects to a real\niPhone via the same StateServer as /ios-qa, screenshots every screen,\nevaluates against Apple HIG, DESIGN.md, and design best practices. Scores\neach dimension 0-10 with \"what would make it a 10\" framing — mirrors\n/plan-design-review for browser. For plan-stage design review (before\nimplementation), use /plan-design-review. For live web visual audits, use\n/design-review.\nUse when asked to \"review the iOS design\", \"audit the iPhone app's\nvisuals\", or \"design QA the iOS app\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"review the iOS design\", \"audit the iPhone app's visuals\", \"design QA the iPhone app\"."
|
||||
},
|
||||
"ios-fix": {
|
||||
"lead": "Autonomous iOS bug fixer.",
|
||||
"routing": "Takes a bug found by /ios-qa, reads the source,\nwrites the fix, rebuilds, redeploys, and verifies the fix on the real\ndevice. Closes the loop: find bug → fix bug → confirm fix — zero human\nintervention. Captures the pre-bug state snapshot as a regression test\nfixture, so the bug can never recur silently.\nUse when /ios-qa reports a bug and you want it fixed automatically, or\nwhen asked to \"fix this iOS bug\", \"patch the iPhone app\", or \"auto-fix\nthe iOS issue\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"fix the iOS bug\", \"patch the iPhone app\", \"auto-fix the iOS issue\"."
|
||||
},
|
||||
"ios-qa": {
|
||||
"lead": "Live-device iOS QA for SwiftUI apps.",
|
||||
"routing": "Connects to a real iPhone via USB\nCoreDevice IPv6 tunnel, reads Swift source to understand every screen, then\nruns a vision-driven agent loop: screenshot → analyze → decide → act →\nverify → repeat. All interaction happens via HTTP to an embedded\nStateServer in the app under test. Optionally exposes the device over\nTailscale so remote agents (OpenClaw, Codex, any HTTP-capable agent) can\nrun iOS QA from anywhere without touching the hardware.\nUse when asked to \"ios qa\", \"test my iPhone app\", \"find bugs on the device\",\nor \"qa the iOS app\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"iOS quality check\", \"test the iPhone app\", \"run iOS QA\"."
|
||||
},
|
||||
"ios-sync": {
|
||||
"lead": "Regenerate the iOS debug bridge against the latest upstream gstack templates.",
|
||||
"routing": "Updates StateServer.swift, DebugOverlay.swift, Package.swift,\nand the typed @Observable state accessors. Use after you upgrade gstack\nor add new ViewModels/properties that need accessor coverage.\nUse when asked to \"resync the iOS debug bridge\", \"regenerate iOS\naccessors\", or \"update the gstack iOS instrumentation\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"resync the iOS debug bridge\", \"regenerate iOS accessors\", \"update the gstack iOS instrumentation\"."
|
||||
},
|
||||
"land-and-deploy": {
|
||||
"lead": "Land and deploy workflow.",
|
||||
"routing": "Merges the PR, waits for CI and deploy,\nverifies production health via canary checks. Takes over after /ship\ncreates the PR. Use when: \"merge\", \"land\", \"deploy\", \"merge and verify\",\n\"land it\", \"ship it to production\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"landing-report": {
|
||||
"lead": "Read-only queue dashboard for workspace-aware ship.",
|
||||
"routing": "Shows which VERSION slots\nare currently claimed by open PRs, which sibling Conductor workspaces have\nWIP work likely to ship soon, and what slot /ship would pick next. No\nmutations — just a snapshot. Use when asked to \"landing report\", \"what's in\nthe queue\", \"show me open PRs\", or \"which version do I claim next\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"learn": {
|
||||
"lead": "Manage project learnings.",
|
||||
"routing": "Review, search, prune, and export what gstack\nhas learned across sessions. Use when asked to \"what have we learned\",\n\"show learnings\", \"prune stale learnings\", or \"export learnings\".\nProactively suggest when the user asks about past patterns or wonders\n\"didn't we fix this before?\"",
|
||||
"voice_line": null
|
||||
},
|
||||
"make-pdf": {
|
||||
"lead": "Turn any markdown file into a publication-quality PDF.",
|
||||
"routing": "Proper 1in margins,\nintelligent page breaks, page numbers, cover pages, running headers, curly\nquotes and em dashes, clickable TOC, diagonal DRAFT watermark. Not a draft\nartifact — a finished artifact. Use when asked to \"make a PDF\", \"export to\nPDF\", \"turn this markdown into a PDF\", or \"generate a document\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"make this a pdf\", \"make it a pdf\", \"export to pdf\", \"turn this into a pdf\", \"turn this markdown into a pdf\", \"generate a pdf\", \"make a pdf from\", \"pdf this markdown\"."
|
||||
},
|
||||
"office-hours": {
|
||||
"lead": "YC Office Hours — two modes.",
|
||||
"routing": "Startup mode: six forcing questions that expose\ndemand reality, status quo, desperate specificity, narrowest wedge, observation,\nand future-fit. Builder mode: design thinking brainstorming for side projects,\nhackathons, learning, and open source. Saves a design doc.\nUse when asked to \"brainstorm this\", \"I have an idea\", \"help me think through\nthis\", \"office hours\", or \"is this worth building\".\nProactively invoke this skill (do NOT answer directly) when the user describes\na new product idea, asks whether something is worth building, wants to think\nthrough design decisions for something that doesn't exist yet, or is exploring\na concept before any code is written.\nUse before /plan-ceo-review or /plan-eng-review.",
|
||||
"voice_line": null
|
||||
},
|
||||
"open-gstack-browser": {
|
||||
"lead": "Launch GStack Browser — AI-controlled Chromium with the sidebar extension baked in.",
|
||||
"routing": "Opens a visible browser window where you can watch every action in real time.\nThe sidebar shows a live activity feed and chat. Anti-bot stealth built in.\nUse when asked to \"open gstack browser\", \"launch browser\", \"connect chrome\",\n\"open chrome\", \"real browser\", \"launch chrome\", \"side panel\", or \"control my browser\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"show me the browser\"."
|
||||
},
|
||||
"pair-agent": {
|
||||
"lead": "Pair a remote AI agent with your browser.",
|
||||
"routing": "One command generates a setup key and\nprints instructions the other agent can follow to connect. Works with OpenClaw,\nHermes, Codex, Cursor, or any agent that can make HTTP requests. The remote agent\ngets its own tab with scoped access (read+write by default, admin on request).\nUse when asked to \"pair agent\", \"connect agent\", \"share browser\", \"remote browser\",\n\"let another agent use my browser\", or \"give browser access\".",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"pair agent\", \"connect agent\", \"share my browser\", \"remote browser access\"."
|
||||
},
|
||||
"plan-ceo-review": {
|
||||
"lead": "CEO/founder-mode plan review.",
|
||||
"routing": "Rethink the problem, find the 10-star product,\nchallenge premises, expand scope when it creates a better product. Four modes:\nSCOPE EXPANSION (dream big), SELECTIVE EXPANSION (hold scope + cherry-pick\nexpansions), HOLD SCOPE (maximum rigor), SCOPE REDUCTION (strip to essentials).\nUse when asked to \"think bigger\", \"expand scope\", \"strategy review\", \"rethink this\",\nor \"is this ambitious enough\".\nProactively suggest when the user is questioning scope or ambition of a plan,\nor when the plan feels like it could be thinking bigger.",
|
||||
"voice_line": null
|
||||
},
|
||||
"plan-design-review": {
|
||||
"lead": "Designer's eye plan review — interactive, like CEO and Eng review.",
|
||||
"routing": "Rates each design dimension 0-10, explains what would make it a 10,\nthen fixes the plan to get there. Works in plan mode. For live site\nvisual audits, use /design-review. Use when asked to \"review the design plan\"\nor \"design critique\".\nProactively suggest when the user has a plan with UI/UX components that\nshould be reviewed before implementation.",
|
||||
"voice_line": null
|
||||
},
|
||||
"plan-devex-review": {
|
||||
"lead": "Interactive developer experience plan review.",
|
||||
"routing": "Explores developer personas,\nbenchmarks against competitors, designs magical moments, and traces friction\npoints before scoring. Three modes: DX EXPANSION (competitive advantage),\nDX POLISH (bulletproof every touchpoint), DX TRIAGE (critical gaps only).\nUse when asked to \"DX review\", \"developer experience audit\", \"devex review\",\nor \"API design review\".\nProactively suggest when the user has a plan for developer-facing products\n(APIs, CLIs, SDKs, libraries, platforms, docs).",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"dx review\", \"developer experience review\", \"devex review\", \"devex audit\", \"API design review\", \"onboarding review\"."
|
||||
},
|
||||
"plan-eng-review": {
|
||||
"lead": "Eng manager-mode plan review.",
|
||||
"routing": "Lock in the execution plan — architecture,\ndata flow, diagrams, edge cases, test coverage, performance. Walks through\nissues interactively with opinionated recommendations. Use when asked to\n\"review the architecture\", \"engineering review\", or \"lock in the plan\".\nProactively suggest when the user has a plan or design doc and is about to\nstart coding — to catch architecture issues before implementation.",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"tech review\", \"technical review\", \"plan engineering review\"."
|
||||
},
|
||||
"plan-tune": {
|
||||
"lead": "Self-tuning question sensitivity + developer psychographic for gstack (v1: observational).",
|
||||
"routing": "Review which AskUserQuestion prompts fire across gstack skills, set per-question preferences\n(never-ask / always-ask / ask-only-for-one-way), inspect the dual-track\nprofile (what you declared vs what your behavior suggests), and enable/disable\nquestion tuning. Conversational interface — no CLI syntax required.\n\nUse when asked to \"tune questions\", \"stop asking me that\", \"too many questions\",\n\"show my profile\", \"what questions have I been asked\", \"show my vibe\",\n\"developer profile\", or \"turn off question tuning\". \n\nProactively suggest when the user says the same gstack question has come up before,\nor when they explicitly override a recommendation for the Nth time.",
|
||||
"voice_line": null
|
||||
},
|
||||
"qa": {
|
||||
"lead": "Systematically QA test a web application and fix bugs found.",
|
||||
"routing": "Runs QA testing,\nthen iteratively fixes bugs in source code, committing each fix atomically and\nre-verifying. Use when asked to \"qa\", \"QA\", \"test this site\", \"find bugs\",\n\"test and fix\", or \"fix what's broken\".\nProactively suggest when the user says a feature is ready for testing\nor asks \"does this work?\". Three tiers: Quick (critical/high only),\nStandard (+ medium), Exhaustive (+ cosmetic). Produces before/after health scores,\nfix evidence, and a ship-readiness summary. For report-only mode, use /qa-only.",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"quality check\", \"test the app\", \"run QA\"."
|
||||
},
|
||||
"qa-only": {
|
||||
"lead": "Report-only QA testing.",
|
||||
"routing": "Systematically tests a web application and produces a\nstructured report with health score, screenshots, and repro steps — but never\nfixes anything. Use when asked to \"just report bugs\", \"qa report only\", or\n\"test but don't fix\". For the full test-fix-verify loop, use /qa instead.\nProactively suggest when the user wants a bug report without any code changes.",
|
||||
"voice_line": "Voice triggers (speech-to-text aliases): \"bug report\", \"just check for bugs\"."
|
||||
},
|
||||
"retro": {
|
||||
"lead": "Weekly engineering retrospective.",
|
||||
"routing": "Analyzes commit history, work patterns,\nand code quality metrics with persistent history and trend tracking.\nTeam-aware: breaks down per-person contributions with praise and growth areas.\nUse when asked to \"weekly retro\", \"what did we ship\", or \"engineering retrospective\".\nProactively suggest at the end of a work week or sprint.",
|
||||
"voice_line": null
|
||||
},
|
||||
"review": {
|
||||
"lead": "Pre-landing PR review.",
|
||||
"routing": "Analyzes diff against the base branch for SQL safety, LLM trust\nboundary violations, conditional side effects, and other structural issues. Use when\nasked to \"review this PR\", \"code review\", \"pre-landing review\", or \"check my diff\".\nProactively suggest when the user is about to merge or land code changes.",
|
||||
"voice_line": null
|
||||
},
|
||||
"scrape": {
|
||||
"lead": "Pull data from a web page.",
|
||||
"routing": "First call on a new intent prototypes the flow\nvia $B primitives and returns JSON. Subsequent calls on a matching intent\nroute to a codified browser-skill and return in ~200ms. Read-only — for\nmutating flows (form fills, clicks, submissions), use /automate.\nUse when asked to \"scrape\", \"get data from\", \"pull\", \"extract from\", or\n\"what's on\" a page.",
|
||||
"voice_line": null
|
||||
},
|
||||
"setup-browser-cookies": {
|
||||
"lead": "Import cookies from your real Chromium browser into the headless browse session.",
|
||||
"routing": "Opens an interactive picker UI where you select which cookie domains to import.\nUse before QA testing authenticated pages. Use when asked to \"import cookies\",\n\"login to the site\", or \"authenticate the browser\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"setup-deploy": {
|
||||
"lead": "Configure deployment settings for /land-and-deploy.",
|
||||
"routing": "Detects your deploy\nplatform (Fly.io, Render, Vercel, Netlify, Heroku, GitHub Actions, custom),\nproduction URL, health check endpoints, and deploy status commands. Writes\nthe configuration to CLAUDE.md so all future deploys are automatic.\nUse when: \"setup deploy\", \"configure deployment\", \"set up land-and-deploy\",\n\"how do I deploy with gstack\", \"add deploy config\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"setup-gbrain": {
|
||||
"lead": "Set up gbrain for this coding agent: install the CLI, initialize a local PGLite or Supabase brain, register MCP, capture per-remote trust policy.",
|
||||
"routing": "One command from zero to \"gbrain is running, and this agent\ncan call it.\" Use when: \"setup gbrain\", \"connect gbrain\", \"start\ngbrain\", \"install gbrain\", \"configure gbrain for this machine\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"ship": {
|
||||
"lead": "Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION, update CHANGELOG, commit, push, create PR.",
|
||||
"routing": "Use when asked to \"ship\", \"deploy\",\n\"push to main\", \"create a PR\", \"merge and push\", or \"get it deployed\".\nProactively invoke this skill (do NOT push/PR directly) when the user says code\nis ready, asks about deploying, wants to push code up, or asks to create a PR.",
|
||||
"voice_line": null
|
||||
},
|
||||
"skillify": {
|
||||
"lead": "Codify the most recent successful /scrape flow into a permanent browser-skill on disk.",
|
||||
"routing": "Future /scrape calls with the same intent run\nthe codified script in ~200ms instead of re-driving the page. Walks\nback through the conversation, synthesizes script.ts + script.test.ts\n+ fixture, runs the test in a temp dir, and asks before committing.\nUse when asked to \"skillify\", \"codify\", \"save this scrape\", or\n\"make this permanent\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"spec": {
|
||||
"lead": "Turn vague intent into a precise, executable spec in five phases.",
|
||||
"routing": "Files the issue,\noptionally spawns a Claude Code agent in a fresh worktree, and lets /ship close\nthe source issue on merge. Use when asked to \"spec this out\", \"file an issue\",\n\"write up a ticket\", \"make this a GitHub issue\", or \"turn this into a backlog item\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"sync-gbrain": {
|
||||
"lead": "Keep gbrain current with this repo's code and refresh agent search guidance in CLAUDE.md. Wraps the gstack-gbrain-sync orchestrator with state",
|
||||
"routing": "probing, native code-surface registration, capability checks,\nand a verdict block. Re-runnable, idempotent. Use when: \"sync gbrain\",\n\"refresh gbrain\", \"re-index this repo\", \"gbrain search isn't finding\nthings\".",
|
||||
"voice_line": null
|
||||
},
|
||||
"unfreeze": {
|
||||
"lead": "Clear the freeze boundary set by /freeze, allowing edits to all directories again.",
|
||||
"routing": "Use when you want to widen edit scope without ending the session.\nUse when asked to \"unfreeze\", \"unlock edits\", \"remove freeze\", or\n\"allow all edits\".",
|
||||
"voice_line": null
|
||||
}
|
||||
}
|
||||
}
|
||||
6
setup
6
setup
|
|
@ -85,7 +85,7 @@ NO_TEAM_MODE=0
|
|||
PLAN_TUNE_HOOKS_MODE="" # "" = resolve from env/config/prompt; "yes"/"no" = explicit
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--host) [ -z "$2" ] && echo "Missing value for --host (expected claude, codex, kiro, factory, opencode, openclaw, hermes, gbrain, or auto)" >&2 && exit 1; HOST="$2"; shift 2 ;;
|
||||
--host) [ -z "$2" ] && echo "Missing value for --host (expected claude, codex, kiro, factory, opencode, cursor, slate, openclaw, hermes, gbrain, or auto)" >&2 && exit 1; HOST="$2"; shift 2 ;;
|
||||
--host=*) HOST="${1#--host=}"; shift ;;
|
||||
--local) LOCAL_INSTALL=1; shift ;;
|
||||
--prefix) SKILL_PREFIX=1; SKILL_PREFIX_FLAG=1; shift ;;
|
||||
|
|
@ -101,7 +101,7 @@ while [ $# -gt 0 ]; do
|
|||
done
|
||||
|
||||
case "$HOST" in
|
||||
claude|codex|kiro|factory|opencode|auto) ;;
|
||||
claude|codex|kiro|factory|opencode|cursor|slate|auto) ;;
|
||||
openclaw)
|
||||
echo ""
|
||||
echo "OpenClaw integration uses a different model — OpenClaw spawns Claude Code"
|
||||
|
|
@ -136,7 +136,7 @@ case "$HOST" in
|
|||
echo "GBrain setup and brain skills ship from the GBrain repo."
|
||||
echo ""
|
||||
exit 0 ;;
|
||||
*) echo "Unknown --host value: $HOST (expected claude, codex, kiro, factory, opencode, openclaw, hermes, gbrain, or auto)" >&2; exit 1 ;;
|
||||
*) echo "Unknown --host value: $HOST (expected claude, codex, kiro, factory, opencode, cursor, slate, openclaw, hermes, gbrain, or auto)" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
# ─── Resolve skill prefix preference ─────────────────────────
|
||||
|
|
|
|||
|
|
@ -269,48 +269,15 @@ Original body content here.
|
|||
});
|
||||
});
|
||||
|
||||
describe('proactive-suggestions.json determinism (regression for v1.45.0.0 CI freshness fail)', () => {
|
||||
test('committed JSON keys are alphabetically sorted', () => {
|
||||
// Reads the actual committed file at scripts/proactive-suggestions.json
|
||||
// and verifies sort order. Catches regressions to non-sorted output.
|
||||
describe('proactive-suggestions.json stays retired', () => {
|
||||
test('the generator no longer emits scripts/proactive-suggestions.json', () => {
|
||||
// The aggregated routing registry was removed (no consumer ever read it).
|
||||
// If someone re-adds the emitter, this pins the decision to delete it —
|
||||
// reintroduce only with an actual consumer, and restore the determinism
|
||||
// tests (sorted keys, root keyed as "gstack", no timestamp fields) that
|
||||
// lived here before.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const json = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, '..', 'scripts', 'proactive-suggestions.json'), 'utf-8'),
|
||||
);
|
||||
const keys = Object.keys(json.skills);
|
||||
const sorted = [...keys].sort();
|
||||
expect(keys).toEqual(sorted);
|
||||
});
|
||||
|
||||
test('root skill is keyed as "gstack" (not the checkout directory name)', () => {
|
||||
// Catches the bug where the root SKILL.md.tmpl's catalog parts get
|
||||
// registered under the directory basename ("seville-v3" in a Conductor
|
||||
// worktree, "gstack" on CI).
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const json = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, '..', 'scripts', 'proactive-suggestions.json'), 'utf-8'),
|
||||
);
|
||||
expect(json.skills).toHaveProperty('gstack');
|
||||
// The directory the test runs in must NOT appear as a key.
|
||||
const repoDir = path.basename(path.resolve(__dirname, '..'));
|
||||
if (repoDir !== 'gstack') {
|
||||
expect(json.skills).not.toHaveProperty(repoDir);
|
||||
}
|
||||
});
|
||||
|
||||
test('schema + catalog_mode + note fields are stable', () => {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const json = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, '..', 'scripts', 'proactive-suggestions.json'), 'utf-8'),
|
||||
);
|
||||
expect(json).toHaveProperty('$schema');
|
||||
expect(json.catalog_mode).toBe('trim');
|
||||
expect(typeof json.note).toBe('string');
|
||||
// No timestamp field — those cause flapping CI freshness checks.
|
||||
expect(json).not.toHaveProperty('generated_at');
|
||||
expect(json).not.toHaveProperty('timestamp');
|
||||
expect(fs.existsSync(path.join(__dirname, '..', 'scripts', 'proactive-suggestions.json'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
* timestamp, a random seed, or any other non-deterministic field into a
|
||||
* generated artifact.
|
||||
*
|
||||
* v1.45.0.0 shipped with a `generated_at` ISO timestamp in
|
||||
* scripts/proactive-suggestions.json that updated every run. CI freshness
|
||||
* checks failed because the committed file's timestamp never matched the
|
||||
* latest gen. Fixed in 43e18af4 — this test pins the contract going forward.
|
||||
* v1.45.0.0 shipped a generated artifact with a `generated_at` ISO timestamp
|
||||
* that updated every run. CI freshness checks failed because the committed
|
||||
* file's timestamp never matched the latest gen. Fixed in 43e18af4 — this
|
||||
* test pins the contract going forward.
|
||||
*
|
||||
* The test pays a small cost (~2 gen-skill-docs invocations, ~3s total) but
|
||||
* catches a class of bugs that's invisible until CI fails.
|
||||
|
|
@ -25,7 +25,6 @@ const REPO_ROOT = path.resolve(import.meta.dir, '..');
|
|||
|
||||
/** Files that gen-skill-docs writes and that must be byte-stable across runs. */
|
||||
const STABLE_OUTPUTS = [
|
||||
'scripts/proactive-suggestions.json',
|
||||
'SKILL.md',
|
||||
'ship/SKILL.md',
|
||||
'plan-ceo-review/SKILL.md',
|
||||
|
|
@ -40,7 +39,6 @@ const STABLE_OUTPUTS = [
|
|||
* non-determinism without paying the cost of snapshotting hundreds of files.
|
||||
*/
|
||||
const STABLE_HOST_ALL_OUTPUTS = [
|
||||
'scripts/proactive-suggestions.json',
|
||||
'SKILL.md',
|
||||
'ship/SKILL.md',
|
||||
'.agents/skills/gstack-ship/SKILL.md',
|
||||
|
|
@ -151,8 +149,8 @@ describe('gen-skill-docs idempotency', () => {
|
|||
throw new Error(
|
||||
`${flapping.length} file(s) changed between two consecutive --host all gen runs:\n` +
|
||||
flapping.map(f => ` - ${f}`).join('\n') +
|
||||
`\nLikely cause: a non-deterministic field leaked into a non-Claude host adapter ` +
|
||||
`(scripts/host-adapters/*.ts). CI freshness checks for that host will flap.`,
|
||||
`\nLikely cause: a non-deterministic field leaked into a non-Claude host's ` +
|
||||
`config or resolver output. CI freshness checks for that host will flap.`,
|
||||
);
|
||||
}
|
||||
}, 300_000); // ~5 min budget for two host-all runs
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ describe('gen-skill-docs --out-dir (B2 render isolation)', () => {
|
|||
}
|
||||
});
|
||||
|
||||
test('global extras (proactive-suggestions.json) are NOT written in out-dir mode', () => {
|
||||
test('retired global extras (proactive-suggestions.json) are not written anywhere', () => {
|
||||
const outDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-out-'));
|
||||
try {
|
||||
const res = spawnSync(
|
||||
|
|
@ -75,8 +75,10 @@ describe('gen-skill-docs --out-dir (B2 render isolation)', () => {
|
|||
{ cwd: ROOT, encoding: 'utf-8', timeout: 120_000 },
|
||||
);
|
||||
expect(res.status).toBe(0);
|
||||
// proactive-suggestions.json lives at a repo path; out-dir mode must skip it.
|
||||
// The proactive-suggestions registry was removed (never had a consumer).
|
||||
// A gen run must not resurrect it in the out-dir or at the repo path.
|
||||
expect(fs.existsSync(path.join(outDir, 'scripts', 'proactive-suggestions.json'))).toBe(false);
|
||||
expect(fs.existsSync(path.join(ROOT, 'scripts', 'proactive-suggestions.json'))).toBe(false);
|
||||
} finally {
|
||||
fs.rmSync(outDir, { recursive: true, force: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1733,13 +1733,6 @@ describe('Codex skill validation', () => {
|
|||
expect(fs.existsSync(path.join(AGENTS_DIR, 'gstack-codex', 'SKILL.md'))).toBe(false);
|
||||
});
|
||||
|
||||
test('/claude skill is external-host-only — no Claude-host variant', () => {
|
||||
// Claude host should not get an outside-voice skill that shells into Claude.
|
||||
expect(fs.existsSync(path.join(ROOT, 'claude', 'SKILL.md'))).toBe(false);
|
||||
// Codex/external hosts should get the generated wrapper.
|
||||
expect(fs.existsSync(path.join(AGENTS_DIR, 'gstack-claude', 'SKILL.md'))).toBe(true);
|
||||
});
|
||||
|
||||
test('Codex skill names follow gstack-{name} convention', () => {
|
||||
const codexDirs = fs.readdirSync(AGENTS_DIR);
|
||||
for (const dir of codexDirs) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue