@a5c-ai/extensions-adapter 6.0.1-staging.ffc4f312f8c0 → 6.0.2

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 (127) hide show
  1. package/dist/binTemplates.d.ts +7 -0
  2. package/dist/binTemplates.d.ts.map +1 -0
  3. package/dist/binTemplates.js +292 -0
  4. package/dist/cli.d.ts +8 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +299 -0
  7. package/dist/compiler.d.ts +15 -0
  8. package/dist/compiler.d.ts.map +1 -0
  9. package/dist/compiler.js +118 -0
  10. package/dist/diff.d.ts +9 -0
  11. package/dist/diff.d.ts.map +1 -0
  12. package/dist/diff.js +183 -0
  13. package/dist/emit.d.ts +3 -0
  14. package/dist/emit.d.ts.map +1 -0
  15. package/dist/emit.js +42 -0
  16. package/dist/extension-adapter.d.ts +3 -0
  17. package/dist/extension-adapter.d.ts.map +1 -0
  18. package/dist/extension-adapter.js +4 -0
  19. package/dist/hookRegistration.d.ts +8 -0
  20. package/dist/hookRegistration.d.ts.map +1 -0
  21. package/dist/hookRegistration.js +9 -0
  22. package/dist/index.d.ts +13 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +13 -0
  25. package/dist/init.d.ts +17 -0
  26. package/dist/init.d.ts.map +1 -0
  27. package/dist/init.js +200 -0
  28. package/dist/installInstructions.d.ts +3 -0
  29. package/dist/installInstructions.d.ts.map +1 -0
  30. package/dist/installInstructions.js +150 -0
  31. package/dist/installSharedGenerator.d.ts +3 -0
  32. package/dist/installSharedGenerator.d.ts.map +1 -0
  33. package/dist/installSharedGenerator.js +229 -0
  34. package/dist/manifestGenerators.d.ts +10 -0
  35. package/dist/manifestGenerators.d.ts.map +1 -0
  36. package/dist/manifestGenerators.js +11 -0
  37. package/dist/marketplaceGenerator.d.ts +3 -0
  38. package/dist/marketplaceGenerator.d.ts.map +1 -0
  39. package/dist/marketplaceGenerator.js +47 -0
  40. package/dist/mcpConfig.d.ts +69 -0
  41. package/dist/mcpConfig.d.ts.map +1 -0
  42. package/dist/mcpConfig.js +115 -0
  43. package/dist/proxiedHookTemplates.d.ts +10 -0
  44. package/dist/proxiedHookTemplates.d.ts.map +1 -0
  45. package/dist/proxiedHookTemplates.js +123 -0
  46. package/dist/resolve.d.ts +3 -0
  47. package/dist/resolve.d.ts.map +1 -0
  48. package/dist/resolve.js +106 -0
  49. package/dist/schema.d.ts +231 -0
  50. package/dist/schema.d.ts.map +1 -0
  51. package/dist/schema.js +340 -0
  52. package/dist/sdkConfig.d.ts +20 -0
  53. package/dist/sdkConfig.d.ts.map +1 -0
  54. package/dist/sdkConfig.js +41 -0
  55. package/dist/targets/adapters/antigravity.d.ts +33 -0
  56. package/dist/targets/adapters/antigravity.d.ts.map +1 -0
  57. package/dist/targets/adapters/antigravity.js +173 -0
  58. package/dist/targets/adapters/base.d.ts +11 -0
  59. package/dist/targets/adapters/base.d.ts.map +1 -0
  60. package/dist/targets/adapters/base.js +19 -0
  61. package/dist/targets/adapters/claude-code.d.ts +10 -0
  62. package/dist/targets/adapters/claude-code.d.ts.map +1 -0
  63. package/dist/targets/adapters/claude-code.js +87 -0
  64. package/dist/targets/adapters/codex.d.ts +14 -0
  65. package/dist/targets/adapters/codex.d.ts.map +1 -0
  66. package/dist/targets/adapters/codex.js +136 -0
  67. package/dist/targets/adapters/cursor.d.ts +10 -0
  68. package/dist/targets/adapters/cursor.d.ts.map +1 -0
  69. package/dist/targets/adapters/cursor.js +63 -0
  70. package/dist/targets/adapters/gemini.d.ts +10 -0
  71. package/dist/targets/adapters/gemini.d.ts.map +1 -0
  72. package/dist/targets/adapters/gemini.js +103 -0
  73. package/dist/targets/adapters/genty.d.ts +12 -0
  74. package/dist/targets/adapters/genty.d.ts.map +1 -0
  75. package/dist/targets/adapters/genty.js +94 -0
  76. package/dist/targets/adapters/github-copilot.d.ts +10 -0
  77. package/dist/targets/adapters/github-copilot.d.ts.map +1 -0
  78. package/dist/targets/adapters/github-copilot.js +66 -0
  79. package/dist/targets/adapters/hermes.d.ts +13 -0
  80. package/dist/targets/adapters/hermes.d.ts.map +1 -0
  81. package/dist/targets/adapters/hermes.js +99 -0
  82. package/dist/targets/adapters/hooks-utils.d.ts +12 -0
  83. package/dist/targets/adapters/hooks-utils.d.ts.map +1 -0
  84. package/dist/targets/adapters/hooks-utils.js +60 -0
  85. package/dist/targets/adapters/index.d.ts +31 -0
  86. package/dist/targets/adapters/index.d.ts.map +1 -0
  87. package/dist/targets/adapters/index.js +75 -0
  88. package/dist/targets/adapters/interface.d.ts +9 -0
  89. package/dist/targets/adapters/interface.d.ts.map +1 -0
  90. package/dist/targets/adapters/interface.js +2 -0
  91. package/dist/targets/adapters/oh-my-pi.d.ts +12 -0
  92. package/dist/targets/adapters/oh-my-pi.d.ts.map +1 -0
  93. package/dist/targets/adapters/oh-my-pi.js +97 -0
  94. package/dist/targets/adapters/openclaw.d.ts +15 -0
  95. package/dist/targets/adapters/openclaw.d.ts.map +1 -0
  96. package/dist/targets/adapters/openclaw.js +170 -0
  97. package/dist/targets/adapters/opencode.d.ts +11 -0
  98. package/dist/targets/adapters/opencode.d.ts.map +1 -0
  99. package/dist/targets/adapters/opencode.js +101 -0
  100. package/dist/targets/adapters/pi.d.ts +12 -0
  101. package/dist/targets/adapters/pi.d.ts.map +1 -0
  102. package/dist/targets/adapters/pi.js +98 -0
  103. package/dist/targets/index.d.ts +7 -0
  104. package/dist/targets/index.d.ts.map +1 -0
  105. package/dist/targets/index.js +79 -0
  106. package/dist/transform.d.ts +4 -0
  107. package/dist/transform.d.ts.map +1 -0
  108. package/dist/transform.js +301 -0
  109. package/dist/transformEmitters.d.ts +8 -0
  110. package/dist/transformEmitters.d.ts.map +1 -0
  111. package/dist/transformEmitters.js +350 -0
  112. package/dist/transformHelpers.d.ts +15 -0
  113. package/dist/transformHelpers.d.ts.map +1 -0
  114. package/dist/transformHelpers.js +246 -0
  115. package/dist/types.d.ts +214 -0
  116. package/dist/types.d.ts.map +1 -0
  117. package/dist/types.js +2 -0
  118. package/dist/utils.d.ts +42 -0
  119. package/dist/utils.d.ts.map +1 -0
  120. package/dist/utils.js +187 -0
  121. package/dist/validate.d.ts +3 -0
  122. package/dist/validate.d.ts.map +1 -0
  123. package/dist/validate.js +188 -0
  124. package/dist/verify.d.ts +6 -0
  125. package/dist/verify.d.ts.map +1 -0
  126. package/dist/verify.js +294 -0
  127. package/package.json +2 -2
