@orderful/droid 0.10.0 → 0.10.2

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 (41) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +2 -2
  3. package/dist/bin/droid.js +1 -1
  4. package/dist/bin/droid.js.map +1 -1
  5. package/dist/commands/install.d.ts.map +1 -1
  6. package/dist/commands/install.js +3 -1
  7. package/dist/commands/install.js.map +1 -1
  8. package/dist/commands/skills.d.ts.map +1 -1
  9. package/dist/commands/skills.js +3 -1
  10. package/dist/commands/skills.js.map +1 -1
  11. package/dist/commands/tui.d.ts.map +1 -1
  12. package/dist/commands/tui.js +40 -14
  13. package/dist/commands/tui.js.map +1 -1
  14. package/dist/lib/quotes.d.ts +11 -0
  15. package/dist/lib/quotes.d.ts.map +1 -0
  16. package/dist/lib/quotes.js +24 -0
  17. package/dist/lib/quotes.js.map +1 -0
  18. package/dist/skills/brain/SKILL.md +39 -17
  19. package/dist/skills/brain/commands/brain.md +1 -0
  20. package/dist/skills/brain/commands/scratchpad.md +1 -0
  21. package/dist/skills/brain/references/metadata.md +14 -13
  22. package/dist/skills/brain/references/naming.md +12 -10
  23. package/dist/skills/brain/references/templates.md +5 -5
  24. package/dist/skills/brain/references/workflows.md +16 -6
  25. package/dist/skills/comments/SKILL.md +2 -2
  26. package/dist/skills/comments/SKILL.yaml +1 -1
  27. package/package.json +1 -1
  28. package/src/bin/droid.ts +1 -1
  29. package/src/commands/install.ts +5 -1
  30. package/src/commands/skills.ts +5 -1
  31. package/src/commands/tui.tsx +42 -14
  32. package/src/lib/quotes.ts +24 -0
  33. package/src/skills/brain/SKILL.md +39 -17
  34. package/src/skills/brain/commands/brain.md +1 -0
  35. package/src/skills/brain/commands/scratchpad.md +1 -0
  36. package/src/skills/brain/references/metadata.md +14 -13
  37. package/src/skills/brain/references/naming.md +12 -10
  38. package/src/skills/brain/references/templates.md +5 -5
  39. package/src/skills/brain/references/workflows.md +16 -6
  40. package/src/skills/comments/SKILL.md +2 -2
  41. package/src/skills/comments/SKILL.yaml +1 -1
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Star Wars themed quotes for the TUI welcome screen.
3
+ * Some hint at specific skills available in droid.
4
+ */
5
+ export const quotes = [
6
+ "I'm probably the droid you are looking for.",
7
+ "These are the skills you're looking for.",
8
+ "I have a bad feeling about this, we might be too efficient.",
9
+ "May the source be with you.",
10
+ "The Force is strong with this one.",
11
+ "This is the way.",
12
+ "I'm fluent in over six million forms of communication. - `/comments`",
13
+ "Much to learn, you still have. - `/coach`",
14
+ "If into the archives you go, only knowledge will you find. - `/project`",
15
+ ];
16
+ /**
17
+ * Get a random quote from the list.
18
+ * Uses a simple random selection - each launch gets a fresh quote.
19
+ */
20
+ export function getRandomQuote() {
21
+ const index = Math.floor(Math.random() * quotes.length);
22
+ return quotes[index];
23
+ }
24
+ //# sourceMappingURL=quotes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quotes.js","sourceRoot":"","sources":["../../src/lib/quotes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,6CAA6C;IAC7C,0CAA0C;IAC1C,6DAA6D;IAC7D,6BAA6B;IAC7B,oCAAoC;IACpC,kBAAkB;IAClB,sEAAsE;IACtE,2CAA2C;IAC3C,yEAAyE;CAC1E,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
@@ -38,12 +38,13 @@ Ideas develop through iteration, not single prompts.
38
38
 
39
39
  **IMPORTANT:** Before using any default paths, ALWAYS read `~/.droid/skills/brain/overrides.yaml` first. If `brain_dir` is configured there, use that path. Only fall back to defaults if the file doesn't exist or lacks a `brain_dir` setting.
40
40
 
