opengstack 0.13.4 → 0.13.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,80 +1,59 @@
1
1
  # OpenGStack
2
2
 
3
- Open source engineering workflow skills for AI coding assistants.
4
-
5
- Forked from [garrytan/gstack](https://github.com/garrytan/gstack) with telemetry and YC references removed.
6
-
7
- ## What is this?
8
-
9
- Skills that give AI agents structured roles for software development. Each skill is a specialist: CEO reviewer, eng manager, designer, QA lead, release engineer, debugger, and more.
10
-
11
- ## Available Skills
12
-
13
- | Skill | What it does |
14
- |-------|--------------|
15
- | `/office-hours` | Brainstorm your product idea before you write code. |
16
- | `/plan-ceo-review` | CEO-level review: find the 10-star product in the request. |
17
- | `/plan-eng-review` | Lock architecture, data flow, edge cases, and tests. |
18
- | `/plan-design-review` | Rate each design dimension 0-10, explain what a 10 looks like. |
19
- | `/design-consultation` | Build a complete design system from scratch. |
20
- | `/design-review` | Design audit + fix loop with atomic commits. |
21
- | `/review` | Pre-landing PR review. Finds bugs that pass CI but break in prod. |
22
- | `/investigate` | Systematic root-cause debugging. |
23
- | `/qa` | Open a real browser, find bugs, fix them, re-verify. |
24
- | `/qa-only` | Same as /qa but report only — no code changes. |
25
- | `/ship` | Run tests, review, push, open PR. One command. |
26
- | `/land-and-deploy` | Merge PR, deploy, verify health. |
27
- | `/document-release` | Update all docs to match what you just shipped. |
28
- | `/retro` | Weekly retro with per-person breakdowns and shipping streaks. |
29
- | `/browse` | Headless browser — real Chromium, real clicks, ~100ms/command. |
30
- | `/setup-browser-cookies` | Import cookies from your real browser for authenticated testing. |
31
- | `/careful` | Warn before destructive commands (rm -rf, DROP TABLE, force-push). |
32
- | `/freeze` | Lock edits to one directory. Hard block, not just a warning. |
33
- | `/guard` | Activate both careful + freeze at once. |
34
- | `/unfreeze` | Remove directory edit restrictions. |
35
- | `/autoplan` | Run all reviews sequentially with auto-decisions. |
36
- | `/codex` | OpenAI Codex CLI wrapper for code review. |
37
- | `/canary` | Post-deploy monitoring. |
38
- | `/benchmark` | Performance regression detection. |
39
- | `/cso` | Security audit. |
3
+ AI engineering workflow skills for Claude/opencode. No telemetry. No tracking.
40
4
 
41
5
  ## Installation
42
6
 
43
- ### For Claude Code / OpenCode
44
-
45
7
  ```bash
46
- # Clone to your skills directory
47
- git clone https://github.com/Ambisphaeric/opengstack.git ~/.claude/skills/opengstack
8
+ npm install -g opengstack
48
9
  ```
49
10
 
50
- ### Browse Setup (optional but recommended)
11
+ Skills auto-install to `~/.claude/skills/` via symlink.
51
12
 
52
- ```bash
53
- cd ~/.claude/skills/opengstack/browse
54
- ./setup
55
- ```
56
-
57
- Requires `bun`: `curl -fsSL https://bun.sh/install | bash`
58
-
59
- ## No Telemetry
60
-
61
- This fork removes all telemetry, analytics, and tracking from the original gstack. Your usage data stays on your machine.
62
-
63
- ## Syncing from Upstream
64
-
65
- This repo auto-syncs daily from `garrytan/gstack` via GitHub Actions. Filters are applied to remove telemetry and YC references.
66
-
67
- To sync manually:
13
+ ## CLI
68
14
 
69
15
  ```bash
70
- git remote add upstream https://github.com/garrytan/gstack.git
71
- git fetch upstream main
72
- git merge upstream/main
73
- ./scripts/filter_skills.py .
74
- ./scripts/cleanup.py .
75
- git commit -m "chore: sync from upstream"
16
+ opengstack --help # Show all commands
17
+ opengstack --list # List available skills
18
+ opengstack --install # Re-install skills (if needed)
76
19
  ```
77
20
 
21
+ ## Usage
22
+
23
+ In opencode/Claude, type any skill name with `/`:
24
+
25
+ | Command | What it does |
26
+ |---------|--------------|
27
+ | `/office-hours` | Brainstorm before building |
28
+ | `/plan-ceo-review` | CEO-level strategic review |
29
+ | `/plan-eng-review` | Lock architecture & edge cases |
30
+ | `/plan-design-review` | Rate design decisions 0-10 |
31
+ | `/design-consultation` | Build a design system from scratch |
32
+ | `/design-review` | Design audit + fix loop |
33
+ | `/review` | Pre-landing PR review |
34
+ | `/investigate` | Root-cause debugging |
35
+ | `/qa` | Open browser, find bugs, fix, verify |
36
+ | `/qa-only` | QA report only — no code changes |
37
+ | `/ship` | Run tests, review, push, open PR |
38
+ | `/land-and-deploy` | Merge PR, deploy, verify health |
39
+ | `/document-release` | Update docs post-ship |
40
+ | `/retro` | Weekly engineering retrospective |
41
+ | `/browse` | Headless browser (real Chromium) |
42
+ | `/setup-browser-cookies` | Import cookies for auth testing |
43
+ | `/careful` | Warn before destructive ops |
44
+ | `/freeze` | Lock edits to one directory |
45
+ | `/guard` | Activate careful + freeze |
46
+ | `/unfreeze` | Remove directory restrictions |
47
+ | `/autoplan` | Run all reviews auto-decisioned |
48
+ | `/codex` | OpenAI Codex CLI wrapper |
49
+ | `/canary` | Post-deploy monitoring |
50
+ | `/benchmark` | Performance regression detection |
51
+ | `/cso` | Security audit |
52
+
53
+ ## Why OpenGStack?
54
+
55
+ Forked from [garrytan/gstack](https://github.com/garrytan/gstack) with telemetry and vendor references removed. Your usage stays local.
56
+
78
57
  ## License
79
58
 
80
59
  MIT
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * OpenGStack CLI - Run skills directly from command line
5
+ * Usage: opengstack <skill-name> [args...]
6
+ * Example: opengstack ship
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ const PKG_DIR = path.dirname(__dirname);
13
+ const SKILLS_DIR = path.join(process.env.HOME || process.env.USERPROFILE, '.claude', 'skills');
14
+
15
+ // Map of skill names to their directories
16
+ const skillMap = {
17
+ 'ship': 'ship',
18
+ 'qa': 'qa',
19
+ 'qa-only': 'qa-only',
20
+ 'review': 'review',
21
+ 'investigate': 'investigate',
22
+ 'design-review': 'design-review',
23
+ 'plan-ceo-review': 'plan-ceo-review',
24
+ 'plan-eng-review': 'plan-eng-review',
25
+ 'plan-design-review': 'plan-design-review',
26
+ 'office-hours': 'office-hours',
27
+ 'design-consultation': 'design-consultation',
28
+ 'design-shotgun': 'design-shotgun',
29
+ 'document-release': 'document-release',
30
+ 'retro': 'retro',
31
+ 'browse': 'browse',
32
+ 'setup-browser-cookies': 'setup-browser-cookies',
33
+ 'setup-deploy': 'setup-deploy',
34
+ 'careful': 'careful',
35
+ 'freeze': 'freeze',
36
+ 'guard': 'guard',
37
+ 'unfreeze': 'unfreeze',
38
+ 'autoplan': 'autoplan',
39
+ 'codex': 'codex',
40
+ 'canary': 'canary',
41
+ 'benchmark': 'benchmark',
42
+ 'cso': 'cso',
43
+ 'connect-chrome': 'connect-chrome',
44
+ 'land-and-deploy': 'land-and-deploy',
45
+ 'gstack-upgrade': 'gstack-upgrade'
46
+ };
47
+
48
+ function showHelp() {
49
+ console.log(`
50
+ OpenGStack - AI Engineering Workflow Skills
51
+
52
+ Usage: opengstack <command> [options]
53
+
54
+ Commands:
55
+ ship Ship workflow: test, review, push, PR
56
+ qa Open browser, find bugs, fix, verify
57
+ qa-only QA report only — no code changes
58
+ review Pre-landing PR review
59
+ investigate Root-cause debugging
60
+ design-review Design audit + fix loop
61
+ plan-ceo-review CEO-level strategic review
62
+ plan-eng-review Lock architecture & edge cases
63
+ plan-design-review Rate design decisions 0-10
64
+ office-hours Brainstorm before building
65
+ design-consultation Build a design system from scratch
66
+ design-shotgun Generate multiple AI design variants
67
+ document-release Update docs post-ship
68
+ retro Weekly engineering retrospective
69
+ browse Headless browser (real Chromium)
70
+ setup-browser-cookies Import cookies for auth testing
71
+ setup-deploy Configure deployment settings
72
+ careful Warn before destructive ops
73
+ freeze Lock edits to one directory
74
+ guard Activate careful + freeze
75
+ unfreeze Remove directory restrictions
76
+ autoplan Run all reviews auto-decisioned
77
+ codex OpenAI Codex CLI wrapper
78
+ canary Post-deploy monitoring
79
+ benchmark Performance regression detection
80
+ cso Security audit
81
+ connect-chrome Launch Chrome with Side Panel
82
+ land-and-deploy Merge PR, deploy, verify health
83
+ gstack-upgrade Upgrade gstack to latest version
84
+
85
+ Options:
86
+ -h, --help Show this help message
87
+ -l, --list List all available skills
88
+ -i, --install Install skills to ~/.claude/skills/
89
+
90
+ In opencode/Claude, use /slash commands:
91
+ /ship, /qa, /review, etc.
92
+ `);
93
+ }
94
+
95
+ function listSkills() {
96
+ console.log('\nAvailable skills:\n');
97
+ Object.entries(skillMap).forEach(([cmd, dir]) => {
98
+ const skillPath = path.join(SKILLS_DIR, dir, 'SKILL.md');
99
+ let description = '';
100
+ if (fs.existsSync(skillPath)) {
101
+ const content = fs.readFileSync(skillPath, 'utf8');
102
+ const match = content.match(/description:\s*\|?\s*([^\n]+)/);
103
+ if (match) {
104
+ description = match[1].trim().substring(0, 60);
105
+ if (description.length === 60) description += '...';
106
+ }
107
+ }
108
+ console.log(` ${cmd.padEnd(20)} ${description}`);
109
+ });
110
+ console.log('');
111
+ }
112
+
113
+ function installSkills() {
114
+ console.log('Installing skills...');
115
+ require('../scripts/install-skills.js');
116
+ }
117
+
118
+ function main() {
119
+ const args = process.argv.slice(2);
120
+ const command = args[0];
121
+
122
+ if (!command || command === '-h' || command === '--help') {
123
+ showHelp();
124
+ process.exit(0);
125
+ }
126
+
127
+ if (command === '-l' || command === '--list') {
128
+ listSkills();
129
+ process.exit(0);
130
+ }
131
+
132
+ if (command === '-i' || command === '--install') {
133
+ installSkills();
134
+ process.exit(0);
135
+ }
136
+
137
+ const skillDir = skillMap[command];
138
+ if (!skillDir) {
139
+ console.error(`Unknown command: ${command}`);
140
+ console.error('Run "opengstack --help" for available commands');
141
+ process.exit(1);
142
+ }
143
+
144
+ // Check if skill is installed
145
+ const skillPath = path.join(SKILLS_DIR, skillDir, 'SKILL.md');
146
+ if (!fs.existsSync(skillPath)) {
147
+ console.error(`Skill "${command}" not installed.`);
148
+ console.error('Run: opengstack --install');
149
+ process.exit(1);
150
+ }
151
+
152
+ // Print instructions for using the skill
153
+ console.log(`\n🎯 Skill: ${command}`);
154
+ console.log(`📍 Location: ${skillPath}`);
155
+ console.log(`\nTo use this skill in opencode/Claude, type:`);
156
+ console.log(` /${command}\n`);
157
+ console.log('The skill instructions will be loaded automatically.\n');
158
+ }
159
+
160
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opengstack",
3
- "version": "0.13.4",
3
+ "version": "0.13.5",
4
4
  "private": false,
5
5
  "description": "AI Engineering Workflow - SKILL.md files that give AI agents structured roles for software development. Forked from gstack but scrubbed clean of all the YC/Garry Tan cruft and telemetry.",
6
6
  "keywords": [
@@ -24,6 +24,9 @@
24
24
  "author": "Ambisphaeric",
25
25
  "type": "commonjs",
26
26
  "main": "SKILL.md",
27
+ "bin": {
28
+ "opengstack": "./bin/opengstack.js"
29
+ },
27
30
  "directories": {
28
31
  "doc": "docs"
29
32
  },
@@ -35,9 +38,11 @@
35
38
  "**/SKILL.md",
36
39
  "**/SKILL.md.tmpl",
37
40
  "scripts/",
38
- "docs/"
41
+ "docs/",
42
+ "bin/"
39
43
  ],
40
44
  "scripts": {
45
+ "postinstall": "node scripts/install-skills.js",
41
46
  "test": "echo \"Error: no test specified\" && exit 1"
42
47
  }
43
48
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- opengstack filter script
4
- Removes telemetry, YC references, garrytan slop from gstack skills
3
+ opengstack filter script - NUKE MODE
4
+ Removes ALL telemetry, YC references, Garry Tan content, and gstack branding
5
5
  """
6
6
 
7
7
  import re
@@ -10,95 +10,119 @@ from pathlib import Path
10
10
 
11
11
 
12
12
  def filter_skill_content(content: str) -> str:
13
- """Apply all filters to skill content"""
13
+ """NUKE all gstack slop from skills"""
14
14
 
15
- # Remove telemetry preamble block (from _UPD to closing brace)
16
- preamble_pattern = r"```bash\n_UPD=.*?(?=\n```|\n\n[A-Z#])"
17
- content = re.sub(preamble_pattern, "", content, flags=re.DOTALL)
15
+ # STEP 1: Remove entire telemetry preamble blocks
16
+ # Match from ```bash to next ```, containing telemetry variables
17
+ content = re.sub(r"```bash\s*\n_UPD=.*?```\s*\n", "", content, flags=re.DOTALL)
18
18
 
19
- # Remove LAKE_INTRO / Completeness Principle section
20
- lake_pattern = r"If `LAKE_INTRO` is `no`:.*?`touch ~\/\.gstack\/\.completeness-intro-seen`\n```\n.*?```\n"
21
- content = re.sub(lake_pattern, "", content, flags=re.DOTALL)
19
+ # Remove any remaining bash blocks with .gstack/ references
20
+ content = re.sub(
21
+ r"```bash\s*\n.*?\.gstack/.*?(?=```)", "", content, flags=re.DOTALL
22
+ )
22
23
 
23
- # Remove telemetry prompts section
24
- tel_pattern = (
25
- r"If `TEL_PROMPTED` is `no` AND.*?(?=If `PROACTIVE_PROMPTED`|## Voice)"
24
+ # STEP 2: Remove entire sections about telemetry/config
25
+ content = re.sub(
26
+ r"If `TEL_PROMPTED` is.*?touch ~/.gstack/\.telemetry-prompted.*?```\s*\n?",
27
+ "",
28
+ content,
29
+ flags=re.DOTALL,
26
30
  )
27
- content = re.sub(tel_pattern, "", content, flags=re.DOTALL)
28
31
 
29
- # Remove PROACTIVE_PROMPTED section
30
- proactive_pattern = r"If `PROACTIVE_PROMPTED` is `no` AND.*?`touch ~\/\.gstack\/\.proactive-prompted`\n```\n"
31
- content = re.sub(proactive_pattern, "", content, flags=re.DOTALL)
32
+ content = re.sub(
33
+ r"If `PROACTIVE_PROMPTED` is.*?touch ~/.gstack/\.proactive-prompted.*?```\s*\n?",
34
+ "",
35
+ content,
36
+ flags=re.DOTALL,
37
+ )
32
38
 
33
- # Remove contributor mode block
34
- contrib_pattern = r"## Contributor Mode.*?(?=## Voice|## Completion)"
35
- content = re.sub(contrib_pattern, "", content, flags=re.DOTALL)
39
+ content = re.sub(
40
+ r"If `LAKE_INTRO` is.*?touch ~/.gstack/\.completeness-intro-seen.*?```\s*\n?",
41
+ "",
42
+ content,
43
+ flags=re.DOTALL,
44
+ )
36
45
 
37
- # Remove telemetry trailer block
38
- tel_trailer_pattern = (
39
- r"## Telemetry \(run last\).*?```bash\n.*?gstack-telemetry-log.*?```\n"
46
+ # STEP 3: Remove ## Contributor Mode section
47
+ content = re.sub(
48
+ r"## Contributor Mode.*?## (Voice|Telemetry|Completion)",
49
+ r"## \1",
50
+ content,
51
+ flags=re.DOTALL,
40
52
  )
41
- content = re.sub(tel_trailer_pattern, "", content, flags=re.DOTALL)
42
53
 
43
- # Remove update check references
44
- content = re.sub(r"UPGRADE_AVAILABLE.*?(?=\n|$)", "", content)
45
- content = re.sub(r"JUST_UPGRADED.*?(?=\n|$)", "", content)
46
- content = re.sub(r"gstack-update-check", "echo", content)
54
+ # STEP 4: Remove ## Telemetry section entirely
55
+ content = re.sub(r"## Telemetry.*?```.*?```\s*\n", "", content, flags=re.DOTALL)
56
+
57
+ # STEP 5: NUKE all gstack binary references
58
+ content = re.sub(r"`?~/.claude/skills/gstack/bin/[^\s`]+`?", "", content)
47
59
 
48
- # Remove gstack-config references (telemetry/proactive settings)
60
+ # STEP 6: Replace gstack paths with opengstack
49
61
  content = re.sub(
50
62
  r"~/.claude/skills/gstack/", "~/.claude/skills/opengstack/", content
51
63
  )
52
64
 
53
- # Remove YC slop - personal notes from Garry Tan
54
- garry_note_pattern = (
55
- r"> A personal note from me, Garry Tan, the creator of GStack:.*?(?=\n\n|\n>)"
56
- )
57
- content = re.sub(garry_note_pattern, "", content, flags=re.DOTALL)
58
-
59
- # Remove YC apply links with ref tracking
60
- content = re.sub(r"ycombinator\.com/apply\?ref=gstack.*", "", content)
65
+ # STEP 7: Remove all .gstack/ directory references
66
+ content = re.sub(r"~?/?\.gstack/[^\s`\n]*", "", content)
61
67
 
62
- # Remove YC partner energy references
63
- content = re.sub(r"YC partner energy for strategy.*", "", content)
68
+ # STEP 8: Remove mkdir/touch commands for .gstack
69
+ content = re.sub(r"mkdir -p[^\n]*\.gstack[^\n]*\n?", "", content)
70
+ content = re.sub(r"touch[^\n]*\.gstack[^\n]*\n?", "", content)
64
71
 
65
- # Remove "exactly the kind of builders Garry respects" type phrases
66
- founder_phrase = r"people with that kind of taste and drive are exactly the kind of builders.*?consider applying to YC.*"
67
- content = re.sub(founder_phrase, "", content, flags=re.DOTALL)
68
-
69
- # Remove garryslist references
70
- content = re.sub(r"https?://garryslist\.org[^\s]*", "", content)
71
- content = re.sub(r"Boil the (Lake|Ocean).*", "", content)
72
+ # STEP 9: NUKE Garry Tan completely
73
+ content = re.sub(
74
+ r"> A personal note from me, Garry Tan.*?(?=\n\n|## )",
75
+ "",
76
+ content,
77
+ flags=re.DOTALL,
78
+ )
79
+ content = re.sub(r"shaped by Garry Tan\'s[^.]*\.", "", content)
80
+ content = re.sub(r"Garry Tan", "", content)
81
+ content = re.sub(r"YC partner energy for strategy[^.]*\.", "", content)
72
82
 
73
- # Remove YC from office-hours description
83
+ # STEP 10: NUKE YC references
74
84
  content = re.sub(r"YC Office Hours", "Office Hours", content)
85
+ content = re.sub(r"ycombinator\.com/apply\?ref=[^\s]*", "", content)
86
+ content = re.sub(r"consider applying to YC[^.]*\.", "", content)
75
87
 
76
- # Remove Garry Tan references
77
- content = re.sub(r"shaped by Garry Tan's.*", "", content)
78
- content = re.sub(r"Garry Tan", "OpenGStack", content)
79
- content = re.sub(r"Gar[r]?y.*Tan", "", content)
88
+ # STEP 11: NUKE garryslist
89
+ content = re.sub(r"https?://garryslist\.org[^\s]*", "", content)
90
+ content = re.sub(r"Boil the (Lake|Ocean)[^.]*\.?", "", content)
80
91
 
81
- # Remove GStack branding where it's the product name
92
+ # STEP 12: Replace GStack with OpenGStack (product name)
82
93
  content = re.sub(r"\bGStack\b", "OpenGStack", content)
83
94
 
84
- # Remove "ref=gstack" from any URLs
95
+ # STEP 13: Remove standalone gstack references in prose
96
+ # But keep it in contexts like "skill system" or "framework"
97
+ content = re.sub(r"\bgstack skills?\b", "skills", content, flags=re.IGNORECASE)
98
+ content = re.sub(
99
+ r"\bgstack[- ]?telemetry\b", "telemetry", content, flags=re.IGNORECASE
100
+ )
101
+
102
+ # STEP 14: Remove ref=gstack from URLs
85
103
  content = re.sub(r"\?ref=gstack", "", content)
86
104
 
87
- # Clean up empty code blocks and whitespace
88
- content = re.sub(r"\n{4,}", "\n\n", content)
89
- content = re.sub(r"```\n\n```", "", content)
105
+ # STEP 15: Remove broken sentence fragments
106
+ content = re.sub(
107
+ r"gstack follows the \*\*[^*]+\*\* principle.*?Read more:",
108
+ "",
109
+ content,
110
+ flags=re.DOTALL,
111
+ )
90
112
 
91
- # Remove orphaned backticks
92
- content = re.sub(r"^```\n*$", "", content, flags=re.MULTILINE)
113
+ # STEP 16: Fix "You are GStack" -> "You are using OpenGStack"
114
+ content = re.sub(r"You are GStack,", "You are using OpenGStack,", content)
115
+ content = re.sub(r"You are OpenGStack,", "You are using OpenGStack,", content)
93
116
 
94
- # Clean leading/trailing whitespace per line
95
- lines = [line.rstrip() for line in content.split("\n")]
96
- content = "\n".join(lines)
117
+ # STEP 17: Remove orphaned code fences and clean up
118
+ content = re.sub(r"\n{3,}", "\n\n", content)
119
+ content = re.sub(r"```\s*\n\s*```", "", content)
120
+ content = re.sub(r"^\s*```\s*$", "", content, flags=re.MULTILINE)
97
121
 
98
- # Remove trailing newlines
99
- content = content.rstrip("\n") + "\n"
122
+ # STEP 18: Clean empty lines at start/end
123
+ content = content.strip()
100
124
 
101
- return content
125
+ return content + "\n"
102
126
 
103
127
 
104
128
  def filter_file(filepath: Path) -> bool:
@@ -120,20 +144,20 @@ def main():
120
144
  Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).parent.parent
121
145
  )
122
146
 
123
- print(f"Filtering skills in: {skills_dir}")
147
+ print(f"NUKING gstack slop from: {skills_dir}")
124
148
 
125
149
  modified = 0
126
150
  for skill_file in skills_dir.rglob("SKILL.md"):
127
151
  if filter_file(skill_file):
128
- print(f" Filtered: {skill_file.relative_to(skills_dir)}")
152
+ print(f" NUKED: {skill_file.relative_to(skills_dir)}")
129
153
  modified += 1
130
154
 
131
155
  for tmpl_file in skills_dir.rglob("SKILL.md.tmpl"):
132
156
  if filter_file(tmpl_file):
133
- print(f" Filtered: {tmpl_file.relative_to(skills_dir)}")
157
+ print(f" NUKED: {tmpl_file.relative_to(skills_dir)}")
134
158
  modified += 1
135
159
 
136
- print(f"\nDone! Modified {modified} files.")
160
+ print(f"\nDone! Cleansed {modified} files of gstack slop.")
137
161
 
138
162
 
139
163
  if __name__ == "__main__":
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const SKILLS_DIR = path.join(process.env.HOME || process.env.USERPROFILE, '.claude', 'skills');
7
+ const PKG_DIR = path.dirname(__dirname);
8
+
9
+ // List of skill directories
10
+ const skills = [
11
+ 'autoplan', 'benchmark', 'browse', 'canary', 'careful', 'codex',
12
+ 'connect-chrome', 'cso', 'design-consultation', 'design-review',
13
+ 'design-shotgun', 'document-release', 'freeze', 'gstack-upgrade',
14
+ 'guard', 'investigate', 'land-and-deploy', 'office-hours',
15
+ 'plan-ceo-review', 'plan-design-review', 'plan-eng-review',
16
+ 'qa', 'qa-only', 'retro', 'review', 'setup-browser-cookies',
17
+ 'setup-deploy', 'ship', 'unfreeze'
18
+ ];
19
+
20
+ console.log('🔗 Installing OpenGStack skills to ~/.claude/skills/...');
21
+
22
+ // Ensure skills directory exists
23
+ if (!fs.existsSync(SKILLS_DIR)) {
24
+ fs.mkdirSync(SKILLS_DIR, { recursive: true });
25
+ }
26
+
27
+ // Create symlinks for each skill
28
+ let installed = 0;
29
+ let skipped = 0;
30
+
31
+ for (const skill of skills) {
32
+ const srcPath = path.join(PKG_DIR, skill);
33
+ const destPath = path.join(SKILLS_DIR, skill);
34
+
35
+ if (!fs.existsSync(srcPath)) {
36
+ console.warn(`⚠️ Skill not found: ${skill}`);
37
+ skipped++;
38
+ continue;
39
+ }
40
+
41
+ try {
42
+ // Remove existing if it's a symlink
43
+ if (fs.existsSync(destPath)) {
44
+ const stat = fs.lstatSync(destPath);
45
+ if (stat.isSymbolicLink()) {
46
+ fs.unlinkSync(destPath);
47
+ } else {
48
+ console.log(`⏭️ Skipping ${skill} (already exists)`);
49
+ skipped++;
50
+ continue;
51
+ }
52
+ }
53
+
54
+ // Create symlink
55
+ fs.symlinkSync(srcPath, destPath, 'dir');
56
+ console.log(`✓ ${skill}`);
57
+ installed++;
58
+ } catch (err) {
59
+ console.error(`✗ ${skill}: ${err.message}`);
60
+ skipped++;
61
+ }
62
+ }
63
+
64
+ console.log(`\n✅ Installed ${installed} skills, skipped ${skipped}`);
65
+ console.log('🎯 Skills are now available in opencode/Claude');