@orderful/droid 0.52.1 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @orderful/droid
2
2
 
3
+ ## 0.53.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#325](https://github.com/Orderful/droid/pull/325) [`241a9ac`](https://github.com/Orderful/droid/commit/241a9acbfb9864c634fe9f858e19b594d80e83f9) Thanks [@frytyler](https://github.com/frytyler)! - Export tools + pack functions from package entry point and add `droid copy-skills` command for agents integration
8
+
9
+ ### Patch Changes
10
+
11
+ - [#323](https://github.com/Orderful/droid/pull/323) [`f1459be`](https://github.com/Orderful/droid/commit/f1459beecb6aa72c5fcc6ee552308a3d39f52aad) Thanks [@frytyler](https://github.com/frytyler)! - Add `planSummary` field to propose-plan evidence schema
12
+
3
13
  ## 0.52.1
4
14
 
5
15
  ### Patch Changes
package/dist/bin/droid.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/bin/droid.ts
4
4
  import { program } from "commander";
5
- import chalk13 from "chalk";
5
+ import chalk14 from "chalk";
6
6
 
7
7
  // src/commands/setup.ts
8
8
  import inquirer from "inquirer";
@@ -1410,6 +1410,39 @@ function findSkillPath(skillName) {
1410
1410
  }
1411
1411
  return null;
1412
1412
  }
