@jstn-sdk/ma 0.1.1 → 0.1.3

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 (38) hide show
  1. package/README.md +7 -5
  2. package/bin/ma.js +12 -0
  3. package/docs/README.md +2 -1
  4. package/docs/getting-started.md +4 -1
  5. package/docs/installed-sdk.md +60 -0
  6. package/docs/qa/{release-readiness-0.1.1.md → release-readiness-0.1.3.md} +8 -8
  7. package/docs/release-spec.md +7 -7
  8. package/docs/skills.md +28 -0
  9. package/package.json +3 -1
  10. package/plugins/meta-architect/.app.json +1 -1
  11. package/plugins/meta-architect/.codex-plugin/plugin.json +1 -1
  12. package/plugins/meta-architect/.mcp.json +1 -1
  13. package/plugins/meta-architect/README.md +1 -1
  14. package/plugins/meta-architect/skills/arch/agents/openai.yaml +2 -2
  15. package/plugins/meta-architect/skills/build/agents/openai.yaml +2 -2
  16. package/plugins/meta-architect/skills/flow/agents/openai.yaml +2 -2
  17. package/plugins/meta-architect/skills/meta-architect/agents/openai.yaml +2 -2
  18. package/plugins/meta-architect/skills/sage/agents/openai.yaml +2 -2
  19. package/plugins/meta-architect/skills/vet/agents/openai.yaml +2 -2
  20. package/plugins/meta-architect/skills/vibe/agents/openai.yaml +2 -2
  21. package/scripts/postinstall.js +7 -3
  22. package/scripts/release-sync.js +348 -0
  23. package/skills/arch/agents/openai.yaml +2 -2
  24. package/skills/build/agents/openai.yaml +2 -2
  25. package/skills/flow/agents/openai.yaml +2 -2
  26. package/skills/meta-architect/agents/openai.yaml +2 -2
  27. package/skills/sage/agents/openai.yaml +2 -2
  28. package/skills/vet/agents/openai.yaml +2 -2
  29. package/skills/vibe/agents/openai.yaml +2 -2
  30. package/src/launcher.js +1 -0
  31. package/src/mcp-live-client.js +1 -1
  32. package/src/skill-installer.js +151 -2
  33. package/src/skills.js +2 -2
  34. package/templates/AGENTS.md +130 -0
  35. package/templates/catalog-manifest.json +22 -0
  36. package/templates/model-instructions/core.md +39 -0
  37. package/templates/model-instructions/release.md +41 -0
  38. package/templates/model-instructions/security.md +22 -0
