@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,158 +0,0 @@
1
- import { existsSync } from 'fs'
2
- import { Generator } from '../base'
3
- import type { TargetPlatform } from '../../schema'
4
- import { buildGeneratedPermissionHookScript } from '../../permissions'
5
-
6
- export class CursorGenerator extends Generator {
7
- readonly platform: TargetPlatform = 'cursor'
8
-
9
- async generate(): Promise<void> {
10
- await Promise.all([
11
- this.generateManifest(),
12
- this.generateMcpConfig('mcp.json'),
13
- this.generateHooks(),
14
- this.generateRules(),
15
- this.generateAgentsMd(),
16
- ])
17
-
18
- this.copySkills()
19
- this.copyCommands()
20
- this.copyAgents()
21
- this.copyScripts()
22
- this.copyAssets()
23
- }
24
-
25
- private async generateManifest(): Promise<void> {
26
- const manifest: Record<string, unknown> = {
27
- name: this.config.name,
28
- description: this.config.description,
29
- version: this.config.version,
30
- author: this.config.author,
31
- }
32
-
33
- if (this.config.repository) manifest.repository = this.config.repository
34
- if (this.config.license) manifest.license = this.config.license
35
- if (this.config.keywords) manifest.keywords = this.config.keywords
36
- if (this.config.brand?.websiteURL) manifest.homepage = this.config.brand.websiteURL
37
- if (this.config.brand?.icon) manifest.logo = this.config.brand.icon
38
-
39
- manifest.skills = './skills/'
40
- if (this.config.commands) manifest.commands = './commands/'
41
- if (this.config.agents) manifest.agents = './agents/'
42
- if (this.config.platforms?.cursor?.rules?.length) manifest.rules = './rules/'
43
- if (this.config.hooks || this.config.permissions) manifest.hooks = './hooks/hooks.json'
44
- if (this.config.mcp) manifest.mcpServers = './mcp.json'
45
-
46
- await this.writeJson('.cursor-plugin/plugin.json', manifest)
47
- }
48
-
49
- private async generateHooks(): Promise<void> {
50
- const permissionScript = buildGeneratedPermissionHookScript(this.config.permissions)
51
- if (!this.config.hooks && !permissionScript) return
52
- const usesPlatformManagedAuth = this.config.platforms?.cursor?.mcpAuth === 'platform'
53
-
54
- // Cursor hooks format matches the canonical format closely
55
- const hooks: Record<string, unknown[]> = {}
56
-
57
- if (permissionScript) {
58
- await this.writeFile('hooks/pluxx-permissions.mjs', permissionScript)
59
- hooks.preToolUse = [{
60
- command: 'node ./hooks/pluxx-permissions.mjs cursor-pretool',
61
- }]
62
- hooks.beforeShellExecution = [{
63
- command: 'node ./hooks/pluxx-permissions.mjs cursor-shell',
64
- }]
65
- hooks.beforeReadFile = [{
66
- command: 'node ./hooks/pluxx-permissions.mjs cursor-read',
67
- }]
68
- hooks.beforeMCPExecution = [{
69
- command: 'node ./hooks/pluxx-permissions.mjs cursor-mcp',
70
- }]
71
- }
72
-
73
- if (!this.config.hooks) {
74
- await this.writeJson('hooks/hooks.json', { version: 1, hooks })
75
- return
76
- }
77
-
78
- for (const [event, entries] of Object.entries(this.config.hooks)) {
79
- if (!entries) continue
80
- const filteredEntries = entries.filter((entry) => {
81
- if (
82
- usesPlatformManagedAuth
83
- && entry.type !== 'prompt'
84
- && entry.command?.includes('check-env.sh')
85
- ) {
86
- return false
87
- }
88
- return true
89
- })
90
-
91
- if (filteredEntries.length === 0) continue
92
-
93
- hooks[event] = [
94
- ...(hooks[event] ?? []),
95
- ...filteredEntries.map(entry => {
96
- const hookDef: Record<string, unknown> = {}
97
- if (entry.type === 'prompt') {
98
- hookDef.type = 'prompt'
99
- hookDef.prompt = entry.prompt
100
- if (entry.model) hookDef.model = entry.model
101
- } else if (entry.command) {
102
- hookDef.command = entry.command.replace('${PLUGIN_ROOT}', '.')
103
- }
104
- if (entry.timeout) hookDef.timeout = entry.timeout
105
- if (entry.matcher) hookDef.matcher = entry.matcher
106
- if (entry.failClosed) hookDef.failClosed = entry.failClosed
107
- if (entry.loop_limit !== undefined) hookDef.loop_limit = entry.loop_limit
108
- return hookDef
109
- }),
110
- ]
111
- }
112
-
113
- await this.writeJson('hooks/hooks.json', { version: 1, hooks })
114
- }
115
-
116
- private async generateRules(): Promise<void> {
117
- const overrides = this.config.platforms?.cursor
118
- if (!overrides?.rules?.length) return
119
-
120
- for (const rule of overrides.rules) {
121
- const frontmatter = [
122
- '---',
123
- `description: "${rule.description}"`,
124
- ]
125
- if (rule.globs) {
126
- if (Array.isArray(rule.globs)) {
127
- frontmatter.push(`globs: ${JSON.stringify(rule.globs)}`)
128
- } else {
129
- frontmatter.push(`globs: "${rule.globs}"`)
130
- }
131
- }
132
- if (rule.alwaysApply !== undefined) {
133
- frontmatter.push(`alwaysApply: ${rule.alwaysApply}`)
134
- }
135
- frontmatter.push('---')
136
-
137
- const content = rule.content ?? ''
138
- const filename = rule.description
139
- .toLowerCase()
140
- .replace(/[^a-z0-9]+/g, '-')
141
- .replace(/^-|-$/g, '')
142
-
143
- await this.writeFile(
144
- `rules/${filename}.mdc`,
145
- frontmatter.join('\n') + '\n\n' + content
146
- )
147
- }
148
- }
149
-
150
- private async generateAgentsMd(): Promise<void> {
151
- if (!this.config.instructions) return
152
- const srcPath = this.resolveConfigPath(this.config.instructions, 'instructions')
153
- if (!existsSync(srcPath)) return
154
-
155
- const content = await Bun.file(srcPath).text()
156
- await this.writeFile('AGENTS.md', content)
157
- }
158
- }
@@ -1,83 +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
- * Gemini CLI uses gemini-extension.json for the plugin manifest,
8
- * GEMINI.md for instructions, and mcpServers in the same format as Claude Code.
9
- */
10
- export class GeminiCliGenerator extends Generator {
11
- readonly platform: TargetPlatform = 'gemini-cli'
12
-
13
- async generate(): Promise<void> {
14
- await Promise.all([
15
- this.generateManifest(),
16
- this.generateInstructions(),
17
- ])
18
-
19
- this.copySkills()
20
- this.copyScripts()
21
- this.copyAssets()
22
- }
23
-
24
- private async generateManifest(): Promise<void> {
25
- const manifest: Record<string, unknown> = {
26
- name: this.config.name,
27
- version: this.config.version,
28
- description: this.config.description,
29
- author: this.config.author,
30
- }
31
-
32
- // MCP servers block
33
- const mcpServers = this.buildMcpServers({
34
- transformRemoteEntry: ({ server, entry }) => ({
35
- type: server.transport === 'sse' ? 'sse' : 'http',
36
- ...entry,
37
- }),
38
- })
39
- if (mcpServers) {
40
- manifest.mcpServers = mcpServers
41
- }
42
-
43
- // Skills paths
44
- manifest.skills = ['./skills/']
45
-
46
- // Hooks block
47
- if (this.config.hooks) {
48
- const hooks: Record<string, unknown[]> = {}
49
-
50
- for (const [event, entries] of Object.entries(this.config.hooks)) {
51
- if (!entries) continue
52
- warnDroppedHookFields(this.platform, event, entries)
53
- const commandEntries = entries.filter(entry => entry.type !== 'prompt' && entry.command)
54
- if (commandEntries.length === 0) continue
55
- hooks[event] = commandEntries.map(entry => ({
56
- command: entry.command!.replace('${PLUGIN_ROOT}', '.'),
57
- }))
58
- }
59
-
60
- manifest.hooks = hooks
61
- }
62
-
63
- await this.writeJson('gemini-extension.json', manifest)
64
- }
65
-
66
- private async generateInstructions(): Promise<void> {
67
- if (!this.config.instructions) return
68
- const srcPath = this.resolveConfigPath(this.config.instructions, 'instructions')
69
- if (!existsSync(srcPath)) return
70
-
71
- const content = await Bun.file(srcPath).text()
72
-
73
- const geminiMd = [
74
- `# ${this.config.brand?.displayName ?? this.config.name}`,
75
- '',
76
- this.config.brand?.shortDescription ?? this.config.description,
77
- '',
78
- content,
79
- ].join('\n')
80
-
81
- await this.writeFile('GEMINI.md', geminiMd)
82
- }
83
- }
@@ -1,32 +0,0 @@
1
- import { Generator } from '../base'
2
- import { generateClaudeFamilyOutputs } from '../shared/claude-family'
3
- import type { TargetPlatform } from '../../schema'
4
-
5
- /**
6
- * GitHub Copilot CLI uses the same plugin manifest format as Claude Code.
7
- * Discovery dirs: .github/skills/, .claude/skills/, .agents/skills/
8
- */
9
- export class GitHubCopilotGenerator extends Generator {
10
- readonly platform: TargetPlatform = 'github-copilot'
11
-
12
- async generate(): Promise<void> {
13
- await generateClaudeFamilyOutputs({
14
- config: this.config,
15
- rootDir: this.rootDir,
16
- platform: this.platform,
17
- options: {
18
- manifestPath: '.claude-plugin/plugin.json',
19
- instructionsFile: 'CLAUDE.md',
20
- pluginRootVar: 'CLAUDE_PLUGIN_ROOT',
21
- },
22
- writeJson: (relativePath, data) => this.writeJson(relativePath, data),
23
- writeFile: (relativePath, content) => this.writeFile(relativePath, content),
24
- })
25
-
26
- this.copySkills()
27
- this.copyCommands()
28
- this.copyAgents()
29
- this.copyScripts()
30
- this.copyAssets()
31
- }
32
- }
@@ -1,51 +0,0 @@
1
- import type { HookEntry, TargetPlatform } from '../schema'
2
-
3
- const MATCHER_PASSTHROUGH_PLATFORMS = new Set<TargetPlatform>([
4
- 'claude-code',
5
- 'cursor',
6
- 'github-copilot',
7
- 'openhands',
8
- ])
9
-
10
- const FAIL_CLOSED_PASSTHROUGH_PLATFORMS = new Set<TargetPlatform>([
11
- 'cursor',
12
- ])
13
-
14
- const LOOP_LIMIT_PASSTHROUGH_PLATFORMS = new Set<TargetPlatform>([
15
- 'cursor',
16
- ])
17
-
18
- export function warnDroppedHookFields(
19
- platform: TargetPlatform,
20
- event: string,
21
- entries: HookEntry[],
22
- ): void {
23
- const hasPromptHooks = entries.some(entry => entry.type === 'prompt')
24
- const hasMatcher = entries.some(entry => entry.matcher !== undefined)
25
- const hasFailClosed = entries.some(entry => entry.failClosed !== undefined)
26
- const hasLoopLimit = entries.some(entry => entry.loop_limit !== undefined)
27
-
28
- if (hasPromptHooks) {
29
- console.warn(
30
- `[pluxx] ${platform} generator dropped unsupported prompt-based hook for event "${event}".`
31
- )
32
- }
33
-
34
- if (hasMatcher && !MATCHER_PASSTHROUGH_PLATFORMS.has(platform)) {
35
- console.warn(
36
- `[pluxx] ${platform} generator dropped unsupported hook field "matcher" for event "${event}".`
37
- )
38
- }
39
-
40
- if (hasFailClosed && !FAIL_CLOSED_PASSTHROUGH_PLATFORMS.has(platform)) {
41
- console.warn(
42
- `[pluxx] ${platform} generator dropped unsupported hook field "failClosed" for event "${event}".`
43
- )
44
- }
45
-
46
- if (hasLoopLimit && !LOOP_LIMIT_PASSTHROUGH_PLATFORMS.has(platform)) {
47
- console.warn(
48
- `[pluxx] ${platform} generator dropped unsupported hook field "loop_limit" for event "${event}".`
49
- )
50
- }
51
- }
@@ -1,71 +0,0 @@
1
- import { rmSync, mkdirSync } from 'fs'
2
- import { resolve, relative } from 'path'
3
- import type { PluginConfig, TargetPlatform } from '../schema'
4
- import { Generator } from './base'
5
- import { ClaudeCodeGenerator } from './claude-code'
6
- import { CursorGenerator } from './cursor'
7
- import { CodexGenerator } from './codex'
8
- import { OpenCodeGenerator } from './opencode'
9
- import { GitHubCopilotGenerator } from './github-copilot'
10
- import { OpenHandsGenerator } from './openhands'
11
- import { WarpGenerator } from './warp'
12
- import { GeminiCliGenerator } from './gemini-cli'
13
- import { RooCodeGenerator } from './roo-code'
14
- import { ClineGenerator } from './cline'
15
- import { AmpGenerator } from './amp'
16
-
17
- const GENERATORS: Record<TargetPlatform, new (config: PluginConfig, rootDir: string) => Generator> = {
18
- 'claude-code': ClaudeCodeGenerator,
19
- cursor: CursorGenerator,
20
- codex: CodexGenerator,
21
- opencode: OpenCodeGenerator,
22
- 'github-copilot': GitHubCopilotGenerator,
23
- openhands: OpenHandsGenerator,
24
- warp: WarpGenerator,
25
- 'gemini-cli': GeminiCliGenerator,
26
- 'roo-code': RooCodeGenerator,
27
- cline: ClineGenerator,
28
- amp: AmpGenerator,
29
- }
30
-
31
- export interface BuildOptions {
32
- /** Override targets from config */
33
- targets?: TargetPlatform[]
34
- /** Clean output directory before building */
35
- clean?: boolean
36
- }
37
-
38
- export async function build(
39
- config: PluginConfig,
40
- rootDir: string,
41
- options: BuildOptions = {},
42
- ): Promise<void> {
43
- const targets = options.targets ?? config.targets
44
- const outDir = resolve(rootDir, config.outDir)
45
-
46
- // CRITICAL: Guard against path traversal — outDir must stay within rootDir
47
- const rel = relative(rootDir, outDir)
48
- if (rel.startsWith('..') || resolve(outDir) === resolve(rootDir)) {
49
- throw new Error(
50
- `outDir "${config.outDir}" resolves outside the project root. Refusing to delete.`
51
- )
52
- }
53
-
54
- if (options.clean !== false) {
55
- rmSync(outDir, { recursive: true, force: true })
56
- }
57
- mkdirSync(outDir, { recursive: true })
58
-
59
- const generators = targets.map(target => {
60
- const GeneratorClass = GENERATORS[target]
61
- if (!GeneratorClass) {
62
- throw new Error(`Unknown target platform: ${target}`)
63
- }
64
- return new GeneratorClass(config, rootDir)
65
- })
66
-
67
- // Build all targets in parallel
68
- await Promise.all(generators.map(g => g.generate()))
69
- }
70
-
71
- export { Generator }