@a16njs/engine 0.4.0 → 0.6.0

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/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import type { A16nPlugin, AgentCustomization, DiscoveryResult, Warning, WrittenFile, CustomizationType } from '@a16njs/models';
1
+ import type { A16nPlugin, AgentCustomization, DiscoveryResult, Warning, WrittenFile, CustomizationType, Workspace } from '@a16njs/models';
2
+ import { type PluginDiscoveryOptions } from './plugin-discovery.js';
3
+ import { type ContentTransformation } from './transformation.js';
2
4
  /**
3
5
  * Options for a conversion operation.
4
6
  */
@@ -11,6 +13,21 @@ export interface ConversionOptions {
11
13
  root: string;
12
14
  /** If true, only discover without writing */
13
15
  dryRun?: boolean;
16
+ /** Override root for discovery (source plugin) */
17
+ sourceRoot?: string;
18
+ /** Override root for emission (target plugin) */
19
+ targetRoot?: string;
20
+ /** Workspace for source discovery (takes precedence over sourceRoot/root) */
21
+ sourceWorkspace?: Workspace;
22
+ /** Workspace for target emission (takes precedence over targetRoot/root) */
23
+ targetWorkspace?: Workspace;
24
+ /**
25
+ * If true, rewrite path references in content during conversion.
26
+ * @deprecated Use `transformations: [new PathRewritingTransformation()]` instead.
27
+ */
28
+ rewritePathRefs?: boolean;
29
+ /** Content transformations to apply between discovery and emission */
30
+ transformations?: ContentTransformation[];
14
31
  }
15
32
  /**
16
33
  * Git-ignore change information.
@@ -47,22 +64,44 @@ export interface PluginInfo {
47
64
  supports: CustomizationType[];
48
65
  source: 'bundled' | 'installed';
49
66
  }
67
+ /**
68
+ * Result of discovering and registering plugins.
69
+ */
70
+ export interface DiscoverAndRegisterResult {
71
+ /** Plugin IDs that were successfully registered */
72
+ registered: string[];
73
+ /** Plugin IDs that were skipped (already registered) */
74
+ skipped: string[];
75
+ /** Errors encountered during discovery */
76
+ errors: Array<{
77
+ packageName: string;
78
+ error: string;
79
+ }>;
80
+ }
50
81
  /**
51
82
  * The a16n conversion engine.
52
83
  * Orchestrates plugins to discover and emit agent customizations.
53
84
  */