41
- | Setting | Default | Description |
42
- |---------|---------|-------------|
43
- | `brain_dir` | (see below) | Where docs are stored |
44
- | `inbox_folder` | (empty) | Optional subfolder for new docs (omit for flat structure) |
41
+ | Setting | Default | Description |
42
+ | -------------- | ----------- | --------------------------------------------------------- |
43
+ | `brain_dir` | (see below) | Where docs are stored |
44
+ | `inbox_folder` | (empty) | Optional subfolder for new docs (omit for flat structure) |
45
45
 
46
46
  Default `brain_dir` by AI tool (only if not configured):
47
+
47
48
  - **claude-code**: `~/.claude/brain`
48
49
  - **opencode**: `~/.config/opencode/brain`
49
50
 
@@ -52,28 +53,42 @@ Default `brain_dir` by AI tool (only if not configured):
52
53
  **Active doc:** Opening or creating a brain doc makes it "active" for the session. Subsequent `/brain add` commands append to it without specifying a path.
53
54
 
54
55
  **Doc types:**
56
+
55
57
  - `plan` - Structured: Context → Exploration → Decision → Next Steps
56
58
  - `research` - Open-ended exploration of a topic
57
59
  - `review` - Code review, document review, or any evaluation task
58
60
  - `note` - Quick capture (fire-and-forget, doesn't become active)
59
61
 
60
- **Comment conventions:** Use `@droid` / `@{user}` markers for async back-and-forth. If droid's `comments` skill is installed, use `/comments check` for full support.
62
+ **Comment conventions:** In markdown files, ALWAYS use blockquote `>` prefix for @mention comments:
63
+
64
+ - `> @droid` - User leaves comment for AI to address
65
+ - `> @{user}` - AI leaves comment for user to address
66
+
67
+ Example conversation:
68
+
69
+ ```markdown
70
+ > @droid Should we add caching here?
71
+
72
+ > @fry Yes, Redis would work well for this use case.
73
+ ```
74
+
75
+ Read user's configured mention from `~/.droid/config.yaml` → `user_mention` (e.g., `@fry`). If droid's `comments` skill is installed, use `/comments check` for full support.
61
76
 
62
77
  **Status lifecycle:** `exploring` → `drafting` → `decided` → `done`
63
78
 
64
79
  ## Commands
65
80
 
66
- | Command | Action |
67
- |---------|--------|
68
- | `/brain` | List recent docs or create new |
69
- | `/brain {topic}` | **Search** for existing doc (fuzzy match) → becomes active |
70
- | `/brain plan {topic}` | Create planning doc (requires `plan` keyword) |
71
- | `/brain research {topic}` | Create research doc (requires `research` keyword) |
72
- | `/brain review {topic}` | Create review doc (requires `review` keyword) |
73
- | `/brain note {text}` | Quick capture (standalone, doesn't become active) |
74
- | `/brain add {text}` | Append to active doc |
75
- | `/brain check` | Address @droid comments in active doc |
76
- | `/brain done` | Finalize active doc, update status |
81
+ | Command | Action |
82
+ | ------------------------- | ---------------------------------------------------------- |
83
+ | `/brain` | List recent docs or create new |
84
+ | `/brain {topic}` | **Search** for existing doc (fuzzy match) → becomes active |
85
+ | `/brain plan {topic}` | Create planning doc (requires `plan` keyword) |
86
+ | `/brain research {topic}` | Create research doc (requires `research` keyword) |
87
+ | `/brain review {topic}` | Create review doc (requires `review` keyword) |
88
+ | `/brain note {text}` | Quick capture (standalone, doesn't become active) |
89
+ | `/brain add {text}` | Append to active doc |
90
+ | `/brain check` | Address @droid comments in active doc |
91
+ | `/brain done` | Finalize active doc, update status |
77
92
 
78
93
  **IMPORTANT:** The default action for `/brain {topic}` is to **SEARCH** for existing docs, NOT create. Only use `/brain plan {topic}` or `/brain research {topic}` when the user explicitly wants to create a new doc.
79
94
 
@@ -115,7 +130,7 @@ Full procedure: `references/workflows.md` § Adding
115
130
 
116
131
  **Trigger:** `/brain check`
117
132
 
118
- **TLDR:** Find `> @droid` comments in active doc, address each one.
133
+ **TLDR:** Find `> @droid` comments in active doc, address each one with `> @{user_mention}` response (always use blockquote `>`).
119
134
 
120
135
  Full procedure: `references/workflows.md` § Checking
121
136
 
@@ -132,11 +147,18 @@ Full procedure: `references/workflows.md` § Finalizing
132
147
  Before creating or modifying brain docs, check if any `brain-*` extension skills are installed:
133
148
 
134
149
  ```
150
+ # Claude Code
135
151
  ~/.claude/skills/brain-*/SKILL.md
152
+
153
+ # OpenCode
154
+ ~/.config/opencode/skills/brain-*/SKILL.md
155
+
156
+ # Shared (droid config)
136
157
  ~/.droid/skills/brain-*/SKILL.md
137
158
  ```
138
159
 
139
160
  If found (e.g., `brain-obsidian`), **ALWAYS** use the extension's templates, workflows, and configuration instead of this skill's defaults. Extensions may provide:
161
+
140
162
  - Alternative template formats (YAML frontmatter, wikilinks)
141
163
  - Folder organization (PARA structure)
142
164
  - Additional commands or integrations
@@ -42,6 +42,7 @@ $ARGUMENTS
42
42
  ## Behavior
43
43
 
44
44
  Refer to the brain skill for:
45
+
45
46
  - **Opening**: How to fuzzy-match, handle multiple matches, set active
46
47
  - **Creating**: Template structure by preset, naming conventions
47
48
  - **Notes**: Quick capture workflow
@@ -42,6 +42,7 @@ $ARGUMENTS
42
42
  ## Behavior
43
43
 
44
44
  Refer to the brain skill for:
45
+
45
46
  - **Opening**: How to fuzzy-match, handle multiple matches, set active
46
47
  - **Creating**: Template structure by preset, naming conventions
47
48
  - **Notes**: Quick capture workflow
@@ -20,12 +20,12 @@ Simple and portable. Works anywhere markdown is supported.
20
20
 
21
21
  ## Fields
22
22
 
23
- | Field | Required | Description |
24
- |-------|----------|-------------|
25
- | `Type` | Yes | Doc type: `plan`, `research`, `review`, or `note` |
26
- | `Status` | Yes (except notes) | Current lifecycle stage |
27
- | `Created` | Yes | Creation date |
28
- | `Updated` | No | Last modification date (add when doc changes) |
23
+ | Field | Required | Description |
24
+ | --------- | ------------------ | ------------------------------------------------- |
25
+ | `Type` | Yes | Doc type: `plan`, `research`, `review`, or `note` |
26
+ | `Status` | Yes (except notes) | Current lifecycle stage |
27
+ | `Created` | Yes | Creation date |
28
+ | `Updated` | No | Last modification date (add when doc changes) |
29
29
 
30
30
  ## Status Lifecycle
31
31
 
@@ -37,13 +37,13 @@ exploring → drafting → decided → done
37
37
 
38
38
  ### Status Definitions
39
39
 
40
- | Status | Meaning | Typical Actions |
41
- |--------|---------|-----------------|
42
- | `exploring` | Initial discovery, gathering information | Research, ask questions, explore options |
43
- | `drafting` | Forming opinions, narrowing options | Document trade-offs, propose approaches |
44
- | `decided` | Decision made, ready to act | Document decision rationale, plan implementation |
45
- | `done` | Work complete, doc is reference material | Archive or link from project |
46
- | `stale` | Abandoned or outdated | Review for archival or deletion |
40
+ | Status | Meaning | Typical Actions |
41
+ | ----------- | ---------------------------------------- | ------------------------------------------------ |
42
+ | `exploring` | Initial discovery, gathering information | Research, ask questions, explore options |
43
+ | `drafting` | Forming opinions, narrowing options | Document trade-offs, propose approaches |
44
+ | `decided` | Decision made, ready to act | Document decision rationale, plan implementation |
45
+ | `done` | Work complete, doc is reference material | Archive or link from project |
46
+ | `stale` | Abandoned or outdated | Review for archival or deletion |
47
47
 
48
48
  ### Transitions
49
49
 
@@ -55,5 +55,6 @@ exploring → drafting → decided → done
55
55
  ## Updating Metadata
56
56
 
57
57
  When modifying a doc:
58
+
58
59
  1. Add or update the `Updated` field to current date
59
60
  2. Update `Status` if the work has progressed
@@ -10,10 +10,10 @@ Conventions for naming brain docs.
10
10
 
11
11
  ## Components
12
12
 
13
- | Component | Description | Examples |
14
- |-----------|-------------|----------|
15
- | `{type}` | Doc type: `plan`, `research`, `note` | `plan`, `research`, `note` |
16
- | `{topic-slug}` | Lowercase, hyphen-separated topic | `auth-refactor`, `caching-strategies` |
13
+ | Component | Description | Examples |
14
+ | -------------- | ------------------------------------ | ------------------------------------- |
15
+ | `{type}` | Doc type: `plan`, `research`, `note` | `plan`, `research`, `note` |
16
+ | `{topic-slug}` | Lowercase, hyphen-separated topic | `auth-refactor`, `caching-strategies` |
17
17
 
18
18
  ## Slug Rules
19
19
 
@@ -25,16 +25,17 @@ Conventions for naming brain docs.
25
25
 
26
26
  ## Examples
27
27
 
28
- | Input | Filename |
29
- |-------|----------|
30
- | `/brain plan Auth Refactor` | `plan-auth-refactor.md` |
31
- | `/brain research Caching Strategies` | `research-caching-strategies.md` |
32
- | `/brain plan Transaction Template Rendering` | `plan-transaction-template-rendering.md` |
33
- | `/brain note Remember to check rate limits` | `note-20250115-remember-to-check-rate.md` |
28
+ | Input | Filename |
29
+ | -------------------------------------------- | ----------------------------------------- |
30
+ | `/brain plan Auth Refactor` | `plan-auth-refactor.md` |
31
+ | `/brain research Caching Strategies` | `research-caching-strategies.md` |
32
+ | `/brain plan Transaction Template Rendering` | `plan-transaction-template-rendering.md` |
33
+ | `/brain note Remember to check rate limits` | `note-20250115-remember-to-check-rate.md` |
34
34
 
35
35
  ## Notes
36
36
 
37
37
  For notes, include the date in the filename:
38
+
38
39
  ```
39
40
  note-{YYYYMMDD}-{slug}.md
40
41
  ```
@@ -44,5 +45,6 @@ This prevents collisions and allows chronological sorting.
44
45
  ## Uniqueness
45
46
 
46
47
  If a file with the generated name already exists:
48
+
47
49
  1. Check if user wants to open existing doc
48
50
  2. Or append a numeric suffix: `plan-auth-refactor-2.md`
@@ -94,9 +94,9 @@ Overall assessment.
94
94
 
95
95
  ## Template Variables
96
96
 
97
- | Variable | Description | Example |
98
- |----------|-------------|---------|
99
- | `{Topic}` | Doc title from user input | "Auth Refactor" |
100
- | `{date}` | Current date (YYYY-MM-DD) | "2025-01-15" |
97
+ | Variable | Description | Example |
98
+ | --------------------- | ------------------------- | --------------------------------------- |
99
+ | `{Topic}` | Doc title from user input | "Auth Refactor" |
100
+ | `{date}` | Current date (YYYY-MM-DD) | "2025-01-15" |
101
101
  | `{brief description}` | Context from conversation | "refactoring the authentication system" |
102
- | `{content}` | User-provided text | (for notes) |
102
+ | `{content}` | User-provided text | (for notes) |
@@ -13,9 +13,11 @@ Detailed procedures for each brain operation.
13
13
  - Use `brain_dir` if configured, otherwise use default for current AI tool
14
14
 
15
15
  2. **Search for matches**
16
+
16
17
  ```
17
18
  Glob: {brain_dir}/**/*{topic}*.md
18
19
  ```
20
+
19
21
  Fuzzy match the topic against doc filenames
20
22
 
21
23
  3. **Handle results:**
@@ -59,11 +61,16 @@ Detailed procedures for each brain operation.
59
61
  - Any constraints or requirements mentioned?
60
62
  - Related work or prior decisions?
61
63
 
62
- 7. **Write the doc** with template structure and initial context
64
+ 7. **Add placeholder comments** for sections needing user input:
65
+ - Read user's mention from `~/.droid/config.yaml` → `user_mention`
66
+ - Use `> @{user_mention}` for placeholders (e.g., `> @fry - Please provide requirements`)
67
+ - Do NOT use `> @droid` - that's for user-to-AI comments
63
68
 
64
- 8. **Set as active doc**
69
+ 8. **Write the doc** with template structure and initial context
65
70
 
66
- 9. **Confirm creation** and summarize what was captured
71
+ 9. **Set as active doc**
72
+
73
+ 10. **Confirm creation** and summarize what was captured
67
74
 
68
75
  ## Notes
69
76
 
@@ -105,8 +112,8 @@ Detailed procedures for each brain operation.
105
112
  2. **Read current content** of active doc
106
113
 
107
114
  3. **Append new section:**
108
- ```markdown
109
115
 
116
+ ```markdown
110
117
  ---
111
118
 
112
119
  ## Update ({date})
@@ -139,8 +146,9 @@ Detailed procedures for each brain operation.
139
146
  4. **For each comment:**
140
147
  - Show the comment and surrounding context
141
148
  - Address the question/request
142
- - Add response as `> @{user_mention}` reply
143
- - Or resolve inline if it was a simple note
149
+ - Add response as blockquote with user's mention: `> @{user_mention} Your response here`
150
+ - Example: `> @fry Yes, the index covers that query pattern.`
151
+ - IMPORTANT: Always include the `>` prefix to maintain blockquote formatting
144
152
 
145
153
  5. **Update the doc** with responses
146
154
 
@@ -182,9 +190,11 @@ Detailed procedures for each brain operation.
182
190
  - Use `brain_dir` if configured, otherwise use default for current AI tool
183
191
 
184
192
  2. **Find recent docs:**
193
+
185
194
  ```
186
195
  Glob: {brain_dir}/**/*.md
187
196
  ```
197
+
188
198
  Sort by modification time, limit to 10-15
189
199
 
190
200
  3. **Present list** with:
@@ -98,8 +98,8 @@ user_mention: "@fry"
98
98
  # Additional AI mentions to recognize (e.g., if you're used to @claude)
99
99
  ai_mentions: "@claude"
100
100
 
101
- # Keep comments after addressing them (default: false)
102
- preserve_comments: false
101
+ # Keep comments after addressing them (default: true)
102
+ preserve_comments: true
103
103
  ```
104
104
 
105
105
  ## File Type Support
@@ -15,7 +15,7 @@ config_schema:
15
15
  preserve_comments:
16
16
  type: boolean
17
17
  description: Keep original comments after addressing (vs removing them)
18
- default: false
18
+ default: true
19
19
  examples:
20
20
  - title: "Action request"
21
21
  code: |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
package/src/bin/droid.ts CHANGED
@@ -14,7 +14,7 @@ const version = getVersion();
14
14
 
15
15
  program
16
16
  .name('droid')
17
- .description('AI workflow toolkit - teaching your droid new tricks')
17
+ .description('Droid, teaching your AI new tricks')
18
18
  .version(version);
19
19
 
20
20
  program
@@ -34,7 +34,11 @@ export async function installCommand(skillName: string): Promise<void> {
34
34
  // Check if skill has configurable options
35
35
  const manifest = loadSkillManifest(join(getBundledSkillsDir(), skillName));
36
36
  if (manifest?.config_schema && Object.keys(manifest.config_schema).length > 0) {
37
- await promptForSkillConfig(skillName, manifest.config_schema, true);
37
+ // If any config option lacks a default, go straight to config (it's required)
38
+ const hasRequiredConfig = Object.values(manifest.config_schema).some(
39
+ (option) => option.default === undefined
40
+ );
41
+ await promptForSkillConfig(skillName, manifest.config_schema, !hasRequiredConfig);
38
42
  }
39
43
 
40
44
  // Show next steps
@@ -127,7 +127,11 @@ export async function skillsCommand(): Promise<void> {
127
127
  // Prompt for config if skill has options
128
128
  const manifest = loadSkillManifest(join(getBundledSkillsDir(), skill.name));
129
129
  if (manifest?.config_schema && Object.keys(manifest.config_schema).length > 0) {
130
- await promptForSkillConfig(skill.name, manifest.config_schema, true);
130
+ // If any config option lacks a default, go straight to config (it's required)
131
+ const hasRequiredConfig = Object.values(manifest.config_schema).some(
132
+ (option) => option.default === undefined
133
+ );
134
+ await promptForSkillConfig(skill.name, manifest.config_schema, !hasRequiredConfig);
131
135
  }
132
136
  } else {
133
137
  console.log(chalk.red(`\n✗ ${result.message}`));
@@ -19,6 +19,7 @@ import { configExists, loadConfig, saveConfig, loadSkillOverrides, saveSkillOver
19
19
  import { configureAIToolPermissions } from './setup.js';
20
20
  import { AITool, BuiltInOutput, ConfigOptionType, type DroidConfig, type OutputPreference, type SkillManifest, type ConfigOption, type SkillOverrides } from '../lib/types.js';
21
21
  import { getVersion, getUpdateInfo, runUpdate, type UpdateInfo } from '../lib/version.js';
22
+ import { getRandomQuote } from '../lib/quotes.js';
22
23
 
23
24
  type Tab = 'skills' | 'commands' | 'agents' | 'settings';
24
25
  type View = 'welcome' | 'setup' | 'menu' | 'detail' | 'configure' | 'readme';
@@ -100,20 +101,28 @@ function getCommandsFromSkills(): Command[] {
100
101
  return commands;
101
102
  }
102
103
 
103
- function detectAITool(): AITool | null {
104
+ function detectInstalledAITools(): Set<AITool> {
105
+ const installed = new Set<AITool>();
104
106
  try {
105
107
  execSync('claude --version', { stdio: 'ignore' });
106
- return AITool.ClaudeCode;
108
+ installed.add(AITool.ClaudeCode);
107
109
  } catch {
108
110
  // Claude Code not found
109
111
  }
110
112
  try {
111
113
  execSync('opencode --version', { stdio: 'ignore' });
112
- return AITool.OpenCode;
114
+ installed.add(AITool.OpenCode);
113
115
  } catch {
114
116
  // OpenCode not found
115
117
  }
116
- return null;
118
+ return installed;
119
+ }
120
+
121
+ function getDefaultAITool(): AITool {
122
+ const installed = detectInstalledAITools();
123
+ if (installed.has(AITool.ClaudeCode)) return AITool.ClaudeCode;
124
+ if (installed.has(AITool.OpenCode)) return AITool.OpenCode;
125
+ return AITool.ClaudeCode;
117
126
  }
118
127
 
119
128
  function getOutputOptions(): Array<{ label: string; value: OutputPreference }> {
@@ -183,12 +192,14 @@ function findAgentSourcePath(agentName: string): string | null {
183
192
  interface WelcomeScreenProps {
184
193
  onContinue: () => void;
185
194
  onUpdate: () => void;
195
+ onExit: () => void;
186
196
  updateInfo: UpdateInfo;
187
197
  isUpdating: boolean;
188
198
  }
189
199
 
190
- function WelcomeScreen({ onContinue, onUpdate, updateInfo, isUpdating }: WelcomeScreenProps) {
200
+ function WelcomeScreen({ onContinue, onUpdate, onExit, updateInfo, isUpdating }: WelcomeScreenProps) {
191
201
  const [selectedButton, setSelectedButton] = useState(0);
202
+ const welcomeQuote = useMemo(() => getRandomQuote(), []);
192
203
 
193
204
  useInput((input, key) => {
194
205
  if (isUpdating) return;
@@ -205,12 +216,15 @@ function WelcomeScreen({ onContinue, onUpdate, updateInfo, isUpdating }: Welcome
205
216
  }
206
217
  }
207
218
  if (input === 'q') {
208
- onContinue();
219
+ onExit();
209
220
  }
210
221
  } else {
211
- if (key.return || input === 'q') {
222
+ if (key.return) {
212
223
  onContinue();
213
224
  }
225
+ if (input === 'q') {
226
+ onExit();
227
+ }
214
228
  }
215
229
  });
216
230
 
@@ -238,7 +252,7 @@ function WelcomeScreen({ onContinue, onUpdate, updateInfo, isUpdating }: Welcome
238
252
  <Text color={colors.textDim}> </Text>
239
253
  <Text color={hasUpdate ? '#eab308' : colors.primary}>●</Text>
240
254
  <Text color={colors.textDim}> ║ </Text>
241
- <Text color={colors.textMuted}>Teaching your droid new tricks</Text>
255
+ <Text color={colors.textMuted}>Droid, teaching your AI new tricks</Text>
242
256
  </Text>
243
257
  <Text>
244
258
  <Text color={colors.textDim}>╚═╦═╦═╝ </Text>
@@ -293,7 +307,7 @@ function WelcomeScreen({ onContinue, onUpdate, updateInfo, isUpdating }: Welcome
293
307
  <>
294
308
  <Box marginTop={2} marginBottom={1}>
295
309
  <Text backgroundColor={colors.primary} color="#ffffff" bold>
296
- {' '}I'm probably the droid you are looking for.{' '}
310
+ {` ${welcomeQuote} `}
297
311
  </Text>
298
312
  </Box>
299
313
 
@@ -314,7 +328,7 @@ interface SetupScreenProps {
314
328
  function SetupScreen({ onComplete, onSkip, initialConfig }: SetupScreenProps) {
315
329
  const [step, setStep] = useState<SetupStep>('ai_tool');
316
330
  const [aiTool, setAITool] = useState<AITool>(
317
- initialConfig?.ai_tool || detectAITool() || AITool.ClaudeCode
331
+ initialConfig?.ai_tool || getDefaultAITool()
318
332
  );
319
333
  const [userMention, setUserMention] = useState(initialConfig?.user_mention || '@user');
320
334
  const [outputPreference, setOutputPreference] = useState<OutputPreference>(
@@ -323,6 +337,8 @@ function SetupScreen({ onComplete, onSkip, initialConfig }: SetupScreenProps) {
323
337
  const [selectedIndex, setSelectedIndex] = useState(0);
324
338
  const [error, setError] = useState<string | null>(null);
325
339
 
340
+ // Cache detection results once on mount (avoids re-running execSync on every render)
341
+ const installedTools = useMemo(() => detectInstalledAITools(), []);
326
342
  const aiToolOptions = useMemo(() => [
327
343
  { label: 'Claude Code', value: AITool.ClaudeCode },
328
344
  { label: 'OpenCode', value: AITool.OpenCode },
@@ -418,7 +434,7 @@ function SetupScreen({ onComplete, onSkip, initialConfig }: SetupScreenProps) {
418
434
  <Text key={option.value}>
419
435
  <Text color={colors.textDim}>{index === selectedIndex ? '>' : ' '} </Text>
420
436
  <Text color={index === selectedIndex ? colors.text : colors.textMuted}>{option.label}</Text>
421
- {option.value === detectAITool() && <Text color={colors.success}> (detected)</Text>}
437
+ {installedTools.has(option.value) && <Text color={colors.success}> (detected)</Text>}
422
438
  </Text>
423
439
  ))}
424
440
  </Box>
@@ -1358,7 +1374,8 @@ function App() {
1358
1374
  setTimeout(() => {
1359
1375
  const result = runUpdate();
1360
1376
  if (result.success) {
1361
- // Exit and let user restart with new version
1377
+ // Print success message before exiting
1378
+ console.log('\n✅ Update complete! Run `droid` to start the new version.\n');
1362
1379
  exit();
1363
1380
  } else {
1364
1381
  setIsUpdating(false);
@@ -1521,8 +1538,18 @@ function App() {
1521
1538
  type: result.success ? 'success' : 'error',
1522
1539
  });
1523
1540
  if (result.success) {
1524
- setView('menu');
1525
- setSelectedAction(0);
1541
+ // Check if skill has required config (options without defaults)
1542
+ const configSchema = skill.config_schema || {};
1543
+ const hasRequiredConfig = Object.values(configSchema).some(
1544
+ (option) => (option as ConfigOption).default === undefined
1545
+ );
1546
+ if (hasRequiredConfig) {
1547
+ // Go to configure view for required setup
1548
+ setView('configure');
1549
+ } else {
1550
+ setView('menu');
1551
+ setSelectedAction(0);
1552
+ }
1526
1553
  }
1527
1554
  }
1528
1555
  }
@@ -1595,6 +1622,7 @@ function App() {
1595
1622
  updateInfo={updateInfo}
1596
1623
  isUpdating={isUpdating}
1597
1624
  onUpdate={handleUpdate}
1625
+ onExit={exit}
1598
1626
  onContinue={() => {
1599
1627
  // If no config exists, show setup first
1600
1628
  if (!configExists()) {
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Star Wars themed quotes for the TUI welcome screen.
3
+ * Some hint at specific skills available in droid.
4
+ */
5
+ export const quotes = [
6
+ "I'm probably the droid you are looking for.",
7
+ "These are the skills you're looking for.",
8
+ "I have a bad feeling about this, we might be too efficient.",
9
+ "May the source be with you.",
10
+ "The Force is strong with this one.",
11
+ "This is the way.",
12
+ "I'm fluent in over six million forms of communication. - `/comments`",
13
+ "Much to learn, you still have. - `/coach`",
14
+ "If into the archives you go, only knowledge will you find. - `/project`",
15
+ ];
16
+
17
+ /**
18
+ * Get a random quote from the list.
19
+ * Uses a simple random selection - each launch gets a fresh quote.
20
+ */
21
+ export function getRandomQuote(): string {
22
+ const index = Math.floor(Math.random() * quotes.length);
23
+ return quotes[index];
24
+ }