@orderful/droid 0.36.0 → 0.38.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.
Files changed (109) hide show
  1. package/.claude-plugin/marketplace.json +1 -118
  2. package/.claude-plugin/plugin.json +49 -0
  3. package/AGENTS.md +4 -0
  4. package/CHANGELOG.md +59 -0
  5. package/README.md +53 -39
  6. package/dist/bin/droid.js +525 -212
  7. package/dist/commands/setup.d.ts.map +1 -1
  8. package/dist/commands/tui/components/PlatformBadges.d.ts.map +1 -1
  9. package/dist/commands/tui/components/SettingsDetails.d.ts.map +1 -1
  10. package/dist/commands/tui/hooks/useAppUpdate.d.ts.map +1 -1
  11. package/dist/commands/tui/views/SetupScreen.d.ts.map +1 -1
  12. package/dist/commands/update.d.ts.map +1 -1
  13. package/dist/index.js +345 -186
  14. package/dist/lib/agents.d.ts +4 -2
  15. package/dist/lib/agents.d.ts.map +1 -1
  16. package/dist/lib/migrations.d.ts.map +1 -1
  17. package/dist/lib/platform.codex.d.ts +36 -0
  18. package/dist/lib/platform.codex.d.ts.map +1 -0
  19. package/dist/lib/platforms.d.ts +30 -24
  20. package/dist/lib/platforms.d.ts.map +1 -1
  21. package/dist/lib/skills.d.ts +4 -2
  22. package/dist/lib/skills.d.ts.map +1 -1
  23. package/dist/lib/types.d.ts +2 -1
  24. package/dist/lib/types.d.ts.map +1 -1
  25. package/dist/tools/brain/.claude-plugin/plugin.json +8 -1
  26. package/dist/tools/brain/TOOL.yaml +1 -1
  27. package/dist/tools/brain/commands/brain.md +3 -2
  28. package/dist/tools/brain/skills/brain/SKILL.md +41 -10
  29. package/dist/tools/brain/skills/brain/references/templates.md +61 -0
  30. package/dist/tools/brain/skills/brain/references/workflows.md +78 -9
  31. package/dist/tools/brain/skills/brain-obsidian/SKILL.md +2 -0
  32. package/dist/tools/coach/.claude-plugin/plugin.json +6 -0
  33. package/dist/tools/coach/skills/coach/SKILL.md +3 -0
  34. package/dist/tools/code-review/.claude-plugin/plugin.json +12 -0
  35. package/dist/tools/code-review/skills/code-review/SKILL.md +2 -0
  36. package/dist/tools/codex/.claude-plugin/plugin.json +9 -0
  37. package/dist/tools/codex/skills/codex/SKILL.md +3 -0
  38. package/dist/tools/comments/.claude-plugin/plugin.json +7 -1
  39. package/dist/tools/comments/TOOL.yaml +1 -1
  40. package/dist/tools/comments/skills/comments/SKILL.md +11 -4
  41. package/dist/tools/droid/.claude-plugin/plugin.json +8 -1
  42. package/dist/tools/droid/TOOL.yaml +4 -2
  43. package/dist/tools/droid/commands/setup.md +125 -0
  44. package/dist/tools/droid/skills/droid/SKILL.md +117 -2
  45. package/dist/tools/plan/.claude-plugin/plugin.json +7 -1
  46. package/dist/tools/plan/TOOL.yaml +1 -1
  47. package/dist/tools/plan/commands/plan.md +3 -1
  48. package/dist/tools/plan/skills/plan/SKILL.md +23 -9
  49. package/dist/tools/plan/skills/plan/references/workflows.md +57 -20
  50. package/dist/tools/project/.claude-plugin/plugin.json +6 -0
  51. package/dist/tools/project/skills/project/SKILL.md +3 -0
  52. package/dist/tools/tech-design/.claude-plugin/plugin.json +7 -1
  53. package/dist/tools/tech-design/TOOL.yaml +1 -1
  54. package/dist/tools/tech-design/commands/tech-design.md +2 -0
  55. package/dist/tools/tech-design/skills/tech-design/SKILL.md +45 -13
  56. package/dist/tools/tech-design/skills/tech-design/references/publish.md +272 -216
  57. package/dist/tools/tech-design/skills/tech-design/references/start.md +50 -20
  58. package/dist/tools/wrapup/.claude-plugin/plugin.json +6 -0
  59. package/dist/tools/wrapup/skills/wrapup/SKILL.md +2 -0
  60. package/package.json +1 -1
  61. package/scripts/build-plugins.ts +154 -6
  62. package/src/bin/droid.ts +16 -0
  63. package/src/commands/setup.ts +107 -2
  64. package/src/commands/tui/components/PlatformBadges.tsx +1 -0
  65. package/src/commands/tui/components/SettingsDetails.tsx +1 -0
  66. package/src/commands/tui/hooks/useAppUpdate.ts +21 -1
  67. package/src/commands/tui/views/SetupScreen.tsx +10 -1
  68. package/src/commands/update.ts +21 -1
  69. package/src/lib/agents.ts +13 -2
  70. package/src/lib/migrations.ts +81 -9
  71. package/src/lib/platform.codex.ts +131 -0
  72. package/src/lib/platforms.ts +127 -6
  73. package/src/lib/skills.ts +53 -6
  74. package/src/lib/types.ts +1 -0
  75. package/src/tools/brain/.claude-plugin/plugin.json +8 -1
  76. package/src/tools/brain/TOOL.yaml +1 -1
  77. package/src/tools/brain/commands/brain.md +3 -2
  78. package/src/tools/brain/skills/brain/SKILL.md +41 -10
  79. package/src/tools/brain/skills/brain/references/templates.md +61 -0
  80. package/src/tools/brain/skills/brain/references/workflows.md +78 -9
  81. package/src/tools/brain/skills/brain-obsidian/SKILL.md +2 -0
  82. package/src/tools/coach/.claude-plugin/plugin.json +6 -0
  83. package/src/tools/coach/skills/coach/SKILL.md +3 -0
  84. package/src/tools/code-review/.claude-plugin/plugin.json +12 -0
  85. package/src/tools/code-review/skills/code-review/SKILL.md +2 -0
  86. package/src/tools/codex/.claude-plugin/plugin.json +9 -0
  87. package/src/tools/codex/skills/codex/SKILL.md +3 -0
  88. package/src/tools/comments/.claude-plugin/plugin.json +7 -1
  89. package/src/tools/comments/TOOL.yaml +1 -1
  90. package/src/tools/comments/skills/comments/SKILL.md +11 -4
  91. package/src/tools/droid/.claude-plugin/plugin.json +8 -1
  92. package/src/tools/droid/TOOL.yaml +4 -2
  93. package/src/tools/droid/commands/setup.md +125 -0
  94. package/src/tools/droid/skills/droid/SKILL.md +117 -2
  95. package/src/tools/plan/.claude-plugin/plugin.json +7 -1
  96. package/src/tools/plan/TOOL.yaml +1 -1
  97. package/src/tools/plan/commands/plan.md +3 -1
  98. package/src/tools/plan/skills/plan/SKILL.md +23 -9
  99. package/src/tools/plan/skills/plan/references/workflows.md +57 -20
  100. package/src/tools/project/.claude-plugin/plugin.json +6 -0
  101. package/src/tools/project/skills/project/SKILL.md +3 -0
  102. package/src/tools/tech-design/.claude-plugin/plugin.json +7 -1
  103. package/src/tools/tech-design/TOOL.yaml +1 -1
  104. package/src/tools/tech-design/commands/tech-design.md +2 -0
  105. package/src/tools/tech-design/skills/tech-design/SKILL.md +45 -13
  106. package/src/tools/tech-design/skills/tech-design/references/publish.md +272 -216
  107. package/src/tools/tech-design/skills/tech-design/references/start.md +50 -20
  108. package/src/tools/wrapup/.claude-plugin/plugin.json +6 -0
  109. package/src/tools/wrapup/skills/wrapup/SKILL.md +2 -0
