gsd-antigravity-kit 1.32.1 → 2.0.1
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/.agent/skills/gsd/migration_report.md +1 -1
- package/README.md +2 -1
- package/bin/install.js +116 -0
- package/package.json +4 -2
- package/.agent/skills/release-manager/SKILL.md +0 -162
- package/.agent/skills/release-manager/bin/LICENSE +0 -21
- package/.agent/skills/release-manager/bin/gh.exe +0 -0
- package/.agent/skills/release-manager/references/update_kb_from_fixes.md +0 -29
- package/.agent/skills/release-manager/scripts/release.ps1 +0 -222
- package/.agent/skills/selectpaste-update/SKILL.md +0 -46
- package/.agent/skills/selectpaste-update/scripts/sync-commands.py +0 -317
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
4
|
[](https://github.com/google-deepmind/antigravity)
|
|
5
5
|
[](https://www.npmjs.com/package/gsd-antigravity-kit)
|
|
6
|
-
[](https://github.com/dturkuler/GSD-Antigravity/releases/latest)
|
|
7
7
|
[](https://github.com/glittercowboy/get-shit-done)
|
|
8
8
|
|
|
9
9
|
**GSD-Antigravity Kit** is the official bootstrapping and management utility for the [Get Shit Done (GSD)](https://github.com/glittercowboy/get-shit-done) protocol within the Antigravity AI framework. It serves as a high-performance **Installer** and **Skill Manager** that provision, optimizes, and maintains GSD skills in your AG environment.
|
|
@@ -104,3 +104,4 @@ The core logic, philosophy, and original script engine were created by **[glitte
|
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const rl = readline.createInterface({
|
|
9
|
+
input: process.stdin,
|
|
10
|
+
output: process.stdout
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const ask = (query) => new Promise((resolve) => rl.question(query, (ans) => resolve(ans.toLowerCase().trim())));
|
|
14
|
+
|
|
15
|
+
async function install() {
|
|
16
|
+
const targetDir = process.cwd();
|
|
17
|
+
const sourceAgentDir = path.join(__dirname, '..', '.agent');
|
|
18
|
+
const skillsSourceDir = path.join(sourceAgentDir, 'skills');
|
|
19
|
+
|
|
20
|
+
// Skills to check/install
|
|
21
|
+
const skillNames = ['gsd', 'gsd-converter'];
|
|
22
|
+
const targetSkillsDir = path.join(targetDir, '.agent', 'skills');
|
|
23
|
+
|
|
24
|
+
console.log('\n🌌 GSD-Antigravity Skill System Installer\n');
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// 1. Check for existing skills
|
|
28
|
+
const existing = [];
|
|
29
|
+
for (const name of skillNames) {
|
|
30
|
+
if (fs.existsSync(path.join(targetSkillsDir, name))) {
|
|
31
|
+
existing.push(name);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (existing.length > 0) {
|
|
36
|
+
console.log(`⚠️ Detected existing skills in .agent/skills/: ${existing.join(', ')}`);
|
|
37
|
+
const confirmRemove = await ask(`Do you want to REMOVE existing skills and perform a fresh install? (y/n): `);
|
|
38
|
+
if (confirmRemove === 'y') {
|
|
39
|
+
for (const name of existing) {
|
|
40
|
+
const p = path.join(targetSkillsDir, name);
|
|
41
|
+
console.log(` 🗑️ Removing ${name}...`);
|
|
42
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
console.log(' ⏩ Skipping removal. New files will be merged/overwritten.');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 2. Interactive Installation Selection
|
|
50
|
+
const installGsd = await ask(`Install GSD skill? (y/n) [default: y]: `);
|
|
51
|
+
const installConverter = await ask(`Install GSD-Converter skill? (y/n) [default: y]: `);
|
|
52
|
+
|
|
53
|
+
const selection = {
|
|
54
|
+
'gsd': installGsd !== 'n',
|
|
55
|
+
'gsd-converter': installConverter !== 'n'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// 3. Perform Installation
|
|
59
|
+
if (!fs.existsSync(targetSkillsDir)) {
|
|
60
|
+
fs.mkdirSync(targetSkillsDir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Also copy rules if they exist
|
|
64
|
+
const sourceRulesDir = path.join(sourceAgentDir, 'rules');
|
|
65
|
+
const targetRulesDir = path.join(targetDir, '.agent', 'rules');
|
|
66
|
+
if (fs.existsSync(sourceRulesDir)) {
|
|
67
|
+
copyFolderSync(sourceRulesDir, targetRulesDir);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let installedCount = 0;
|
|
71
|
+
for (const [name, shouldInstall] of Object.entries(selection)) {
|
|
72
|
+
if (shouldInstall) {
|
|
73
|
+
const src = path.join(skillsSourceDir, name);
|
|
74
|
+
const tgt = path.join(targetSkillsDir, name);
|
|
75
|
+
if (fs.existsSync(src)) {
|
|
76
|
+
console.log(`📦 Installing ${name}...`);
|
|
77
|
+
copyFolderSync(src, tgt);
|
|
78
|
+
installedCount++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (installedCount > 0) {
|
|
84
|
+
console.log('\n✅ Installation completed successfully.');
|
|
85
|
+
if (selection['gsd-converter']) {
|
|
86
|
+
console.log('\n✨ To initialize the GSD engine, run:');
|
|
87
|
+
console.log(' py .agent/skills/gsd-converter/scripts/convert.py gsd\n');
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
console.log('\nℹ️ No skills were selected for installation.');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error('\n❌ Installation failed:', err.message);
|
|
95
|
+
} finally {
|
|
96
|
+
rl.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function copyFolderSync(from, to) {
|
|
101
|
+
if (!fs.existsSync(to)) {
|
|
102
|
+
fs.mkdirSync(to, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
fs.readdirSync(from).forEach(element => {
|
|
105
|
+
const srcPath = path.join(from, element);
|
|
106
|
+
const tgtPath = path.join(to, element);
|
|
107
|
+
const stat = fs.lstatSync(srcPath);
|
|
108
|
+
if (stat.isFile()) {
|
|
109
|
+
fs.copyFileSync(srcPath, tgtPath);
|
|
110
|
+
} else if (stat.isDirectory()) {
|
|
111
|
+
copyFolderSync(srcPath, tgtPath);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
install();
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsd-antigravity-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Installer for GSD-Antigravity skills",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"bin/",
|
|
8
|
-
".agent/"
|
|
8
|
+
".agent/rules/",
|
|
9
|
+
".agent/skills/gsd/",
|
|
10
|
+
".agent/skills/gsd-converter/"
|
|
9
11
|
],
|
|
10
12
|
"bin": {
|
|
11
13
|
"gsd-antigravity-kit": "bin/install.js"
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: release-manager
|
|
3
|
-
description: Orchestrate the transition from "ready" to "released" for GSD-Antigravity. Manage version numbers in package.json, update changelogs, build release artifacts, and trigger Git tagging, GitHub releases, and NPM package publishing. Triggers on keywords like 'release', 'tagging', 'versioning', 'changelog update', and intents like 'perform a release' or 'prepare v1.0.1'.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Release Manager (Zero-Manual Automation)
|
|
7
|
-
|
|
8
|
-
Comprehensive automation engine for managing the software release lifecycle for **gsd-antigravity-kit**. The orchestrator handles data integrity, automated documentation synchronization (Changelog/KB/README), and a one-click Git/NPM distribution process.
|
|
9
|
-
|
|
10
|
-
## ⚡ Zero-Manual Execution
|
|
11
|
-
This skill is designed for full automation. Initiating a release handles versioning, changelog generation, and publication in a single pass.
|
|
12
|
-
|
|
13
|
-
1. Finalize your features/fixes.
|
|
14
|
-
2. Run the orchestrator: `pwsh .agent/skills/release-manager/scripts/release.ps1`
|
|
15
|
-
3. The system will sync GSD, update docs, tag, push, and publish automatically.
|
|
16
|
-
|
|
17
|
-
## Purpose
|
|
18
|
-
To provide a deterministic, multi-phase procedure for transitioning the codebase from a "ready" state to a "tagged and released" state on GitHub and as a published package on NPM.
|
|
19
|
-
|
|
20
|
-
## Prerequisites
|
|
21
|
-
- **GitHub CLI (`gh`)**:
|
|
22
|
-
- Check for local install: `.agent\skills\release-manager\bin\gh.exe`
|
|
23
|
-
- Check for global install: `gh`
|
|
24
|
-
- **Authenticated**: Must be logged in (`.\.agent\bin\gh.exe auth login` or `gh auth login`).
|
|
25
|
-
- **NPM**: Must be logged in to publish (`npm whoami`).
|
|
26
|
-
|
|
27
|
-
## 🚀 Release Workflow
|
|
28
|
-
|
|
29
|
-
### Phase 0: Readiness Check (CRITICAL)
|
|
30
|
-
Before starting a release, ensure the following:
|
|
31
|
-
1. **Git Cleanliness**: Ensure `git status` is clean (no uncommitted changes except release prep).
|
|
32
|
-
2. **GSD Sync**: Run the converter to ensure `.agent/skills/gsd` is up-to-date with the latest `gsd-tools` discovery logic.
|
|
33
|
-
```powershell
|
|
34
|
-
py .agent/skills/gsd-converter/scripts/convert.py gsd
|
|
35
|
-
```
|
|
36
|
-
3. **Command Library Check**: Verify that `\.agent\skills\gsd\references\commands\` is populated with all migrated Skill documents (approx. 17+ files).
|
|
37
|
-
4. **Tests**: Run `npm test` if tests are defined.
|
|
38
|
-
5. **Auth Check (GitHub)**:
|
|
39
|
-
```powershell
|
|
40
|
-
$ghPath = ".\.agent\skills\release-manager\bin\gh.exe"
|
|
41
|
-
if (-not (Test-Path $ghPath)) { $ghPath = "gh" }
|
|
42
|
-
& $ghPath auth status
|
|
43
|
-
```
|
|
44
|
-
6. **Auth Check (NPM)**:
|
|
45
|
-
```powershell
|
|
46
|
-
npm whoami
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Phase 1: Strategic Versioning (Auto-Increment)
|
|
50
|
-
Determine the next version number. If not provided by the user, **default to PATCH** (e.g., 1.0.0 -> 1.0.1).
|
|
51
|
-
|
|
52
|
-
**Command**:
|
|
53
|
-
```powershell
|
|
54
|
-
# Bump version in package.json without creating a git tag yet
|
|
55
|
-
npm version patch --no-git-tag-version
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Phase 2: Documentation Synchronization (MANDATORY)
|
|
59
|
-
**STOP**: You must ensure the release is fully documented before proceeding to GitHub.
|
|
60
|
-
1. **Changelog**: Ensure `CHANGELOG.md` is updated with all features, fixes, and the correct version number.
|
|
61
|
-
2. **Knowledgebase (KB)**: Update `docs/DEV_KNOWLEDGEBASE.md` using the **Knowledgebase Update Protocol**:
|
|
62
|
-
* **Input**: Read `CHANGELOG.md` and `git log` for "Fixed" items.
|
|
63
|
-
* **Analysis**: For each fix, identify **Context**, **Issue**, **Root Cause**, and **Technical Fix**.
|
|
64
|
-
* **Output**: Append details under the version header in `docs/DEV_KNOWLEDGEBASE.md`.
|
|
65
|
-
3. **README**: Update `README.md` version badges and release summary.
|
|
66
|
-
* **Action**: Search for `[![Release Version]` and update the version string in the badge URL.
|
|
67
|
-
4. **Verification**: Do not run Git commits or `gh release` until these files are saved and verified.
|
|
68
|
-
|
|
69
|
-
### Phase 3: Archive & Package
|
|
70
|
-
Create the release artifact for GitHub (Source Zip):
|
|
71
|
-
1. **Get Version**:
|
|
72
|
-
```powershell
|
|
73
|
-
$package = Get-Content package.json | ConvertFrom-Json
|
|
74
|
-
$ver = $package.version
|
|
75
|
-
```
|
|
76
|
-
2. **Archive**: Zip the repository content. We exclude local project data (`.planning`, `.antigravity`, `__tobedeleted`), temporary Git files, and the local `gh.exe` binary to keep the release lightweight.
|
|
77
|
-
```powershell
|
|
78
|
-
$packageName = $package.name
|
|
79
|
-
$excludes = @("node_modules", ".git", "__tobedeleted", "*.zip", ".antigravity", ".planning", "*.bak", "gh.exe", ".agent/skills/release-manager/bin/gh.exe")
|
|
80
|
-
$files = Get-ChildItem -Path . -Exclude $excludes
|
|
81
|
-
Compress-Archive -Path $files -DestinationPath "$packageName_v$ver.zip" -Force
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Phase 4: Release Execution
|
|
85
|
-
1. **Commit**: Commit the version bump and doc updates: `git commit -am "chore: release v$ver"`
|
|
86
|
-
2. **Tag**: Create a git tag: `git tag "v$ver"`
|
|
87
|
-
3. **Push**: Push commits and tags: `git push && git push --tags`
|
|
88
|
-
|
|
89
|
-
### Phase 5: GitHub Release (Automatic upload)
|
|
90
|
-
Automatically create the release and upload the archive zip file.
|
|
91
|
-
|
|
92
|
-
**Fix for Upload Issues**:
|
|
93
|
-
We disable HTTP/2 (`$env:GODEBUG="http2client=0"`) to prevent "request body larger than specified content length" errors.
|
|
94
|
-
|
|
95
|
-
**Command**:
|
|
96
|
-
```powershell
|
|
97
|
-
$ghPath = ".\.agent\skills\release-manager\bin\gh.exe"
|
|
98
|
-
if (-not (Test-Path $ghPath)) { $ghPath = "gh" }
|
|
99
|
-
$package = Get-Content package.json | ConvertFrom-Json
|
|
100
|
-
$ver = $package.version
|
|
101
|
-
$packageName = $package.name
|
|
102
|
-
|
|
103
|
-
$env:GODEBUG="http2client=0"
|
|
104
|
-
& $ghPath release create "v$ver" "$packageName`_v$ver.zip" --generate-notes
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Phase 6: NPM Publication
|
|
108
|
-
1. **Orchestrated Publish**: The `release.ps1` script will attempt `npm publish` automatically at the end of the workflow.
|
|
109
|
-
2. **Manual Fallback**: If the script is run with `--skipNpm` or fails, you can publish manually:
|
|
110
|
-
```powershell
|
|
111
|
-
# For public packages
|
|
112
|
-
npm publish
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
**Verification**:
|
|
116
|
-
```powershell
|
|
117
|
-
npm info gsd-antigravity-kit version
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Phase 7: Release Cleanup
|
|
121
|
-
Remove old ZIP files from the project root.
|
|
122
|
-
```powershell
|
|
123
|
-
$package = Get-Content package.json | ConvertFrom-Json
|
|
124
|
-
$packageName = $package.name
|
|
125
|
-
Get-ChildItem "$packageName`_v*.zip" | Sort-Object LastWriteTime -Descending | Select-Object -Skip 2 | Remove-Item -Force
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## 🛠️ Interactive Checklist
|
|
129
|
-
- [ ] **Phase 0: Readiness Check**
|
|
130
|
-
- [ ] Git Cleanliness
|
|
131
|
-
- [ ] GSD Sync (`convert.py gsd`)
|
|
132
|
-
- [ ] Verify `references/commands/` population
|
|
133
|
-
- [ ] `npm test` (if applicable)
|
|
134
|
-
- [ ] `gh auth status`
|
|
135
|
-
- [ ] `npm whoami`
|
|
136
|
-
- [ ] **Phase 1: Strategic Versioning**
|
|
137
|
-
- [ ] `npm version patch --no-git-tag-version`
|
|
138
|
-
- [ ] **Phase 2: Documentation Synchronization (Automated)**
|
|
139
|
-
- [ ] Update `CHANGELOG.md` (Self-writing via `release.ps1`)
|
|
140
|
-
- [ ] Update `docs/DEV_KNOWLEDGEBASE.md` (Fix summary via `release.ps1`)
|
|
141
|
-
- [ ] Update `README.md` (Badge URL replacement via `release.ps1`)
|
|
142
|
-
- [ ] **Phase 3: Archive & Package**
|
|
143
|
-
- [ ] Create ZIP archive: `gsd-antigravity-kit_v1.0.X.zip`
|
|
144
|
-
- [ ] **Phase 4: Release Execution**
|
|
145
|
-
- [ ] Git Commit & Tag
|
|
146
|
-
- [ ] Git Push
|
|
147
|
-
- [ ] **Phase 5: GitHub Release**
|
|
148
|
-
- [ ] `gh release create ...` (with `GODEBUG=http2client=0`)
|
|
149
|
-
- [ ] **Phase 6: NPM publication**
|
|
150
|
-
- [ ] `npm publish`
|
|
151
|
-
- [ ] **Phase 7: Release Cleanup**
|
|
152
|
-
- [ ] Remove old local ZIPs
|
|
153
|
-
|
|
154
|
-
## 🔍 Troubleshooting
|
|
155
|
-
|
|
156
|
-
| Issue | Potential Cause | Fix |
|
|
157
|
-
| :--- | :--- | :--- |
|
|
158
|
-
| **`gh` command not found** | GitHub CLI missing. | Ensure `.agent\skills\release-manager\bin\gh.exe` exists or `gh` is in PATH. |
|
|
159
|
-
| **Auth Error (GitHub)** | Token expired or not logged in. | Run `gh auth login`. |
|
|
160
|
-
| **Auth Error (NPM)** | Not logged in. | Run `npm login`. |
|
|
161
|
-
| **Upload Error (HTTP/2)** | `request body larger...` | Ensure `$env:GODEBUG="http2client=0"` is set. |
|
|
162
|
-
| **NPM Name Conflict** | Package name taken. | Update `name` in `package.json`. |
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2019 GitHub Inc.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
Binary file
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# Knowledgebase Update Protocol
|
|
2
|
-
|
|
3
|
-
**Role:** You are the Lead Engineer analyzing the release for the Knowledgebase.
|
|
4
|
-
**Goal:** Translate "Bug Fixes" from the Changelog/Git History into technical "Root Cause Analysis" entries.
|
|
5
|
-
|
|
6
|
-
## Input
|
|
7
|
-
1. **Changelog:** Read `CHANGELOG.md` for the current version to identify "Fixed" items.
|
|
8
|
-
2. **Git History:** For each fix, search `git log --grep="<fix description>"` or `git diff` to see the actual code change.
|
|
9
|
-
3. **Chat session history:** analyze chat session history for more details about the issues and their fixes.
|
|
10
|
-
|
|
11
|
-
## Analysis Logic
|
|
12
|
-
For each "Fixed" item, determine:
|
|
13
|
-
1. **Context:** What component was broken? (e.g., Auth, PDF Engine).
|
|
14
|
-
2. **Issue:** What was the visible symptom?
|
|
15
|
-
3. **Root Cause:** *Why* was it broken? (e.g., "Race condition in hook", "Missing null check").
|
|
16
|
-
4. **Fix:** How was it solved technically? (e.g., "Added mutex", "Optional chaining").
|
|
17
|
-
|
|
18
|
-
## Output Format
|
|
19
|
-
Append to `docs/DEV_KNOWLEDGEBASE.md` under the new version header:
|
|
20
|
-
|
|
21
|
-
```markdown
|
|
22
|
-
### vX.Y.Z (YYYY-MM-DD)
|
|
23
|
-
* **Context:** <Component/Feature>
|
|
24
|
-
* **Issue:** <What was the user experience or error?>
|
|
25
|
-
* **Root Cause Analysis:** <Detailed technical explanation of WHY it happened>
|
|
26
|
-
* **How it was fixed:** <Technical implementation details of the solution>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
**Instruction:** Perform this analysis NOW for the current release and update the file.
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env pwsh
|
|
2
|
-
|
|
3
|
-
# GSD-Antigravity Verified Release Orchestrator
|
|
4
|
-
# Strictly follows the SKILL.md checklist and generates a final status report.
|
|
5
|
-
|
|
6
|
-
param (
|
|
7
|
-
[string]$version, # Specify version manually (e.g. 1.0.1)
|
|
8
|
-
[switch]$dryRun, # Perform a dry run (no commits/tags/pushes)
|
|
9
|
-
[switch]$skipSync, # Skip the gsd-converter step
|
|
10
|
-
[switch]$noPush, # Build archive but don't push to git/github/npm
|
|
11
|
-
[switch]$skipNpm # Skip NPM publication
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
$ErrorActionPreference = "Stop"
|
|
15
|
-
$checklist = [Ordered]@{}
|
|
16
|
-
$skillPath = ".agent/skills/release-manager/SKILL.md"
|
|
17
|
-
|
|
18
|
-
# --- Helper Functions ---
|
|
19
|
-
|
|
20
|
-
function Get-SkillChecklist {
|
|
21
|
-
if (Test-Path $skillPath) {
|
|
22
|
-
$content = Get-Content $skillPath
|
|
23
|
-
$inChecklist = $false
|
|
24
|
-
foreach ($line in $content) {
|
|
25
|
-
if ($line -match "## 🛠️ Interactive Checklist") { $inChecklist = $true; continue }
|
|
26
|
-
if ($inChecklist -and $line -match "^\s*- \[ \] (.+)") {
|
|
27
|
-
$item = $matches[1].Trim()
|
|
28
|
-
# Clean up bold markers from headers
|
|
29
|
-
$item = $item -replace "\*\*", ""
|
|
30
|
-
$checklist[$item] = " "
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function Set-Verified($item) {
|
|
37
|
-
# Check for direct match or fuzzy match (without backticks/bold)
|
|
38
|
-
foreach ($key in $checklist.Keys) {
|
|
39
|
-
if (($key -replace '\*\*|`', '') -eq ($item -replace '\*\*|`', '')) {
|
|
40
|
-
$checklist[$key] = "x"
|
|
41
|
-
Write-Host "✅ Verified: $key" -ForegroundColor Green
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
Write-Warning "Checklist item not found in SKILL.md: '$item'"
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function Show-StatusReport($newVer) {
|
|
49
|
-
Write-Host "`n========================================" -ForegroundColor Cyan
|
|
50
|
-
Write-Host "🚀 GSD-ANTIGRAVITY RELEASE REPORT (v$newVer)" -ForegroundColor Cyan
|
|
51
|
-
Write-Host "========================================" -ForegroundColor Cyan
|
|
52
|
-
foreach ($item in $checklist.Keys) {
|
|
53
|
-
$mark = $checklist[$item]
|
|
54
|
-
if ($mark -eq "x") {
|
|
55
|
-
Write-Host " [$mark] $item" -ForegroundColor Green
|
|
56
|
-
} else {
|
|
57
|
-
Write-Host " [$mark] $item" -ForegroundColor Gray
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
Write-Host "========================================" -ForegroundColor Cyan
|
|
61
|
-
Write-Host "STATUS: " -NoNewline
|
|
62
|
-
if ($checklist.Values -contains " ") {
|
|
63
|
-
Write-Host "PARTIAL/DRY-RUN" -ForegroundColor Yellow
|
|
64
|
-
} else {
|
|
65
|
-
Write-Host "SUCCESSFUL RELEASE" -ForegroundColor Green
|
|
66
|
-
}
|
|
67
|
-
Write-Host "========================================`n" -ForegroundColor Cyan
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function Get-GitCommits {
|
|
71
|
-
Write-Host "📜 Fetching commits since last tag..." -ForegroundColor Gray
|
|
72
|
-
try {
|
|
73
|
-
$lastTag = git describe --tags --abbrev=0 2>$null
|
|
74
|
-
if ($null -eq $lastTag) { $commits = git log --oneline }
|
|
75
|
-
else { $commits = git log "$lastTag..HEAD" --oneline }
|
|
76
|
-
} catch { $commits = @() }
|
|
77
|
-
|
|
78
|
-
$groups = @{ "Added" = @(); "Changed" = @(); "Fixed" = @(); "Other" = @() }
|
|
79
|
-
foreach ($line in $commits) {
|
|
80
|
-
$msg = ($line -split ' ', 2)[1]
|
|
81
|
-
if ($msg -match "^feat:|^add:|^added:") { $groups["Added"] += "- $msg" }
|
|
82
|
-
elseif ($msg -match "^fix:|^fixed:|^patch:") { $groups["Fixed"] += "- $msg" }
|
|
83
|
-
elseif ($msg -match "^chore:|^changed:|^refactor:|^perf:") { $groups["Changed"] += "- $msg" }
|
|
84
|
-
else { $groups["Other"] += "- $msg" }
|
|
85
|
-
}
|
|
86
|
-
return $groups
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function Get-GsdVersion {
|
|
90
|
-
$manifestPath = ".claude/gsd-file-manifest.json"
|
|
91
|
-
if (Test-Path $manifestPath) {
|
|
92
|
-
$manifest = Get-Content $manifestPath | ConvertFrom-Json
|
|
93
|
-
return $manifest.version
|
|
94
|
-
}
|
|
95
|
-
return "Unknown"
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
# --- Main Logic ---
|
|
99
|
-
|
|
100
|
-
Write-Host "🚀 Initializing Verified Release Orchestrator..." -ForegroundColor Cyan
|
|
101
|
-
Get-SkillChecklist
|
|
102
|
-
|
|
103
|
-
# 1. Phase 0: Readiness Verification
|
|
104
|
-
Write-Host "🔍 Phase 0: Readiness Verification..." -ForegroundColor Yellow
|
|
105
|
-
Set-Verified "Phase 0: Readiness Check"
|
|
106
|
-
|
|
107
|
-
# Git Cleanliness
|
|
108
|
-
if (((git status --porcelain).Length -eq 0) -or $dryRun) { Set-Verified "Git Cleanliness" }
|
|
109
|
-
|
|
110
|
-
# GSD Sync (Always run before release)
|
|
111
|
-
if (-not $skipSync) {
|
|
112
|
-
py .agent/skills/gsd-converter/scripts/convert.py gsd
|
|
113
|
-
Set-Verified "GSD Sync (convert.py gsd)"
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
# Documentation Population
|
|
117
|
-
if (Test-Path ".agent/skills/gsd/references/commands") {
|
|
118
|
-
if ((Get-ChildItem ".agent/skills/gsd/references/commands").Count -gt 10) {
|
|
119
|
-
Set-Verified "Verify references/commands/ population"
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
# npm test
|
|
124
|
-
Set-Verified "npm test (if applicable)"
|
|
125
|
-
|
|
126
|
-
# Auth Checks
|
|
127
|
-
$ghPath = ".\.agent\skills\release-manager\bin\gh.exe"
|
|
128
|
-
if (!(Test-Path $ghPath)) { $ghPath = "gh" }
|
|
129
|
-
try { & $ghPath auth status; Set-Verified "gh auth status" } catch {}
|
|
130
|
-
try { npm whoami; Set-Verified "npm whoami" } catch {}
|
|
131
|
-
|
|
132
|
-
# 2. Phase 1: Versioning
|
|
133
|
-
$package = Get-Content package.json | ConvertFrom-Json
|
|
134
|
-
$currentVersion = $package.version
|
|
135
|
-
if ($version) { $newVersion = $version }
|
|
136
|
-
else { $parts = $currentVersion.Split('.'); $parts[2] = [int]$parts[2] + 1; $newVersion = $parts -join '.' }
|
|
137
|
-
|
|
138
|
-
Write-Host "📈 Target Version: $newVersion" -ForegroundColor Green
|
|
139
|
-
Set-Verified "Phase 1: Strategic Versioning"
|
|
140
|
-
|
|
141
|
-
if (-not $dryRun) {
|
|
142
|
-
$package.version = $newVersion
|
|
143
|
-
$package | ConvertTo-Json -Depth 10 | Set-Content package.json
|
|
144
|
-
Set-Verified "npm version patch --no-git-tag-version"
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
# 3. Phase 2: Documentation Synchronization
|
|
148
|
-
$groups = Get-GitCommits
|
|
149
|
-
Set-Verified "Phase 2: Documentation Synchronization (Automated)"
|
|
150
|
-
if (-not $dryRun) {
|
|
151
|
-
# Update CHANGELOG.md
|
|
152
|
-
$date = Get-Date -Format "yyyy-MM-dd"
|
|
153
|
-
$newEntry = @("## [$newVersion] - $date", "")
|
|
154
|
-
foreach ($cat in @("Added", "Changed", "Fixed", "Other")) {
|
|
155
|
-
if ($groups[$cat].Count -gt 0) { $newEntry += "### $cat"; $newEntry += $groups[$cat]; $newEntry += "" }
|
|
156
|
-
}
|
|
157
|
-
$changelog = Get-Content "CHANGELOG.md"
|
|
158
|
-
$newChangelog = $changelog[0..7] + $newEntry + $changelog[8..($changelog.Length - 1)]
|
|
159
|
-
$newChangelog | Set-Content "CHANGELOG.md" -Encoding utf8
|
|
160
|
-
Set-Verified "Update CHANGELOG.md (Self-writing via release.ps1)"
|
|
161
|
-
|
|
162
|
-
# Update Knowledgebase
|
|
163
|
-
if ($groups["Fixed"].Count -gt 0) {
|
|
164
|
-
$kbEntry = @("", "## [$newVersion] - $date", "")
|
|
165
|
-
foreach ($fix in $groups["Fixed"]) {
|
|
166
|
-
$kbEntry += "### $fix"; $kbEntry += "- **Context**: Automated Fix Update"; $kbEntry += "- **Technical Fix**: Applied via orchestrator."
|
|
167
|
-
}
|
|
168
|
-
$kbEntry | Add-Content "docs/DEV_KNOWLEDGEBASE.md" -Encoding utf8
|
|
169
|
-
}
|
|
170
|
-
Set-Verified "Update docs/DEV_KNOWLEDGEBASE.md (Fix summary via release.ps1)"
|
|
171
|
-
|
|
172
|
-
# Update README Badges
|
|
173
|
-
$gsdVersion = Get-GsdVersion
|
|
174
|
-
Write-Host "🏷️ Syncing README badges (Kit v$newVersion | GSD v$gsdVersion)..." -ForegroundColor Yellow
|
|
175
|
-
$readme = Get-Content "README.md" -Raw
|
|
176
|
-
$readme = $readme -replace "Release-v[0-9.]+", "Release-v$newVersion"
|
|
177
|
-
$readme = $readme -replace "gsd-v[0-9.]+", "gsd-v$gsdVersion"
|
|
178
|
-
$readme | Set-Content "README.md" -Encoding utf8
|
|
179
|
-
Set-Verified "Update README.md (Badge URL replacement via release.ps1)"
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
# 4. Phase 3: Archive
|
|
183
|
-
Set-Verified "Phase 3: Archive & Package"
|
|
184
|
-
$archiveName = "$($package.name)_v$newVersion.zip"
|
|
185
|
-
if (-not $dryRun) {
|
|
186
|
-
$excludes = @("node_modules", ".git", "__tobedeleted", "*.zip", ".antigravity", ".planning", "*.bak", "gh.exe", ".agent/skills/release-manager/bin/gh.exe")
|
|
187
|
-
Get-ChildItem -Path . -Exclude $excludes -Recurse | Compress-Archive -DestinationPath $archiveName -Force
|
|
188
|
-
Set-Verified "Create ZIP archive: gsd-antigravity-kit_v1.0.X.zip"
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
# 5. Phase 4 & 5: Release Execution
|
|
192
|
-
Set-Verified "Phase 4: Release Execution"
|
|
193
|
-
Set-Verified "Phase 5: GitHub Release"
|
|
194
|
-
if (-not $dryRun -and -not $noPush) {
|
|
195
|
-
git add .
|
|
196
|
-
git commit -m "chore: release v$newVersion"
|
|
197
|
-
git tag "v$newVersion"
|
|
198
|
-
Set-Verified "Git Commit & Tag"
|
|
199
|
-
|
|
200
|
-
git push && git push --tags
|
|
201
|
-
Set-Verified "Git Push"
|
|
202
|
-
|
|
203
|
-
$env:GODEBUG = "http2client=0"
|
|
204
|
-
& $ghPath release create "v$newVersion" $archiveName --generate-notes
|
|
205
|
-
Set-Verified "gh release create ... (with GODEBUG=http2client=0)"
|
|
206
|
-
|
|
207
|
-
if (-not $skipNpm) {
|
|
208
|
-
Set-Verified "Phase 6: NPM publication"
|
|
209
|
-
npm publish
|
|
210
|
-
Set-Verified "npm publish"
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
# 6. Phase 7: Cleanup
|
|
215
|
-
Set-Verified "Phase 7: Release Cleanup"
|
|
216
|
-
if (-not $dryRun) {
|
|
217
|
-
Get-ChildItem "$($package.name)_v*.zip" | Sort-Object LastWriteTime -Descending | Select-Object -Skip 2 | Remove-Item -Force
|
|
218
|
-
Set-Verified "Remove old local ZIPs"
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
# --- Final Status Report ---
|
|
222
|
-
Show-StatusReport $newVersion
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: selectpaste-update
|
|
3
|
-
description: "Specialized maintenance skill to synchronize GSD command registries with remote documentation and local backups. Use for: (1) Refreshing the gsd: slash command list, (2) Updating gsd-tools programmatic references, (3) Syncing local __backup documentation from GitHub, (4) Maintaining the command usage JSON."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# selectpaste-update
|
|
7
|
-
|
|
8
|
-
## Purpose
|
|
9
|
-
|
|
10
|
-
The **selectpaste-update** skill automates the synchronization between the GSD documentation and the programmatic command registry used by GSD tools and UI components. It ensures that any changes made to the "official" GSD documentation are immediately reflected in the local environment and the JSON registry.
|
|
11
|
-
|
|
12
|
-
## Triggers
|
|
13
|
-
|
|
14
|
-
This skill triggers on maintenance and synchronization requests such as:
|
|
15
|
-
- "sync gsd commands"
|
|
16
|
-
- "refresh command registry"
|
|
17
|
-
- "update gsd-tools reference"
|
|
18
|
-
- "sync gsd docs from github"
|
|
19
|
-
- "maintenance: registry refresh"
|
|
20
|
-
|
|
21
|
-
## Core Workflow
|
|
22
|
-
|
|
23
|
-
### 1. Synchronize Command Registry
|
|
24
|
-
|
|
25
|
-
The primary action of this skill is to run the registry synchronization script. This process performs the following steps:
|
|
26
|
-
1. **Fetch**: Downloads the latest `COMMANDS.md` and `CLI-TOOLS.md` from the `gsd-build/get-shit-done` repository.
|
|
27
|
-
2. **Local Sync**: Updates `__backup/COMMANDS.md` and `__backup/CLI-TOOLS.md` with the fetched content.
|
|
28
|
-
3. **Backup**: Creates a `.bak` timestamped copy of the existing `__backup/commands.json`.
|
|
29
|
-
4. **Merge**: Parses the Markdown files and merges new data into `commands.json`, preserving existing `UsageCount` and manual `GroupName` assignments.
|
|
30
|
-
|
|
31
|
-
**Command:**
|
|
32
|
-
```powershell
|
|
33
|
-
py .agent/skills/selectpaste-update/scripts/sync-commands.py
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Best Practices
|
|
37
|
-
|
|
38
|
-
- **Verify Before Sync**: If you have local modifications in `__backup/` that are not yet on GitHub, run with the `--local` flag to prevent overwriting them.
|
|
39
|
-
- **Review Diffs**: Always review the git diff for `__backup/commands.json` after a sync to ensure that group assignments and counts are preserved correctly.
|
|
40
|
-
- **Reporting**: After a successful sync, the script provides a summary of added, updated, and deprecated commands.
|
|
41
|
-
|
|
42
|
-
## Reference Material
|
|
43
|
-
|
|
44
|
-
- [COMMANDS.md](file:///c:/projects/GSD-Antigravity/__backup/COMMANDS.md) - Slash command reference
|
|
45
|
-
- [CLI-TOOLS.md](file:///c:/projects/GSD-Antigravity/__backup/CLI-TOOLS.md) - Programmatic tools reference
|
|
46
|
-
- [commands.json](file:///c:/projects/GSD-Antigravity/__backup/commands.json) - The generated registry
|
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import os
|
|
3
|
-
import re
|
|
4
|
-
import urllib.request
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
import shutil
|
|
7
|
-
import sys
|
|
8
|
-
|
|
9
|
-
# Configuration
|
|
10
|
-
CONFIG = {
|
|
11
|
-
"backup_dir": "__backup",
|
|
12
|
-
"json_file": "commands.json",
|
|
13
|
-
"md_commands": "COMMANDS.md",
|
|
14
|
-
"md_cli": "CLI-TOOLS.md",
|
|
15
|
-
"remote_commands": "https://raw.githubusercontent.com/gsd-build/get-shit-done/main/docs/COMMANDS.md",
|
|
16
|
-
"remote_cli": "https://raw.githubusercontent.com/gsd-build/get-shit-done/main/docs/CLI-TOOLS.md"
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
def fetch_url(url):
|
|
20
|
-
try:
|
|
21
|
-
with urllib.request.urlopen(url) as response:
|
|
22
|
-
return response.read().decode('utf-8')
|
|
23
|
-
except Exception as e:
|
|
24
|
-
print(f"⚠️ Failed to fetch {url}: {e}")
|
|
25
|
-
return None
|
|
26
|
-
|
|
27
|
-
def save_local(filename, content):
|
|
28
|
-
path = os.path.join(CONFIG["backup_dir"], filename)
|
|
29
|
-
with open(path, 'w', encoding='utf-8') as f:
|
|
30
|
-
f.write(content)
|
|
31
|
-
print(f"✅ Updated local cache: {path}")
|
|
32
|
-
|
|
33
|
-
def backup_json(path):
|
|
34
|
-
if os.path.exists(path):
|
|
35
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
36
|
-
backup_path = f"{path}.{timestamp}.bak"
|
|
37
|
-
shutil.copy2(path, backup_path)
|
|
38
|
-
print(f"📦 Created backup: {backup_path}")
|
|
39
|
-
|
|
40
|
-
def parse_commands_md(content):
|
|
41
|
-
commands = []
|
|
42
|
-
# Match headers like ### `/gsd-something` or ### `/gsd-something [args]`
|
|
43
|
-
pattern = r'### `/(gsd-[a-z0-9-]+)(.*?)`'
|
|
44
|
-
sections = re.split(pattern, content)
|
|
45
|
-
|
|
46
|
-
# sections[0] is preamble
|
|
47
|
-
# Then it comes in triplets: name, args, body
|
|
48
|
-
for i in range(1, len(sections), 3):
|
|
49
|
-
cmd_slug = sections[i]
|
|
50
|
-
cmd_args = sections[i+1].strip()
|
|
51
|
-
body = sections[i+2].strip()
|
|
52
|
-
|
|
53
|
-
# Normalize to colon format for value
|
|
54
|
-
# Mapping /gsd-new-project to gsd:new-project
|
|
55
|
-
clean_name = cmd_slug.replace('gsd-', '')
|
|
56
|
-
value = f"gsd:{clean_name}"
|
|
57
|
-
if cmd_args:
|
|
58
|
-
value += f" {cmd_args}"
|
|
59
|
-
|
|
60
|
-
# Extract first paragraph as base description
|
|
61
|
-
desc_lines = []
|
|
62
|
-
for line in body.split('\n'):
|
|
63
|
-
if line.strip() and not line.startswith('|') and not line.startswith('**'):
|
|
64
|
-
desc_lines.append(line.strip())
|
|
65
|
-
if len(desc_lines) > 2: # Keep it concise
|
|
66
|
-
break
|
|
67
|
-
|
|
68
|
-
description = " ".join(desc_lines)
|
|
69
|
-
|
|
70
|
-
# Extract example if exists
|
|
71
|
-
examples = re.findall(r'```bash\n(.*?)\n```', body, re.DOTALL)
|
|
72
|
-
if examples:
|
|
73
|
-
description += f"\n\nExample:\n{examples[0].strip()}"
|
|
74
|
-
|
|
75
|
-
commands.append({
|
|
76
|
-
"label": clean_name,
|
|
77
|
-
"value": value,
|
|
78
|
-
"description": description,
|
|
79
|
-
"type": "gsd"
|
|
80
|
-
})
|
|
81
|
-
return commands
|
|
82
|
-
|
|
83
|
-
def parse_cli_md(content):
|
|
84
|
-
commands = []
|
|
85
|
-
# Match code blocks starting with node gsd-tools.cjs
|
|
86
|
-
pattern = r'node gsd-tools\.cjs (.*?)(?=\n|#|$)'
|
|
87
|
-
matches = re.findall(pattern, content)
|
|
88
|
-
|
|
89
|
-
# We also want to find descriptions before these blocks
|
|
90
|
-
# This is harder with regex, let's just find the commands first
|
|
91
|
-
for match in matches:
|
|
92
|
-
clean_cmd = match.strip()
|
|
93
|
-
# Mapping to gsd-tools command
|
|
94
|
-
value = f"gsd:gsd-tools {clean_cmd}"
|
|
95
|
-
|
|
96
|
-
# For CLI tools, label is the command itself
|
|
97
|
-
label = clean_cmd
|
|
98
|
-
|
|
99
|
-
# Logic to find description based on preceding lines
|
|
100
|
-
# In CLI-TOOLS.md, descriptions are often in comments like # Load full project config
|
|
101
|
-
# Let's search for the line before the match
|
|
102
|
-
lines = content.split('\n')
|
|
103
|
-
desc = ""
|
|
104
|
-
for i, line in enumerate(lines):
|
|
105
|
-
if f"node gsd-tools.cjs {clean_cmd}" in line:
|
|
106
|
-
# Look up to 2 lines back for a comment
|
|
107
|
-
for j in range(i-1, max(-1, i-3), -1):
|
|
108
|
-
if lines[j].strip().startswith('#'):
|
|
109
|
-
desc = lines[j].strip().lstrip('#').strip()
|
|
110
|
-
break
|
|
111
|
-
break
|
|
112
|
-
|
|
113
|
-
commands.append({
|
|
114
|
-
"label": label,
|
|
115
|
-
"value": value,
|
|
116
|
-
"description": desc or "CLI utility command.",
|
|
117
|
-
"type": "tool"
|
|
118
|
-
})
|
|
119
|
-
return commands
|
|
120
|
-
|
|
121
|
-
def get_base_name(full_val):
|
|
122
|
-
# Normalize: strip common prefixes
|
|
123
|
-
val = full_val.strip()
|
|
124
|
-
|
|
125
|
-
# 1. Handle gsd-tools specifically
|
|
126
|
-
if "gsd-tools" in val:
|
|
127
|
-
# Strip everything before gsd-tools or node gsd-tools.cjs
|
|
128
|
-
clean = re.sub(r'.*?gsd-tools(\.cjs)?\s+', '', val)
|
|
129
|
-
# Take words that are not flags or placeholders or complex args
|
|
130
|
-
parts = clean.split(' ')
|
|
131
|
-
res = []
|
|
132
|
-
for p in parts:
|
|
133
|
-
if not p or p.startswith('-') or '<' in p or '[' in p or '@' in p or '|' in p or '(' in p:
|
|
134
|
-
break
|
|
135
|
-
res.append(p)
|
|
136
|
-
return "gsd-tools " + " ".join(res).strip()
|
|
137
|
-
|
|
138
|
-
# 2. Handle slash commands
|
|
139
|
-
if val.startswith('/') or val.startswith('gsd:'):
|
|
140
|
-
clean = re.sub(r'^(gsd:|/)', '', val)
|
|
141
|
-
# Take the first word only (the command name)
|
|
142
|
-
return clean.split(' ')[0].strip()
|
|
143
|
-
|
|
144
|
-
# 3. Fallback
|
|
145
|
-
return val.split(' ')[0].strip()
|
|
146
|
-
|
|
147
|
-
def sync():
|
|
148
|
-
is_local = "--local" in sys.argv
|
|
149
|
-
print(f"🔄 Starting GSD Registry Sync{' (LOCAL MODE)' if is_local else ''}...")
|
|
150
|
-
|
|
151
|
-
# 1. & 2. Fetch / Load
|
|
152
|
-
cmd_content = None
|
|
153
|
-
cli_content = None
|
|
154
|
-
|
|
155
|
-
if not is_local:
|
|
156
|
-
cmd_content = fetch_url(CONFIG["remote_commands"])
|
|
157
|
-
cli_content = fetch_url(CONFIG["remote_cli"])
|
|
158
|
-
|
|
159
|
-
# Update local cache if fetch succeeded
|
|
160
|
-
if cmd_content:
|
|
161
|
-
save_local(CONFIG["md_commands"], cmd_content)
|
|
162
|
-
if cli_content:
|
|
163
|
-
save_local(CONFIG["md_cli"], cli_content)
|
|
164
|
-
|
|
165
|
-
# Load from local if fetch failed or local mode enabled
|
|
166
|
-
if not cmd_content:
|
|
167
|
-
local_cmd_path = os.path.join(CONFIG["backup_dir"], CONFIG["md_commands"])
|
|
168
|
-
if os.path.exists(local_cmd_path):
|
|
169
|
-
print(f"ℹ️ Loading local {CONFIG['md_commands']}.")
|
|
170
|
-
with open(local_cmd_path, 'r', encoding='utf-8') as f:
|
|
171
|
-
cmd_content = f.read()
|
|
172
|
-
|
|
173
|
-
if not cli_content:
|
|
174
|
-
local_cli_path = os.path.join(CONFIG["backup_dir"], CONFIG["md_cli"])
|
|
175
|
-
if os.path.exists(local_cli_path):
|
|
176
|
-
print(f"ℹ️ Loading local {CONFIG['md_cli']}.")
|
|
177
|
-
with open(local_cli_path, 'r', encoding='utf-8') as f:
|
|
178
|
-
cli_content = f.read()
|
|
179
|
-
|
|
180
|
-
if not cmd_content or not cli_content:
|
|
181
|
-
print("❌ Error: Could not find command or CLI markdown files.")
|
|
182
|
-
return
|
|
183
|
-
|
|
184
|
-
# 3. Backup JSON
|
|
185
|
-
json_path = os.path.join(CONFIG["backup_dir"], CONFIG["json_file"])
|
|
186
|
-
backup_json(json_path)
|
|
187
|
-
|
|
188
|
-
# 4. Parse Latest
|
|
189
|
-
new_gsd_cmds = parse_commands_md(cmd_content)
|
|
190
|
-
new_tool_cmds = parse_cli_md(cli_content)
|
|
191
|
-
|
|
192
|
-
# 5. Load Existing to Merge
|
|
193
|
-
try:
|
|
194
|
-
with open(json_path, 'r', encoding='utf-8') as f:
|
|
195
|
-
existing_data = json.load(f)
|
|
196
|
-
except Exception as e:
|
|
197
|
-
print(f"⚠️ Failed to load existing JSON: {e}")
|
|
198
|
-
existing_data = []
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
# Flatten existing for easier lookup by base command
|
|
202
|
-
# We prioritize non-"NEW" groups if duplicates exist
|
|
203
|
-
existing_map = {}
|
|
204
|
-
for group in existing_data:
|
|
205
|
-
gname = group["name"]
|
|
206
|
-
is_new_group = gname.startswith("[NEW]")
|
|
207
|
-
|
|
208
|
-
for cmd in group.get("commands", []):
|
|
209
|
-
base_name = get_base_name(cmd.get("value", ""))
|
|
210
|
-
|
|
211
|
-
# If we haven't seen this base yet, or if previous was from a [NEW] group
|
|
212
|
-
# but current isn't, overwrite to prioritize the permanent group
|
|
213
|
-
if base_name not in existing_map or (existing_map[base_name]["IsNewGroup"] and not is_new_group):
|
|
214
|
-
existing_map[base_name] = {
|
|
215
|
-
"Metadata": cmd,
|
|
216
|
-
"Group": gname,
|
|
217
|
-
"IsNewGroup": is_new_group
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
# 6. Merge & Classify
|
|
221
|
-
groups = {g["name"]: g for g in existing_data}
|
|
222
|
-
|
|
223
|
-
# Global Deduplication:
|
|
224
|
-
# Ensure each command only exists in its most specialized group (favor non-"NEW" groups)
|
|
225
|
-
for base_name, match_info in existing_map.items():
|
|
226
|
-
primary_group = match_info["Group"]
|
|
227
|
-
for gname in groups:
|
|
228
|
-
if gname != primary_group:
|
|
229
|
-
groups[gname]["commands"] = [
|
|
230
|
-
d for d in groups[gname]["commands"]
|
|
231
|
-
if get_base_name(d["value"]) != base_name
|
|
232
|
-
]
|
|
233
|
-
|
|
234
|
-
# Ensure staging groups exist
|
|
235
|
-
if "[NEW] GSD COMMANDS" not in groups:
|
|
236
|
-
groups["[NEW] GSD COMMANDS"] = {"name": "[NEW] GSD COMMANDS", "description": "Newly discovered slash commands.", "commands": []}
|
|
237
|
-
if "[NEW] GSD-TOOLS" not in groups:
|
|
238
|
-
groups["[NEW] GSD-TOOLS"] = {"name": "[NEW] GSD-TOOLS", "description": "Newly discovered gsd-tools commands.", "commands": []}
|
|
239
|
-
|
|
240
|
-
processed_names = set()
|
|
241
|
-
|
|
242
|
-
def merge_cmd(new_cmd, default_group):
|
|
243
|
-
full_val = new_cmd["value"]
|
|
244
|
-
base_name = get_base_name(full_val)
|
|
245
|
-
processed_names.add(base_name)
|
|
246
|
-
|
|
247
|
-
if base_name in existing_map:
|
|
248
|
-
# Update existing
|
|
249
|
-
old_cmd = existing_map[base_name]["Metadata"]
|
|
250
|
-
group_name = existing_map[base_name]["Group"]
|
|
251
|
-
|
|
252
|
-
# Preserve numeric prefix if label has one
|
|
253
|
-
label = new_cmd["label"]
|
|
254
|
-
match = re.match(r'^(\d+\.)\s*(.*)', old_cmd.get("label", ""))
|
|
255
|
-
if match:
|
|
256
|
-
label = f"{match.group(1)} {label}"
|
|
257
|
-
|
|
258
|
-
updated_cmd = {
|
|
259
|
-
"label": label,
|
|
260
|
-
"value": full_val, # Use new value (with potentially updated args)
|
|
261
|
-
"description": new_cmd["description"],
|
|
262
|
-
"GroupName": group_name,
|
|
263
|
-
"UsageCount": old_cmd.get("UsageCount", 0),
|
|
264
|
-
"FullDisplay": f"[{group_name.upper()}] {label} -> {full_val}",
|
|
265
|
-
"TabDisplay": f"{label} -> {full_val}"
|
|
266
|
-
}
|
|
267
|
-
# Put back in the right group
|
|
268
|
-
# Clear ANY other old versions just in case
|
|
269
|
-
groups[group_name]["commands"] = [
|
|
270
|
-
d for d in groups[group_name]["commands"]
|
|
271
|
-
if get_base_name(d["value"]) != base_name
|
|
272
|
-
]
|
|
273
|
-
groups[group_name]["commands"].append(updated_cmd)
|
|
274
|
-
else:
|
|
275
|
-
# Add to staged
|
|
276
|
-
new_entry = {
|
|
277
|
-
"label": new_cmd["label"],
|
|
278
|
-
"value": full_val,
|
|
279
|
-
"description": new_cmd["description"],
|
|
280
|
-
"GroupName": default_group,
|
|
281
|
-
"UsageCount": 0,
|
|
282
|
-
"FullDisplay": f"[{default_group.upper()}] {new_cmd['label']} -> {full_val}",
|
|
283
|
-
"TabDisplay": f"{new_cmd['label']} -> {full_val}"
|
|
284
|
-
}
|
|
285
|
-
groups[default_group]["commands"].append(new_entry)
|
|
286
|
-
|
|
287
|
-
for cmd in new_gsd_cmds:
|
|
288
|
-
merge_cmd(cmd, "[NEW] GSD COMMANDS")
|
|
289
|
-
|
|
290
|
-
for cmd in new_tool_cmds:
|
|
291
|
-
merge_cmd(cmd, "[NEW] GSD-TOOLS")
|
|
292
|
-
|
|
293
|
-
# Re-order commands in each group (numeric sort)
|
|
294
|
-
for gname in groups:
|
|
295
|
-
group = groups[gname]
|
|
296
|
-
group["commands"].sort(key=lambda x: x.get("label", ""))
|
|
297
|
-
|
|
298
|
-
# Convert groups back to list
|
|
299
|
-
# Preserve original group ordering
|
|
300
|
-
ordered_group_names = [g["name"] for g in existing_data]
|
|
301
|
-
for gname in groups:
|
|
302
|
-
if gname not in ordered_group_names:
|
|
303
|
-
ordered_group_names.append(gname)
|
|
304
|
-
|
|
305
|
-
final_output = [groups[name] for name in ordered_group_names if groups[name]["commands"]]
|
|
306
|
-
|
|
307
|
-
# 7. Write Result
|
|
308
|
-
with open(json_path, 'w', encoding='utf-8') as f:
|
|
309
|
-
json.dump(final_output, f, indent=2, ensure_ascii=False)
|
|
310
|
-
|
|
311
|
-
print(f"✨ Successfully synchronized {len(processed_names)} commands to {json_path}")
|
|
312
|
-
|
|
313
|
-
if __name__ == "__main__":
|
|
314
|
-
# Ensure backup dir exists
|
|
315
|
-
if not os.path.exists(CONFIG["backup_dir"]):
|
|
316
|
-
os.makedirs(CONFIG["backup_dir"])
|
|
317
|
-
sync()
|