@@ -0,0 +1,348 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execFileSync } from "node:child_process";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import process from "node:process";
7
+
8
+ const WATCHED_PREFIXES = [
9
+ "bin/",
10
+ "docs/",
11
+ "example/",
12
+ "mcp/",
13
+ "missions/",
14
+ "plugins/",
15
+ "prompts/",
16
+ ".codex/prompts/",
17
+ "sprint/",
18
+ "templates/",
19
+ "src/",
20
+ "skills/",
21
+ ];
22
+
23
+ const WATCHED_FILES = new Set([
24
+ "README.md",
25
+ "RELEASE.md",
26
+ "CHANGELOG.md",
27
+ "package.json",
28
+ "package-lock.json",
29
+ ]);
30
+
31
+ function parseArgs(argv) {
32
+ const args = {
33
+ bump: "",
34
+ fromRef: "",
35
+ toRef: "",
36
+ githubOutput: "",
37
+ };
38
+
39
+ for (let index = 0; index < argv.length; index += 1) {
40
+ const token = argv[index];
41
+ if (token === "--bump") {
42
+ args.bump = argv[index + 1] ?? "";
43
+ index += 1;
44
+ continue;
45
+ }
46
+ if (token === "--from-ref") {
47
+ args.fromRef = argv[index + 1] ?? "";
48
+ index += 1;
49
+ continue;
50
+ }
51
+ if (token === "--to-ref") {
52
+ args.toRef = argv[index + 1] ?? "";
53
+ index += 1;
54
+ continue;
55
+ }
56
+ if (token === "--github-output") {
57
+ args.githubOutput = argv[index + 1] ?? "";
58
+ index += 1;
59
+ }
60
+ }
61
+
62
+ return args;
63
+ }
64
+
65
+ function readJson(file) {
66
+ return JSON.parse(fs.readFileSync(file, "utf8"));
67
+ }
68
+
69
+ function writeJson(file, value) {
70
+ fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
71
+ }
72
+
73
+ function readText(file) {
74
+ return fs.readFileSync(file, "utf8");
75
+ }
76
+
77
+ function writeText(file, content) {
78
+ fs.writeFileSync(file, content);
79
+ }
80
+
81
+ function replaceRequired(text, search, replacement, label) {
82
+ if (!text.includes(search)) {
83
+ throw new Error(`Expected to find ${label}: ${search}`);
84
+ }
85
+ return text.replaceAll(search, replacement);
86
+ }
87
+
88
+ function parseVersion(version) {
89
+ const match = version.match(/^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/);
90
+ if (!match?.groups) {
91
+ throw new Error(`Unsupported version for release-sync: ${version}`);
92
+ }
93
+ return {
94
+ major: Number(match.groups.major),
95
+ minor: Number(match.groups.minor),
96
+ patch: Number(match.groups.patch),
97
+ };
98
+ }
99
+
100
+ function bumpVersion(version, bumpKind) {
101
+ const parsed = parseVersion(version);
102
+ if (bumpKind === "major") {
103
+ return `${parsed.major + 1}.0.0`;
104
+ }
105
+ if (bumpKind === "minor") {
106
+ return `${parsed.major}.${parsed.minor + 1}.0`;
107
+ }
108
+ return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
109
+ }
110
+
111
+ function git(args) {
112
+ return execFileSync("git", args, { encoding: "utf8" }).trim();
113
+ }
114
+
115
+ function getChangedFiles({ fromRef, toRef }) {
116
+ if (process.env.RELEASE_SYNC_CHANGED_FILES) {
117
+ return process.env.RELEASE_SYNC_CHANGED_FILES.split("\n")
118
+ .map((line) => line.trim())
119
+ .filter(Boolean);
120
+ }
121
+
122
+ if (fromRef && toRef && !/^0+$/.test(fromRef)) {
123
+ return git(["diff", "--name-only", fromRef, toRef])
124
+ .split("\n")
125
+ .map((line) => line.trim())
126
+ .filter(Boolean);
127
+ }
128
+
129
+ return git(["status", "--porcelain"])
130
+ .split("\n")
131
+ .map((line) => line.slice(3).trim())
132
+ .filter(Boolean);
133
+ }
134
+
135
+ function isWatchedPath(file) {
136
+ return WATCHED_FILES.has(file) || WATCHED_PREFIXES.some((prefix) => file.startsWith(prefix));
137
+ }
138
+
139
+ function prependChangelog(version) {
140
+ const changelogPath = "CHANGELOG.md";
141
+ const changelog = readText(changelogPath);
142
+ if (changelog.includes(`## v${version}`)) {
143
+ return;
144
+ }
145
+
146
+ const next = changelog.replace(
147
+ "# Changelog\n\n",
148
+ `# Changelog\n\n## v${version}\n\n- Release line prepared automatically for v${version}.\n\n`,
149
+ );
150
+ writeText(changelogPath, next);
151
+ }
152
+
153
+ function updateCurrentSurfaceFile(file, oldVersion, nextVersion) {
154
+ const oldTag = `v${oldVersion}`;
155
+ const nextTag = `v${nextVersion}`;
156
+ const oldQa = `release-readiness-${oldVersion}.md`;
157
+ const nextQa = `release-readiness-${nextVersion}.md`;
158
+ let content = readText(file);
159
+ if (content.includes(oldTag)) {
160
+ content = content.replaceAll(oldTag, nextTag);
161
+ }
162
+ if (content.includes(oldVersion)) {
163
+ content = content.replaceAll(oldVersion, nextVersion);
164
+ }
165
+ content = content.replaceAll(oldQa, nextQa);
166
+ writeText(file, content);
167
+ }
168
+
169
+ function syncPackageVersion(nextVersion) {
170
+ const pkg = readJson("package.json");
171
+ const lock = readJson("package-lock.json");
172
+ pkg.version = nextVersion;
173
+ lock.version = nextVersion;
174
+ if (lock.packages?.[""]) {
175
+ lock.packages[""].version = nextVersion;
176
+ }
177
+ writeJson("package.json", pkg);
178
+ writeJson("package-lock.json", lock);
179
+ }
180
+
181
+ function syncPluginVersions(nextVersion) {
182
+ for (const file of [
183
+ path.join("plugins", "meta-architect", ".app.json"),
184
+ path.join("plugins", "meta-architect", ".mcp.json"),
185
+ path.join("plugins", "meta-architect", ".codex-plugin", "plugin.json"),
186
+ ]) {
187
+ const value = readJson(file);
188
+ value.version = nextVersion;
189
+ writeJson(file, value);
190
+ }
191
+ }
192
+
193
+ function syncCurrentReleaseFiles(oldVersion, nextVersion) {
194
+ for (const file of [
195
+ "README.md",
196
+ "RELEASE.md",
197
+ path.join("docs", "README.md"),
198
+ path.join("docs", "getting-started.md"),
199
+ path.join("docs", "release-spec.md"),
200
+ path.join("plugins", "meta-architect", "README.md"),
201
+ ]) {
202
+ updateCurrentSurfaceFile(file, oldVersion, nextVersion);
203
+ }
204
+
205
+ const oldQa = path.join("docs", "qa", `release-readiness-${oldVersion}.md`);
206
+ const nextQa = path.join("docs", "qa", `release-readiness-${nextVersion}.md`);
207
+ fs.renameSync(oldQa, nextQa);
208
+ updateCurrentSurfaceFile(nextQa, oldVersion, nextVersion);
209
+ }
210
+
211
+ function syncSupportingCode(oldVersion, nextVersion) {
212
+ const replacements = [
213
+ {
214
+ file: path.join("src", "skills.js"),
215
+ search: `release-readiness-${oldVersion}.md`,
216
+ replacement: `release-readiness-${nextVersion}.md`,
217
+ label: "skills QA path",
218
+ },
219
+ {
220
+ file: path.join("src", "mcp-live-client.js"),
221
+ search: `version: "${oldVersion}"`,
222
+ replacement: `version: "${nextVersion}"`,
223
+ label: "mcp live client version",
224
+ },
225
+ {
226
+ file: path.join("test", "package-install-smoke.test.js"),
227
+ search: `jstn-sdk-ma-${oldVersion}.tgz`,
228
+ replacement: `jstn-sdk-ma-${nextVersion}.tgz`,
229
+ label: "tarball filename",
230
+ },
231
+ {
232
+ file: path.join("test", "policy.test.js"),
233
+ search: `release/${oldVersion}`,
234
+ replacement: `release/${nextVersion}`,
235
+ label: "release branch example",
236
+ },
237
+ ];
238
+
239
+ for (const entry of replacements) {
240
+ const content = readText(entry.file);
241
+ writeText(entry.file, replaceRequired(content, entry.search, entry.replacement, entry.label));
242
+ }
243
+ }
244
+
245
+ function rewriteReleaseState(nextVersion, previousVersion) {
246
+ const releasePath = "RELEASE.md";
247
+ const qaPath = path.join("docs", "qa", `release-readiness-${nextVersion}.md`);
248
+
249
+ let release = readText(releasePath);
250
+ release = release.replace(
251
+ /- npm package: `@jstn-sdk\/ma@[0-9]+\.[0-9]+\.[0-9]+`/,
252
+ `- npm package: \`@jstn-sdk/ma@${nextVersion}\``,
253
+ );
254
+ release = release.replace(
255
+ /- publishability note: .+/,
256
+ `- publishability note: \`${previousVersion}\` is already published, so \`${nextVersion}\` is the next publishable package line`,
257
+ );
258
+ release = release.replace(
259
+ /- GitHub release: .+/,
260
+ `- GitHub release: pending publish for \`v${nextVersion}\``,
261
+ );
262
+ release = release.replace(
263
+ /- npm publication has not been run yet for `@jstn-sdk\/ma@[0-9]+\.[0-9]+\.[0-9]+`/,
264
+ `- npm publication has not been run yet for \`@jstn-sdk/ma@${nextVersion}\``,
265
+ );
266
+ writeText(releasePath, release);
267
+
268
+ let qa = readText(qaPath);
269
+ qa = qa.replace(
270
+ /- npm package: `@jstn-sdk\/ma@[0-9]+\.[0-9]+\.[0-9]+`/,
271
+ `- npm package: \`@jstn-sdk/ma@${nextVersion}\``,
272
+ );
273
+ qa = qa.replace(
274
+ /- publishability note: .+/,
275
+ `- publishability note: \`${previousVersion}\` is already published, so \`${nextVersion}\` is the next publishable package line`,
276
+ );
277
+ qa = qa.replace(
278
+ /- GitHub release: .+/,
279
+ `- GitHub release: pending publish for \`v${nextVersion}\``,
280
+ );
281
+ writeText(qaPath, qa);
282
+ }
283
+
284
+ function writeGithubOutput(outputPath, { updated, version }) {
285
+ const lines = [`updated=${updated}`, `version=${version}`];
286
+ fs.appendFileSync(outputPath, `${lines.join("\n")}\n`);
287
+ }
288
+
289
+ function main() {
290
+ const args = parseArgs(process.argv.slice(2));
291
+ const pkg = readJson("package.json");
292
+ const currentVersion = pkg.version;
293
+ const changedFiles = getChangedFiles(args);
294
+ const relevantChanges = changedFiles.filter(isWatchedPath);
295
+
296
+ if (relevantChanges.length === 0) {
297
+ if (args.githubOutput) {
298
+ writeGithubOutput(args.githubOutput, { updated: "false", version: currentVersion });
299
+ }
300
+ console.log(
301
+ JSON.stringify(
302
+ {
303
+ updated: false,
304
+ version: currentVersion,
305
+ changedFiles,
306
+ },
307
+ null,
308
+ 2,
309
+ ),
310
+ );
311
+ return;
312
+ }
313
+
314
+ const bumpKind = args.bump || "patch";
315
+ const nextVersion = bumpVersion(currentVersion, bumpKind);
316
+
317
+ syncPackageVersion(nextVersion);
318
+ syncPluginVersions(nextVersion);
319
+ prependChangelog(nextVersion);
320
+ syncCurrentReleaseFiles(currentVersion, nextVersion);
321
+ syncSupportingCode(currentVersion, nextVersion);
322
+ rewriteReleaseState(nextVersion, currentVersion);
323
+
324
+ if (args.githubOutput) {
325
+ writeGithubOutput(args.githubOutput, { updated: "true", version: nextVersion });
326
+ }
327
+
328
+ console.log(
329
+ JSON.stringify(
330
+ {
331
+ updated: true,
332
+ bump: bumpKind,
333
+ previousVersion: currentVersion,
334
+ version: nextVersion,
335
+ changedFiles: relevantChanges,
336
+ },
337
+ null,
338
+ 2,
339
+ ),
340
+ );
341
+ }
342
+
343
+ try {
344
+ main();
345
+ } catch (error) {
346
+ console.error(error.message);
347
+ process.exitCode = 1;
348
+ }
@@ -1,4 +1,4 @@
1
1
  interface:
