@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 +23 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +180 -0
- package/dist/emit.js.map +1 -0
- package/dist/format.d.ts +14 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +45 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +19 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +122 -0
- package/dist/parse.js.map +1 -0
- package/dist/utils.d.ts +29 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +51 -0
- package/dist/utils.js.map +1 -0
- package/package.json +3 -2
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
|
package/dist/emit.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"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"}
|
package/dist/format.d.ts
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/parse.d.ts
ADDED
|
@@ -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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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -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.
|
|
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
|
-
"
|
|
36
|
+
"yaml": "^2.8.2",
|
|
37
|
+
"@a16njs/models": "0.7.0"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@types/node": "^20.0.0",
|