1413
+ function getSkillPath(skillName) {
1414
+ const result = findSkillPath(skillName);
1415
+ return result?.skillDir ?? null;
1416
+ }
1417
+ function copySkillsToDir(skillNames, dest) {
1418
+ if (!existsSync7(dest)) {
1419
+ mkdirSync5(dest, { recursive: true });
1420
+ }
1421
+ const results = [];
1422
+ for (const name of skillNames) {
1423
+ const skillDir = getSkillPath(name);
1424
+ if (!skillDir) {
1425
+ results.push({
1426
+ name,
1427
+ success: false,
1428
+ error: `Skill '${name}' not found in bundled tools`
1429
+ });
1430
+ continue;
1431
+ }
1432
+ const targetDir = join8(dest, name);
1433
+ try {
1434
+ copyDirectoryRecursive(skillDir, targetDir, () => true);
1435
+ results.push({ name, success: true, dest: targetDir });
1436
+ } catch (err) {
1437
+ results.push({
1438
+ name,
1439
+ success: false,
1440
+ error: `Failed to copy: ${err}`
1441
+ });
1442
+ }
1443
+ }
1444
+ return results;
1445
+ }
1413
1446
  function getBundledSkills() {
1414
1447
  if (!existsSync7(BUNDLED_SKILLS_DIR)) {
1415
1448
  return [];
@@ -6101,6 +6134,52 @@ async function slackPostCommand(options) {
6101
6134
  }
6102
6135
  }
6103
6136
 
6137
+ // src/commands/copy-skills.ts
6138
+ import chalk13 from "chalk";
6139
+ import { readFileSync as readFileSync13 } from "fs";
6140
+ function copySkillsCommand(options) {
6141
+ const { dest, manifest, names } = options;
6142
+ let skillNames;
6143
+ if (manifest) {
6144
+ try {
6145
+ const content = readFileSync13(manifest, "utf-8");
6146
+ skillNames = JSON.parse(content);
6147
+ if (!Array.isArray(skillNames)) {
6148
+ console.error(
6149
+ chalk13.red("Manifest must be a JSON array of skill names")
6150
+ );
6151
+ process.exit(1);
6152
+ }
6153
+ } catch (err) {
6154
+ console.error(chalk13.red(`Failed to read manifest: ${err}`));
6155
+ process.exit(1);
6156
+ }
6157
+ } else if (names?.length) {
6158
+ skillNames = names;
6159
+ } else {
6160
+ console.error(
6161
+ chalk13.red("Provide either --manifest <file> or --names <names...>")
6162
+ );
6163
+ process.exit(1);
6164
+ }
6165
+ if (skillNames.length === 0) {
6166
+ console.log(chalk13.yellow("No skills to copy."));
6167
+ return;
6168
+ }
6169
+ const results = copySkillsToDir(skillNames, dest);
6170
+ for (const result of results) {
6171
+ if (result.success) {
6172
+ console.log(chalk13.green(` \u2713 ${result.name} \u2192 ${result.dest}`));
6173
+ } else {
6174
+ console.error(chalk13.red(` \u2717 ${result.name}: ${result.error}`));
6175
+ }
6176
+ }
6177
+ const failed = results.filter((r) => !r.success);
6178
+ if (failed.length > 0) {
6179
+ process.exit(1);
6180
+ }
6181
+ }
6182
+
6104
6183
  // src/bin/droid.ts
6105
6184
  var version = getVersion();
6106
6185
  program.name("droid").description("Droid, teaching your AI new tricks").version(version);
@@ -6121,6 +6200,7 @@ program.command("update").description("Update droid and installed tools").option
6121
6200
  program.command("tui").description("Launch interactive TUI dashboard").action(tuiCommand);
6122
6201
  program.command("pack").description("Create zip packs for distribution").argument("[audience]", "Target audience (e.g., engineering, customer-support)").option("-l, --list", "List available audiences and tool counts").option("-t, --tool <name>", "Pack a single tool by name").option("-o, --output <dir>", "Output directory (default: cwd)").action(packCommand);
6123
6202
  program.command("exec <tool> <script>").description("Execute a tool script").argument("[args...]", "Arguments to pass to the script").allowUnknownOption().action(execCommand);
6203
+ program.command("copy-skills").description("Copy bundled skills to a destination directory").requiredOption("-d, --dest <dir>", "Destination directory").option("-m, --manifest <file>", "JSON file with array of skill names").option("-n, --names <names...>", "Skill names to copy").action(copySkillsCommand);
6124
6204
  var integrations = program.command("integrations").description("Manage external service integrations");
6125
6205
  var integrationsSetup = integrations.command("setup").description("Set up an integration");
6126
6206
  integrationsSetup.command("slack").description("Set up Slack integration").action(integrationsSetupSlackCommand);
@@ -6132,8 +6212,8 @@ if (configExists()) {
6132
6212
  const config = loadConfig();
6133
6213
  const newPlatforms = syncNewPlatforms(config);
6134
6214
  if (newPlatforms.length > 0) {
6135
- console.log(chalk13.green(`\u2713 Detected new platform(s): ${newPlatforms.join(", ")}`));
6136
- console.log(chalk13.gray(" Synced installed tools to new platform(s)\n"));
6215
+ console.log(chalk14.green(`\u2713 Detected new platform(s): ${newPlatforms.join(", ")}`));
6216
+ console.log(chalk14.gray(" Synced installed tools to new platform(s)\n"));
6137
6217
  saveConfig(config);
6138
6218
  }
6139
6219
  }
@@ -0,0 +1,15 @@
1
+ interface CopySkillsOptions {
2
+ dest: string;
3
+ manifest?: string;
4
+ names?: string[];
5
+ }
6
+ /**
7
+ * Copy bundled skills to a destination directory.
8
+ *
9
+ * Usage:
10
+ * droid copy-skills -d dist/assets/skills -m droid-skills.json
11
+ * droid copy-skills -d dist/assets/skills -n propose-plan edi-schema
12
+ */
13
+ export declare function copySkillsCommand(options: CopySkillsOptions): void;
14
+ export {};
15
+ //# sourceMappingURL=copy-skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-skills.d.ts","sourceRoot":"","sources":["../../src/commands/copy-skills.ts"],"names":[],"mappings":"AAIA,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAgDlE"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from './lib/types';
2
2
  export * from './lib/config';
3
3
  export * from './lib/skills';
4
+ export * from './lib/tools';
5
+ export * from './lib/pack';
4
6
  export * from './lib/version';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -602,6 +602,9 @@ async function checkForUpdates() {
602
602
  // src/lib/tools.ts
603
603
  var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
604
604
  var BUNDLED_TOOLS_DIR = join5(__dirname2, "../tools");
605
+ function getBundledToolsDir() {
606
+ return BUNDLED_TOOLS_DIR;
607
+ }
605
608
  function loadToolManifest(toolDir) {
606
609
  const manifestPath = join5(toolDir, "TOOL.yaml");
607
610
  if (!existsSync4(manifestPath)) {
@@ -645,6 +648,58 @@ function isToolInstalled(toolName) {
645
648
  const requiredSkills = tool.includes.skills.filter((s) => s.required).map((s) => s.name);
646
649
  return requiredSkills.some((skillName) => skillName in installedTools);
647
650
  }
651
+ function getInstalledToolVersion(toolName) {
652
+ const config = loadConfig();
653
+ const installedTools = getPlatformTools(config);
654
+ const tool = getBundledTools().find((t) => t.name === toolName);
655
+ if (!tool) return null;
656
+ const requiredSkills = tool.includes.skills.filter((s) => s.required).map((s) => s.name);
657
+ for (const skillName of requiredSkills) {
658
+ if (installedTools[skillName]) {
659
+ return installedTools[skillName].version;
660
+ }
661
+ }
662
+ return null;
663
+ }
664
+ function getToolUpdateStatus(toolName) {
665
+ const installedVersion = getInstalledToolVersion(toolName);
666
+ const tool = getBundledTools().find((t) => t.name === toolName);
667
+ const bundledVersion = tool?.version || null;
668
+ if (!installedVersion || !bundledVersion) {
669
+ return {
670
+ name: toolName,
671
+ hasUpdate: false,
672
+ installedVersion,
673
+ bundledVersion
674
+ };
675
+ }
676
+ const hasUpdate = compareSemver(bundledVersion, installedVersion) > 0;
677
+ return {
678
+ name: toolName,
679
+ hasUpdate,
680
+ installedVersion,
681
+ bundledVersion
682
+ };
683
+ }
684
+ function getToolsWithUpdates() {
685
+ const config = loadConfig();
686
+ const installedTools = getPlatformTools(config);
687
+ const bundledTools = getBundledTools();
688
+ const toolsWithUpdates = [];
689
+ for (const tool of bundledTools) {
690
+ const requiredSkills = tool.includes.skills.filter((s) => s.required).map((s) => s.name);
691
+ const isInstalled = requiredSkills.some(
692
+ (skillName) => skillName in installedTools
693
+ );
694
+ if (isInstalled) {
695
+ const updateStatus = getToolUpdateStatus(tool.name);
696
+ if (updateStatus.hasUpdate) {
697
+ toolsWithUpdates.push(updateStatus);
698
+ }
699
+ }
700
+ }
701
+ return toolsWithUpdates;
702
+ }
648
703
 
649
704
  // src/lib/agents.ts
650
705
  var __dirname3 = dirname4(fileURLToPath3(import.meta.url));
@@ -1316,6 +1371,39 @@ function findSkillPath(skillName) {
1316
1371
  }
1317
1372
  return null;
1318
1373
  }
1374
+ function getSkillPath(skillName) {
1375
+ const result = findSkillPath(skillName);
1376
+ return result?.skillDir ?? null;
1377
+ }
1378
+ function copySkillsToDir(skillNames, dest) {
1379
+ if (!existsSync7(dest)) {
1380
+ mkdirSync5(dest, { recursive: true });
1381
+ }
1382
+ const results = [];
1383
+ for (const name of skillNames) {
1384
+ const skillDir = getSkillPath(name);
1385
+ if (!skillDir) {
1386
+ results.push({
1387
+ name,
1388
+ success: false,
1389
+ error: `Skill '${name}' not found in bundled tools`
1390
+ });
1391
+ continue;
1392
+ }
1393
+ const targetDir = join8(dest, name);
1394
+ try {
1395
+ copyDirectoryRecursive(skillDir, targetDir, () => true);
1396
+ results.push({ name, success: true, dest: targetDir });
1397
+ } catch (err) {
1398
+ results.push({
1399
+ name,
1400
+ success: false,
1401
+ error: `Failed to copy: ${err}`
1402
+ });
1403
+ }
1404
+ }
1405
+ return results;
1406
+ }
1319
1407
  function getBundledSkills() {
1320
1408
  if (!existsSync7(BUNDLED_SKILLS_DIR)) {
1321
1409
  return [];
@@ -1822,6 +1910,239 @@ function uninstallCommand(commandName, skillName) {
1822
1910
  }
1823
1911
  return { success: false, message: `Command not installed: ${commandName}` };
1824
1912
  }
1913
+
1914
+ // src/lib/pack.ts
1915
+ import { existsSync as existsSync8, readdirSync as readdirSync7, readFileSync as readFileSync7, createWriteStream } from "fs";
1916
+ import { join as join9 } from "path";
1917
+ import archiver from "archiver";
1918
+ function getToolsForAudience(audience) {
1919
+ const allTools = getBundledTools();
1920
+ return allTools.filter((tool) => {
1921
+ if (!tool.audience || tool.audience.length === 0) {
1922
+ return false;
1923
+ }
1924
+ return tool.audience.includes(audience) || tool.audience.includes("all" /* All */);
1925
+ });
1926
+ }
1927
+ function getAudienceInfo() {
1928
+ const allTools = getBundledTools();
1929
+ const audiences = Object.values(ToolAudience).filter(
1930
+ (a) => a !== "all" /* All */
1931
+ );
1932
+ return audiences.map((audience) => {
1933
+ const tools = allTools.filter(
1934
+ (tool) => tool.audience && (tool.audience.includes(audience) || tool.audience.includes("all" /* All */))
1935
+ );
1936
+ return {
1937
+ audience,
1938
+ toolCount: tools.length,
1939
+ toolNames: tools.map((t) => t.name)
1940
+ };
1941
+ }).filter((info) => info.toolCount > 0);
1942
+ }
1943
+ function collectToolArtifacts(tool) {
1944
+ const artifacts = [];
1945
+ const toolDir = join9(getBundledToolsDir(), tool.name);
1946
+ for (const skill of tool.includes.skills) {
1947
+ const skillDir = join9(toolDir, "skills", skill.name);
1948
+ if (!existsSync8(skillDir)) continue;
1949
+ const skillMd = join9(skillDir, "SKILL.md");
1950
+ if (existsSync8(skillMd)) {
1951
+ artifacts.push({
1952
+ sourcePath: skillMd,
1953
+ zipPath: `skills/${skill.name}/SKILL.md`
1954
+ });
1955
+ }
1956
+ const refsDir = join9(skillDir, "references");
1957
+ if (existsSync8(refsDir)) {
1958
+ const refFiles = readdirSync7(refsDir).filter((f) => !f.startsWith("."));
1959
+ for (const file of refFiles) {
1960
+ artifacts.push({
1961
+ sourcePath: join9(refsDir, file),
1962
+ zipPath: `skills/${skill.name}/references/${file}`
1963
+ });
1964
+ }
1965
+ }
1966
+ }
1967
+ const commandsDir = join9(toolDir, "commands");
1968
+ if (existsSync8(commandsDir)) {
1969
+ const commandFiles = readdirSync7(commandsDir).filter(
1970
+ (f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md"
1971
+ );
1972
+ for (const file of commandFiles) {
1973
+ artifacts.push({
1974
+ sourcePath: join9(commandsDir, file),
1975
+ zipPath: `commands/${file}`
1976
+ });
1977
+ }
1978
+ }
1979
+ const agentsDir = join9(toolDir, "agents");
1980
+ if (existsSync8(agentsDir)) {
1981
+ const agentFiles = readdirSync7(agentsDir).filter(
1982
+ (f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md"
1983
+ );
1984
+ for (const file of agentFiles) {
1985
+ artifacts.push({
1986
+ sourcePath: join9(agentsDir, file),
1987
+ zipPath: `agents/${file}`
1988
+ });
1989
+ }
1990
+ }
1991
+ return artifacts;
1992
+ }
1993
+ function generateClaudeMd(tools) {
1994
+ const skillNames = [];
1995
+ for (const tool of tools) {
1996
+ for (const skill of tool.includes.skills) {
1997
+ skillNames.push(skill.name);
1998
+ }
1999
+ }
2000
+ const lines = [
2001
+ "# Droid Skills",
2002
+ "",
2003
+ "Skills installed from a Droid pack. Each skill teaches your AI new capabilities.",
2004
+ ""
2005
+ ];
2006
+ for (const name of skillNames) {
2007
+ lines.push(`- [${name}](skills/${name}/SKILL.md)`);
2008
+ }
2009
+ lines.push("");
2010
+ return lines.join("\n");
2011
+ }
2012
+ function generateReadme(label, tools) {
2013
+ const lines = [
2014
+ `# Droid Pack: ${label}`,
2015
+ "",
2016
+ "This pack contains AI skills, commands, and agents for Claude Desktop.",
2017
+ "",
2018
+ "## Installation",
2019
+ "",
2020
+ "1. Unzip this archive",
2021
+ "2. Copy the contents into your `~/.claude/` directory:",
2022
+ "",
2023
+ "```bash",
2024
+ "# From the directory where you unzipped:",
2025
+ "cp -r skills/ ~/.claude/skills/",
2026
+ "cp -r commands/ ~/.claude/commands/",
2027
+ "cp -r agents/ ~/.claude/agents/",
2028
+ "```",
2029
+ "",
2030
+ "3. Append the contents of `CLAUDE.md` to your `~/.claude/CLAUDE.md`:",
2031
+ "",
2032
+ "```bash",
2033
+ "cat CLAUDE.md >> ~/.claude/CLAUDE.md",
2034
+ "```",
2035
+ "",
2036
+ "## Included Tools",
2037
+ ""
2038
+ ];
2039
+ for (const tool of tools) {
2040
+ lines.push(`- **${tool.name}** (v${tool.version}) \u2014 ${tool.description}`);
2041
+ }
2042
+ lines.push("");
2043
+ return lines.join("\n");
2044
+ }
2045
+ function checkDependencies(tools) {
2046
+ const warnings = [];
2047
+ const toolNames = new Set(tools.map((t) => t.name));
2048
+ for (const tool of tools) {
2049
+ if (!tool.dependencies) continue;
2050
+ for (const dep of tool.dependencies) {
2051
+ if (!toolNames.has(dep)) {
2052
+ warnings.push(
2053
+ `Tool '${tool.name}' depends on '${dep}' which is not included in this pack`
2054
+ );
2055
+ }
2056
+ }
2057
+ }
2058
+ return warnings;
2059
+ }
2060
+ async function buildToolPack(options) {
2061
+ const { toolName, outputDir } = options;
2062
+ const allTools = getBundledTools();
2063
+ const tool = allTools.find((t) => t.name === toolName);
2064
+ if (!tool) {
2065
+ return {
2066
+ success: false,
2067
+ message: `Tool '${toolName}' not found`
2068
+ };
2069
+ }
2070
+ const tools = [tool];
2071
+ const warnings = checkDependencies(tools);
2072
+ const filename = `droid-${toolName}.zip`;
2073
+ const outputPath = join9(outputDir, filename);
2074
+ return new Promise((resolve) => {
2075
+ const output = createWriteStream(outputPath);
2076
+ const archive = archiver("zip", { zlib: { level: 9 } });
2077
+ output.on("close", () => {
2078
+ resolve({
2079
+ success: true,
2080
+ message: `Pack created: ${filename}`,
2081
+ outputPath,
2082
+ toolCount: 1,
2083
+ warnings: warnings.length > 0 ? warnings : void 0
2084
+ });
2085
+ });
2086
+ archive.on("error", (err) => {
2087
+ resolve({
2088
+ success: false,
2089
+ message: `Failed to create pack: ${err.message}`
2090
+ });
2091
+ });
2092
+ archive.pipe(output);
2093
+ const artifacts = collectToolArtifacts(tool);
2094
+ for (const artifact of artifacts) {
2095
+ const content = readFileSync7(artifact.sourcePath, "utf-8");
2096
+ archive.append(content, { name: artifact.zipPath });
2097
+ }
2098
+ archive.append(generateClaudeMd(tools), { name: "CLAUDE.md" });
2099
+ archive.append(generateReadme(toolName, tools), { name: "README.md" });
2100
+ archive.finalize();
2101
+ });
2102
+ }
2103
+ async function buildPack(options) {
2104
+ const { audience, outputDir } = options;
2105
+ const tools = getToolsForAudience(audience);
2106
+ if (tools.length === 0) {
2107
+ return {
2108
+ success: false,
2109
+ message: `No tools found for audience '${audience}'`
2110
+ };
2111
+ }
2112
+ const warnings = checkDependencies(tools);
2113
+ const filename = `droid-${audience}-pack.zip`;
2114
+ const outputPath = join9(outputDir, filename);
2115
+ return new Promise((resolve) => {
2116
+ const output = createWriteStream(outputPath);
2117
+ const archive = archiver("zip", { zlib: { level: 9 } });
2118
+ output.on("close", () => {
2119
+ resolve({
2120
+ success: true,
2121
+ message: `Pack created: ${filename}`,
2122
+ outputPath,
2123
+ toolCount: tools.length,
2124
+ warnings: warnings.length > 0 ? warnings : void 0
2125
+ });
2126
+ });
2127
+ archive.on("error", (err) => {
2128
+ resolve({
2129
+ success: false,
2130
+ message: `Failed to create pack: ${err.message}`
2131
+ });
2132
+ });
2133
+ archive.pipe(output);
2134
+ for (const tool of tools) {
2135
+ const artifacts = collectToolArtifacts(tool);
2136
+ for (const artifact of artifacts) {
2137
+ const content = readFileSync7(artifact.sourcePath, "utf-8");
2138
+ archive.append(content, { name: artifact.zipPath });
2139
+ }
2140
+ }
2141
+ archive.append(generateClaudeMd(tools), { name: "CLAUDE.md" });
2142
+ archive.append(generateReadme(audience, tools), { name: "README.md" });
2143
+ archive.finalize();
2144
+ });
2145
+ }
1825
2146
  export {
1826
2147
  AIToolValue as AITool,
1827
2148
  BuiltInOutput,
@@ -1830,20 +2151,27 @@ export {
1830
2151
  SkillStatus,
1831
2152
  ToolAudience,
1832
2153
  addRepo,
2154
+ buildPack,
2155
+ buildToolPack,
1833
2156
  checkForUpdates,
1834
2157
  compareSemver,
1835
2158
  configExists,
2159
+ copySkillsToDir,
1836
2160
  ensureConfigDir,
1837
2161
  findSkillPath,
1838
2162
  getAITag,
2163
+ getAudienceInfo,
1839
2164
  getAutoUpdateConfig,
1840
2165
  getBundledSkills,
1841
2166
  getBundledSkillsDir,
2167
+ getBundledTools,
2168
+ getBundledToolsDir,
1842
2169
  getCommandsInstallPath,
1843
2170
  getConfigDir,
1844
2171
  getConfigPath,
1845
2172
  getConfigValue,
1846
2173
  getInstalledSkill,
2174
+ getInstalledToolVersion,
1847
2175
  getPlatformConfigPath,
1848
2176
  getPlatformTools,
1849
2177
  getPlatformToolsFor,
@@ -1851,20 +2179,26 @@ export {
1851
2179
  getRepoPath,
1852
2180
  getRepos,
1853
2181
  getSkillOverridesPath,
2182
+ getSkillPath,
1854
2183
  getSkillStatusDisplay,
1855
2184
  getSkillUpdateStatus,
1856
2185
  getSkillsInstallPath,
1857
2186
  getSkillsWithUpdates,
1858
2187
  getToolSettings,
2188
+ getToolUpdateStatus,
2189
+ getToolsForAudience,
2190
+ getToolsWithUpdates,
1859
2191
  getUpdateInfo,
1860
2192
  getVersion,
1861
2193
  installCommand,
1862
2194
  installSkill,
1863
2195
  isCommandInstalled,
1864
2196
  isSkillInstalled,
2197
+ isToolInstalled,
1865
2198
  loadConfig,
1866
2199
  loadSkillManifest,
1867
2200
  loadSkillOverrides,
2201
+ loadToolManifest,
1868
2202
  removeRepo,
1869
2203
  runUpdate,
1870
2204
  saveConfig,
@@ -34,6 +34,29 @@ export declare function findSkillPath(skillName: string): {
34
34
  toolDir: string;
35
35
  skillDir: string;
36
36
  } | null;
37
+ /**
38
+ * Resolve the absolute path to a bundled skill directory by name.
39
+ * Returns null if the skill is not found.
40
+ *
41
+ * This is the stable public API for consumers who need to locate skill
42
+ * assets (e.g., for copying into a build output). It encapsulates droid's
43
+ * internal `tools/{tool}/skills/{name}/` directory structure.
44
+ */
45
+ export declare function getSkillPath(skillName: string): string | null;
46
+ /**
47
+ * Copy bundled skills to a destination directory.
48
+ * Each skill is copied as `{dest}/{skillName}/` with SKILL.md, references/, etc.
49
+ *
50
+ * @param skillNames - Array of skill names to copy
51
+ * @param dest - Target directory (created if it doesn't exist)
52
+ * @returns Results per skill: success/failure with paths
53
+ */
54
+ export declare function copySkillsToDir(skillNames: string[], dest: string): Array<{
55
+ name: string;
56
+ success: boolean;
57
+ dest?: string;
58
+ error?: string;
59
+ }>;
37
60
  /**
38
61
  * Get all bundled skills from all tools
39
62
  */
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"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"}
@@ -32,6 +32,7 @@ Produce a single JSON object matching the schema in `references/output-schema.js
32
32
  "name": "string — short human-readable plan name (optional)",
33
33
  "category": "string — grouping category, e.g. org-provisioning (optional)",
34
34
  "evidence": {
35
+ "planSummary": "string — one-line plain-language summary, e.g. 'Provision org and billing for Acme Corp' (optional)",
35
36
  "reasoning": "string — your analysis of what needs to happen and why",
36
37
  "sourceSummary": "string — concise summary of what you found during research",
37
38
  "sourceRaw": "string — JSON-stringified raw source data from research (optional)",
@@ -53,9 +54,10 @@ Produce a single JSON object matching the schema in `references/output-schema.js
53
54
 
54
55
  ### Evidence Fields
55
56
 
56
- | Field | Purpose | Reviewer Experience |
57
- | --------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
58
- | `reasoning` | Your analysis what needs to happen and why. Cite actual data from your research, not assumptions. | First thing they read. Establishes trust. |
57
+ | Field | Purpose | Reviewer Experience |
58
+ | --------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
59
+ | `planSummary` | Optional. One-line plain-language summary of the plan. Keep it short and specific entity names, not jargon. | Shown in headers and card previews. Quick scan context. |
60
+ | `reasoning` | Your analysis — what needs to happen and why. Cite actual data from your research, not assumptions. | First thing they read. Establishes trust. |
59
61
  | `sourceSummary` | Concise summary of what you found. Include entity names, IDs, and states. | Quick scan to verify you looked at the right things. |
60
62
  | `sourceRaw` | Optional. JSON-stringified raw API responses or data snapshots. | Expandable detail for reviewers who want to verify. |
61
63
  | `impactSummary` | What changes if the plan is approved. Be specific — "creates org X with ISA ID Y" not "creates an org". | The core decision point. Reviewer approves based on this. |
@@ -129,6 +131,7 @@ Say you've been asked to provision an Orderful org for a new customer. You've qu
129
131
  ```json
130
132
  {
131
133
  "evidence": {
134
+ "planSummary": "Provision org, billing, and welcome email for Acme Corp",
132
135
  "reasoning": "Salesforce account Acme Corp (SF-001234) was signed on 2026-03-01 and has no Orderful OrgID. No existing Orderful org matches by name or ISA ID. The account's primary contact is jane@acme.com. ISA ID derived from Customer_ISA_ID__c field: ACME001.",
133
136
  "sourceSummary": "Salesforce: Acme Corp (SF-001234), signed 2026-03-01, no OrgID. Orderful: no org named 'Acme Corp', no ISA ID 'ACME001' in use.",
134
137
  "sourceRaw": "{\"salesforce_account\":{\"Id\":\"SF-001234\",\"Name\":\"Acme Corp\",\"OrgID__c\":null,\"Customer_ISA_ID__c\":\"ACME001\"},\"orderful_org_search\":{\"results\":[]}}",
@@ -18,6 +18,10 @@
18
18
  "description": "Research findings and rationale supporting the plan",
19
19
  "required": ["reasoning", "sourceSummary", "impactSummary"],
20
20
  "properties": {
21
+ "planSummary": {
22
+ "type": "string",
23
+ "description": "One-line plain-language summary of the plan"
24
+ },
21
25
  "reasoning": {
22
26
  "type": "string",
23
27
  "description": "Analysis of what needs to happen and why"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.52.1",
3
+ "version": "0.53.0",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
package/src/bin/droid.ts CHANGED
@@ -21,6 +21,7 @@ import { integrationsSetupSlackCommand, integrationsStatusCommand, slackPostComm
21
21
  import { getVersion } from '../lib/version';
22
22
  import { loadConfig, saveConfig, configExists } from '../lib/config';
23
23
  import { syncNewPlatforms } from '../lib/platforms';
24
+ import { copySkillsCommand } from '../commands/copy-skills';
24
25
 
25
26
  const version = getVersion();
26
27
 
@@ -120,6 +121,14 @@ program
120
121
  .allowUnknownOption()
121
122
  .action(execCommand);
122
123
 
124
+ program
125
+ .command('copy-skills')
126
+ .description('Copy bundled skills to a destination directory')
127
+ .requiredOption('-d, --dest <dir>', 'Destination directory')
128
+ .option('-m, --manifest <file>', 'JSON file with array of skill names')
129
+ .option('-n, --names <names...>', 'Skill names to copy')
130
+ .action(copySkillsCommand);
131
+
123
132
  // Integrations command with subcommands
124
133
  const integrations = program
125
134
  .command('integrations')
@@ -0,0 +1,66 @@
1
+ import chalk from 'chalk';
2
+ import { readFileSync } from 'fs';
3
+ import { copySkillsToDir } from '../lib/skills';
4
+
5
+ interface CopySkillsOptions {
6
+ dest: string;
7
+ manifest?: string;
8
+ names?: string[];
9
+ }
10
+
11
+ /**
12
+ * Copy bundled skills to a destination directory.
13
+ *
14
+ * Usage:
15
+ * droid copy-skills -d dist/assets/skills -m droid-skills.json
16
+ * droid copy-skills -d dist/assets/skills -n propose-plan edi-schema
17
+ */
18
+ export function copySkillsCommand(options: CopySkillsOptions): void {
19
+ const { dest, manifest, names } = options;
20
+
21
+ // Resolve skill names from manifest file or --names flag
22
+ let skillNames: string[];
23
+
24
+ if (manifest) {
25
+ try {
26
+ const content = readFileSync(manifest, 'utf-8');
27
+ skillNames = JSON.parse(content);
28
+ if (!Array.isArray(skillNames)) {
29
+ console.error(
30
+ chalk.red('Manifest must be a JSON array of skill names'),
31
+ );
32
+ process.exit(1);
33
+ }
34
+ } catch (err) {
35
+ console.error(chalk.red(`Failed to read manifest: ${err}`));
36
+ process.exit(1);
37
+ }
38
+ } else if (names?.length) {
39
+ skillNames = names;
40
+ } else {
41
+ console.error(
42
+ chalk.red('Provide either --manifest <file> or --names <names...>'),
43
+ );
44
+ process.exit(1);
45
+ }
46
+
47
+ if (skillNames.length === 0) {
48
+ console.log(chalk.yellow('No skills to copy.'));
49
+ return;
50
+ }
51
+
52
+ const results = copySkillsToDir(skillNames, dest);
53
+
54
+ for (const result of results) {
55
+ if (result.success) {
56
+ console.log(chalk.green(` ✓ ${result.name} → ${result.dest}`));
57
+ } else {
58
+ console.error(chalk.red(` ✗ ${result.name}: ${result.error}`));
59
+ }
60
+ }
61
+
62
+ const failed = results.filter((r) => !r.success);
63
+ if (failed.length > 0) {
64
+ process.exit(1);
65
+ }
66
+ }
package/src/index.ts CHANGED
@@ -2,4 +2,6 @@
2
2
  export * from './lib/types';
3
3
  export * from './lib/config';
4
4
  export * from './lib/skills';
5
+ export * from './lib/tools';
6
+ export * from './lib/pack';
5
7
  export * from './lib/version';
package/src/lib/skills.ts CHANGED
@@ -213,6 +213,69 @@ export function findSkillPath(
213
213
  return null;
214
214
  }
215
215
 
216
+ /**
217
+ * Resolve the absolute path to a bundled skill directory by name.
218
+ * Returns null if the skill is not found.
219
+ *
220
+ * This is the stable public API for consumers who need to locate skill
221
+ * assets (e.g., for copying into a build output). It encapsulates droid's
222
+ * internal `tools/{tool}/skills/{name}/` directory structure.
223
+ */
224
+ export function getSkillPath(skillName: string): string | null {
225
+ const result = findSkillPath(skillName);
226
+ return result?.skillDir ?? null;
227
+ }
228
+
229
+ /**
230
+ * Copy bundled skills to a destination directory.
231
+ * Each skill is copied as `{dest}/{skillName}/` with SKILL.md, references/, etc.
232
+ *
233
+ * @param skillNames - Array of skill names to copy
234
+ * @param dest - Target directory (created if it doesn't exist)
235
+ * @returns Results per skill: success/failure with paths
236
+ */
237
+ export function copySkillsToDir(
238
+ skillNames: string[],
239
+ dest: string,
240
+ ): Array<{ name: string; success: boolean; dest?: string; error?: string }> {
241
+ if (!existsSync(dest)) {
242
+ mkdirSync(dest, { recursive: true });
243
+ }
244
+
245
+ const results: Array<{
246
+ name: string;
247
+ success: boolean;
248
+ dest?: string;
249
+ error?: string;
250
+ }> = [];
251
+
252
+ for (const name of skillNames) {
253
+ const skillDir = getSkillPath(name);
254
+ if (!skillDir) {
255
+ results.push({
256
+ name,
257
+ success: false,
258
+ error: `Skill '${name}' not found in bundled tools`,
259
+ });
260
+ continue;
261
+ }
262
+
263
+ const targetDir = join(dest, name);
264
+ try {
265
+ copyDirectoryRecursive(skillDir, targetDir, () => true);
266
+ results.push({ name, success: true, dest: targetDir });
267
+ } catch (err) {
268
+ results.push({
269
+ name,
270
+ success: false,
271
+ error: `Failed to copy: ${err}`,
272
+ });
273
+ }
274
+ }
275
+
276
+ return results;
277
+ }
278
+
216
279
  /**
217
280
  * Get all bundled skills from all tools
218
281
  */
@@ -32,6 +32,7 @@ Produce a single JSON object matching the schema in `references/output-schema.js
32
32
  "name": "string — short human-readable plan name (optional)",
33
33
  "category": "string — grouping category, e.g. org-provisioning (optional)",
34
34
  "evidence": {
35
+ "planSummary": "string — one-line plain-language summary, e.g. 'Provision org and billing for Acme Corp' (optional)",
35
36
  "reasoning": "string — your analysis of what needs to happen and why",
36
37
  "sourceSummary": "string — concise summary of what you found during research",
37
38
  "sourceRaw": "string — JSON-stringified raw source data from research (optional)",
@@ -53,9 +54,10 @@ Produce a single JSON object matching the schema in `references/output-schema.js
53
54
 
54
55
  ### Evidence Fields
55
56
 
56
- | Field | Purpose | Reviewer Experience |
57
- | --------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
58
- | `reasoning` | Your analysis what needs to happen and why. Cite actual data from your research, not assumptions. | First thing they read. Establishes trust. |
57
+ | Field | Purpose | Reviewer Experience |
58
+ | --------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
59
+ | `planSummary` | Optional. One-line plain-language summary of the plan. Keep it short and specific entity names, not jargon. | Shown in headers and card previews. Quick scan context. |
60
+ | `reasoning` | Your analysis — what needs to happen and why. Cite actual data from your research, not assumptions. | First thing they read. Establishes trust. |
59
61
  | `sourceSummary` | Concise summary of what you found. Include entity names, IDs, and states. | Quick scan to verify you looked at the right things. |
60
62
  | `sourceRaw` | Optional. JSON-stringified raw API responses or data snapshots. | Expandable detail for reviewers who want to verify. |
61
63
  | `impactSummary` | What changes if the plan is approved. Be specific — "creates org X with ISA ID Y" not "creates an org". | The core decision point. Reviewer approves based on this. |
@@ -129,6 +131,7 @@ Say you've been asked to provision an Orderful org for a new customer. You've qu
129
131
  ```json
130
132
  {
131
133
  "evidence": {
134
+ "planSummary": "Provision org, billing, and welcome email for Acme Corp",
132
135
  "reasoning": "Salesforce account Acme Corp (SF-001234) was signed on 2026-03-01 and has no Orderful OrgID. No existing Orderful org matches by name or ISA ID. The account's primary contact is jane@acme.com. ISA ID derived from Customer_ISA_ID__c field: ACME001.",
133
136
  "sourceSummary": "Salesforce: Acme Corp (SF-001234), signed 2026-03-01, no OrgID. Orderful: no org named 'Acme Corp', no ISA ID 'ACME001' in use.",
134
137
  "sourceRaw": "{\"salesforce_account\":{\"Id\":\"SF-001234\",\"Name\":\"Acme Corp\",\"OrgID__c\":null,\"Customer_ISA_ID__c\":\"ACME001\"},\"orderful_org_search\":{\"results\":[]}}",
@@ -18,6 +18,10 @@
18
18
  "description": "Research findings and rationale supporting the plan",
19
19
  "required": ["reasoning", "sourceSummary", "impactSummary"],
20
20
  "properties": {
21
+ "planSummary": {
22
+ "type": "string",
23
+ "description": "One-line plain-language summary of the plan"
24
+ },
21
25
  "reasoning": {
22
26
  "type": "string",
23
27
  "description": "Analysis of what needs to happen and why"