2
- display_name: "MA Architect"
3
- short_description: "Architecture and stack design for MA flows"
2
+ display_name: "$arch"
3
+ short_description: "Architecture-first product and system design"
4
4
  default_prompt: "Use $arch to produce architecture, stack rationale, subsystem boundaries, tradeoffs, and a phased delivery plan."
@@ -1,4 +1,4 @@
1
1
  interface:
2
- display_name: "MA Build"
3
- short_description: "Gated build planning and branch/worktree prep"
2
+ display_name: "$build"
3
+ short_description: "Build-readiness decision and next implementation step"
4
4
  default_prompt: "Use $build to decide whether implementation is ready, what remains blocked, and what the next build slice should be."
@@ -1,4 +1,4 @@
1
1
  interface:
2
- display_name: "MA Flow"
3
- short_description: "Business logic and state validation for MA"
2
+ display_name: "$flow"
3
+ short_description: "Logic, states, transitions, and blockers"
4
4
  default_prompt: "Use $flow to map business logic, state transitions, invariants, and blockers in the current design."
@@ -1,4 +1,4 @@
1
1
  interface:
2
- display_name: "Meta-Architect"
3
- short_description: "Core orchestration and gated build workflows"
2
+ display_name: "$meta-architect"
3
+ short_description: "Full Meta-Architect skill workflow"
4
4
  default_prompt: "Use $meta-architect to run the full Meta-Architect workflow inside Codex, then route through $arch, $sage, $flow, $vet, $vibe, and $build as needed."
