@orderful/droid 0.37.0 → 0.39.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/.claude-plugin/marketplace.json +1 -118
- package/.claude-plugin/plugin.json +51 -0
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +53 -0
- package/README.md +70 -39
- package/dist/bin/droid.js +658 -212
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/tui/components/PlatformBadges.d.ts.map +1 -1
- package/dist/commands/tui/components/SettingsDetails.d.ts.map +1 -1
- package/dist/commands/tui/hooks/useAppUpdate.d.ts.map +1 -1
- package/dist/commands/tui/views/SetupScreen.d.ts.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/index.js +345 -186
- package/dist/lib/agents.d.ts +4 -2
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/migrations.d.ts.map +1 -1
- package/dist/lib/platform.codex.d.ts +36 -0
- package/dist/lib/platform.codex.d.ts.map +1 -0
- package/dist/lib/platforms.d.ts +30 -24
- package/dist/lib/platforms.d.ts.map +1 -1
- package/dist/lib/secrets.d.ts +7 -0
- package/dist/lib/secrets.d.ts.map +1 -0
- package/dist/lib/skills.d.ts +4 -2
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/types.d.ts +2 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tools/brain/.claude-plugin/plugin.json +8 -1
- package/dist/tools/brain/TOOL.yaml +1 -1
- package/dist/tools/brain/skills/brain/SKILL.md +6 -3
- package/dist/tools/brain/skills/brain/references/workflows.md +9 -5
- package/dist/tools/brain/skills/brain-obsidian/SKILL.md +2 -0
- package/dist/tools/coach/.claude-plugin/plugin.json +6 -0
- package/dist/tools/coach/skills/coach/SKILL.md +3 -0
- package/dist/tools/code-review/.claude-plugin/plugin.json +12 -0
- package/dist/tools/code-review/skills/code-review/SKILL.md +2 -0
- package/dist/tools/codex/.claude-plugin/plugin.json +9 -0
- package/dist/tools/codex/skills/codex/SKILL.md +3 -0
- package/dist/tools/comments/.claude-plugin/plugin.json +6 -0
- package/dist/tools/comments/skills/comments/SKILL.md +5 -0
- package/dist/tools/droid/.claude-plugin/plugin.json +8 -1
- package/dist/tools/droid/TOOL.yaml +4 -2
- package/dist/tools/droid/commands/setup.md +125 -0
- package/dist/tools/droid/skills/droid/SKILL.md +117 -2
- package/dist/tools/plan/.claude-plugin/plugin.json +6 -0
- package/dist/tools/plan/skills/plan/SKILL.md +2 -0
- package/dist/tools/project/.claude-plugin/plugin.json +6 -0
- package/dist/tools/project/skills/project/SKILL.md +3 -0
- package/dist/tools/status-update/.claude-plugin/plugin.json +22 -0
- package/dist/tools/status-update/TOOL.yaml +21 -0
- package/dist/tools/status-update/commands/status-update.md +27 -0
- package/dist/tools/status-update/skills/status-update/SKILL.md +253 -0
- package/dist/tools/status-update/skills/status-update/references/formatting.md +203 -0
- package/dist/tools/tech-design/.claude-plugin/plugin.json +7 -1
- package/dist/tools/tech-design/TOOL.yaml +1 -1
- package/dist/tools/tech-design/commands/tech-design.md +2 -0
- package/dist/tools/tech-design/skills/tech-design/SKILL.md +39 -9
- package/dist/tools/tech-design/skills/tech-design/references/publish.md +272 -216
- package/dist/tools/tech-design/skills/tech-design/references/start.md +50 -20
- package/dist/tools/wrapup/.claude-plugin/plugin.json +6 -0
- package/dist/tools/wrapup/skills/wrapup/SKILL.md +2 -0
- package/package.json +1 -1
- package/scripts/build-plugins.ts +154 -6
- package/src/bin/droid.ts +35 -0
- package/src/commands/auth.ts +150 -0
- package/src/commands/setup.ts +107 -2
- package/src/commands/tui/components/PlatformBadges.tsx +1 -0
- package/src/commands/tui/components/SettingsDetails.tsx +1 -0
- package/src/commands/tui/hooks/useAppUpdate.ts +21 -1
- package/src/commands/tui/views/SetupScreen.tsx +10 -1
- package/src/commands/update.ts +21 -1
- package/src/lib/agents.ts +13 -2
- package/src/lib/migrations.ts +81 -9
- package/src/lib/platform.codex.ts +131 -0
- package/src/lib/platforms.ts +127 -6
- package/src/lib/secrets.ts +12 -0
- package/src/lib/skills.ts +53 -6
- package/src/lib/types.ts +1 -0
- package/src/tools/brain/.claude-plugin/plugin.json +8 -1
- package/src/tools/brain/TOOL.yaml +1 -1
- package/src/tools/brain/skills/brain/SKILL.md +6 -3
- package/src/tools/brain/skills/brain/references/workflows.md +9 -5
- package/src/tools/brain/skills/brain-obsidian/SKILL.md +2 -0
- package/src/tools/coach/.claude-plugin/plugin.json +6 -0
- package/src/tools/coach/skills/coach/SKILL.md +3 -0
- package/src/tools/code-review/.claude-plugin/plugin.json +12 -0
- package/src/tools/code-review/skills/code-review/SKILL.md +2 -0
- package/src/tools/codex/.claude-plugin/plugin.json +9 -0
- package/src/tools/codex/skills/codex/SKILL.md +3 -0
- package/src/tools/comments/.claude-plugin/plugin.json +6 -0
- package/src/tools/comments/skills/comments/SKILL.md +5 -0
- package/src/tools/droid/.claude-plugin/plugin.json +8 -1
- package/src/tools/droid/TOOL.yaml +4 -2
- package/src/tools/droid/commands/setup.md +125 -0
- package/src/tools/droid/skills/droid/SKILL.md +117 -2
- package/src/tools/plan/.claude-plugin/plugin.json +6 -0
- package/src/tools/plan/skills/plan/SKILL.md +2 -0
- package/src/tools/project/.claude-plugin/plugin.json +6 -0
- package/src/tools/project/skills/project/SKILL.md +3 -0
- package/src/tools/status-update/.claude-plugin/plugin.json +22 -0
- package/src/tools/status-update/TOOL.yaml +21 -0
- package/src/tools/status-update/commands/status-update.md +27 -0
- package/src/tools/status-update/skills/status-update/SKILL.md +253 -0
- package/src/tools/status-update/skills/status-update/references/formatting.md +203 -0
- package/src/tools/tech-design/.claude-plugin/plugin.json +7 -1
- package/src/tools/tech-design/TOOL.yaml +1 -1
- package/src/tools/tech-design/commands/tech-design.md +2 -0
- package/src/tools/tech-design/skills/tech-design/SKILL.md +39 -9
- package/src/tools/tech-design/skills/tech-design/references/publish.md +272 -216
- package/src/tools/tech-design/skills/tech-design/references/start.md +50 -20
- package/src/tools/wrapup/.claude-plugin/plugin.json +6 -0
- package/src/tools/wrapup/skills/wrapup/SKILL.md +2 -0
package/dist/bin/droid.js
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
// src/bin/droid.ts
|
|
4
4
|
import { program } from "commander";
|
|
5
|
+
import chalk12 from "chalk";
|
|
5
6
|
|
|
6
7
|
// src/commands/setup.ts
|
|
7
8
|
import inquirer from "inquirer";
|
|
8
9
|
import chalk2 from "chalk";
|
|
9
10
|
import { execSync as execSync3 } from "child_process";
|
|
10
|
-
import { existsSync as
|
|
11
|
-
import { join as
|
|
12
|
-
import { homedir as
|
|
11
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "fs";
|
|
12
|
+
import { join as join9 } from "path";
|
|
13
|
+
import { homedir as homedir5 } from "os";
|
|
13
14
|
|
|
14
15
|
// src/lib/config.ts
|
|
15
16
|
import {
|
|
@@ -297,49 +298,124 @@ function removeRepo(name) {
|
|
|
297
298
|
|
|
298
299
|
// src/lib/skills.ts
|
|
299
300
|
import {
|
|
300
|
-
existsSync as
|
|
301
|
-
readdirSync as
|
|
301
|
+
existsSync as existsSync7,
|
|
302
|
+
readdirSync as readdirSync6,
|
|
302
303
|
readFileSync as readFileSync6,
|
|
303
|
-
mkdirSync as
|
|
304
|
+
mkdirSync as mkdirSync5,
|
|
304
305
|
writeFileSync as writeFileSync4,
|
|
305
|
-
rmSync as
|
|
306
|
+
rmSync as rmSync3
|
|
306
307
|
} from "fs";
|
|
307
|
-
import { join as
|
|
308
|
+
import { join as join8, dirname as dirname6, basename } from "path";
|
|
308
309
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
309
310
|
import YAML4 from "yaml";
|
|
310
311
|
|
|
311
312
|
// src/lib/agents.ts
|
|
312
|
-
import { existsSync as
|
|
313
|
-
import { join as
|
|
313
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync3 } from "fs";
|
|
314
|
+
import { join as join6, dirname as dirname4 } from "path";
|
|
314
315
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
315
316
|
import YAML3 from "yaml";
|
|
316
317
|
|
|
317
318
|
// src/lib/platforms.ts
|
|
318
|
-
import { join as
|
|
319
|
-
import { homedir as
|
|
319
|
+
import { join as join3 } from "path";
|
|
320
|
+
import { homedir as homedir3 } from "os";
|
|
320
321
|
import { execSync } from "child_process";
|
|
321
|
-
import { existsSync as
|
|
322
|
+
import { existsSync as existsSync3 } from "fs";
|
|
323
|
+
|
|
324
|
+
// src/lib/platform.codex.ts
|
|
325
|
+
import { join as join2, dirname } from "path";
|
|
326
|
+
import { homedir as homedir2 } from "os";
|
|
327
|
+
import {
|
|
328
|
+
existsSync as existsSync2,
|
|
329
|
+
mkdirSync as mkdirSync2,
|
|
330
|
+
rmSync,
|
|
331
|
+
symlinkSync,
|
|
332
|
+
lstatSync,
|
|
333
|
+
readdirSync as readdirSync2,
|
|
334
|
+
readlinkSync
|
|
335
|
+
} from "fs";
|
|
322
336
|
var UNIFIED_SKILLS_PATH = join2(homedir2(), ".claude", "skills");
|
|
337
|
+
var CODEX_SKILLS_PATH = join2(homedir2(), ".codex", "skills");
|
|
338
|
+
function createCodexSymlink(skillName) {
|
|
339
|
+
const source = join2(UNIFIED_SKILLS_PATH, skillName);
|
|
340
|
+
const target = join2(CODEX_SKILLS_PATH, skillName);
|
|
341
|
+
if (!existsSync2(source)) {
|
|
342
|
+
console.warn(`Warning: Cannot create Codex symlink - source skill not found: ${source}`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (!existsSync2(dirname(target))) {
|
|
346
|
+
mkdirSync2(dirname(target), { recursive: true });
|
|
347
|
+
}
|
|
348
|
+
if (existsSync2(target)) {
|
|
349
|
+
try {
|
|
350
|
+
const stat = lstatSync(target);
|
|
351
|
+
if (stat.isSymbolicLink()) {
|
|
352
|
+
const currentTarget = readlinkSync(target);
|
|
353
|
+
if (currentTarget === source) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
rmSync(target);
|
|
357
|
+
} else {
|
|
358
|
+
console.warn(`Warning: ${target} exists and is not a symlink - skipping to preserve user content`);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
} catch (error) {
|
|
362
|
+
console.warn(`Warning: Could not check ${target}: ${error}`);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
symlinkSync(source, target);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.warn(`Warning: Could not create Codex symlink ${target} \u2192 ${source}: ${error}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function removeCodexSymlink(skillName) {
|
|
373
|
+
const target = join2(CODEX_SKILLS_PATH, skillName);
|
|
374
|
+
if (!existsSync2(target)) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
try {
|
|
378
|
+
const stat = lstatSync(target);
|
|
379
|
+
if (stat.isSymbolicLink()) {
|
|
380
|
+
rmSync(target);
|
|
381
|
+
}
|
|
382
|
+
} catch (error) {
|
|
383
|
+
console.warn(`Warning: Could not remove Codex symlink ${target}: ${error}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// src/lib/platforms.ts
|
|
388
|
+
var UNIFIED_SKILLS_PATH2 = join3(homedir3(), ".claude", "skills");
|
|
323
389
|
var PLATFORM_PATHS = {
|
|
324
390
|
["claude-code" /* ClaudeCode */]: {
|
|
325
|
-
skills:
|
|
326
|
-
commands:
|
|
327
|
-
agents:
|
|
328
|
-
config:
|
|
391
|
+
skills: UNIFIED_SKILLS_PATH2,
|
|
392
|
+
commands: join3(homedir3(), ".claude", "commands"),
|
|
393
|
+
agents: join3(homedir3(), ".claude", "agents"),
|
|
394
|
+
config: join3(homedir3(), ".claude", "CLAUDE.md")
|
|
329
395
|
},
|
|
330
396
|
["opencode" /* OpenCode */]: {
|
|
331
|
-
skills:
|
|
332
|
-
commands:
|
|
333
|
-
agents:
|
|
334
|
-
config:
|
|
397
|
+
skills: UNIFIED_SKILLS_PATH2,
|
|
398
|
+
commands: join3(homedir3(), ".config", "opencode", "command"),
|
|
399
|
+
agents: join3(homedir3(), ".config", "opencode", "agent"),
|
|
400
|
+
config: join3(homedir3(), ".config", "opencode", "AGENTS.md")
|
|
335
401
|
},
|
|
336
402
|
["cursor" /* Cursor */]: {
|
|
337
|
-
skills:
|
|
403
|
+
skills: UNIFIED_SKILLS_PATH2,
|
|
338
404
|
// Cursor doesn't have a documented global commands path - commands are discovered from projects
|
|
339
405
|
// We use this path as a placeholder; commands may not work the same as Claude Code/OpenCode
|
|
340
|
-
commands:
|
|
341
|
-
agents:
|
|
342
|
-
config:
|
|
406
|
+
commands: join3(homedir3(), ".cursor", "commands"),
|
|
407
|
+
agents: join3(homedir3(), ".cursor", "agents"),
|
|
408
|
+
config: join3(homedir3(), ".cursor", "rules")
|
|
409
|
+
},
|
|
410
|
+
["openai-codex" /* OpenAICodex */]: {
|
|
411
|
+
// Codex reads from ~/.codex/skills/ but we install to unified path and symlink
|
|
412
|
+
skills: UNIFIED_SKILLS_PATH2,
|
|
413
|
+
commands: null,
|
|
414
|
+
// Not supported - Codex has built-in commands only
|
|
415
|
+
agents: null,
|
|
416
|
+
// Not supported - Codex is single-agent
|
|
417
|
+
config: null
|
|
418
|
+
// No config file integration
|
|
343
419
|
}
|
|
344
420
|
};
|
|
345
421
|
function getSkillsPath(platform) {
|
|
@@ -361,8 +437,8 @@ function detectAllPlatforms() {
|
|
|
361
437
|
detected.push("claude-code" /* ClaudeCode */);
|
|
362
438
|
} catch {
|
|
363
439
|
}
|
|
364
|
-
const cursorDir =
|
|
365
|
-
if (
|
|
440
|
+
const cursorDir = join3(homedir3(), ".cursor");
|
|
441
|
+
if (existsSync3(cursorDir)) {
|
|
366
442
|
detected.push("cursor" /* Cursor */);
|
|
367
443
|
}
|
|
368
444
|
try {
|
|
@@ -370,6 +446,10 @@ function detectAllPlatforms() {
|
|
|
370
446
|
detected.push("opencode" /* OpenCode */);
|
|
371
447
|
} catch {
|
|
372
448
|
}
|
|
449
|
+
const codexDir = join3(homedir3(), ".codex");
|
|
450
|
+
if (existsSync3(codexDir)) {
|
|
451
|
+
detected.push("openai-codex" /* OpenAICodex */);
|
|
452
|
+
}
|
|
373
453
|
return detected;
|
|
374
454
|
}
|
|
375
455
|
function getActivePlatforms(config) {
|
|
@@ -377,21 +457,56 @@ function getActivePlatforms(config) {
|
|
|
377
457
|
const ignored = config.ignored_platforms ?? [];
|
|
378
458
|
return detected.filter((p) => !ignored.includes(p));
|
|
379
459
|
}
|
|
460
|
+
function syncNewPlatforms(config) {
|
|
461
|
+
const detected = detectAllPlatforms();
|
|
462
|
+
const ignored = config.ignored_platforms ?? [];
|
|
463
|
+
const active = detected.filter((p) => !ignored.includes(p));
|
|
464
|
+
const initializedPlatforms = Object.keys(config.platforms ?? {});
|
|
465
|
+
const newPlatforms = active.filter((p) => !initializedPlatforms.includes(p));
|
|
466
|
+
if (newPlatforms.length === 0) {
|
|
467
|
+
return [];
|
|
468
|
+
}
|
|
469
|
+
const primaryTools = config.platforms?.["claude-code"]?.tools ?? {};
|
|
470
|
+
for (const platform of newPlatforms) {
|
|
471
|
+
initializePlatformTools(platform, primaryTools, config);
|
|
472
|
+
}
|
|
473
|
+
return newPlatforms;
|
|
474
|
+
}
|
|
475
|
+
function initializePlatformTools(platform, primaryTools, config) {
|
|
476
|
+
if (platform === "openai-codex" /* OpenAICodex */) {
|
|
477
|
+
for (const skillName of Object.keys(primaryTools)) {
|
|
478
|
+
createCodexSymlink(skillName);
|
|
479
|
+
}
|
|
480
|
+
if (!config.platforms) {
|
|
481
|
+
config.platforms = {};
|
|
482
|
+
}
|
|
483
|
+
config.platforms[platform] = {
|
|
484
|
+
tools: { ...primaryTools }
|
|
485
|
+
};
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (!config.platforms) {
|
|
489
|
+
config.platforms = {};
|
|
490
|
+
}
|
|
491
|
+
config.platforms[platform] = {
|
|
492
|
+
tools: { ...primaryTools }
|
|
493
|
+
};
|
|
494
|
+
}
|
|
380
495
|
|
|
381
496
|
// src/lib/tools.ts
|
|
382
|
-
import { existsSync as
|
|
383
|
-
import { join as
|
|
497
|
+
import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync3 } from "fs";
|
|
498
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
384
499
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
385
500
|
import YAML2 from "yaml";
|
|
386
501
|
|
|
387
502
|
// src/lib/version.ts
|
|
388
503
|
import { readFileSync as readFileSync2 } from "fs";
|
|
389
504
|
import { fileURLToPath } from "url";
|
|
390
|
-
import { dirname, join as
|
|
505
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
391
506
|
import { execSync as execSync2 } from "child_process";
|
|
392
507
|
import chalk from "chalk";
|
|
393
|
-
var __dirname =
|
|
394
|
-
var packageJsonPath =
|
|
508
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
509
|
+
var packageJsonPath = join4(__dirname, "../../package.json");
|
|
395
510
|
function getVersion() {
|
|
396
511
|
try {
|
|
397
512
|
const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
@@ -445,14 +560,14 @@ function runUpdate() {
|
|
|
445
560
|
}
|
|
446
561
|
|
|
447
562
|
// src/lib/tools.ts
|
|
448
|
-
var __dirname2 =
|
|
449
|
-
var BUNDLED_TOOLS_DIR =
|
|
563
|
+
var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
|
|
564
|
+
var BUNDLED_TOOLS_DIR = join5(__dirname2, "../tools");
|
|
450
565
|
function getBundledToolsDir() {
|
|
451
566
|
return BUNDLED_TOOLS_DIR;
|
|
452
567
|
}
|
|
453
568
|
function loadToolManifest(toolDir) {
|
|
454
|
-
const manifestPath =
|
|
455
|
-
if (!
|
|
569
|
+
const manifestPath = join5(toolDir, "TOOL.yaml");
|
|
570
|
+
if (!existsSync4(manifestPath)) {
|
|
456
571
|
return null;
|
|
457
572
|
}
|
|
458
573
|
try {
|
|
@@ -472,13 +587,13 @@ function loadToolManifest(toolDir) {
|
|
|
472
587
|
}
|
|
473
588
|
}
|
|
474
589
|
function getBundledTools() {
|
|
475
|
-
if (!
|
|
590
|
+
if (!existsSync4(BUNDLED_TOOLS_DIR)) {
|
|
476
591
|
return [];
|
|
477
592
|
}
|
|
478
|
-
const toolDirs =
|
|
593
|
+
const toolDirs = readdirSync3(BUNDLED_TOOLS_DIR, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
479
594
|
const tools = [];
|
|
480
595
|
for (const toolName of toolDirs) {
|
|
481
|
-
const manifest = loadToolManifest(
|
|
596
|
+
const manifest = loadToolManifest(join5(BUNDLED_TOOLS_DIR, toolName));
|
|
482
597
|
if (manifest) {
|
|
483
598
|
tools.push(manifest);
|
|
484
599
|
}
|
|
@@ -547,8 +662,8 @@ function getToolsWithUpdates() {
|
|
|
547
662
|
}
|
|
548
663
|
|
|
549
664
|
// src/lib/agents.ts
|
|
550
|
-
var __dirname3 =
|
|
551
|
-
var BUNDLED_TOOLS_DIR2 =
|
|
665
|
+
var __dirname3 = dirname4(fileURLToPath3(import.meta.url));
|
|
666
|
+
var BUNDLED_TOOLS_DIR2 = join6(__dirname3, "../tools");
|
|
552
667
|
function getAgentsInstallPath(platform) {
|
|
553
668
|
return getAgentsPath(platform);
|
|
554
669
|
}
|
|
@@ -569,7 +684,7 @@ function parseAgentFrontmatter(content) {
|
|
|
569
684
|
}
|
|
570
685
|
}
|
|
571
686
|
function loadAgentManifest(agentPath) {
|
|
572
|
-
if (!
|
|
687
|
+
if (!existsSync5(agentPath)) {
|
|
573
688
|
return null;
|
|
574
689
|
}
|
|
575
690
|
const content = readFileSync4(agentPath, "utf-8");
|
|
@@ -577,7 +692,7 @@ function loadAgentManifest(agentPath) {
|
|
|
577
692
|
if (!frontmatter || !frontmatter.name) {
|
|
578
693
|
return null;
|
|
579
694
|
}
|
|
580
|
-
const toolDir =
|
|
695
|
+
const toolDir = dirname4(dirname4(agentPath));
|
|
581
696
|
const toolManifest = loadToolManifest(toolDir);
|
|
582
697
|
return {
|
|
583
698
|
name: frontmatter.name,
|
|
@@ -597,8 +712,9 @@ function getInstalledAgentsDir() {
|
|
|
597
712
|
function isAgentInstalled(agentName) {
|
|
598
713
|
const config = loadConfig();
|
|
599
714
|
const agentsDir = getAgentsInstallPath(config.platform);
|
|
600
|
-
|
|
601
|
-
|
|
715
|
+
if (!agentsDir) return false;
|
|
716
|
+
const agentPath = join6(agentsDir, `${agentName}.md`);
|
|
717
|
+
return existsSync5(agentPath);
|
|
602
718
|
}
|
|
603
719
|
function generateClaudeCodeAgent(manifest, agentContent) {
|
|
604
720
|
const lines = [
|
|
@@ -644,10 +760,13 @@ function installAgentFromPath(agentPath, agentName, platform) {
|
|
|
644
760
|
const agentContent = frontmatterMatch ? rawContent.slice(frontmatterMatch[0].length) : rawContent;
|
|
645
761
|
const installedContent = targetPlatform === "claude-code" /* ClaudeCode */ ? generateClaudeCodeAgent(manifest, agentContent) : generateOpenCodeAgent(manifest, agentContent);
|
|
646
762
|
const agentsDir = getAgentsInstallPath(targetPlatform);
|
|
647
|
-
if (!
|
|
648
|
-
|
|
763
|
+
if (!agentsDir) {
|
|
764
|
+
return { success: false, message: `Platform ${targetPlatform} does not support agents` };
|
|
765
|
+
}
|
|
766
|
+
if (!existsSync5(agentsDir)) {
|
|
767
|
+
mkdirSync3(agentsDir, { recursive: true });
|
|
649
768
|
}
|
|
650
|
-
const outputPath =
|
|
769
|
+
const outputPath = join6(agentsDir, `${agentName}.md`);
|
|
651
770
|
writeFileSync2(outputPath, installedContent);
|
|
652
771
|
const targetDir = targetPlatform === "claude-code" /* ClaudeCode */ ? "~/.claude/agents/" : targetPlatform === "cursor" /* Cursor */ ? "~/.cursor/agents/" : "~/.config/opencode/agent/";
|
|
653
772
|
return { success: true, message: `Installed ${agentName} to ${targetDir}` };
|
|
@@ -659,8 +778,11 @@ function uninstallAgent(agentName, platform) {
|
|
|
659
778
|
const config = loadConfig();
|
|
660
779
|
const targetPlatform = platform ?? config.platform;
|
|
661
780
|
const agentsDir = getAgentsInstallPath(targetPlatform);
|
|
662
|
-
|
|
663
|
-
|
|
781
|
+
if (!agentsDir) {
|
|
782
|
+
return { success: true, message: `Platform ${targetPlatform} does not support agents` };
|
|
783
|
+
}
|
|
784
|
+
const agentPath = join6(agentsDir, `${agentName}.md`);
|
|
785
|
+
if (!existsSync5(agentPath)) {
|
|
664
786
|
return { success: false, message: `Agent not installed: ${agentName}` };
|
|
665
787
|
}
|
|
666
788
|
try {
|
|
@@ -673,20 +795,20 @@ function uninstallAgent(agentName, platform) {
|
|
|
673
795
|
|
|
674
796
|
// src/lib/migrations.ts
|
|
675
797
|
import {
|
|
676
|
-
existsSync as
|
|
798
|
+
existsSync as existsSync6,
|
|
677
799
|
appendFileSync,
|
|
678
|
-
mkdirSync as
|
|
800
|
+
mkdirSync as mkdirSync4,
|
|
679
801
|
renameSync,
|
|
680
|
-
rmSync,
|
|
681
|
-
readdirSync as
|
|
802
|
+
rmSync as rmSync2,
|
|
803
|
+
readdirSync as readdirSync5,
|
|
682
804
|
readFileSync as readFileSync5,
|
|
683
805
|
writeFileSync as writeFileSync3
|
|
684
806
|
} from "fs";
|
|
685
|
-
import { join as
|
|
686
|
-
import { homedir as
|
|
807
|
+
import { join as join7, dirname as dirname5 } from "path";
|
|
808
|
+
import { homedir as homedir4 } from "os";
|
|
687
809
|
var MIGRATIONS_LOG_FILE = ".migrations.log";
|
|
688
810
|
function getMigrationsLogPath() {
|
|
689
|
-
return
|
|
811
|
+
return join7(getConfigDir(), MIGRATIONS_LOG_FILE);
|
|
690
812
|
}
|
|
691
813
|
function logMigration(toolName, fromVersion, toVersion, status, error) {
|
|
692
814
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -694,9 +816,9 @@ function logMigration(toolName, fromVersion, toVersion, status, error) {
|
|
|
694
816
|
` : `${timestamp} ${toolName} ${fromVersion} \u2192 ${toVersion} ${status}
|
|
695
817
|
`;
|
|
696
818
|
const logPath = getMigrationsLogPath();
|
|
697
|
-
const logDir =
|
|
698
|
-
if (!
|
|
699
|
-
|
|
819
|
+
const logDir = dirname5(logPath);
|
|
820
|
+
if (!existsSync6(logDir)) {
|
|
821
|
+
mkdirSync4(logDir, { recursive: true });
|
|
700
822
|
}
|
|
701
823
|
appendFileSync(logPath, logEntry);
|
|
702
824
|
}
|
|
@@ -706,16 +828,16 @@ function createConfigDirMigration(skillName, version2) {
|
|
|
706
828
|
version: version2,
|
|
707
829
|
description: `Move ${skillName} config to unprefixed location`,
|
|
708
830
|
up: (configDir) => {
|
|
709
|
-
const oldDir =
|
|
710
|
-
const newDir =
|
|
711
|
-
if (
|
|
712
|
-
const parentDir =
|
|
713
|
-
if (!
|
|
714
|
-
|
|
831
|
+
const oldDir = join7(configDir, "skills", skillName);
|
|
832
|
+
const newDir = join7(configDir, "skills", unprefixedName);
|
|
833
|
+
if (existsSync6(oldDir) && !existsSync6(newDir)) {
|
|
834
|
+
const parentDir = dirname5(newDir);
|
|
835
|
+
if (!existsSync6(parentDir)) {
|
|
836
|
+
mkdirSync4(parentDir, { recursive: true });
|
|
715
837
|
}
|
|
716
838
|
renameSync(oldDir, newDir);
|
|
717
|
-
} else if (
|
|
718
|
-
|
|
839
|
+
} else if (existsSync6(oldDir) && existsSync6(newDir)) {
|
|
840
|
+
rmSync2(oldDir, { recursive: true });
|
|
719
841
|
}
|
|
720
842
|
}
|
|
721
843
|
};
|
|
@@ -731,10 +853,10 @@ function createPlatformSyncMigration(version2) {
|
|
|
731
853
|
const originalPlatform = config.platform;
|
|
732
854
|
for (const platformKey of ["claude-code" /* ClaudeCode */, "opencode" /* OpenCode */, "cursor" /* Cursor */]) {
|
|
733
855
|
const skillsPath = getSkillsPath(platformKey);
|
|
734
|
-
if (!
|
|
856
|
+
if (!existsSync6(skillsPath)) continue;
|
|
735
857
|
config.platform = platformKey;
|
|
736
858
|
const trackedTools = getPlatformTools(config);
|
|
737
|
-
const installedDirs =
|
|
859
|
+
const installedDirs = readdirSync5(skillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
738
860
|
for (const skillName of installedDirs) {
|
|
739
861
|
const normalizedName = skillName.replace(/^droid-/, "");
|
|
740
862
|
const isTracked = trackedTools[skillName] || trackedTools[`droid-${normalizedName}`] || trackedTools[normalizedName];
|
|
@@ -807,12 +929,12 @@ function createOpenCodeSkillsPathMigration(version2) {
|
|
|
807
929
|
if (config.platform !== "opencode" /* OpenCode */) {
|
|
808
930
|
return;
|
|
809
931
|
}
|
|
810
|
-
const oldSkillsPath =
|
|
932
|
+
const oldSkillsPath = join7(getSkillsPath("opencode" /* OpenCode */), "..", "skills");
|
|
811
933
|
const newSkillsPath = getSkillsPath("opencode" /* OpenCode */);
|
|
812
|
-
if (!
|
|
934
|
+
if (!existsSync6(oldSkillsPath)) {
|
|
813
935
|
return;
|
|
814
936
|
}
|
|
815
|
-
if (!
|
|
937
|
+
if (!existsSync6(newSkillsPath)) {
|
|
816
938
|
try {
|
|
817
939
|
renameSync(oldSkillsPath, newSkillsPath);
|
|
818
940
|
} catch (error) {
|
|
@@ -822,11 +944,11 @@ function createOpenCodeSkillsPathMigration(version2) {
|
|
|
822
944
|
}
|
|
823
945
|
return;
|
|
824
946
|
}
|
|
825
|
-
const skillDirs =
|
|
947
|
+
const skillDirs = readdirSync5(oldSkillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
826
948
|
for (const skillName of skillDirs) {
|
|
827
|
-
const oldSkillDir =
|
|
828
|
-
const newSkillDir =
|
|
829
|
-
if (!
|
|
949
|
+
const oldSkillDir = join7(oldSkillsPath, skillName);
|
|
950
|
+
const newSkillDir = join7(newSkillsPath, skillName);
|
|
951
|
+
if (!existsSync6(newSkillDir)) {
|
|
830
952
|
try {
|
|
831
953
|
renameSync(oldSkillDir, newSkillDir);
|
|
832
954
|
} catch (error) {
|
|
@@ -837,9 +959,9 @@ function createOpenCodeSkillsPathMigration(version2) {
|
|
|
837
959
|
}
|
|
838
960
|
}
|
|
839
961
|
try {
|
|
840
|
-
const remaining =
|
|
962
|
+
const remaining = readdirSync5(oldSkillsPath);
|
|
841
963
|
if (remaining.length === 0) {
|
|
842
|
-
|
|
964
|
+
rmSync2(oldSkillsPath, { recursive: true });
|
|
843
965
|
}
|
|
844
966
|
} catch (error) {
|
|
845
967
|
console.warn(
|
|
@@ -855,25 +977,25 @@ function createClaudeCodeCommandCleanupMigration(version2) {
|
|
|
855
977
|
description: "Remove non-alias commands from Claude Code",
|
|
856
978
|
up: () => {
|
|
857
979
|
const commandsPath = getCommandsPath("claude-code" /* ClaudeCode */);
|
|
858
|
-
if (!
|
|
980
|
+
if (!commandsPath || !existsSync6(commandsPath)) {
|
|
859
981
|
return;
|
|
860
982
|
}
|
|
861
983
|
const bundledTools = getBundledTools();
|
|
862
|
-
const
|
|
984
|
+
const deletableDroidCommands = /* @__PURE__ */ new Set();
|
|
863
985
|
for (const tool of bundledTools) {
|
|
864
986
|
for (const cmd of tool.includes.commands) {
|
|
865
|
-
if (
|
|
866
|
-
|
|
987
|
+
if (!cmd.is_alias) {
|
|
988
|
+
deletableDroidCommands.add(cmd.name);
|
|
867
989
|
}
|
|
868
990
|
}
|
|
869
991
|
}
|
|
870
|
-
const commandFiles =
|
|
992
|
+
const commandFiles = readdirSync5(commandsPath, { withFileTypes: true }).filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md")).map((dirent) => dirent.name);
|
|
871
993
|
for (const file of commandFiles) {
|
|
872
994
|
const commandName = file.replace(".md", "");
|
|
873
|
-
if (
|
|
874
|
-
const commandFilePath =
|
|
995
|
+
if (deletableDroidCommands.has(commandName)) {
|
|
996
|
+
const commandFilePath = join7(commandsPath, file);
|
|
875
997
|
try {
|
|
876
|
-
|
|
998
|
+
rmSync2(commandFilePath);
|
|
877
999
|
} catch (error) {
|
|
878
1000
|
console.warn(
|
|
879
1001
|
`Warning: Could not remove command ${commandFilePath}: ${error}`
|
|
@@ -889,13 +1011,13 @@ function createOpenCodePluginCleanupMigration(version2) {
|
|
|
889
1011
|
version: version2,
|
|
890
1012
|
description: "Remove opencode-skills plugin from opencode.json",
|
|
891
1013
|
up: () => {
|
|
892
|
-
const opencodeConfigPath =
|
|
893
|
-
|
|
1014
|
+
const opencodeConfigPath = join7(
|
|
1015
|
+
homedir4(),
|
|
894
1016
|
".config",
|
|
895
1017
|
"opencode",
|
|
896
1018
|
"opencode.json"
|
|
897
1019
|
);
|
|
898
|
-
if (!
|
|
1020
|
+
if (!existsSync6(opencodeConfigPath)) {
|
|
899
1021
|
return;
|
|
900
1022
|
}
|
|
901
1023
|
let config;
|
|
@@ -932,24 +1054,24 @@ function createUnifiedSkillsPathMigration(version2) {
|
|
|
932
1054
|
version: version2,
|
|
933
1055
|
description: "Copy OpenCode skills to unified ~/.claude/skills/ location",
|
|
934
1056
|
up: () => {
|
|
935
|
-
const oldOpenCodeSkillsPath =
|
|
936
|
-
|
|
1057
|
+
const oldOpenCodeSkillsPath = join7(
|
|
1058
|
+
homedir4(),
|
|
937
1059
|
".config",
|
|
938
1060
|
"opencode",
|
|
939
1061
|
"skill"
|
|
940
1062
|
);
|
|
941
|
-
const unifiedSkillsPath =
|
|
942
|
-
if (!
|
|
1063
|
+
const unifiedSkillsPath = join7(homedir4(), ".claude", "skills");
|
|
1064
|
+
if (!existsSync6(oldOpenCodeSkillsPath)) {
|
|
943
1065
|
return;
|
|
944
1066
|
}
|
|
945
|
-
if (!
|
|
946
|
-
|
|
1067
|
+
if (!existsSync6(unifiedSkillsPath)) {
|
|
1068
|
+
mkdirSync4(unifiedSkillsPath, { recursive: true });
|
|
947
1069
|
}
|
|
948
|
-
const skillDirs =
|
|
1070
|
+
const skillDirs = readdirSync5(oldOpenCodeSkillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
949
1071
|
for (const skillName of skillDirs) {
|
|
950
|
-
const sourcePath =
|
|
951
|
-
const destPath =
|
|
952
|
-
if (
|
|
1072
|
+
const sourcePath = join7(oldOpenCodeSkillsPath, skillName);
|
|
1073
|
+
const destPath = join7(unifiedSkillsPath, skillName);
|
|
1074
|
+
if (existsSync6(destPath)) {
|
|
953
1075
|
continue;
|
|
954
1076
|
}
|
|
955
1077
|
try {
|
|
@@ -964,11 +1086,11 @@ function createUnifiedSkillsPathMigration(version2) {
|
|
|
964
1086
|
};
|
|
965
1087
|
}
|
|
966
1088
|
function copyDirRecursive(src, dest) {
|
|
967
|
-
|
|
968
|
-
const entries =
|
|
1089
|
+
mkdirSync4(dest, { recursive: true });
|
|
1090
|
+
const entries = readdirSync5(src, { withFileTypes: true });
|
|
969
1091
|
for (const entry of entries) {
|
|
970
|
-
const srcPath =
|
|
971
|
-
const destPath =
|
|
1092
|
+
const srcPath = join7(src, entry.name);
|
|
1093
|
+
const destPath = join7(dest, entry.name);
|
|
972
1094
|
if (entry.isDirectory()) {
|
|
973
1095
|
copyDirRecursive(srcPath, destPath);
|
|
974
1096
|
} else {
|
|
@@ -977,6 +1099,49 @@ function copyDirRecursive(src, dest) {
|
|
|
977
1099
|
}
|
|
978
1100
|
}
|
|
979
1101
|
}
|
|
1102
|
+
function createOpenCodeSkillsCleanupMigration(version2) {
|
|
1103
|
+
return {
|
|
1104
|
+
version: version2,
|
|
1105
|
+
description: "Remove droid-managed skills from OpenCode platform-specific directory",
|
|
1106
|
+
up: () => {
|
|
1107
|
+
const oldOpenCodeSkillsPath = join7(
|
|
1108
|
+
homedir4(),
|
|
1109
|
+
".config",
|
|
1110
|
+
"opencode",
|
|
1111
|
+
"skill"
|
|
1112
|
+
);
|
|
1113
|
+
const unifiedSkillsPath = join7(homedir4(), ".claude", "skills");
|
|
1114
|
+
if (!existsSync6(oldOpenCodeSkillsPath)) {
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
const bundledTools = getBundledTools();
|
|
1118
|
+
const droidManagedSkills = /* @__PURE__ */ new Set();
|
|
1119
|
+
for (const tool of bundledTools) {
|
|
1120
|
+
for (const skill of tool.includes.skills) {
|
|
1121
|
+
droidManagedSkills.add(skill.name);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
const skillDirs = readdirSync5(oldOpenCodeSkillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
1125
|
+
for (const skillName of skillDirs) {
|
|
1126
|
+
if (!droidManagedSkills.has(skillName)) {
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
const unifiedPath = join7(unifiedSkillsPath, skillName);
|
|
1130
|
+
if (!existsSync6(unifiedPath)) {
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
const oldPath = join7(oldOpenCodeSkillsPath, skillName);
|
|
1134
|
+
try {
|
|
1135
|
+
rmSync2(oldPath, { recursive: true });
|
|
1136
|
+
} catch (error) {
|
|
1137
|
+
console.warn(
|
|
1138
|
+
`Warning: Could not remove OpenCode skill ${skillName}: ${error}`
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
980
1145
|
var PACKAGE_MIGRATIONS = [
|
|
981
1146
|
createPlatformSyncMigration("0.25.0"),
|
|
982
1147
|
createConfigSkillNameMigration("0.27.2"),
|
|
@@ -985,7 +1150,8 @@ var PACKAGE_MIGRATIONS = [
|
|
|
985
1150
|
// Retry: 0.28.0 migration had platform check that prevented running after platform switch
|
|
986
1151
|
createClaudeCodeCommandCleanupMigration("0.28.1"),
|
|
987
1152
|
createOpenCodePluginCleanupMigration("0.29.2"),
|
|
988
|
-
createUnifiedSkillsPathMigration("0.30.0")
|
|
1153
|
+
createUnifiedSkillsPathMigration("0.30.0"),
|
|
1154
|
+
createOpenCodeSkillsCleanupMigration("0.37.1")
|
|
989
1155
|
];
|
|
990
1156
|
var TOOL_MIGRATIONS = {
|
|
991
1157
|
brain: [createConfigDirMigration("droid-brain", "0.2.3")],
|
|
@@ -1109,8 +1275,8 @@ function runPackageMigrations(packageVersion) {
|
|
|
1109
1275
|
// src/lib/skills.ts
|
|
1110
1276
|
var DROID_SKILLS_START = "<!-- droid-skills-start -->";
|
|
1111
1277
|
var DROID_SKILLS_END = "<!-- droid-skills-end -->";
|
|
1112
|
-
var __dirname4 =
|
|
1113
|
-
var BUNDLED_SKILLS_DIR =
|
|
1278
|
+
var __dirname4 = dirname6(fileURLToPath4(import.meta.url));
|
|
1279
|
+
var BUNDLED_SKILLS_DIR = join8(__dirname4, "../tools");
|
|
1114
1280
|
function getSkillsInstallPath(platform) {
|
|
1115
1281
|
return getSkillsPath(platform);
|
|
1116
1282
|
}
|
|
@@ -1122,8 +1288,11 @@ function getPlatformConfigPath(platform) {
|
|
|
1122
1288
|
}
|
|
1123
1289
|
function updatePlatformConfigSkills(platform, installedSkills) {
|
|
1124
1290
|
const configPath = getPlatformConfigPath(platform);
|
|
1291
|
+
if (!configPath) {
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1125
1294
|
let content = "";
|
|
1126
|
-
if (
|
|
1295
|
+
if (existsSync7(configPath)) {
|
|
1127
1296
|
content = readFileSync6(configPath, "utf-8");
|
|
1128
1297
|
}
|
|
1129
1298
|
const skillLines = installedSkills.map((name) => {
|
|
@@ -1142,9 +1311,9 @@ ${DROID_SKILLS_END}` : "";
|
|
|
1142
1311
|
} else if (skillsSection) {
|
|
1143
1312
|
content = content.trim() + "\n\n" + skillsSection + "\n";
|
|
1144
1313
|
}
|
|
1145
|
-
const configDir =
|
|
1146
|
-
if (!
|
|
1147
|
-
|
|
1314
|
+
const configDir = dirname6(configPath);
|
|
1315
|
+
if (!existsSync7(configDir)) {
|
|
1316
|
+
mkdirSync5(configDir, { recursive: true });
|
|
1148
1317
|
}
|
|
1149
1318
|
writeFileSync4(configPath, content, "utf-8");
|
|
1150
1319
|
}
|
|
@@ -1165,8 +1334,8 @@ function parseSkillFrontmatter(content) {
|
|
|
1165
1334
|
}
|
|
1166
1335
|
}
|
|
1167
1336
|
function loadSkillManifest(skillDir) {
|
|
1168
|
-
const skillMdPath =
|
|
1169
|
-
if (!
|
|
1337
|
+
const skillMdPath = join8(skillDir, "SKILL.md");
|
|
1338
|
+
if (!existsSync7(skillMdPath)) {
|
|
1170
1339
|
return null;
|
|
1171
1340
|
}
|
|
1172
1341
|
const content = readFileSync6(skillMdPath, "utf-8");
|
|
@@ -1174,7 +1343,7 @@ function loadSkillManifest(skillDir) {
|
|
|
1174
1343
|
if (!frontmatter || !frontmatter.name) {
|
|
1175
1344
|
return null;
|
|
1176
1345
|
}
|
|
1177
|
-
const toolDir =
|
|
1346
|
+
const toolDir = dirname6(dirname6(skillDir));
|
|
1178
1347
|
const toolManifest = loadToolManifest(toolDir);
|
|
1179
1348
|
return {
|
|
1180
1349
|
name: frontmatter.name,
|
|
@@ -1186,17 +1355,17 @@ function loadSkillManifest(skillDir) {
|
|
|
1186
1355
|
};
|
|
1187
1356
|
}
|
|
1188
1357
|
function findSkillPath(skillName) {
|
|
1189
|
-
if (!
|
|
1358
|
+
if (!existsSync7(BUNDLED_SKILLS_DIR)) {
|
|
1190
1359
|
return null;
|
|
1191
1360
|
}
|
|
1192
|
-
const toolDirs =
|
|
1361
|
+
const toolDirs = readdirSync6(BUNDLED_SKILLS_DIR, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
1193
1362
|
for (const toolName of toolDirs) {
|
|
1194
|
-
const skillsDir =
|
|
1195
|
-
if (!
|
|
1196
|
-
const skillDir =
|
|
1197
|
-
if (
|
|
1363
|
+
const skillsDir = join8(BUNDLED_SKILLS_DIR, toolName, "skills");
|
|
1364
|
+
if (!existsSync7(skillsDir)) continue;
|
|
1365
|
+
const skillDir = join8(skillsDir, skillName);
|
|
1366
|
+
if (existsSync7(skillDir) && existsSync7(join8(skillDir, "SKILL.md"))) {
|
|
1198
1367
|
return {
|
|
1199
|
-
toolDir:
|
|
1368
|
+
toolDir: join8(BUNDLED_SKILLS_DIR, toolName),
|
|
1200
1369
|
skillDir
|
|
1201
1370
|
};
|
|
1202
1371
|
}
|
|
@@ -1204,17 +1373,17 @@ function findSkillPath(skillName) {
|
|
|
1204
1373
|
return null;
|
|
1205
1374
|
}
|
|
1206
1375
|
function getBundledSkills() {
|
|
1207
|
-
if (!
|
|
1376
|
+
if (!existsSync7(BUNDLED_SKILLS_DIR)) {
|
|
1208
1377
|
return [];
|
|
1209
1378
|
}
|
|
1210
|
-
const toolDirs =
|
|
1379
|
+
const toolDirs = readdirSync6(BUNDLED_SKILLS_DIR, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
1211
1380
|
const skills = [];
|
|
1212
1381
|
for (const toolName of toolDirs) {
|
|
1213
|
-
const skillsDir =
|
|
1214
|
-
if (!
|
|
1215
|
-
const skillSubdirs =
|
|
1382
|
+
const skillsDir = join8(BUNDLED_SKILLS_DIR, toolName, "skills");
|
|
1383
|
+
if (!existsSync7(skillsDir)) continue;
|
|
1384
|
+
const skillSubdirs = readdirSync6(skillsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
1216
1385
|
for (const skillName of skillSubdirs) {
|
|
1217
|
-
const manifest = loadSkillManifest(
|
|
1386
|
+
const manifest = loadSkillManifest(join8(skillsDir, skillName));
|
|
1218
1387
|
if (manifest) {
|
|
1219
1388
|
skills.push(manifest);
|
|
1220
1389
|
}
|
|
@@ -1306,13 +1475,13 @@ function updateAllSkills() {
|
|
|
1306
1475
|
return result;
|
|
1307
1476
|
}
|
|
1308
1477
|
function copyDirectoryRecursive(source, target, fileFilter) {
|
|
1309
|
-
if (!
|
|
1310
|
-
|
|
1478
|
+
if (!existsSync7(target)) {
|
|
1479
|
+
mkdirSync5(target, { recursive: true });
|
|
1311
1480
|
}
|
|
1312
|
-
const entries =
|
|
1481
|
+
const entries = readdirSync6(source, { withFileTypes: true });
|
|
1313
1482
|
for (const entry of entries) {
|
|
1314
|
-
const sourcePath =
|
|
1315
|
-
const targetPath =
|
|
1483
|
+
const sourcePath = join8(source, entry.name);
|
|
1484
|
+
const targetPath = join8(target, entry.name);
|
|
1316
1485
|
if (entry.isDirectory()) {
|
|
1317
1486
|
copyDirectoryRecursive(sourcePath, targetPath, fileFilter);
|
|
1318
1487
|
} else if (entry.isFile() && fileFilter(entry.name)) {
|
|
@@ -1353,7 +1522,7 @@ function installSkill(skillName) {
|
|
|
1353
1522
|
}
|
|
1354
1523
|
}
|
|
1355
1524
|
const skillsPath = getSkillsInstallPath(config.platform);
|
|
1356
|
-
const targetSkillDir =
|
|
1525
|
+
const targetSkillDir = join8(skillsPath, skillName);
|
|
1357
1526
|
const commandsPath = getCommandsInstallPath(config.platform);
|
|
1358
1527
|
const tools = getPlatformTools(config);
|
|
1359
1528
|
const renamedSkills = [
|
|
@@ -1368,10 +1537,10 @@ function installSkill(skillName) {
|
|
|
1368
1537
|
];
|
|
1369
1538
|
if (renamedSkills.includes(skillName)) {
|
|
1370
1539
|
const droidPrefixedName = `droid-${skillName}`;
|
|
1371
|
-
const droidPrefixedDir =
|
|
1372
|
-
if (
|
|
1540
|
+
const droidPrefixedDir = join8(skillsPath, droidPrefixedName);
|
|
1541
|
+
if (existsSync7(droidPrefixedDir)) {
|
|
1373
1542
|
try {
|
|
1374
|
-
|
|
1543
|
+
rmSync3(droidPrefixedDir, { recursive: true });
|
|
1375
1544
|
} catch (error) {
|
|
1376
1545
|
console.warn(
|
|
1377
1546
|
`Warning: Could not remove old skill directory ${droidPrefixedDir}: ${error}`
|
|
@@ -1384,18 +1553,18 @@ function installSkill(skillName) {
|
|
|
1384
1553
|
saveConfig(config);
|
|
1385
1554
|
}
|
|
1386
1555
|
}
|
|
1387
|
-
const commandsSource =
|
|
1388
|
-
const agentsSource =
|
|
1556
|
+
const commandsSource = join8(toolDir, "commands");
|
|
1557
|
+
const agentsSource = join8(toolDir, "agents");
|
|
1389
1558
|
const isAlreadyInstalled = tools[skillName];
|
|
1390
1559
|
if (!isAlreadyInstalled) {
|
|
1391
1560
|
const toolName2 = basename(toolDir);
|
|
1392
|
-
if (
|
|
1393
|
-
const commandFiles =
|
|
1561
|
+
if (commandsPath && existsSync7(commandsSource)) {
|
|
1562
|
+
const commandFiles = readdirSync6(commandsSource).filter(
|
|
1394
1563
|
(f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md"
|
|
1395
1564
|
);
|
|
1396
1565
|
for (const file of commandFiles) {
|
|
1397
|
-
const targetCommandPath =
|
|
1398
|
-
if (
|
|
1566
|
+
const targetCommandPath = join8(commandsPath, file);
|
|
1567
|
+
if (existsSync7(targetCommandPath)) {
|
|
1399
1568
|
const commandName = file.replace(".md", "");
|
|
1400
1569
|
if (commandName === toolName2) {
|
|
1401
1570
|
continue;
|
|
@@ -1407,8 +1576,8 @@ function installSkill(skillName) {
|
|
|
1407
1576
|
}
|
|
1408
1577
|
}
|
|
1409
1578
|
}
|
|
1410
|
-
if (
|
|
1411
|
-
const agentFiles =
|
|
1579
|
+
if (existsSync7(agentsSource)) {
|
|
1580
|
+
const agentFiles = readdirSync6(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md")).map((dirent) => dirent.name.replace(".md", ""));
|
|
1412
1581
|
for (const agentName of agentFiles) {
|
|
1413
1582
|
if (isAgentInstalled(agentName)) {
|
|
1414
1583
|
return {
|
|
@@ -1419,30 +1588,30 @@ function installSkill(skillName) {
|
|
|
1419
1588
|
}
|
|
1420
1589
|
}
|
|
1421
1590
|
}
|
|
1422
|
-
if (!
|
|
1423
|
-
|
|
1591
|
+
if (!existsSync7(skillsPath)) {
|
|
1592
|
+
mkdirSync5(skillsPath, { recursive: true });
|
|
1424
1593
|
}
|
|
1425
|
-
const skillMdSource =
|
|
1426
|
-
if (
|
|
1427
|
-
if (!
|
|
1428
|
-
|
|
1594
|
+
const skillMdSource = join8(skillDir, "SKILL.md");
|
|
1595
|
+
if (existsSync7(skillMdSource)) {
|
|
1596
|
+
if (!existsSync7(targetSkillDir)) {
|
|
1597
|
+
mkdirSync5(targetSkillDir, { recursive: true });
|
|
1429
1598
|
}
|
|
1430
|
-
const skillMdTarget =
|
|
1599
|
+
const skillMdTarget = join8(targetSkillDir, "SKILL.md");
|
|
1431
1600
|
const content = readFileSync6(skillMdSource, "utf-8");
|
|
1432
1601
|
writeFileSync4(skillMdTarget, content);
|
|
1433
1602
|
}
|
|
1434
|
-
const referencesSource =
|
|
1435
|
-
if (
|
|
1436
|
-
const targetReferencesDir =
|
|
1603
|
+
const referencesSource = join8(skillDir, "references");
|
|
1604
|
+
if (existsSync7(referencesSource)) {
|
|
1605
|
+
const targetReferencesDir = join8(targetSkillDir, "references");
|
|
1437
1606
|
copyDirectoryRecursive(
|
|
1438
1607
|
referencesSource,
|
|
1439
1608
|
targetReferencesDir,
|
|
1440
1609
|
(f) => f.endsWith(".md")
|
|
1441
1610
|
);
|
|
1442
1611
|
}
|
|
1443
|
-
const scriptsSource =
|
|
1444
|
-
if (
|
|
1445
|
-
const targetScriptsDir =
|
|
1612
|
+
const scriptsSource = join8(skillDir, "scripts");
|
|
1613
|
+
if (existsSync7(scriptsSource)) {
|
|
1614
|
+
const targetScriptsDir = join8(targetSkillDir, "scripts");
|
|
1446
1615
|
copyDirectoryRecursive(
|
|
1447
1616
|
scriptsSource,
|
|
1448
1617
|
targetScriptsDir,
|
|
@@ -1451,14 +1620,20 @@ function installSkill(skillName) {
|
|
|
1451
1620
|
}
|
|
1452
1621
|
const activePlatforms = getActivePlatforms(config);
|
|
1453
1622
|
const targetPlatforms = activePlatforms.length > 0 ? activePlatforms : [config.platform];
|
|
1454
|
-
if (
|
|
1455
|
-
|
|
1623
|
+
if (targetPlatforms.includes("openai-codex" /* OpenAICodex */)) {
|
|
1624
|
+
createCodexSymlink(skillName);
|
|
1625
|
+
}
|
|
1626
|
+
if (existsSync7(commandsSource)) {
|
|
1627
|
+
const commandFiles = readdirSync6(commandsSource).filter(
|
|
1456
1628
|
(f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md"
|
|
1457
1629
|
);
|
|
1458
1630
|
for (const platform of targetPlatforms) {
|
|
1459
1631
|
const platformCommandsPath = getCommandsInstallPath(platform);
|
|
1460
|
-
if (!
|
|
1461
|
-
|
|
1632
|
+
if (!platformCommandsPath) {
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
if (!existsSync7(platformCommandsPath)) {
|
|
1636
|
+
mkdirSync5(platformCommandsPath, { recursive: true });
|
|
1462
1637
|
}
|
|
1463
1638
|
for (const file of commandFiles) {
|
|
1464
1639
|
const commandName = file.replace(".md", "");
|
|
@@ -1468,8 +1643,8 @@ function installSkill(skillName) {
|
|
|
1468
1643
|
const isAlias = typeof commandMeta === "object" && commandMeta.is_alias;
|
|
1469
1644
|
const shouldInstall = platform === "opencode" /* OpenCode */ || platform === "cursor" /* Cursor */ || isAlias;
|
|
1470
1645
|
if (shouldInstall) {
|
|
1471
|
-
const sourcePath =
|
|
1472
|
-
const targetPath =
|
|
1646
|
+
const sourcePath = join8(commandsSource, file);
|
|
1647
|
+
const targetPath = join8(platformCommandsPath, file);
|
|
1473
1648
|
const content = readFileSync6(sourcePath, "utf-8");
|
|
1474
1649
|
writeFileSync4(targetPath, content);
|
|
1475
1650
|
}
|
|
@@ -1477,10 +1652,10 @@ function installSkill(skillName) {
|
|
|
1477
1652
|
}
|
|
1478
1653
|
}
|
|
1479
1654
|
const installedAgents = [];
|
|
1480
|
-
if (
|
|
1481
|
-
const agentFiles =
|
|
1655
|
+
if (existsSync7(agentsSource)) {
|
|
1656
|
+
const agentFiles = readdirSync6(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md")).map((dirent) => dirent.name.replace(".md", ""));
|
|
1482
1657
|
for (const agentName of agentFiles) {
|
|
1483
|
-
const agentPath =
|
|
1658
|
+
const agentPath = join8(agentsSource, `${agentName}.md`);
|
|
1484
1659
|
let anySuccess = false;
|
|
1485
1660
|
for (const platform of targetPlatforms) {
|
|
1486
1661
|
const result = installAgentFromPath(agentPath, agentName, platform);
|
|
@@ -1527,23 +1702,27 @@ function uninstallSkill(skillName) {
|
|
|
1527
1702
|
}
|
|
1528
1703
|
const activePlatforms = getActivePlatforms(config);
|
|
1529
1704
|
const targetPlatforms = activePlatforms.length > 0 ? activePlatforms : [config.platform];
|
|
1705
|
+
removeCodexSymlink(skillName);
|
|
1530
1706
|
const skillsPath = getSkillsInstallPath(config.platform);
|
|
1531
|
-
const skillDir =
|
|
1532
|
-
if (
|
|
1533
|
-
|
|
1707
|
+
const skillDir = join8(skillsPath, skillName);
|
|
1708
|
+
if (existsSync7(skillDir)) {
|
|
1709
|
+
rmSync3(skillDir, { recursive: true });
|
|
1534
1710
|
}
|
|
1535
1711
|
const skillPath = findSkillPath(skillName);
|
|
1536
|
-
const commandsSource = skillPath ?
|
|
1537
|
-
if (commandsSource &&
|
|
1538
|
-
const commandFiles =
|
|
1712
|
+
const commandsSource = skillPath ? join8(skillPath.toolDir, "commands") : null;
|
|
1713
|
+
if (commandsSource && existsSync7(commandsSource)) {
|
|
1714
|
+
const commandFiles = readdirSync6(commandsSource).filter(
|
|
1539
1715
|
(f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md"
|
|
1540
1716
|
);
|
|
1541
1717
|
for (const platform of targetPlatforms) {
|
|
1542
1718
|
const platformCommandsPath = getCommandsInstallPath(platform);
|
|
1719
|
+
if (!platformCommandsPath) {
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1543
1722
|
for (const file of commandFiles) {
|
|
1544
|
-
const commandPath =
|
|
1545
|
-
if (
|
|
1546
|
-
|
|
1723
|
+
const commandPath = join8(platformCommandsPath, file);
|
|
1724
|
+
if (existsSync7(commandPath)) {
|
|
1725
|
+
rmSync3(commandPath);
|
|
1547
1726
|
}
|
|
1548
1727
|
}
|
|
1549
1728
|
}
|
|
@@ -1555,9 +1734,9 @@ function uninstallSkill(skillName) {
|
|
|
1555
1734
|
agentsToRemove.add(agentName);
|
|
1556
1735
|
}
|
|
1557
1736
|
}
|
|
1558
|
-
const agentsSource = skillPath ?
|
|
1559
|
-
if (agentsSource &&
|
|
1560
|
-
const agentFiles =
|
|
1737
|
+
const agentsSource = skillPath ? join8(skillPath.toolDir, "agents") : null;
|
|
1738
|
+
if (agentsSource && existsSync7(agentsSource)) {
|
|
1739
|
+
const agentFiles = readdirSync6(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md")).map((dirent) => dirent.name.replace(".md", ""));
|
|
1561
1740
|
for (const agentName of agentFiles) {
|
|
1562
1741
|
agentsToRemove.add(agentName);
|
|
1563
1742
|
}
|
|
@@ -1581,7 +1760,8 @@ function uninstallSkill(skillName) {
|
|
|
1581
1760
|
var PLATFORM_LABELS = {
|
|
1582
1761
|
["claude-code" /* ClaudeCode */]: "Claude Code",
|
|
1583
1762
|
["cursor" /* Cursor */]: "Cursor",
|
|
1584
|
-
["opencode" /* OpenCode */]: "OpenCode"
|
|
1763
|
+
["opencode" /* OpenCode */]: "OpenCode",
|
|
1764
|
+
["openai-codex" /* OpenAICodex */]: "Codex"
|
|
1585
1765
|
};
|
|
1586
1766
|
var DROID_PERMISSIONS = [
|
|
1587
1767
|
"Read(~/.droid/**)",
|
|
@@ -1590,6 +1770,75 @@ var DROID_PERMISSIONS = [
|
|
|
1590
1770
|
"Glob(~/.droid/**)",
|
|
1591
1771
|
"Grep(~/.droid/**)"
|
|
1592
1772
|
];
|
|
1773
|
+
var SKILL_OVERRIDES_TEMPLATE = `---
|
|
1774
|
+
overrides: []
|
|
1775
|
+
---
|
|
1776
|
+
|
|
1777
|
+
# Skill Override Template
|
|
1778
|
+
|
|
1779
|
+
Copy this file to \`{skill_name}.md\` to override a skill's behaviour.
|
|
1780
|
+
|
|
1781
|
+
## Format
|
|
1782
|
+
|
|
1783
|
+
\`\`\`yaml
|
|
1784
|
+
---
|
|
1785
|
+
overrides: [command1, command2]
|
|
1786
|
+
---
|
|
1787
|
+
\`\`\`
|
|
1788
|
+
|
|
1789
|
+
Then add sections for each command you're overriding:
|
|
1790
|
+
|
|
1791
|
+
\`\`\`markdown
|
|
1792
|
+
## {command1}
|
|
1793
|
+
|
|
1794
|
+
Your custom instructions for this command...
|
|
1795
|
+
\`\`\`
|
|
1796
|
+
|
|
1797
|
+
---
|
|
1798
|
+
|
|
1799
|
+
## Example: Override brain search with qmd
|
|
1800
|
+
|
|
1801
|
+
Create \`brain.md\` with:
|
|
1802
|
+
|
|
1803
|
+
\`\`\`yaml
|
|
1804
|
+
---
|
|
1805
|
+
overrides: [search]
|
|
1806
|
+
---
|
|
1807
|
+
\`\`\`
|
|
1808
|
+
|
|
1809
|
+
## search
|
|
1810
|
+
|
|
1811
|
+
Use qmd for semantic search:
|
|
1812
|
+
|
|
1813
|
+
1. Run \`qmd query "{query}" -c brain -n 10 --files\`
|
|
1814
|
+
2. Parse CSV output, extract filepath (field 3)
|
|
1815
|
+
3. If qmd fails, fall back to Glob
|
|
1816
|
+
|
|
1817
|
+
---
|
|
1818
|
+
|
|
1819
|
+
## Registration
|
|
1820
|
+
|
|
1821
|
+
After creating an override file, ask droid to register it:
|
|
1822
|
+
|
|
1823
|
+
> "Register my brain override"
|
|
1824
|
+
|
|
1825
|
+
Droid will:
|
|
1826
|
+
1. Parse the frontmatter to find which commands are overridden
|
|
1827
|
+
2. Update config.yaml with override metadata
|
|
1828
|
+
3. The skill will use your override on next invocation
|
|
1829
|
+
`;
|
|
1830
|
+
function createSkillOverridesDir() {
|
|
1831
|
+
const overridesDir = join9(getConfigDir(), "skill_overrides");
|
|
1832
|
+
const templatePath = join9(overridesDir, "_template.md");
|
|
1833
|
+
if (existsSync8(templatePath)) {
|
|
1834
|
+
return { created: false, alreadyExists: true };
|
|
1835
|
+
}
|
|
1836
|
+
if (!existsSync8(overridesDir)) {
|
|
1837
|
+
mkdirSync6(overridesDir, { recursive: true });
|
|
1838
|
+
}
|
|
1839
|
+
writeFileSync5(templatePath, SKILL_OVERRIDES_TEMPLATE, "utf-8");
|
|
1840
|
+
return { created: true, alreadyExists: false };
|
|
1841
|
+
}
|
|
1593
1842
|
function detectGitUsername() {
|
|
1594
1843
|
try {
|
|
1595
1844
|
return execSync3("git config user.name", { encoding: "utf-8" }).trim();
|
|
@@ -1600,13 +1849,13 @@ function detectGitUsername() {
|
|
|
1600
1849
|
function configurePlatformPermissions(platform) {
|
|
1601
1850
|
const added = [];
|
|
1602
1851
|
if (platform === "claude-code" /* ClaudeCode */) {
|
|
1603
|
-
const settingsPath =
|
|
1604
|
-
const claudeDir =
|
|
1605
|
-
if (!
|
|
1606
|
-
|
|
1852
|
+
const settingsPath = join9(homedir5(), ".claude", "settings.json");
|
|
1853
|
+
const claudeDir = join9(homedir5(), ".claude");
|
|
1854
|
+
if (!existsSync8(claudeDir)) {
|
|
1855
|
+
mkdirSync6(claudeDir, { recursive: true });
|
|
1607
1856
|
}
|
|
1608
1857
|
let settings = {};
|
|
1609
|
-
if (
|
|
1858
|
+
if (existsSync8(settingsPath)) {
|
|
1610
1859
|
try {
|
|
1611
1860
|
settings = JSON.parse(readFileSync7(settingsPath, "utf-8"));
|
|
1612
1861
|
} catch {
|
|
@@ -1637,20 +1886,20 @@ function configurePlatformPermissions(platform) {
|
|
|
1637
1886
|
return { added, alreadyPresent: added.length === 0 };
|
|
1638
1887
|
}
|
|
1639
1888
|
if (platform === "opencode" /* OpenCode */) {
|
|
1640
|
-
const globalConfigDir =
|
|
1641
|
-
if (!
|
|
1642
|
-
|
|
1889
|
+
const globalConfigDir = join9(homedir5(), ".config", "opencode");
|
|
1890
|
+
if (!existsSync8(globalConfigDir)) {
|
|
1891
|
+
mkdirSync6(globalConfigDir, { recursive: true });
|
|
1643
1892
|
}
|
|
1644
1893
|
return { added: [], alreadyPresent: true };
|
|
1645
1894
|
}
|
|
1646
1895
|
if (platform === "cursor" /* Cursor */) {
|
|
1647
|
-
const cursorDir =
|
|
1648
|
-
if (!
|
|
1649
|
-
|
|
1896
|
+
const cursorDir = join9(homedir5(), ".cursor");
|
|
1897
|
+
if (!existsSync8(cursorDir)) {
|
|
1898
|
+
mkdirSync6(cursorDir, { recursive: true });
|
|
1650
1899
|
}
|
|
1651
|
-
const agentsDir =
|
|
1652
|
-
if (!
|
|
1653
|
-
|
|
1900
|
+
const agentsDir = join9(cursorDir, "agents");
|
|
1901
|
+
if (!existsSync8(agentsDir)) {
|
|
1902
|
+
mkdirSync6(agentsDir, { recursive: true });
|
|
1654
1903
|
}
|
|
1655
1904
|
return { added: [], alreadyPresent: true };
|
|
1656
1905
|
}
|
|
@@ -1758,6 +2007,12 @@ async function setupCommand() {
|
|
|
1758
2007
|
};
|
|
1759
2008
|
saveConfig(config);
|
|
1760
2009
|
console.log(chalk2.green("\n\u2713 Config saved to ~/.droid/config.yaml"));
|
|
2010
|
+
const { created: overridesCreated, alreadyExists: overridesExist } = createSkillOverridesDir();
|
|
2011
|
+
if (overridesCreated) {
|
|
2012
|
+
console.log(chalk2.green("\u2713 Created ~/.droid/skill_overrides/ with template"));
|
|
2013
|
+
} else if (overridesExist) {
|
|
2014
|
+
console.log(chalk2.gray(" Skill overrides directory already exists"));
|
|
2015
|
+
}
|
|
1761
2016
|
for (const platform of activePlatforms) {
|
|
1762
2017
|
const { added, alreadyPresent, error } = configurePlatformPermissions(platform);
|
|
1763
2018
|
if (error) {
|
|
@@ -1772,6 +2027,16 @@ async function setupCommand() {
|
|
|
1772
2027
|
console.log(chalk2.gray(` ${PLATFORM_LABELS[platform]} directories ready`));
|
|
1773
2028
|
}
|
|
1774
2029
|
}
|
|
2030
|
+
if (activePlatforms.includes("openai-codex" /* OpenAICodex */)) {
|
|
2031
|
+
const trackedTools = getPlatformTools(config);
|
|
2032
|
+
const toolNames = Object.keys(trackedTools);
|
|
2033
|
+
if (toolNames.length > 0) {
|
|
2034
|
+
for (const skillName of toolNames) {
|
|
2035
|
+
createCodexSymlink(skillName);
|
|
2036
|
+
}
|
|
2037
|
+
console.log(chalk2.green("\u2713 Created Codex symlinks for installed tools"));
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
1775
2040
|
if (activePlatforms.length > 1) {
|
|
1776
2041
|
console.log(chalk2.green(`
|
|
1777
2042
|
\u2713 Will install to ${activePlatforms.length} platforms: ${activePlatforms.map((p) => PLATFORM_LABELS[p]).join(", ")}`));
|
|
@@ -2159,7 +2424,7 @@ async function uninstallCommand(toolName) {
|
|
|
2159
2424
|
|
|
2160
2425
|
// src/commands/update.ts
|
|
2161
2426
|
import chalk8 from "chalk";
|
|
2162
|
-
import { execSync as execSync5 } from "child_process";
|
|
2427
|
+
import { execSync as execSync5, spawnSync } from "child_process";
|
|
2163
2428
|
async function updateCommand(tool, options) {
|
|
2164
2429
|
if (tool) {
|
|
2165
2430
|
console.log(chalk8.yellow("\n\u26A0 Per-tool updates not implemented yet"));
|
|
@@ -2214,6 +2479,22 @@ async function updateCommand(tool, options) {
|
|
|
2214
2479
|
execSync5("npm install -g @orderful/droid@latest", { stdio: "inherit" });
|
|
2215
2480
|
console.log(chalk8.green(`
|
|
2216
2481
|
\u2713 Updated to v${latestVersion}`));
|
|
2482
|
+
const autoUpdateConfig = getAutoUpdateConfig();
|
|
2483
|
+
if (autoUpdateConfig.tools) {
|
|
2484
|
+
console.log(chalk8.bold("\n\u{1F916} Syncing tools...\n"));
|
|
2485
|
+
const toolResult = spawnSync("droid", ["update", "--tools"], {
|
|
2486
|
+
stdio: "inherit",
|
|
2487
|
+
timeout: 6e4
|
|
2488
|
+
// 60s timeout for tool sync
|
|
2489
|
+
});
|
|
2490
|
+
if (toolResult.error || toolResult.status !== 0) {
|
|
2491
|
+
console.log(
|
|
2492
|
+
chalk8.yellow(
|
|
2493
|
+
"Tool sync incomplete - run `droid update --tools` to retry"
|
|
2494
|
+
)
|
|
2495
|
+
);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2217
2498
|
} catch {
|
|
2218
2499
|
console.log(chalk8.yellow("\n\u26A0 Could not check for updates"));
|
|
2219
2500
|
console.log(chalk8.gray("Package may not be published yet."));
|
|
@@ -2413,7 +2694,8 @@ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
|
2413
2694
|
var PLATFORM_LABELS2 = {
|
|
2414
2695
|
["claude-code" /* ClaudeCode */]: "Claude Code",
|
|
2415
2696
|
["cursor" /* Cursor */]: "Cursor",
|
|
2416
|
-
["opencode" /* OpenCode */]: "OpenCode"
|
|
2697
|
+
["opencode" /* OpenCode */]: "OpenCode",
|
|
2698
|
+
["openai-codex" /* OpenAICodex */]: "OpenAI Codex"
|
|
2417
2699
|
};
|
|
2418
2700
|
function SettingsDetails({
|
|
2419
2701
|
isFocused,
|
|
@@ -2510,7 +2792,8 @@ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
|
2510
2792
|
var PLATFORM_INFO = {
|
|
2511
2793
|
["claude-code" /* ClaudeCode */]: { color: "#da7756", label: "Claude" },
|
|
2512
2794
|
["cursor" /* Cursor */]: { color: "#22d3ee", label: "Cursor" },
|
|
2513
|
-
["opencode" /* OpenCode */]: { color: "#4ade80", label: "OpenCode" }
|
|
2795
|
+
["opencode" /* OpenCode */]: { color: "#4ade80", label: "OpenCode" },
|
|
2796
|
+
["openai-codex" /* OpenAICodex */]: { color: "#10b981", label: "Codex" }
|
|
2514
2797
|
};
|
|
2515
2798
|
function PlatformBadges({
|
|
2516
2799
|
detected,
|
|
@@ -2790,7 +3073,8 @@ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
|
2790
3073
|
var PLATFORM_LABELS3 = {
|
|
2791
3074
|
["claude-code" /* ClaudeCode */]: "Claude Code",
|
|
2792
3075
|
["cursor" /* Cursor */]: "Cursor",
|
|
2793
|
-
["opencode" /* OpenCode */]: "OpenCode"
|
|
3076
|
+
["opencode" /* OpenCode */]: "OpenCode",
|
|
3077
|
+
["openai-codex" /* OpenAICodex */]: "Codex"
|
|
2794
3078
|
};
|
|
2795
3079
|
function SetupScreen({ onComplete, onSkip, initialConfig, detectedPlatforms }) {
|
|
2796
3080
|
const [step, setStep] = useState3("user_mention");
|
|
@@ -2853,6 +3137,12 @@ function SetupScreen({ onComplete, onSkip, initialConfig, detectedPlatforms }) {
|
|
|
2853
3137
|
for (const platform of detectedPlatforms) {
|
|
2854
3138
|
configurePlatformPermissions(platform);
|
|
2855
3139
|
}
|
|
3140
|
+
if (detectedPlatforms.includes("openai-codex" /* OpenAICodex */)) {
|
|
3141
|
+
const trackedTools = getPlatformTools(config);
|
|
3142
|
+
for (const skillName of Object.keys(trackedTools)) {
|
|
3143
|
+
createCodexSymlink(skillName);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
2856
3146
|
onComplete();
|
|
2857
3147
|
}
|
|
2858
3148
|
}
|
|
@@ -3088,8 +3378,8 @@ function ReadmeViewer({ title, content, onClose }) {
|
|
|
3088
3378
|
// src/commands/tui/views/ToolExplorer.tsx
|
|
3089
3379
|
import { Box as Box11, Text as Text12, useInput as useInput5 } from "ink";
|
|
3090
3380
|
import { useState as useState5, useMemo as useMemo3 } from "react";
|
|
3091
|
-
import { existsSync as
|
|
3092
|
-
import { join as
|
|
3381
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
3382
|
+
import { join as join10 } from "path";
|
|
3093
3383
|
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3094
3384
|
function ToolExplorer({ tool, onViewSource, onClose }) {
|
|
3095
3385
|
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
@@ -3100,7 +3390,7 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
|
|
|
3100
3390
|
result.push({
|
|
3101
3391
|
type: "skill",
|
|
3102
3392
|
name: skill.name,
|
|
3103
|
-
path:
|
|
3393
|
+
path: join10(toolDir, tool.name, "skills", skill.name, "SKILL.md")
|
|
3104
3394
|
});
|
|
3105
3395
|
}
|
|
3106
3396
|
for (const cmd of tool.includes.commands) {
|
|
@@ -3108,14 +3398,14 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
|
|
|
3108
3398
|
result.push({
|
|
3109
3399
|
type: "command",
|
|
3110
3400
|
name: `/${cmdName}`,
|
|
3111
|
-
path:
|
|
3401
|
+
path: join10(toolDir, tool.name, "commands", `${cmdName}.md`)
|
|
3112
3402
|
});
|
|
3113
3403
|
}
|
|
3114
3404
|
for (const agent of tool.includes.agents) {
|
|
3115
3405
|
result.push({
|
|
3116
3406
|
type: "agent",
|
|
3117
3407
|
name: agent,
|
|
3118
|
-
path:
|
|
3408
|
+
path: join10(toolDir, tool.name, "agents", agent, "AGENT.md")
|
|
3119
3409
|
});
|
|
3120
3410
|
}
|
|
3121
3411
|
return result;
|
|
@@ -3133,12 +3423,12 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
|
|
|
3133
3423
|
}
|
|
3134
3424
|
if (key.return && items.length > 0) {
|
|
3135
3425
|
const item = items[selectedIndex];
|
|
3136
|
-
if (
|
|
3426
|
+
if (existsSync9(item.path)) {
|
|
3137
3427
|
const content = readFileSync8(item.path, "utf-8");
|
|
3138
3428
|
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
3139
3429
|
} else {
|
|
3140
3430
|
const yamlPath = item.path.replace(".md", ".yaml");
|
|
3141
|
-
if (
|
|
3431
|
+
if (existsSync9(yamlPath)) {
|
|
3142
3432
|
const content = readFileSync8(yamlPath, "utf-8");
|
|
3143
3433
|
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
3144
3434
|
}
|
|
@@ -3677,6 +3967,7 @@ function ReposViewerScreen({ onClose }) {
|
|
|
3677
3967
|
// src/commands/tui/hooks/useAppUpdate.ts
|
|
3678
3968
|
import { useState as useState8, useMemo as useMemo5 } from "react";
|
|
3679
3969
|
import { useApp } from "ink";
|
|
3970
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
3680
3971
|
function useAppUpdate({ onUpdateSuccess, onUpdateFailure }) {
|
|
3681
3972
|
const { exit } = useApp();
|
|
3682
3973
|
const [isUpdating, setIsUpdating] = useState8(false);
|
|
@@ -3689,6 +3980,18 @@ function useAppUpdate({ onUpdateSuccess, onUpdateFailure }) {
|
|
|
3689
3980
|
const blue = "\x1B[38;2;99;102;241m";
|
|
3690
3981
|
const dim = "\x1B[38;2;106;106;106m";
|
|
3691
3982
|
const reset = "\x1B[0m";
|
|
3983
|
+
const autoUpdateConfig = getAutoUpdateConfig();
|
|
3984
|
+
let toolStatus = `${blue}App updated.${reset}`;
|
|
3985
|
+
if (autoUpdateConfig.tools) {
|
|
3986
|
+
console.log("\nSyncing tools...");
|
|
3987
|
+
const toolResult = spawnSync2("droid", ["update", "--tools"], {
|
|
3988
|
+
stdio: "inherit",
|
|
3989
|
+
timeout: 6e4
|
|
3990
|
+
// 60s timeout for tool sync
|
|
3991
|
+
});
|
|
3992
|
+
const toolsSynced = !toolResult.error && toolResult.status === 0;
|
|
3993
|
+
toolStatus = toolsSynced ? `${blue}App and tools updated.${reset}` : `${blue}App updated.${reset} Tools sync incomplete - run ${blue}droid update --tools${reset} to retry.`;
|
|
3994
|
+
}
|
|
3692
3995
|
const message = `
|
|
3693
3996
|
${dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${reset}
|
|
3694
3997
|
|
|
@@ -3696,6 +3999,7 @@ ${dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u
|
|
|
3696
3999
|
${dim}\u2551${reset} ${blue}\u25CF${reset} ${blue}\u25CF${reset} ${dim}\u2551${reset} ${blue}"It's quite possible this system${reset}
|
|
3697
4000
|
${dim}\u255A\u2550\u2566\u2550\u2566\u2550\u255D${reset} ${blue}is now fully operational."${reset}
|
|
3698
4001
|
|
|
4002
|
+
${toolStatus}
|
|
3699
4003
|
Run ${blue}droid${reset} to start the new version.
|
|
3700
4004
|
|
|
3701
4005
|
${dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${reset}
|
|
@@ -4254,8 +4558,8 @@ async function tuiCommand() {
|
|
|
4254
4558
|
// src/commands/exec.ts
|
|
4255
4559
|
import chalk9 from "chalk";
|
|
4256
4560
|
import { spawn } from "child_process";
|
|
4257
|
-
import { existsSync as
|
|
4258
|
-
import { join as
|
|
4561
|
+
import { existsSync as existsSync10 } from "fs";
|
|
4562
|
+
import { join as join11 } from "path";
|
|
4259
4563
|
function getRuntime(toolPath) {
|
|
4260
4564
|
if (toolPath.endsWith(".ts") || toolPath.endsWith(".js")) {
|
|
4261
4565
|
return { cmd: "bun", args: ["run", toolPath] };
|
|
@@ -4268,21 +4572,21 @@ function getRuntime(toolPath) {
|
|
|
4268
4572
|
async function execCommand(tool, script, args) {
|
|
4269
4573
|
const config = loadConfig();
|
|
4270
4574
|
const skillsPath = getSkillsPath(config.platform);
|
|
4271
|
-
const toolDir =
|
|
4272
|
-
if (!
|
|
4575
|
+
const toolDir = join11(skillsPath, tool);
|
|
4576
|
+
if (!existsSync10(toolDir)) {
|
|
4273
4577
|
console.error(chalk9.red(`Tool '${tool}' not found at ${toolDir}`));
|
|
4274
4578
|
process.exit(1);
|
|
4275
4579
|
}
|
|
4276
|
-
const scriptsDir =
|
|
4277
|
-
if (!
|
|
4580
|
+
const scriptsDir = join11(toolDir, "scripts");
|
|
4581
|
+
if (!existsSync10(scriptsDir)) {
|
|
4278
4582
|
console.error(chalk9.red(`No scripts directory in tool '${tool}'`));
|
|
4279
4583
|
process.exit(1);
|
|
4280
4584
|
}
|
|
4281
4585
|
const extensions = [".ts", ".js", ".py"];
|
|
4282
4586
|
let scriptPath = null;
|
|
4283
4587
|
for (const ext of extensions) {
|
|
4284
|
-
const candidate =
|
|
4285
|
-
if (
|
|
4588
|
+
const candidate = join11(scriptsDir, script + ext);
|
|
4589
|
+
if (existsSync10(candidate)) {
|
|
4286
4590
|
scriptPath = candidate;
|
|
4287
4591
|
break;
|
|
4288
4592
|
}
|
|
@@ -4444,6 +4748,135 @@ async function reposGetCommand(name) {
|
|
|
4444
4748
|
console.log(JSON.stringify(repo, null, 2));
|
|
4445
4749
|
}
|
|
4446
4750
|
|
|
4751
|
+
// src/commands/auth.ts
|
|
4752
|
+
import inquirer5 from "inquirer";
|
|
4753
|
+
import chalk11 from "chalk";
|
|
4754
|
+
import { execSync as execSync6 } from "child_process";
|
|
4755
|
+
|
|
4756
|
+
// src/lib/secrets.ts
|
|
4757
|
+
function hasSlackToken() {
|
|
4758
|
+
return !!process.env.SLACK_USER_TOKEN;
|
|
4759
|
+
}
|
|
4760
|
+
|
|
4761
|
+
// src/commands/auth.ts
|
|
4762
|
+
var SLACK_SCOPES = "chat:write,canvases:write";
|
|
4763
|
+
var REDIRECT_URI = "https://localhost:9876/callback";
|
|
4764
|
+
function getShellConfig() {
|
|
4765
|
+
const shell = process.env.SHELL || "/bin/zsh";
|
|
4766
|
+
if (shell.includes("zsh")) return "~/.zshrc";
|
|
4767
|
+
if (shell.includes("bash")) return "~/.bashrc";
|
|
4768
|
+
if (shell.includes("fish")) return "~/.config/fish/config.fish";
|
|
4769
|
+
return "~/.profile";
|
|
4770
|
+
}
|
|
4771
|
+
async function authSlackCommand() {
|
|
4772
|
+
console.log(chalk11.bold("\n\u{1F510} Slack Authentication Setup\n"));
|
|
4773
|
+
if (hasSlackToken()) {
|
|
4774
|
+
console.log(chalk11.green("\u2713 SLACK_USER_TOKEN is already set in your environment\n"));
|
|
4775
|
+
const { reauth } = await inquirer5.prompt([
|
|
4776
|
+
{
|
|
4777
|
+
type: "confirm",
|
|
4778
|
+
name: "reauth",
|
|
4779
|
+
message: "Re-authenticate anyway?",
|
|
4780
|
+
default: false
|
|
4781
|
+
}
|
|
4782
|
+
]);
|
|
4783
|
+
if (!reauth) {
|
|
4784
|
+
return;
|
|
4785
|
+
}
|
|
4786
|
+
console.log("");
|
|
4787
|
+
}
|
|
4788
|
+
const clientId = process.env.SLACK_CLIENT_ID;
|
|
4789
|
+
const clientSecret = process.env.SLACK_CLIENT_SECRET;
|
|
4790
|
+
if (!clientId || !clientSecret) {
|
|
4791
|
+
const shellConfig = getShellConfig();
|
|
4792
|
+
console.log(chalk11.yellow("Missing Slack app credentials.\n"));
|
|
4793
|
+
console.log(chalk11.gray('Get Client ID and Client Secret from 1Password ("Droid Slack App")'));
|
|
4794
|
+
console.log(chalk11.gray(`and add to your ${shellConfig}:
|
|
4795
|
+
`));
|
|
4796
|
+
console.log(chalk11.white(' export SLACK_CLIENT_ID="your-client-id"'));
|
|
4797
|
+
console.log(chalk11.white(' export SLACK_CLIENT_SECRET="your-client-secret"\n'));
|
|
4798
|
+
console.log(chalk11.gray("Then reload and run this again:\n"));
|
|
4799
|
+
console.log(chalk11.white(` source ${shellConfig} && droid auth slack
|
|
4800
|
+
`));
|
|
4801
|
+
return;
|
|
4802
|
+
}
|
|
4803
|
+
const authorizeUrl = `https://slack.com/oauth/v2/authorize?client_id=${clientId}&user_scope=${SLACK_SCOPES}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`;
|
|
4804
|
+
console.log(chalk11.yellow("Step 1: Authorize in Slack\n"));
|
|
4805
|
+
console.log(chalk11.gray(' Go to the "Droid Setup" canvas in #rnd-updates and click "Install Droid Slack App"'));
|
|
4806
|
+
console.log(chalk11.gray(" Or open this URL:\n"));
|
|
4807
|
+
console.log(chalk11.cyan(` ${authorizeUrl}
|
|
4808
|
+
`));
|
|
4809
|
+
console.log(chalk11.gray(" After clicking Allow, you'll be redirected to a URL that won't load."));
|
|
4810
|
+
console.log(chalk11.gray(' Copy the "code" parameter from the URL bar.\n'));
|
|
4811
|
+
const { code } = await inquirer5.prompt([
|
|
4812
|
+
{
|
|
4813
|
+
type: "input",
|
|
4814
|
+
name: "code",
|
|
4815
|
+
message: "Paste the code:",
|
|
4816
|
+
validate: (input) => input.length > 0 || "Code is required"
|
|
4817
|
+
}
|
|
4818
|
+
]);
|
|
4819
|
+
console.log(chalk11.yellow("\nStep 2: Exchanging code for token...\n"));
|
|
4820
|
+
try {
|
|
4821
|
+
const response = execSync6(
|
|
4822
|
+
`curl -s -X POST https://slack.com/api/oauth.v2.access -d "client_id=${clientId}" -d "client_secret=${clientSecret}" -d "code=${code}" -d "redirect_uri=${REDIRECT_URI}"`,
|
|
4823
|
+
{ encoding: "utf-8" }
|
|
4824
|
+
);
|
|
4825
|
+
const result = JSON.parse(response);
|
|
4826
|
+
if (!result.ok) {
|
|
4827
|
+
console.log(chalk11.red(`\u2717 Slack API error: ${result.error}`));
|
|
4828
|
+
if (result.error === "invalid_code") {
|
|
4829
|
+
console.log(chalk11.gray(" The code may have expired. Try again with a fresh code."));
|
|
4830
|
+
}
|
|
4831
|
+
return;
|
|
4832
|
+
}
|
|
4833
|
+
const token = result.authed_user?.access_token;
|
|
4834
|
+
if (!token) {
|
|
4835
|
+
console.log(chalk11.red("\u2717 No user token in response"));
|
|
4836
|
+
console.log(chalk11.gray(" Response:"), JSON.stringify(result, null, 2));
|
|
4837
|
+
return;
|
|
4838
|
+
}
|
|
4839
|
+
const shellConfig = getShellConfig();
|
|
4840
|
+
console.log(chalk11.green("\u2713 Got token!\n"));
|
|
4841
|
+
console.log(chalk11.yellow("Step 3: Save the token\n"));
|
|
4842
|
+
console.log(chalk11.gray(` Add to your ${shellConfig}:
|
|
4843
|
+
`));
|
|
4844
|
+
console.log(chalk11.white(` export SLACK_USER_TOKEN="${token}"
|
|
4845
|
+
`));
|
|
4846
|
+
console.log(chalk11.gray(" Then reload your shell:\n"));
|
|
4847
|
+
console.log(chalk11.white(` source ${shellConfig}
|
|
4848
|
+
`));
|
|
4849
|
+
} catch (error) {
|
|
4850
|
+
console.log(chalk11.red("\u2717 Failed to exchange code for token"));
|
|
4851
|
+
if (error instanceof Error) {
|
|
4852
|
+
console.log(chalk11.gray(` ${error.message}`));
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
async function authStatusCommand() {
|
|
4857
|
+
console.log(chalk11.bold("\n\u{1F510} Auth Status\n"));
|
|
4858
|
+
const clientId = process.env.SLACK_CLIENT_ID;
|
|
4859
|
+
const clientSecret = process.env.SLACK_CLIENT_SECRET;
|
|
4860
|
+
const hasCredentials = clientId && clientSecret;
|
|
4861
|
+
if (hasCredentials) {
|
|
4862
|
+
console.log(chalk11.green("\u2713 Slack app credentials configured"));
|
|
4863
|
+
} else {
|
|
4864
|
+
console.log(chalk11.yellow("\u2717 Slack app credentials missing"));
|
|
4865
|
+
console.log(chalk11.gray(" Run: droid auth slack"));
|
|
4866
|
+
}
|
|
4867
|
+
if (hasSlackToken()) {
|
|
4868
|
+
const token = process.env.SLACK_USER_TOKEN;
|
|
4869
|
+
const masked = token.slice(0, 10) + "..." + token.slice(-4);
|
|
4870
|
+
console.log(chalk11.green("\u2713 Slack user token configured"));
|
|
4871
|
+
console.log(chalk11.gray(` Token: ${masked}`));
|
|
4872
|
+
} else {
|
|
4873
|
+
console.log(chalk11.yellow("\u2717 Slack user token missing"));
|
|
4874
|
+
if (hasCredentials) {
|
|
4875
|
+
console.log(chalk11.gray(" Run: droid auth slack"));
|
|
4876
|
+
}
|
|
4877
|
+
}
|
|
4878
|
+
}
|
|
4879
|
+
|
|
4447
4880
|
// src/bin/droid.ts
|
|
4448
4881
|
var version = getVersion();
|
|
4449
4882
|
program.name("droid").description("Droid, teaching your AI new tricks").version(version);
|
|
@@ -4463,6 +4896,19 @@ program.command("uninstall <tool>").description("Uninstall a tool").action(unins
|
|
|
4463
4896
|
program.command("update").description("Update droid and installed tools").option("--tools", "Only update tools").option("--cli", "Only update the CLI").argument("[tool]", "Update a specific tool").action(updateCommand);
|
|
4464
4897
|
program.command("tui").description("Launch interactive TUI dashboard").action(tuiCommand);
|
|
4465
4898
|
program.command("exec <tool> <script>").description("Execute a tool script").argument("[args...]", "Arguments to pass to the script").allowUnknownOption().action(execCommand);
|
|
4899
|
+
var auth = program.command("auth").description("Set up authentication for external services");
|
|
4900
|
+
auth.command("slack").description("Set up Slack authentication for status updates").action(authSlackCommand);
|
|
4901
|
+
auth.command("status").description("Show authentication status").action(authStatusCommand);
|
|
4902
|
+
auth.action(authStatusCommand);
|
|
4903
|
+
if (configExists()) {
|
|
4904
|
+
const config = loadConfig();
|
|
4905
|
+
const newPlatforms = syncNewPlatforms(config);
|
|
4906
|
+
if (newPlatforms.length > 0) {
|
|
4907
|
+
console.log(chalk12.green(`\u2713 Detected new platform(s): ${newPlatforms.join(", ")}`));
|
|
4908
|
+
console.log(chalk12.gray(" Synced installed tools to new platform(s)\n"));
|
|
4909
|
+
saveConfig(config);
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4466
4912
|
if (process.argv.length === 2) {
|
|
4467
4913
|
tuiCommand();
|
|
4468
4914
|
} else {
|