@audiodn/agent-kit 0.1.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -0
  3. package/assets/content/instructions.md +48 -0
  4. package/assets/content/partials/auth.md +17 -0
  5. package/assets/content/partials/compatibility.md +10 -0
  6. package/assets/content/partials/playback.md +14 -0
  7. package/assets/content/partials/processing.md +17 -0
  8. package/assets/content/partials/security.md +14 -0
  9. package/assets/content/partials/upload.md +17 -0
  10. package/assets/content/partials/webhooks.md +13 -0
  11. package/assets/skill/SKILL.md +52 -0
  12. package/assets/skill/references/authentication.md +27 -0
  13. package/assets/skill/references/playback.md +29 -0
  14. package/assets/skill/references/processing.md +23 -0
  15. package/assets/skill/references/security.md +30 -0
  16. package/assets/skill/references/upload-flow.md +29 -0
  17. package/assets/skill/references/webhooks.md +19 -0
  18. package/assets/skill/scripts/known-endpoints.json +28 -0
  19. package/assets/skill/scripts/validate.mjs +287 -0
  20. package/assets/skill/templates/cloudflare-worker.md +39 -0
  21. package/assets/skill/templates/nextjs.md +47 -0
  22. package/assets/skill/templates/node-server.md +44 -0
  23. package/assets/skill/templates/vue-nuxt.md +49 -0
  24. package/assets/snapshots/llms-full.txt +347 -0
  25. package/assets/snapshots/openapi.json +1416 -0
  26. package/assets/snapshots/sources.json +19 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +101 -0
  29. package/dist/commands/init.d.ts +8 -0
  30. package/dist/commands/init.js +35 -0
  31. package/dist/commands/list.d.ts +1 -0
  32. package/dist/commands/list.js +28 -0
  33. package/dist/commands/uninstall.d.ts +6 -0
  34. package/dist/commands/uninstall.js +20 -0
  35. package/dist/commands/validate.d.ts +5 -0
  36. package/dist/commands/validate.js +18 -0
  37. package/dist/core/formats.d.ts +23 -0
  38. package/dist/core/formats.js +43 -0
  39. package/dist/core/fsutil.d.ts +5 -0
  40. package/dist/core/fsutil.js +34 -0
  41. package/dist/core/install.d.ts +16 -0
  42. package/dist/core/install.js +76 -0
  43. package/dist/core/markers.d.ts +5 -0
  44. package/dist/core/markers.js +13 -0
  45. package/dist/core/merge.d.ts +21 -0
  46. package/dist/core/merge.js +38 -0
  47. package/dist/core/prompt.d.ts +2 -0
  48. package/dist/core/prompt.js +23 -0
  49. package/dist/core/render.d.ts +5 -0
  50. package/dist/core/render.js +21 -0
  51. package/dist/core/report.d.ts +10 -0
  52. package/dist/core/report.js +35 -0
  53. package/dist/core/skill.d.ts +13 -0
  54. package/dist/core/skill.js +117 -0
  55. package/dist/paths.d.ts +5 -0
  56. package/dist/paths.js +10 -0
  57. package/dist/version.d.ts +1 -0
  58. package/dist/version.js +12 -0
  59. package/package.json +61 -0
