cralph 1.0.0-alpha.6 → 1.0.0-beta.1
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/README.md +11 -7
- package/package.json +1 -1
- package/src/paths.ts +47 -8
package/README.md
CHANGED
|
@@ -26,13 +26,11 @@ cralph wraps this into a CLI with config, logging, and TODO tracking.
|
|
|
26
26
|
## Install
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# Install Bun (if not already installed)
|
|
30
|
+
curl -fsSL https://bun.sh/install | bash
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npm install -g cralph
|
|
32
|
+
# Install cralph
|
|
33
|
+
bun add -g cralph
|
|
36
34
|
```
|
|
37
35
|
|
|
38
36
|
## Quick Start
|
|
@@ -142,13 +140,19 @@ Use `--yes` to skip confirmation (for CI/automation).
|
|
|
142
140
|
- **Enter** - Confirm
|
|
143
141
|
- **Ctrl+C** - Exit
|
|
144
142
|
|
|
143
|
+
## Platform Notes
|
|
144
|
+
|
|
145
|
+
### macOS Protected Directories
|
|
146
|
+
|
|
147
|
+
cralph gracefully handles macOS permission errors (`EPERM`, `EACCES`) when scanning directories. Protected locations like `~/Pictures/Photo Booth Library` or iCloud folders are silently skipped, allowing the CLI to run from any directory including root (`/`).
|
|
148
|
+
|
|
145
149
|
## Testing
|
|
146
150
|
|
|
147
151
|
```bash
|
|
148
152
|
bun test
|
|
149
153
|
```
|
|
150
154
|
|
|
151
|
-
- **Unit tests** - Config, prompt building, CLI
|
|
155
|
+
- **Unit tests** - Config, prompt building, CLI, access error handling
|
|
152
156
|
- **E2E tests** - Full loop with Claude (requires auth)
|
|
153
157
|
|
|
154
158
|
## Requirements
|
package/package.json
CHANGED
package/src/paths.ts
CHANGED
|
@@ -11,10 +11,29 @@ const CONTROLS = dim("↑↓ Navigate • Space Toggle • Enter • Ctrl+C Exit
|
|
|
11
11
|
* List directories in a given path
|
|
12
12
|
*/
|
|
13
13
|
async function listDirectories(basePath: string): Promise<string[]> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
try {
|
|
15
|
+
const entries = await readdir(basePath, { withFileTypes: true });
|
|
16
|
+
return entries
|
|
17
|
+
.filter((e) => e.isDirectory() && !e.name.startsWith("."))
|
|
18
|
+
.map((e) => e.name);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
// Silently skip directories we can't access (EPERM, EACCES)
|
|
21
|
+
if (isAccessError(error)) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if an error is a permission/access error
|
|
30
|
+
*/
|
|
31
|
+
export function isAccessError(error: unknown): boolean {
|
|
32
|
+
if (error && typeof error === "object" && "code" in error) {
|
|
33
|
+
const code = (error as { code: string }).code;
|
|
34
|
+
return code === "EPERM" || code === "EACCES";
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
18
37
|
}
|
|
19
38
|
|
|
20
39
|
/**
|
|
@@ -37,7 +56,7 @@ const EXCLUDED_DIRS = [
|
|
|
37
56
|
/**
|
|
38
57
|
* List directories recursively up to a certain depth
|
|
39
58
|
*/
|
|
40
|
-
async function listDirectoriesRecursive(
|
|
59
|
+
export async function listDirectoriesRecursive(
|
|
41
60
|
basePath: string,
|
|
42
61
|
maxDepth: number = 3
|
|
43
62
|
): Promise<string[]> {
|
|
@@ -46,7 +65,17 @@ async function listDirectoriesRecursive(
|
|
|
46
65
|
async function walk(dir: string, depth: number) {
|
|
47
66
|
if (depth > maxDepth) return;
|
|
48
67
|
|
|
49
|
-
|
|
68
|
+
let entries;
|
|
69
|
+
try {
|
|
70
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
71
|
+
} catch (error) {
|
|
72
|
+
// Silently skip directories we can't access (EPERM, EACCES)
|
|
73
|
+
if (isAccessError(error)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
|
|
50
79
|
for (const entry of entries) {
|
|
51
80
|
// Skip hidden and excluded directories
|
|
52
81
|
if (!entry.isDirectory()) continue;
|
|
@@ -66,14 +95,24 @@ async function listDirectoriesRecursive(
|
|
|
66
95
|
/**
|
|
67
96
|
* List files matching patterns in a directory (recursive)
|
|
68
97
|
*/
|
|
69
|
-
async function listFilesRecursive(
|
|
98
|
+
export async function listFilesRecursive(
|
|
70
99
|
basePath: string,
|
|
71
100
|
extensions: string[]
|
|
72
101
|
): Promise<string[]> {
|
|
73
102
|
const results: string[] = [];
|
|
74
103
|
|
|
75
104
|
async function walk(dir: string) {
|
|
76
|
-
|
|
105
|
+
let entries;
|
|
106
|
+
try {
|
|
107
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
108
|
+
} catch (error) {
|
|
109
|
+
// Silently skip directories we can't access (EPERM, EACCES)
|
|
110
|
+
if (isAccessError(error)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
|
|
77
116
|
for (const entry of entries) {
|
|
78
117
|
const fullPath = join(dir, entry.name);
|
|
79
118
|
if (entry.isDirectory()) {
|