@@ -50,14 +50,40 @@ The codex skill will:
50
50
  - UI/UX considerations (from DESIGN.md if present)
51
51
  - Key constraints or requirements
52
52
 
53
- ### 3. Create Research Doc via Brain Skill
54
-
55
- Use the brain skill to create a research document:
53
+ ### 3. Create Unified Tech Design Folder
54
+
55
+ Create a single folder to hold all tech design artifacts:
56
+
57
+ ```bash
58
+ # Get brain config
59
+ brain_config=$(droid config --get tools.brain)
60
+ brain_dir=$(echo "$brain_config" | grep -o '"brain_dir": *"[^"]*"' | cut -d'"' -f4)
61
+ inbox_folder=$(echo "$brain_config" | grep -o '"inbox_folder": *"[^"]*"' | cut -d'"' -f4)
62
+
63
+ # Determine base path
64
+ if [ -n "$inbox_folder" ]; then
65
+ base_path="$inbox_folder"
66
+ else
67
+ base_path="$brain_dir"
68
+ fi
69
+
70
+ # Create folder structure
71
+ design_folder="$base_path/tech-designs/{project}"
72
+ mkdir -p "$design_folder"
73
+ ```
56
74
 
75
+ **Folder structure:**
57
76
  ```
58
- /brain research tech-design-{project}-research
77
+ {brain_dir}/{inbox?}/tech-designs/{project}/
78
+ ├── research.md
79
+ ├── thought-doc.md
80
+ └── [diagrams, artifacts as needed]
59
81
  ```
