@a5c-ai/extension-mux 5.0.1-staging.0cf58b544cb8
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 +58 -0
- package/dist/binTemplates.d.ts +7 -0
- package/dist/binTemplates.d.ts.map +1 -0
- package/dist/binTemplates.js +292 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +299 -0
- package/dist/compiler.d.ts +15 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +118 -0
- package/dist/diff.d.ts +9 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +183 -0
- package/dist/emit.d.ts +3 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +42 -0
- package/dist/hookRegistration.d.ts +8 -0
- package/dist/hookRegistration.d.ts.map +1 -0
- package/dist/hookRegistration.js +9 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/init.d.ts +17 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +200 -0
- package/dist/installInstructions.d.ts +3 -0
- package/dist/installInstructions.d.ts.map +1 -0
- package/dist/installInstructions.js +150 -0
- package/dist/installSharedGenerator.d.ts +3 -0
- package/dist/installSharedGenerator.d.ts.map +1 -0
- package/dist/installSharedGenerator.js +229 -0
- package/dist/manifestGenerators.d.ts +10 -0
- package/dist/manifestGenerators.d.ts.map +1 -0
- package/dist/manifestGenerators.js +11 -0
- package/dist/marketplaceGenerator.d.ts +3 -0
- package/dist/marketplaceGenerator.d.ts.map +1 -0
- package/dist/marketplaceGenerator.js +46 -0
- package/dist/proxiedHookTemplates.d.ts +10 -0
- package/dist/proxiedHookTemplates.d.ts.map +1 -0
- package/dist/proxiedHookTemplates.js +122 -0
- package/dist/resolve.d.ts +3 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +106 -0
- package/dist/schema.d.ts +231 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +340 -0
- package/dist/sdkConfig.d.ts +20 -0
- package/dist/sdkConfig.d.ts.map +1 -0
- package/dist/sdkConfig.js +41 -0
- package/dist/targets/adapters/base.d.ts +10 -0
- package/dist/targets/adapters/base.d.ts.map +1 -0
- package/dist/targets/adapters/base.js +16 -0
- package/dist/targets/adapters/claude-code.d.ts +9 -0
- package/dist/targets/adapters/claude-code.d.ts.map +1 -0
- package/dist/targets/adapters/claude-code.js +83 -0
- package/dist/targets/adapters/codex.d.ts +13 -0
- package/dist/targets/adapters/codex.d.ts.map +1 -0
- package/dist/targets/adapters/codex.js +103 -0
- package/dist/targets/adapters/cursor.d.ts +9 -0
- package/dist/targets/adapters/cursor.d.ts.map +1 -0
- package/dist/targets/adapters/cursor.js +57 -0
- package/dist/targets/adapters/gemini.d.ts +9 -0
- package/dist/targets/adapters/gemini.d.ts.map +1 -0
- package/dist/targets/adapters/gemini.js +86 -0
- package/dist/targets/adapters/github-copilot.d.ts +9 -0
- package/dist/targets/adapters/github-copilot.d.ts.map +1 -0
- package/dist/targets/adapters/github-copilot.js +61 -0
- package/dist/targets/adapters/hermes.d.ts +13 -0
- package/dist/targets/adapters/hermes.d.ts.map +1 -0
- package/dist/targets/adapters/hermes.js +96 -0
- package/dist/targets/adapters/hooks-utils.d.ts +12 -0
- package/dist/targets/adapters/hooks-utils.d.ts.map +1 -0
- package/dist/targets/adapters/hooks-utils.js +60 -0
- package/dist/targets/adapters/index.d.ts +27 -0
- package/dist/targets/adapters/index.d.ts.map +1 -0
- package/dist/targets/adapters/index.js +64 -0
- package/dist/targets/adapters/interface.d.ts +8 -0
- package/dist/targets/adapters/interface.d.ts.map +1 -0
- package/dist/targets/adapters/interface.js +2 -0
- package/dist/targets/adapters/oh-my-pi.d.ts +11 -0
- package/dist/targets/adapters/oh-my-pi.d.ts.map +1 -0
- package/dist/targets/adapters/oh-my-pi.js +88 -0
- package/dist/targets/adapters/openclaw.d.ts +14 -0
- package/dist/targets/adapters/openclaw.d.ts.map +1 -0
- package/dist/targets/adapters/openclaw.js +165 -0
- package/dist/targets/adapters/opencode.d.ts +10 -0
- package/dist/targets/adapters/opencode.d.ts.map +1 -0
- package/dist/targets/adapters/opencode.js +93 -0
- package/dist/targets/adapters/pi.d.ts +11 -0
- package/dist/targets/adapters/pi.d.ts.map +1 -0
- package/dist/targets/adapters/pi.js +90 -0
- package/dist/targets/index.d.ts +7 -0
- package/dist/targets/index.d.ts.map +1 -0
- package/dist/targets/index.js +77 -0
- package/dist/transform.d.ts +4 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +243 -0
- package/dist/transformEmitters.d.ts +8 -0
- package/dist/transformEmitters.d.ts.map +1 -0
- package/dist/transformEmitters.js +340 -0
- package/dist/transformHelpers.d.ts +13 -0
- package/dist/transformHelpers.d.ts.map +1 -0
- package/dist/transformHelpers.js +239 -0
- package/dist/types.d.ts +204 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +42 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +187 -0
- package/dist/validate.d.ts +3 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +188 -0
- package/dist/verify.d.ts +6 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +294 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @a5c-ai/extension-mux
|
|
2
|
+
|
|
3
|
+
Cross-harness plugin compiler for converting a unified `plugin.json` source tree into harness-specific plugin packages.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @a5c-ai/extension-mux
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
CLI usage:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @a5c-ai/extension-mux extension-mux --help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This package ships the built compiler in `dist/` and this package README for npm auditability.
|
|
18
|
+
|
|
19
|
+
## CLI Surface
|
|
20
|
+
|
|
21
|
+
The current public CLI commands are:
|
|
22
|
+
|
|
23
|
+
- `compile --target <name|all> --output <dir>` to emit target plugin surfaces
|
|
24
|
+
- `validate --source <dir>` to validate a unified plugin directory without writing output
|
|
25
|
+
- `init --name <name> [--template <minimal|full|hooks-only>] [--output <dir>]` to scaffold a valid unified plugin source tree
|
|
26
|
+
- `list-targets` to print the supported target registry
|
|
27
|
+
|
|
28
|
+
The `diff` command is still reserved and currently exits with a not-implemented error. The supported targets are `claude-code`, `codex`, `cursor`, `gemini`, `github-copilot`, `pi`, `oh-my-pi`, `opencode`, and `openclaw`.
|
|
29
|
+
|
|
30
|
+
## API Surface
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {
|
|
34
|
+
compile,
|
|
35
|
+
compileAll,
|
|
36
|
+
validateDirectory,
|
|
37
|
+
validateSchema,
|
|
38
|
+
} from "@a5c-ai/extension-mux";
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The package exports the compiler pipeline and related types:
|
|
42
|
+
|
|
43
|
+
- manifest schema and package types
|
|
44
|
+
- directory validation, target resolution, transform, emit, and verify helpers
|
|
45
|
+
- target registry accessors and compilation entrypoints
|
|
46
|
+
|
|
47
|
+
## Validation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm run build --workspace=@a5c-ai/extension-mux
|
|
51
|
+
npm run test --workspace=@a5c-ai/extension-mux
|
|
52
|
+
npm run verify:metadata
|
|
53
|
+
npm pack --json --dry-run --workspace=@a5c-ai/extension-mux
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Release Expectations
|
|
57
|
+
|
|
58
|
+
`@a5c-ai/extension-mux` is published from the central release workflows. Keep this README aligned with the actual command set and compiler exports, and keep `package.json#files` limited to the built compiler plus package documentation.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { A5cPluginManifest, TargetProfile } from './types.js';
|
|
2
|
+
export declare function generateCliBinScript(manifest: A5cPluginManifest, targetProfile: TargetProfile): string;
|
|
3
|
+
export declare function generateInstallScript(_manifest: A5cPluginManifest, targetProfile: TargetProfile): string;
|
|
4
|
+
declare function generateGeminiInstallScript(manifest: A5cPluginManifest): string;
|
|
5
|
+
export declare function generateUninstallScript(_manifest: A5cPluginManifest, targetProfile: TargetProfile): string;
|
|
6
|
+
export { generateGeminiInstallScript as _generateGeminiInstallScript };
|
|
7
|
+
//# sourceMappingURL=binTemplates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binTemplates.d.ts","sourceRoot":"","sources":["../src/binTemplates.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAUnE,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,GAC3B,MAAM,CAqGR;AAED,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,iBAAiB,EAC5B,aAAa,EAAE,aAAa,GAC3B,MAAM,CA2ER;AAED,iBAAS,2BAA2B,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CA8BxE;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,iBAAiB,EAC5B,aAAa,EAAE,aAAa,GAC3B,MAAM,CA4ER;AAED,OAAO,EAAE,2BAA2B,IAAI,4BAA4B,EAAE,CAAC"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// CLI bin script templates for targets with npm distribution
|
|
2
|
+
import { resolveTargetCliName } from './sdkConfig.js';
|
|
3
|
+
function getExt(targetProfile) {
|
|
4
|
+
return targetProfile.packageMetadata?.binScriptExt ?? '.js';
|
|
5
|
+
}
|
|
6
|
+
function getCliName(manifest, targetProfile) {
|
|
7
|
+
return resolveTargetCliName(manifest, targetProfile);
|
|
8
|
+
}
|
|
9
|
+
export function generateCliBinScript(manifest, targetProfile) {
|
|
10
|
+
const cliName = getCliName(manifest, targetProfile);
|
|
11
|
+
const ext = getExt(targetProfile);
|
|
12
|
+
return `#!/usr/bin/env node
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { spawnSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
19
|
+
let shared;
|
|
20
|
+
try { shared = require('./install-shared'); } catch {}
|
|
21
|
+
|
|
22
|
+
function printUsage() {
|
|
23
|
+
console.error([
|
|
24
|
+
'Usage:',
|
|
25
|
+
' ${cliName} install [--global]',
|
|
26
|
+
' ${cliName} install --workspace [path]',
|
|
27
|
+
' ${cliName} uninstall',
|
|
28
|
+
].join('\\n'));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseInstallArgs(argv) {
|
|
32
|
+
let scope = 'global';
|
|
33
|
+
let workspace = null;
|
|
34
|
+
const passthrough = [];
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
37
|
+
const arg = argv[i];
|
|
38
|
+
if (arg === '--global') {
|
|
39
|
+
scope = 'global';
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (arg === '--workspace') {
|
|
43
|
+
scope = 'workspace';
|
|
44
|
+
const next = argv[i + 1];
|
|
45
|
+
if (next && !next.startsWith('-')) {
|
|
46
|
+
workspace = path.resolve(next);
|
|
47
|
+
i += 1;
|
|
48
|
+
} else {
|
|
49
|
+
workspace = process.cwd();
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
passthrough.push(arg);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { scope, workspace, passthrough };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function runNodeScript(scriptPath, args, extraEnv = {}) {
|
|
60
|
+
const result = spawnSync(process.execPath, [scriptPath, ...args], {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
stdio: 'inherit',
|
|
63
|
+
env: { ...process.env, ...extraEnv },
|
|
64
|
+
});
|
|
65
|
+
process.exitCode = result.status ?? 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function main() {
|
|
69
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
70
|
+
if (!command || command === '--help' || command === '-h' || command === 'help') {
|
|
71
|
+
printUsage();
|
|
72
|
+
process.exitCode = command ? 0 : 1;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (command === 'install') {
|
|
77
|
+
if (shared && typeof shared.harnessCliRoute === 'function' && shared.harnessCliRoute(rest, PACKAGE_ROOT, runNodeScript)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const parsed = parseInstallArgs(rest);
|
|
81
|
+
if (parsed.scope === 'workspace') {
|
|
82
|
+
const args = [];
|
|
83
|
+
if (parsed.workspace) {
|
|
84
|
+
args.push('--workspace', parsed.workspace);
|
|
85
|
+
}
|
|
86
|
+
args.push(...parsed.passthrough);
|
|
87
|
+
runNodeScript(
|
|
88
|
+
path.join(PACKAGE_ROOT, 'scripts', 'team-install${ext}'),
|
|
89
|
+
args,
|
|
90
|
+
{ PLUGIN_PACKAGE_ROOT: PACKAGE_ROOT },
|
|
91
|
+
);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
runNodeScript(path.join(PACKAGE_ROOT, 'bin', 'install${ext}'), parsed.passthrough);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (command === 'uninstall') {
|
|
99
|
+
runNodeScript(path.join(PACKAGE_ROOT, 'bin', 'uninstall${ext}'), rest);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
printUsage();
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main();
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
export function generateInstallScript(_manifest, targetProfile) {
|
|
111
|
+
if (targetProfile.componentSupport?.agents === 'native' && targetProfile.packageMetadata?.installLifecycle === 'plugin-scripts') {
|
|
112
|
+
return `#!/usr/bin/env node
|
|
113
|
+
'use strict';
|
|
114
|
+
|
|
115
|
+
const path = require('path');
|
|
116
|
+
const shared = require('./install-shared');
|
|
117
|
+
|
|
118
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
119
|
+
|
|
120
|
+
function main() {
|
|
121
|
+
const pluginRoot = shared.getHomePluginRoot();
|
|
122
|
+
const marketplacePath = shared.getHomeMarketplacePath();
|
|
123
|
+
|
|
124
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Installing plugin to \${pluginRoot}\`);
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
shared.copyPluginBundle(PACKAGE_ROOT, pluginRoot);
|
|
128
|
+
shared.ensureMarketplaceEntry(marketplacePath, pluginRoot);
|
|
129
|
+
if (typeof shared.registerCopilotPlugin === 'function') {
|
|
130
|
+
shared.registerCopilotPlugin(pluginRoot);
|
|
131
|
+
}
|
|
132
|
+
if (typeof shared.installCopilotSurface === 'function' && typeof shared.getCopilotHome === 'function') {
|
|
133
|
+
shared.installCopilotSurface(PACKAGE_ROOT, shared.getCopilotHome());
|
|
134
|
+
}
|
|
135
|
+
if (typeof shared.warnWindowsHooks === 'function') {
|
|
136
|
+
shared.warnWindowsHooks();
|
|
137
|
+
}
|
|
138
|
+
if (typeof shared.harnessInstall === 'function') {
|
|
139
|
+
shared.harnessInstall(PACKAGE_ROOT, pluginRoot);
|
|
140
|
+
}
|
|
141
|
+
shared.runPostInstall && shared.runPostInstall(pluginRoot);
|
|
142
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Installation complete!\`);
|
|
143
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Restart your IDE/CLI to pick up the plugin.\`);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error(\`[\${shared.PLUGIN_NAME}] Failed to install: \${err.message}\`);
|
|
146
|
+
process.exitCode = 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
main();
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
return `#!/usr/bin/env node
|
|
154
|
+
'use strict';
|
|
155
|
+
|
|
156
|
+
const path = require('path');
|
|
157
|
+
const shared = require('./install-shared');
|
|
158
|
+
|
|
159
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
160
|
+
|
|
161
|
+
function main() {
|
|
162
|
+
const pluginRoot = shared.getHomePluginRoot();
|
|
163
|
+
const marketplacePath = shared.getHomeMarketplacePath();
|
|
164
|
+
|
|
165
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Installing plugin to \${pluginRoot}\`);
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
shared.copyPluginBundle(PACKAGE_ROOT, pluginRoot);
|
|
169
|
+
shared.ensureMarketplaceEntry(marketplacePath, pluginRoot);
|
|
170
|
+
if (typeof shared.harnessInstall === 'function') {
|
|
171
|
+
shared.harnessInstall(PACKAGE_ROOT, pluginRoot);
|
|
172
|
+
}
|
|
173
|
+
shared.runPostInstall && shared.runPostInstall(pluginRoot);
|
|
174
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Installation complete!\`);
|
|
175
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Restart your IDE/CLI to pick up the plugin.\`);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
console.error(\`[\${shared.PLUGIN_NAME}] Failed to install: \${err.message}\`);
|
|
178
|
+
process.exitCode = 1;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main();
|
|
183
|
+
`;
|
|
184
|
+
}
|
|
185
|
+
function generateGeminiInstallScript(manifest) {
|
|
186
|
+
const cliName = resolveTargetCliName(manifest, { name: 'gemini' });
|
|
187
|
+
return `#!/usr/bin/env node
|
|
188
|
+
'use strict';
|
|
189
|
+
|
|
190
|
+
const path = require('path');
|
|
191
|
+
const { spawnSync } = require('child_process');
|
|
192
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
193
|
+
|
|
194
|
+
function main() {
|
|
195
|
+
console.log('[${cliName}] Installing extension...');
|
|
196
|
+
const result = spawnSync('gemini', ['extensions', 'install', PACKAGE_ROOT], {
|
|
197
|
+
stdio: 'inherit', timeout: 60000
|
|
198
|
+
});
|
|
199
|
+
if (result.status === 0) {
|
|
200
|
+
console.log('[${cliName}] Extension installed via Gemini CLI.');
|
|
201
|
+
} else {
|
|
202
|
+
const linkResult = spawnSync('gemini', ['extensions', 'link', PACKAGE_ROOT], {
|
|
203
|
+
stdio: 'inherit', timeout: 60000
|
|
204
|
+
});
|
|
205
|
+
if (linkResult.status === 0) {
|
|
206
|
+
console.log('[${cliName}] Extension linked via Gemini CLI.');
|
|
207
|
+
} else {
|
|
208
|
+
console.error('[${cliName}] Gemini CLI not available. Install manually: gemini extensions install ' + PACKAGE_ROOT);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
main();
|
|
214
|
+
`;
|
|
215
|
+
}
|
|
216
|
+
export function generateUninstallScript(_manifest, targetProfile) {
|
|
217
|
+
if (targetProfile.componentSupport?.agents === 'native' && targetProfile.packageMetadata?.installLifecycle === 'plugin-scripts') {
|
|
218
|
+
return `#!/usr/bin/env node
|
|
219
|
+
'use strict';
|
|
220
|
+
|
|
221
|
+
const path = require('path');
|
|
222
|
+
const fs = require('fs');
|
|
223
|
+
const shared = require('./install-shared');
|
|
224
|
+
|
|
225
|
+
function main() {
|
|
226
|
+
const pluginRoot = shared.getHomePluginRoot();
|
|
227
|
+
const marketplacePath = typeof shared.getHomeMarketplacePath === 'function'
|
|
228
|
+
? shared.getHomeMarketplacePath()
|
|
229
|
+
: null;
|
|
230
|
+
const copilotHome = typeof shared.getCopilotHome === 'function'
|
|
231
|
+
? shared.getCopilotHome()
|
|
232
|
+
: null;
|
|
233
|
+
|
|
234
|
+
if (!fs.existsSync(pluginRoot)) {
|
|
235
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Plugin not installed at \${pluginRoot}\`);
|
|
236
|
+
} else {
|
|
237
|
+
try {
|
|
238
|
+
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
239
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Uninstalled from \${pluginRoot}\`);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
console.error(\`[\${shared.PLUGIN_NAME}] Failed to uninstall: \${err.message}\`);
|
|
242
|
+
process.exitCode = 1;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
if (typeof shared.deregisterCopilotPlugin === 'function') {
|
|
249
|
+
shared.deregisterCopilotPlugin(pluginRoot);
|
|
250
|
+
}
|
|
251
|
+
if (copilotHome && typeof shared.removeManagedHooks === 'function') {
|
|
252
|
+
shared.removeManagedHooks(copilotHome);
|
|
253
|
+
}
|
|
254
|
+
if (marketplacePath && typeof shared.removeMarketplaceEntry === 'function') {
|
|
255
|
+
shared.removeMarketplaceEntry(marketplacePath);
|
|
256
|
+
}
|
|
257
|
+
} catch (err) {
|
|
258
|
+
console.error(\`[\${shared.PLUGIN_NAME}] Failed to clean up uninstall state: \${err.message}\`);
|
|
259
|
+
process.exitCode = 1;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
main();
|
|
264
|
+
`;
|
|
265
|
+
}
|
|
266
|
+
return `#!/usr/bin/env node
|
|
267
|
+
'use strict';
|
|
268
|
+
|
|
269
|
+
const fs = require('fs');
|
|
270
|
+
const shared = require('./install-shared');
|
|
271
|
+
|
|
272
|
+
function main() {
|
|
273
|
+
const pluginRoot = shared.getHomePluginRoot();
|
|
274
|
+
|
|
275
|
+
if (!fs.existsSync(pluginRoot)) {
|
|
276
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Plugin not installed at \${pluginRoot}\`);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
282
|
+
console.log(\`[\${shared.PLUGIN_NAME}] Uninstalled from \${pluginRoot}\`);
|
|
283
|
+
} catch (err) {
|
|
284
|
+
console.error(\`[\${shared.PLUGIN_NAME}] Failed to uninstall: \${err.message}\`);
|
|
285
|
+
process.exitCode = 1;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
main();
|
|
290
|
+
`;
|
|
291
|
+
}
|
|
292
|
+
export { generateGeminiInstallScript as _generateGeminiInstallScript };
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAaA,UAAU,KAAK;IACb,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAyRD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAiB,GAAG,MAAM,CAkCpE"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CLI for extension-mux compiler
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as process from 'process';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { compile, compileAll } from './compiler.js';
|
|
7
|
+
import { diffTarget, formatDiffResult } from './diff.js';
|
|
8
|
+
import { INIT_TEMPLATES, scaffoldPlugin } from './init.js';
|
|
9
|
+
import { validate } from './validate.js';
|
|
10
|
+
import { getAllTargets } from './targets/index.js';
|
|
11
|
+
const defaultIo = {
|
|
12
|
+
stdout: (message) => console.log(message),
|
|
13
|
+
stderr: (message) => console.error(message),
|
|
14
|
+
};
|
|
15
|
+
function showUsage(io) {
|
|
16
|
+
io.stdout(`
|
|
17
|
+
extension-mux - Cross-harness plugin compiler for AI coding agents
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
extension-mux compile --target <name|all> --output <dir> [options]
|
|
21
|
+
extension-mux validate [options]
|
|
22
|
+
extension-mux diff --target <name> --existing <dir> [options]
|
|
23
|
+
extension-mux init --name <name> [options]
|
|
24
|
+
extension-mux list-targets [options]
|
|
25
|
+
|
|
26
|
+
Commands:
|
|
27
|
+
compile Compile UPF source to target format(s)
|
|
28
|
+
validate Validate UPF source without compiling
|
|
29
|
+
diff Compare compiled output against existing directory
|
|
30
|
+
init Scaffold a new UPF plugin
|
|
31
|
+
list-targets List available compilation targets
|
|
32
|
+
|
|
33
|
+
Options:
|
|
34
|
+
--source <dir> UPF source directory (default: current directory)
|
|
35
|
+
--target <name> Target harness name or "all"
|
|
36
|
+
--output <dir> Output directory
|
|
37
|
+
--existing <dir> Path to existing plugin directory (for diff)
|
|
38
|
+
--name <name> Plugin name (for init)
|
|
39
|
+
--template <name> Template to use: ${INIT_TEMPLATES.join(', ')}
|
|
40
|
+
--verify Run verification checks after compilation
|
|
41
|
+
--dry-run Show what would be emitted without writing files
|
|
42
|
+
--json Output structured JSON result
|
|
43
|
+
--verbose Verbose logging
|
|
44
|
+
--help, -h Show this help message
|
|
45
|
+
|
|
46
|
+
Valid targets:
|
|
47
|
+
claude-code, codex, cursor, gemini, github-copilot,
|
|
48
|
+
pi, oh-my-pi, opencode, openclaw
|
|
49
|
+
`);
|
|
50
|
+
}
|
|
51
|
+
function parseArgs(args) {
|
|
52
|
+
const parsed = {};
|
|
53
|
+
for (let i = 0; i < args.length; i++) {
|
|
54
|
+
const arg = args[i];
|
|
55
|
+
if (arg.startsWith('--')) {
|
|
56
|
+
const key = arg.slice(2);
|
|
57
|
+
if (key === 'verify' ||
|
|
58
|
+
key === 'dry-run' ||
|
|
59
|
+
key === 'json' ||
|
|
60
|
+
key === 'verbose' ||
|
|
61
|
+
key === 'help') {
|
|
62
|
+
parsed[key] = true;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const value = args[i + 1];
|
|
66
|
+
if (value && !value.startsWith('--')) {
|
|
67
|
+
parsed[key] = value;
|
|
68
|
+
i++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (arg === '-h') {
|
|
73
|
+
parsed.help = true;
|
|
74
|
+
}
|
|
75
|
+
else if (!parsed.command) {
|
|
76
|
+
parsed.command = arg;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return parsed;
|
|
80
|
+
}
|
|
81
|
+
function printDiagnostics(diagnostics, verbose = false) {
|
|
82
|
+
const errors = diagnostics.filter((d) => d.level === 'error');
|
|
83
|
+
const warnings = diagnostics.filter((d) => d.level === 'warning');
|
|
84
|
+
const infos = diagnostics.filter((d) => d.level === 'info');
|
|
85
|
+
for (const diag of errors) {
|
|
86
|
+
console.error(`\x1b[31m[ERROR]\x1b[0m ${diag.message}`);
|
|
87
|
+
if (diag.source)
|
|
88
|
+
console.error(` Source: ${diag.source}`);
|
|
89
|
+
if (diag.suggestion)
|
|
90
|
+
console.error(` Suggestion: ${diag.suggestion}`);
|
|
91
|
+
}
|
|
92
|
+
for (const diag of warnings) {
|
|
93
|
+
console.warn(`\x1b[33m[WARNING]\x1b[0m ${diag.message}`);
|
|
94
|
+
if (diag.source && verbose)
|
|
95
|
+
console.warn(` Source: ${diag.source}`);
|
|
96
|
+
}
|
|
97
|
+
if (verbose) {
|
|
98
|
+
for (const diag of infos) {
|
|
99
|
+
console.log(`\x1b[36m[INFO]\x1b[0m ${diag.message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (errors.length > 0 || warnings.length > 0 || infos.length > 0) {
|
|
103
|
+
console.log(`\nSummary: ${errors.length} error(s), ${warnings.length} warning(s), ${infos.length} info`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function runCompile(parsed, io) {
|
|
107
|
+
const source = parsed.source || process.cwd();
|
|
108
|
+
const target = parsed.target;
|
|
109
|
+
const output = parsed.output;
|
|
110
|
+
const dryRun = parsed['dry-run'];
|
|
111
|
+
const verifyOutput = parsed.verify;
|
|
112
|
+
const jsonOutput = parsed.json;
|
|
113
|
+
const verbose = parsed.verbose;
|
|
114
|
+
if (!target || !output) {
|
|
115
|
+
io.stderr('Error: --target and --output are required');
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
if (target === 'all') {
|
|
119
|
+
const results = compileAll(source, output, { dryRun, verifyOutput });
|
|
120
|
+
if (jsonOutput) {
|
|
121
|
+
io.stdout(JSON.stringify(results, null, 2));
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
for (const result of results) {
|
|
125
|
+
io.stdout(`\n=== Target: ${result.target} ===`);
|
|
126
|
+
io.stdout(`Status: ${result.status}`);
|
|
127
|
+
io.stdout(`Output: ${result.outputDir}`);
|
|
128
|
+
io.stdout(`Emitted files: ${result.emittedFiles.length}`);
|
|
129
|
+
printDiagnostics(result.diagnostics, verbose);
|
|
130
|
+
}
|
|
131
|
+
const failed = results.filter((r) => r.status === 'error').length;
|
|
132
|
+
const warnings = results.filter((r) => r.status === 'warning').length;
|
|
133
|
+
const success = results.filter((r) => r.status === 'success').length;
|
|
134
|
+
io.stdout(`\n=== Overall ===\n${success} succeeded, ${warnings} with warnings, ${failed} failed`);
|
|
135
|
+
}
|
|
136
|
+
const hasErrors = results.some((r) => r.status === 'error');
|
|
137
|
+
return hasErrors ? 1 : 0;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const result = compile({
|
|
141
|
+
source,
|
|
142
|
+
target,
|
|
143
|
+
output,
|
|
144
|
+
dryRun,
|
|
145
|
+
verifyOutput,
|
|
146
|
+
});
|
|
147
|
+
if (jsonOutput) {
|
|
148
|
+
io.stdout(JSON.stringify(result, null, 2));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
io.stdout(`Target: ${result.target}`);
|
|
152
|
+
io.stdout(`Status: ${result.status}`);
|
|
153
|
+
io.stdout(`Output: ${result.outputDir}`);
|
|
154
|
+
io.stdout(`Emitted files: ${result.emittedFiles.length}`);
|
|
155
|
+
if (result.verificationChecklist.length > 0 && verbose) {
|
|
156
|
+
io.stdout('\nVerification:');
|
|
157
|
+
for (const check of result.verificationChecklist) {
|
|
158
|
+
io.stdout(` ${check}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
printDiagnostics(result.diagnostics, verbose);
|
|
162
|
+
}
|
|
163
|
+
return result.status === 'error' ? 1 : 0;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function runValidate(parsed, io) {
|
|
167
|
+
const source = parsed.source || process.cwd();
|
|
168
|
+
const jsonOutput = parsed.json;
|
|
169
|
+
const verbose = parsed.verbose;
|
|
170
|
+
const result = validate(source);
|
|
171
|
+
if (jsonOutput) {
|
|
172
|
+
io.stdout(JSON.stringify(result, null, 2));
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
io.stdout(`Valid: ${result.valid}`);
|
|
176
|
+
printDiagnostics(result.diagnostics, verbose);
|
|
177
|
+
}
|
|
178
|
+
return result.valid ? 0 : 1;
|
|
179
|
+
}
|
|
180
|
+
function runListTargets(parsed, io) {
|
|
181
|
+
const jsonOutput = parsed.json;
|
|
182
|
+
const targets = getAllTargets();
|
|
183
|
+
if (jsonOutput) {
|
|
184
|
+
io.stdout(JSON.stringify(targets, null, 2));
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
io.stdout('Available targets:');
|
|
188
|
+
for (const target of targets) {
|
|
189
|
+
io.stdout(` - ${target}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
function runDiff(parsed, io) {
|
|
195
|
+
const source = parsed.source || process.cwd();
|
|
196
|
+
const target = parsed.target;
|
|
197
|
+
const existing = parsed.existing;
|
|
198
|
+
const jsonOutput = parsed.json;
|
|
199
|
+
const verbose = parsed.verbose;
|
|
200
|
+
if (!target || !existing) {
|
|
201
|
+
io.stderr('Error: --target and --existing are required');
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
if (target === 'all') {
|
|
205
|
+
io.stderr('Error: diff currently supports a single target; pass a specific --target name');
|
|
206
|
+
return 1;
|
|
207
|
+
}
|
|
208
|
+
const result = diffTarget({ source, target, existing });
|
|
209
|
+
if (jsonOutput) {
|
|
210
|
+
io.stdout(JSON.stringify(result, null, 2));
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
io.stdout(formatDiffResult(result));
|
|
214
|
+
if (result.diagnostics.length > 0) {
|
|
215
|
+
printDiagnostics(result.diagnostics, verbose);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return result.status === 'match' ? 0 : 1;
|
|
219
|
+
}
|
|
220
|
+
function runInit(parsed, io) {
|
|
221
|
+
const name = parsed.name;
|
|
222
|
+
const template = parsed.template;
|
|
223
|
+
const output = parsed.output ?? process.cwd();
|
|
224
|
+
const dryRun = parsed['dry-run'];
|
|
225
|
+
const jsonOutput = parsed.json;
|
|
226
|
+
if (!name) {
|
|
227
|
+
io.stderr('Error: --name is required');
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
const result = scaffoldPlugin({
|
|
231
|
+
name,
|
|
232
|
+
template: template,
|
|
233
|
+
output,
|
|
234
|
+
dryRun,
|
|
235
|
+
});
|
|
236
|
+
if (jsonOutput) {
|
|
237
|
+
io.stdout(JSON.stringify(result, null, 2));
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
io.stdout(`Scaffolded template: ${result.template}`);
|
|
241
|
+
io.stdout(`Output: ${result.outputDir}`);
|
|
242
|
+
io.stdout(`Files: ${result.writtenFiles.length}`);
|
|
243
|
+
if (parsed.verbose) {
|
|
244
|
+
for (const file of result.writtenFiles) {
|
|
245
|
+
io.stdout(` - ${file}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (dryRun) {
|
|
249
|
+
io.stdout('Dry run only. No files were written.');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return 0;
|
|
253
|
+
}
|
|
254
|
+
export function runCli(args, io = defaultIo) {
|
|
255
|
+
const parsed = parseArgs(args);
|
|
256
|
+
if (parsed.help || !parsed.command) {
|
|
257
|
+
showUsage(io);
|
|
258
|
+
return 0;
|
|
259
|
+
}
|
|
260
|
+
const command = parsed.command;
|
|
261
|
+
try {
|
|
262
|
+
switch (command) {
|
|
263
|
+
case 'compile':
|
|
264
|
+
return runCompile(parsed, io);
|
|
265
|
+
case 'validate':
|
|
266
|
+
return runValidate(parsed, io);
|
|
267
|
+
case 'list-targets':
|
|
268
|
+
return runListTargets(parsed, io);
|
|
269
|
+
case 'diff':
|
|
270
|
+
return runDiff(parsed, io);
|
|
271
|
+
case 'init':
|
|
272
|
+
return runInit(parsed, io);
|
|
273
|
+
default:
|
|
274
|
+
io.stderr(`Unknown command: ${command}`);
|
|
275
|
+
showUsage(io);
|
|
276
|
+
return 1;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
io.stderr(`Fatal error: ${error.message}`);
|
|
281
|
+
if (parsed.verbose) {
|
|
282
|
+
io.stderr(error.stack ?? '');
|
|
283
|
+
}
|
|
284
|
+
return 1;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function isExecutedDirectly() {
|
|
288
|
+
const entryPoint = process.argv[1];
|
|
289
|
+
if (!entryPoint) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
return path.resolve(entryPoint) === fileURLToPath(import.meta.url);
|
|
293
|
+
}
|
|
294
|
+
function main() {
|
|
295
|
+
process.exit(runCli(process.argv.slice(2)));
|
|
296
|
+
}
|
|
297
|
+
if (isExecutedDirectly()) {
|
|
298
|
+
main();
|
|
299
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CompilationResult } from './types.js';
|
|
2
|
+
export interface CompileOptions {
|
|
3
|
+
source: string;
|
|
4
|
+
target: string;
|
|
5
|
+
output: string;
|
|
6
|
+
outputBaseDir?: string;
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
verifyOutput?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function compile(options: CompileOptions): CompilationResult;
|
|
11
|
+
export declare function compileAll(source: string, outputBaseDir: string, options?: {
|
|
12
|
+
dryRun?: boolean;
|
|
13
|
+
verifyOutput?: boolean;
|
|
14
|
+
}): CompilationResult[];
|
|
15
|
+
//# sourceMappingURL=compiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AASpD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAiHlE;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACzD,iBAAiB,EAAE,CAgBrB"}
|