@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.
- package/.github/workflows/changeset-check.yml +43 -0
- package/.github/workflows/release.yml +6 -3
- package/CHANGELOG.md +20 -0
- package/bun.lock +357 -14
- package/dist/bin/droid.js +12 -1
- package/dist/bin/droid.js.map +1 -1
- package/dist/commands/setup.d.ts +8 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +67 -0
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/tui.d.ts +2 -0
- package/dist/commands/tui.d.ts.map +1 -0
- package/dist/commands/tui.js +480 -0
- package/dist/commands/tui.js.map +1 -0
- package/dist/lib/types.d.ts +5 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/version.d.ts +5 -0
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +19 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/skills/comments/SKILL.md +8 -0
- package/dist/skills/comments/SKILL.yaml +32 -0
- package/package.json +14 -1
- package/src/bin/droid.ts +12 -1
- package/src/commands/setup.ts +77 -0
- package/src/commands/tui.tsx +1102 -0
- package/src/lib/skills.test.ts +75 -1
- package/src/lib/types.ts +7 -0
- package/src/lib/version.test.ts +20 -1
- package/src/lib/version.ts +19 -1
- package/src/skills/comments/SKILL.md +8 -0
- package/src/skills/comments/SKILL.yaml +32 -0
- package/tsconfig.json +5 -3
package/dist/lib/version.js
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/dist/lib/version.js.map
CHANGED
|
@@ -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,
|
|
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"}
|
|
@@ -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.
|
|
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
|
-
|
|
64
|
+
// If no command provided, launch TUI
|
|
65
|
+
if (process.argv.length === 2) {
|
|
66
|
+
tuiCommand();
|
|
67
|
+
} else {
|
|
68
|
+
program.parse();
|
|
69
|
+
}
|
package/src/commands/setup.ts
CHANGED
|
@@ -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
|
}
|