@orderful/droid 0.2.0 → 0.3.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.
@@ -17,6 +17,23 @@ export function getVersion() {
17
17
  return '0.0.0';
18
18
  }
19
19
  }
20
+ /**
21
+ * Compare two semver versions
22
+ * Returns: 1 if a > b, -1 if a < b, 0 if equal
23
+ */
24
+ export function compareSemver(a, b) {
25
+ const partsA = a.split('.').map(Number);
26
+ const partsB = b.split('.').map(Number);
27
+ for (let i = 0; i < 3; i++) {
28
+ const numA = partsA[i] || 0;
29
+ const numB = partsB[i] || 0;
30
+ if (numA > numB)
31
+ return 1;
32
+ if (numA < numB)
33
+ return -1;
34
+ }
35
+ return 0;
36
+ }
20
37
  /**
21
38
  * Check for updates (non-blocking)
22
39
  * Shows a message if a new version is available
@@ -29,7 +46,8 @@ export async function checkForUpdates() {
29
46
  encoding: 'utf-8',
30
47
  timeout: 3000,
31
48
  }).trim();
32
- if (latestVersion && latestVersion !== currentVersion) {
49
+ // Only show update message if latest is actually newer
50
+ if (latestVersion && compareSemver(latestVersion, currentVersion) > 0) {
33
51
  console.log(chalk.yellow(`\n⚠️ A new version of droid is available (${currentVersion} → ${latestVersion})`));
34
52
  console.log(chalk.yellow(` Run ${chalk.bold('droid update')} to upgrade\n`));
35
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/lib/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAE9D;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;QAC/D,OAAO,GAAG,CAAC,OAAiB,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,cAAc,GAAG,UAAU,EAAE,CAAC;QAEpC,uCAAuC;QACvC,MAAM,aAAa,GAAG,QAAQ,CAAC,8CAA8C,EAAE;YAC7E,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,IAAI,aAAa,IAAI,aAAa,KAAK,cAAc,EAAE,CAAC;YACtD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,8CAA8C,cAAc,MAAM,aAAa,GAAG,CACnF,CACF,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/lib/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAE9D;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;QAC/D,OAAO,GAAG,CAAC,OAAiB,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS,EAAE,CAAS;IAChD,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAExC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,IAAI,GAAG,IAAI;YAAE,OAAO,CAAC,CAAC;QAC1B,IAAI,IAAI,GAAG,IAAI;YAAE,OAAO,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,cAAc,GAAG,UAAU,EAAE,CAAC;QAEpC,uCAAuC;QACvC,MAAM,aAAa,GAAG,QAAQ,CAAC,8CAA8C,EAAE;YAC7E,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,uDAAuD;QACvD,IAAI,aAAa,IAAI,aAAa,CAAC,aAAa,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;YACtE,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,8CAA8C,cAAc,MAAM,aAAa,GAAG,CACnF,CACF,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;AACH,CAAC"}
@@ -1,3 +1,11 @@
1
+ ---
2
+ name: comments
3
+ description: Inline conversation in any file via @droid/@user markers
4
+ globs:
5
+ - "**/*"
6
+ alwaysApply: false
7
+ ---
8
+
1
9
  # Comments Skill
2
10
 
3
11
  Enable inline conversation in any file using `> @droid` and `> @{user}` markers.
@@ -16,3 +16,35 @@ config_schema:
16
16
  type: boolean
17
17
  description: Keep original comments after addressing (vs removing them)
18
18
  default: false
19
+ examples:
20
+ - title: "Action request"
21
+ code: |
22
+ // @droid use PascalCase for enum keys
23
+ enum Status {
24
+ PENDING = 'pending'
25
+ }
26
+ - title: "Ask a question"
27
+ code: |
28
+ > @droid Should we cache this?
29
+
30
+ > @fry Yes, add Redis caching here...
31
+ - title: "TODO with context"
32
+ code: |
33
+ // @droid TODO: refactor to async/await
34
+ // The callback pattern is unwieldy
35
+ function fetchData(callback) {
36
+ api.get('/data', callback);
37
+ }
38
+ - title: "Code review note"
39
+ code: |
40
+ // @droid potential memory leak here
41
+ // Clear interval on unmount?
42
+ useEffect(() => {
43
+ setInterval(poll, 1000);
44
+ }, []);
45
+ - title: "Multi-line discussion"
46
+ code: |
47
+ > @droid Best approach for pagination?
48
+ > We have ~10k records.
49
+
50
+ > @fry Use cursor-based, limit 50.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,21 +36,34 @@
36
36
  "engines": {
37
37
  "node": ">=18.0.0"
38
38
  },
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "registry": "https://registry.npmjs.org/"
42
+ },
39
43
  "dependencies": {
44
+ "@opentui/core": "^0.1.59",
45
+ "@opentui/react": "^0.1.59",
40
46
  "chalk": "^5.3.0",
41
47
  "commander": "^12.1.0",
48
+ "ink": "^6.5.1",
49
+ "ink-select-input": "^6.2.0",
50
+ "ink-text-input": "^6.0.0",
42
51
  "inquirer": "^9.2.15",
43
52
  "ora": "^8.0.1",
53
+ "react": "^19.2.1",
44
54
  "yaml": "^2.4.1"
45
55
  },
