From e0d0467a00e2b37864ea1acc4418eb48606bb471 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Wed, 15 Apr 2026 00:17:51 +0530 Subject: [PATCH] refactor: achieve 100% platform-independent verification - Eliminate final bash/cmd hook legacy files. - Add PowerShell installation alternatives to install.md. - Refactor skills indexer to an exportable function. - Update test suite to use direct imports, removing shell execution dependency. - All 29/29 tests passing cross-platform. --- hooks/run-hook.cmd | 4 --- hooks/session-start | 42 ---------------------------- install.md | 15 ++++++++-- scripts/generate-skills-index.ts | 20 ++++++------- skills/INDEX.md | 8 ++++-- skills/using-hyperstack/SKILL.md | 2 +- tests/skills-index-behaviour.test.ts | 4 +-- 7 files changed, 30 insertions(+), 65 deletions(-) delete mode 100644 hooks/run-hook.cmd delete mode 100755 hooks/session-start diff --git a/hooks/run-hook.cmd b/hooks/run-hook.cmd deleted file mode 100644 index cc7daa3..0000000 --- a/hooks/run-hook.cmd +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -:: Cross-platform hook dispatcher for Hyperstack -:: Usage: run-hook.cmd -bash "%~dp0%~1" %2 %3 %4 %5 diff --git a/hooks/session-start b/hooks/session-start deleted file mode 100755 index 378f033..0000000 --- a/hooks/session-start +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -# SessionStart hook for Hyperstack plugin -# Injects using-hyperstack skill as EXTREMELY_IMPORTANT context at session start - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" - -if ! skill_content=$(cat "${PLUGIN_ROOT}/skills/using-hyperstack/SKILL.md" 2>&1); then - error_msg="Hyperstack session-start hook failed: could not read ${PLUGIN_ROOT}/skills/using-hyperstack/SKILL.md" - printf '{"error": "%s"}\n' "$error_msg" >&2 - exit 1 -fi - -# Escape string for JSON embedding -escape_for_json() { - local s="$1" - s="${s//\\/\\\\}" - s="${s//\"/\\\"}" - s="${s//$'\n'/\\n}" - s="${s//$'\r'/\\r}" - s="${s//$'\t'/\\t}" - printf '%s' "$s" -} - -skill_escaped=$(escape_for_json "$skill_content") -session_context="\nYou have Hyperstack.\n\n**Below is the full content of your 'hyperstack:using-hyperstack' skill - your introduction to using Hyperstack. For all other skills, use the 'Skill' tool:**\n\n${skill_escaped}\n" - -# Platform detection - emit the correct JSON field for the current harness -if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then - # Cursor - printf '{\n "additional_context": "%s"\n}\n' "$session_context" -elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -z "${COPILOT_CLI:-}" ]; then - # Claude Code - printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context" -else - # Copilot CLI or unknown platform - SDK standard format - printf '{\n "additionalContext": "%s"\n}\n' "$session_context" -fi - -exit 0 diff --git a/install.md b/install.md index d72fc57..8cb7262 100644 --- a/install.md +++ b/install.md @@ -51,14 +51,21 @@ If the directory already exists (upgrade scenario), pull the latest instead of c **Qwen Code note:** Qwen Code does not have a skill plugin system or SessionStart hook. Skills are cloned to `~/.qwen/skills/hyperstack/` and available on disk for the agent to reference manually (via `read_file` or the agent's own knowledge of the repo structure). The 21 skills are not auto-loaded at session start. -To handle both cases automatically, use this one-liner (clone if missing, pull if present): +To handle both cases automatically, use the one-liner for your platform: +**macOS / Linux (Shell):** ```bash EXT_DIR="$HOME/.gemini/extensions/hyperstack" && \ ([ -d "$EXT_DIR" ] && git -C "$EXT_DIR" pull || git clone https://github.com/orkait/hyperstack.git "$EXT_DIR") ``` -Replace `~/.claude/skills` with the correct path for the current environment (see table above). For example, on Qwen Code use `~/.qwen/skills/hyperstack`. +**Windows (PowerShell):** +```powershell +$EXT_DIR = Join-Path $HOME ".gemini/extensions/hyperstack" +if (Test-Path $EXT_DIR) { git -C $EXT_DIR pull } else { git clone https://github.com/orkait/hyperstack.git $EXT_DIR } +``` + +Replace `~/.claude/skills` or the path variables with the correct path for the current environment (see table above). The repository includes a `hooks/session-start.mjs` entrypoint that injects the generated Hyperstack bootstrap into every session automatically. This is how the @@ -96,8 +103,12 @@ Or run the hard check manually: ```bash # 1. Remove ANY container based on the hyperstack image +# macOS/Linux: docker ps -aq --filter "ancestor=ghcr.io/orkait/hyperstack:main" | xargs -r docker rm -f +# Windows (PowerShell): +docker ps -aq --filter "ancestor=ghcr.io/orkait/hyperstack:main" | ForEach-Object { docker rm -f $_ } + # 2. Run the fresh singleton container docker run -d --name hyperstack-mcp --restart unless-stopped \ --memory=512m --cpus=1 \ diff --git a/scripts/generate-skills-index.ts b/scripts/generate-skills-index.ts index ef2d60b..a09d2b0 100644 --- a/scripts/generate-skills-index.ts +++ b/scripts/generate-skills-index.ts @@ -3,17 +3,12 @@ import * as path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const REPO_ROOT = path.resolve(__dirname, ".."); -const SKILLS_DIR = path.join(REPO_ROOT, "skills"); -const INDEX_FILE = path.join(SKILLS_DIR, "INDEX.md"); - -interface SkillInfo { - name: string; - category: string; - description: string; -} -function generateIndex() { +export function generateIndex() { + const REPO_ROOT = path.resolve(__dirname, ".."); + const SKILLS_DIR = path.join(REPO_ROOT, "skills"); + const INDEX_FILE = path.join(SKILLS_DIR, "INDEX.md"); + console.log("Generating skills index (cross-platform)..."); const skills: SkillInfo[] = []; @@ -94,4 +89,7 @@ These skills need a \`category:\` added to their frontmatter. console.log(`Wrote ${INDEX_FILE}`); } -generateIndex(); +// Only run if called directly +if (import.meta.url === `file://${process.argv[1]}`) { + generateIndex(); +} diff --git a/skills/INDEX.md b/skills/INDEX.md index bfb8c83..85bef18 100644 --- a/skills/INDEX.md +++ b/skills/INDEX.md @@ -1,7 +1,7 @@ # Hyperstack Skills Index Auto-generated from each skill's frontmatter `category` field. -Regenerate with: `bash scripts/generate-skills-index.sh` +Regenerate with: `bun scripts/generate-skills-index.ts` or `npm run skills:index` Categories: - **core** - workflow, discipline, and gates used on every task @@ -33,10 +33,10 @@ Categories: | Skill | Description | |---|---| | `behaviour-analysis` | Systematic UI/UX behaviour analysis for interactive applications. Audits every user action, state transition, view mode, | -| `designer` | Evidence-based design decision engine. Intention gate that produces non-slop | | `design-patterns-skill` | Apply core programming principles and design patterns from Clean Code, The Pragmatic Programmer, Code Complete, Refactor | +| `designer` | | | `readme-writer` | Writes or rewrites project README files using repository evidence instead of generic filler. Use when creating a new REA | -| `security-review` | Security code review for vulnerabilities. Use when asked to security review, find vulnerabilities, check for security is | +| `security-review` | Security code review for vulnerabilities. Use when asked to "security review", "find vulnerabilities", "check for securi | | `shadcn-expert` | Advanced shadcn/ui architect specializing in Base UI, Tailwind v4, data-slot patterns, and component composition. Use wh | ## Meta (skills about skills) @@ -45,3 +45,5 @@ Categories: |---|---| | `testing-skills` | Use when creating or editing Hyperstack skills, before shipping them, to verify they actually work under pressure and re | | `using-hyperstack` | Bootstrap - establishes Hyperstack MCP tools and skills before any technical work. Auto-loaded at session start via Sess | + + diff --git a/skills/using-hyperstack/SKILL.md b/skills/using-hyperstack/SKILL.md index e768363..d5b7463 100644 --- a/skills/using-hyperstack/SKILL.md +++ b/skills/using-hyperstack/SKILL.md @@ -107,7 +107,7 @@ If MCP tools fail or are unavailable: Use the `Skill` tool to load these before the relevant task type. -**Full skill index:** See `skills/INDEX.md` - all skills grouped by category (core / domain / meta). Regenerate with `bash scripts/generate-skills-index.sh` after adding or editing any skill. +**Full skill index:** See `skills/INDEX.md` - all skills grouped by category (core / domain / meta). Regenerate with `npm run skills:index` after adding or editing any skill. ### Announcement Iron Law diff --git a/tests/skills-index-behaviour.test.ts b/tests/skills-index-behaviour.test.ts index 986633a..3c4fdf7 100644 --- a/tests/skills-index-behaviour.test.ts +++ b/tests/skills-index-behaviour.test.ts @@ -1,7 +1,7 @@ import { test, expect } from "bun:test"; -import { execSync } from "node:child_process"; import { readFileSync, readdirSync, existsSync } from "node:fs"; import { resolve, join } from "node:path"; +import { generateIndex } from "../scripts/generate-skills-index.ts"; const SKILLS_DIR = resolve("skills"); const INDEX_PATH = resolve("skills/INDEX.md"); @@ -27,7 +27,7 @@ function getSkillCategory(skillName: string): string | null { test("skills/INDEX.md stays in sync with actual skill directories", () => { const currentIndex = normalize(readFileSync(INDEX_PATH, "utf8")); - execSync("bash scripts/generate-skills-index.sh", { stdio: "pipe" }); + generateIndex(); const regenerated = normalize(readFileSync(INDEX_PATH, "utf8")); expect(currentIndex).toBe(regenerated);