x7-code-line 0.1.0 → 0.2.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/README.md +66 -6
- package/package.json +1 -1
- package/src/cache.js +14 -0
- package/src/cli.js +21 -3
- package/src/diff-lines.js +38 -6
- package/src/install.js +114 -17
package/README.md
CHANGED
|
@@ -100,6 +100,7 @@ git diff --no-ext-diff --unified=0 --no-color
|
|
|
100
100
|
- 变更类型:`add` 或 `delete`
|
|
101
101
|
- 行号
|
|
102
102
|
- 行内容
|
|
103
|
+
- 该文件当前的修改时间
|
|
103
104
|
|
|
104
105
|
如果该 ID 不在 AI diff 缓存中,就写入普通 diff 缓存:
|
|
105
106
|
|
|
@@ -146,6 +147,15 @@ git diff --cached --no-ext-diff --unified=0 --no-color
|
|
|
146
147
|
.x7-code-line/pending-commit.json
|
|
147
148
|
```
|
|
148
149
|
|
|
150
|
+
计算完成后,会清除当前仓库在以下两份中心缓存中的条目:
|
|
151
|
+
|
|
152
|
+
```text
|
|
153
|
+
.x7-code-line/ai-diff-line-ids.json
|
|
154
|
+
.x7-code-line/normal-diff-line-ids.json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
其他项目的缓存不会被修改。
|
|
158
|
+
|
|
149
159
|
`commit-msg` 会读取该结果,并追加或替换 trailer:
|
|
150
160
|
|
|
151
161
|
```text
|
|
@@ -160,23 +170,43 @@ x7-ai-lines: 0
|
|
|
160
170
|
|
|
161
171
|
## 多项目安装
|
|
162
172
|
|
|
163
|
-
|
|
173
|
+
`install` 至少需要传入一个目录;0 目录场景会直接失败。
|
|
174
|
+
|
|
175
|
+
默认要求传入绝对路径:
|
|
164
176
|
|
|
165
177
|
```sh
|
|
166
|
-
npx --no-install x7-code-line install --dir
|
|
178
|
+
npx --no-install x7-code-line install --dir /abs/path/repo-a --dir /abs/path/repo-b
|
|
167
179
|
```
|
|
168
180
|
|
|
169
|
-
|
|
181
|
+
例如,将 `x7-code-line` 安装在 C 盘目录,但指定 D 盘的 Git 项目:
|
|
170
182
|
|
|
171
|
-
|
|
183
|
+
```powershell
|
|
184
|
+
cd C:\x7-tools\x7-code-line-host
|
|
185
|
+
npx --no-install x7-code-line install --dir D:\work\repo-a --dir D:\work\repo-b
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
如果确实需要解析相对路径,必须显式传 `--relative`:
|
|
172
189
|
|
|
173
190
|
```sh
|
|
174
|
-
|
|
191
|
+
npx --no-install x7-code-line install --relative --dir ../repo-a --dir ../repo-b
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
此时相对路径基于当前执行 `install` 命令的目录解析。
|
|
195
|
+
|
|
196
|
+
也可以在安装时通过环境变量指定多个目标目录。
|
|
197
|
+
|
|
198
|
+
如果通过环境变量或配置文件传相对路径,需要同时设置 `X7_CODE_LINE_RELATIVE_PATHS=1`。
|
|
199
|
+
|
|
200
|
+
macOS / Linux 示例:
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
X7_CODE_LINE_RELATIVE_PATHS=1 X7_CODE_LINE_DIRS="../repo-a:../repo-b" npm install x7-code-line
|
|
175
204
|
```
|
|
176
205
|
|
|
177
206
|
Windows 使用 `;` 作为目录分隔符:
|
|
178
207
|
|
|
179
208
|
```powershell
|
|
209
|
+
$env:X7_CODE_LINE_RELATIVE_PATHS = "1"
|
|
180
210
|
$env:X7_CODE_LINE_DIRS = "../repo-a;../repo-b"
|
|
181
211
|
npm install x7-code-line
|
|
182
212
|
```
|
|
@@ -231,10 +261,40 @@ which npx
|
|
|
231
261
|
$env:X7_CODE_LINE_DIRS = "D:\repo-a;D:\repo-b"
|
|
232
262
|
```
|
|
233
263
|
|
|
264
|
+
## 增量添加目录
|
|
265
|
+
|
|
266
|
+
在已经完成安装的当前安装目录下,可以使用 `addDir` 增量添加新的项目目录:
|
|
267
|
+
|
|
268
|
+
```sh
|
|
269
|
+
x7-code-line addDir --dir /abs/path/repo-c
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
这个命令只做两件事:
|
|
273
|
+
|
|
274
|
+
- 把新目录追加到当前安装目录 `.x7-code-line/projects.json`
|
|
275
|
+
- 为新目录安装 `.git/hooks/pre-commit` 和 `.git/hooks/commit-msg`
|
|
276
|
+
|
|
277
|
+
它不会重写当前安装目录的 `.cursor/hooks.json`、`.codex/hooks.json`,也不会重写已有项目集合。
|
|
278
|
+
|
|
279
|
+
`addDir` 会沿用与 `install` 相同的严格校验:
|
|
280
|
+
|
|
281
|
+
- 目录必须真实存在
|
|
282
|
+
- 目录必须可访问
|
|
283
|
+
- 目录必须包含可用的 `.git` 目录
|
|
284
|
+
|
|
285
|
+
任一新增目录不满足条件时,命令直接失败,不会继续追加。
|
|
286
|
+
|
|
287
|
+
默认只接受绝对路径;如果需要解析相对路径,需要显式传入:
|
|
288
|
+
|
|
289
|
+
```sh
|
|
290
|
+
x7-code-line addDir --relative --dir ../repo-c
|
|
291
|
+
```
|
|
292
|
+
|
|
234
293
|
## CLI 命令
|
|
235
294
|
|
|
236
295
|
```sh
|
|
237
|
-
x7-code-line install
|
|
296
|
+
x7-code-line install --dir <absolute-path> [--dir <absolute-path> ...] [--config path] [--relative]
|
|
297
|
+
x7-code-line addDir --dir <absolute-path> [--dir <absolute-path> ...] [--config path] [--relative]
|
|
238
298
|
x7-code-line prompt-submit
|
|
239
299
|
x7-code-line stop
|
|
240
300
|
x7-code-line git-pre-commit
|
package/package.json
CHANGED
package/src/cache.js
CHANGED
|
@@ -67,6 +67,19 @@ function addProjectId(cache, projectDir, id) {
|
|
|
67
67
|
return false;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
function clearProjectIds(cache, projectDir) {
|
|
71
|
+
const key = path.resolve(projectDir);
|
|
72
|
+
if (!cache.projects || typeof cache.projects !== "object" || Array.isArray(cache.projects)) {
|
|
73
|
+
cache.projects = {};
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (Object.prototype.hasOwnProperty.call(cache.projects, key)) {
|
|
77
|
+
delete cache.projects[key];
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
70
83
|
function ensureJson(filePath, defaultValue) {
|
|
71
84
|
if (!fs.existsSync(filePath)) {
|
|
72
85
|
writeJson(filePath, defaultValue);
|
|
@@ -92,6 +105,7 @@ module.exports = {
|
|
|
92
105
|
PENDING_COMMIT_FILE,
|
|
93
106
|
PROJECTS_CACHE_FILE,
|
|
94
107
|
addProjectId,
|
|
108
|
+
clearProjectIds,
|
|
95
109
|
ensureCacheFiles,
|
|
96
110
|
getCacheDir,
|
|
97
111
|
getProjectIds,
|
package/src/cli.js
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
const fs = require("node:fs");
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
const { handlePromptSubmit, handleStop } = require("./agent-hooks");
|
|
6
|
-
const { install } = require("./install");
|
|
6
|
+
const { addDirs, install } = require("./install");
|
|
7
7
|
const { appendCommitTrailer } = require("./git-trailer");
|
|
8
8
|
const {
|
|
9
9
|
AI_DIFF_CACHE_FILE,
|
|
10
|
+
NORMAL_DIFF_CACHE_FILE,
|
|
10
11
|
PENDING_COMMIT_FILE,
|
|
12
|
+
clearProjectIds,
|
|
11
13
|
getCacheDir,
|
|
12
14
|
getProjectIds,
|
|
13
15
|
readLineIdCache
|
|
@@ -20,6 +22,8 @@ async function runCli(argv) {
|
|
|
20
22
|
switch (command) {
|
|
21
23
|
case "install":
|
|
22
24
|
return install(parseInstallOptions(rest));
|
|
25
|
+
case "addDir":
|
|
26
|
+
return addDirs(parseInstallOptions(rest));
|
|
23
27
|
case "prompt-submit":
|
|
24
28
|
case "cursor-before-submit-prompt":
|
|
25
29
|
case "codex-user-prompt-submit":
|
|
@@ -44,13 +48,16 @@ async function runCli(argv) {
|
|
|
44
48
|
function parseInstallOptions(args) {
|
|
45
49
|
const options = {
|
|
46
50
|
targetDirs: [],
|
|
47
|
-
configPath: undefined
|
|
51
|
+
configPath: undefined,
|
|
52
|
+
relativePaths: false
|
|
48
53
|
};
|
|
49
54
|
|
|
50
55
|
for (let index = 0; index < args.length; index += 1) {
|
|
51
56
|
const arg = args[index];
|
|
52
57
|
if (arg === "--config" || arg === "-c") {
|
|
53
58
|
options.configPath = requireValue(args, (index += 1), arg);
|
|
59
|
+
} else if (arg === "--relative") {
|
|
60
|
+
options.relativePaths = true;
|
|
54
61
|
} else if (arg === "--dir" || arg === "-d") {
|
|
55
62
|
options.targetDirs.push(requireValue(args, (index += 1), arg));
|
|
56
63
|
} else if (arg === "--dirs") {
|
|
@@ -75,6 +82,7 @@ async function runGitPreCommit() {
|
|
|
75
82
|
const baseDir = getBaseDir();
|
|
76
83
|
const projectDir = process.cwd();
|
|
77
84
|
const aiCache = readLineIdCache(baseDir, AI_DIFF_CACHE_FILE);
|
|
85
|
+
const normalCache = readLineIdCache(baseDir, NORMAL_DIFF_CACHE_FILE);
|
|
78
86
|
const aiIds = getProjectIds(aiCache, projectDir);
|
|
79
87
|
const stagedIds = getGitDiffLineIds(projectDir, { staged: true });
|
|
80
88
|
const aiLineCount = stagedIds.filter((id) => aiIds.has(id)).length;
|
|
@@ -83,6 +91,10 @@ async function runGitPreCommit() {
|
|
|
83
91
|
pendingPath,
|
|
84
92
|
`${JSON.stringify({ projects: { [path.resolve(projectDir)]: { aiLineCount } } }, null, 2)}\n`
|
|
85
93
|
);
|
|
94
|
+
clearProjectIds(aiCache, projectDir);
|
|
95
|
+
clearProjectIds(normalCache, projectDir);
|
|
96
|
+
writeCacheFile(baseDir, AI_DIFF_CACHE_FILE, aiCache);
|
|
97
|
+
writeCacheFile(baseDir, NORMAL_DIFF_CACHE_FILE, normalCache);
|
|
86
98
|
|
|
87
99
|
if (process.env.X7_CODE_LINE_DEBUG === "1") {
|
|
88
100
|
console.error(`[x7-code-line] git pre-commit ai lines: ${aiLineCount}`);
|
|
@@ -113,10 +125,16 @@ function readPendingAiLineCount(baseDir, projectDir) {
|
|
|
113
125
|
}
|
|
114
126
|
}
|
|
115
127
|
|
|
128
|
+
function writeCacheFile(baseDir, fileName, cache) {
|
|
129
|
+
const cacheDir = getCacheDir(baseDir);
|
|
130
|
+
fs.writeFileSync(path.join(cacheDir, fileName), `${JSON.stringify(cache, null, 2)}\n`);
|
|
131
|
+
}
|
|
132
|
+
|
|
116
133
|
function printHelp() {
|
|
117
134
|
const text = [
|
|
118
135
|
"Usage:",
|
|
119
|
-
" x7-code-line install
|
|
136
|
+
" x7-code-line install --dir <absolute-path> [--dir <absolute-path> ...] [--config path] [--relative]",
|
|
137
|
+
" x7-code-line addDir --dir <absolute-path> [--dir <absolute-path> ...] [--config path] [--relative]",
|
|
120
138
|
" x7-code-line prompt-submit",
|
|
121
139
|
" x7-code-line stop",
|
|
122
140
|
" x7-code-line git-pre-commit",
|
package/src/diff-lines.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const crypto = require("node:crypto");
|
|
4
|
+
const fs = require("node:fs");
|
|
4
5
|
const path = require("node:path");
|
|
5
6
|
const { execFileSync } = require("node:child_process");
|
|
6
7
|
|
|
@@ -16,14 +17,15 @@ function getGitDiffLineIds(projectDir, options = {}) {
|
|
|
16
17
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
return parseDiffLineIds(projectDir, diff);
|
|
20
|
+
return parseDiffLineIds(projectDir, diff, options);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function parseDiffLineIds(projectDir, diff) {
|
|
23
|
+
function parseDiffLineIds(projectDir, diff, options = {}) {
|
|
23
24
|
const ids = [];
|
|
24
25
|
let currentFile = "";
|
|
25
26
|
let oldLine = 0;
|
|
26
27
|
let newLine = 0;
|
|
28
|
+
const fileMtimes = new Map();
|
|
27
29
|
|
|
28
30
|
for (const line of diff.split(/\r?\n/u)) {
|
|
29
31
|
if (line.startsWith("+++ b/")) {
|
|
@@ -47,10 +49,21 @@ function parseDiffLineIds(projectDir, diff) {
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
50
|
-
ids.push(
|
|
52
|
+
ids.push(
|
|
53
|
+
makeLineId(projectDir, currentFile, "add", newLine, line.slice(1), getFileModifiedTime(projectDir, currentFile, fileMtimes))
|
|
54
|
+
);
|
|
51
55
|
newLine += 1;
|
|
52
56
|
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
53
|
-
ids.push(
|
|
57
|
+
ids.push(
|
|
58
|
+
makeLineId(
|
|
59
|
+
projectDir,
|
|
60
|
+
currentFile,
|
|
61
|
+
"delete",
|
|
62
|
+
oldLine,
|
|
63
|
+
line.slice(1),
|
|
64
|
+
getFileModifiedTime(projectDir, currentFile, fileMtimes)
|
|
65
|
+
)
|
|
66
|
+
);
|
|
54
67
|
oldLine += 1;
|
|
55
68
|
} else if (line.startsWith(" ")) {
|
|
56
69
|
oldLine += 1;
|
|
@@ -61,7 +74,7 @@ function parseDiffLineIds(projectDir, diff) {
|
|
|
61
74
|
return ids;
|
|
62
75
|
}
|
|
63
76
|
|
|
64
|
-
function makeLineId(projectDir, filePath, changeType, lineNumber, content) {
|
|
77
|
+
function makeLineId(projectDir, filePath, changeType, lineNumber, content, modifiedTimeMs = 0) {
|
|
65
78
|
return crypto
|
|
66
79
|
.createHash("sha256")
|
|
67
80
|
.update(
|
|
@@ -70,12 +83,31 @@ function makeLineId(projectDir, filePath, changeType, lineNumber, content) {
|
|
|
70
83
|
file: filePath,
|
|
71
84
|
changeType,
|
|
72
85
|
lineNumber,
|
|
73
|
-
content
|
|
86
|
+
content,
|
|
87
|
+
modifiedTimeMs
|
|
74
88
|
})
|
|
75
89
|
)
|
|
76
90
|
.digest("hex");
|
|
77
91
|
}
|
|
78
92
|
|
|
93
|
+
function getFileModifiedTime(projectDir, filePath, fileMtimes) {
|
|
94
|
+
const key = `${path.resolve(projectDir)}\0${filePath}`;
|
|
95
|
+
if (fileMtimes.has(key)) {
|
|
96
|
+
return fileMtimes.get(key);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const absolutePath = path.resolve(projectDir, filePath);
|
|
100
|
+
let modifiedTimeMs = 0;
|
|
101
|
+
try {
|
|
102
|
+
modifiedTimeMs = fs.statSync(absolutePath).mtimeMs;
|
|
103
|
+
} catch {
|
|
104
|
+
modifiedTimeMs = 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fileMtimes.set(key, modifiedTimeMs);
|
|
108
|
+
return modifiedTimeMs;
|
|
109
|
+
}
|
|
110
|
+
|
|
79
111
|
module.exports = {
|
|
80
112
|
getGitDiffLineIds,
|
|
81
113
|
makeLineId,
|
package/src/install.js
CHANGED
|
@@ -2,32 +2,63 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require("node:fs");
|
|
4
4
|
const path = require("node:path");
|
|
5
|
-
const { CACHE_DIR_NAME, ensureCacheFiles, writeProjects } = require("./cache");
|
|
5
|
+
const { CACHE_DIR_NAME, ensureCacheFiles, readProjects, writeProjects } = require("./cache");
|
|
6
6
|
|
|
7
7
|
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
8
8
|
|
|
9
9
|
function install(options = {}) {
|
|
10
|
+
const pathBaseDir = path.resolve(options.pathBaseDir || process.env.INIT_CWD || process.cwd());
|
|
10
11
|
const config = readConfig(options.configPath || process.env.X7_CODE_LINE_CONFIG);
|
|
11
|
-
const targetDirs = normalizeTargetDirs(options.targetDirs, config
|
|
12
|
-
|
|
12
|
+
const targetDirs = normalizeTargetDirs(options.targetDirs, config, {
|
|
13
|
+
relativePaths: Boolean(options.relativePaths || process.env.X7_CODE_LINE_RELATIVE_PATHS === "1"),
|
|
14
|
+
pathBaseDir
|
|
15
|
+
});
|
|
16
|
+
const cacheBaseDir = path.resolve(options.cacheBaseDir || pathBaseDir);
|
|
17
|
+
const validatedTargets = validateTargets(targetDirs, config);
|
|
13
18
|
|
|
14
19
|
installCurrentProjectFiles(cacheBaseDir);
|
|
15
|
-
for (const
|
|
16
|
-
installGitHooksInto(
|
|
20
|
+
for (const target of validatedTargets) {
|
|
21
|
+
installGitHooksInto(target, cacheBaseDir);
|
|
17
22
|
}
|
|
18
|
-
writeProjects(cacheBaseDir,
|
|
23
|
+
writeProjects(cacheBaseDir, validatedTargets.map((target) => target.targetDir));
|
|
19
24
|
|
|
20
|
-
return
|
|
25
|
+
return validatedTargets.map((target) => target.targetDir);
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
function installFromPostinstall() {
|
|
24
29
|
const envDirs = splitList(process.env.X7_CODE_LINE_DIRS);
|
|
25
|
-
const targetDirs = envDirs.length > 0 ? envDirs : [process.env.INIT_CWD || process.cwd()];
|
|
26
30
|
|
|
27
31
|
return install({
|
|
28
32
|
configPath: process.env.X7_CODE_LINE_CONFIG,
|
|
29
|
-
targetDirs
|
|
33
|
+
targetDirs: envDirs,
|
|
34
|
+
relativePaths: process.env.X7_CODE_LINE_RELATIVE_PATHS === "1"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function addDirs(options = {}) {
|
|
39
|
+
const explicitDirs = Array.isArray(options.targetDirs) ? options.targetDirs.map((dir) => String(dir || "").trim()).filter(Boolean) : [];
|
|
40
|
+
if (explicitDirs.length === 0) {
|
|
41
|
+
throw new Error("addDir requires at least one --dir value");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const pathBaseDir = path.resolve(options.pathBaseDir || process.env.INIT_CWD || process.cwd());
|
|
45
|
+
const config = readConfig(options.configPath || process.env.X7_CODE_LINE_CONFIG);
|
|
46
|
+
const targetDirs = normalizeTargetDirs(explicitDirs, config, {
|
|
47
|
+
relativePaths: Boolean(options.relativePaths || process.env.X7_CODE_LINE_RELATIVE_PATHS === "1"),
|
|
48
|
+
pathBaseDir
|
|
30
49
|
});
|
|
50
|
+
const cacheBaseDir = path.resolve(options.cacheBaseDir || pathBaseDir);
|
|
51
|
+
assertInstalledBase(cacheBaseDir);
|
|
52
|
+
|
|
53
|
+
const validatedTargets = validateTargets(targetDirs, config);
|
|
54
|
+
for (const target of validatedTargets) {
|
|
55
|
+
installGitHooksInto(target, cacheBaseDir);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const mergedProjects = [...new Set([...readProjects(cacheBaseDir), ...validatedTargets.map((target) => target.targetDir)])];
|
|
59
|
+
writeProjects(cacheBaseDir, mergedProjects);
|
|
60
|
+
|
|
61
|
+
return validatedTargets.map((target) => target.targetDir);
|
|
31
62
|
}
|
|
32
63
|
|
|
33
64
|
function installCurrentProjectFiles(cacheBaseDir) {
|
|
@@ -52,11 +83,8 @@ function installCurrentProjectFiles(cacheBaseDir) {
|
|
|
52
83
|
);
|
|
53
84
|
}
|
|
54
85
|
|
|
55
|
-
function installGitHooksInto(
|
|
56
|
-
const
|
|
57
|
-
ensureDirectory(resolvedTarget);
|
|
58
|
-
const gitTargets = resolveGitTargets(resolvedTarget, config);
|
|
59
|
-
for (const gitDir of gitTargets) {
|
|
86
|
+
function installGitHooksInto(target, cacheBaseDir = process.cwd()) {
|
|
87
|
+
for (const gitDir of target.gitDirs) {
|
|
60
88
|
installGitHooks(gitDir, cacheBaseDir);
|
|
61
89
|
}
|
|
62
90
|
}
|
|
@@ -87,6 +115,63 @@ function resolveGitTargets(targetDir, config = {}) {
|
|
|
87
115
|
});
|
|
88
116
|
}
|
|
89
117
|
|
|
118
|
+
function validateTargets(targetDirs, config = {}) {
|
|
119
|
+
return targetDirs.map((targetDir) => validateTarget(targetDir, config));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function validateTarget(targetDir, config = {}) {
|
|
123
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
124
|
+
assertReachableDirectory(resolvedTarget);
|
|
125
|
+
const gitDirs = resolveGitTargets(resolvedTarget, config);
|
|
126
|
+
|
|
127
|
+
if (gitDirs.length === 0) {
|
|
128
|
+
throw new Error(`Target directory must contain a reachable .git directory: ${resolvedTarget}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const configured = Array.isArray(config.gitDirs) ? config.gitDirs : [];
|
|
132
|
+
if (configured.length > 0) {
|
|
133
|
+
const missing = configured
|
|
134
|
+
.map((gitDir) => path.resolve(resolvedTarget, gitDir))
|
|
135
|
+
.filter((gitDir) => !gitDirs.includes(gitDir));
|
|
136
|
+
if (missing.length > 0) {
|
|
137
|
+
throw new Error(`Configured gitDirs are not reachable in target directory: ${missing.join(", ")}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return { targetDir: resolvedTarget, gitDirs };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function assertReachableDirectory(directory) {
|
|
145
|
+
let stat;
|
|
146
|
+
try {
|
|
147
|
+
stat = fs.statSync(directory);
|
|
148
|
+
} catch {
|
|
149
|
+
throw new Error(`Target directory does not exist or is not reachable: ${directory}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!stat.isDirectory()) {
|
|
153
|
+
throw new Error(`Target path is not a directory: ${directory}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
fs.accessSync(directory, fs.constants.R_OK);
|
|
158
|
+
} catch {
|
|
159
|
+
throw new Error(`Target directory is not readable: ${directory}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function assertInstalledBase(cacheBaseDir) {
|
|
164
|
+
const installFile = path.join(cacheBaseDir, CACHE_DIR_NAME, "install.json");
|
|
165
|
+
try {
|
|
166
|
+
const stat = fs.statSync(installFile);
|
|
167
|
+
if (!stat.isFile()) {
|
|
168
|
+
throw new Error("");
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
throw new Error(`Current directory is not an installed x7-code-line base: ${cacheBaseDir}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
90
175
|
function readConfig(configPath) {
|
|
91
176
|
if (!configPath) {
|
|
92
177
|
return {};
|
|
@@ -103,15 +188,26 @@ function readConfig(configPath) {
|
|
|
103
188
|
return config;
|
|
104
189
|
}
|
|
105
190
|
|
|
106
|
-
function normalizeTargetDirs(targetDirs = [], config = {}) {
|
|
191
|
+
function normalizeTargetDirs(targetDirs = [], config = {}, options = {}) {
|
|
107
192
|
const configuredDirs = Array.isArray(config.dirs) ? config.dirs : [];
|
|
108
193
|
const dirs = [...targetDirs, ...configuredDirs].map((dir) => String(dir || "").trim()).filter(Boolean);
|
|
109
194
|
|
|
110
195
|
if (dirs.length === 0) {
|
|
111
|
-
|
|
196
|
+
throw new Error("install requires at least one target directory");
|
|
112
197
|
}
|
|
113
198
|
|
|
114
|
-
|
|
199
|
+
const baseDir = path.resolve(options.pathBaseDir || process.env.INIT_CWD || process.cwd());
|
|
200
|
+
return [...new Set(dirs)].map((dir) => normalizeTargetDir(dir, options.relativePaths, baseDir));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function normalizeTargetDir(dir, relativePaths, baseDir) {
|
|
204
|
+
if (path.isAbsolute(dir)) {
|
|
205
|
+
return path.resolve(dir);
|
|
206
|
+
}
|
|
207
|
+
if (!relativePaths) {
|
|
208
|
+
throw new Error(`Target directory must be an absolute path unless --relative is used: ${dir}`);
|
|
209
|
+
}
|
|
210
|
+
return path.resolve(baseDir, dir);
|
|
115
211
|
}
|
|
116
212
|
|
|
117
213
|
function splitList(value) {
|
|
@@ -165,6 +261,7 @@ function ensureDirectory(directory) {
|
|
|
165
261
|
}
|
|
166
262
|
|
|
167
263
|
module.exports = {
|
|
264
|
+
addDirs,
|
|
168
265
|
CACHE_DIR_NAME,
|
|
169
266
|
install,
|
|
170
267
|
installFromPostinstall
|