46
56
  "devDependencies": {
47
57
  "@changesets/changelog-github": "^0.5.2",
48
58
  "@changesets/cli": "^2.29.8",
49
59
  "@types/bun": "latest",
60
+ "@types/ink-text-input": "^2.0.5",
50
61
  "@types/inquirer": "^9.0.7",
51
62
  "@types/node": "^20.11.24",
63
+ "@types/react": "^19.2.7",
52
64
  "@typescript-eslint/eslint-plugin": "^8.49.0",
53
65
  "@typescript-eslint/parser": "^8.49.0",
66
+ "esbuild": "^0.27.1",
54
67
  "eslint": "^8.57.0",
55
68
  "prettier": "^3.2.5",
56
69
  "typescript": "^5.4.2"
package/src/bin/droid.ts CHANGED
@@ -7,6 +7,7 @@ import { skillsCommand } from '../commands/skills.js';
7
7
  import { installCommand } from '../commands/install.js';
8
8
  import { uninstallCommand } from '../commands/uninstall.js';
9
9
  import { updateCommand } from '../commands/update.js';
10
+ import { tuiCommand } from '../commands/tui.js';
10
11
  import { getVersion, checkForUpdates } from '../lib/version.js';
11
12
 
12
13
  const version = getVersion();
@@ -52,7 +53,17 @@ program
52
53
  .argument('[skill]', 'Update a specific skill')
53
54
  .action(updateCommand);
54
55
 
56
+ program
57
+ .command('tui')
58
+ .description('Launch interactive TUI dashboard')
59
+ .action(tuiCommand);
60
+
55
61
  // Check for updates on any command (non-blocking)
56
62
  checkForUpdates().catch(() => {});
57
63
 
58
- program.parse();
64
+ // If no command provided, launch TUI
65
+ if (process.argv.length === 2) {
66
+ tuiCommand();
67
+ } else {
68
+ program.parse();
69
+ }
@@ -1,10 +1,24 @@
1
1
  import inquirer from 'inquirer';
2
2
  import chalk from 'chalk';
3
3
  import { execSync } from 'child_process';
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { homedir } from 'os';
4
7
  import { loadConfig, saveConfig, configExists } from '../lib/config.js';
5
8
  import { getBundledSkills } from '../lib/skills.js';
6
9
  import { AITool, BuiltInOutput, type DroidConfig, type OutputPreference } from '../lib/types.js';
7
10
 
11
+ /**
12
+ * Permissions droid needs to operate without constant prompts
13
+ */
14
+ const DROID_PERMISSIONS = [
15
+ 'Read(~/.droid/**)',
16
+ 'Write(~/.droid/**)',
17
+ 'Edit(~/.droid/**)',
18
+ 'Glob(~/.droid/**)',
19
+ 'Grep(~/.droid/**)',
20
+ ];
21
+
8
22
  /**
9
23
  * Detect which AI tool is installed
10
24
  */
@@ -37,6 +51,60 @@ function detectGitUsername(): string {
37
51
  }
38
52
  }
39
53
 
54
+ /**
55
+ * Configure AI tool permissions for droid
56
+ */
57
+ export function configureAIToolPermissions(aiTool: AITool): { added: string[]; alreadyPresent: boolean } {
58
+ const added: string[] = [];
59
+
60
+ if (aiTool === AITool.ClaudeCode) {
61
+ const settingsPath = join(homedir(), '.claude', 'settings.json');
62
+
63
+ // Ensure .claude directory exists
64
+ const claudeDir = join(homedir(), '.claude');
65
+ if (!existsSync(claudeDir)) {
66
+ mkdirSync(claudeDir, { recursive: true });
67
+ }
68
+
69
+ // Load or create settings
70
+ let settings: { permissions?: { allow?: string[]; deny?: string[]; ask?: string[] } } = {};
71
+ if (existsSync(settingsPath)) {
72
+ try {
73
+ settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
74
+ } catch {
75
+ // Invalid JSON, start fresh
76
+ settings = {};
77
+ }
78
+ }
79
+
80
+ // Ensure permissions structure exists
81
+ if (!settings.permissions) {
82
+ settings.permissions = {};
83
+ }
84
+ if (!Array.isArray(settings.permissions.allow)) {
85
+ settings.permissions.allow = [];
86
+ }
87
+
88
+ // Add missing permissions
89
+ for (const perm of DROID_PERMISSIONS) {
90
+ if (!settings.permissions.allow.includes(perm)) {
91
+ settings.permissions.allow.push(perm);
92
+ added.push(perm);
93
+ }
94
+ }
95
+
96
+ // Save if we added anything
97
+ if (added.length > 0) {
98
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
99
+ }
100
+
101
+ return { added, alreadyPresent: added.length === 0 };
102
+ }
103
+
104
+ // OpenCode - TODO: implement when we know the settings path
105
+ return { added: [], alreadyPresent: true };
106
+ }
107
+
40
108
  /**
41
109
  * Get available output options (built-in + skills that provide output)
42
110
  */
@@ -145,5 +213,14 @@ export async function setupCommand(): Promise<void> {
145
213
  saveConfig(config);
146
214
 
147
215
  console.log(chalk.green('\n✓ Config saved to ~/.droid/config.yaml'));
216
+
217
+ // Configure AI tool permissions
218
+ const { added, alreadyPresent } = configureAIToolPermissions(answers.ai_tool);
219
+ if (added.length > 0) {
220
+ console.log(chalk.green(`✓ Added droid permissions to ${answers.ai_tool} settings`));
221
+ } else if (alreadyPresent) {
222
+ console.log(chalk.gray(` Droid permissions already configured in ${answers.ai_tool}`));
223
+ }
224
+
148
225
  console.log(chalk.gray('\nRun `droid skills` to browse and install skills.'));
149
226
  }