clank-cli 0.1.52 → 0.1.53
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/package.json +28 -23
- package/src/ClassifyFiles.ts +6 -1
- package/src/Config.ts +3 -1
- package/src/FsUtil.ts +10 -1
- package/src/Gitignore.ts +3 -2
- package/src/OverlayLinks.ts +12 -2
- package/src/commands/Add.ts +39 -14
- package/src/commands/Link.ts +6 -1
- package/src/commands/Move.ts +3 -1
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clank-cli",
|
|
3
|
-
"version": "0.1.52",
|
|
4
3
|
"description": "Keep AI files in a separate overlay repository",
|
|
4
|
+
"version": "0.1.53",
|
|
5
|
+
"author": "",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"bin": {
|
|
7
8
|
"clank": "./bin/clank.ts"
|
|
@@ -10,21 +11,6 @@
|
|
|
10
11
|
"bin/",
|
|
11
12
|
"src/"
|
|
12
13
|
],
|
|
13
|
-
"engines": {
|
|
14
|
-
"node": ">=22.6.0"
|
|
15
|
-
},
|
|
16
|
-
"keywords": [
|
|
17
|
-
"ai",
|
|
18
|
-
"assistant",
|
|
19
|
-
"claude",
|
|
20
|
-
"gemini",
|
|
21
|
-
"copilot",
|
|
22
|
-
"configuration",
|
|
23
|
-
"worktree",
|
|
24
|
-
"git"
|
|
25
|
-
],
|
|
26
|
-
"author": "",
|
|
27
|
-
"license": "MIT",
|
|
28
14
|
"dependencies": {
|
|
29
15
|
"commander": "^14.0.2",
|
|
30
16
|
"cosmiconfig": "^9.0.0"
|
|
@@ -32,19 +18,38 @@
|
|
|
32
18
|
"devDependencies": {
|
|
33
19
|
"@biomejs/biome": "^2.3.8",
|
|
34
20
|
"@types/node": "^24.10.1",
|
|
21
|
+
"@typescript/native-preview": "7.0.0-dev.20251221.1",
|
|
22
|
+
"@vitest/ui": "^4.0.16",
|
|
35
23
|
"execa": "^9.6.1",
|
|
36
24
|
"monobump": "^0.1.5",
|
|
37
|
-
"
|
|
25
|
+
"npm-run-all2": "^7.0.2",
|
|
26
|
+
"syncpack": "^13.0.4",
|
|
38
27
|
"vitest": "^4.0.14"
|
|
39
28
|
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"keywords": [
|
|
31
|
+
"agent",
|
|
32
|
+
"ai",
|
|
33
|
+
"assistant",
|
|
34
|
+
"claude",
|
|
35
|
+
"configuration",
|
|
36
|
+
"copilot",
|
|
37
|
+
"gemini",
|
|
38
|
+
"git",
|
|
39
|
+
"worktree"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22.6.0"
|
|
43
|
+
},
|
|
40
44
|
"scripts": {
|
|
41
|
-
"test": "vitest",
|
|
42
|
-
"test:ui": "vitest --ui",
|
|
43
|
-
"typecheck": "tsc --noEmit",
|
|
44
45
|
"bump": "monobump",
|
|
46
|
+
"fix": "biome check --fix --unsafe",
|
|
47
|
+
"fix:pkgJsonFormat": "syncpack format",
|
|
45
48
|
"global": "pnpm link --global",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
+
"lint": "biome check --error-on-warnings",
|
|
50
|
+
"test": "vitest",
|
|
51
|
+
"test:once": "vitest run",
|
|
52
|
+
"test:ui": "vitest --ui",
|
|
53
|
+
"typecheck": "tsgo"
|
|
49
54
|
}
|
|
50
55
|
}
|
package/src/ClassifyFiles.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { lstat } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, join } from "node:path";
|
|
3
3
|
import { agentFiles } from "./AgentFiles.ts";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
isTrackedByGit,
|
|
6
|
+
relativePath,
|
|
7
|
+
resolveSymlinkTarget,
|
|
8
|
+
walkDirectory,
|
|
9
|
+
} from "./FsUtil.ts";
|
|
5
10
|
import type { GitContext } from "./Git.ts";
|
|
6
11
|
import { type MapperContext, targetToOverlay } from "./Mapper.ts";
|
|
7
12
|
|
package/src/Config.ts
CHANGED
|
@@ -89,7 +89,9 @@ function getConfigDir(): string {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/** Validate overlay repository exists, throw if not */
|
|
92
|
-
export async function validateOverlayExists(
|
|
92
|
+
export async function validateOverlayExists(
|
|
93
|
+
overlayRoot: string,
|
|
94
|
+
): Promise<void> {
|
|
93
95
|
if (!(await fileExists(overlayRoot))) {
|
|
94
96
|
throw new Error(
|
|
95
97
|
`Overlay repository not found at ${overlayRoot}\nRun 'clank init' to create it`,
|
package/src/FsUtil.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
lstat,
|
|
3
|
+
mkdir,
|
|
4
|
+
readdir,
|
|
5
|
+
readFile,
|
|
6
|
+
readlink,
|
|
7
|
+
symlink,
|
|
8
|
+
unlink,
|
|
9
|
+
writeFile,
|
|
10
|
+
} from "node:fs/promises";
|
|
2
11
|
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
3
12
|
import { execFileAsync } from "./Exec.ts";
|
|
4
13
|
|
package/src/Gitignore.ts
CHANGED
|
@@ -169,7 +169,6 @@ async function parseGitignoreFile(
|
|
|
169
169
|
result.negationWarnings.push(...parsed.negationWarnings);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
|
|
173
172
|
/** Parse a single gitignore line */
|
|
174
173
|
function parseLine(
|
|
175
174
|
trimmed: string,
|
|
@@ -195,7 +194,9 @@ function parseGitignoreContent(
|
|
|
195
194
|
options: ParseOptions,
|
|
196
195
|
): { patterns: GitignorePattern[]; negationWarnings: string[] } {
|
|
197
196
|
const rawLines = content.split("\n");
|
|
198
|
-
const lines = options.skipClankSection
|
|
197
|
+
const lines = options.skipClankSection
|
|
198
|
+
? filterClankLines(rawLines)
|
|
199
|
+
: rawLines;
|
|
199
200
|
const basePath = options.basePath ?? "";
|
|
200
201
|
|
|
201
202
|
const patterns: GitignorePattern[] = [];
|
package/src/OverlayLinks.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { lstat } from "node:fs/promises";
|
|
2
2
|
import { dirname, join, relative } from "node:path";
|
|
3
3
|
import { managedAgentDirs } from "./AgentFiles.ts";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
createSymlink,
|
|
6
|
+
ensureDir,
|
|
7
|
+
getLinkTarget,
|
|
8
|
+
resolveSymlinkTarget,
|
|
9
|
+
walkDirectory,
|
|
10
|
+
} from "./FsUtil.ts";
|
|
11
|
+
import {
|
|
12
|
+
getPromptRelPath,
|
|
13
|
+
type MapperContext,
|
|
14
|
+
overlayToTarget,
|
|
15
|
+
} from "./Mapper.ts";
|
|
6
16
|
|
|
7
17
|
export type ManagedFileState =
|
|
8
18
|
| { kind: "valid" }
|
package/src/commands/Add.ts
CHANGED
|
@@ -119,25 +119,50 @@ async function addSingleFile(
|
|
|
119
119
|
const params = { overlayPath, symlinkDir, gitRoot, overlayRoot, agents };
|
|
120
120
|
await createAgentLinks(params);
|
|
121
121
|
} else if (isPromptFile(normalizedPath)) {
|
|
122
|
-
|
|
123
|
-
const promptRelPath = getPromptRelPath(normalizedPath);
|
|
124
|
-
if (promptRelPath) {
|
|
125
|
-
const created = await createPromptLinks(overlayPath, promptRelPath, gitRoot);
|
|
126
|
-
if (created.length) {
|
|
127
|
-
console.log(`Created symlinks: ${created.map((p) => relative(cwd, p)).join(", ")}`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
122
|
+
await handlePromptFile(normalizedPath, overlayPath, gitRoot, cwd);
|
|
130
123
|
} else {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
124
|
+
await handleRegularFile(normalizedPath, overlayPath, overlayRoot, cwd);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Handle prompt file symlink creation */
|
|
129
|
+
async function handlePromptFile(
|
|
130
|
+
normalizedPath: string,
|
|
131
|
+
overlayPath: string,
|
|
132
|
+
gitRoot: string,
|
|
133
|
+
cwd: string,
|
|
134
|
+
): Promise<void> {
|
|
135
|
+
const promptRelPath = getPromptRelPath(normalizedPath);
|
|
136
|
+
if (promptRelPath) {
|
|
137
|
+
const created = await createPromptLinks(
|
|
138
|
+
overlayPath,
|
|
139
|
+
promptRelPath,
|
|
140
|
+
gitRoot,
|
|
141
|
+
);
|
|
142
|
+
if (created.length) {
|
|
143
|
+
console.log(
|
|
144
|
+
`Created symlinks: ${created.map((p) => relative(cwd, p)).join(", ")}`,
|
|
145
|
+
);
|
|
137
146
|
}
|
|
138
147
|
}
|
|
139
148
|
}
|
|
140
149
|
|
|
150
|
+
/** Handle regular file symlink creation */
|
|
151
|
+
async function handleRegularFile(
|
|
152
|
+
normalizedPath: string,
|
|
153
|
+
overlayPath: string,
|
|
154
|
+
overlayRoot: string,
|
|
155
|
+
cwd: string,
|
|
156
|
+
): Promise<void> {
|
|
157
|
+
if (await isSymlinkToOverlay(normalizedPath, overlayRoot)) {
|
|
158
|
+
console.log(`Symlink already exists: ${relative(cwd, normalizedPath)}`);
|
|
159
|
+
} else {
|
|
160
|
+
const linkTarget = getLinkTarget(normalizedPath, overlayPath);
|
|
161
|
+
await createSymlink(linkTarget, normalizedPath);
|
|
162
|
+
console.log(`Created symlink: ${relative(cwd, normalizedPath)}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
141
166
|
async function isDirectory(path: string): Promise<boolean> {
|
|
142
167
|
try {
|
|
143
168
|
return (await lstat(path)).isDirectory();
|
package/src/commands/Link.ts
CHANGED
|
@@ -6,7 +6,12 @@ import {
|
|
|
6
6
|
classifyAgentFiles,
|
|
7
7
|
formatAgentFileProblems,
|
|
8
8
|
} from "../ClassifyFiles.ts";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
type ClankConfig,
|
|
11
|
+
expandPath,
|
|
12
|
+
loadConfig,
|
|
13
|
+
validateOverlayExists,
|
|
14
|
+
} from "../Config.ts";
|
|
10
15
|
import { addGitExcludes } from "../Exclude.ts";
|
|
11
16
|
import {
|
|
12
17
|
createSymlink,
|
package/src/commands/Move.ts
CHANGED
|
@@ -167,6 +167,8 @@ async function recreatePromptLinks(
|
|
|
167
167
|
const created = await createPromptLinks(overlayPath, promptRelPath, gitRoot);
|
|
168
168
|
|
|
169
169
|
if (created.length > 0) {
|
|
170
|
-
console.log(
|
|
170
|
+
console.log(
|
|
171
|
+
`Updated symlinks: ${created.map((p) => relative(gitRoot, p)).join(", ")}`,
|
|
172
|
+
);
|
|
171
173
|
}
|
|
172
174
|
}
|