@hanv89/azure-arch-skill 0.4.0 → 0.5.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.
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.withFatalReturn = exports.parseFrontmatter = exports.fetchManifest = exports.headOk = exports.fetchText = exports.fetchWithTimeout = exports.safeResolveTarget = exports.baseUrl = exports.USER_AGENT = exports.FETCH_TIMEOUT_MS = exports.CANARY_ICON_PATH = exports.MANIFEST_PATH = exports.SKILL_NAME = exports.DEFAULT_BASE_RAW_URL = void 0;
29
+ exports.withFatalReturn = exports.stripFrontmatter = exports.parseFrontmatter = exports.fetchManifest = exports.headOk = exports.fetchText = exports.fetchWithTimeout = exports.safeResolveTarget = exports.baseUrl = exports.USER_AGENT = exports.FETCH_TIMEOUT_MS = exports.CANARY_ICON_PATH = exports.MANIFEST_PATH = exports.SKILL_NAME = exports.DEFAULT_BASE_RAW_URL = void 0;
30
30
  const fs = __importStar(require("node:fs/promises"));
31
31
  const path = __importStar(require("node:path"));
32
32
  const package_json_1 = __importDefault(require("../../package.json"));
@@ -258,6 +258,20 @@ function parseFrontmatter(md) {
258
258
  return out;
259
259
  }
260
260
  exports.parseFrontmatter = parseFrontmatter;
