@a16njs/plugin-a16n 0.1.0 → 0.2.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/emit.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * IR Emission - Write intermediate representation to .a16n/ directory
3
+ *
4
+ * This module implements the emit() function for the a16n plugin,
5
+ * which writes IR items to disk in the .a16n/ directory structure.
6
+ *
7
+ * Directory structure: .a16n/<type>/<name>.md
8
+ * - Type directories use kebab-case matching CustomizationType enum values
9
+ * - Files have YAML frontmatter with version, type, and type-specific fields
10
+ * - AgentSkillIO uses verbatim AgentSkills.io format (NO IR frontmatter)
11
+ * - relativeDir field creates subdirectories to preserve structure
12
+ */
13
+ import { type AgentCustomization, type EmitResult, type EmitOptions } from '@a16njs/models';
14
+ /**
15
+ * Emit IR items to .a16n/ directory structure.
16
+ *
17
+ * @param models - IR items to emit
18
+ * @param root - Project root directory
19
+ * @param options - Emission options (dryRun, etc.)
20
+ * @returns EmitResult with written files and warnings
21
+ */
22
+ export declare function emit(models: AgentCustomization[], root: string, options?: EmitOptions): Promise<EmitResult>;
23
+ //# sourceMappingURL=emit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EACf,KAAK,WAAW,EAQjB,MAAM,gBAAgB,CAAC;AAgBxB;;;;;;;GAOG;AACH,wBAAsB,IAAI,CACxB,MAAM,EAAE,kBAAkB,EAAE,EAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,CA2CrB"}
package/dist/emit.js ADDED
@@ -0,0 +1,180 @@
1
+ /**
2
+ * IR Emission - Write intermediate representation to .a16n/ directory
3
+ *
4
+ * This module implements the emit() function for the a16n plugin,
5
+ * which writes IR items to disk in the .a16n/ directory structure.
6
+ *
7
+ * Directory structure: .a16n/<type>/<name>.md
8
+ * - Type directories use kebab-case matching CustomizationType enum values
9
+ * - Files have YAML frontmatter with version, type, and type-specific fields
10
+ * - AgentSkillIO uses verbatim AgentSkills.io format (NO IR frontmatter)
11
+ * - relativeDir field creates subdirectories to preserve structure
12
+ */
13
+ import * as fs from 'fs/promises';
14
+ import * as path from 'path';
15
+ import { WarningCode, isAgentSkillIO, isManualPrompt, writeAgentSkillIO, } from '@a16njs/models';
16
+ import { formatIRFile } from './format.js';
17
+ import { slugify } from './utils.js';
18
+ /**
19
+ * Check if a path exists on the filesystem.
20
+ */
21
+ async function pathExists(p) {
22
+ try {
23
+ await fs.access(p);
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /**
31
+ * Emit IR items to .a16n/ directory structure.
32
+ *
33
+ * @param models - IR items to emit
34
+ * @param root - Project root directory
35
+ * @param options - Emission options (dryRun, etc.)
36
+ * @returns EmitResult with written files and warnings
37
+ */
38
+ export async function emit(models, root, options) {
39
+ const written = [];
40
+ const warnings = [];
41
+ const unsupported = [];
42
+ const dryRun = options?.dryRun ?? false;
43
+ // Group items by CustomizationType
44
+ const byType = new Map();
45
+ for (const item of models) {
46
+ const items = byType.get(item.type) || [];
47
+ items.push(item);
48
+ byType.set(item.type, items);
49
+ }
50
+ // Process each type
51
+ for (const [type, items] of byType.entries()) {
52
+ // Base directory for this type (kebab-case matching enum value)
53
+ const baseDir = path.join(root, '.a16n', type);
54
+ for (const item of items) {
55
+ try {
56
+ if (isAgentSkillIO(item)) {
57
+ // AgentSkillIO: use writeAgentSkillIO() for verbatim format
58
+ await emitAgentSkillIO(item, baseDir, written, dryRun);
59
+ }
60
+ else {
61
+ // All other types: use formatIRFile() for IR frontmatter
62
+ await emitStandardIR(item, baseDir, written, dryRun);
63
+ }
64
+ }
65
+ catch (error) {
66
+ warnings.push({
67
+ code: WarningCode.Skipped,
68
+ message: `Failed to emit ${item.type} "${item.id}": ${error instanceof Error ? error.message : String(error)}`,
69
+ sources: item.sourcePath ? [item.sourcePath] : [],
70
+ });
71
+ }
72
+ }
73
+ }
74
+ return {
75
+ written,
76
+ warnings,
77
+ unsupported,
78
+ };
79
+ }
80
+ /**
81
+ * Emit a standard IR item (all types except AgentSkillIO).
82
+ * Uses formatIRFile() to generate YAML frontmatter + content.
83
+ */
84
+ async function emitStandardIR(item, baseDir, written, dryRun) {
85
+ // Determine target directory (handle relativeDir subdirectories)
86
+ // Validate that relativeDir doesn't escape baseDir via path traversal
87
+ const baseDirResolved = path.resolve(baseDir);
88
+ const targetDir = item.relativeDir
89
+ ? path.resolve(baseDirResolved, item.relativeDir)
90
+ : baseDirResolved;
91
+ const rel = path.relative(baseDirResolved, targetDir);
92
+ if (rel.startsWith('..') || path.isAbsolute(rel)) {
93
+ throw new Error(`Invalid relativeDir "${item.relativeDir}" (escapes ${baseDir})`);
94
+ }
95
+ // Extract name from ID and slugify for filename
96
+ // For ManualPrompt with path separators, extract basename only
97
+ const rawName = extractNameFromId(item.id);
98
+ const name = isManualPrompt(item) ? path.basename(rawName) : rawName;
99
+ const filename = `${slugify(name)}.md`;
100
+ const filePath = path.join(targetDir, filename);
101
+ // Format content with IR frontmatter
102
+ const content = formatIRFile(item);
103
+ // Check if file already exists before writing
104
+ const existed = await pathExists(filePath);
105
+ // Write file (unless dry-run)
106
+ if (!dryRun) {
107
+ await fs.mkdir(targetDir, { recursive: true });
108
+ await fs.writeFile(filePath, content, 'utf-8');
109
+ }
110
+ written.push({
111
+ path: filePath,
112
+ type: item.type,
113
+ itemCount: 1,
114
+ isNewFile: !existed,
115
+ sourceItems: [item],
116
+ });
117
+ }
118
+ /**
119
+ * Emit an AgentSkillIO item using verbatim AgentSkills.io format.
120
+ * Uses writeAgentSkillIO() from @a16njs/models.
121
+ *
122
+ * NOTE: AgentSkillIO uses a flat directory-per-skill layout. The spec and all
123
+ * current tools (Cursor, Claude Code) only support flat skill directories, so
124
+ * relativeDir is not used here. If the spec or tools add nested skill support
125
+ * in the future, relativeDir handling (with path-traversal validation) would
126
+ * need to be added — see emitStandardIR for the pattern.
127
+ */
128
+ async function emitAgentSkillIO(item, baseDir, written, dryRun) {
129
+ // Use item.name for directory name — extractNameFromId mangles full source
130
+ // paths (e.g., ".cursor/skills/my-skill/SKILL" → "cursor-skills-my-skill-skill")
131
+ const skillDirName = slugify(item.name);
132
+ const skillDir = path.join(baseDir, skillDirName);
133
+ // Prepare frontmatter (AgentSkills.io format)
134
+ // Use original human-readable name, not the slugified directory name
135
+ const frontmatter = {
136
+ name: item.name,
137
+ description: item.description,
138
+ };
139
+ // Check if skill directory already exists before writing
140
+ const existed = await pathExists(skillDir);
141
+ // Write AgentSkillIO using shared utility (verbatim format)
142
+ if (!dryRun) {
143
+ await fs.mkdir(baseDir, { recursive: true });
144
+ await writeAgentSkillIO(skillDir, frontmatter, item.content, item.files || {});
145
+ }
146
+ written.push({
147
+ path: skillDir,
148
+ type: item.type,
149
+ itemCount: 1,
150
+ isNewFile: !existed,
151
+ sourceItems: [item],
152
+ });
153
+ }
154
+ /**
155
+ * Extract name from ID and strip file extension.
156
+ * ID format: <type>:<name> or <type>:hash:<hash>
157
+ *
158
+ * Example:
159
+ * - 'global-prompt:.cursor/rules/blogging.mdc' -> '.cursor/rules/blogging'
160
+ * - 'file-rule:typescript.mdc' -> 'typescript'
161
+ *
162
+ * @throws Error if ID is malformed (missing name after type prefix)
163
+ */
164
+ function extractNameFromId(id) {
165
+ const parts = id.split(':');
166
+ let name = id;
167
+ if (parts.length >= 2) {
168
+ // Skip first part (type) and return rest
169
+ name = parts.slice(1).join(':');
170
+ }
171
+ // Strip file extension (e.g., .mdc, .md, .txt)
172
+ // This ensures slugified names don't include the original extension
173
+ const stripped = name.replace(/\.[^/.]+$/, '');
174
+ // Validate that we have a usable name (not empty after stripping)
175
+ if (!stripped) {
176
+ throw new Error(`Invalid ID "${id}": cannot extract a usable name after type prefix`);
177
+ }
178
+ return stripped;
179
+ }
180
+ //# sourceMappingURL=emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit.js","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type AgentCustomization } from '@a16njs/models';
2
+ /**
3
+ * Format an IR item as a markdown file with YAML frontmatter.
4
+ *
5
+ * Format: ---\n{yaml}---\n\n{content}\n
6
+ *
7
+ * Includes: version, type, relativeDir (if present), type-specific fields
8
+ * Excludes: sourcePath (omitted from IR format), metadata (not serialized), name (filename is the identifier)
9
+ *
10
+ * @param item - The IR item to format
11
+ * @returns Formatted markdown string with YAML frontmatter
12
+ */
13
+ export declare function formatIRFile(item: AgentCustomization): string;
14
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,kBAAkB,EAYxB,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAgC7D"}
package/dist/format.js ADDED
@@ -0,0 +1,45 @@
1
+ import * as yaml from 'yaml';
2
+ import { isFileRule, isSimpleAgentSkill, isAgentIgnore, } from '@a16njs/models';
3
+ /**
4
+ * Format an IR item as a markdown file with YAML frontmatter.
5
+ *
6
+ * Format: ---\n{yaml}---\n\n{content}\n
7
+ *
8
+ * Includes: version, type, relativeDir (if present), type-specific fields
9
+ * Excludes: sourcePath (omitted from IR format), metadata (not serialized), name (filename is the identifier)
10
+ *
11
+ * @param item - The IR item to format
12
+ * @returns Formatted markdown string with YAML frontmatter
13
+ */
14
+ export function formatIRFile(item) {
15
+ // Build frontmatter object based on type
16
+ const frontmatter = {
17
+ version: item.version,
18
+ type: item.type,
19
+ };
20
+ // Add relativeDir if present (check undefined, not truthy, to preserve empty strings)
21
+ if (item.relativeDir !== undefined) {
22
+ frontmatter.relativeDir = item.relativeDir;
23
+ }
24
+ // Add type-specific fields (DO NOT include name, sourcePath, or metadata)
25
+ if (isFileRule(item)) {
26
+ frontmatter.globs = item.globs;
27
+ }
28
+ else if (isSimpleAgentSkill(item)) {
29
+ frontmatter.description = item.description;
30
+ }
31
+ else if (isAgentIgnore(item)) {
32
+ frontmatter.patterns = item.patterns;
33
+ }
34
+ // ManualPrompt: DO NOT include promptName (derived from relativeDir + filename)
35
+ // GlobalPrompt: no extra fields
36
+ // Generate YAML frontmatter with clean, readable output
37
+ const yamlStr = yaml.stringify(frontmatter, {
38
+ lineWidth: 0, // Disable line wrapping
39
+ defaultStringType: 'PLAIN', // Use plain strings when possible
40
+ defaultKeyType: 'PLAIN', // Use plain keys (no quotes)
41
+ }).trim();
42
+ // Format as: ---\n{yaml}---\n\n{content}\n
43
+ return `---\n${yamlStr}\n---\n\n${item.content}\n`;
44
+ }
45
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EASL,UAAU,EACV,kBAAkB,EAElB,aAAa,GACd,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,IAAwB;IACnD,yCAAyC;IACzC,MAAM,WAAW,GAA4B;QAC3C,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,IAAI;KAChB,CAAC;IAEF,sFAAsF;IACtF,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACnC,WAAW,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAC7C,CAAC;IAED,0EAA0E;IAC1E,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACjC,CAAC;SAAM,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,WAAW,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAC7C,CAAC;SAAM,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,WAAW,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IACvC,CAAC;IACD,gFAAgF;IAChF,gCAAgC;IAEhC,wDAAwD;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE;QAC1C,SAAS,EAAE,CAAC,EAAE,wBAAwB;QACtC,iBAAiB,EAAE,OAAO,EAAE,kCAAkC;QAC9D,cAAc,EAAE,OAAO,EAAE,6BAA6B;KACvD,CAAC,CAAC,IAAI,EAAE,CAAC;IAEV,2CAA2C;IAC3C,OAAO,QAAQ,OAAO,YAAY,IAAI,CAAC,OAAO,IAAI,CAAC;AACrD,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @a16njs/plugin-a16n - a16n Intermediate Representation (IR) Plugin
3
+ *
4
+ * This plugin enables reading and writing the a16n intermediate representation
5
+ * to/from disk in a human-readable, git-friendly format with versioned schema support.
6
+ *
7
+ * Plugin ID: 'a16n'
8
+ * CLI usage: --from a16n, --to a16n
9
+ *
10
+ * Directory structure: .a16n/<type>/<name>.md
11
+ */
12
+ import type { A16nPlugin } from '@a16njs/models';
13
+ export { parseIRFile, type ParseIRFileResult } from './parse.js';
14
+ export { formatIRFile } from './format.js';
15
+ export { extractRelativeDir, slugify, getNameWithoutExtension } from './utils.js';
16
+ export { emit } from './emit.js';
17
+ /**
18
+ * The a16n IR plugin.
19
+ *
20
+ * Supports reading and writing the a16n intermediate representation format.
21
+ * Files are stored in `.a16n/` directory with YAML frontmatter containing
22
+ * version and type metadata.
23
+ */
24
+ declare const plugin: A16nPlugin;
25
+ export default plugin;
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAmC,MAAM,gBAAgB,CAAC;AAKlF,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;GAMG;AACH,QAAA,MAAM,MAAM,EAAE,UAmCb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @a16njs/plugin-a16n - a16n Intermediate Representation (IR) Plugin
3
+ *
4
+ * This plugin enables reading and writing the a16n intermediate representation
5
+ * to/from disk in a human-readable, git-friendly format with versioned schema support.
6
+ *
7
+ * Plugin ID: 'a16n'
8
+ * CLI usage: --from a16n, --to a16n
9
+ *
10
+ * Directory structure: .a16n/<type>/<name>.md
11
+ */
12
+ import { CustomizationType } from '@a16njs/models';
13
+ import { emit as emitImpl } from './emit.js';
14
+ // Export utility functions and types
15
+ export { parseIRFile } from './parse.js';
16
+ export { formatIRFile } from './format.js';
17
+ export { extractRelativeDir, slugify, getNameWithoutExtension } from './utils.js';
18
+ export { emit } from './emit.js';
19
+ /**
20
+ * The a16n IR plugin.
21
+ *
22
+ * Supports reading and writing the a16n intermediate representation format.
23
+ * Files are stored in `.a16n/` directory with YAML frontmatter containing
24
+ * version and type metadata.
25
+ */
26
+ const plugin = {
27
+ id: 'a16n',
28
+ name: 'a16n Intermediate Representation',
29
+ supports: [
30
+ CustomizationType.GlobalPrompt,
31
+ CustomizationType.FileRule,
32
+ CustomizationType.SimpleAgentSkill,
33
+ CustomizationType.AgentSkillIO,
34
+ CustomizationType.AgentIgnore,
35
+ CustomizationType.ManualPrompt,
36
+ ],
37
+ /**
38
+ * Discover IR files from .a16n/ directory.
39
+ *
40
+ * @param root - Project root directory
41
+ * @returns Discovery result with parsed IR items
42
+ */
43
+ async discover(root) {
44
+ // TODO: Implement in Milestone 5
45
+ return {
46
+ items: [],
47
+ warnings: [],
48
+ };
49
+ },
50
+ /**
51
+ * Emit IR files to .a16n/ directory.
52
+ *
53
+ * @param models - IR items to emit
54
+ * @param root - Project root directory
55
+ * @param options - Emission options
56
+ * @returns Emit result with written files
57
+ */
58
+ emit: emitImpl,
59
+ };
60
+ export default plugin;
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE7C,qCAAqC;AACrC,OAAO,EAAE,WAAW,EAA0B,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,MAAM,GAAe;IACzB,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,kCAAkC;IACxC,QAAQ,EAAE;QACR,iBAAiB,CAAC,YAAY;QAC9B,iBAAiB,CAAC,QAAQ;QAC1B,iBAAiB,CAAC,gBAAgB;QAClC,iBAAiB,CAAC,YAAY;QAC9B,iBAAiB,CAAC,WAAW;QAC7B,iBAAiB,CAAC,YAAY;KAC/B;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,iCAAiC;QACjC,OAAO;YACL,KAAK,EAAE,EAAE;YACT,QAAQ,EAAE,EAAE;SACb,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,IAAI,EAAE,QAAQ;CACf,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type AgentCustomization } from '@a16njs/models';
2
+ /**
3
+ * Result of parsing an IR file.
4
+ * Either returns a parsed item or an error message.
5
+ */
6
+ export interface ParseIRFileResult {
7
+ item?: AgentCustomization;
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Parse an IR file from disk.
12
+ *
13
+ * @param filepath - Absolute path to the file
14
+ * @param filename - Filename (used to derive name)
15
+ * @param relativePath - Path relative to .a16n/ directory (e.g., ".a16n/global-prompt/coding-standards.md")
16
+ * @returns ParseIRFileResult with either item or error
17
+ */
18
+ export declare function parseIRFile(filepath: string, filename: string, relativePath: string): Promise<ParseIRFileResult>;
19
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,kBAAkB,EAUxB,MAAM,gBAAgB,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAiBD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,iBAAiB,CAAC,CA2H5B"}
package/dist/parse.js ADDED
@@ -0,0 +1,122 @@
1
+ import * as fs from 'fs/promises';
2
+ import matter from 'gray-matter';
3
+ import { CustomizationType, createId, parseIRVersion, } from '@a16njs/models';
4
+ /**
5
+ * Parse an IR file from disk.
6
+ *
7
+ * @param filepath - Absolute path to the file
8
+ * @param filename - Filename (used to derive name)
9
+ * @param relativePath - Path relative to .a16n/ directory (e.g., ".a16n/global-prompt/coding-standards.md")
10
+ * @returns ParseIRFileResult with either item or error
11
+ */
12
+ export async function parseIRFile(filepath, filename, relativePath) {
13
+ try {
14
+ // Read file content
15
+ const content = await fs.readFile(filepath, 'utf-8');
16
+ // Parse YAML frontmatter
17
+ let parsed;
18
+ try {
19
+ parsed = matter(content);
20
+ }
21
+ catch (err) {
22
+ return { error: `Failed to parse YAML frontmatter: ${err.message}` };
23
+ }
24
+ const frontmatter = parsed.data;
25
+ const body = parsed.content;
26
+ // Validate required fields
27
+ if (!frontmatter.version) {
28
+ return { error: 'Missing required field: version' };
29
+ }
30
+ if (!frontmatter.type) {
31
+ return { error: 'Missing required field: type' };
32
+ }
33
+ // Validate version format
34
+ const versionParsed = parseIRVersion(frontmatter.version);
35
+ if (!versionParsed) {
36
+ return { error: `Invalid version format: ${frontmatter.version}` };
37
+ }
38
+ // Validate type
39
+ const type = frontmatter.type;
40
+ if (!Object.values(CustomizationType).includes(type)) {
41
+ return { error: `Invalid type: ${frontmatter.type}` };
42
+ }
43
+ // Extract name from filename (without extension)
44
+ const nameWithoutExt = filename.replace(/\.[^.]+$/, '');
45
+ // Build base IR item
46
+ // metadata is NOT serialized to IR files (transient only), so initialize as empty
47
+ const baseItem = {
48
+ id: createId(type, relativePath),
49
+ type,
50
+ version: frontmatter.version,
51
+ sourcePath: relativePath,
52
+ content: body,
53
+ relativeDir: frontmatter.relativeDir,
54
+ metadata: {},
55
+ };
56
+ // Handle type-specific fields
57
+ switch (type) {
58
+ case CustomizationType.GlobalPrompt: {
59
+ const item = {
60
+ ...baseItem,
61
+ type: CustomizationType.GlobalPrompt,
62
+ };
63
+ return { item };
64
+ }
65
+ case CustomizationType.FileRule: {
66
+ if (!frontmatter.globs || !Array.isArray(frontmatter.globs)) {
67
+ return { error: 'FileRule requires globs array in frontmatter' };
68
+ }
69
+ const item = {
70
+ ...baseItem,
71
+ type: CustomizationType.FileRule,
72
+ globs: frontmatter.globs,
73
+ };
74
+ return { item };
75
+ }
76
+ case CustomizationType.SimpleAgentSkill: {
77
+ if (!frontmatter.description || typeof frontmatter.description !== 'string') {
78
+ return { error: 'SimpleAgentSkill requires description string in frontmatter' };
79
+ }
80
+ const item = {
81
+ ...baseItem,
82
+ type: CustomizationType.SimpleAgentSkill,
83
+ description: frontmatter.description,
84
+ };
85
+ return { item };
86
+ }
87
+ case CustomizationType.ManualPrompt: {
88
+ // Derive promptName from relativeDir + filename
89
+ const promptName = frontmatter.relativeDir
90
+ ? `${frontmatter.relativeDir}/${nameWithoutExt}`
91
+ : nameWithoutExt;
92
+ const item = {
93
+ ...baseItem,
94
+ type: CustomizationType.ManualPrompt,
95
+ promptName,
96
+ };
97
+ return { item };
98
+ }
99
+ case CustomizationType.AgentIgnore: {
100
+ if (!frontmatter.patterns || !Array.isArray(frontmatter.patterns)) {
101
+ return { error: 'AgentIgnore requires patterns array in frontmatter' };
102
+ }
103
+ const item = {
104
+ ...baseItem,
105
+ type: CustomizationType.AgentIgnore,
106
+ patterns: frontmatter.patterns,
107
+ };
108
+ return { item };
109
+ }
110
+ case CustomizationType.AgentSkillIO:
111
+ // AgentSkillIO uses verbatim AgentSkills.io format (NO IR frontmatter)
112
+ // This should be handled by readAgentSkillIO from @a16njs/models
113
+ return { error: 'AgentSkillIO should be parsed using readAgentSkillIO()' };
114
+ default:
115
+ return { error: `Unsupported type: ${type}` };
116
+ }
117
+ }
118
+ catch (err) {
119
+ return { error: `Failed to read file: ${err.message}` };
120
+ }
121
+ }
122
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Extract relative directory from a full path relative to base directory.
3
+ *
4
+ * Example:
5
+ * - extractRelativeDir('.a16n/global-prompt/coding-standards.md', '.a16n') -> undefined
6
+ * - extractRelativeDir('.a16n/global-prompt/shared/company/standards.md', '.a16n/global-prompt') -> 'shared/company'
7
+ *
8
+ * @param fullPath - Full path to the file
9
+ * @param baseDir - Base directory to extract relative path from
10
+ * @returns Relative directory or undefined if file is directly in baseDir
11
+ */
12
+ export declare function extractRelativeDir(fullPath: string, baseDir: string): string | undefined;
13
+ /**
14
+ * Slugify a name for use as a filename.
15
+ * Converts to lowercase, replaces spaces and special chars with hyphens.
16
+ *
17
+ * @param name - Name to slugify
18
+ * @returns Slugified name safe for filesystem
19
+ */
20
+ export declare function slugify(name: string): string;
21
+ /**
22
+ * Get filename without extension.
23
+ * Uses path.parse().name to properly handle dotfiles (e.g., ".env" -> ".env").
24
+ *
25
+ * @param filename - Filename with extension
26
+ * @returns Filename without extension
27
+ */
28
+ export declare function getNameWithoutExtension(filename: string): string;
29
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiBxF;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM5C;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhE"}
package/dist/utils.js ADDED
@@ -0,0 +1,51 @@
1
+ import * as path from 'path';
2
+ /**
3
+ * Extract relative directory from a full path relative to base directory.
4
+ *
5
+ * Example:
6
+ * - extractRelativeDir('.a16n/global-prompt/coding-standards.md', '.a16n') -> undefined
7
+ * - extractRelativeDir('.a16n/global-prompt/shared/company/standards.md', '.a16n/global-prompt') -> 'shared/company'
8
+ *
9
+ * @param fullPath - Full path to the file
10
+ * @param baseDir - Base directory to extract relative path from
11
+ * @returns Relative directory or undefined if file is directly in baseDir
12
+ */
13
+ export function extractRelativeDir(fullPath, baseDir) {
14
+ const normalized = path.normalize(fullPath);
15
+ const normalizedBase = path.normalize(baseDir);
16
+ // Get the directory containing the file
17
+ const fileDir = path.dirname(normalized);
18
+ // Get relative path from base to file directory
19
+ const relative = path.relative(normalizedBase, fileDir);
20
+ // If relative path is empty or goes up directories (..), return undefined
21
+ if (!relative || relative === '.' || relative.startsWith('..')) {
22
+ return undefined;
23
+ }
24
+ // Normalize to POSIX-style forward slashes for cross-platform consistency
25
+ return relative.split(path.sep).join('/');
26
+ }
27
+ /**
28
+ * Slugify a name for use as a filename.
29
+ * Converts to lowercase, replaces spaces and special chars with hyphens.
30
+ *
31
+ * @param name - Name to slugify
32
+ * @returns Slugified name safe for filesystem
33
+ */
34
+ export function slugify(name) {
35
+ return name
36
+ .toLowerCase()
37
+ .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with hyphens
38
+ .replace(/^-+|-+$/g, '') // Trim leading/trailing hyphens
39
+ || 'unnamed'; // Fallback if empty
40
+ }
41
+ /**
42
+ * Get filename without extension.
43
+ * Uses path.parse().name to properly handle dotfiles (e.g., ".env" -> ".env").
44
+ *
45
+ * @param filename - Filename with extension
46
+ * @returns Filename without extension
47
+ */
48
+ export function getNameWithoutExtension(filename) {
49
+ return path.parse(filename).name;
50
+ }
51
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,OAAe;IAClE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAE/C,wCAAwC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzC,gDAAgD;IAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IAExD,0EAA0E;IAC1E,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,0EAA0E;IAC1E,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAE,wCAAwC;SACrE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAM,gCAAgC;WAC3D,SAAS,CAAC,CAAE,oBAAoB;AACvC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACtD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;AACnC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a16njs/plugin-a16n",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "a16n Intermediate Representation (IR) plugin for a16n",
5
5
  "license": "AGPL-3.0",
6
6
  "author": "Texarkanine",
@@ -33,7 +33,8 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "gray-matter": "^4.0.3",
36
- "@a16njs/models": "0.6.0"
36
+ "yaml": "^2.8.2",
37
+ "@a16njs/models": "0.7.0"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@types/node": "^20.0.0",