60
82
 
83
+ ### 4. Create Research Doc
84
+
85
+ Create `research.md` in the design folder:
86
+
61
87
  **Research doc template:**
62
88
 
63
89
  ```markdown
@@ -81,7 +107,7 @@ Research for tech design: {brief description from PRD}
81
107
 
82
108
  ## Codebase Discoveries
83
109
 
84
- {This section populated in step 4}
110
+ {This section populated in step 5}
85
111
 
86
112
  ## Key Findings
87
113
 
@@ -174,13 +200,9 @@ Update the "Codebase Discoveries" section with specific discoveries:
174
200
  - Testing: {e.g., unit test setup, integration test patterns}
175
201
  ```
176
202
 
177
- ### 5. Create Thought Doc via Brain Skill
178
-
179
- Use the brain skill to create the planning document:
203
+ ### 5. Create Thought Doc
180
204
 
181
- ```
182
- /brain plan tech-design-{project}
183
- ```
205
+ Create `thought-doc.md` in the same folder:
184
206
 
185
207
  **Thought doc template:**
186
208
 
@@ -191,7 +213,7 @@ Use the brain skill to create the planning document:
191
213
  **Status:** exploring
192
214
  **Created:** {current date}
193
215
  **PRD:** [[codex:projects/{project}/PRD]]
194
- **Research:** [[tech-design-{project}-research]]
216
+ **Research:** [[research|Research doc in same folder]]
195
217
 
196
218
  Tech design for {project}.
197
219
 
@@ -203,7 +225,7 @@ Tech design for {project}.
203
225
  - Who it's for and why it matters
204
226
  - Key constraints or requirements
205
227
 
206
- See research doc for codebase context and discovered patterns.
228
+ See research.md for codebase context and discovered patterns.
207
229
 
208
230
  ## Proposal
209
231
 
@@ -214,15 +236,18 @@ See research doc for codebase context and discovered patterns.
214
236
  {What we don't know yet - to be explored}
215
237
  ```
216
238
 
217
- Set thought doc as active in brain's session tracking.
239
+ Set thought-doc.md path for session tracking (active design).
218
240
 
219
241
  ### 6. Provide Links to User
220
242
 
221
- After creating both docs, give the user the file paths:
243
+ After creating both docs, give the user the folder path:
222
244
 
223
245
  ```
224
- Research doc created: {full_path_to_research_doc}
225
- ✓ Tech design created: {full_path_to_thought_doc}
246
+ Tech design folder created: {design_folder}/
247
+
248
+ Files:
249
+ • research.md - Codex context and codebase discoveries
250
+ • thought-doc.md - Design iteration workspace (active)
226
251
 
227
252
  Research doc includes:
228
253
  • Codex project context from {project}
@@ -230,6 +255,7 @@ Research doc includes:
230
255
  • Technical stack notes
231
256
 
232
257
  You can continue working on this with:
258
+ • /tech-design search {project} - Resume this design later
233
259
  • /tech-design draft - Auto-generate sections from research
234
260
  • /tech-design think {topic} - Explore specific ideas
235
261
  • /brain add {text} - Quick additions to active doc
@@ -243,7 +269,7 @@ What would you like to do next?
243
269
  - **Brain not configured:** "Brain skill not configured. Please run `/brain` to set up your brain directory first."
244
270
  - **Codex not configured:** "Codex skill not configured. Please run `/codex` to set up your codex repository first."
245
271
  - **PRD missing:** Offer to create via `/codex new {project}` or continue without context
246
- - **Docs already exist:** "Tech design docs for '{project}' already exist. Open existing or create new?"
272
+ - **Folder already exists:** "Tech design for '{project}' already exists at {path}. Resume with `/tech-design search {project}` or create with different name?"
247
273
  - **Codebase exploration fails:** Gracefully note what couldn't be found, continue with what's available
248
274
 
249
275
  ## Example Output
@@ -257,8 +283,11 @@ Research findings:
257
283
  🔗 Identified 5 integration points (validation, storage, API)
258
284
  📦 Dependencies: 2 already available, 1 to add
259
285
 
260
- Research doc created: {brain_dir}/0-Inbox/research/tech-design-transaction-templates-research.md
261
- ✓ Tech design created: {brain_dir}/0-Inbox/plans/tech-design-transaction-templates.md
286
+ Tech design folder created: {brain_dir}/0-Inbox/tech-designs/transaction-templates/
287
+
288
+ Files:
289
+ • research.md - Codex context and codebase discoveries
290
+ • thought-doc.md - Design iteration workspace (active)
262
291
 
263
292
  Research doc includes:
264
293
  • Codex project context from transaction-templates
@@ -266,6 +295,7 @@ Research doc includes:
266
295
  • Technical stack notes
267
296
 
268
297
  You can continue working on this with:
298
+ • /tech-design search transaction-templates - Resume this design later
269
299
  • /tech-design draft - Auto-generate sections from research
270
300
  • /tech-design think {topic} - Explore specific ideas
271
301
  • /brain add {text} - Quick additions to active doc
@@ -12,5 +12,11 @@
12
12
  "droid",
13
13
  "ai",
14
14
  "wrapup"
15
+ ],
16
+ "skills": [
17
+ "./skills/wrapup/SKILL.md"
18
+ ],
19
+ "commands": [
20
+ "./commands/wrapup.md"
15
21
  ]
