helloagents 3.0.23 → 3.0.25
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +15 -8
- package/README_CN.md +15 -8
- package/bootstrap-lite.md +1 -1
- package/bootstrap.md +1 -1
- package/gemini-extension.json +1 -1
- package/install.ps1 +11 -11
- package/package.json +1 -1
- package/scripts/cli-codex-config.mjs +50 -3
- package/scripts/cli-codex-hooks-state.mjs +264 -0
- package/scripts/cli-codex.mjs +21 -14
- package/scripts/cli-doctor-codex.mjs +26 -3
- package/scripts/cli-host-detect.mjs +3 -3
- package/scripts/cli-messages.mjs +1 -1
- package/scripts/cli-utils.mjs +4 -3
- package/scripts/delivery-gate.mjs +20 -11
- package/scripts/notify-closeout.mjs +22 -2
- package/scripts/notify-route.mjs +22 -15
- package/scripts/notify-sound.mjs +94 -0
- package/scripts/notify-ui.mjs +43 -11
- package/scripts/notify.mjs +241 -66
- package/scripts/project-session-cleanup.mjs +27 -1
- package/scripts/ralph-loop.mjs +76 -81
- package/scripts/runtime-scope.mjs +45 -17
- package/scripts/session-capsule.mjs +1 -0
- package/scripts/turn-state-cli.mjs +24 -2
- package/scripts/turn-stop-gate.mjs +7 -5
- package/skills/commands/help/SKILL.md +1 -1
package/scripts/ralph-loop.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { readFileSync } from 'node:fs';
|
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { execSync } from 'node:child_process';
|
|
10
10
|
import { homedir } from 'node:os';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
11
12
|
import { clearVerifyEvidence, detectCommands, hasUnsafeVerifyCommand, writeVerifyEvidence } from './verify-state.mjs';
|
|
12
13
|
import {
|
|
13
14
|
getRuntimeEvidencePath,
|
|
@@ -34,10 +35,6 @@ function readSettings() {
|
|
|
34
35
|
// ── Circuit Breaker (consecutive failure tracking) ───────────────────
|
|
35
36
|
const BREAKER_FILE_NAME = 'loop-breaker.json';
|
|
36
37
|
|
|
37
|
-
function getBreakerPath(cwd, options = {}) {
|
|
38
|
-
return getRuntimeEvidencePath(cwd, BREAKER_FILE_NAME, options);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
38
|
function readBreaker(cwd, options = {}) {
|
|
42
39
|
return readRuntimeEvidence(cwd, BREAKER_FILE_NAME, options)
|
|
43
40
|
|| { consecutive_failures: 0, last_failure: null };
|
|
@@ -92,111 +89,109 @@ function runVerify(commands, cwd) {
|
|
|
92
89
|
return failures;
|
|
93
90
|
}
|
|
94
91
|
|
|
95
|
-
// ── Result Handlers ──────────────────────────────────────────────────
|
|
96
|
-
|
|
97
|
-
function handleSuccess(cwd, isSubagent, options = {}) {
|
|
98
|
-
resetBreaker(cwd, options);
|
|
99
|
-
writeVerifyEvidence(cwd, {
|
|
100
|
-
commands: detectCommands(cwd),
|
|
101
|
-
fastOnly: isSubagent,
|
|
102
|
-
source: isSubagent ? 'subagent' : 'stop',
|
|
103
|
-
}, options);
|
|
104
|
-
|
|
105
|
-
if (isSubagent) {
|
|
106
|
-
process.stdout.write(JSON.stringify({
|
|
107
|
-
hookSpecificOutput: {
|
|
108
|
-
hookEventName: HOOK_EVENT,
|
|
109
|
-
additionalContext: '子代理快速验证通过(lint/typecheck)。请控制器审查变更后继续。',
|
|
110
|
-
},
|
|
111
|
-
suppressOutput: true,
|
|
112
|
-
}));
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Progress detection: warn if claiming done but no git changes
|
|
117
|
-
if (!hasGitChanges(cwd)) {
|
|
118
|
-
process.stdout.write(JSON.stringify({
|
|
119
|
-
hookSpecificOutput: {
|
|
120
|
-
hookEventName: HOOK_EVENT,
|
|
121
|
-
additionalContext: '⚠️ [Ralph Loop] 验证通过但未检测到代码变更(git diff 为空)。如果确实完成了编码任务,请确认变更已保存。',
|
|
122
|
-
},
|
|
123
|
-
suppressOutput: true,
|
|
124
|
-
}));
|
|
125
|
-
} else {
|
|
126
|
-
process.stdout.write(JSON.stringify({ suppressOutput: true }));
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function handleFailure(failures, cwd, options = {}) {
|
|
131
|
-
clearVerifyEvidence(cwd, options);
|
|
132
|
-
const breaker = readBreaker(cwd, options);
|
|
133
|
-
breaker.consecutive_failures += 1;
|
|
134
|
-
breaker.last_failure = new Date().toISOString();
|
|
135
|
-
writeBreaker(cwd, breaker, options);
|
|
136
|
-
|
|
137
|
-
const breakerWarning = breaker.consecutive_failures >= 3
|
|
138
|
-
? `\n\n⚠️ [断路器] 已连续 ${breaker.consecutive_failures} 次验证失败。当前修复思路可能有误,先处理:\n 1. 重新分析根因,不要继续在同一方向上硬修\n 2. 检查是否存在架构层面的问题\n 3. 考虑回退到上一个正常状态重新开始`
|
|
139
|
-
: '';
|
|
140
|
-
|
|
141
|
-
const details = failures.map(f => `\u2717 ${f.cmd}\n${f.output}`).join('\n\n');
|
|
142
|
-
process.stdout.write(JSON.stringify({
|
|
143
|
-
decision: 'block',
|
|
144
|
-
reason: `[Ralph Loop] 验证失败:\n\n${details}\n\n请先修复以上问题,再报告完成。${breakerWarning}`,
|
|
145
|
-
suppressOutput: true,
|
|
146
|
-
}));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
92
|
/** Filter commands to fast checks only for subagent mode. Returns null if no fast commands found. */
|
|
150
93
|
function filterSubagentCommands(commands) {
|
|
151
94
|
const fast = commands.filter(cmd =>
|
|
152
95
|
/lint|typecheck|type-check|ruff check|mypy|eslint|tsc/.test(cmd)
|
|
153
96
|
);
|
|
154
97
|
if (fast.length === 0) {
|
|
155
|
-
|
|
98
|
+
return {
|
|
156
99
|
hookSpecificOutput: {
|
|
157
100
|
hookEventName: HOOK_EVENT,
|
|
158
101
|
additionalContext: '子代理完成。未找到快速验证命令,请控制器手动审查变更。',
|
|
159
102
|
},
|
|
160
103
|
suppressOutput: true,
|
|
161
|
-
}
|
|
162
|
-
return null;
|
|
104
|
+
};
|
|
163
105
|
}
|
|
164
|
-
return fast;
|
|
106
|
+
return { commands: fast };
|
|
165
107
|
}
|
|
166
108
|
|
|
167
|
-
|
|
168
|
-
async function main() {
|
|
109
|
+
export function evaluateRalphLoop(data = {}, runtime = {}) {
|
|
169
110
|
const settings = readSettings();
|
|
170
111
|
if (settings.ralph_loop_enabled === false) {
|
|
171
|
-
|
|
172
|
-
return;
|
|
112
|
+
return { suppressOutput: true };
|
|
173
113
|
}
|
|
174
114
|
|
|
175
|
-
let data = {};
|
|
176
|
-
try { data = JSON.parse(readFileSync(0, 'utf-8')); } catch {}
|
|
177
115
|
const cwd = data.cwd || process.cwd();
|
|
178
116
|
const runtimeOptions = { payload: data };
|
|
117
|
+
const isSubagent = runtime.isSubagent ?? IS_SUBAGENT;
|
|
118
|
+
const hookEventName = runtime.hookEventName || HOOK_EVENT;
|
|
179
119
|
|
|
180
120
|
let commands = detectCommands(cwd);
|
|
181
121
|
if (!commands?.length) {
|
|
182
|
-
|
|
183
|
-
return;
|
|
122
|
+
return { suppressOutput: true };
|
|
184
123
|
}
|
|
185
124
|
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
if (!commands) return;
|
|
125
|
+
if (isSubagent) {
|
|
126
|
+
const filtered = filterSubagentCommands(commands);
|
|
127
|
+
if (!filtered?.commands) return filtered || { suppressOutput: true };
|
|
128
|
+
commands = filtered.commands;
|
|
189
129
|
}
|
|
190
130
|
|
|
191
131
|
const failures = runVerify(commands, cwd);
|
|
192
|
-
if (failures.length === 0)
|
|
193
|
-
|
|
194
|
-
|
|
132
|
+
if (failures.length === 0) {
|
|
133
|
+
resetBreaker(cwd, runtimeOptions);
|
|
134
|
+
writeVerifyEvidence(cwd, {
|
|
135
|
+
commands: detectCommands(cwd),
|
|
136
|
+
fastOnly: isSubagent,
|
|
137
|
+
source: isSubagent ? 'subagent' : 'stop',
|
|
138
|
+
}, runtimeOptions);
|
|
139
|
+
|
|
140
|
+
if (isSubagent) {
|
|
141
|
+
return {
|
|
142
|
+
hookSpecificOutput: {
|
|
143
|
+
hookEventName,
|
|
144
|
+
additionalContext: '子代理快速验证通过(lint/typecheck)。请控制器审查变更后继续。',
|
|
145
|
+
},
|
|
146
|
+
suppressOutput: true,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
195
149
|
|
|
196
|
-
|
|
197
|
-
|
|
150
|
+
if (!hasGitChanges(cwd)) {
|
|
151
|
+
return {
|
|
152
|
+
hookSpecificOutput: {
|
|
153
|
+
hookEventName,
|
|
154
|
+
additionalContext: '⚠️ [Ralph Loop] 验证通过但未检测到代码变更(git diff 为空)。如果确实完成了编码任务,请确认变更已保存。',
|
|
155
|
+
},
|
|
156
|
+
suppressOutput: true,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { suppressOutput: true };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
clearVerifyEvidence(cwd, runtimeOptions);
|
|
164
|
+
const breaker = readBreaker(cwd, runtimeOptions);
|
|
165
|
+
breaker.consecutive_failures += 1;
|
|
166
|
+
breaker.last_failure = new Date().toISOString();
|
|
167
|
+
writeBreaker(cwd, breaker, runtimeOptions);
|
|
168
|
+
|
|
169
|
+
const breakerWarning = breaker.consecutive_failures >= 3
|
|
170
|
+
? `\n\n⚠️ [断路器] 已连续 ${breaker.consecutive_failures} 次验证失败。当前修复思路可能有误,先处理:\n 1. 重新分析根因,不要继续在同一方向上硬修\n 2. 检查是否存在架构层面的问题\n 3. 考虑回退到上一个正常状态重新开始`
|
|
171
|
+
: '';
|
|
172
|
+
const details = failures.map(f => `\u2717 ${f.cmd}\n${f.output}`).join('\n\n');
|
|
173
|
+
return {
|
|
198
174
|
decision: 'block',
|
|
199
|
-
reason: `[Ralph Loop]
|
|
175
|
+
reason: `[Ralph Loop] 验证失败:\n\n${details}\n\n请先修复以上问题,再报告完成。${breakerWarning}`,
|
|
200
176
|
suppressOutput: true,
|
|
201
|
-
}
|
|
202
|
-
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ── Main ──────────────────────────────────────────────────────────────
|
|
181
|
+
function main() {
|
|
182
|
+
let data = {};
|
|
183
|
+
try { data = JSON.parse(readFileSync(0, 'utf-8')); } catch {}
|
|
184
|
+
process.stdout.write(JSON.stringify(evaluateRalphLoop(data)));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
188
|
+
try {
|
|
189
|
+
main();
|
|
190
|
+
} catch (error) {
|
|
191
|
+
process.stdout.write(JSON.stringify({
|
|
192
|
+
decision: 'block',
|
|
193
|
+
reason: `[Ralph Loop] 验证脚本执行异常,已暂停完成通知。\n原因:${error?.message || error}`,
|
|
194
|
+
suppressOutput: true,
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -18,6 +18,12 @@ export const DEFAULT_STATE_SESSION_TOKEN = 'default'
|
|
|
18
18
|
export const USER_RUNTIME_DIR_NAME = 'runtime'
|
|
19
19
|
export { cleanupUserRuntimeRoot, getUserRuntimeRoot, USER_RUNTIME_MAX_AGE_MS }
|
|
20
20
|
|
|
21
|
+
const gitTopLevelCache = new Map()
|
|
22
|
+
const gitBranchNameCache = new Map()
|
|
23
|
+
const gitShortHeadCache = new Map()
|
|
24
|
+
const workspaceNameCache = new Map()
|
|
25
|
+
let userRuntimeCleanupDone = false
|
|
26
|
+
|
|
21
27
|
function normalizePath(filePath = '') {
|
|
22
28
|
return filePath ? normalize(resolve(filePath)) : ''
|
|
23
29
|
}
|
|
@@ -35,6 +41,13 @@ function runGit(cwd, args = []) {
|
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
|
|
44
|
+
function readCachedValue(cache, key, loader) {
|
|
45
|
+
if (cache.has(key)) return cache.get(key)
|
|
46
|
+
const value = loader()
|
|
47
|
+
cache.set(key, value)
|
|
48
|
+
return value
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
function getHomeDir(env = process.env) {
|
|
39
52
|
return env.HOME || env.USERPROFILE || homedir()
|
|
40
53
|
}
|
|
@@ -55,35 +68,46 @@ function samePath(left, right) {
|
|
|
55
68
|
}
|
|
56
69
|
|
|
57
70
|
function resolveGitTopLevel(cwd) {
|
|
58
|
-
const
|
|
59
|
-
|
|
71
|
+
const normalizedCwd = normalizePath(cwd || process.cwd())
|
|
72
|
+
return readCachedValue(gitTopLevelCache, normalizedCwd, () => {
|
|
73
|
+
const absolute = runGit(normalizedCwd, ['rev-parse', '--path-format=absolute', '--show-toplevel'])
|
|
74
|
+
if (absolute) return normalize(resolve(absolute))
|
|
60
75
|
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
const raw = runGit(normalizedCwd, ['rev-parse', '--show-toplevel'])
|
|
77
|
+
return raw ? normalize(resolve(normalizedCwd, raw)) : ''
|
|
78
|
+
})
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
function resolveGitBranchName(cwd) {
|
|
66
|
-
const
|
|
67
|
-
|
|
82
|
+
const normalizedCwd = normalizePath(cwd || process.cwd())
|
|
83
|
+
return readCachedValue(gitBranchNameCache, normalizedCwd, () => {
|
|
84
|
+
const branchName = runGit(normalizedCwd, ['rev-parse', '--abbrev-ref', 'HEAD'])
|
|
85
|
+
if (branchName && branchName !== 'HEAD') return branchName
|
|
68
86
|
|
|
69
|
-
|
|
70
|
-
|
|
87
|
+
const symbolicName = runGit(normalizedCwd, ['symbolic-ref', '--quiet', '--short', 'HEAD'])
|
|
88
|
+
return symbolicName && symbolicName !== 'HEAD' ? symbolicName : ''
|
|
89
|
+
})
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
function resolveGitShortHead(cwd) {
|
|
74
|
-
|
|
93
|
+
const normalizedCwd = normalizePath(cwd || process.cwd())
|
|
94
|
+
return readCachedValue(gitShortHeadCache, normalizedCwd, () =>
|
|
95
|
+
runGit(normalizedCwd, ['rev-parse', '--short', 'HEAD']))
|
|
75
96
|
}
|
|
76
97
|
|
|
77
98
|
function resolveWorkspaceName(cwd) {
|
|
78
|
-
const
|
|
79
|
-
|
|
99
|
+
const normalizedCwd = normalizePath(cwd || process.cwd())
|
|
100
|
+
return readCachedValue(workspaceNameCache, normalizedCwd, () => {
|
|
101
|
+
const branchName = resolveGitBranchName(normalizedCwd)
|
|
102
|
+
if (branchName) return sanitizeRuntimeSegment(branchName, 'workspace')
|
|
80
103
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
if (resolveGitTopLevel(normalizedCwd)) {
|
|
105
|
+
const shortHead = sanitizeRuntimeSegment(resolveGitShortHead(normalizedCwd), '')
|
|
106
|
+
return shortHead ? `detached-${shortHead}` : 'detached'
|
|
107
|
+
}
|
|
85
108
|
|
|
86
|
-
|
|
109
|
+
return 'workspace'
|
|
110
|
+
})
|
|
87
111
|
}
|
|
88
112
|
|
|
89
113
|
export function sanitizeRuntimeSegment(value = '', fallback = '') {
|
|
@@ -245,6 +269,7 @@ export function writeActiveProjectSession(scope, { host = '', source = '', env =
|
|
|
245
269
|
host,
|
|
246
270
|
source,
|
|
247
271
|
aliases,
|
|
272
|
+
...(current.cleanupCheckedAt ? { cleanupCheckedAt: current.cleanupCheckedAt } : {}),
|
|
248
273
|
updatedAt: new Date().toISOString(),
|
|
249
274
|
})
|
|
250
275
|
return activePath
|
|
@@ -323,7 +348,10 @@ function buildTransientRuntimeDir(cwd, options = {}) {
|
|
|
323
348
|
.update(`${normalizedCwd.toLowerCase()}::${token}`)
|
|
324
349
|
.digest('hex')
|
|
325
350
|
.slice(0, 16)
|
|
326
|
-
|
|
351
|
+
if (!userRuntimeCleanupDone) {
|
|
352
|
+
cleanupUserRuntimeRoot()
|
|
353
|
+
userRuntimeCleanupDone = true
|
|
354
|
+
}
|
|
327
355
|
|
|
328
356
|
return {
|
|
329
357
|
cwd: normalizedCwd,
|
|
@@ -142,6 +142,7 @@ export function clearCapsuleSection(cwd, section, options = {}) {
|
|
|
142
142
|
|
|
143
143
|
const capsule = readSessionCapsule(cwd, options)
|
|
144
144
|
if (!Object.prototype.hasOwnProperty.call(capsule, section)) return false
|
|
145
|
+
if (capsule[section] == null) return false
|
|
145
146
|
capsule[section] = null
|
|
146
147
|
capsule[`${section}UpdatedAt`] = new Date().toISOString()
|
|
147
148
|
writeSessionCapsule(cwd, capsule, options)
|
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from 'node:child_process'
|
|
3
|
-
import {
|
|
3
|
+
import { existsSync, realpathSync } from 'node:fs'
|
|
4
|
+
import { homedir } from 'node:os'
|
|
5
|
+
import { dirname, join, resolve } from 'node:path'
|
|
4
6
|
import { fileURLToPath } from 'node:url'
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
function normalizePath(filePath = '') {
|
|
9
|
+
const resolved = resolve(filePath)
|
|
10
|
+
try {
|
|
11
|
+
return realpathSync(resolved)
|
|
12
|
+
} catch {
|
|
13
|
+
return resolved
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function samePath(left, right) {
|
|
18
|
+
const a = normalizePath(left)
|
|
19
|
+
const b = normalizePath(right)
|
|
20
|
+
return process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const localScriptPath = join(dirname(fileURLToPath(import.meta.url)), 'turn-state.mjs')
|
|
24
|
+
const runtimeScriptPath = join(homedir(), '.helloagents', 'helloagents', 'scripts', 'turn-state.mjs')
|
|
25
|
+
const scriptPath = existsSync(runtimeScriptPath) && !samePath(runtimeScriptPath, localScriptPath)
|
|
26
|
+
? runtimeScriptPath
|
|
27
|
+
: localScriptPath
|
|
28
|
+
|
|
7
29
|
const result = spawnSync(process.execPath, [scriptPath, ...process.argv.slice(2)], {
|
|
8
30
|
stdio: 'inherit',
|
|
9
31
|
windowsHide: true,
|
|
@@ -143,18 +143,20 @@ function validateTurnState(routeContext, turnState, cwd, payload = {}) {
|
|
|
143
143
|
return buildBlockReason(routeContext, `当前 turn-state 为 \`${turnState.kind}\`,不能作为本轮结束状态。`, cwd)
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
function
|
|
147
|
-
const payload = readStdinJson()
|
|
146
|
+
export function evaluateTurnStopGate(payload = {}) {
|
|
148
147
|
const cwd = payload.cwd || process.cwd()
|
|
149
148
|
const routeContext = getApplicableRouteContext({ cwd, payload })
|
|
150
149
|
|
|
151
150
|
if (!routeContext || !ENFORCED_COMMANDS.has(routeContext.skillName)) {
|
|
152
|
-
|
|
153
|
-
return
|
|
151
|
+
return { decision: 'continue' }
|
|
154
152
|
}
|
|
155
153
|
|
|
156
154
|
const reason = validateTurnState(routeContext, getMainTurnState(cwd, payload), cwd, payload)
|
|
157
|
-
|
|
155
|
+
return reason ? { decision: 'block', reason } : { decision: 'continue' }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function main() {
|
|
159
|
+
process.stdout.write(JSON.stringify(evaluateTurnStopGate(readStdinJson())))
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
@@ -39,7 +39,7 @@ Trigger: ~help
|
|
|
39
39
|
完成时:hello-verify, hello-reflect
|
|
40
40
|
|
|
41
41
|
### 当前设置
|
|
42
|
-
优先使用当前会话上下文中已注入的“当前用户设置”、该配置文件原始 JSON 或此前读取结果摘要显示;若会话上下文不存在该信息,或缺少下表任一配置项,才读取一次 `~/.helloagents/helloagents.json
|
|
42
|
+
优先使用当前会话上下文中已注入的“当前用户设置”、该配置文件原始 JSON 或此前读取结果摘要显示;若会话上下文不存在该信息,或缺少下表任一配置项,才读取一次 `~/.helloagents/helloagents.json`,并在后续轮次复用。对 Codex 来说,首次对话前若当前上下文仍缺少这些配置项,或刚经历压缩/恢复后的首次对话,同样先读取一次再继续。
|
|
43
43
|
如果当前 CLI 存在工作区限制导致家目录不可读,则明确说明“无法直接读取配置文件,以下按已注入设置或默认值展示”,不要改用无关工具或伪造已读取结果。
|
|
44
44
|
| 配置项 | 默认值 | 作用 | 适用 CLI |
|
|
45
45
|
|--------|-------|------|---------|
|