@@ -0,0 +1,101 @@
1
+ // OpenCode harness output adapter
2
+ import { BaseHarnessOutputAdapter } from './base.js';
3
+ import { iterateHooks, slugify, applyPattern, resolveHookPath, getPattern, getJsPattern, resolveSdkConfig, } from './hooks-utils.js';
4
+ import { generateOpenCodeAccomplishSkill } from '../../transformHelpers.js';
5
+ import { buildOpencodeMcp, toJsonContent } from '../../mcpConfig.js';
6
+ export class OpenCodeAdapter extends BaseHarnessOutputAdapter {
7
+ generateMcpConfig(manifest, _targetProfile) {
8
+ if (!manifest.mcpServers || Object.keys(manifest.mcpServers).length === 0) {
9
+ return null;
10
+ }
11
+ return { path: 'opencode.json', content: toJsonContent(buildOpencodeMcp(manifest.mcpServers)) };
12
+ }
13
+ generateHookRegistration(manifest, targetProfile, _diagnostics) {
14
+ const content = generateOpenCodeHooksJson(manifest, targetProfile);
15
+ return { path: targetProfile.hookRegistrationOutputPath || 'hooks/hooks.json', content };
16
+ }
17
+ generateManifestFiles(_sourceDir, manifest, _targetProfile, _diagnostics) {
18
+ const files = [];
19
+ files.push({
20
+ path: 'plugin.json',
21
+ content: generateOpenCodeManifest(manifest, this.targetName),
22
+ });
23
+ return files;
24
+ }
25
+ generateExtraTargetFiles(_sourceDir, manifest, _targetProfile, _diagnostics) {
26
+ const files = [];
27
+ const accomplishSkill = generateOpenCodeAccomplishSkill(manifest);
28
+ if (accomplishSkill) {
29
+ files.push({ path: `accomplish-skills/${manifest.name}/SKILL.md`, content: accomplishSkill });
30
+ }
31
+ return files;
32
+ }
33
+ }
34
+ export function generateOpenCodeManifest(manifest, targetName = 'opencode') {
35
+ const pluginJson = {
36
+ name: manifest.name,
37
+ version: manifest.version,
38
+ description: manifest.description,
39
+ author: manifest.author,
40
+ license: manifest.license,
41
+ harness: targetName,
42
+ // Only declare the hooks/ dir when the plugin ships hooks (hook-free for atlas).
43
+ ...(manifest.hooks && Object.keys(manifest.hooks).length > 0 ? { hooks: 'hooks/' } : {}),
44
+ commands: 'commands/',
45
+ skills: 'skills/',
46
+ };
47
+ if (manifest.repository) {
48
+ pluginJson.repository = manifest.repository;
49
+ }
50
+ if (manifest.keywords) {
51
+ pluginJson.keywords = manifest.keywords;
52
+ }
53
+ return JSON.stringify(pluginJson, null, 2) + '\n';
54
+ }
55
+ export function generateOpenCodeHooksJson(manifest, targetProfile) {
56
+ const hooks = {};
57
+ const pat = getPattern(manifest, targetProfile.name);
58
+ const sdk = resolveSdkConfig(manifest);
59
+ iterateHooks(manifest, targetProfile, (canonical, native, handler) => {
60
+ const slug = slugify(canonical);
61
+ const adapter = targetProfile.adapterName;
62
+ if (handler === 'proxy') {
63
+ hooks[native] = [{
64
+ type: 'command',
65
+ command: `${sdk.proxyBinary} invoke --adapter ${adapter} --json`,
66
+ description: `${manifest.name} ${canonical} hook`,
67
+ timeoutMs: canonical === 'ShellEnv' ? 5000 : 30000,
68
+ }];
69
+ }
70
+ else if (handler === true) {
71
+ hooks[native] = [{
72
+ type: 'command',
73
+ command: `echo '{}'`,
74
+ description: `${manifest.name} ${canonical} hook`,
75
+ timeoutMs: canonical === 'ShellEnv' ? 5000 : 30000,
76
+ }];
77
+ }
78
+ else {
79
+ const jsPat = getJsPattern(manifest, targetProfile.name);
80
+ let handlerScript;
81
+ if (jsPat) {
82
+ handlerScript = applyPattern(jsPat, manifest.name, slug, native);
83
+ }
84
+ else {
85
+ const p = resolveHookPath(handler, slug, manifest.name, native, pat);
86
+ handlerScript = p ? p.replace(/\.sh$/, '.js') : 'echo {}';
87
+ }
88
+ hooks[native] = [{
89
+ type: 'command',
90
+ script: `./${handlerScript}`,
91
+ description: `${manifest.name} ${canonical} hook`,
92
+ timeoutMs: canonical === 'ShellEnv' ? 5000 : 30000,
93
+ }];
94
+ }
95
+ });
96
+ return JSON.stringify({
97
+ version: 1,
98
+ description: `${manifest.name} hook registration for OpenCode.`,
99
+ hooks,
100
+ }, null, 2) + '\n';
101
+ }
@@ -0,0 +1,12 @@
1
+ import type { A5cPluginManifest, TargetProfile, TransformedFile, Diagnostic } from '../../types.js';
2
+ import { BaseHarnessOutputAdapter } from './base.js';
3
+ export declare class PiAdapter extends BaseHarnessOutputAdapter {
4
+ generateMcpConfig(manifest: A5cPluginManifest, _targetProfile: TargetProfile): TransformedFile | null;
5
+ generateManifestFiles(_sourceDir: string, manifest: A5cPluginManifest, _targetProfile: TargetProfile, _diagnostics: Diagnostic[]): TransformedFile[];
6
+ }
7
+ type ResolvedManifest = A5cPluginManifest & {
8
+ npmPackageName?: string;
9
+ };
10
+ export declare function generatePiManifest(manifest: ResolvedManifest, targetName?: string): string;
11
+ export {};
12
+ //# sourceMappingURL=pi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../src/targets/adapters/pi.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACpG,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAOrD,qBAAa,SAAU,SAAQ,wBAAwB;IAErD,iBAAiB,CACf,QAAQ,EAAE,iBAAiB,EAC3B,cAAc,EAAE,aAAa,GAC5B,eAAe,GAAG,IAAI;IAIzB,qBAAqB,CACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,iBAAiB,EAC3B,cAAc,EAAE,aAAa,EAC7B,YAAY,EAAE,UAAU,EAAE,GACzB,eAAe,EAAE;CAQrB;AAED,KAAK,gBAAgB,GAAG,iBAAiB,GAAG;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAwCF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,UAAU,SAAO,GAAG,MAAM,CAkDxF"}
@@ -0,0 +1,98 @@
1
+ // Pi harness output adapter
2
+ import { BaseHarnessOutputAdapter } from './base.js';
3
+ import { resolveTargetCliName, resolveTargetNpmPackageName, } from '../../sdkConfig.js';
4
+ import { emitJsonMcpConfig } from '../../mcpConfig.js';
5
+ export class PiAdapter extends BaseHarnessOutputAdapter {
6
+ generateMcpConfig(manifest, _targetProfile) {
7
+ return emitJsonMcpConfig(manifest, '.mcp.json');
8
+ }
9
+ generateManifestFiles(_sourceDir, manifest, _targetProfile, _diagnostics) {
10
+ const files = [];
11
+ files.push({
12
+ path: 'package.json',
13
+ content: generatePiManifest(manifest, this.targetName),
14
+ });
15
+ return files;
16
+ }
17
+ }
18
+ function buildNpmRepository(manifest, npmPackageName) {
19
+ if (!manifest.repository)
20
+ return undefined;
21
+ let url = typeof manifest.repository === 'string'
22
+ ? manifest.repository
23
+ : manifest.repository.url;
24
+ if (!url.startsWith('git+'))
25
+ url = `git+${url}`;
26
+ if (!url.endsWith('.git'))
27
+ url = `${url}.git`;
28
+ const directory = `plugins/${npmPackageName.split('/').pop()}`;
29
+ return { type: 'git', url, directory };
30
+ }
31
+ function buildNpmHomepage(manifest, npmPackageName) {
32
+ if (!manifest.repository)
33
+ return undefined;
34
+ const url = typeof manifest.repository === 'string'
35
+ ? manifest.repository
36
+ : manifest.repository.url;
37
+ const base = url.replace(/\.git$/, '').replace(/^git\+/, '');
38
+ const directory = `plugins/${npmPackageName.split('/').pop()}`;
39
+ return `${base}/tree/main/${directory}#readme`;
40
+ }
41
+ function buildNpmBugs(manifest) {
42
+ if (!manifest.repository)
43
+ return undefined;
44
+ const url = typeof manifest.repository === 'string'
45
+ ? manifest.repository
46
+ : manifest.repository.url;
47
+ const base = url.replace(/\.git$/, '').replace(/^git\+/, '');
48
+ return { url: `${base}/issues` };
49
+ }
50
+ export function generatePiManifest(manifest, targetName = 'pi') {
51
+ const target = { name: targetName };
52
+ const packageJson = {
53
+ name: resolveTargetNpmPackageName(manifest, target),
54
+ version: manifest.version,
55
+ type: 'module',
56
+ description: `${manifest.description} — ${targetName}`,
57
+ keywords: [targetName, manifest.name, 'orchestration'],
58
+ [targetName]: {
59
+ extensions: ['./extensions'],
60
+ skills: ['./skills'],
61
+ },
62
+ peerDependencies: {
63
+ '@earendil-works/pi-coding-agent': '*',
64
+ },
65
+ scripts: {
66
+ test: 'npm run validate:ci',
67
+ 'test:integration': 'node --test test/integration.test.js',
68
+ 'test:packaged-install': 'node test/packaged-install.test.cjs',
69
+ 'validate:ci': 'npm run test:integration && npm run test:packaged-install',
70
+ deploy: 'npm publish --access public',
71
+ 'deploy:staging': 'npm publish --access public --tag staging',
72
+ },
73
+ bin: { [resolveTargetCliName(manifest, target)]: 'bin/cli.cjs' },
74
+ files: [
75
+ 'bin/',
76
+ 'package.json',
77
+ 'versions.json',
78
+ 'README.md',
79
+ 'AGENTS.md',
80
+ 'extensions/',
81
+ 'skills/',
82
+ 'commands/',
83
+ 'scripts/',
84
+ // hooks/ only when the plugin actually ships hooks (hook-free plugins
85
+ // like atlas emit no hooks/ dir, and verify rejects listed-but-missing
86
+ // publishable paths).
87
+ ...(manifest.hooks && Object.keys(manifest.hooks).length > 0 ? ['hooks/'] : []),
88
+ ],
89
+ author: typeof manifest.author === 'string' ? manifest.author : manifest.author.name,
90
+ license: manifest.license,
91
+ publishConfig: { access: 'public' },
92
+ };
93
+ const piPkgName = packageJson.name;
94
+ packageJson.repository = buildNpmRepository(manifest, piPkgName);
95
+ packageJson.homepage = buildNpmHomepage(manifest, piPkgName);
96
+ packageJson.bugs = buildNpmBugs(manifest);
97
+ return JSON.stringify(packageJson, null, 2) + '\n';
98
+ }
@@ -0,0 +1,7 @@
1
+ import type { TargetProfile } from '../types.js';
2
+ export declare const TARGET_REGISTRY: Record<string, TargetProfile>;
3
+ export declare const HOOK_NAME_MAP: Record<string, Record<string, string>>;
4
+ export declare function getTargetProfile(name: string): TargetProfile | null;
5
+ export declare function requireTargetProfile(name: string): TargetProfile;
6
+ export declare function getAllTargets(): string[];
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/targets/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiEjD,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAEzD,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAoB,CAAC;AAEtF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAInE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAMhE;AAED,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAExC"}
@@ -0,0 +1,79 @@
1
+ // Catalog-backed target registry and hook name mapping
2
+ import { getHookNameMap, getPluginTargetDescriptor, listPluginTargetDescriptors, } from '@a5c-ai/atlas/catalog';
3
+ function toManifestFormat(format) {
4
+ if (format === 'plugin.json')
5
+ return 'plugin.json';
6
+ if (format === 'state-only' || format.includes('package.json'))
7
+ return 'package.json';
8
+ return 'multiple';
9
+ }
10
+ function toCommandFormat(format) {
11
+ if (format === 'extension-manifest')
12
+ return 'toml';
13
+ if (format === 'none' || format === 'package-json commands')
14
+ return 'none';
15
+ return 'markdown';
16
+ }
17
+ function toHookRegistrationFormat(format) {
18
+ return format || null;
19
+ }
20
+ function toScriptVariants(variants) {
21
+ return (variants ?? []).filter((variant) => variant === 'bash' ||
22
+ variant === 'powershell' ||
23
+ variant === 'javascript' ||
24
+ variant === 'typescript');
25
+ }
26
+ function canonicalTargetName(name) {
27
+ // Try exact match first; if not found, check if any target's adapterName or cliCommand matches
28
+ if (getPluginTargetDescriptor(name))
29
+ return name;
30
+ for (const target of listPluginTargetDescriptors()) {
31
+ if (target.adapterName === name || target.cliCommand === name)
32
+ return target.targetId;
33
+ }
34
+ return name;
35
+ }
36
+ function toTargetProfile(target) {
37
+ return {
38
+ name: target.targetId,
39
+ displayName: target.displayName,
40
+ adapterName: target.adapterName,
41
+ pluginRootEnvVar: target.pluginRootEnvVar ?? null,
42
+ supportedHooks: new Map(Object.entries(target.supportedHooks)),
43
+ commandFormat: toCommandFormat(target.commandFormat),
44
+ skillHandling: target.skillHandling ?? 'none',
45
+ manifestFormat: toManifestFormat(target.manifestFormat),
46
+ hookRegistrationFormat: toHookRegistrationFormat(target.hookRegistrationFormat),
47
+ hookRegistrationOutputPath: target.hookRegistrationOutputPath ?? null,
48
+ hookRegistrationAliasPaths: target.hookRegistrationAliasPaths ?? [],
49
+ harnessManifestPath: target.harnessManifestPath ?? null,
50
+ requiredSurfaceFile: target.requiredSurfaceFile ?? null,
51
+ scriptVariants: toScriptVariants(target.scriptVariants),
52
+ npmPublishable: target.npmPublishable,
53
+ adapterFamily: target.adapterFamily ?? 'shell-hook',
54
+ distribution: target.distribution ?? 'marketplace',
55
+ pluginRootEnvVarForExtension: target.pluginRootEnvVarForExtension ?? undefined,
56
+ marketplacePath: target.marketplacePath,
57
+ installLayout: target.installLayout,
58
+ packageMetadata: target.packageMetadata,
59
+ componentSupport: target.componentSupport,
60
+ };
61
+ }
62
+ export const TARGET_REGISTRY = Object.fromEntries(listPluginTargetDescriptors().map((target) => [target.targetId, toTargetProfile(target)]));
63
+ export const HOOK_NAME_MAP = getHookNameMap();
64
+ export function getTargetProfile(name) {
65
+ const descriptor = getPluginTargetDescriptor(canonicalTargetName(name));
66
+ if (descriptor)
67
+ return toTargetProfile(descriptor);
68
+ return null;
69
+ }
70
+ export function requireTargetProfile(name) {
71
+ const profile = getTargetProfile(name);
72
+ if (!profile) {
73
+ throw new Error(`Unknown target profile: ${name}`);
74
+ }
75
+ return profile;
76
+ }
77
+ export function getAllTargets() {
78
+ return listPluginTargetDescriptors().map((target) => target.targetId);
79
+ }
@@ -0,0 +1,4 @@
1
+ import type { A5cPluginManifest, TargetProfile, TransformResult } from './types.js';
2
+ export declare function transform(sourceDir: string, manifest: A5cPluginManifest, targetProfile: TargetProfile): TransformResult;
3
+ export { getCommandPaths } from './utils.js';
4
+ //# sourceMappingURL=transform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,eAAe,EAGhB,MAAM,YAAY,CAAC;AA6BpB,wBAAgB,SAAS,CACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,GAC3B,eAAe,CAwCjB;AA8HD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,301 @@
1
+ // Stage 3: TRANSFORM - Transform components to target format
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { buildSkillFromCommand, getCommandPaths, markdownToToml, } from './utils.js';
5
+ import { getAdapter } from './targets/adapters/index.js';
6
+ import { buildTemplateVars, interpolateTemplate, generatePs1Wrapper, generateJsBridge, generateTsHookStub, } from './transformHelpers.js';
7
+ import { generateManifests, copyAgentFiles, copyContextFiles, copyIncludedFiles, generateExtraFiles, } from './transformEmitters.js';
8
+ function toOutputPath(value) {
9
+ return value.replace(/\\/g, '/');
10
+ }
11
+ export function transform(sourceDir, manifest, targetProfile) {
12
+ const files = [];
13
+ const diagnostics = [];
14
+ // Transform commands
15
+ const commandFiles = transformCommands(sourceDir, manifest, targetProfile, diagnostics);
16
+ files.push(...commandFiles);
17
+ // Transform skills
18
+ const skillFiles = transformSkills(sourceDir, manifest, targetProfile, diagnostics);
19
+ files.push(...skillFiles);
20
+ // Transform hooks
21
+ const hookFiles = transformHooks(sourceDir, manifest, targetProfile, diagnostics);
22
+ files.push(...hookFiles);
23
+ // Generate manifests
24
+ const manifestFiles = generateManifests(sourceDir, manifest, targetProfile, diagnostics);
25
+ files.push(...manifestFiles);
26
+ // Emit native MCP config (one file per target) when manifest.mcpServers present
27
+ emitMcpConfig(manifest, targetProfile, files);
28
+ // Copy agent instruction files
29
+ const agentFiles = copyAgentFiles(sourceDir, manifest, targetProfile, diagnostics);
30
+ files.push(...agentFiles);
31
+ // Copy context files
32
+ const contextFiles = copyContextFiles(sourceDir, manifest, targetProfile, diagnostics);
33
+ files.push(...contextFiles);
34
+ // Copy included files (versions.json, assets, etc.) — skip hooks/ since transformHooks handles those
35
+ const includedFiles = copyIncludedFiles(sourceDir, manifest, ['hooks/', 'hooks']);
36
+ files.push(...includedFiles);
37
+ // Generate extra files (Pi/oh-my-pi extensions, etc.)
38
+ const extraFiles = generateExtraFiles(sourceDir, manifest, targetProfile, diagnostics);
39
+ files.push(...extraFiles);
40
+ return { files, diagnostics };
41
+ }
42
+ function transformCommands(sourceDir, manifest, targetProfile, _diagnostics) {
43
+ const files = [];
44
+ if (targetProfile.commandFormat === 'none') {
45
+ return files;
46
+ }
47
+ if (!manifest.commands) {
48
+ return files;
49
+ }
50
+ const commandPaths = [];
51
+ if (typeof manifest.commands === 'string') {
52
+ // Directory path - glob all .md files
53
+ const commandDir = path.join(sourceDir, manifest.commands);
54
+ if (fs.existsSync(commandDir)) {
55
+ const entries = fs.readdirSync(commandDir);
56
+ for (const entry of entries) {
57
+ if (entry.endsWith('.md')) {
58
+ commandPaths.push(toOutputPath(path.join(manifest.commands, entry)));
59
+ }
60
+ }
61
+ }
62
+ }
63
+ else {
64
+ commandPaths.push(...manifest.commands.map(toOutputPath));
65
+ }
66
+ for (const cmdPath of commandPaths) {
67
+ const fullPath = path.join(sourceDir, cmdPath);
68
+ if (!fs.existsSync(fullPath)) {
69
+ continue;
70
+ }
71
+ const vars = buildTemplateVars(manifest, targetProfile);
72
+ const content = interpolateTemplate(fs.readFileSync(fullPath, 'utf-8'), vars);
73
+ if (targetProfile.commandFormat === 'toml') {
74
+ // Convert to TOML (Gemini)
75
+ const tomlContent = markdownToToml(content);
76
+ const basename = path.basename(cmdPath, '.md');
77
+ files.push({
78
+ path: `commands/${basename}.toml`,
79
+ content: tomlContent,
80
+ });
81
+ }
82
+ else {
83
+ // Copy as-is (Markdown)
84
+ files.push({
85
+ path: cmdPath,
86
+ content,
87
+ });
88
+ }
89
+ }
90
+ return files;
91
+ }
92
+ function transformSkills(sourceDir, manifest, targetProfile, _diagnostics) {
93
+ const files = [];
94
+ if (targetProfile.skillHandling === 'none') {
95
+ return files;
96
+ }
97
+ const vars = buildTemplateVars(manifest, targetProfile);
98
+ // Always copy standalone skills
99
+ if (manifest.skills && Array.isArray(manifest.skills)) {
100
+ for (const skill of manifest.skills) {
101
+ const fullPath = path.join(sourceDir, skill.file);
102
+ if (fs.existsSync(fullPath)) {
103
+ files.push({
104
+ path: skill.file,
105
+ content: interpolateTemplate(fs.readFileSync(fullPath, 'utf-8'), vars),
106
+ });
107
+ }
108
+ }
109
+ }
110
+ // Derive skills from commands if needed
111
+ if (targetProfile.skillHandling === 'derived-from-commands') {
112
+ const commandPaths = getCommandPaths(sourceDir, manifest);
113
+ // Build set of standalone skill names to avoid duplicating them
114
+ const standaloneSkillNames = new Set();
115
+ if (manifest.skills && Array.isArray(manifest.skills)) {
116
+ for (const skill of manifest.skills) {
117
+ standaloneSkillNames.add(skill.name);
118
+ }
119
+ }
120
+ for (const cmdPath of commandPaths) {
121
+ const fullPath = path.join(sourceDir, cmdPath);
122
+ if (!fs.existsSync(fullPath))
123
+ continue;
124
+ const basename = path.basename(cmdPath, '.md');
125
+ if (standaloneSkillNames.has(basename)) {
126
+ continue;
127
+ }
128
+ const content = interpolateTemplate(fs.readFileSync(fullPath, 'utf-8'), vars);
129
+ const derivedSkill = buildSkillFromCommand(basename, content);
130
+ files.push({
131
+ path: `skills/${basename}/SKILL.md`,
132
+ content: derivedSkill,
133
+ });
134
+ }
135
+ }
136
+ return files;
137
+ }
138
+ // Re-export for backward compatibility
139
+ export { getCommandPaths } from './utils.js';
140
+ function transformHooks(sourceDir, manifest, targetProfile, diagnostics) {
141
+ const files = [];
142
+ if (!manifest.hooks || targetProfile.supportedHooks.size === 0) {
143
+ return files;
144
+ }
145
+ const isProgrammatic = targetProfile.adapterFamily === 'programmatic';
146
+ const override = manifest.targets?.[targetProfile.name];
147
+ const hookFilePattern = typeof override?.hookFilePattern === 'string'
148
+ ? override.hookFilePattern
149
+ : typeof manifest.hookFilePattern === 'string' ? manifest.hookFilePattern : undefined;
150
+ const hookJsPattern = typeof override?.hookJsPattern === 'string'
151
+ ? override.hookJsPattern
152
+ : hookFilePattern ? hookFilePattern.replace(/\.sh$/, '.js') : undefined;
153
+ const toSlug = (s) => s
154
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
155
+ .replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
156
+ const toNativeSlug = (s) => s.replace(/[._]/g, '-').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
157
+ for (const [canonicalHook, handlerValue] of Object.entries(manifest.hooks)) {
158
+ if (handlerValue === null)
159
+ continue;
160
+ const nativeHook = targetProfile.supportedHooks.get(canonicalHook);
161
+ if (!nativeHook)
162
+ continue;
163
+ if (typeof handlerValue !== 'string' || handlerValue === 'proxy')
164
+ continue;
165
+ const hookSlug = toSlug(canonicalHook);
166
+ const nativeSlug = toNativeSlug(nativeHook);
167
+ const sourceScript = path.join(sourceDir, handlerValue);
168
+ if (!fs.existsSync(sourceScript))
169
+ continue;
170
+ const rawContent = fs.readFileSync(sourceScript, 'utf-8');
171
+ const content = rawContent.replace(/ADAPTER_NAME="\$\{ADAPTER_NAME:\?[^}]*\}"/, `ADAPTER_NAME="\${ADAPTER_NAME:-${targetProfile.adapterName}}"`);
172
+ // Determine bash output filename via hookFilePattern
173
+ let outName;
174
+ if (hookFilePattern) {
175
+ outName = hookFilePattern
176
+ .replace(/\{\{name\}\}/g, manifest.name)
177
+ .replace(/\{\{slug\}\}/g, hookSlug)
178
+ .replace(/\{\{native\}\}/g, nativeSlug);
179
+ }
180
+ else {
181
+ outName = path.basename(handlerValue);
182
+ }
183
+ files.push({ path: `hooks/${outName}`, content, executable: true });
184
+ if (targetProfile.scriptVariants.includes('powershell')) {
185
+ const ps1Name = outName.replace(/\.sh$/, '.ps1');
186
+ files.push({ path: `hooks/${ps1Name}`, content: generatePs1Wrapper(hookSlug, targetProfile.adapterName, handlerValue) });
187
+ }
188
+ // JS bridge for programmatic targets — uses hookJsPattern if available
189
+ if (isProgrammatic) {
190
+ let jsName;
191
+ if (hookJsPattern) {
192
+ jsName = hookJsPattern
193
+ .replace(/\{\{name\}\}/g, manifest.name)
194
+ .replace(/\{\{slug\}\}/g, hookSlug)
195
+ .replace(/\{\{native\}\}/g, nativeSlug);
196
+ }
197
+ else {
198
+ jsName = outName.replace(/\.sh$/, '.js');
199
+ }
200
+ files.push({ path: `hooks/${jsName}`, content: generateJsBridge(jsName.replace(/\.js$/, ''), handlerValue, targetProfile), executable: true });
201
+ }
202
+ // TS hook stubs for targets with typescript variant (openclaw)
203
+ if (targetProfile.scriptVariants.includes('typescript')) {
204
+ const tsName = `${nativeSlug}.ts`;
205
+ files.push({
206
+ path: `extensions/hooks/${tsName}`,
207
+ content: generateTsHookStub(nativeHook, nativeSlug, outName, targetProfile),
208
+ });
209
+ }
210
+ }
211
+ // Emit Stop hook script for programmatic targets that declare Stop in the
212
+ // manifest but don't list it in supportedHooks (e.g. openclaw).
213
+ if (targetProfile.adapterFamily === 'programmatic' && typeof manifest.hooks?.Stop === 'string' && !targetProfile.supportedHooks.has('Stop')) {
214
+ const stopSource = path.join(sourceDir, manifest.hooks.Stop);
215
+ if (fs.existsSync(stopSource)) {
216
+ const rawContent = fs.readFileSync(stopSource, 'utf-8');
217
+ const content = rawContent.replace(/ADAPTER_NAME="\$\{ADAPTER_NAME:\?[^}]*\}"/, `ADAPTER_NAME="\${ADAPTER_NAME:-${targetProfile.adapterName}}"`);
218
+ const stopOutName = hookFilePattern
219
+ ? hookFilePattern
220
+ .replace(/\{\{name\}\}/g, manifest.name)
221
+ .replace(/\{\{slug\}\}/g, toSlug('Stop'))
222
+ .replace(/\{\{native\}\}/g, 'stop-hook')
223
+ : path.basename(manifest.hooks.Stop);
224
+ const stopPath = `hooks/${stopOutName}`;
225
+ if (!files.some((file) => file.path === stopPath)) {
226
+ files.push({ path: stopPath, content, executable: true });
227
+ }
228
+ }
229
+ }
230
+ // Generate hook registration file for shell-hook targets
231
+ if (targetProfile.hookRegistrationFormat) {
232
+ const hookRegFile = generateHookRegistrationFile(manifest, targetProfile, diagnostics);
233
+ if (hookRegFile) {
234
+ files.push(hookRegFile);
235
+ for (const aliasPath of targetProfile.hookRegistrationAliasPaths) {
236
+ files.push({ path: aliasPath, content: hookRegFile.content });
237
+ }
238
+ }
239
+ }
240
+ return files;
241
+ }
242
+ function generateHookRegistrationFile(manifest, targetProfile, diagnostics) {
243
+ const adapter = getAdapter(targetProfile.name);
244
+ if (!adapter)
245
+ return null;
246
+ return adapter.generateHookRegistration(manifest, targetProfile, diagnostics);
247
+ }
248
+ /**
249
+ * Global MCP emit step: produces exactly one native MCP config file per target
250
+ * from manifest.mcpServers. When a file already exists at the target MCP path
251
+ * (e.g. gemini's gemini-extension.json emitted by generateManifestFiles), the
252
+ * MCP keys are deep-merged into it rather than emitting a duplicate path.
253
+ */
254
+ function emitMcpConfig(manifest, targetProfile, files) {
255
+ if (!manifest.mcpServers || Object.keys(manifest.mcpServers).length === 0) {
256
+ return;
257
+ }
258
+ const adapter = getAdapter(targetProfile.name);
259
+ if (!adapter)
260
+ return;
261
+ const mcpFile = adapter.generateMcpConfig(manifest, targetProfile);
262
+ if (!mcpFile)
263
+ return;
264
+ const existing = files.find((f) => f.path === mcpFile.path);
265
+ if (!existing) {
266
+ files.push(mcpFile);
267
+ return;
268
+ }
269
+ // A file already lives at this path — merge MCP keys into it (JSON only).
270
+ if (mcpFile.path.endsWith('.json')) {
271
+ try {
272
+ const base = JSON.parse(existing.content);
273
+ const incoming = JSON.parse(mcpFile.content);
274
+ existing.content = toMergedJsonContent(base, incoming);
275
+ return;
276
+ }
277
+ catch {
278
+ // Fall through to replacement if either side is not valid JSON.
279
+ }
280
+ }
281
+ // Non-JSON or unparseable: the MCP content takes precedence.
282
+ existing.content = mcpFile.content;
283
+ }
284
+ function toMergedJsonContent(base, incoming) {
285
+ const merged = { ...base };
286
+ for (const [key, value] of Object.entries(incoming)) {
287
+ const baseValue = merged[key];
288
+ if (baseValue &&
289
+ typeof baseValue === 'object' &&
290
+ !Array.isArray(baseValue) &&
291
+ value &&
292
+ typeof value === 'object' &&
293
+ !Array.isArray(value)) {
294
+ merged[key] = { ...baseValue, ...value };
295
+ }
296
+ else {
297
+ merged[key] = value;
298
+ }
299
+ }
300
+ return JSON.stringify(merged, null, 2) + '\n';
301
+ }
@@ -0,0 +1,8 @@
1
+ import type { A5cPluginManifest, TargetProfile, TransformedFile, Diagnostic } from './types.js';
2
+ export declare function generateManifests(sourceDir: string, manifest: A5cPluginManifest, targetProfile: TargetProfile, _diagnostics: Diagnostic[]): TransformedFile[];
3
+ export declare function copyAgentFiles(sourceDir: string, manifest: A5cPluginManifest, targetProfile: TargetProfile, _diagnostics: Diagnostic[]): TransformedFile[];
4
+ export declare function copyContextFiles(sourceDir: string, manifest: A5cPluginManifest, targetProfile: TargetProfile, _diagnostics: Diagnostic[]): TransformedFile[];
5
+ export declare function copyIncludedFiles(sourceDir: string, manifest: A5cPluginManifest, excludePatterns?: string[]): TransformedFile[];
6
+ export declare function collectDir(sourceDir: string, relDir: string, files: TransformedFile[]): void;
7
+ export declare function generateExtraFiles(sourceDir: string, manifest: A5cPluginManifest, targetProfile: TargetProfile, _diagnostics: Diagnostic[]): TransformedFile[];
8
+ //# sourceMappingURL=transformEmitters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformEmitters.d.ts","sourceRoot":"","sources":["../src/transformEmitters.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAgFhG,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,UAAU,EAAE,GACzB,eAAe,EAAE,CAsHnB;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,UAAU,EAAE,GACzB,eAAe,EAAE,CAkBnB;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,UAAU,EAAE,GACzB,eAAe,EAAE,CAkBnB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,GAAE,MAAM,EAAO,GAC7B,eAAe,EAAE,CA8CnB;AAED,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,EAAE,GACvB,IAAI,CAeN;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,UAAU,EAAE,GACzB,eAAe,EAAE,CAoFnB"}