@@ -0,0 +1,19 @@
1
+ {
2
+ "openapiVersion": "1.0.0",
3
+ "generatedBy": "scripts/sync-canonical.mjs",
4
+ "note": "Seeded from the canonical monorepo openapi.json (ahead of the current public deploy). scripts/sync-canonical.mjs refreshes from https://audiodeliverynetwork.com once redeployed.",
5
+ "sources": [
6
+ {
7
+ "name": "openapi.json",
8
+ "url": "https://audiodeliverynetwork.com/openapi.json",
9
+ "fetchedAt": "2026-07-13",
10
+ "sha256": "e1554673a4d7287a15460bdc2761e0bbc38286b4e101df26b00fc5e9205e2deb"
11
+ },
12
+ {
13
+ "name": "llms-full.txt",
14
+ "url": "https://audiodeliverynetwork.com/llms-full.txt",
15
+ "fetchedAt": "2026-07-13",
16
+ "sha256": "41980ad7654d997132683399b6a4ab38fce87944ebb9c5cf1779daea996d18af"
17
+ }
18
+ ]
19
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { runInit } from './commands/init.js';
3
+ import { runList } from './commands/list.js';
4
+ import { runUninstall } from './commands/uninstall.js';
5
+ import { runValidate } from './commands/validate.js';
6
+ import { getVersion } from './version.js';
7
+ const VALUE_FLAGS = new Set(['formats', 'cwd', 'severity', 'path']);
8
+ function parseArgs(argv) {
9
+ const _ = [];
10
+ const flags = {};
11
+ for (let i = 0; i < argv.length; i++) {
12
+ const arg = argv[i];
13
+ if (arg.startsWith('--')) {
14
+ const eq = arg.indexOf('=');
15
+ if (eq !== -1) {
16
+ flags[arg.slice(2, eq)] = arg.slice(eq + 1);
17
+ }
18
+ else {
19
+ const key = arg.slice(2);
20
+ if (VALUE_FLAGS.has(key) && i + 1 < argv.length && !argv[i + 1].startsWith('-')) {
21
+ flags[key] = argv[++i];
22
+ }
23
+ else {
24
+ flags[key] = true;
25
+ }
26
+ }
27
+ }
28
+ else if (arg.startsWith('-') && arg.length > 1) {
29
+ for (const ch of arg.slice(1))
30
+ flags[ch] = true;
31
+ }
32
+ else {
33
+ _.push(arg);
34
+ }
35
+ }
36
+ return { _, flags };
37
+ }
38
+ const HELP = `@audiodn/agent-kit — AudioDN guidance for AI coding agents
39
+
40
+ Usage:
41
+ npx @audiodn/agent-kit <command> [options]
42
+
43
+ Commands:
44
+ init Install AudioDN guidance (merge-safe, idempotent)
45
+ validate [path] Scan a project for common AudioDN mistakes
46
+ uninstall Remove managed blocks / kit-owned files only
47
+ list Show formats and bundled documentation versions
48
+
49
+ Options:
50
+ -y, --yes Non-interactive; install all formats unless --formats given
51
+ --formats <ids> Comma-separated: agents,claude,copilot,cursor,skill (or "all")
52
+ --dry-run Report actions without writing
53
+ --force Overwrite unmanaged conflicts
54
+ --cwd <dir> Target directory (default: current directory)
55
+ --json (validate) Output findings as JSON
56
+ -v, --version Print version
57
+ -h, --help Show this help
58
+ `;
59
+ async function main() {
60
+ const { _, flags } = parseArgs(process.argv.slice(2));
61
+ const cmd = _[0];
62
+ const cwd = flags.cwd || process.cwd();
63
+ if (flags.version || flags.v) {
64
+ console.log(getVersion());
65
+ return 0;
66
+ }
67
+ if (!cmd || flags.help || flags.h || cmd === 'help') {
68
+ console.log(HELP);
69
+ return 0;
70
+ }
71
+ switch (cmd) {
72
+ case 'init':
73
+ return runInit({
74
+ cwd,
75
+ yes: Boolean(flags.yes || flags.y),
76
+ dryRun: Boolean(flags['dry-run']),
77
+ force: Boolean(flags.force),
78
+ formats: typeof flags.formats === 'string' ? flags.formats : undefined,
79
+ });
80
+ case 'validate':
81
+ return runValidate({ path: _[1] || cwd, json: Boolean(flags.json) });
82
+ case 'uninstall':
83
+ return runUninstall({
84
+ cwd,
85
+ dryRun: Boolean(flags['dry-run']),
86
+ formats: typeof flags.formats === 'string' ? flags.formats : undefined,
87
+ });
88
+ case 'list':
89
+ return runList();
90
+ default:
91
+ console.error(`Unknown command: ${cmd}\n`);
92
+ console.log(HELP);
93
+ return 1;
94
+ }
95
+ }
96
+ main()
97
+ .then((code) => process.exit(code))
98
+ .catch((err) => {
99
+ console.error(err instanceof Error ? err.message : err);
100
+ process.exit(1);
101
+ });
@@ -0,0 +1,8 @@
1
+ export interface InitArgs {
2
+ cwd: string;
3
+ yes: boolean;
4
+ dryRun: boolean;
5
+ force: boolean;
6
+ formats?: string;
7
+ }
8
+ export declare function runInit(args: InitArgs): Promise<number>;
@@ -0,0 +1,35 @@
1
+ import { ALL_FORMAT_IDS, parseFormatList } from '../core/formats.js';
2
+ import { installFormats } from '../core/install.js';
3
+ import { promptFormats } from '../core/prompt.js';
4
+ import { hasConflicts, printReport } from '../core/report.js';
5
+ import { getVersion } from '../version.js';
6
+ export async function runInit(args) {
7
+ let ids;
8
+ if (args.formats) {
9
+ const { ids: parsed, unknown } = parseFormatList(args.formats);
10
+ if (unknown.length) {
11
+ console.error(`Unknown format(s): ${unknown.join(', ')}. Valid: ${ALL_FORMAT_IDS.join(', ')}, all`);
12
+ return 1;
13
+ }
14
+ ids = parsed.length ? parsed : [...ALL_FORMAT_IDS];
15
+ }
16
+ else if (args.yes) {
17
+ ids = [...ALL_FORMAT_IDS];
18
+ }
19
+ else {
20
+ ids = await promptFormats();
21
+ }
22
+ if (ids.length === 0) {
23
+ console.log('No formats selected. Nothing to do.');
24
+ return 0;
25
+ }
26
+ const entries = installFormats({
27
+ cwd: args.cwd,
28
+ version: getVersion(),
29
+ formats: ids,
30
+ dryRun: args.dryRun,
31
+ force: args.force,
32
+ });
33
+ printReport(entries, { dryRun: args.dryRun });
34
+ return hasConflicts(entries) && !args.force ? 2 : 0;
35
+ }
@@ -0,0 +1 @@
1
+ export declare function runList(): number;
@@ -0,0 +1,28 @@
1
+ import { join } from 'node:path';
2
+ import { FORMATS } from '../core/formats.js';
3
+ import { readIfExists } from '../core/fsutil.js';
4
+ import { SNAPSHOTS_DIR } from '../paths.js';
5
+ import { getVersion } from '../version.js';
6
+ export function runList() {
7
+ console.log(`@audiodn/agent-kit v${getVersion()}`);
8
+ console.log('\nFormats:');
9
+ for (const f of FORMATS) {
10
+ const target = f.kind === 'skill' ? `${f.target}/` : f.target;
11
+ console.log(` ${f.id.padEnd(8)} -> ${target} (${f.label})`);
12
+ }
13
+ const raw = readIfExists(join(SNAPSHOTS_DIR, 'sources.json'));
14
+ if (raw) {
15
+ try {
16
+ const s = JSON.parse(raw);
17
+ console.log('\nBundled canonical snapshots:');
18
+ console.log(` OpenAPI version: ${s.openapiVersion ?? 'unknown'}`);
19
+ for (const src of s.sources ?? []) {
20
+ console.log(` - ${src.name} (${src.url}) fetched ${src.fetchedAt ?? 'unknown'}`);
21
+ }
22
+ }
23
+ catch {
24
+ /* ignore malformed snapshot manifest */
25
+ }
26
+ }
27
+ return 0;
28
+ }
@@ -0,0 +1,6 @@
1
+ export interface UninstallArgs {
2
+ cwd: string;
3
+ dryRun: boolean;
4
+ formats?: string;
5
+ }
6
+ export declare function runUninstall(args: UninstallArgs): number;
@@ -0,0 +1,20 @@
1
+ import { ALL_FORMAT_IDS, parseFormatList } from '../core/formats.js';
2
+ import { uninstallFormats } from '../core/install.js';
3
+ import { printReport } from '../core/report.js';
4
+ export function runUninstall(args) {
5
+ let ids;
6
+ if (args.formats) {
7
+ const { ids: parsed, unknown } = parseFormatList(args.formats);
8
+ if (unknown.length) {
9
+ console.error(`Unknown format(s): ${unknown.join(', ')}.`);
10
+ return 1;
11
+ }
12
+ ids = parsed.length ? parsed : [...ALL_FORMAT_IDS];
13
+ }
14
+ else {
15
+ ids = [...ALL_FORMAT_IDS];
16
+ }
17
+ const entries = uninstallFormats({ cwd: args.cwd, formats: ids, dryRun: args.dryRun });
18
+ printReport(entries, { dryRun: args.dryRun });
19
+ return 0;
20
+ }
@@ -0,0 +1,5 @@
1
+ export interface ValidateArgs {
2
+ path: string;
3
+ json: boolean;
4
+ }
5
+ export declare function runValidate(args: ValidateArgs): Promise<number>;
@@ -0,0 +1,18 @@
1
+ import { join, resolve } from 'node:path';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { SKILL_DIR } from '../paths.js';
4
+ // The validator implementation lives in the portable Skill payload so it can be
5
+ // run standalone (without the kit) once installed. We import it here to avoid
6
+ // duplicating the rule logic.
7
+ export async function runValidate(args) {
8
+ const validatorUrl = pathToFileURL(join(SKILL_DIR, 'scripts', 'validate.mjs')).href;
9
+ const mod = await import(validatorUrl);
10
+ const result = await mod.runValidation(resolve(args.path), {});
11
+ if (args.json) {
12
+ console.log(JSON.stringify(result, null, 2));
13
+ }
14
+ else {
15
+ mod.printFindings(result);
16
+ }
17
+ return result.errorCount > 0 ? 1 : 0;
18
+ }
@@ -0,0 +1,23 @@
1
+ export type FormatId = 'agents' | 'claude' | 'copilot' | 'cursor' | 'skill';
2
+ export interface BlockFormat {
3
+ id: Exclude<FormatId, 'skill'>;
4
+ label: string;
5
+ kind: 'block';
6
+ target: string;
7
+ appendIfUnmanaged: boolean;
8
+ preambleOnCreate?: string;
9
+ }
10
+ export interface SkillFormat {
11
+ id: 'skill';
12
+ label: string;
13
+ kind: 'skill';
14
+ target: string;
15
+ }
16
+ export type Format = BlockFormat | SkillFormat;
17
+ export declare const FORMATS: Format[];
18
+ export declare const ALL_FORMAT_IDS: FormatId[];
19
+ export declare function resolveFormats(ids: FormatId[]): Format[];
20
+ export declare function parseFormatList(value: string): {
21
+ ids: FormatId[];
22
+ unknown: string[];
23
+ };
@@ -0,0 +1,43 @@
1
+ const CURSOR_FRONTMATTER = `---
2
+ description: AudioDN integration guidance for AI coding agents (auth, upload flow, processing readiness, playback, security).
3
+ globs:
4
+ alwaysApply: false
5
+ ---`;
6
+ export const FORMATS = [
7
+ { id: 'agents', label: 'AGENTS.md', kind: 'block', target: 'AGENTS.md', appendIfUnmanaged: true },
8
+ { id: 'claude', label: 'CLAUDE.md', kind: 'block', target: 'CLAUDE.md', appendIfUnmanaged: true },
9
+ {
10
+ id: 'copilot',
11
+ label: 'GitHub Copilot',
12
+ kind: 'block',
13
+ target: '.github/copilot-instructions.md',
14
+ appendIfUnmanaged: true,
15
+ },
16
+ {
17
+ id: 'cursor',
18
+ label: 'Cursor rule',
19
+ kind: 'block',
20
+ target: '.cursor/rules/audiodn.mdc',
21
+ appendIfUnmanaged: false,
22
+ preambleOnCreate: CURSOR_FRONTMATTER,
23
+ },
24
+ { id: 'skill', label: 'AudioDN Skill', kind: 'skill', target: '.agents/skills/audiodn' },
25
+ ];
26
+ export const ALL_FORMAT_IDS = FORMATS.map((f) => f.id);
27
+ export function resolveFormats(ids) {
28
+ return FORMATS.filter((f) => ids.includes(f.id));
29
+ }
30
+ export function parseFormatList(value) {
31
+ const ids = [];
32
+ const unknown = [];
33
+ for (const raw of value.split(',').map((s) => s.trim()).filter(Boolean)) {
34
+ if (raw === 'all') {
35
+ return { ids: [...ALL_FORMAT_IDS], unknown: [] };
36
+ }
37
+ if (ALL_FORMAT_IDS.includes(raw))
38
+ ids.push(raw);
39
+ else
40
+ unknown.push(raw);
41
+ }
42
+ return { ids, unknown };
43
+ }
@@ -0,0 +1,5 @@
1
+ export declare function sha256(input: string | Buffer): string;
2
+ export declare function readIfExists(path: string): string | null;
3
+ export declare function writeFileEnsuringDir(path: string, content: string): void;
4
+ export declare function listFilesRecursive(root: string): string[];
5
+ export declare function relativeFiles(root: string): string[];
@@ -0,0 +1,34 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join, relative } from 'node:path';
4
+ export function sha256(input) {
5
+ return createHash('sha256').update(input).digest('hex');
6
+ }
7
+ export function readIfExists(path) {
8
+ try {
9
+ return readFileSync(path, 'utf8');
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ export function writeFileEnsuringDir(path, content) {
16
+ mkdirSync(dirname(path), { recursive: true });
17
+ writeFileSync(path, content, 'utf8');
18
+ }
19
+ export function listFilesRecursive(root) {
20
+ const out = [];
21
+ if (!existsSync(root))
22
+ return out;
23
+ for (const name of readdirSync(root)) {
24
+ const full = join(root, name);
25
+ if (statSync(full).isDirectory())
26
+ out.push(...listFilesRecursive(full));
27
+ else
28
+ out.push(full);
29
+ }
30
+ return out;
31
+ }
32
+ export function relativeFiles(root) {
33
+ return listFilesRecursive(root).map((f) => relative(root, f).split('\\').join('/'));
34
+ }
@@ -0,0 +1,16 @@
1
+ import type { FormatId } from './formats.js';
2
+ import type { ReportEntry } from './report.js';
3
+ export interface InstallOptions {
4
+ cwd: string;
5
+ version: string;
6
+ formats: FormatId[];
7
+ dryRun?: boolean;
8
+ force?: boolean;
9
+ }
10
+ export declare function installFormats(opts: InstallOptions): ReportEntry[];
11
+ export interface UninstallOptions {
12
+ cwd: string;
13
+ formats: FormatId[];
14
+ dryRun?: boolean;
15
+ }
16
+ export declare function uninstallFormats(opts: UninstallOptions): ReportEntry[];
@@ -0,0 +1,76 @@
1
+ import { rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { FORMATS } from './formats.js';
4
+ import { readIfExists, writeFileEnsuringDir } from './fsutil.js';
5
+ import { mergeBlock, removeBlock } from './merge.js';
6
+ import { buildGuidanceBody } from './render.js';
7
+ import { installSkill, uninstallSkill } from './skill.js';
8
+ export function installFormats(opts) {
9
+ const body = buildGuidanceBody();
10
+ const entries = [];
11
+ const selected = FORMATS.filter((f) => opts.formats.includes(f.id));
12
+ for (const fmt of selected) {
13
+ if (fmt.kind === 'skill') {
14
+ entries.push(...installSkill(opts.cwd, opts.version, { dryRun: opts.dryRun, force: opts.force }));
15
+ continue;
16
+ }
17
+ const target = join(opts.cwd, fmt.target);
18
+ const existing = readIfExists(target);
19
+ const result = mergeBlock(existing, opts.version, body, {
20
+ preambleOnCreate: fmt.preambleOnCreate,
21
+ appendIfUnmanaged: fmt.appendIfUnmanaged,
22
+ });
23
+ if (result.action === 'conflict') {
24
+ const fresh = mergeBlock(null, opts.version, body, {
25
+ preambleOnCreate: fmt.preambleOnCreate,
26
+ appendIfUnmanaged: fmt.appendIfUnmanaged,
27
+ }).content;
28
+ if (opts.force) {
29
+ if (!opts.dryRun)
30
+ writeFileEnsuringDir(target, fresh);
31
+ entries.push({ path: fmt.target, action: 'update', note: 'forced overwrite' });
32
+ }
33
+ else {
34
+ if (!opts.dryRun)
35
+ writeFileEnsuringDir(`${target}.audiodn.new`, fresh);
36
+ entries.push({ path: fmt.target, action: 'conflict', note: 'wrote .audiodn.new' });
37
+ }
38
+ continue;
39
+ }
40
+ if (result.content !== null && result.action !== 'unchanged' && !opts.dryRun) {
41
+ writeFileEnsuringDir(target, result.content);
42
+ }
43
+ entries.push({ path: fmt.target, action: result.action });
44
+ }
45
+ return entries;
46
+ }
47
+ export function uninstallFormats(opts) {
48
+ const entries = [];
49
+ const selected = FORMATS.filter((f) => opts.formats.includes(f.id));
50
+ for (const fmt of selected) {
51
+ if (fmt.kind === 'skill') {
52
+ entries.push(...uninstallSkill(opts.cwd, { dryRun: opts.dryRun }));
53
+ continue;
54
+ }
55
+ const target = join(opts.cwd, fmt.target);
56
+ const existing = readIfExists(target);
57
+ if (existing === null)
58
+ continue;
59
+ const { content, changed } = removeBlock(existing);
60
+ if (!changed) {
61
+ entries.push({ path: fmt.target, action: 'skipped', note: 'no managed block' });
62
+ continue;
63
+ }
64
+ if (content.trim() === '') {
65
+ if (!opts.dryRun)
66
+ rmSync(target);
67
+ entries.push({ path: fmt.target, action: 'removed' });
68
+ }
69
+ else {
70
+ if (!opts.dryRun)
71
+ writeFileEnsuringDir(target, content);
72
+ entries.push({ path: fmt.target, action: 'removed', note: 'kept your content' });
73
+ }
74
+ }
75
+ return entries;
76
+ }
@@ -0,0 +1,5 @@
1
+ export declare const END_MARKER = "<!-- AUDIODN:END -->";
2
+ export declare const BLOCK_RE: RegExp;
3
+ export declare function beginMarker(version: string): string;
4
+ export declare function buildBlock(version: string, body: string): string;
5
+ export declare function hasBlock(content: string): boolean;
@@ -0,0 +1,13 @@
1
+ export const END_MARKER = '<!-- AUDIODN:END -->';
2
+ // Matches a managed block regardless of the version recorded in the BEGIN marker.
3
+ export const BLOCK_RE = /<!-- AUDIODN:BEGIN[\s\S]*?<!-- AUDIODN:END -->/;
4
+ export function beginMarker(version) {
5
+ return `<!-- AUDIODN:BEGIN v${version} (managed by @audiodn/agent-kit — do not edit inside) -->`;
6
+ }
7
+ export function buildBlock(version, body) {
8
+ return `${beginMarker(version)}\n${body.trim()}\n${END_MARKER}`;
9
+ }
10
+ export function hasBlock(content) {
11
+ BLOCK_RE.lastIndex = 0;
12
+ return BLOCK_RE.test(content);
13
+ }
@@ -0,0 +1,21 @@
1
+ export type MergeAction = 'create' | 'update' | 'unchanged' | 'append' | 'conflict';
2
+ export interface MergeResult {
3
+ content: string | null;
4
+ action: MergeAction;
5
+ }
6
+ export interface MergeOptions {
7
+ preambleOnCreate?: string;
8
+ appendIfUnmanaged: boolean;
9
+ }
10
+ /**
11
+ * Merge a managed block into (possibly existing) file content without ever
12
+ * clobbering user content outside the block.
13
+ */
14
+ export declare function mergeBlock(existing: string | null, version: string, body: string, opts: MergeOptions): MergeResult;
15
+ /**
16
+ * Remove the managed block. Returns changed=false if there was no block.
17
+ */
18
+ export declare function removeBlock(existing: string): {
19
+ content: string;
20
+ changed: boolean;
21
+ };
@@ -0,0 +1,38 @@
1
+ import { BLOCK_RE, buildBlock, hasBlock } from './markers.js';
2
+ /**
3
+ * Merge a managed block into (possibly existing) file content without ever
4
+ * clobbering user content outside the block.
5
+ */
6
+ export function mergeBlock(existing, version, body, opts) {
7
+ const block = buildBlock(version, body);
8
+ if (existing === null) {
9
+ const preamble = opts.preambleOnCreate ? `${opts.preambleOnCreate.replace(/\n*$/, '')}\n\n` : '';
10
+ return { content: `${preamble}${block}\n`, action: 'create' };
11
+ }
12
+ if (hasBlock(existing)) {
13
+ const replaced = existing.replace(BLOCK_RE, block);
14
+ if (replaced === existing)
15
+ return { content: existing, action: 'unchanged' };
16
+ return { content: replaced, action: 'update' };
17
+ }
18
+ if (opts.appendIfUnmanaged) {
19
+ const sep = existing.endsWith('\n') ? '\n' : '\n\n';
20
+ return { content: `${existing}${sep}${block}\n`, action: 'append' };
21
+ }
22
+ return { content: null, action: 'conflict' };
23
+ }
24
+ /**
25
+ * Remove the managed block. Returns changed=false if there was no block.
26
+ */
27
+ export function removeBlock(existing) {
28
+ if (!hasBlock(existing))
29
+ return { content: existing, changed: false };
30
+ let out = existing.replace(BLOCK_RE, '');
31
+ // Tidy the whitespace left behind.
32
+ out = out.replace(/\n{3,}/g, '\n\n').replace(/^\s+/, '');
33
+ if (out.trim().length > 0)
34
+ out = out.replace(/\s+$/, '') + '\n';
35
+ else
36
+ out = '';
37
+ return { content: out, changed: true };
38
+ }
@@ -0,0 +1,2 @@
1
+ import { type FormatId } from './formats.js';
2
+ export declare function promptFormats(): Promise<FormatId[]>;
@@ -0,0 +1,23 @@
1
+ import { createInterface } from 'node:readline';
2
+ import { ALL_FORMAT_IDS, FORMATS } from './formats.js';
3
+ export async function promptFormats() {
4
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
5
+ const question = (q) => new Promise((res) => rl.question(q, res));
6
+ console.log('AudioDN Agent Kit — select formats to install:');
7
+ FORMATS.forEach((f, i) => console.log(` ${i + 1}. ${f.id.padEnd(8)} ${f.label}`));
8
+ const ans = (await question('Enter comma-separated ids or numbers [all]: ')).trim();
9
+ rl.close();
10
+ if (!ans || ans.toLowerCase() === 'all')
11
+ return [...ALL_FORMAT_IDS];
12
+ const ids = [];
13
+ for (const token of ans.split(',').map((s) => s.trim()).filter(Boolean)) {
14
+ const num = Number(token);
15
+ if (Number.isInteger(num) && num >= 1 && num <= FORMATS.length) {
16
+ ids.push(FORMATS[num - 1].id);
17
+ }
18
+ else if (ALL_FORMAT_IDS.includes(token)) {
19
+ ids.push(token);
20
+ }
21
+ }
22
+ return [...new Set(ids)];
23
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Build the canonical guidance body shared by all block-format outputs. Single
3
+ * source of truth: assets/content/instructions.md with {{PARTIALS}} expanded.
4
+ */
5
+ export declare function buildGuidanceBody(): string;
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { CONTENT_DIR } from '../paths.js';
4
+ const PARTIAL_ORDER = [
5
+ 'auth',
6
+ 'upload',
7
+ 'processing',
8
+ 'playback',
9
+ 'webhooks',
10
+ 'security',
11
+ 'compatibility',
12
+ ];
13
+ /**
14
+ * Build the canonical guidance body shared by all block-format outputs. Single
15
+ * source of truth: assets/content/instructions.md with {{PARTIALS}} expanded.
16
+ */
17
+ export function buildGuidanceBody() {
18
+ const instructions = readFileSync(join(CONTENT_DIR, 'instructions.md'), 'utf8');
19
+ const partials = PARTIAL_ORDER.map((name) => readFileSync(join(CONTENT_DIR, 'partials', `${name}.md`), 'utf8').trim()).join('\n\n');
20
+ return instructions.replace('{{PARTIALS}}', partials).trim();
21
+ }
@@ -0,0 +1,10 @@
1
+ export type Action = 'create' | 'update' | 'unchanged' | 'append' | 'conflict' | 'removed' | 'skipped';
2
+ export interface ReportEntry {
3
+ path: string;
4
+ action: Action;
5
+ note?: string;
6
+ }
7
+ export declare function printReport(entries: ReportEntry[], opts?: {
8
+ dryRun?: boolean;
9
+ }): void;
10
+ export declare function hasConflicts(entries: ReportEntry[]): boolean;
@@ -0,0 +1,35 @@
1
+ const SYMBOL = {
2
+ create: '+ created ',
3
+ update: '~ updated ',
4
+ append: '~ appended',
5
+ unchanged: '= unchanged',
6
+ conflict: '! conflict',
7
+ removed: '- removed ',
8
+ skipped: '. skipped ',
9
+ };
10
+ export function printReport(entries, opts = {}) {
11
+ const prefix = opts.dryRun ? '[dry-run] ' : '';
12
+ if (entries.length === 0) {
13
+ console.log(`${prefix}Nothing to do.`);
14
+ return;
15
+ }
16
+ for (const e of entries) {
17
+ const label = SYMBOL[e.action] ?? e.action;
18
+ const note = e.note ? ` (${e.note})` : '';
19
+ console.log(`${prefix}${label} ${e.path}${note}`);
20
+ }
21
+ const counts = entries.reduce((acc, e) => {
22
+ acc[e.action] = (acc[e.action] ?? 0) + 1;
23
+ return acc;
24
+ }, {});
25
+ const summary = Object.entries(counts)
26
+ .map(([k, v]) => `${v} ${k}`)
27
+ .join(', ');
28
+ console.log(`${prefix}Summary: ${summary}`);
29
+ if (counts.conflict) {
30
+ console.log(`${prefix}Conflicts were written as *.audiodn.new — review and merge, or re-run with --force.`);
31
+ }
32
+ }
33
+ export function hasConflicts(entries) {
34
+ return entries.some((e) => e.action === 'conflict');
35
+ }
@@ -0,0 +1,13 @@
1
+ import type { ReportEntry } from './report.js';
2
+ export declare const SKILL_TARGET_DIR = ".agents/skills/audiodn";
3
+ interface SkillOptions {
4
+ dryRun?: boolean;
5
+ force?: boolean;
6
+ }
7
+ /**
8
+ * Install the Skill payload directory. Kit-owned files are tracked in a
9
+ * manifest so re-runs are idempotent and user edits are never clobbered.
10
+ */
11
+ export declare function installSkill(cwd: string, version: string, opts?: SkillOptions): ReportEntry[];
12
+ export declare function uninstallSkill(cwd: string, opts?: SkillOptions): ReportEntry[];
13
+ export {};