@orchid-labs/pluxx 0.1.0 → 0.1.3

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.
Files changed (110) hide show
  1. package/README.md +110 -515
  2. package/bin/pluxx.js +19 -28
  3. package/dist/agents.d.ts +16 -0
  4. package/dist/agents.d.ts.map +1 -0
  5. package/dist/cli/agent.d.ts +69 -0
  6. package/dist/cli/agent.d.ts.map +1 -1
  7. package/dist/cli/doctor.d.ts +3 -0
  8. package/dist/cli/doctor.d.ts.map +1 -1
  9. package/dist/cli/entry.d.ts +2 -0
  10. package/dist/cli/entry.d.ts.map +1 -0
  11. package/dist/cli/eval.d.ts +22 -0
  12. package/dist/cli/eval.d.ts.map +1 -0
  13. package/dist/cli/index.d.ts +26 -3
  14. package/dist/cli/index.d.ts.map +1 -1
  15. package/dist/cli/index.js +21810 -0
  16. package/dist/cli/init-from-mcp.d.ts +34 -3
  17. package/dist/cli/init-from-mcp.d.ts.map +1 -1
  18. package/dist/cli/install.d.ts +3 -0
  19. package/dist/cli/install.d.ts.map +1 -1
  20. package/dist/cli/lint.d.ts +7 -1
  21. package/dist/cli/lint.d.ts.map +1 -1
  22. package/dist/cli/mcp-proxy.d.ts +10 -0
  23. package/dist/cli/mcp-proxy.d.ts.map +1 -0
  24. package/dist/cli/migrate.d.ts.map +1 -1
  25. package/dist/cli/primitive-summary.d.ts +14 -0
  26. package/dist/cli/primitive-summary.d.ts.map +1 -0
  27. package/dist/cli/prompt.d.ts +1 -1
  28. package/dist/cli/publish.d.ts +6 -1
  29. package/dist/cli/publish.d.ts.map +1 -1
  30. package/dist/cli/sync-from-mcp.d.ts.map +1 -1
  31. package/dist/cli/test.d.ts +2 -0
  32. package/dist/cli/test.d.ts.map +1 -1
  33. package/dist/cli/verify-install.d.ts +25 -0
  34. package/dist/cli/verify-install.d.ts.map +1 -0
  35. package/dist/commands.d.ts +10 -0
  36. package/dist/commands.d.ts.map +1 -0
  37. package/dist/compiler-intent.d.ts +165 -0
  38. package/dist/compiler-intent.d.ts.map +1 -0
  39. package/dist/config/load.d.ts.map +1 -1
  40. package/dist/delegation.d.ts +11 -0
  41. package/dist/delegation.d.ts.map +1 -0
  42. package/dist/generators/amp/index.d.ts.map +1 -1
  43. package/dist/generators/base.d.ts +5 -0
  44. package/dist/generators/base.d.ts.map +1 -1
  45. package/dist/generators/claude-code/index.d.ts +2 -0
  46. package/dist/generators/claude-code/index.d.ts.map +1 -1
  47. package/dist/generators/cline/index.d.ts.map +1 -1
  48. package/dist/generators/codex/index.d.ts +5 -0
  49. package/dist/generators/codex/index.d.ts.map +1 -1
  50. package/dist/generators/cursor/index.d.ts +1 -0
  51. package/dist/generators/cursor/index.d.ts.map +1 -1
  52. package/dist/generators/gemini-cli/index.d.ts.map +1 -1
  53. package/dist/generators/github-copilot/index.d.ts.map +1 -1
  54. package/dist/generators/opencode/index.d.ts +1 -0
  55. package/dist/generators/opencode/index.d.ts.map +1 -1
  56. package/dist/generators/openhands/index.d.ts.map +1 -1
  57. package/dist/generators/roo-code/index.d.ts.map +1 -1
  58. package/dist/generators/shared/claude-family.d.ts.map +1 -1
  59. package/dist/generators/warp/index.d.ts.map +1 -1
  60. package/dist/index.d.ts +4 -1
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +5464 -548
  63. package/dist/mcp/introspect.d.ts +43 -1
  64. package/dist/mcp/introspect.d.ts.map +1 -1
  65. package/dist/permissions.d.ts.map +1 -1
  66. package/dist/schema.d.ts +91 -42
  67. package/dist/schema.d.ts.map +1 -1
  68. package/dist/text-files.d.ts +5 -0
  69. package/dist/text-files.d.ts.map +1 -0
  70. package/dist/validation/platform-rules.d.ts +35 -1
  71. package/dist/validation/platform-rules.d.ts.map +1 -1
  72. package/package.json +16 -14
  73. package/src/cli/agent.ts +0 -1030
  74. package/src/cli/dev.ts +0 -112
  75. package/src/cli/doctor.ts +0 -588
  76. package/src/cli/index.ts +0 -2414
  77. package/src/cli/init-from-mcp.ts +0 -1611
  78. package/src/cli/install.ts +0 -698
  79. package/src/cli/lint.ts +0 -1219
  80. package/src/cli/migrate.ts +0 -614
  81. package/src/cli/prompt.ts +0 -82
  82. package/src/cli/publish.ts +0 -401
  83. package/src/cli/runtime.ts +0 -86
  84. package/src/cli/sync-from-mcp.ts +0 -563
  85. package/src/cli/test.ts +0 -134
  86. package/src/compatibility/matrix.ts +0 -149
  87. package/src/config/define.ts +0 -20
  88. package/src/config/load.ts +0 -74
  89. package/src/generators/amp/index.ts +0 -63
  90. package/src/generators/base.ts +0 -188
  91. package/src/generators/claude-code/index.ts +0 -29
  92. package/src/generators/cline/index.ts +0 -35
  93. package/src/generators/codex/index.ts +0 -120
  94. package/src/generators/cursor/index.ts +0 -158
  95. package/src/generators/gemini-cli/index.ts +0 -83
  96. package/src/generators/github-copilot/index.ts +0 -32
  97. package/src/generators/hooks-warning.ts +0 -51
  98. package/src/generators/index.ts +0 -71
  99. package/src/generators/opencode/index.ts +0 -526
  100. package/src/generators/openhands/index.ts +0 -32
  101. package/src/generators/roo-code/index.ts +0 -35
  102. package/src/generators/shared/claude-family.ts +0 -215
  103. package/src/generators/warp/index.ts +0 -32
  104. package/src/hook-events.ts +0 -33
  105. package/src/index.ts +0 -23
  106. package/src/mcp/introspect.ts +0 -834
  107. package/src/permissions.ts +0 -258
  108. package/src/schema.ts +0 -312
  109. package/src/user-config.ts +0 -177
  110. package/src/validation/platform-rules.ts +0 -565