54
85
  export declare class A16nEngine {
55
- private plugins;
86
+ private registry;
87
+ private loader;
56
88
  /**
57
89
  * Create a new engine with the given plugins.
58
- * @param plugins - Plugins to register
90
+ * @param plugins - Plugins to register (registered as 'bundled')
59
91
  */
60
92
  constructor(plugins?: A16nPlugin[]);
61
93
  /**
62
94
  * Register a plugin with the engine.
63
95
  * @param plugin - The plugin to register
96
+ * @param source - Whether the plugin is bundled or installed
97
+ */
98
+ registerPlugin(plugin: A16nPlugin, source?: 'bundled' | 'installed'): void;
99
+ /**
100
+ * Discover and register installed plugins from node_modules.
101
+ * @param options - Plugin discovery options
102
+ * @returns Result with registered, skipped, and error info
64
103
  */
65
- registerPlugin(plugin: A16nPlugin): void;
104
+ discoverAndRegisterPlugins(options?: PluginDiscoveryOptions): Promise<DiscoverAndRegisterResult>;
66
105
  /**
67
106
  * List all registered plugins.
68
107
  * @returns Array of plugin info
@@ -77,10 +116,10 @@ export declare class A16nEngine {
77
116
  /**
78
117
  * Discover customizations using a specific plugin.
79
118
  * @param pluginId - The plugin to use for discovery
80
- * @param root - The project root to scan
119
+ * @param rootOrWorkspace - The project root path or Workspace to scan
81
120
  * @returns Discovery result with items and warnings
82
121
  */
83
- discover(pluginId: string, root: string): Promise<DiscoveryResult>;
122
+ discover(pluginId: string, rootOrWorkspace: string | Workspace): Promise<DiscoveryResult>;
84
123
  /**
85
124
  * Convert customizations from one format to another.
86
125
  * @param options - Conversion options
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,OAAO,EACP,WAAW,EACX,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAExB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,8BAA8B;IAC9B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,2CAA2C;IAC3C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;CACjC;AAED;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAsC;IAErD;;;OAGG;gBACS,OAAO,GAAE,UAAU,EAAO;IAMtC;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIxC;;;OAGG;IACH,WAAW,IAAI,UAAU,EAAE;IAS3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI7C;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAQxE;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA0BrE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,35 +1,61 @@
1
+ import { PluginRegistry } from './plugin-registry.js';
2
+ import { PluginLoader, PluginConflictStrategy } from './plugin-loader.js';
3
+ import { LocalWorkspace } from './workspace.js';
4
+ import { PathRewritingTransformation, } from './transformation.js';
1
5
  /**
2
6
  * The a16n conversion engine.
3
7
  * Orchestrates plugins to discover and emit agent customizations.
4
8
  */
5
9
  export class A16nEngine {
6
- plugins = new Map();
10
+ registry = new PluginRegistry();
11
+ loader;
7
12
  /**
8
13
  * Create a new engine with the given plugins.
9
- * @param plugins - Plugins to register
14
+ * @param plugins - Plugins to register (registered as 'bundled')
10
15
  */
11
16
  constructor(plugins = []) {
17
+ this.loader = new PluginLoader(PluginConflictStrategy.PREFER_BUNDLED);
12
18
  for (const plugin of plugins) {
13
- this.registerPlugin(plugin);
19
+ this.registerPlugin(plugin, 'bundled');
14
20
  }
15
21
  }
16
22
  /**
17
23
  * Register a plugin with the engine.
18
24
  * @param plugin - The plugin to register
25
+ * @param source - Whether the plugin is bundled or installed
19
26
  */
20
- registerPlugin(plugin) {
21
- this.plugins.set(plugin.id, plugin);
27
+ registerPlugin(plugin, source = 'bundled') {
28
+ this.registry.register({ plugin, source });
29
+ }
30
+ /**
31
+ * Discover and register installed plugins from node_modules.
32
+ * @param options - Plugin discovery options
33
+ * @returns Result with registered, skipped, and error info
34
+ */
35
+ async discoverAndRegisterPlugins(options) {
36
+ const candidates = await this.loader.loadInstalled(options);
37
+ const resolved = this.loader.resolveConflicts(this.registry, candidates);
38
+ const registered = [];
39
+ for (const reg of resolved.loaded) {
40
+ this.registry.register(reg);
41
+ registered.push(reg.plugin.id);
42
+ }
43
+ return {
44
+ registered,
45
+ skipped: resolved.skipped.map((s) => s.plugin.id),
46
+ errors: resolved.errors,
47
+ };
22
48
  }
23
49
  /**
24
50
  * List all registered plugins.
25
51
  * @returns Array of plugin info
26
52
  */
27
53
  listPlugins() {
28
- return Array.from(this.plugins.values()).map((p) => ({
29
- id: p.id,
30
- name: p.name,
31
- supports: p.supports,
32
- source: 'bundled',
54
+ return this.registry.list().map((r) => ({
55
+ id: r.plugin.id,
56
+ name: r.plugin.name,
57
+ supports: r.plugin.supports,
58
+ source: r.source,
33
59
  }));
34
60
  }
35
61
  /**
@@ -38,20 +64,23 @@ export class A16nEngine {
38
64
  * @returns The plugin or undefined if not found
39
65
  */
40
66
  getPlugin(id) {
41
- return this.plugins.get(id);
67
+ return this.registry.getPlugin(id);
42
68
  }
43
69
  /**
44
70
  * Discover customizations using a specific plugin.
45
71
  * @param pluginId - The plugin to use for discovery
46
- * @param root - The project root to scan
72
+ * @param rootOrWorkspace - The project root path or Workspace to scan
47
73
  * @returns Discovery result with items and warnings
48
74
  */
49
- async discover(pluginId, root) {
75
+ async discover(pluginId, rootOrWorkspace) {
50
76
  const plugin = this.getPlugin(pluginId);
51
77
  if (!plugin) {
52
78
  throw new Error(`Unknown plugin: ${pluginId}`);
53
79
  }
54
- return plugin.discover(root);
80
+ const workspace = typeof rootOrWorkspace === 'string'
81
+ ? new LocalWorkspace('discover', rootOrWorkspace)
82
+ : rootOrWorkspace;
83
+ return plugin.discover(workspace);
55
84
  }
56
85
  /**
57
86
  * Convert customizations from one format to another.
@@ -67,16 +96,52 @@ export class A16nEngine {
67
96
  if (!targetPlugin) {
68
97
  throw new Error(`Unknown target: ${options.target}`);
69
98
  }
70
- // Discover from source
71
- const discovery = await sourcePlugin.discover(options.root);
72
- // Emit to target (pass dryRun to calculate what would be written)
73
- const emission = await targetPlugin.emit(discovery.items, options.root, {
99
+ // Resolve source and target roots/workspaces
100
+ // Priority: explicit workspace > explicit root override > default root
101
+ const discoverRoot = options.sourceRoot ?? options.root;
102
+ const emitRoot = options.targetRoot ?? options.root;
103
+ const sourceWorkspace = options.sourceWorkspace
104
+ ?? new LocalWorkspace('source', discoverRoot);
105
+ const targetWorkspace = options.targetWorkspace
106
+ ?? new LocalWorkspace('target', emitRoot);
107
+ // Discover from source using workspace
108
+ const discovery = await sourcePlugin.discover(sourceWorkspace);
109
+ // Collect warnings
110
+ const warnings = [...discovery.warnings];
111
+ // Build transformations list
112
+ const transformations = options.transformations
113
+ ? [...options.transformations]
114
+ : [];
115
+ // Backward compatibility: rewritePathRefs maps to PathRewritingTransformation
116
+ if (options.rewritePathRefs && !transformations.some((t) => t.id === 'path-rewriting')) {
117
+ transformations.push(new PathRewritingTransformation());
118
+ }
119
+ // Apply transformation pipeline
120
+ let itemsToEmit = discovery.items;
121
+ if (transformations.length > 0) {
122
+ for (const transformation of transformations) {
123
+ const transformContext = {
124
+ items: itemsToEmit,
125
+ sourcePlugin,
126
+ targetPlugin,
127
+ sourceRoot: sourceWorkspace.root,
128
+ targetRoot: targetWorkspace.root,
129
+ trialEmit: (items) => targetPlugin.emit(items, targetWorkspace, { dryRun: true }),
130
+ };
131
+ const result = await transformation.transform(transformContext);
132
+ itemsToEmit = result.items;
133
+ warnings.push(...result.warnings);
134
+ }
135
+ }
136
+ // Single emission at the end using workspace
137
+ const emission = await targetPlugin.emit(itemsToEmit, targetWorkspace, {
74
138
  dryRun: options.dryRun,
75
139
  });
140
+ warnings.push(...emission.warnings);
76
141
  return {
77
142
  discovered: discovery.items,
78
143
  written: emission.written,
79
- warnings: [...discovery.warnings, ...emission.warnings],
144
+ warnings,
80
145
  unsupported: emission.unsupported,
81
146
  };
82
147
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6DA;;;GAGG;AACH,MAAM,OAAO,UAAU;IACb,OAAO,GAA4B,IAAI,GAAG,EAAE,CAAC;IAErD;;;OAGG;IACH,YAAY,UAAwB,EAAE;QACpC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,cAAc,CAAC,MAAkB;QAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAED;;;OAGG;IACH,WAAW;QACT,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnD,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,MAAM,EAAE,SAAkB;SAC3B,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,EAAU;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,IAAY;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,OAA0B;QACtC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEpD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,uBAAuB;QACvB,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAE5D,kEAAkE;QAClE,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE;YACtE,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,OAAO;YACL,UAAU,EAAE,SAAS,CAAC,KAAK;YAC3B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,QAAQ,EAAE,CAAC,GAAG,SAAS,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC;YACvD,WAAW,EAAE,QAAQ,CAAC,WAAW;SAClC,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { AgentCustomization, Warning } from '@a16njs/models';
2
+ import type { WrittenFile } from '@a16njs/models';
3
+ /**
4
+ * A mapping from source-relative paths to target-relative paths.
5
+ * Used to rewrite path references in content during format conversion.
6
+ */
7
+ export type PathMapping = Map<string, string>;
8
+ /**
9
+ * Result of a path rewriting operation.
10
+ */
11
+ export interface RewriteResult {
12
+ /** The items with rewritten content (cloned; originals are not mutated) */
13
+ items: AgentCustomization[];
14
+ /** Count of replacements made across all items */
15
+ replacementCount: number;
16
+ }
17
+ /**
18
+ * Build a mapping from source-relative paths to target-relative paths.
19
+ *
20
+ * Given the discovered items (with sourcePaths relative to sourceRoot)
21
+ * and the written files (with absolute paths under targetRoot), produces
22
+ * a Map where keys are source-relative paths and values are target-relative paths.
23
+ *
24
+ * The mapping is derived from the `sourceItems` array on each WrittenFile,
25
+ * which links each output file back to the source AgentCustomization(s) that
26
+ * produced it. This handles merges, extension changes, and directory flattening
27
+ * naturally because the mapping is derived from actual emit output.
28
+ *
29
+ * @param _discovered - Items discovered from the source plugin (unused directly; mapping comes from written.sourceItems)
30
+ * @param written - Files written (or planned) by the target plugin
31
+ * @param _sourceRoot - Root directory used for discovery (unused; sourcePaths are already relative)
32
+ * @param targetRoot - Root directory used for emission
33
+ * @returns A map of source-relative path to target-relative path
34
+ */
35
+ export declare function buildMapping(_discovered: AgentCustomization[], written: WrittenFile[], _sourceRoot: string, targetRoot: string): PathMapping;
36
+ /**
37
+ * Rewrite file path references in item content.
38
+ *
39
+ * For each item, every occurrence of a mapped source path in the content
40
+ * is replaced with the corresponding target path. Replacements are applied
41
+ * longest-first to prevent partial match corruption (e.g., replacing
42
+ * "foo/bar.mdc.bak" before "foo/bar.mdc").
43
+ *
44
+ * Items are cloned before modification; originals are not mutated.
45
+ *
46
+ * @param items - The items whose content should be rewritten
47
+ * @param mapping - The source-to-target path mapping
48
+ * @returns Cloned items with rewritten content and replacement count
49
+ */
50
+ export declare function rewriteContent(items: AgentCustomization[], mapping: PathMapping): RewriteResult;
51
+ /**
52
+ * Detect orphan path references in item content.
53
+ *
54
+ * An "orphan" is a string in content that looks like it could be a
55
+ * source-format file path (matches known source plugin path prefixes
56
+ * and file extensions) but is NOT present in the mapping (i.e., it
57
+ * wasn't converted).
58
+ *
59
+ * @param items - The items to scan for orphan references
60
+ * @param mapping - The source-to-target path mapping
61
+ * @param sourcePluginPrefixes - Known directory prefixes for the source format
62
+ * (e.g., ['.cursor/rules/', '.cursor/skills/'] for cursor plugin)
63
+ * @param sourceExtensions - Known file extensions for the source format
64
+ * (e.g., ['.mdc', '.md'] for cursor plugin)
65
+ * @returns Warnings about orphan references found
66
+ */
67
+ export declare function detectOrphans(items: AgentCustomization[], mapping: PathMapping, sourcePluginPrefixes: string[], sourceExtensions: string[]): Warning[];
68
+ //# sourceMappingURL=path-rewriter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-rewriter.d.ts","sourceRoot":"","sources":["../src/path-rewriter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,kDAAkD;IAClD,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,kBAAkB,EAAE,EACjC,OAAO,EAAE,WAAW,EAAE,EACtB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,WAAW,CAoBb;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,OAAO,EAAE,WAAW,GACnB,aAAa,CAgDf;AASD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,OAAO,EAAE,WAAW,EACpB,oBAAoB,EAAE,MAAM,EAAE,EAC9B,gBAAgB,EAAE,MAAM,EAAE,GACzB,OAAO,EAAE,CAmCX"}
@@ -0,0 +1,144 @@
1
+ import * as path from 'path';
2
+ import { WarningCode } from '@a16njs/models';
3
+ /**
4
+ * Build a mapping from source-relative paths to target-relative paths.
5
+ *
6
+ * Given the discovered items (with sourcePaths relative to sourceRoot)
7
+ * and the written files (with absolute paths under targetRoot), produces
8
+ * a Map where keys are source-relative paths and values are target-relative paths.
9
+ *
10
+ * The mapping is derived from the `sourceItems` array on each WrittenFile,
11
+ * which links each output file back to the source AgentCustomization(s) that
12
+ * produced it. This handles merges, extension changes, and directory flattening
13
+ * naturally because the mapping is derived from actual emit output.
14
+ *
15
+ * @param _discovered - Items discovered from the source plugin (unused directly; mapping comes from written.sourceItems)
16
+ * @param written - Files written (or planned) by the target plugin
17
+ * @param _sourceRoot - Root directory used for discovery (unused; sourcePaths are already relative)
18
+ * @param targetRoot - Root directory used for emission
19
+ * @returns A map of source-relative path to target-relative path
20
+ */
21
+ export function buildMapping(_discovered, written, _sourceRoot, targetRoot) {
22
+ const mapping = new Map();
23
+ for (const file of written) {
24
+ // Compute target-relative path using POSIX separators for consistency
25
+ const targetRelative = path.relative(targetRoot, file.path).split(path.sep).join('/');
26
+ // Map each source item's sourcePath to this target path
27
+ if (file.sourceItems) {
28
+ for (const sourceItem of file.sourceItems) {
29
+ if (sourceItem.sourcePath) {
30
+ // Normalize to POSIX separators
31
+ const normalizedSourcePath = sourceItem.sourcePath.split(path.sep).join('/');
32
+ mapping.set(normalizedSourcePath, targetRelative);
33
+ }
34
+ }
35
+ }
36
+ }
37
+ return mapping;
38
+ }
39
+ /**
40
+ * Rewrite file path references in item content.
41
+ *
42
+ * For each item, every occurrence of a mapped source path in the content
43
+ * is replaced with the corresponding target path. Replacements are applied
44
+ * longest-first to prevent partial match corruption (e.g., replacing
45
+ * "foo/bar.mdc.bak" before "foo/bar.mdc").
46
+ *
47
+ * Items are cloned before modification; originals are not mutated.
48
+ *
49
+ * @param items - The items whose content should be rewritten
50
+ * @param mapping - The source-to-target path mapping
51
+ * @returns Cloned items with rewritten content and replacement count
52
+ */
53
+ export function rewriteContent(items, mapping) {
54
+ if (mapping.size === 0) {
55
+ // No mapping → return clones with no changes
56
+ return {
57
+ items: items.map((item) => ({ ...item })),
58
+ replacementCount: 0,
59
+ };
60
+ }
61
+ // Sort replacements longest-first to prevent partial match corruption
62
+ const sortedEntries = Array.from(mapping.entries()).sort((a, b) => b[0].length - a[0].length);
63
+ let totalReplacements = 0;
64
+ const rewrittenItems = items.map((item) => {
65
+ // Clone the item (shallow clone is sufficient since we only modify content)
66
+ const clone = { ...item };
67
+ let content = clone.content;
68
+ // NOTE: Replacements are applied sequentially. This is safe because source
69
+ // and target paths use different plugin directory prefixes, so a target path
70
+ // won't match a subsequent source key.
71
+ for (const [sourcePath, targetPath] of sortedEntries) {
72
+ // Count occurrences before replacement
73
+ let count = 0;
74
+ let idx = 0;
75
+ while ((idx = content.indexOf(sourcePath, idx)) !== -1) {
76
+ count++;
77
+ idx += sourcePath.length;
78
+ }
79
+ if (count > 0) {
80
+ // Use split+join for exact string replacement (no regex escaping needed)
81
+ content = content.split(sourcePath).join(targetPath);
82
+ totalReplacements += count;
83
+ }
84
+ }
85
+ clone.content = content;
86
+ return clone;
87
+ });
88
+ return {
89
+ items: rewrittenItems,
90
+ replacementCount: totalReplacements,
91
+ };
92
+ }
93
+ /**
94
+ * Escape a string for use in a RegExp.
95
+ */
96
+ function escapeRegExp(str) {
97
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
98
+ }
99
+ /**
100
+ * Detect orphan path references in item content.
101
+ *
102
+ * An "orphan" is a string in content that looks like it could be a
103
+ * source-format file path (matches known source plugin path prefixes
104
+ * and file extensions) but is NOT present in the mapping (i.e., it
105
+ * wasn't converted).
106
+ *
107
+ * @param items - The items to scan for orphan references
108
+ * @param mapping - The source-to-target path mapping
109
+ * @param sourcePluginPrefixes - Known directory prefixes for the source format
110
+ * (e.g., ['.cursor/rules/', '.cursor/skills/'] for cursor plugin)
111
+ * @param sourceExtensions - Known file extensions for the source format
112
+ * (e.g., ['.mdc', '.md'] for cursor plugin)
113
+ * @returns Warnings about orphan references found
114
+ */
115
+ export function detectOrphans(items, mapping, sourcePluginPrefixes, sourceExtensions) {
116
+ if (sourcePluginPrefixes.length === 0 || sourceExtensions.length === 0) {
117
+ return [];
118
+ }
119
+ const warnings = [];
120
+ const mappedPaths = new Set(mapping.keys());
121
+ const seen = new Set();
122
+ // Build a regex that matches: <prefix><non-whitespace-path><extension>
123
+ // e.g., .cursor/rules/some-file.mdc
124
+ const escapedPrefixes = sourcePluginPrefixes.map(escapeRegExp).join('|');
125
+ const escapedExtensions = sourceExtensions.map(escapeRegExp).join('|');
126
+ const pattern = new RegExp(`(?:${escapedPrefixes})[^\\s)\\]}>,"']+(?:${escapedExtensions})`, 'g');
127
+ for (const item of items) {
128
+ const matches = item.content.matchAll(pattern);
129
+ for (const match of matches) {
130
+ const foundPath = match[0];
131
+ const key = `${item.sourcePath ?? ''}::${foundPath}`;
132
+ if (!mappedPaths.has(foundPath) && !seen.has(key)) {
133
+ seen.add(key);
134
+ warnings.push({
135
+ code: WarningCode.OrphanPathRef,
136
+ message: `Orphan path reference: '${foundPath}' is not in the conversion set`,
137
+ sources: item.sourcePath ? [item.sourcePath] : undefined,
138
+ });
139
+ }
140
+ }
141
+ }
142
+ return warnings;
143
+ }
144
+ //# sourceMappingURL=path-rewriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-rewriter.js","sourceRoot":"","sources":["../src/path-rewriter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { A16nPlugin } from '@a16njs/models';
2
+ /**
3
+ * Options for controlling where plugin discovery searches.
4
+ */
5
+ export interface PluginDiscoveryOptions {
6
+ /** Custom search paths to scan for plugins. If omitted, uses getDefaultSearchPaths(). */
7
+ searchPaths?: string[];
8
+ }
9
+ /**
10
+ * Result of a plugin discovery scan.
11
+ */
12
+ export interface PluginDiscoveryResult {
13
+ /** Successfully loaded and validated plugins. */
14
+ plugins: A16nPlugin[];
15
+ /** Packages that were found but failed to load or validate. */
16
+ errors: PluginLoadError[];
17
+ }
18
+ /**
19
+ * Describes a plugin package that was found but could not be loaded.
20
+ */
21
+ export interface PluginLoadError {
22
+ /** The npm package name (e.g. "a16n-plugin-foo"). */
23
+ packageName: string;
24
+ /** Human-readable description of what went wrong. */
25
+ error: string;
26
+ }
27
+ /**
28
+ * Scan search paths for installed `a16n-plugin-*` packages, dynamically import
29
+ * each one, validate its default export, and return the valid plugins along
30
+ * with error info for any that failed.
31
+ *
32
+ * @param options - Optional search path overrides
33
+ * @returns Discovered plugins and any load errors
34
+ */
35
+ export declare function discoverInstalledPlugins(options?: PluginDiscoveryOptions): Promise<PluginDiscoveryResult>;
36
+ /**
37
+ * Type-guard that checks whether an unknown value satisfies the A16nPlugin interface.
38
+ *
39
+ * Validates presence and types of: id (string), name (string), supports (array),
40
+ * discover (function), emit (function).
41
+ *
42
+ * @param obj - The value to check
43
+ * @returns True if obj is a valid A16nPlugin
44
+ */
45
+ export declare function isValidPlugin(obj: unknown): obj is A16nPlugin;
46
+ /**
47
+ * Compute the default search paths for plugin discovery.
48
+ *
49
+ * Returns paths where `a16n-plugin-*` packages might be installed:
50
+ * - The global npm `node_modules` directory (derived from this package's location)
51
+ * - The local `node_modules` in the current working directory
52
+ *
53
+ * In a global install, the engine lives inside node_modules:
54
+ * .../lib/node_modules/@a16njs/engine/dist/plugin-discovery.js
55
+ * so walking up finds the node_modules parent directly.
56
+ *
57
+ * In a monorepo (e.g. pnpm workspace), the engine lives at:
58
+ * <root>/packages/engine/dist/plugin-discovery.js
59
+ * so we also check for a node_modules child directory at each level.
60
+ *
61
+ * @returns Array of directory paths to scan
62
+ */
63
+ export declare function getDefaultSearchPaths(): string[];
64
+ //# sourceMappingURL=plugin-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-discovery.d.ts","sourceRoot":"","sources":["../src/plugin-discovery.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,+DAA+D;IAC/D,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;CACf;AAKD;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CAyDhC;AAwBD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU,CAY7D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAiChD"}