@useorgx/wizard 0.1.10 → 0.1.11

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/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import * as clack from "@clack/prompts";
5
+ import { spawnSync as spawnSync3 } from "child_process";
5
6
  import { hostname } from "os";
6
7
  import { Command } from "commander";
7
8
  import pc3 from "picocolors";
@@ -54,6 +55,7 @@ var XDG_CONFIG_HOME = process.env.XDG_CONFIG_HOME?.trim() || join(HOME, ".config
54
55
  var ORGX_WIZARD_CONFIG_HOME = process.env.ORGX_WIZARD_CONFIG_HOME?.trim() || join(XDG_CONFIG_HOME, "useorgx", "wizard");
55
56
  var ORGX_WIZARD_AUTH_PATH = join(ORGX_WIZARD_CONFIG_HOME, "auth.json");
56
57
  var ORGX_WIZARD_STATE_PATH = join(ORGX_WIZARD_CONFIG_HOME, "state.json");
58
+ var ORGX_SKILL_EXTENSIONS_DIR = join(ORGX_WIZARD_CONFIG_HOME, "skill-extensions");
57
59
  var ORGX_WIZARD_KEYTAR_SERVICE = "@useorgx/wizard";
58
60
  var ORGX_WIZARD_KEYTAR_ACCOUNT = "orgx-api-key";
59
61
  function normalizeOrgxApiBaseUrl(baseUrl) {
@@ -978,6 +980,39 @@ function parseAgentRoster(value) {
978
980
  ...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
979
981
  };
980
982
  }
983
+ function parseSkillFileRecord(value) {
984
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
985
+ return void 0;
986
+ }
987
+ const record = value;
988
+ if (!isNonEmptyString2(record.path) || !isNonEmptyString2(record.skillId) || !isNonEmptyString2(record.contentSha256) || !isNonEmptyString2(record.updatedAt)) {
989
+ return void 0;
990
+ }
991
+ return {
992
+ contentSha256: record.contentSha256.trim(),
993
+ path: record.path.trim(),
994
+ skillId: record.skillId.trim(),
995
+ updatedAt: record.updatedAt.trim(),
996
+ ...isNonEmptyString2(record.coreSha256) ? { coreSha256: record.coreSha256.trim() } : {}
997
+ };
998
+ }
999
+ function parseSkillFiles(value) {
1000
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1001
+ return void 0;
1002
+ }
1003
+ const record = value;
1004
+ if (!isNonEmptyString2(record.updatedAt) || !Array.isArray(record.files)) {
1005
+ return void 0;
1006
+ }
1007
+ const files = record.files.map((entry) => parseSkillFileRecord(entry)).filter((entry) => Boolean(entry));
1008
+ if (files.length === 0) {
1009
+ return void 0;
1010
+ }
1011
+ return {
1012
+ files,
1013
+ updatedAt: record.updatedAt.trim()
1014
+ };
1015
+ }
981
1016
  function createWizardState(now = (/* @__PURE__ */ new Date()).toISOString()) {
982
1017
  return {
983
1018
  installationId: `wizard-${randomUUID()}`,
@@ -990,6 +1025,7 @@ function sanitizeWizardStateRecord(record) {
990
1025
  const agentRoster = parseAgentRoster(record.agentRoster);
991
1026
  const demoInitiative = parseDemoInitiative(record.demoInitiative);
992
1027
  const onboardingTask = parseOnboardingTask(record.onboardingTask);
1028
+ const skillFiles = parseSkillFiles(record.skillFiles);
993
1029
  return {
994
1030
  installationId: record.installationId.trim(),
995
1031
  createdAt: record.createdAt.trim(),
@@ -997,7 +1033,8 @@ function sanitizeWizardStateRecord(record) {
997
1033
  ...continuity ? { continuity } : {},
998
1034
  ...agentRoster ? { agentRoster } : {},
999
1035
  ...demoInitiative ? { demoInitiative } : {},
1000
- ...onboardingTask ? { onboardingTask } : {}
1036
+ ...onboardingTask ? { onboardingTask } : {},
1037
+ ...skillFiles ? { skillFiles } : {}
1001
1038
  };
1002
1039
  }
1003
1040
  function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
@@ -1018,6 +1055,8 @@ function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
1018
1055
  if (demoInitiative !== void 0) state.demoInitiative = demoInitiative;
1019
1056
  const onboardingTask = parseOnboardingTask(parsed.onboardingTask);
1020
1057
  if (onboardingTask !== void 0) state.onboardingTask = onboardingTask;
1058
+ const skillFiles = parseSkillFiles(parsed.skillFiles);
1059
+ if (skillFiles !== void 0) state.skillFiles = skillFiles;
1021
1060
  return state;
1022
1061
  }
1023
1062
  function writeWizardState(value, statePath = ORGX_WIZARD_STATE_PATH) {
@@ -3005,7 +3044,9 @@ async function ensureOnboardingTask(workspace, options = {}) {
3005
3044
  }
3006
3045
 
3007
3046
  // src/lib/skills.ts
3008
- import { join as join2 } from "path";
3047
+ import { createHash } from "crypto";
3048
+ import { existsSync as existsSync3, readdirSync } from "fs";
3049
+ import { basename, join as join2 } from "path";
3009
3050
  var DEFAULT_ORGX_SKILL_PACKS = [
3010
3051
  "morning-briefing",
3011
3052
  "initiative-kickoff",
@@ -3017,6 +3058,9 @@ var EXCLUDED_PACK_DIRS = /* @__PURE__ */ new Set([".github", "scripts"]);
3017
3058
  var ORGX_SKILLS_OWNER = "useorgx";
3018
3059
  var ORGX_SKILLS_REPO = "skills";
3019
3060
  var ORGX_SKILLS_REF = "main";
3061
+ var SKILL_EXTENSION_SCOPES = ["user", "workspace", "project"];
3062
+ var COMPOSED_SKILL_MARKER = "ORGX SKILL COMPOSED v1";
3063
+ var EXTENSION_FRONTMATTER_DELIMITER = "---";
3020
3064
  var CURSOR_RULES_CONTENT = `# OrgX Rules
3021
3065
 
3022
3066
  - Prefer \`workspace_id\` on OrgX tool calls. \`command_center_id\` is a deprecated alias and should only be used for backwards compatibility. If both are present, they must match.
@@ -3072,6 +3116,219 @@ Install and use these packs alongside this base skill:
3072
3116
  4. When you scaffold, decide up front whether \`continue_on_error\` is acceptable.
3073
3117
  5. Carry \`_context\` through any widget-producing flows so the UI can render and resume correctly.
3074
3118
  `;
3119
+ function sha256(value) {
3120
+ return createHash("sha256").update(value).digest("hex");
3121
+ }
3122
+ function normalizeSkillId(value) {
3123
+ const normalized = value.trim().toLowerCase();
3124
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
3125
+ throw new Error(
3126
+ "Skill id must start with a letter or number and only contain letters, numbers, '.', '_' or '-'."
3127
+ );
3128
+ }
3129
+ return normalized;
3130
+ }
3131
+ function normalizeSkillExtensionScope(value) {
3132
+ const normalized = (value ?? "user").trim().toLowerCase();
3133
+ if (!SKILL_EXTENSION_SCOPES.includes(normalized)) {
3134
+ throw new Error("Skill extension scope must be one of: user, workspace, project.");
3135
+ }
3136
+ return normalized;
3137
+ }
3138
+ function defaultExtensionTitle(skillId, scope) {
3139
+ const prefix = scope === "user" ? "Personal" : scope === "workspace" ? "Workspace" : "Project";
3140
+ return `${prefix} ${skillId} behavior`;
3141
+ }
3142
+ function extensionFilePath(skillId, scope, extensionsDir = ORGX_SKILL_EXTENSIONS_DIR) {
3143
+ return join2(extensionsDir, `${scope}.${skillId}.md`);
3144
+ }
3145
+ function extensionTemplate(input) {
3146
+ const body = input.content?.trim() ? input.content.trim() : [
3147
+ `# ${input.title}`,
3148
+ "",
3149
+ "- Add your custom behavior here.",
3150
+ "- These instructions are appended after the OrgX-managed core skill when the wizard syncs skills."
3151
+ ].join("\n");
3152
+ return [
3153
+ EXTENSION_FRONTMATTER_DELIMITER,
3154
+ `skill: ${input.skillId}`,
3155
+ `scope: ${input.scope}`,
3156
+ "enabled: true",
3157
+ `title: ${input.title}`,
3158
+ EXTENSION_FRONTMATTER_DELIMITER,
3159
+ "",
3160
+ body,
3161
+ ""
3162
+ ].join("\n");
3163
+ }
3164
+ function parseFrontmatter(content) {
3165
+ if (!content.startsWith(`${EXTENSION_FRONTMATTER_DELIMITER}
3166
+ `)) {
3167
+ return { body: content, data: {} };
3168
+ }
3169
+ const closeIndex = content.indexOf(`
3170
+ ${EXTENSION_FRONTMATTER_DELIMITER}
3171
+ `, 4);
3172
+ if (closeIndex === -1) {
3173
+ return { body: content, data: {} };
3174
+ }
3175
+ const raw = content.slice(4, closeIndex);
3176
+ const body = content.slice(closeIndex + 5);
3177
+ const data = {};
3178
+ for (const line of raw.split("\n")) {
3179
+ const index = line.indexOf(":");
3180
+ if (index === -1) {
3181
+ continue;
3182
+ }
3183
+ const key = line.slice(0, index).trim();
3184
+ const value = line.slice(index + 1).trim();
3185
+ if (key) {
3186
+ data[key] = value;
3187
+ }
3188
+ }
3189
+ return { body, data };
3190
+ }
3191
+ function serializeFrontmatter(data, body) {
3192
+ return [
3193
+ EXTENSION_FRONTMATTER_DELIMITER,
3194
+ ...Object.entries(data).map(([key, value]) => `${key}: ${value}`),
3195
+ EXTENSION_FRONTMATTER_DELIMITER,
3196
+ "",
3197
+ body.trimEnd(),
3198
+ ""
3199
+ ].join("\n");
3200
+ }
3201
+ function parseSkillExtension(path, content) {
3202
+ const fallbackId = basename(path, ".md");
3203
+ const fallbackParts = fallbackId.split(".");
3204
+ const fallbackScope = normalizeSkillExtensionScope(
3205
+ SKILL_EXTENSION_SCOPES.includes(fallbackParts[0]) ? fallbackParts.shift() : "user"
3206
+ );
3207
+ const fallbackSkillId = normalizeSkillId(fallbackParts.join(".") || fallbackId);
3208
+ const parsed = parseFrontmatter(content);
3209
+ const skillId = normalizeSkillId(parsed.data.skill || fallbackSkillId);
3210
+ const scope = normalizeSkillExtensionScope(parsed.data.scope || fallbackScope);
3211
+ const title = parsed.data.title?.trim() || defaultExtensionTitle(skillId, scope);
3212
+ return {
3213
+ content: parsed.body.trim(),
3214
+ enabled: parsed.data.enabled?.trim().toLowerCase() !== "false",
3215
+ id: `${scope}.${skillId}`,
3216
+ path,
3217
+ scope,
3218
+ skillId,
3219
+ title
3220
+ };
3221
+ }
3222
+ function listSkillExtensions(options = {}) {
3223
+ const extensionsDir = options.extensionsDir ?? ORGX_SKILL_EXTENSIONS_DIR;
3224
+ if (!existsSync3(extensionsDir)) {
3225
+ return [];
3226
+ }
3227
+ return readdirSync(extensionsDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => {
3228
+ const path = join2(extensionsDir, entry.name);
3229
+ const content = readTextIfExists(path);
3230
+ return content === null ? null : parseSkillExtension(path, content);
3231
+ }).filter((entry) => Boolean(entry)).sort((left, right) => left.id.localeCompare(right.id));
3232
+ }
3233
+ function addSkillExtension(options) {
3234
+ const skillId = normalizeSkillId(options.skillId);
3235
+ const scope = normalizeSkillExtensionScope(options.scope);
3236
+ const path = extensionFilePath(skillId, scope, options.extensionsDir);
3237
+ const title = options.title?.trim() || defaultExtensionTitle(skillId, scope);
3238
+ const existing = readTextIfExists(path);
3239
+ if (existing !== null && options.overwrite !== true) {
3240
+ const extension2 = parseSkillExtension(path, existing);
3241
+ if (!extension2) {
3242
+ throw new Error(`Could not parse existing skill extension at ${path}.`);
3243
+ }
3244
+ return {
3245
+ changed: false,
3246
+ created: false,
3247
+ extension: extension2,
3248
+ path
3249
+ };
3250
+ }
3251
+ const nextContent = extensionTemplate({
3252
+ scope,
3253
+ skillId,
3254
+ title,
3255
+ ...options.content !== void 0 ? { content: options.content } : {}
3256
+ });
3257
+ writeTextFile(path, nextContent);
3258
+ const extension = parseSkillExtension(path, nextContent);
3259
+ if (!extension) {
3260
+ throw new Error(`Could not parse newly written skill extension at ${path}.`);
3261
+ }
3262
+ return {
3263
+ changed: existing !== nextContent,
3264
+ created: existing === null,
3265
+ extension,
3266
+ path
3267
+ };
3268
+ }
3269
+ function setSkillExtensionEnabled(input) {
3270
+ const skillId = normalizeSkillId(input.skillId);
3271
+ const scope = normalizeSkillExtensionScope(input.scope);
3272
+ const path = extensionFilePath(skillId, scope, input.extensionsDir);
3273
+ const existing = readTextIfExists(path);
3274
+ if (existing === null) {
3275
+ if (!input.enabled) {
3276
+ throw new Error(`No ${scope} extension exists for ${skillId}.`);
3277
+ }
3278
+ return addSkillExtension({
3279
+ scope,
3280
+ skillId,
3281
+ ...input.extensionsDir !== void 0 ? { extensionsDir: input.extensionsDir } : {}
3282
+ });
3283
+ }
3284
+ const parsed = parseFrontmatter(existing);
3285
+ const data = {
3286
+ skill: parsed.data.skill || skillId,
3287
+ scope: parsed.data.scope || scope,
3288
+ enabled: input.enabled ? "true" : "false",
3289
+ title: parsed.data.title || defaultExtensionTitle(skillId, scope)
3290
+ };
3291
+ const nextContent = serializeFrontmatter(data, parsed.body);
3292
+ if (nextContent !== existing) {
3293
+ writeTextFile(path, nextContent);
3294
+ }
3295
+ const extension = parseSkillExtension(path, nextContent);
3296
+ if (!extension) {
3297
+ throw new Error(`Could not parse skill extension at ${path}.`);
3298
+ }
3299
+ return {
3300
+ changed: nextContent !== existing,
3301
+ created: false,
3302
+ extension,
3303
+ path
3304
+ };
3305
+ }
3306
+ function composeSkillContent(skillId, coreContent, extensions) {
3307
+ const enabled = extensions.filter((extension) => extension.enabled && extension.skillId === skillId);
3308
+ if (enabled.length === 0) {
3309
+ return coreContent;
3310
+ }
3311
+ const extensionBlocks = enabled.map((extension) => [
3312
+ `<!-- extension: ${extension.id} -->`,
3313
+ extension.content.trim()
3314
+ ].join("\n"));
3315
+ return [
3316
+ `<!-- ${COMPOSED_SKILL_MARKER}`,
3317
+ `skill: ${skillId}`,
3318
+ `core-sha256: ${sha256(coreContent)}`,
3319
+ "generated: true",
3320
+ `edit-with: orgx-wizard skills extensions edit ${skillId}`,
3321
+ "-->",
3322
+ `<!-- ORGX CORE BEGIN ${skillId} -->`,
3323
+ coreContent.trimEnd(),
3324
+ `<!-- ORGX CORE END ${skillId} -->`,
3325
+ "",
3326
+ "<!-- ORGX USER EXTENSIONS BEGIN -->",
3327
+ ...extensionBlocks,
3328
+ "<!-- ORGX USER EXTENSIONS END -->",
3329
+ ""
3330
+ ].join("\n");
3331
+ }
3075
3332
  function encodeRepoPath(value) {
3076
3333
  return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
3077
3334
  }
@@ -3137,12 +3394,35 @@ async function fetchRemoteText(sourceUrl, fetchImpl) {
3137
3394
  }
3138
3395
  return readResponseText(response);
3139
3396
  }
3140
- function writeManagedFile(path, content, label, sourceUrl) {
3397
+ function getTrackedSkillFile(records, path) {
3398
+ return records.find((record) => record.path === path);
3399
+ }
3400
+ function writeManagedFile(path, content, label, sourceUrl, tracking) {
3141
3401
  const existing = readTextIfExists(path);
3402
+ const tracked = tracking ? getTrackedSkillFile(tracking.records, path) : void 0;
3403
+ if (tracking && existing !== null && tracked && sha256(existing) !== tracked.contentSha256 && tracking.force !== true) {
3404
+ return {
3405
+ label,
3406
+ path,
3407
+ changed: false,
3408
+ skipped: true,
3409
+ reason: "manual edits detected; move them into a skill extension or rerun with --force",
3410
+ ...sourceUrl ? { sourceUrl } : {}
3411
+ };
3412
+ }
3142
3413
  const changed = existing !== content;
3143
3414
  if (changed) {
3144
3415
  writeTextFile(path, content);
3145
3416
  }
3417
+ if (tracking) {
3418
+ tracking.stagedRecords.set(path, {
3419
+ contentSha256: sha256(content),
3420
+ ...tracking.coreContent ? { coreSha256: sha256(tracking.coreContent) } : {},
3421
+ path,
3422
+ skillId: tracking.skillId,
3423
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3424
+ });
3425
+ }
3146
3426
  return {
3147
3427
  label,
3148
3428
  path,
@@ -3207,19 +3487,27 @@ async function fetchAvailablePackNames(fetchImpl, ref) {
3207
3487
  const entries = await fetchDirectoryEntries("", fetchImpl, ref);
3208
3488
  return entries.filter((e) => e.type === "dir" && !EXCLUDED_PACK_DIRS.has(e.name)).map((e) => e.name);
3209
3489
  }
3210
- async function installSkillPack(skillName, claudeSkillsDir, fetchImpl, ref) {
3490
+ async function installSkillPack(skillName, claudeSkillsDir, fetchImpl, ref, tracking) {
3211
3491
  const rootPath = skillName;
3212
3492
  const files = await listRemoteSkillFiles(rootPath, fetchImpl, ref);
3213
3493
  const writes = [];
3214
3494
  for (const file of files) {
3215
- const content = await fetchRemoteText(file.sourceUrl, fetchImpl);
3495
+ const coreContent = await fetchRemoteText(file.sourceUrl, fetchImpl);
3216
3496
  const relativePath = file.path.slice(`${rootPath}/`.length);
3497
+ const content = relativePath === "SKILL.md" ? composeSkillContent(skillName, coreContent, tracking.extensions) : coreContent;
3217
3498
  writes.push(
3218
3499
  writeManagedFile(
3219
3500
  join2(claudeSkillsDir, skillName, relativePath),
3220
3501
  content,
3221
3502
  `${skillName}/${relativePath}`,
3222
- file.sourceUrl
3503
+ file.sourceUrl,
3504
+ {
3505
+ coreContent,
3506
+ records: tracking.records,
3507
+ skillId: skillName,
3508
+ stagedRecords: tracking.stagedRecords,
3509
+ ...tracking.force !== void 0 ? { force: tracking.force } : {}
3510
+ }
3223
3511
  )
3224
3512
  );
3225
3513
  }
@@ -3237,6 +3525,13 @@ async function installOrgxSkills(options = {}) {
3237
3525
  const claudeSkillsDir = options.claudeSkillsDir ?? CLAUDE_SKILLS_DIR;
3238
3526
  const claudeOrgxSkillPath = options.claudeOrgxSkillPath ?? CLAUDE_ORGX_SKILL_PATH;
3239
3527
  const cursorRulePath = options.cursorRulePath ?? CURSOR_ORGX_RULE_PATH;
3528
+ const statePath = options.statePath ?? ORGX_WIZARD_STATE_PATH;
3529
+ const extensions = listSkillExtensions(
3530
+ options.skillExtensionsDir !== void 0 ? { extensionsDir: options.skillExtensionsDir } : {}
3531
+ );
3532
+ const enabledExtensions = extensions.filter((extension) => extension.enabled);
3533
+ const trackedRecords = readWizardState(statePath)?.skillFiles?.files ?? [];
3534
+ const stagedRecords = /* @__PURE__ */ new Map();
3240
3535
  const plan = planOrgxSkillsInstall(options.pluginTargets ?? []);
3241
3536
  const requestedNames = options.skillNames ?? [];
3242
3537
  let skillNames;
@@ -3250,34 +3545,101 @@ async function installOrgxSkills(options = {}) {
3250
3545
  skillNames = resolveSkillPackNames(requestedNames);
3251
3546
  }
3252
3547
  const writes = [
3253
- writeManagedFile(cursorRulePath, CURSOR_RULES_CONTENT, "cursor-rules")
3548
+ writeManagedFile(
3549
+ cursorRulePath,
3550
+ composeSkillContent("cursor-rules", CURSOR_RULES_CONTENT, extensions),
3551
+ "cursor-rules",
3552
+ void 0,
3553
+ {
3554
+ coreContent: CURSOR_RULES_CONTENT,
3555
+ records: trackedRecords,
3556
+ skillId: "cursor-rules",
3557
+ stagedRecords,
3558
+ ...options.force !== void 0 ? { force: options.force } : {}
3559
+ }
3560
+ )
3254
3561
  ];
3255
3562
  if (plan.installClaudeSkillBootstrap) {
3256
3563
  writes.push(
3257
- writeManagedFile(claudeOrgxSkillPath, CLAUDE_ORGX_SKILL_CONTENT, "claude-orgx-skill")
3564
+ writeManagedFile(
3565
+ claudeOrgxSkillPath,
3566
+ composeSkillContent("orgx", CLAUDE_ORGX_SKILL_CONTENT, extensions),
3567
+ "claude-orgx-skill",
3568
+ void 0,
3569
+ {
3570
+ coreContent: CLAUDE_ORGX_SKILL_CONTENT,
3571
+ records: trackedRecords,
3572
+ skillId: "orgx",
3573
+ stagedRecords,
3574
+ ...options.force !== void 0 ? { force: options.force } : {}
3575
+ }
3576
+ )
3258
3577
  );
3259
3578
  }
3260
3579
  const packs = [];
3261
3580
  if (plan.installClaudeSkillPacks) {
3262
3581
  for (const skillName of skillNames) {
3263
- packs.push(await installSkillPack(skillName, claudeSkillsDir, fetchImpl, ref));
3582
+ packs.push(await installSkillPack(skillName, claudeSkillsDir, fetchImpl, ref, {
3583
+ extensions,
3584
+ records: trackedRecords,
3585
+ stagedRecords,
3586
+ ...options.force !== void 0 ? { force: options.force } : {}
3587
+ }));
3264
3588
  }
3265
3589
  }
3590
+ if (stagedRecords.size > 0) {
3591
+ updateWizardState((current) => {
3592
+ const previousFiles = current.skillFiles?.files ?? [];
3593
+ const nextFiles = /* @__PURE__ */ new Map();
3594
+ for (const record of previousFiles) {
3595
+ nextFiles.set(record.path, record);
3596
+ }
3597
+ for (const record of stagedRecords.values()) {
3598
+ nextFiles.set(record.path, record);
3599
+ }
3600
+ return {
3601
+ ...current,
3602
+ skillFiles: {
3603
+ files: [...nextFiles.values()].sort((left, right) => left.path.localeCompare(right.path)),
3604
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3605
+ }
3606
+ };
3607
+ }, statePath);
3608
+ }
3609
+ const notes = [...plan.notes];
3610
+ if (enabledExtensions.length > 0) {
3611
+ notes.push(
3612
+ `Applied ${enabledExtensions.length} enabled OrgX skill extension${enabledExtensions.length === 1 ? "" : "s"} while syncing skills/rules.`
3613
+ );
3614
+ }
3615
+ if (writes.some((write) => write.skipped) || packs.some((pack) => pack.files.some((file) => file.skipped))) {
3616
+ notes.push("Some generated skill files were skipped because local manual edits were detected.");
3617
+ }
3266
3618
  return {
3267
- notes: plan.notes,
3619
+ extensions,
3620
+ notes,
3268
3621
  writes,
3269
3622
  packs
3270
3623
  };
3271
3624
  }
3625
+ function getSkillStatus(options = {}) {
3626
+ const state = readWizardState(options.statePath ?? ORGX_WIZARD_STATE_PATH);
3627
+ return {
3628
+ extensions: listSkillExtensions(
3629
+ options.extensionsDir !== void 0 ? { extensionsDir: options.extensionsDir } : {}
3630
+ ),
3631
+ trackedFiles: state?.skillFiles?.files ?? []
3632
+ };
3633
+ }
3272
3634
 
3273
3635
  // src/lib/plugins.ts
3274
3636
  import { spawn } from "child_process";
3275
3637
  import {
3276
- existsSync as existsSync3,
3638
+ existsSync as existsSync4,
3277
3639
  mkdirSync as mkdirSync2,
3278
3640
  mkdtempSync,
3279
3641
  readFileSync as readFileSync2,
3280
- readdirSync,
3642
+ readdirSync as readdirSync2,
3281
3643
  rmSync,
3282
3644
  statSync as statSync2,
3283
3645
  writeFileSync as writeFileSync2
@@ -3343,8 +3705,8 @@ function encodeRepoPath2(value) {
3343
3705
  return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
3344
3706
  }
3345
3707
  function isLikelyRepoFilePath(path) {
3346
- const basename = path.split("/").pop() ?? path;
3347
- return basename.includes(".") && !/^\.[^./]+$/.test(basename);
3708
+ const basename2 = path.split("/").pop() ?? path;
3709
+ return basename2.includes(".") && !/^\.[^./]+$/.test(basename2);
3348
3710
  }
3349
3711
  function buildContentsUrl2(spec, path) {
3350
3712
  const encodedPath = encodeRepoPath2(path);
@@ -3454,7 +3816,7 @@ async function fetchRemoteBytes(sourceUrl, fetchImpl) {
3454
3816
  return Buffer.from(await response.arrayBuffer());
3455
3817
  }
3456
3818
  function readBytesIfExists(path) {
3457
- if (!existsSync3(path)) return null;
3819
+ if (!existsSync4(path)) return null;
3458
3820
  try {
3459
3821
  if (!statSync2(path).isFile()) {
3460
3822
  return null;
@@ -3478,17 +3840,17 @@ function writeBytesIfChanged(path, bytes) {
3478
3840
  return true;
3479
3841
  }
3480
3842
  function removePathIfExists(path) {
3481
- if (!existsSync3(path)) return false;
3843
+ if (!existsSync4(path)) return false;
3482
3844
  rmSync(path, { force: true, recursive: true });
3483
3845
  return true;
3484
3846
  }
3485
3847
  function listRelativeFiles(root, base = root) {
3486
- if (!existsSync3(root)) return [];
3848
+ if (!existsSync4(root)) return [];
3487
3849
  if (!statSync2(root).isDirectory()) {
3488
3850
  return [];
3489
3851
  }
3490
3852
  const files = [];
3491
- for (const entry of readdirSync(root, { withFileTypes: true })) {
3853
+ for (const entry of readdirSync2(root, { withFileTypes: true })) {
3492
3854
  const nextPath = join3(root, entry.name);
3493
3855
  if (entry.isDirectory()) {
3494
3856
  files.push(...listRelativeFiles(nextPath, base));
@@ -3501,15 +3863,15 @@ function listRelativeFiles(root, base = root) {
3501
3863
  return files.sort();
3502
3864
  }
3503
3865
  function pruneEmptyDirectories(root, current = root) {
3504
- if (!existsSync3(current) || !statSync2(current).isDirectory()) {
3866
+ if (!existsSync4(current) || !statSync2(current).isDirectory()) {
3505
3867
  return false;
3506
3868
  }
3507
3869
  let changed = false;
3508
- for (const entry of readdirSync(current, { withFileTypes: true })) {
3870
+ for (const entry of readdirSync2(current, { withFileTypes: true })) {
3509
3871
  if (!entry.isDirectory()) continue;
3510
3872
  changed = pruneEmptyDirectories(root, join3(current, entry.name)) || changed;
3511
3873
  }
3512
- if (current !== root && readdirSync(current).length === 0) {
3874
+ if (current !== root && readdirSync2(current).length === 0) {
3513
3875
  rmSync(current, { force: true, recursive: true });
3514
3876
  return true;
3515
3877
  }
@@ -3519,7 +3881,7 @@ async function syncManagedRepoTree(spec, destinationRoot, fetchImpl) {
3519
3881
  const remoteFiles = await collectRemoteRepoFiles(spec, fetchImpl);
3520
3882
  let changed = false;
3521
3883
  const expected = new Set(remoteFiles.map((file) => file.localPath));
3522
- if (existsSync3(destinationRoot) && !statSync2(destinationRoot).isDirectory()) {
3884
+ if (existsSync4(destinationRoot) && !statSync2(destinationRoot).isDirectory()) {
3523
3885
  rmSync(destinationRoot, { force: true, recursive: true });
3524
3886
  changed = true;
3525
3887
  }
@@ -3610,7 +3972,7 @@ function upsertCodexMarketplaceEntry(path) {
3610
3972
  return writeJsonIfChanged(path, nextDocument);
3611
3973
  }
3612
3974
  function removeCodexMarketplaceEntry(path) {
3613
- if (!existsSync3(path)) {
3975
+ if (!existsSync4(path)) {
3614
3976
  return false;
3615
3977
  }
3616
3978
  const { document, plugins } = readMarketplacePlugins(path);
@@ -3788,7 +4150,7 @@ async function buildClaudeStatus(paths, runner) {
3788
4150
  message: "Claude Code was not detected."
3789
4151
  };
3790
4152
  }
3791
- const marketplaceExists = existsSync3(paths.claudeMarketplaceManifestPath);
4153
+ const marketplaceExists = existsSync4(paths.claudeMarketplaceManifestPath);
3792
4154
  return {
3793
4155
  target: "claude",
3794
4156
  available: true,
@@ -3799,7 +4161,7 @@ async function buildClaudeStatus(paths, runner) {
3799
4161
  function buildCodexStatus(paths, runner) {
3800
4162
  return (async () => {
3801
4163
  const available = detectSurface("codex").detected || await commandExists("codex", runner);
3802
- const pluginExists = existsSync3(paths.codexPluginDir);
4164
+ const pluginExists = existsSync4(paths.codexPluginDir);
3803
4165
  const marketplaceExists = codexMarketplaceHasOrgxEntry(paths.codexMarketplacePath);
3804
4166
  const installed = pluginExists && marketplaceExists;
3805
4167
  if (installed) {
@@ -4700,28 +5062,67 @@ async function printPluginStatusSection() {
4700
5062
  }
4701
5063
  function printSkillInstallReport(report) {
4702
5064
  for (const write of report.writes) {
4703
- const icon = write.changed ? ICON.ok : ICON.skip;
4704
- const state = write.changed ? pc3.green("updated ") : pc3.dim("unchanged");
5065
+ const icon = write.skipped ? ICON.warn : write.changed ? ICON.ok : ICON.skip;
5066
+ const state = write.skipped ? pc3.yellow("skipped ") : write.changed ? pc3.green("updated ") : pc3.dim("unchanged");
4705
5067
  console.log(` ${icon} ${pc3.bold(write.label.padEnd(10))} ${state}`);
5068
+ if (write.reason) {
5069
+ console.log(` ${pc3.dim(write.reason)}`);
5070
+ }
4706
5071
  }
4707
5072
  for (const pack of report.packs) {
4708
5073
  const changedCount = pack.files.filter((f) => f.changed).length;
4709
- const icon = changedCount > 0 ? ICON.ok : ICON.skip;
4710
- const state = changedCount > 0 ? pc3.green(`${changedCount} updated `) : pc3.dim("unchanged");
5074
+ const skippedCount = pack.files.filter((f) => f.skipped).length;
5075
+ const icon = skippedCount > 0 ? ICON.warn : changedCount > 0 ? ICON.ok : ICON.skip;
5076
+ const state = skippedCount > 0 ? pc3.yellow(`${skippedCount} skipped `) : changedCount > 0 ? pc3.green(`${changedCount} updated `) : pc3.dim("unchanged");
4711
5077
  console.log(` ${icon} ${pc3.bold(pack.name.padEnd(10))} ${state} ${pc3.dim(`${pack.files.length} files`)}`);
5078
+ for (const file of pack.files.filter((entry) => entry.skipped && entry.reason)) {
5079
+ console.log(` ${pc3.dim(`${file.label}: ${file.reason}`)}`);
5080
+ }
4712
5081
  }
4713
5082
  for (const note of report.notes) {
4714
5083
  console.log(` ${ICON.skip} ${pc3.dim(note)}`);
4715
5084
  }
4716
5085
  }
4717
5086
  function countSkillReportChanges(report) {
4718
- const writeChanges = report.writes.filter((write) => write.changed).length;
5087
+ const writeChanges = report.writes.filter((write) => write.changed && !write.skipped).length;
4719
5088
  const packChanges = report.packs.reduce(
4720
- (count, pack) => count + pack.files.filter((file) => file.changed).length,
5089
+ (count, pack) => count + pack.files.filter((file) => file.changed && !file.skipped).length,
4721
5090
  0
4722
5091
  );
4723
5092
  return writeChanges + packChanges;
4724
5093
  }
5094
+ function printSkillExtensions(extensions) {
5095
+ if (extensions.length === 0) {
5096
+ console.log(` ${ICON.skip} ${pc3.dim("no skill extensions yet")}`);
5097
+ return;
5098
+ }
5099
+ for (const extension of extensions) {
5100
+ const icon = extension.enabled ? ICON.ok : ICON.skip;
5101
+ const state = extension.enabled ? pc3.green("enabled ") : pc3.dim("disabled ");
5102
+ console.log(
5103
+ ` ${icon} ${pc3.bold(extension.id.padEnd(24))} ${state} ${pc3.dim(extension.path)}`
5104
+ );
5105
+ }
5106
+ }
5107
+ function printSkillExtensionWrite(result) {
5108
+ const action = result.created ? "created" : result.changed ? "updated" : "unchanged";
5109
+ const color = result.created || result.changed ? pc3.green : pc3.dim;
5110
+ console.log(
5111
+ ` ${result.created || result.changed ? ICON.ok : ICON.skip} ${pc3.bold(result.extension.id.padEnd(24))} ${color(action)}`
5112
+ );
5113
+ console.log(` ${pc3.dim(result.path)}`);
5114
+ }
5115
+ function openPathInEditor(path) {
5116
+ const editor = process.env.EDITOR || process.env.VISUAL;
5117
+ if (!editor) {
5118
+ return false;
5119
+ }
5120
+ const result = spawnSync3(editor, [path], {
5121
+ shell: true,
5122
+ stdio: "inherit"
5123
+ });
5124
+ return result.status === 0;
5125
+ }
4725
5126
  async function safeTrackWizardTelemetry(event, properties = {}) {
4726
5127
  try {
4727
5128
  await trackWizardTelemetry(event, properties);
@@ -5022,7 +5423,7 @@ async function promptOptionalCompanionPluginTargets(input) {
5022
5423
  return [];
5023
5424
  }
5024
5425
  const selection = await multiselectPrompt({
5025
- message: "Install companion OrgX plugins into detected tools?",
5426
+ message: "Install OrgX companion plugins/rules into your detected AI tools?",
5026
5427
  options: installable.map((status) => ({
5027
5428
  value: status.target,
5028
5429
  label: `Install ${formatPluginTargetLabel(status.target)}`,
@@ -5050,10 +5451,10 @@ async function installSelectedCompanionPlugins(input) {
5050
5451
  if (input.targets.length === 0) {
5051
5452
  return "skipped";
5052
5453
  }
5053
- const spinner = createOrgxSpinner("Installing OrgX companion plugins");
5454
+ const spinner = createOrgxSpinner("Installing OrgX companion plugins/rules");
5054
5455
  spinner.start();
5055
5456
  const report = await installOrgxPlugins({ targets: input.targets });
5056
- spinner.succeed("OrgX companion plugins processed");
5457
+ spinner.succeed("OrgX companion plugins/rules processed");
5057
5458
  printPluginMutationReport(report);
5058
5459
  printPluginSkillOwnershipNote(input.targets);
5059
5460
  await safeTrackWizardTelemetry("plugins_installed", {
@@ -5155,13 +5556,13 @@ function printDoctorReport(report, assessment) {
5155
5556
  }
5156
5557
  async function main() {
5157
5558
  const program = new Command();
5158
- program.name("orgx-wizard").description("One-line CLI onboarding for OrgX surfaces.").showHelpAfterError();
5159
- const pkgVersion = true ? "0.1.10" : void 0;
5559
+ program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
5560
+ const pkgVersion = true ? "0.1.11" : void 0;
5160
5561
  program.version(pkgVersion ?? "unknown", "-V, --version");
5161
5562
  program.hook("preAction", () => {
5162
5563
  console.log(renderBanner(pkgVersion));
5163
5564
  });
5164
- program.command("setup").description("Patch all detected automated OrgX surfaces.").option("--preset <name>", "run a setup bundle (currently: founder)").action(async (options) => {
5565
+ program.command("setup").description("Add OrgX MCP configs, skills/rules, and companion plugins to detected tools.").option("--preset <name>", "run a setup bundle (currently: founder)").action(async (options) => {
5165
5566
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
5166
5567
  await safeTrackWizardTelemetry("wizard_started", {
5167
5568
  command: "setup",
@@ -5527,7 +5928,7 @@ async function main() {
5527
5928
  );
5528
5929
  }
5529
5930
  });
5530
- program.command("uninstall").description("Remove OrgX-managed surfaces, companion plugins/rules, auth, and wizard state from this machine.").option("--keep-auth", "Keep the wizard-local saved OrgX API key.").option("--keep-state", "Keep wizard-local setup state.").option("--skip-plugins", "Skip companion plugin and Cursor rules removal.").option("--skip-surfaces", "Skip MCP surface config removal.").action(async (options) => {
5931
+ program.command("uninstall").description("Remove OrgX-managed MCP tool configs, companion plugins/rules, auth, and wizard state from this machine.").option("--keep-auth", "Keep the wizard-local saved OrgX API key.").option("--keep-state", "Keep wizard-local setup state.").option("--skip-plugins", "Skip companion plugin and Cursor rules removal.").option("--skip-surfaces", "Skip MCP tool config removal.").action(async (options) => {
5531
5932
  await safeTrackWizardTelemetry("wizard_uninstalled", {
5532
5933
  keep_auth: Boolean(options.keepAuth),
5533
5934
  keep_state: Boolean(options.keepState),
@@ -5584,28 +5985,28 @@ async function main() {
5584
5985
  const results = removeSurface(names);
5585
5986
  printMutationResults(results);
5586
5987
  });
5587
- const mcp = program.command("mcp").description("Manage OrgX MCP entries for Claude, Cursor, Codex, VS Code, Windsurf, and Zed.");
5588
- mcp.command("add").description("Add OrgX MCP entries to one client or all supported MCP clients.").argument("[surfaces...]", "claude, cursor, codex, vscode, windsurf, zed, or all", ["all"]).action(async (names) => {
5988
+ const mcp = program.command("mcp").description("Add or remove OrgX MCP config entries in Claude, Cursor, Codex, VS Code, Windsurf, and Zed.");
5989
+ mcp.command("add").description("Add OrgX MCP entries to one tool config or all supported MCP tool configs.").argument("[surfaces...]", "claude, cursor, codex, vscode, windsurf, zed, or all", ["all"]).action(async (names) => {
5589
5990
  const results = await addMcpSurface(names);
5590
5991
  printMutationResults(results);
5591
5992
  });
5592
- mcp.command("remove").description("Remove OrgX MCP entries from one client or all supported MCP clients.").argument("[surfaces...]", "claude, cursor, codex, vscode, windsurf, zed, or all", ["all"]).action((names) => {
5993
+ mcp.command("remove").description("Remove OrgX MCP entries from one tool config or all supported MCP tool configs.").argument("[surfaces...]", "claude, cursor, codex, vscode, windsurf, zed, or all", ["all"]).action((names) => {
5593
5994
  const results = removeMcpSurface(names);
5594
5995
  printMutationResults(results);
5595
5996
  });
5596
- const plugins = program.command("plugins").description("Install or remove Cursor rules and companion OrgX plugins for Claude Code, Codex, and OpenClaw.");
5597
- plugins.command("list").description("Show companion plugin availability and install status.").action(async () => {
5997
+ const plugins = program.command("plugins").description("Install or remove OrgX companion plugins/rules in Cursor, Claude Code, Codex, and OpenClaw.");
5998
+ plugins.command("list").description("Show companion plugin/rules availability and install status in your tools.").action(async () => {
5598
5999
  const spinner = createOrgxSpinner("Checking OrgX plugin and Cursor rules status");
5599
6000
  spinner.start();
5600
6001
  const statuses = await listOrgxPluginStatuses();
5601
6002
  spinner.succeed("OrgX plugin and Cursor rules status checked");
5602
6003
  printPluginStatusReport(statuses);
5603
6004
  });
5604
- plugins.command("add").description("Install Cursor OrgX rules or companion plugins into Claude Code, Codex, and OpenClaw.").argument("[targets...]", "cursor, claude, codex, openclaw, or all", ["all"]).action(async (targets) => {
5605
- const spinner = createOrgxSpinner("Installing OrgX companion plugins");
6005
+ plugins.command("add").description("Install OrgX companion plugins/rules into Cursor, Claude Code, Codex, and OpenClaw.").argument("[targets...]", "cursor, claude, codex, openclaw, or all", ["all"]).action(async (targets) => {
6006
+ const spinner = createOrgxSpinner("Installing OrgX companion plugins/rules");
5606
6007
  spinner.start();
5607
6008
  const report = await installOrgxPlugins({ targets });
5608
- spinner.succeed("OrgX companion plugins processed");
6009
+ spinner.succeed("OrgX companion plugins/rules processed");
5609
6010
  printPluginMutationReport(report);
5610
6011
  printPluginSkillOwnershipNote(report.results.map((result) => result.target));
5611
6012
  await safeTrackWizardTelemetry("plugins_installed", {
@@ -5615,11 +6016,11 @@ async function main() {
5615
6016
  target_count: report.results.length
5616
6017
  });
5617
6018
  });
5618
- plugins.command("remove").description("Uninstall managed Cursor OrgX rules or companion plugins from Claude Code, Codex, and OpenClaw.").argument("[targets...]", "cursor, claude, codex, openclaw, or all", ["all"]).action(async (targets) => {
5619
- const spinner = createOrgxSpinner("Removing OrgX companion plugins");
6019
+ plugins.command("remove").description("Uninstall managed OrgX companion plugins/rules from Cursor, Claude Code, Codex, and OpenClaw.").argument("[targets...]", "cursor, claude, codex, openclaw, or all", ["all"]).action(async (targets) => {
6020
+ const spinner = createOrgxSpinner("Removing OrgX companion plugins/rules");
5620
6021
  spinner.start();
5621
6022
  const report = await uninstallOrgxPlugins({ targets });
5622
- spinner.succeed("OrgX companion plugins removed");
6023
+ spinner.succeed("OrgX companion plugins/rules removed");
5623
6024
  printPluginMutationReport(report);
5624
6025
  await safeTrackWizardTelemetry("plugins_removed", {
5625
6026
  changed_count: countPluginReportChanges(report),
@@ -5694,16 +6095,17 @@ async function main() {
5694
6095
  process.exitCode = 1;
5695
6096
  }
5696
6097
  });
5697
- const skills = program.command("skills").description("Install OrgX rules and Claude skill packs.");
5698
- skills.command("add").description("Write standalone OrgX editor rules and Claude skill packs, skipping surfaces already owned by companion plugins.").argument("[packs...]", "skill pack names or 'all'", ["all"]).action(async (packs) => {
6098
+ const skills = program.command("skills").description("Install OrgX skills and rules into supported local tools.");
6099
+ skills.command("add").description("Write standalone OrgX Cursor rules and Claude skills, skipping tool surfaces already owned by companion plugins.").argument("[packs...]", "skill pack names or 'all'", ["all"]).option("--force", "Overwrite generated skill files even when manual edits are detected.").action(async (packs, options) => {
5699
6100
  const pluginTargets = (await listOrgxPluginStatuses()).filter((status) => status.installed).map((status) => status.target);
5700
- const spinner = createOrgxSpinner("Installing OrgX rules and skills");
6101
+ const spinner = createOrgxSpinner("Installing OrgX skills/rules into tools");
5701
6102
  spinner.start();
5702
6103
  const report = await installOrgxSkills({
6104
+ force: options.force === true,
5703
6105
  pluginTargets,
5704
6106
  skillNames: packs
5705
6107
  });
5706
- spinner.succeed("OrgX rules and skills installed");
6108
+ spinner.succeed("OrgX skills/rules installed into tools");
5707
6109
  printSkillInstallReport(report);
5708
6110
  await safeTrackWizardTelemetry("skills_installed", {
5709
6111
  changed_count: countSkillReportChanges(report),
@@ -5711,9 +6113,99 @@ async function main() {
5711
6113
  pack_count: report.packs.length,
5712
6114
  plugin_managed_target_count: pluginTargets.length,
5713
6115
  requested_pack_count: packs.length,
6116
+ skill_extension_count: report.extensions.length,
5714
6117
  write_count: report.writes.length
5715
6118
  });
5716
6119
  });
6120
+ skills.command("sync").description("Recompose installed OrgX skills/rules from core skills plus local extensions.").argument("[packs...]", "skill pack names or 'all'", ["all"]).option("--force", "Overwrite generated skill files even when manual edits are detected.").action(async (packs, options) => {
6121
+ const pluginTargets = (await listOrgxPluginStatuses()).filter((status) => status.installed).map((status) => status.target);
6122
+ const spinner = createOrgxSpinner("Syncing OrgX skills/rules and extensions into tools");
6123
+ spinner.start();
6124
+ const report = await installOrgxSkills({
6125
+ force: options.force === true,
6126
+ pluginTargets,
6127
+ skillNames: packs
6128
+ });
6129
+ spinner.succeed("OrgX skills/rules synced into tools");
6130
+ printSkillInstallReport(report);
6131
+ await safeTrackWizardTelemetry("skills_synced", {
6132
+ changed_count: countSkillReportChanges(report),
6133
+ command: "skills:sync",
6134
+ pack_count: report.packs.length,
6135
+ plugin_managed_target_count: pluginTargets.length,
6136
+ requested_pack_count: packs.length,
6137
+ skill_extension_count: report.extensions.length,
6138
+ write_count: report.writes.length
6139
+ });
6140
+ });
6141
+ skills.command("status").description("Show installed skill/rule tracking and local skill extensions.").action(async () => {
6142
+ const report = getSkillStatus();
6143
+ console.log(pc3.dim(" extensions"));
6144
+ printSkillExtensions(report.extensions);
6145
+ console.log("");
6146
+ console.log(pc3.dim(" generated files"));
6147
+ if (report.trackedFiles.length === 0) {
6148
+ console.log(` ${ICON.skip} ${pc3.dim("no generated skill files tracked yet")}`);
6149
+ } else {
6150
+ for (const file of report.trackedFiles) {
6151
+ console.log(` ${ICON.ok} ${pc3.bold(file.skillId.padEnd(24))} ${pc3.dim(file.path)}`);
6152
+ }
6153
+ }
6154
+ });
6155
+ const skillExtensions = skills.command("extensions").description("Create, edit, and sync user extensions appended after OrgX core skills.");
6156
+ skillExtensions.command("list").description("List local OrgX skill extensions.").action(() => {
6157
+ printSkillExtensions(listSkillExtensions());
6158
+ });
6159
+ skillExtensions.command("add").description("Create a local extension file for an OrgX skill.").argument("<skill>", "Skill id, for example orgx, cursor-rules, or morning-briefing").option("--scope <scope>", "Extension scope: user, workspace, or project.", "user").option("--title <title>", "Extension title.").option("--content <content>", "Initial extension body content.").option("--overwrite", "Overwrite an existing extension file.").action((skill, options) => {
6160
+ const result = addSkillExtension({
6161
+ overwrite: options.overwrite === true,
6162
+ skillId: skill,
6163
+ ...options.content !== void 0 ? { content: options.content } : {},
6164
+ ...options.scope !== void 0 ? { scope: options.scope } : {},
6165
+ ...options.title !== void 0 ? { title: options.title } : {}
6166
+ });
6167
+ printSkillExtensionWrite(result);
6168
+ console.log(
6169
+ ` ${ICON.skip} ${pc3.dim(`Run ${getCmd()} skills sync to apply it to configured tools.`)}`
6170
+ );
6171
+ });
6172
+ skillExtensions.command("edit").description("Create if needed, then open a local OrgX skill extension in $EDITOR.").argument("<skill>", "Skill id, for example orgx, cursor-rules, or morning-briefing").option("--scope <scope>", "Extension scope: user, workspace, or project.", "user").action((skill, options) => {
6173
+ const result = addSkillExtension({
6174
+ skillId: skill,
6175
+ ...options.scope !== void 0 ? { scope: options.scope } : {}
6176
+ });
6177
+ printSkillExtensionWrite(result);
6178
+ if (!openPathInEditor(result.path)) {
6179
+ console.log(
6180
+ ` ${ICON.warn} ${pc3.yellow("editor not opened")} ${pc3.dim(`Set EDITOR or edit ${result.path}`)}`
6181
+ );
6182
+ }
6183
+ console.log(
6184
+ ` ${ICON.skip} ${pc3.dim(`Run ${getCmd()} skills sync to apply it to configured tools.`)}`
6185
+ );
6186
+ });
6187
+ skillExtensions.command("enable").description("Enable a local OrgX skill extension.").argument("<skill>", "Skill id, for example orgx, cursor-rules, or morning-briefing").option("--scope <scope>", "Extension scope: user, workspace, or project.", "user").action((skill, options) => {
6188
+ const result = setSkillExtensionEnabled({
6189
+ enabled: true,
6190
+ skillId: skill,
6191
+ ...options.scope !== void 0 ? { scope: options.scope } : {}
6192
+ });
6193
+ printSkillExtensionWrite(result);
6194
+ console.log(
6195
+ ` ${ICON.skip} ${pc3.dim(`Run ${getCmd()} skills sync to apply it to configured tools.`)}`
6196
+ );
6197
+ });
6198
+ skillExtensions.command("disable").description("Disable a local OrgX skill extension without deleting it.").argument("<skill>", "Skill id, for example orgx, cursor-rules, or morning-briefing").option("--scope <scope>", "Extension scope: user, workspace, or project.", "user").action((skill, options) => {
6199
+ const result = setSkillExtensionEnabled({
6200
+ enabled: false,
6201
+ skillId: skill,
6202
+ ...options.scope !== void 0 ? { scope: options.scope } : {}
6203
+ });
6204
+ printSkillExtensionWrite(result);
6205
+ console.log(
6206
+ ` ${ICON.skip} ${pc3.dim(`Run ${getCmd()} skills sync to apply it to configured tools.`)}`
6207
+ );
6208
+ });
5717
6209
  await program.parseAsync(process.argv);
5718
6210
  }
5719
6211
  main().catch((error) => {