gm-qwen 2.0.380
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/.gitignore +14 -0
- package/CLAUDE.md +11 -0
- package/CONTRIBUTING.md +26 -0
- package/LICENSE +21 -0
- package/README.md +19 -0
- package/agents/gm.md +23 -0
- package/bin/plugkit +11 -0
- package/bin/plugkit-darwin-arm64 +0 -0
- package/bin/plugkit-darwin-x64 +0 -0
- package/bin/plugkit-linux-arm64 +0 -0
- package/bin/plugkit-linux-x64 +0 -0
- package/bin/plugkit-win32-arm64.exe +0 -0
- package/bin/plugkit-win32-x64.exe +0 -0
- package/bin/plugkit.exe +0 -0
- package/bin/plugkit.js +22 -0
- package/cli.js +49 -0
- package/gm.json +27 -0
- package/hooks/hooks.json +63 -0
- package/package.json +41 -0
- package/scripts/bootstrap.cmd +14 -0
- package/scripts/bootstrap.js +110 -0
- package/scripts/bootstrap.sh +28 -0
- package/scripts/postinstall-kilo.js +132 -0
- package/scripts/postinstall-oc.js +133 -0
- package/scripts/postinstall.js +137 -0
- package/scripts/run-hook.sh +12 -0
- package/scripts/watch-cascade.js +166 -0
- package/skills/browser/SKILL.md +170 -0
- package/skills/code-search/SKILL.md +57 -0
- package/skills/create-lang-plugin/SKILL.md +180 -0
- package/skills/gm/SKILL.md +138 -0
- package/skills/gm-complete/SKILL.md +107 -0
- package/skills/gm-emit/SKILL.md +111 -0
- package/skills/gm-execute/SKILL.md +133 -0
- package/skills/planning/SKILL.md +93 -0
- package/skills/ssh/SKILL.md +86 -0
- package/skills/update-docs/SKILL.md +113 -0
package/.gitignore
ADDED
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# CLAUDE
|
|
2
|
+
|
|
3
|
+
## Technical Notes
|
|
4
|
+
|
|
5
|
+
Hook response format: `{"decision":"allow|block","reason":"text"}` with exit code 0.
|
|
6
|
+
|
|
7
|
+
Tool names for this platform:
|
|
8
|
+
|
|
9
|
+
When filtering transcript history by sessionId, use: `if (sessionId && entry.sessionId === sessionId)`
|
|
10
|
+
|
|
11
|
+
Verification file `.gm-stop-verified` is auto-added to .gitignore and tracks session completion state.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Please ensure all code follows the conventions established in this project.
|
|
4
|
+
|
|
5
|
+
## Before Committing
|
|
6
|
+
|
|
7
|
+
Run the build to verify everything is working:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm run build plugforge-starter [output-dir]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Platform Conventions
|
|
14
|
+
|
|
15
|
+
- Each platform adapter in `platforms/` extends PlatformAdapter or CLIAdapter
|
|
16
|
+
- File generation logic goes in `createFileStructure()`
|
|
17
|
+
- Use TemplateBuilder methods for shared generation logic
|
|
18
|
+
- Skills are auto-discovered from plugforge-starter/skills/
|
|
19
|
+
|
|
20
|
+
## Testing
|
|
21
|
+
|
|
22
|
+
Build all 9 platform outputs:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
node cli.js plugforge-starter /tmp/test-build
|
|
26
|
+
```
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# gm-qwen for Qwen Code
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g gm-qwen
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Or install directly:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
qwen extensions install https://github.com/AnEntrypoint/gm-qwen
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Restart Qwen Code to activate.
|
|
16
|
+
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
Installs the gm state machine agent (PLAN→EXECUTE→EMIT→VERIFY→COMPLETE) into Qwen Code with full hook support, skills, and automated git enforcement.
|
package/agents/gm.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gm
|
|
3
|
+
description: Agent (not skill) - immutable programming state machine. Always invoke for all work coordination.
|
|
4
|
+
agent: true
|
|
5
|
+
enforce: critical
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# GM — Skill-First Orchestrator
|
|
9
|
+
|
|
10
|
+
**Invoke the `gm` skill immediately.** Use the Skill tool with `skill: "gm"`.
|
|
11
|
+
|
|
12
|
+
**CRITICAL: Skills are invoked via the Skill tool ONLY. Do NOT use the Agent tool to load skills. Skills are not agents. Use: `Skill tool` with `skill: "gm"` (or `"planning"`, `"gm-execute"`, `"gm-emit"`, `"gm-complete"`, `"update-docs"`). Using the Agent tool for skills is a violation.**
|
|
13
|
+
|
|
14
|
+
All work coordination, planning, execution, and verification happens through the skill tree:
|
|
15
|
+
- `gm` skill → `planning` skill → `gm-execute` skill → `gm-emit` skill → `gm-complete` skill → `update-docs` skill
|
|
16
|
+
|
|
17
|
+
All code execution uses `exec:<lang>` via the Bash tool — never direct `Bash(node ...)` or `Bash(npm ...)`.
|
|
18
|
+
|
|
19
|
+
To send stdin to a running background task: `exec:type` with task_id on line 1 and input on line 2.
|
|
20
|
+
|
|
21
|
+
Do not use `EnterPlanMode`. Do not run code directly via Bash. Invoke `gm` skill first.
|
|
22
|
+
|
|
23
|
+
Responses to the user must be two sentences maximum, only when the user needs to know something, and in plain conversational language — no file paths, filenames, symbols, or technical identifiers.
|
package/bin/plugkit
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/plugkit.exe
ADDED
|
Binary file
|
package/bin/plugkit.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const dir = __dirname;
|
|
8
|
+
const platform = os.platform();
|
|
9
|
+
const arch = os.arch();
|
|
10
|
+
|
|
11
|
+
let bin;
|
|
12
|
+
if (platform === 'win32') {
|
|
13
|
+
bin = path.join(dir, arch === 'arm64' ? 'plugkit-win32-arm64.exe' : 'plugkit-win32-x64.exe');
|
|
14
|
+
if (!require('fs').existsSync(bin)) bin = path.join(dir, 'plugkit.exe');
|
|
15
|
+
} else if (platform === 'darwin') {
|
|
16
|
+
bin = path.join(dir, arch === 'arm64' ? 'plugkit-darwin-arm64' : 'plugkit-darwin-x64');
|
|
17
|
+
} else {
|
|
18
|
+
bin = path.join(dir, arch === 'arm64' || arch === 'aarch64' ? 'plugkit-linux-arm64' : 'plugkit-linux-x64');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = spawnSync(bin, process.argv.slice(2), { stdio: 'inherit' });
|
|
22
|
+
process.exit(result.status ?? 1);
|
package/cli.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
7
|
+
const destDir = path.join(homeDir, '.qwen', 'extensions', 'gm-qwen');
|
|
8
|
+
|
|
9
|
+
const srcDir = __dirname;
|
|
10
|
+
const isUpgrade = fs.existsSync(destDir);
|
|
11
|
+
|
|
12
|
+
console.log(isUpgrade ? 'Upgrading gm-qwen...' : 'Installing gm-qwen...');
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
16
|
+
|
|
17
|
+
const filesToCopy = [["agents","agents"],["hooks","hooks"],["scripts","scripts"],["skills","skills"],["bin","bin"],["gm.json","gm.json"],["README.md","README.md"],["CLAUDE.md","CLAUDE.md"]];
|
|
18
|
+
|
|
19
|
+
function copyRecursive(src, dst) {
|
|
20
|
+
if (!fs.existsSync(src)) return;
|
|
21
|
+
if (fs.statSync(src).isDirectory()) {
|
|
22
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
23
|
+
fs.readdirSync(src).forEach(f => copyRecursive(path.join(src, f), path.join(dst, f)));
|
|
24
|
+
} else {
|
|
25
|
+
fs.copyFileSync(src, dst);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
filesToCopy.forEach(([src, dst]) => copyRecursive(path.join(srcDir, src), path.join(destDir, dst)));
|
|
30
|
+
|
|
31
|
+
const qwenExtJson = path.join(destDir, 'qwen-extension.json');
|
|
32
|
+
if (!fs.existsSync(qwenExtJson)) {
|
|
33
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(srcDir, 'package.json'), 'utf-8'));
|
|
34
|
+
fs.writeFileSync(qwenExtJson, JSON.stringify({
|
|
35
|
+
name: 'gm',
|
|
36
|
+
version: pkg.version,
|
|
37
|
+
description: pkg.description,
|
|
38
|
+
hooks: './hooks/hooks.json',
|
|
39
|
+
skills: './skills'
|
|
40
|
+
}, null, 2) + '\n');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const destPath = process.platform === 'win32' ? destDir.replace(/\\/g, '/') : destDir;
|
|
44
|
+
console.log(`✓ gm-qwen ${isUpgrade ? 'upgraded' : 'installed'} to ${destPath}`);
|
|
45
|
+
console.log('Restart Qwen Code to activate.');
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.error('Installation failed:', e.message);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
package/gm.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gm",
|
|
3
|
+
"version": "2.0.380",
|
|
4
|
+
"description": "State machine agent with hooks, skills, and automated git enforcement",
|
|
5
|
+
"author": "AnEntrypoint",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"claude-code",
|
|
9
|
+
"claude-plugin",
|
|
10
|
+
"wfgy",
|
|
11
|
+
"automation"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/AnEntrypoint/gm",
|
|
14
|
+
"agents": [
|
|
15
|
+
{
|
|
16
|
+
"name": "gm",
|
|
17
|
+
"description": "Agent (not skill) - immutable programming state machine. Always invoke for all work coordination."
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=16.0.0"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"plugkitVersion": "0.1.93"
|
|
27
|
+
}
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Hooks for gm Qwen Code extension",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"PreToolUse": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "*",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook pre-tool-use",
|
|
11
|
+
"timeout": 3600
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"SessionStart": [
|
|
17
|
+
{
|
|
18
|
+
"matcher": "*",
|
|
19
|
+
"hooks": [
|
|
20
|
+
{
|
|
21
|
+
"type": "command",
|
|
22
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook session-start",
|
|
23
|
+
"timeout": 180000
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"UserPromptSubmit": [
|
|
29
|
+
{
|
|
30
|
+
"matcher": "*",
|
|
31
|
+
"hooks": [
|
|
32
|
+
{
|
|
33
|
+
"type": "command",
|
|
34
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook prompt-submit",
|
|
35
|
+
"timeout": 60000
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"Stop": [
|
|
41
|
+
{
|
|
42
|
+
"matcher": "*",
|
|
43
|
+
"hooks": [
|
|
44
|
+
{
|
|
45
|
+
"type": "command",
|
|
46
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook stop",
|
|
47
|
+
"timeout": 300000
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"matcher": "*",
|
|
53
|
+
"hooks": [
|
|
54
|
+
{
|
|
55
|
+
"type": "command",
|
|
56
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook stop-git",
|
|
57
|
+
"timeout": 60000
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gm-qwen",
|
|
3
|
+
"version": "2.0.380",
|
|
4
|
+
"description": "State machine agent with hooks, skills, and automated git enforcement",
|
|
5
|
+
"author": "AnEntrypoint",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"qwen-code",
|
|
9
|
+
"agent",
|
|
10
|
+
"state-machine",
|
|
11
|
+
"automation",
|
|
12
|
+
"gm"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/AnEntrypoint/gm-qwen#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/AnEntrypoint/gm-qwen/issues"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=16.0.0"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"agents/",
|
|
26
|
+
"bin/",
|
|
27
|
+
"hooks/",
|
|
28
|
+
"scripts/",
|
|
29
|
+
"skills/",
|
|
30
|
+
"gm.json",
|
|
31
|
+
"cli.js",
|
|
32
|
+
"install.js",
|
|
33
|
+
"README.md",
|
|
34
|
+
"CLAUDE.md",
|
|
35
|
+
".gitignore",
|
|
36
|
+
"CONTRIBUTING.md"
|
|
37
|
+
],
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@qwen-code/qwen-code": "*"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
setlocal
|
|
3
|
+
set "PLUGKIT=%CLAUDE_PLUGIN_ROOT%\bin\plugkit.exe"
|
|
4
|
+
set "BINDIR=%CLAUDE_PLUGIN_ROOT%\bin"
|
|
5
|
+
set "VERFILE=%BINDIR%\.plugkit-version"
|
|
6
|
+
if not exist "%BINDIR%" mkdir "%BINDIR%"
|
|
7
|
+
if not exist "%PLUGKIT%" (
|
|
8
|
+
powershell -NoProfile -ExecutionPolicy Bypass -Command "$g=ConvertFrom-Json(Get-Content '%CLAUDE_PLUGIN_ROOT%\gm.json' -Raw);$v=$g.plugkitVersion;[Net.ServicePointManager]::SecurityProtocol='Tls12';$r=[Net.HttpWebRequest]::Create(\"https://github.com/AnEntrypoint/rs-plugkit/releases/download/v$v/plugkit-win32-x64.exe\");$r.Timeout=1000;$r.AllowAutoRedirect=$true;try{$rs=$r.GetResponse();$s=$rs.GetResponseStream();$f=[IO.File]::Create('%PLUGKIT%');$s.CopyTo($f);$f.Dispose();$rs.Dispose();Set-Content -NoNewline '%VERFILE%' $v}catch{}" 2>nul
|
|
9
|
+
)
|
|
10
|
+
if exist "%PLUGKIT%" (
|
|
11
|
+
powershell -NoProfile -ExecutionPolicy Bypass -Command "try{$p=Split-Path '%CLAUDE_PLUGIN_ROOT%';Get-ChildItem $p -Directory|Where-Object{$_.FullName -ne '%CLAUDE_PLUGIN_ROOT%'}|ForEach-Object{Remove-Item $_.FullName -Recurse -Force -ErrorAction SilentlyContinue}}catch{}" 2>nul
|
|
12
|
+
"%PLUGKIT%" bootstrap
|
|
13
|
+
)
|
|
14
|
+
endlocal
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const { execFileSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || process.env.CODEX_PLUGIN_ROOT;
|
|
9
|
+
if (!pluginRoot) process.exit(0);
|
|
10
|
+
|
|
11
|
+
const IS_WIN = process.platform === 'win32';
|
|
12
|
+
const binDir = path.join(pluginRoot, 'bin');
|
|
13
|
+
const binPath = path.join(binDir, IS_WIN ? 'plugkit.exe' : 'plugkit');
|
|
14
|
+
const pendingPath = binPath + '.pending';
|
|
15
|
+
const versionFile = path.join(binDir, '.plugkit-version');
|
|
16
|
+
const pendingVersionFile = pendingPath + '.version';
|
|
17
|
+
|
|
18
|
+
function getAssetName() {
|
|
19
|
+
const os = process.platform === 'win32' ? 'win32' : process.platform === 'darwin' ? 'darwin' : 'linux';
|
|
20
|
+
const cpu = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
21
|
+
const ext = process.platform === 'win32' ? '.exe' : '';
|
|
22
|
+
return `plugkit-${os}-${cpu}${ext}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function killDaemon() {
|
|
26
|
+
try {
|
|
27
|
+
execFileSync(binPath, ['runner', 'stop'], { timeout: 5000, stdio: 'ignore' });
|
|
28
|
+
} catch {}
|
|
29
|
+
if (IS_WIN) {
|
|
30
|
+
try { execFileSync('taskkill', ['/F', '/IM', 'plugkit.exe'], { timeout: 3000, stdio: 'ignore' }); } catch {}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function applyPending() {
|
|
35
|
+
if (!fs.existsSync(pendingPath)) return;
|
|
36
|
+
killDaemon();
|
|
37
|
+
const oldPath = binPath + '.old';
|
|
38
|
+
try { fs.unlinkSync(oldPath); } catch {}
|
|
39
|
+
try { fs.renameSync(binPath, oldPath); } catch {}
|
|
40
|
+
try {
|
|
41
|
+
fs.renameSync(pendingPath, binPath);
|
|
42
|
+
if (fs.existsSync(pendingVersionFile)) {
|
|
43
|
+
try { fs.renameSync(pendingVersionFile, versionFile); } catch {}
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
try { if (!fs.existsSync(binPath)) fs.renameSync(oldPath, binPath); } catch {}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
applyPending();
|
|
51
|
+
|
|
52
|
+
function getRequiredVersion() {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(fs.readFileSync(path.join(pluginRoot, 'gm.json'), 'utf8')).plugkitVersion || null;
|
|
55
|
+
} catch { return null; }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getCurrentVersion() {
|
|
59
|
+
try { return fs.readFileSync(versionFile, 'utf8').trim() || null; } catch { return null; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const required = getRequiredVersion();
|
|
63
|
+
const current = getCurrentVersion();
|
|
64
|
+
if (current && current === required) process.exit(0);
|
|
65
|
+
|
|
66
|
+
function download(version, dest, cb) {
|
|
67
|
+
const asset = getAssetName();
|
|
68
|
+
const urlPath = `/AnEntrypoint/rs-plugkit/releases/download/v${version}/${asset}`;
|
|
69
|
+
if (!fs.existsSync(path.dirname(dest))) fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
70
|
+
const follow = (url) => {
|
|
71
|
+
const mod = url.startsWith('https') ? https : require('http');
|
|
72
|
+
const opts = { ...require('url').parse(url), headers: { 'User-Agent': 'gm-bootstrap' } };
|
|
73
|
+
mod.get(opts, res => {
|
|
74
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) return follow(res.headers.location);
|
|
75
|
+
if (res.statusCode !== 200) return cb(new Error(`HTTP ${res.statusCode}`));
|
|
76
|
+
const chunks = [];
|
|
77
|
+
res.on('data', c => chunks.push(c));
|
|
78
|
+
res.on('end', () => {
|
|
79
|
+
try {
|
|
80
|
+
fs.writeFileSync(dest, Buffer.concat(chunks));
|
|
81
|
+
try { fs.chmodSync(dest, 0o755); } catch {}
|
|
82
|
+
cb(null);
|
|
83
|
+
} catch (e) { cb(e); }
|
|
84
|
+
});
|
|
85
|
+
}).on('error', cb);
|
|
86
|
+
};
|
|
87
|
+
follow(`https://github.com${urlPath}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
killDaemon();
|
|
91
|
+
|
|
92
|
+
download(required, binPath, (err) => {
|
|
93
|
+
if (err && err.code === 'EBUSY') {
|
|
94
|
+
download(required, pendingPath, (err2) => {
|
|
95
|
+
if (err2) {
|
|
96
|
+
process.stderr.write(`bootstrap: pending failed: ${err2.message}\n`);
|
|
97
|
+
process.exit(fs.existsSync(binPath) ? 0 : 1);
|
|
98
|
+
}
|
|
99
|
+
try { fs.writeFileSync(pendingVersionFile, required); } catch {}
|
|
100
|
+
process.exit(0);
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (err) {
|
|
105
|
+
process.stderr.write(`bootstrap: ${err.message}\n`);
|
|
106
|
+
process.exit(fs.existsSync(binPath) ? 0 : 1);
|
|
107
|
+
}
|
|
108
|
+
try { fs.writeFileSync(versionFile, required); } catch {}
|
|
109
|
+
process.exit(0);
|
|
110
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT}}"
|
|
4
|
+
[ -z "$PLUGIN_ROOT" ] && exit 0
|
|
5
|
+
BINDIR="$PLUGIN_ROOT/bin"
|
|
6
|
+
VERFILE="$BINDIR/.plugkit-version"
|
|
7
|
+
IS_WIN=0
|
|
8
|
+
case "$(uname -s 2>/dev/null)" in MINGW*|CYGWIN*|MSYS*) IS_WIN=1;; esac
|
|
9
|
+
[ -f /proc/version ] && grep -qi microsoft /proc/version 2>/dev/null && IS_WIN=1
|
|
10
|
+
if [ $IS_WIN -eq 1 ]; then EXT=".exe"; OS="win32"; else EXT=""; OS="$(uname -s | tr '[:upper:]' '[:lower:]')"; fi
|
|
11
|
+
case "$(uname -m 2>/dev/null)" in arm64|aarch64) ARCH="arm64";; *) ARCH="x64";; esac
|
|
12
|
+
ASSET="plugkit-${OS}-${ARCH}${EXT}"
|
|
13
|
+
PLUGKIT="$BINDIR/plugkit${EXT}"
|
|
14
|
+
mkdir -p "$BINDIR"
|
|
15
|
+
if [ ! -f "$PLUGKIT" ]; then
|
|
16
|
+
VER="$(node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('$PLUGIN_ROOT/gm.json','utf8')).plugkitVersion)" 2>/dev/null || python3 -c "import json,sys;d=json.load(open('$PLUGIN_ROOT/gm.json'));sys.stdout.write(d['plugkitVersion'])" 2>/dev/null || python -c "import json,sys;d=json.load(open('$PLUGIN_ROOT/gm.json'));sys.stdout.write(d['plugkitVersion'])" 2>/dev/null || sed -n 's/.*"plugkitVersion"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$PLUGIN_ROOT/gm.json" | head -1)"
|
|
17
|
+
[ -z "$VER" ] && exit 0
|
|
18
|
+
URL="https://github.com/AnEntrypoint/rs-plugkit/releases/download/v${VER}/${ASSET}"
|
|
19
|
+
curl -fsSL --location --max-time 30 "$URL" -o "$PLUGKIT" 2>/dev/null || exit 0
|
|
20
|
+
chmod +x "$PLUGKIT" 2>/dev/null || true
|
|
21
|
+
printf '%s' "$VER" > "$VERFILE"
|
|
22
|
+
fi
|
|
23
|
+
PARENT="$(dirname "$PLUGIN_ROOT")"
|
|
24
|
+
for d in "$PARENT"/*/; do
|
|
25
|
+
[ "$d" = "$PLUGIN_ROOT/" ] && continue
|
|
26
|
+
rm -rf "$d" 2>/dev/null || true
|
|
27
|
+
done
|
|
28
|
+
"$PLUGKIT" bootstrap
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
|
|
7
|
+
function isInsideNodeModules() {
|
|
8
|
+
return __dirname.includes(path.sep + 'node_modules' + path.sep);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getProjectRoot() {
|
|
12
|
+
if (!isInsideNodeModules()) return null;
|
|
13
|
+
let current = __dirname;
|
|
14
|
+
while (current !== path.dirname(current)) {
|
|
15
|
+
current = path.dirname(current);
|
|
16
|
+
if (path.basename(current) === 'node_modules') return path.dirname(current);
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function safeCopyFile(src, dst) {
|
|
22
|
+
try {
|
|
23
|
+
const dstDir = path.dirname(dst);
|
|
24
|
+
if (!fs.existsSync(dstDir)) fs.mkdirSync(dstDir, { recursive: true });
|
|
25
|
+
fs.writeFileSync(dst, fs.readFileSync(src));
|
|
26
|
+
return true;
|
|
27
|
+
} catch { return false; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function safeCopyDirectory(src, dst) {
|
|
31
|
+
try {
|
|
32
|
+
if (!fs.existsSync(src)) return false;
|
|
33
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
34
|
+
fs.readdirSync(src, { withFileTypes: true }).forEach(entry => {
|
|
35
|
+
const srcPath = path.join(src, entry.name);
|
|
36
|
+
const dstPath = path.join(dst, entry.name);
|
|
37
|
+
if (entry.isDirectory()) safeCopyDirectory(srcPath, dstPath);
|
|
38
|
+
else if (entry.isFile()) safeCopyFile(srcPath, dstPath);
|
|
39
|
+
});
|
|
40
|
+
return true;
|
|
41
|
+
} catch { return false; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function updateGitignore(projectRoot) {
|
|
45
|
+
try {
|
|
46
|
+
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
47
|
+
const entry = '.gm-stop-verified';
|
|
48
|
+
let content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
|
|
49
|
+
if (content.includes(entry)) return;
|
|
50
|
+
if (content && !content.endsWith('\n')) content += '\n';
|
|
51
|
+
fs.writeFileSync(gitignorePath, content + entry + '\n', 'utf-8');
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getRequiredVersion(sourceDir) {
|
|
56
|
+
try {
|
|
57
|
+
const gm = JSON.parse(fs.readFileSync(path.join(sourceDir, 'gm.json'), 'utf-8'));
|
|
58
|
+
return gm.plugkitVersion || null;
|
|
59
|
+
} catch { return null; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getInstalledVersion(binPath) {
|
|
63
|
+
try {
|
|
64
|
+
const { spawnSync } = require('child_process');
|
|
65
|
+
const r = spawnSync(binPath, ['--version'], { encoding: 'utf8', timeout: 5000 });
|
|
66
|
+
const m = (r.stdout || '').trim().match(/(\d+\.\d+\.\d+)/);
|
|
67
|
+
return m ? m[1] : null;
|
|
68
|
+
} catch { return null; }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function downloadBin(version, dest, callback) {
|
|
72
|
+
const IS_WIN = process.platform === 'win32';
|
|
73
|
+
const asset = IS_WIN ? 'plugkit.exe' : 'plugkit';
|
|
74
|
+
const urlPath = version
|
|
75
|
+
? `/AnEntrypoint/rs-plugkit/releases/download/v${version}/${asset}`
|
|
76
|
+
: `/AnEntrypoint/rs-plugkit/releases/latest/download/${asset}`;
|
|
77
|
+
const destDir = path.dirname(dest);
|
|
78
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
79
|
+
const follow = (url) => {
|
|
80
|
+
const mod = url.startsWith('https') ? https : require('http');
|
|
81
|
+
const opts = { ...require('url').parse(url), headers: { 'User-Agent': 'gm-postinstall' } };
|
|
82
|
+
mod.get(opts, res => {
|
|
83
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) return follow(res.headers.location);
|
|
84
|
+
if (res.statusCode !== 200) return callback(new Error(`HTTP ${res.statusCode}`));
|
|
85
|
+
const chunks = [];
|
|
86
|
+
res.on('data', c => chunks.push(c));
|
|
87
|
+
res.on('end', () => { try { fs.writeFileSync(dest, Buffer.concat(chunks)); try { fs.chmodSync(dest, 0o755); } catch {} callback(null); } catch (e) { callback(e); } });
|
|
88
|
+
}).on('error', callback);
|
|
89
|
+
};
|
|
90
|
+
follow(`https://github.com${urlPath}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function install() {
|
|
94
|
+
if (!isInsideNodeModules()) return;
|
|
95
|
+
const projectRoot = getProjectRoot();
|
|
96
|
+
if (!projectRoot) return;
|
|
97
|
+
const kiloDir = path.join(projectRoot, '.config', 'kilo');
|
|
98
|
+
const sourceDir = path.dirname(__dirname);
|
|
99
|
+
|
|
100
|
+
safeCopyDirectory(path.join(sourceDir, 'agents'), path.join(kiloDir, 'agents'));
|
|
101
|
+
safeCopyDirectory(path.join(sourceDir, 'hooks'), path.join(kiloDir, 'hooks'));
|
|
102
|
+
safeCopyDirectory(path.join(sourceDir, 'skills'), path.join(kiloDir, 'skills'));
|
|
103
|
+
safeCopyFile(path.join(sourceDir, 'kilocode.json'), path.join(kiloDir, 'kilocode.json'));
|
|
104
|
+
safeCopyFile(path.join(sourceDir, '.mcp.json'), path.join(kiloDir, '.mcp.json'));
|
|
105
|
+
safeCopyFile(path.join(sourceDir, 'gm.mjs'), path.join(kiloDir, 'gm.mjs'));
|
|
106
|
+
safeCopyFile(path.join(sourceDir, 'index.mjs'), path.join(kiloDir, 'index.mjs'));
|
|
107
|
+
safeCopyFile(path.join(sourceDir, 'README.md'), path.join(kiloDir, 'README.md'));
|
|
108
|
+
safeCopyFile(path.join(sourceDir, 'LICENSE'), path.join(kiloDir, 'LICENSE'));
|
|
109
|
+
safeCopyFile(path.join(sourceDir, 'CONTRIBUTING.md'), path.join(kiloDir, 'CONTRIBUTING.md'));
|
|
110
|
+
safeCopyFile(path.join(sourceDir, '.gitignore'), path.join(kiloDir, '.gitignore'));
|
|
111
|
+
safeCopyFile(path.join(sourceDir, '.editorconfig'), path.join(kiloDir, '.editorconfig'));
|
|
112
|
+
|
|
113
|
+
const pluginDir = path.join(kiloDir, 'plugin');
|
|
114
|
+
if (!fs.existsSync(pluginDir)) fs.mkdirSync(pluginDir, { recursive: true });
|
|
115
|
+
const gmMjsSrc = path.join(sourceDir, 'gm.mjs');
|
|
116
|
+
if (fs.existsSync(gmMjsSrc)) safeCopyFile(gmMjsSrc, path.join(pluginDir, 'gm.mjs'));
|
|
117
|
+
fs.writeFileSync(path.join(pluginDir, 'index.js'), "export { default } from './gm.mjs';\n", 'utf-8');
|
|
118
|
+
|
|
119
|
+
updateGitignore(projectRoot);
|
|
120
|
+
|
|
121
|
+
const IS_WIN = process.platform === 'win32';
|
|
122
|
+
const binDest = path.join(kiloDir, 'hooks', 'bin', IS_WIN ? 'plugkit.exe' : 'plugkit');
|
|
123
|
+
const requiredVersion = getRequiredVersion(sourceDir);
|
|
124
|
+
const installedVersion = fs.existsSync(binDest) ? getInstalledVersion(binDest) : null;
|
|
125
|
+
if (!installedVersion || (requiredVersion && installedVersion !== requiredVersion)) {
|
|
126
|
+
downloadBin(requiredVersion, binDest, (err) => {
|
|
127
|
+
if (err) process.stderr.write('plugkit download failed: ' + err.message + '\n');
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
install();
|