@@ -1,149 +0,0 @@
1
- import type { TargetPlatform } from '../schema'
2
- import { PLATFORM_VALIDATION_RULES, type PlatformRules } from '../validation/platform-rules'
3
-
4
- export interface PlatformCompatibilityRow {
5
- platform: TargetPlatform
6
- status: 'Primary' | 'Beta'
7
- docsBasis: string
8
- manifest: string
9
- hooks: string
10
- mcp: string
11
- instructions: string
12
- verification: string
13
- }
14
-
15
- const ALL_TARGET_PLATFORMS: TargetPlatform[] = [
16
- 'claude-code',
17
- 'cursor',
18
- 'codex',
19
- 'opencode',
20
- 'github-copilot',
21
- 'openhands',
22
- 'warp',
23
- 'gemini-cli',
24
- 'roo-code',
25
- 'cline',
26
- 'amp',
27
- ]
28
-
29
- const PRIMARY_TARGETS = new Set<TargetPlatform>(['claude-code', 'cursor', 'codex', 'opencode'])
30
- const RELEASE_SMOKE_TARGETS = new Set<TargetPlatform>(['claude-code', 'cursor', 'codex', 'opencode'])
31
- const GENERATOR_FIXTURE_TARGETS = new Set<TargetPlatform>(ALL_TARGET_PLATFORMS)
32
-
33
- const INHERITED_RULES: Partial<Record<TargetPlatform, keyof typeof PLATFORM_VALIDATION_RULES>> = {
34
- 'github-copilot': 'claude-code',
35
- }
36
-
37
- function getRuleEntry(platform: TargetPlatform): {
38
- rules: PlatformRules
39
- docsBasis: string
40
- } {
41
- const directRule = PLATFORM_VALIDATION_RULES[platform as keyof typeof PLATFORM_VALIDATION_RULES]
42
- if (directRule) {
43
- return {
44
- rules: directRule,
45
- docsBasis: 'Official docs audited',
46
- }
47
- }
48
-
49
- const inheritedPlatform = INHERITED_RULES[platform]
50
- if (!inheritedPlatform) {
51
- throw new Error(`No platform rules available for ${platform}`)
52
- }
53
-
54
- return {
55
- rules: PLATFORM_VALIDATION_RULES[inheritedPlatform],
56
- docsBasis: `Inherits ${humanizePlatformName(inheritedPlatform)} packaging`,
57
- }
58
- }
59
-
60
- const PLATFORM_LABELS: Record<TargetPlatform, string> = {
61
- 'claude-code': 'Claude Code',
62
- cursor: 'Cursor',
63
- codex: 'Codex',
64
- opencode: 'OpenCode',
65
- 'github-copilot': 'GitHub Copilot',
66
- openhands: 'OpenHands',
67
- warp: 'Warp',
68
- 'gemini-cli': 'Gemini CLI',
69
- 'roo-code': 'Roo Code',
70
- cline: 'Cline',
71
- amp: 'AMP',
72
- }
73
-
74
- function humanizePlatformName(platform: TargetPlatform): string {
75
- return PLATFORM_LABELS[platform]
76
- }
77
-
78
- function formatFiles(files: string[], emptyLabel: string): string {
79
- return files.length > 0 ? files.join(', ') : emptyLabel
80
- }
81
-
82
- function getVerificationLabel(platform: TargetPlatform): string {
83
- const labels: string[] = []
84
-
85
- if (RELEASE_SMOKE_TARGETS.has(platform)) {
86
- labels.push('Release smoke')
87
- }
88
-
89
- if (GENERATOR_FIXTURE_TARGETS.has(platform)) {
90
- labels.push('Generator fixture')
91
- }
92
-
93
- return labels.join(' + ')
94
- }
95
-
96
- export function getPlatformCompatibilityMatrix(): PlatformCompatibilityRow[] {
97
- return ALL_TARGET_PLATFORMS.map((platform) => {
98
- const { rules, docsBasis } = getRuleEntry(platform)
99
-
100
- return {
101
- platform,
102
- status: PRIMARY_TARGETS.has(platform) ? 'Primary' : 'Beta',
103
- docsBasis,
104
- manifest: formatFiles(rules.manifest.files, 'No dedicated manifest'),
105
- hooks: rules.hooks.supported
106
- ? formatFiles(rules.hooks.files, 'Supported')
107
- : 'No documented standalone hook surface',
108
- mcp: formatFiles(rules.mcp.files, 'None documented'),
109
- instructions: formatFiles(rules.instructions.files, 'None documented'),
110
- verification: getVerificationLabel(platform),
111
- }
112
- })
113
- }
114
-
115
- export function renderCompatibilityMatrixMarkdown(): string {
116
- const rows = getPlatformCompatibilityMatrix()
117
-
118
- const lines = [
119
- '<!-- Generated by `bun scripts/generate-compatibility-matrix.ts`. Do not edit by hand. -->',
120
- '# Compatibility Matrix',
121
- '',
122
- 'This matrix is generated from the repo-owned platform rules and verification surface.',
123
- '',
124
- 'Verification labels:',
125
- '- `Release smoke`: exercised end to end by the real CLI against the real example plugins on the core four.',
126
- '- `Generator fixture`: covered by the repo build/test fixture layer.',
127
- '',
128
- '| Platform | Status | Docs Basis | Manifest | Hooks | MCP | Instructions | Verification |',
129
- '|----------|--------|------------|----------|-------|-----|--------------|--------------|',
130
- ]
131
-
132
- for (const row of rows) {
133
- lines.push(
134
- `| ${humanizePlatformName(row.platform)} | ${row.status} | ${row.docsBasis} | ${row.manifest} | ${row.hooks} | ${row.mcp} | ${row.instructions} | ${row.verification} |`,
135
- )
136
- }
137
-
138
- lines.push(
139
- '',
140
- '## Notes',
141
- '',
142
- '- The prime-time launch path remains Claude Code, Cursor, Codex, and OpenCode.',
143
- '- GitHub Copilot currently inherits the Claude Code packaging model in Pluxx rather than using its own separately audited plugin surface.',
144
- '- Beta platforms are generated and fixture-tested, but they do not yet have the same release-smoke coverage as the core four.',
145
- '',
146
- )
147
-
148
- return `${lines.join('\n')}\n`
149
- }
@@ -1,20 +0,0 @@
1
- import { type PluginConfig, PluginConfigSchema } from '../schema'
2
-
3
- /**
4
- * Define a plugin configuration with full type checking and validation.
5
- *
6
- * Usage in pluxx.config.ts:
7
- * ```ts
8
- * import { definePlugin } from 'pluxx'
9
- *
10
- * export default definePlugin({
11
- * name: 'my-plugin',
12
- * description: 'My awesome plugin',
13
- * author: { name: 'Me' },
14
- * // ...
15
- * })
16
- * ```
17
- */
18
- export function definePlugin(config: PluginConfig): PluginConfig {
19
- return PluginConfigSchema.parse(config)
20
- }
@@ -1,74 +0,0 @@
1
- import { resolve, extname, dirname } from 'path'
2
- import { existsSync } from 'fs'
3
- import { rm } from 'fs/promises'
4
- import { fileURLToPath, pathToFileURL } from 'url'
5
- import { PluginConfigSchema, type PluginConfig } from '../schema'
6
-
7
- export const CONFIG_FILES = [
8
- 'pluxx.config.ts',
9
- 'pluxx.config.js',
10
- 'pluxx.config.json',
11
- ]
12
-
13
- function getRuntimePackageEntry(): string {
14
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..')
15
- const distEntry = resolve(packageRoot, 'dist', 'index.js')
16
-
17
- if (existsSync(distEntry)) {
18
- return distEntry
19
- }
20
-
21
- return resolve(packageRoot, 'src', 'index.ts')
22
- }
23
-
24
- async function importConfigModule(filepath: string): Promise<unknown> {
25
- const ext = extname(filepath)
26
- const runtimeEntryUrl = pathToFileURL(getRuntimePackageEntry()).href
27
- const source = await Bun.file(filepath).text()
28
- const rewritten = source
29
- .replace(/from\s+(['"])pluxx\1/g, `from ${JSON.stringify(runtimeEntryUrl)}`)
30
- .replace(/import\s+(['"])pluxx\1/g, `import ${JSON.stringify(runtimeEntryUrl)}`)
31
-
32
- const transpiler = new Bun.Transpiler({
33
- loader: ext === '.ts' ? 'ts' : 'js',
34
- })
35
-
36
- const tempFile = resolve(
37
- dirname(filepath),
38
- `.pluxx-load-config-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`,
39
- )
40
- await Bun.write(tempFile, transpiler.transformSync(rewritten))
41
-
42
- try {
43
- const mod = await import(`${pathToFileURL(tempFile).href}?t=${Date.now()}`)
44
- return mod.default ?? mod
45
- } finally {
46
- await rm(tempFile, { force: true })
47
- }
48
- }
49
-
50
- /**
51
- * Load and validate a pluxx config from the given directory.
52
- */
53
- export async function loadConfig(dir: string = process.cwd()): Promise<PluginConfig> {
54
- for (const filename of CONFIG_FILES) {
55
- const filepath = resolve(dir, filename)
56
- if (!existsSync(filepath)) continue
57
-
58
- const ext = extname(filename)
59
-
60
- if (ext === '.ts' || ext === '.js') {
61
- const raw = await importConfigModule(filepath)
62
- return PluginConfigSchema.parse(raw)
63
- }
64
-
65
- if (ext === '.json') {
66
- const text = await Bun.file(filepath).text()
67
- return PluginConfigSchema.parse(JSON.parse(text))
68
- }
69
- }
70
-
71
- throw new Error(
72
- `No pluxx config found in ${dir}. Expected one of: ${CONFIG_FILES.join(', ')}`
73
- )
74
- }
@@ -1,63 +0,0 @@
1
- import { existsSync } from 'fs'
2
- import { Generator } from '../base'
3
- import { warnDroppedHookFields } from '../hooks-warning'
4
- import type { TargetPlatform } from '../../schema'
5
-
6
- /**
7
- * Amp uses AGENT.md for instructions, .amp/settings.json for hooks,
8
- * MCP config, and skills/ for skills. No plugin manifest.
9
- */
10
- export class AmpGenerator extends Generator {
11
- readonly platform: TargetPlatform = 'amp'
12
-
13
- async generate(): Promise<void> {
14
- await Promise.all([
15
- this.generateAgentMd(),
16
- this.generateSettings(),
17
- this.generateMcpConfig('mcp.json'),
18
- ])
19
-
20
- this.copySkills()
21
- this.copyScripts()
22
- }
23
-
24
- private async generateAgentMd(): Promise<void> {
25
- if (!this.config.instructions) return
26
- const srcPath = this.resolveConfigPath(this.config.instructions, 'instructions')
27
- if (!existsSync(srcPath)) return
28
-
29
- const content = await Bun.file(srcPath).text()
30
-
31
- const agentMd = [
32
- `# ${this.config.brand?.displayName ?? this.config.name}`,
33
- '',
34
- this.config.brand?.shortDescription ?? this.config.description,
35
- '',
36
- content,
37
- ].join('\n')
38
-
39
- await this.writeFile('AGENT.md', agentMd)
40
- }
41
-
42
- private async generateSettings(): Promise<void> {
43
- if (!this.config.hooks) return
44
-
45
- // Amp hooks use a post-execute format
46
- const hooks: Record<string, unknown[]> = {}
47
-
48
- for (const [event, entries] of Object.entries(this.config.hooks)) {
49
- if (!entries) continue
50
- warnDroppedHookFields(this.platform, event, entries)
51
- const commandEntries = entries.filter(entry => entry.type !== 'prompt' && entry.command)
52
- if (commandEntries.length === 0) continue
53
- hooks[event] = commandEntries.map(entry => ({
54
- type: 'post-execute',
55
- command: entry.command!.replace('${PLUGIN_ROOT}', '.'),
56
- ...(entry.timeout ? { timeout: entry.timeout } : {}),
57
- }))
58
- }
59
-
60
- await this.writeJson('.amp/settings.json', { hooks })
61
- }
62
-
63
- }
@@ -1,188 +0,0 @@
1
- import { resolve, join, relative } from 'path'
2
- import { mkdirSync, existsSync, cpSync } from 'fs'
3
- import type { PluginConfig, TargetPlatform, McpServer } from '../schema'
4
-
5
- type McpRemoteServer = Exclude<McpServer, { transport: 'stdio' }>
6
-
7
- interface McpConfigOptions {
8
- includeDefaultAuthHeaders?: boolean
9
- transformRemoteEntry?: (context: {
10
- name: string
11
- server: McpRemoteServer
12
- entry: Record<string, unknown>
13
- }) => Record<string, unknown>
14
- }
15
-
16
- export abstract class Generator {
17
- abstract readonly platform: TargetPlatform
18
-
19
- constructor(
20
- protected config: PluginConfig,
21
- protected rootDir: string,
22
- ) {}
23
-
24
- /** The output directory for this platform */
25
- get outDir(): string {
26
- return resolve(this.rootDir, this.config.outDir, this.platform)
27
- }
28
-
29
- /** Generate all platform-specific files */
30
- abstract generate(): Promise<void>
31
-
32
- /** Write a file to the output directory */
33
- protected async writeFile(relativePath: string, content: string): Promise<void> {
34
- const filepath = join(this.outDir, relativePath)
35
- const dir = filepath.substring(0, filepath.lastIndexOf('/'))
36
- mkdirSync(dir, { recursive: true })
37
- await Bun.write(filepath, content)
38
- }
39
-
40
- /** Write JSON to the output directory */
41
- protected async writeJson(relativePath: string, data: unknown): Promise<void> {
42
- await this.writeFile(relativePath, JSON.stringify(data, null, 2) + '\n')
43
- }
44
-
45
- /** Copy a directory from source to output */
46
- protected copyDir(srcRelative: string, destRelative: string, configKey: string): void {
47
- const src = this.resolveConfigPath(srcRelative, configKey)
48
- if (!existsSync(src)) return
49
- const dest = join(this.outDir, destRelative)
50
- mkdirSync(dest, { recursive: true })
51
- cpSync(src, dest, { recursive: true })
52
- }
53
-
54
- /** Resolve a user-configured path and ensure it stays within the project root. */
55
- protected resolveConfigPath(configPath: string, configKey: string): string {
56
- const resolvedPath = resolve(this.rootDir, configPath)
57
- const rel = relative(this.rootDir, resolvedPath)
58
- if (rel.startsWith('..')) {
59
- throw new Error(
60
- `${configKey} path "${configPath}" resolves outside the project root.`
61
- )
62
- }
63
- return resolvedPath
64
- }
65
-
66
- /** Copy skills directory, applying any platform-specific frontmatter */
67
- protected copySkills(): void {
68
- this.copyDir(this.config.skills, 'skills/', 'skills')
69
- }
70
-
71
- /** Copy commands directory if it exists */
72
- protected copyCommands(): void {
73
- if (this.config.commands) {
74
- this.copyDir(this.config.commands, 'commands/', 'commands')
75
- }
76
- }
77
-
78
- /** Copy agents directory if it exists */
79
- protected copyAgents(): void {
80
- if (this.config.agents) {
81
- this.copyDir(this.config.agents, 'agents/', 'agents')
82
- }
83
- }
84
-
85
- /** Copy scripts directory if it exists */
86
- protected copyScripts(): void {
87
- if (this.config.scripts) {
88
- this.copyDir(this.config.scripts, 'scripts/', 'scripts')
89
- }
90
- }
91
-
92
- /** Copy assets directory if it exists */
93
- protected copyAssets(): void {
94
- if (this.config.assets) {
95
- this.copyDir(this.config.assets, 'assets/', 'assets')
96
- }
97
- }
98
-
99
- /** Build canonical MCP server configs for target-specific output shaping. */
100
- protected buildMcpServers(options: McpConfigOptions = {}): Record<string, unknown> | undefined {
101
- if (!this.config.mcp) return undefined
102
-
103
- const {
104
- includeDefaultAuthHeaders = true,
105
- transformRemoteEntry,
106
- } = options
107
-
108
- const mcpServers: Record<string, unknown> = {}
109
-
110
- for (const [name, server] of Object.entries(this.config.mcp)) {
111
- if (server.transport === 'stdio') {
112
- mcpServers[name] = {
113
- command: server.command,
114
- args: server.args ?? [],
115
- env: server.env ?? {},
116
- }
117
- continue
118
- }
119
-
120
- const remoteServer: McpRemoteServer = server
121
-
122
- let entry: Record<string, unknown> = {
123
- url: remoteServer.url,
124
- }
125
-
126
- if (includeDefaultAuthHeaders) {
127
- const headers = this.getMcpAuthHeaders(remoteServer)
128
- if (headers) {
129
- entry.headers = headers
130
- }
131
- }
132
-
133
- if (transformRemoteEntry) {
134
- entry = transformRemoteEntry({ name, server: remoteServer, entry })
135
- }
136
-
137
- mcpServers[name] = entry
138
- }
139
-
140
- return mcpServers
141
- }
142
-
143
- protected getMcpAuthMode(): 'inline' | 'platform' {
144
- if (this.platform === 'claude-code') {
145
- return this.config.platforms?.['claude-code']?.mcpAuth ?? 'inline'
146
- }
147
-
148
- if (this.platform === 'cursor') {
149
- return this.config.platforms?.cursor?.mcpAuth ?? 'inline'
150
- }
151
-
152
- return 'inline'
153
- }
154
-
155
- /** Generate MCP config JSON in the common `{ mcpServers }` shape. */
156
- protected async generateMcpConfig(relativePath: string, options: McpConfigOptions = {}): Promise<void> {
157
- const mcpServers = this.buildMcpServers(options)
158
- if (!mcpServers) return
159
- await this.writeJson(relativePath, { mcpServers })
160
- }
161
-
162
- private getMcpAuthHeaders(server: McpRemoteServer): Record<string, string> | undefined {
163
- if (this.getMcpAuthMode() === 'platform' || server.auth?.type === 'platform') {
164
- return undefined
165
- }
166
-
167
- if (server.auth?.type === 'bearer' && server.auth.envVar) {
168
- return {
169
- Authorization: `Bearer ${this.getEnvVarReference(server.auth.envVar)}`,
170
- }
171
- }
172
-
173
- if (server.auth?.type === 'header' && server.auth.envVar) {
174
- return {
175
- [server.auth.headerName]: server.auth.headerTemplate.replace(
176
- '${value}',
177
- this.getEnvVarReference(server.auth.envVar),
178
- ),
179
- }
180
- }
181
-
182
- return undefined
183
- }
184
-
185
- private getEnvVarReference(envVar: string): string {
186
- return `\${${envVar}}`
187
- }
188
- }
@@ -1,29 +0,0 @@
1
- import { Generator } from '../base'
2
- import { generateClaudeFamilyOutputs } from '../shared/claude-family'
3
- import type { TargetPlatform } from '../../schema'
4
-
5
- export class ClaudeCodeGenerator extends Generator {
6
- readonly platform: TargetPlatform = 'claude-code'
7
-
8
- async generate(): Promise<void> {
9
- await generateClaudeFamilyOutputs({
10
- config: this.config,
11
- rootDir: this.rootDir,
12
- platform: this.platform,
13
- options: {
14
- manifestPath: '.claude-plugin/plugin.json',
15
- instructionsFile: 'CLAUDE.md',
16
- pluginRootVar: 'CLAUDE_PLUGIN_ROOT',
17
- includeStandardHooksManifest: false,
18
- },
19
- writeJson: (relativePath, data) => this.writeJson(relativePath, data),
20
- writeFile: (relativePath, content) => this.writeFile(relativePath, content),
21
- })
22
-
23
- this.copySkills()
24
- this.copyCommands()
25
- this.copyAgents()
26
- this.copyScripts()
27
- this.copyAssets()
28
- }
29
- }
@@ -1,35 +0,0 @@
1
- import { existsSync } from 'fs'
2
- import { Generator } from '../base'
3
- import type { TargetPlatform } from '../../schema'
4
-
5
- /**
6
- * Cline uses .clinerules for instructions, .cline/mcp.json for MCP config,
7
- * and .cline/skills/ for skills. No plugin manifest.
8
- */
9
- export class ClineGenerator extends Generator {
10
- readonly platform: TargetPlatform = 'cline'
11
-
12
- async generate(): Promise<void> {
13
- await Promise.all([
14
- this.generateMcpConfig('.cline/mcp.json'),
15
- this.generateRules(),
16
- ])
17
-
18
- this.copyClineSkills()
19
- this.copyScripts()
20
- }
21
-
22
- private async generateRules(): Promise<void> {
23
- if (!this.config.instructions) return
24
- const srcPath = this.resolveConfigPath(this.config.instructions, 'instructions')
25
- if (!existsSync(srcPath)) return
26
-
27
- const content = await Bun.file(srcPath).text()
28
- await this.writeFile('.clinerules', content)
29
- }
30
-
31
- /** Copy skills into .cline/skills/ instead of the default skills/ */
32
- private copyClineSkills(): void {
33
- this.copyDir(this.config.skills, '.cline/skills/', 'skills')
34
- }
35
- }
@@ -1,120 +0,0 @@
1
- import { existsSync } from 'fs'
2
- import { Generator } from '../base'
3
- import type { TargetPlatform } from '../../schema'
4
-
5
- export class CodexGenerator extends Generator {
6
- readonly platform: TargetPlatform = 'codex'
7
-
8
- async generate(): Promise<void> {
9
- await Promise.all([
10
- this.generateManifest(),
11
- this.generateMcpConfig('.mcp.json', {
12
- includeDefaultAuthHeaders: false,
13
- transformRemoteEntry: ({ name, server }) => {
14
- const entry: Record<string, unknown> = {
15
- url: server.url,
16
- }
17
-
18
- if (server.auth?.type === 'bearer' && server.auth.envVar) {
19
- entry.bearer_token_env_var = server.auth.envVar
20
- } else if (server.auth?.type === 'header' && server.auth.envVar) {
21
- const isBearerAuthorizationHeader =
22
- server.auth.headerName === 'Authorization'
23
- && server.auth.headerTemplate === 'Bearer ${value}'
24
-
25
- if (isBearerAuthorizationHeader) {
26
- entry.bearer_token_env_var = server.auth.envVar
27
- } else if (server.auth.headerTemplate === '${value}') {
28
- entry.env_http_headers = {
29
- [server.auth.headerName]: server.auth.envVar,
30
- }
31
- } else if (!server.auth.headerTemplate.includes('${value}')) {
32
- entry.http_headers = {
33
- [server.auth.headerName]: server.auth.headerTemplate,
34
- }
35
- } else {
36
- console.warn(
37
- `[pluxx] codex generator: MCP server "${name}" uses auth.type "header" with a templated header Codex cannot express exactly. `
38
- + 'Supported Codex auth outputs are bearer_token_env_var, env_http_headers, and http_headers; this header was omitted.'
39
- )
40
- }
41
- }
42
-
43
- return entry
44
- },
45
- }),
46
- this.generateAgentsMd(),
47
- ])
48
-
49
- this.copySkills()
50
- this.copyAgents()
51
- this.copyScripts()
52
- this.copyAssets()
53
- }
54
-
55
- private async generateManifest(): Promise<void> {
56
- const manifest: Record<string, unknown> = {
57
- name: this.config.name,
58
- version: this.config.version,
59
- description: this.config.description,
60
- author: this.config.author,
61
- }
62
-
63
- if (this.config.repository) manifest.homepage = this.config.repository
64
- if (this.config.repository) manifest.repository = this.config.repository
65
- if (this.config.license) manifest.license = this.config.license
66
- if (this.config.keywords) manifest.keywords = this.config.keywords
67
-
68
- manifest.skills = './skills/'
69
- if (this.config.mcp) manifest.mcpServers = './.mcp.json'
70
-
71
- // Codex supports rich interface metadata
72
- if (this.config.brand) {
73
- const iface: Record<string, unknown> = {
74
- displayName: this.config.brand.displayName,
75
- shortDescription: this.config.brand.shortDescription ?? this.config.description,
76
- category: this.config.brand.category,
77
- }
78
-
79
- if (this.config.brand.longDescription) {
80
- iface.longDescription = this.config.brand.longDescription
81
- }
82
- if (this.config.brand.color) {
83
- iface.brandColor = this.config.brand.color
84
- }
85
- if (this.config.brand.icon) {
86
- iface.composerIcon = this.config.brand.icon
87
- iface.logo = this.config.brand.icon
88
- }
89
- if (this.config.brand.defaultPrompts) {
90
- iface.defaultPrompt = this.config.brand.defaultPrompts
91
- }
92
- if (this.config.brand.websiteURL) {
93
- iface.websiteURL = this.config.brand.websiteURL
94
- }
95
- if (this.config.brand.screenshots) {
96
- iface.screenshots = this.config.brand.screenshots
97
- }
98
-
99
- // Merge Codex-specific interface overrides
100
- const codexOverrides = this.config.platforms?.codex?.interface
101
- if (codexOverrides) {
102
- Object.assign(iface, codexOverrides)
103
- }
104
-
105
- iface.developerName = this.config.author.name
106
-
107
- manifest.interface = iface
108
- }
109
-
110
- await this.writeJson('.codex-plugin/plugin.json', manifest)
111
- }
112
- private async generateAgentsMd(): Promise<void> {
113
- if (!this.config.instructions) return
114
- const srcPath = this.resolveConfigPath(this.config.instructions, 'instructions')
115
- if (!existsSync(srcPath)) return
116
-
117
- const content = await Bun.file(srcPath).text()
118
- await this.writeFile('AGENTS.md', content)
119
- }
120
- }