261
+ /**
262
+ * Strip the leading `---\n...\n---\n` YAML frontmatter block from a markdown
263
+ * string. Returns the body unchanged if no frontmatter is detected.
264
+ *
265
+ * Used by adapters that re-render the upstream SKILL.md for a different host
266
+ * (e.g. Cursor's `.mdc` rule files, which carry their own frontmatter shape
267
+ * and embed the SKILL.md body without its original frontmatter).
268
+ */
269
+ function stripFrontmatter(md) {
270
+ const text = md.replace(/^/, "");
271
+ const match = text.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
272
+ return match ? text.slice(match[0].length) : text;
273
+ }
274
+ exports.stripFrontmatter = stripFrontmatter;
261
275
  async function withFatalReturn(fn) {
262
276
  try {
263
277
  return await fn();
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.cursorAdapter = exports.parseFrontmatter = exports.fetchWithTimeout = void 0;
27
+ const fs = __importStar(require("node:fs/promises"));
28
+ const path = __importStar(require("node:path"));
29
+ const _shared_1 = require("./_shared");
30
+ Object.defineProperty(exports, "fetchWithTimeout", { enumerable: true, get: function () { return _shared_1.fetchWithTimeout; } });
31
+ Object.defineProperty(exports, "parseFrontmatter", { enumerable: true, get: function () { return _shared_1.parseFrontmatter; } });
32
+ const RULE_BASENAME = "azure-arch-skill.mdc";
33
+ const RULE_DESCRIPTION = "Use this rule when drawing Microsoft Azure or Microsoft Fabric architecture diagrams using PlantUML. " +
34
+ "Triggers on \"draw Azure architecture\", \"create deployment diagram\", \"Lakehouse + Notebook + Warehouse diagram\", " +
35
+ "\"vẽ Azure\", \"PlantUML diagram for [project]\".";
36
+ function defaultTarget() {
37
+ return path.join(process.cwd(), ".cursor", "rules");
38
+ }
39
+ const CWD_DISPLAY = "<cwd>/.cursor/rules";
40
+ async function resolveTarget(target) {
41
+ return (0, _shared_1.safeResolveTarget)(target, process.cwd(), CWD_DISPLAY);
42
+ }
43
+ /**
44
+ * Provenance marker emitted into the rendered .mdc body so `list` / `uninstall`
45
+ * can recognise our rule without re-fetching upstream. The marker is the first
46
+ * non-frontmatter line after the closing `---`.
47
+ */
48
+ function provenanceMarker(version, requiresIcons) {
49
+ return `<!-- ${_shared_1.SKILL_NAME} v${version} (requires_icons: ${requiresIcons}) -->`;
50
+ }
51
+ const PROVENANCE_RE = new RegExp(`<!--\\s*${_shared_1.SKILL_NAME}\\s+v([^\\s]+)\\s+\\(requires_icons:\\s*([^)]+)\\)\\s*-->`);
52
+ function renderRule(skillBody, version, requiresIcons) {
53
+ return [
54
+ "---",
55
+ `description: ${RULE_DESCRIPTION}`,
56
+ "alwaysApply: false",
57
+ "---",
58
+ "",
59
+ provenanceMarker(version, requiresIcons),
60
+ "",
61
+ skillBody.trimStart(),
62
+ ].join("\n");
63
+ }
64
+ async function isOurRuleFile(file) {
65
+ try {
66
+ const body = await fs.readFile(file, "utf8");
67
+ const match = body.match(PROVENANCE_RE);
68
+ if (!match)
69
+ return { ours: false };
70
+ return { ours: true, version: match[1] };
71
+ }
72
+ catch {
73
+ return { ours: false };
74
+ }
75
+ }
76
+ async function install(opts) {
77
+ return (0, _shared_1.withFatalReturn)(async () => {
78
+ const targetDir = await resolveTarget(opts.target ?? defaultTarget());
79
+ const base = (0, _shared_1.baseUrl)();
80
+ const ruleFile = path.join(targetDir, RULE_BASENAME);
81
+ // Fetch manifest first so we know which file is the canonical SKILL.md
82
+ // and what version / requires_icons to embed in the provenance marker.
83
+ const manifest = await (0, _shared_1.fetchManifest)(base);
84
+ if (manifest.name !== _shared_1.SKILL_NAME) {
85
+ throw new Error(`manifest name mismatch: expected '${_shared_1.SKILL_NAME}', got '${manifest.name}'. CLI and bundle are out of sync.`);
86
+ }
87
+ const skillUrl = `${base}/${manifest.files[0].src}`;
88
+ const skillMd = await (0, _shared_1.fetchText)(skillUrl);
89
+ const fm = (0, _shared_1.parseFrontmatter)(skillMd);
90
+ if (!fm.requires_icons) {
91
+ throw new Error("SKILL.md missing requires_icons frontmatter");
92
+ }
93
+ const canaryUrl = `${base}/${_shared_1.CANARY_ICON_PATH}`;
94
+ const reachable = await (0, _shared_1.headOk)(canaryUrl);
95
+ if (!reachable) {
96
+ throw new Error(`icon-set unreachable - HEAD ${canaryUrl} failed (skill declares requires_icons=${fm.requires_icons}; this release verifies reachability only, strict semver match planned)`);
97
+ }
98
+ const exists = await fs.stat(ruleFile).then(() => true).catch(() => false);
99
+ if (exists && !opts.overwrite) {
100
+ const probe = await isOurRuleFile(ruleFile);
101
+ throw new Error(probe.ours
102
+ ? `${ruleFile} already contains an install. Run 'azure-arch-skill update --agent=cursor' to refresh.`
103
+ : `${ruleFile} exists but is not one of ours (no '${_shared_1.SKILL_NAME}' provenance marker). Move/rename the file or remove it manually if intentional.`);
104
+ }
105
+ await fs.mkdir(targetDir, { recursive: true });
106
+ const body = (0, _shared_1.stripFrontmatter)(skillMd);
107
+ const rendered = renderRule(body, fm.version ?? manifest.version, fm.requires_icons);
108
+ await fs.writeFile(ruleFile, rendered, "utf8");
109
+ process.stdout.write(`installed ${_shared_1.SKILL_NAME} to ${ruleFile}\n`);
110
+ return 0;
111
+ });
112
+ }
113
+ async function uninstall(opts) {
114
+ return (0, _shared_1.withFatalReturn)(async () => {
115
+ const targetDir = await resolveTarget(opts.target ?? defaultTarget());
116
+ const ruleFile = path.join(targetDir, RULE_BASENAME);
117
+ const exists = await fs.stat(ruleFile).then(() => true).catch(() => false);
118
+ if (!exists) {
119
+ process.stdout.write(`(nothing to uninstall at ${ruleFile})\n`);
120
+ return 0;
121
+ }
122
+ const probe = await isOurRuleFile(ruleFile);
123
+ if (!probe.ours) {
124
+ throw new Error(`refusing to remove ${ruleFile} - missing '${_shared_1.SKILL_NAME}' provenance marker. Move/rename the file or remove it manually if intentional.`);
125
+ }
126
+ await fs.unlink(ruleFile);
127
+ process.stdout.write(`uninstalled ${_shared_1.SKILL_NAME} from ${ruleFile}\n`);
128
+ return 0;
129
+ });
130
+ }
131
+ async function update(opts) {
132
+ return install({ ...opts, overwrite: true });
133
+ }
134
+ async function list(opts) {
135
+ return (0, _shared_1.withFatalReturn)(async () => {
136
+ const targetDir = await resolveTarget(opts.target ?? defaultTarget());
137
+ const ruleFile = path.join(targetDir, RULE_BASENAME);
138
+ const probe = await isOurRuleFile(ruleFile);
139
+ if (!probe.ours) {
140
+ process.stdout.write("(no skills installed)\n");
141
+ return 0;
142
+ }
143
+ process.stdout.write(`${_shared_1.SKILL_NAME}\t${probe.version ?? "?"}\n`);
144
+ return 0;
145
+ });
146
+ }
147
+ exports.cursorAdapter = { install, uninstall, update, list };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SUPPORTED_AGENTS = exports.ADAPTERS = void 0;
4
+ const claude_code_1 = require("./claude-code");
5
+ const codex_1 = require("./codex");
6
+ const cursor_1 = require("./cursor");
7
+ // Single source of truth for the supported-agent set. Add a new adapter by
8
+ // importing it here and adding one entry below; both `src/index.ts` (CLI
9
+ // dispatch + --agent help text) and `adapters-roundtrip.test.ts` (test loop)
10
+ // read from this map directly.
11
+ exports.ADAPTERS = {
12
+ "claude-code": claude_code_1.claudeCodeAdapter,
13
+ "codex": codex_1.codexAdapter,
14
+ "cursor": cursor_1.cursorAdapter,
15
+ };
16
+ exports.SUPPORTED_AGENTS = Object.keys(exports.ADAPTERS);
package/dist/index.js CHANGED
@@ -6,18 +6,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const commander_1 = require("commander");
8
8
  const package_json_1 = __importDefault(require("../package.json"));
9
- const claude_code_1 = require("./adapters/claude-code");
10
- const codex_1 = require("./adapters/codex");
11
- const ADAPTERS = {
12
- "claude-code": claude_code_1.claudeCodeAdapter,
13
- "codex": codex_1.codexAdapter,
14
- };
15
- const SUPPORTED_AGENTS = Object.keys(ADAPTERS);
9
+ const registry_1 = require("./adapters/registry");
16
10
  function pickAdapter(agent) {
17
- if (!(agent in ADAPTERS)) {
18
- throw new Error(`unknown agent: ${agent} (supported: ${SUPPORTED_AGENTS.join(", ")})`);
11
+ if (!(agent in registry_1.ADAPTERS)) {
12
+ throw new Error(`unknown agent: ${agent} (supported: ${registry_1.SUPPORTED_AGENTS.join(", ")})`);
19
13
  }
20
- return ADAPTERS[agent];
14
+ return registry_1.ADAPTERS[agent];
21
15
  }
22
16
  const program = new commander_1.Command()
23
17
  .name("azure-arch-skill")
@@ -27,7 +21,7 @@ function defineSubcommand(name, description) {
27
21
  program
28
22
  .command(name)
29
23
  .description(description)
30
- .requiredOption("--agent <name>", `target AI agent (${SUPPORTED_AGENTS.join("|")})`)
24
+ .requiredOption("--agent <name>", `target AI agent (${registry_1.SUPPORTED_AGENTS.join("|")})`)
31
25
  .option("--target <dir>", "override target directory (validation use)")
32
26
  .action(async (opts) => {
33
27
  // Set process.exitCode so any pending async cleanup (file handles, the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanv89/azure-arch-skill",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Install the Azure architecture diagram skill into your AI coding agent (Claude Code, Codex CLI, Cursor).",
5
5
  "bin": {
6
6
  "azure-arch-skill": "dist/index.js"