16
22
  }
@@ -18,6 +18,8 @@ Wrapup has no configuration of its own. It reads config from other installed too
18
18
 
19
19
  If these tools aren't configured, wrapup skips those artifacts and focuses on git state and conversation summary.
20
20
 
21
+ **Overrides:** This skill supports user-defined overrides. See `/droid` skill § Skill Overrides.
22
+
21
23
  ## Context Lifeboat Pattern
22
24
 
23
25
  This skill runs at the END of sessions when context pressure is highest.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.36.0",
3
+ "version": "0.38.0",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,9 +15,14 @@
15
15
  */
16
16
 
17
17
  import { mkdirSync, writeFileSync, existsSync, readdirSync, readFileSync } from 'fs';
18
- import { join } from 'path';
18
+ import { join, basename } from 'path';
19
19
  import { parse as parseYaml } from 'yaml';
20
20
 
21
+ interface CommandInclude {
22
+ name: string;
23
+ is_alias?: boolean;
24
+ }
25
+
21
26
  interface ToolManifest {
22
27
  name: string;
23
28
  description: string;
@@ -26,7 +31,7 @@ interface ToolManifest {
26
31
  system?: boolean;
27
32
  includes: {
28
33
  skills: Array<{ name: string; required: boolean; description?: string }>;
29
- commands: string[];
34
+ commands: CommandInclude[];
30
35
  agents: string[];
31
36
  };
32
37
  dependencies?: string[];
@@ -44,6 +49,9 @@ interface PluginJson {
44
49
  repository?: string;
45
50
  license?: string;
46
51
  keywords?: string[];
52
+ skills?: string[];
53
+ commands?: string[];
54
+ agents?: string[];
47
55
  }
48
56
 
49
57
  interface MarketplacePlugin {
@@ -111,11 +119,122 @@ function getToolDirs(): string[] {
111
119
  return entries
112
120
  .filter((e) => e.isDirectory())
113
121
  .map((e) => join(TOOLS_DIR, e.name))
114
- .filter((dir) => existsSync(join(dir, 'TOOL.yaml')));
122
+ .filter((dir) => existsSync(join(dir, 'TOOL.yaml')))
123
+ .sort(); // Ensure deterministic order across platforms
124
+ }
125
+
126
+ /**
127
+ * Get relative paths to skill files for plugin.json.
128
+ * Paths are relative to the tool's .claude-plugin directory.
129
+ */
130
+ function getSkillPaths(manifest: ToolManifest): string[] {
131
+ const skills = manifest.includes.skills ?? [];
132
+ return skills.map((skill) => `./skills/${skill.name}/SKILL.md`);
133
+ }
134
+
135
+ /**
136
+ * Get relative paths to command files for plugin.json.
137
+ * Excludes alias commands since they're just pointers to the main command.
138
+ */
139
+ function getCommandPaths(manifest: ToolManifest): string[] {
140
+ const commands = manifest.includes.commands ?? [];
141
+ return commands
142
+ .filter((cmd) => !cmd.is_alias)
143
+ .map((cmd) => `./commands/${cmd.name}.md`);
144
+ }
145
+
146
+ /**
147
+ * Get relative paths to agent files for plugin.json.
148
+ */
149
+ function getAgentPaths(manifest: ToolManifest): string[] {
150
+ const agents = manifest.includes.agents ?? [];
151
+ return agents.map((agent) => `./agents/${agent}.md`);
152
+ }
153
+
154
+ /**
155
+ * Get repo-root-relative paths to skill files for combined plugin.json.
156
+ */
157
+ function getSkillPathsFromRoot(manifest: ToolManifest, toolDir: string): string[] {
158
+ const skills = manifest.includes.skills ?? [];
159
+ return skills.map((skill) => `./${toolDir}/skills/${skill.name}/SKILL.md`);
160
+ }
161
+
162
+ /**
163
+ * Get repo-root-relative paths to command files for combined plugin.json.
164
+ * Excludes alias commands since they're just pointers to the main command.
165
+ */
166
+ function getCommandPathsFromRoot(manifest: ToolManifest, toolDir: string): string[] {
167
+ const commands = manifest.includes.commands ?? [];
168
+ return commands
169
+ .filter((cmd) => !cmd.is_alias)
170
+ .map((cmd) => `./${toolDir}/commands/${cmd.name}.md`);
171
+ }
172
+
173
+ /**
174
+ * Get repo-root-relative paths to agent files for combined plugin.json.
175
+ */
176
+ function getAgentPathsFromRoot(manifest: ToolManifest, toolDir: string): string[] {
177
+ const agents = manifest.includes.agents ?? [];
178
+ return agents.map((agent) => `./${toolDir}/agents/${agent}.md`);
179
+ }
180
+
181
+ /**
182
+ * Get the droid tool version for the combined plugin.json.
183
+ */
184
+ function getDroidVersion(toolDirs: string[]): string {
185
+ const droidDir = toolDirs.find((dir) => basename(dir) === 'droid');
186
+ if (droidDir) {
187
+ const manifest = loadToolManifest(droidDir);
188
+ if (manifest) return manifest.version;
189
+ }
190
+ return '0.0.0';
191
+ }
192
+
193
+ /**
194
+ * Build combined plugin.json at repo root for marketplace installations.
195
+ * Aggregates all tools' content with repo-root-relative paths.
196
+ */
197
+ function buildCombinedPlugin(toolDirs: string[]): void {
198
+ const allSkills: string[] = [];
199
+ const allCommands: string[] = [];
200
+ const allAgents: string[] = [];
201
+
202
+ for (const toolDir of toolDirs) {
203
+ const manifest = loadToolManifest(toolDir);
204
+ if (!manifest) continue;
205
+
206
+ allSkills.push(...getSkillPathsFromRoot(manifest, toolDir));
207
+ allCommands.push(...getCommandPathsFromRoot(manifest, toolDir));
208
+ allAgents.push(...getAgentPathsFromRoot(manifest, toolDir));
209
+ }
210
+
211
+ // Ensure directory exists
212
+ const marketplaceDir = '.claude-plugin';
213
+ mkdirSync(marketplaceDir, { recursive: true });
214
+
215
+ const combinedPlugin: PluginJson = {
216
+ name: 'droid',
217
+ version: getDroidVersion(toolDirs),
218
+ description: 'AI-powered development tools for Claude Code',
219
+ author: AUTHOR,
220
+ repository: REPO,
221
+ license: 'MIT',
222
+ keywords: ['droid', 'ai', 'tools'],
223
+ };
224
+
225
+ if (allSkills.length > 0) combinedPlugin.skills = allSkills;
226
+ if (allCommands.length > 0) combinedPlugin.commands = allCommands;
227
+ if (allAgents.length > 0) combinedPlugin.agents = allAgents;
228
+
229
+ writeFileSync(
230
+ join(marketplaceDir, 'plugin.json'),
231
+ JSON.stringify(combinedPlugin, null, 2) + '\n'
232
+ );
233
+ console.log(`Combined plugin.json created at .claude-plugin/plugin.json`);
115
234
  }
116
235
 
117
236
  function createPluginJson(manifest: ToolManifest, pluginName: string): PluginJson {
118
- return {
237
+ const json: PluginJson = {
119
238
  name: pluginName,
120
239
  version: manifest.version,
121
240
  description: manifest.description,
@@ -124,6 +243,26 @@ function createPluginJson(manifest: ToolManifest, pluginName: string): PluginJso
124
243
  license: 'MIT',
125
244
  keywords: [...new Set(['droid', 'ai', manifest.name])],
126
245
  };
246
+
247
+ // Add skills if present
248
+ const skills = getSkillPaths(manifest);
249
+ if (skills.length > 0) {
250
+ json.skills = skills;
251
+ }
252
+
253
+ // Add commands if present
254
+ const commands = getCommandPaths(manifest);
255
+ if (commands.length > 0) {
256
+ json.commands = commands;
257
+ }
258
+
259
+ // Add agents if present
260
+ const agents = getAgentPaths(manifest);
261
+ if (agents.length > 0) {
262
+ json.agents = agents;
263
+ }
264
+
265
+ return json;
127
266
  }
128
267
 
129
268
  function buildPluginManifest(toolDir: string): MarketplacePlugin | null {
@@ -165,15 +304,21 @@ function buildMarketplace(plugins: MarketplacePlugin[]): void {
165
304
  const marketplaceDir = '.claude-plugin';
166
305
  mkdirSync(marketplaceDir, { recursive: true });
167
306
 
307
+ // Only advertise "droid" in marketplace - Claude Code ignores the path field
308
+ // and always reads from repo root, so per-tool entries would fail with name
309
+ // mismatch errors. Users install "droid" to get all tools.
310
+ // Per-tool plugin.json files still exist for direct installs via --path.
311
+ const marketplacePlugins = plugins.filter((p) => p.name === 'droid');
312
+
168
313
  const marketplace: MarketplaceJson = {
169
314
  name: 'droid',
170
315
  description: 'AI-powered development tools for Claude Code',
171
316
  owner: AUTHOR,
172
- plugins: plugins.sort((a, b) => a.name.localeCompare(b.name)),
317
+ plugins: marketplacePlugins,
173
318
  };
174
319
 
175
320
  writeFileSync(join(marketplaceDir, 'marketplace.json'), JSON.stringify(marketplace, null, 2) + '\n');
176
- console.log(`\nMarketplace manifest created at .claude-plugin/marketplace.json with ${plugins.length} plugins`);
321
+ console.log(`\nMarketplace manifest created at .claude-plugin/marketplace.json with ${marketplacePlugins.length} plugin(s)`);
177
322
  }
178
323
 
179
324
  function main(): void {
@@ -193,6 +338,9 @@ function main(): void {
193
338
  // Build marketplace manifest
194
339
  buildMarketplace(plugins);
195
340
 
341
+ // Build combined plugin.json for marketplace installations
342
+ buildCombinedPlugin(toolDirs);
343
+
196
344
  console.log('\nPlugin manifest generation complete!');
197
345
  console.log(`\nTo test locally:`);
198
346
  console.log(` /plugin marketplace add ${process.cwd()}`);
package/src/bin/droid.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { program } from 'commander';
4
+ import chalk from 'chalk';
4
5
  import { setupCommand } from '../commands/setup';
5
6
  import { configCommand } from '../commands/config';
6
7
  import { skillsCommand } from '../commands/skills';
@@ -16,6 +17,8 @@ import {
16
17
  reposGetCommand,
17
18
  } from '../commands/repos';
18
19
  import { getVersion } from '../lib/version';
20
+ import { loadConfig, saveConfig, configExists } from '../lib/config';
21
+ import { syncNewPlatforms } from '../lib/platforms';
19
22
 
20
23
  const version = getVersion();
21
24
 
@@ -106,6 +109,19 @@ program
106
109
  .allowUnknownOption()
107
110
  .action(execCommand);
108
111
 
112
+ // Sync new platforms before running any command
113
+ // This ensures that newly installed platforms (e.g., Codex) get all existing tools
114
+ if (configExists()) {
115
+ const config = loadConfig();
116
+ const newPlatforms = syncNewPlatforms(config);
117
+
118
+ if (newPlatforms.length > 0) {
119
+ console.log(chalk.green(`✓ Detected new platform(s): ${newPlatforms.join(', ')}`));
120
+ console.log(chalk.gray(' Synced installed tools to new platform(s)\n'));
121
+ saveConfig(config);
122
+ }
123
+ }
124
+
109
125
  // If no command provided, launch TUI
110
126
  if (process.argv.length === 2) {
111
127
  tuiCommand();
@@ -4,10 +4,11 @@ import { execSync } from 'child_process';
4
4
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
5
5
  import { join } from 'path';
6
6
  import { homedir } from 'os';
7
- import { loadConfig, saveConfig, configExists } from '../lib/config';
7
+ import { loadConfig, saveConfig, configExists, getConfigDir } from '../lib/config';
8
8
  import { getBundledSkills } from '../lib/skills';
9
9
  import { Platform, BuiltInOutput, type DroidConfig, type OutputPreference } from '../lib/types';
10
- import { detectAllPlatforms } from '../lib/platforms';
10
+ import { detectAllPlatforms, createCodexSymlink } from '../lib/platforms';
11
+ import { getPlatformTools } from '../lib/types';
11
12
 
12
13
  // Re-export for external use
13
14
  export { detectAllPlatforms, getActivePlatforms } from '../lib/platforms';
@@ -16,6 +17,7 @@ const PLATFORM_LABELS: Record<Platform, string> = {
16
17
  [Platform.ClaudeCode]: 'Claude Code',
17
18
  [Platform.Cursor]: 'Cursor',
18
19
  [Platform.OpenCode]: 'OpenCode',
20
+ [Platform.OpenAICodex]: 'Codex',
19
21
  };
20
22
 
21
23
  /**
@@ -29,6 +31,89 @@ const DROID_PERMISSIONS = [
29
31
  'Grep(~/.droid/**)',
30
32
  ];
31
33
 
34
+ /**
35
+ * Template content for skill overrides
36
+ */
37
+ const SKILL_OVERRIDES_TEMPLATE = `---
38
+ overrides: []
39
+ ---
40
+
41
+ # Skill Override Template
42
+
43
+ Copy this file to \`{skill_name}.md\` to override a skill's behaviour.
44
+
45
+ ## Format
46
+
47
+ \`\`\`yaml
48
+ ---
49
+ overrides: [command1, command2]
50
+ ---
51
+ \`\`\`
52
+
53
+ Then add sections for each command you're overriding:
54
+
55
+ \`\`\`markdown
56
+ ## {command1}
57
+
58
+ Your custom instructions for this command...
59
+ \`\`\`
60
+
61
+ ---
62
+
63
+ ## Example: Override brain search with qmd
64
+
65
+ Create \`brain.md\` with:
66
+
67
+ \`\`\`yaml
68
+ ---
69
+ overrides: [search]
70
+ ---
71
+ \`\`\`
72
+
73
+ ## search
74
+
75
+ Use qmd for semantic search:
76
+
77
+ 1. Run \`qmd query "{query}" -c brain -n 10 --files\`
78
+ 2. Parse CSV output, extract filepath (field 3)
79
+ 3. If qmd fails, fall back to Glob
80
+
81
+ ---
82
+
83
+ ## Registration
84
+
85
+ After creating an override file, ask droid to register it:
86
+
87
+ > "Register my brain override"
88
+
89
+ Droid will:
90
+ 1. Parse the frontmatter to find which commands are overridden
91
+ 2. Update config.yaml with override metadata
92
+ 3. The skill will use your override on next invocation
93
+ `;
94
+
95
+ /**
96
+ * Create skill_overrides directory with template
97
+ */
98
+ function createSkillOverridesDir(): { created: boolean; alreadyExists: boolean } {
99
+ const overridesDir = join(getConfigDir(), 'skill_overrides');
100
+ const templatePath = join(overridesDir, '_template.md');
101
+
102
+ // Check if directory already exists with template
103
+ if (existsSync(templatePath)) {
104
+ return { created: false, alreadyExists: true };
105
+ }
106
+
107
+ // Create directory if needed
108
+ if (!existsSync(overridesDir)) {
109
+ mkdirSync(overridesDir, { recursive: true });
110
+ }
111
+
112
+ // Write template
113
+ writeFileSync(templatePath, SKILL_OVERRIDES_TEMPLATE, 'utf-8');
114
+ return { created: true, alreadyExists: false };
115
+ }
116
+
32
117
  /**
33
118
  * Try to get git username
34
119
  */
@@ -261,6 +346,14 @@ export async function setupCommand(): Promise<void> {
261
346
 
262
347
  console.log(chalk.green('\n✓ Config saved to ~/.droid/config.yaml'));
263
348
 
349
+ // Create skill_overrides directory with template
350
+ const { created: overridesCreated, alreadyExists: overridesExist } = createSkillOverridesDir();
351
+ if (overridesCreated) {
352
+ console.log(chalk.green('✓ Created ~/.droid/skill_overrides/ with template'));
353
+ } else if (overridesExist) {
354
+ console.log(chalk.gray(' Skill overrides directory already exists'));
355
+ }
356
+
264
357
  // Configure permissions for ALL active platforms
265
358
  for (const platform of activePlatforms) {
266
359
  const { added, alreadyPresent, error } = configurePlatformPermissions(platform);
@@ -277,6 +370,18 @@ export async function setupCommand(): Promise<void> {
277
370
  }
278
371
  }
279
372
 
373
+ // Create Codex symlinks for tracked tools only (not all skills in directory)
374
+ if (activePlatforms.includes(Platform.OpenAICodex)) {
375
+ const trackedTools = getPlatformTools(config);
376
+ const toolNames = Object.keys(trackedTools);
377
+ if (toolNames.length > 0) {
378
+ for (const skillName of toolNames) {
379
+ createCodexSymlink(skillName);
380
+ }
381
+ console.log(chalk.green('✓ Created Codex symlinks for installed tools'));
382
+ }
383
+ }
384
+
280
385
  // Show summary if multiple platforms
281
386
  if (activePlatforms.length > 1) {
282
387
  console.log(chalk.green(`\n✓ Will install to ${activePlatforms.length} platforms: ${activePlatforms.map(p => PLATFORM_LABELS[p]).join(', ')}`));
@@ -5,6 +5,7 @@ const PLATFORM_INFO: Record<Platform, { color: string; label: string }> = {
5
5
  [Platform.ClaudeCode]: { color: '#da7756', label: 'Claude' },
6
6
  [Platform.Cursor]: { color: '#22d3ee', label: 'Cursor' },
7
7
  [Platform.OpenCode]: { color: '#4ade80', label: 'OpenCode' },
8
+ [Platform.OpenAICodex]: { color: '#10b981', label: 'Codex' },
8
9
  };
9
10
 
10
11
  export function PlatformBadges({
@@ -14,6 +14,7 @@ const PLATFORM_LABELS: Record<Platform, string> = {
14
14
  [Platform.ClaudeCode]: 'Claude Code',
15
15
  [Platform.Cursor]: 'Cursor',
16
16
  [Platform.OpenCode]: 'OpenCode',
17
+ [Platform.OpenAICodex]: 'OpenAI Codex',
17
18
  };
18
19
 
19
20
  export function SettingsDetails({
@@ -1,7 +1,8 @@
1
1
  import { useState, useMemo } from 'react';
2
2
  import { useApp } from 'ink';
3
+ import { spawnSync } from 'child_process';
3
4
  import { getUpdateInfo, runUpdate, type UpdateInfo } from '../../../lib/version';
4
- import { setAutoUpdateConfig } from '../../../lib/config';
5
+ import { setAutoUpdateConfig, getAutoUpdateConfig } from '../../../lib/config';
5
6
 
6
7
  export interface UseAppUpdateOptions {
7
8
  onUpdateSuccess: (message: string) => void;
@@ -31,6 +32,24 @@ export function useAppUpdate({ onUpdateSuccess, onUpdateFailure }: UseAppUpdateO
31
32
  const blue = '\x1b[38;2;99;102;241m'; // #6366f1
32
33
  const dim = '\x1b[38;2;106;106;106m';
33
34
  const reset = '\x1b[0m';
35
+
36
+ // Check if auto-update tools is enabled
37
+ const autoUpdateConfig = getAutoUpdateConfig();
38
+ let toolStatus = `${blue}App updated.${reset}`;
39
+
40
+ if (autoUpdateConfig.tools) {
41
+ // Spawn NEW binary to sync tools (has new bundled manifests)
42
+ console.log('\nSyncing tools...');
43
+ const toolResult = spawnSync('droid', ['update', '--tools'], {
44
+ stdio: 'inherit',
45
+ timeout: 60000, // 60s timeout for tool sync
46
+ });
47
+
48
+ const toolsSynced = !toolResult.error && toolResult.status === 0;
49
+ toolStatus = toolsSynced
50
+ ? `${blue}App and tools updated.${reset}`
51
+ : `${blue}App updated.${reset} Tools sync incomplete - run ${blue}droid update --tools${reset} to retry.`;
52
+ }
34
53
  const message = `
35
54
  ${dim}────────────────────────────────────────────────────────${reset}
36
55
 
@@ -38,6 +57,7 @@ ${dim}────────────────────────
38
57
  ${dim}║${reset} ${blue}●${reset} ${blue}●${reset} ${dim}║${reset} ${blue}"It's quite possible this system${reset}
39
58
  ${dim}╚═╦═╦═╝${reset} ${blue}is now fully operational."${reset}
40
59
 
60
+ ${toolStatus}
41
61
  Run ${blue}droid${reset} to start the new version.
42
62
 
43
63
  ${dim}────────────────────────────────────────────────────────${reset}
@@ -3,9 +3,10 @@ import TextInput from 'ink-text-input';
3
3
  import { useState } from 'react';
4
4
  import { colors } from '../constants';
5
5
  import type { SetupStep } from '../types';
6
- import { Platform, BuiltInOutput, type DroidConfig } from '../../../lib/types';
6
+ import { Platform, BuiltInOutput, type DroidConfig, getPlatformTools } from '../../../lib/types';
7
7
  import { loadConfig, saveConfig, getAutoUpdateConfig, setAutoUpdateConfig } from '../../../lib/config';
8
8
  import { configurePlatformPermissions } from '../../setup';
9
+ import { createCodexSymlink } from '../../../lib/platforms';
9
10
 
10
11
  export interface SetupScreenProps {
11
12
  onComplete: () => void;
@@ -18,6 +19,7 @@ const PLATFORM_LABELS: Record<Platform, string> = {
18
19
  [Platform.ClaudeCode]: 'Claude Code',
19
20
  [Platform.Cursor]: 'Cursor',
20
21
  [Platform.OpenCode]: 'OpenCode',
22
+ [Platform.OpenAICodex]: 'Codex',
21
23
  };
22
24
 
23
25
  export function SetupScreen({ onComplete, onSkip, initialConfig, detectedPlatforms }: SetupScreenProps) {
@@ -94,6 +96,13 @@ export function SetupScreen({ onComplete, onSkip, initialConfig, detectedPlatfor
94
96
  for (const platform of detectedPlatforms) {
95
97
  configurePlatformPermissions(platform);
96
98
  }
99
+ // Create Codex symlinks for tracked tools only (not all skills in directory)
100
+ if (detectedPlatforms.includes(Platform.OpenAICodex)) {
101
+ const trackedTools = getPlatformTools(config);
102
+ for (const skillName of Object.keys(trackedTools)) {
103
+ createCodexSymlink(skillName);
104
+ }
105
+ }
97
106
  onComplete();
98
107
  }
99
108
  }