@@ -1,4 +1,4 @@
1
1
  interface:
2
- display_name: "MA Sage"
3
- short_description: "GitMCP-backed OSS evidence selection"
2
+ display_name: "$sage"
3
+ short_description: "Evidence-backed stack and OSS validation"
4
4
  default_prompt: "Use $sage to validate stack choices with official docs, upstream repos, and approved GitMCP-backed sources."
@@ -1,4 +1,4 @@
1
1
  interface:
2
- display_name: "MA Vet"
3
- short_description: "Security, CVE, and risk review for MA"
2
+ display_name: "$vet"
3
+ short_description: "Security and trust-boundary review"
4
4
  default_prompt: "Use $vet to review trust boundaries, security risks, abuse cases, and safer alternatives in the current design."
@@ -1,4 +1,4 @@
1
1
  interface:
2
- display_name: "MA Vibe"
3
- short_description: "DX and UX review before Meta-Architect build"
2
+ display_name: "$vibe"
3
+ short_description: "DX and UX review before implementation"
4
4
  default_prompt: "Use $vibe to review developer and user experience risks before the build lane proceeds."
package/src/launcher.js CHANGED
@@ -6,6 +6,7 @@ const nativeCommands = new Set([
6
6
  "init",
7
7
  "idea",
8
8
  "skills",
9
+ "sdk-path",
9
10
  "status",
10
11
  "merge",
11
12
  "release",
@@ -38,7 +38,7 @@ export class McpSseClient {
38
38
  capabilities: {},
39
39
  clientInfo: {
40
40
  name: "meta-architect",
41
- version: "0.1.1",
41
+ version: "0.1.3",
42
42
  },
43
43
  });
44
44
 
@@ -3,9 +3,16 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { packageRoot } from "./paths.js";
5
5
 
6
+ function resolveCodexHome() {
7
+ return process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
8
+ }
9
+
6
10
  function resolveSkillInstallRoot() {
7
- const codexHome = process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
8
- return path.join(codexHome, "skills");
11
+ return path.join(resolveCodexHome(), "skills");
12
+ }
13
+
14
+ function resolveSupportBundleRoot() {
15
+ return path.join(resolveCodexHome(), "meta-architect-sdk");
9
16
  }
10
17
 
11
18
  async function copyDir(src, dest) {
@@ -26,6 +33,10 @@ export function getSkillInstallRoot() {
26
33
  return resolveSkillInstallRoot();
27
34
  }
28
35
 
36
+ export function getSupportBundleRoot() {
37
+ return resolveSupportBundleRoot();
38
+ }
39
+
29
40
  export async function loadSkillManifest() {
30
41
  const manifestPath = path.join(packageRoot, "skills", "index.json");
31
42
  const manifest = JSON.parse(await fs.readFile(manifestPath, "utf8"));
@@ -47,3 +58,141 @@ export async function installSkills({ targetRoot = getSkillInstallRoot() } = {})
47
58
 
48
59
  return { targetRoot, installed };
49
60
  }
61
+
62
+ async function copyPath(src, dest) {
63
+ const stat = await fs.stat(src);
64
+ if (stat.isDirectory()) {
65
+ await fs.rm(dest, { recursive: true, force: true });
66
+ await copyDir(src, dest);
67
+ return "directory";
68
+ }
69
+
70
+ await fs.mkdir(path.dirname(dest), { recursive: true });
71
+ await fs.copyFile(src, dest);
72
+ return "file";
73
+ }
74
+
75
+ export async function installSupportBundle({ targetRoot = getSupportBundleRoot() } = {}) {
76
+ const assets = [
77
+ {
78
+ name: "mcp",
79
+ src: path.join(packageRoot, "mcp"),
80
+ dest: path.join(targetRoot, "mcp"),
81
+ },
82
+ {
83
+ name: "sprint",
84
+ src: path.join(packageRoot, "sprint"),
85
+ dest: path.join(targetRoot, "sprint"),
86
+ },
87
+ {
88
+ name: "prompts",
89
+ src: path.join(packageRoot, ".codex", "prompts"),
90
+ dest: path.join(targetRoot, "prompts"),
91
+ },
92
+ {
93
+ name: "scripts",
94
+ src: path.join(packageRoot, "scripts"),
95
+ dest: path.join(targetRoot, "scripts"),
96
+ },
97
+ {
98
+ name: "plugin",
99
+ src: path.join(packageRoot, "plugins", "meta-architect"),
100
+ dest: path.join(targetRoot, "plugins", "meta-architect"),
101
+ },
102
+ {
103
+ name: "templates",
104
+ src: path.join(packageRoot, "templates"),
105
+ dest: path.join(targetRoot, "templates"),
106
+ },
107
+ {
108
+ name: "docs-readme",
109
+ src: path.join(packageRoot, "docs", "README.md"),
110
+ dest: path.join(targetRoot, "docs", "README.md"),
111
+ },
112
+ ];
113
+
114
+ await fs.mkdir(targetRoot, { recursive: true });
115
+ const installed = [];
116
+ for (const asset of assets) {
117
+ await copyPath(asset.src, asset.dest);
118
+ installed.push({ name: asset.name, dest: asset.dest });
119
+ }
120
+
121
+ const pkg = JSON.parse(await fs.readFile(path.join(packageRoot, "package.json"), "utf8"));
122
+ const manifest = {
123
+ packageName: pkg.name,
124
+ packageVersion: pkg.version,
125
+ installedAt: new Date().toISOString(),
126
+ root: targetRoot,
127
+ assets: installed,
128
+ };
129
+ await fs.writeFile(
130
+ path.join(targetRoot, "asset-manifest.json"),
131
+ `${JSON.stringify(manifest, null, 2)}\n`,
132
+ );
133
+
134
+ return { targetRoot, installed };
135
+ }
136
+
137
+ export async function areSkillsInstalled({ targetRoot = getSkillInstallRoot() } = {}) {
138
+ const skills = await loadSkillManifest();
139
+
140
+ for (const skill of skills) {
141
+ const skillDir = path.join(targetRoot, path.basename(skill.path));
142
+ try {
143
+ await fs.access(path.join(skillDir, "SKILL.md"));
144
+ } catch {
145
+ return false;
146
+ }
147
+ }
148
+
149
+ return true;
150
+ }
151
+
152
+ export async function isSupportBundleInstalled({ targetRoot = getSupportBundleRoot() } = {}) {
153
+ const requiredFiles = [
154
+ path.join(targetRoot, "asset-manifest.json"),
155
+ path.join(targetRoot, "mcp", "servers.json"),
156
+ path.join(targetRoot, "sprint", "07-release.md"),
157
+ path.join(targetRoot, "prompts", "onboarding.md"),
158
+ path.join(targetRoot, "scripts", "skills-install.js"),
159
+ path.join(targetRoot, "plugins", "meta-architect", ".codex-plugin", "plugin.json"),
160
+ path.join(targetRoot, "templates", "AGENTS.md"),
161
+ ];
162
+
163
+ for (const file of requiredFiles) {
164
+ try {
165
+ await fs.access(file);
166
+ } catch {
167
+ return false;
168
+ }
169
+ }
170
+
171
+ return true;
172
+ }
173
+
174
+ export async function ensureSkillsInstalled({ targetRoot = getSkillInstallRoot() } = {}) {
175
+ if (process.env.MA_SKIP_AUTO_INSTALL === "1") {
176
+ return { targetRoot, installed: [], skipped: true };
177
+ }
178
+
179
+ if (await areSkillsInstalled({ targetRoot })) {
180
+ return { targetRoot, installed: [], skipped: false };
181
+ }
182
+
183
+ const result = await installSkills({ targetRoot });
184
+ return { ...result, skipped: false };
185
+ }
186
+
187
+ export async function ensureSupportBundleInstalled({ targetRoot = getSupportBundleRoot() } = {}) {
188
+ if (process.env.MA_SKIP_AUTO_INSTALL === "1") {
189
+ return { targetRoot, installed: [], skipped: true };
190
+ }
191
+
192
+ if (await isSupportBundleInstalled({ targetRoot })) {
193
+ return { targetRoot, installed: [], skipped: false };
194
+ }
195
+
196
+ const result = await installSupportBundle({ targetRoot });
197
+ return { ...result, skipped: false };
198
+ }
package/src/skills.js CHANGED
@@ -344,8 +344,8 @@ export async function runInit() {
344
344
  path.join(getRepoRoot(), "docs", "release-spec.md"),
345
345
  ],
346
346
  [
347
- path.join(packageRoot, "docs", "qa", "release-readiness-0.1.1.md"),
348
- path.join(getRepoRoot(), "docs", "qa", "release-readiness-0.1.1.md"),
347
+ path.join(packageRoot, "docs", "qa", "release-readiness-0.1.3.md"),
348
+ path.join(getRepoRoot(), "docs", "qa", "release-readiness-0.1.3.md"),
349
349
  ],
350
350
  ];
351
351