@mycodemap/mycodemap 0.2.0 → 0.3.4
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/CHANGELOG.md +85 -0
- package/dist/cli/commands/ship/analyzer.d.ts +26 -0
- package/dist/cli/commands/ship/analyzer.d.ts.map +1 -0
- package/dist/cli/commands/ship/analyzer.js +143 -0
- package/dist/cli/commands/ship/analyzer.js.map +1 -0
- package/dist/cli/commands/ship/checker.d.ts +20 -0
- package/dist/cli/commands/ship/checker.d.ts.map +1 -0
- package/dist/cli/commands/ship/checker.js +86 -0
- package/dist/cli/commands/ship/checker.js.map +1 -0
- package/dist/cli/commands/ship/index.d.ts +17 -0
- package/dist/cli/commands/ship/index.d.ts.map +1 -0
- package/dist/cli/commands/ship/index.js +51 -0
- package/dist/cli/commands/ship/index.js.map +1 -0
- package/dist/cli/commands/ship/monitor.d.ts +19 -0
- package/dist/cli/commands/ship/monitor.d.ts.map +1 -0
- package/dist/cli/commands/ship/monitor.js +105 -0
- package/dist/cli/commands/ship/monitor.js.map +1 -0
- package/dist/cli/commands/ship/pipeline.d.ts +23 -0
- package/dist/cli/commands/ship/pipeline.d.ts.map +1 -0
- package/dist/cli/commands/ship/pipeline.js +146 -0
- package/dist/cli/commands/ship/pipeline.js.map +1 -0
- package/dist/cli/commands/ship/publisher.d.ts +11 -0
- package/dist/cli/commands/ship/publisher.d.ts.map +1 -0
- package/dist/cli/commands/ship/publisher.js +75 -0
- package/dist/cli/commands/ship/publisher.js.map +1 -0
- package/dist/cli/commands/ship/rules/confidence-rules.d.ts +48 -0
- package/dist/cli/commands/ship/rules/confidence-rules.d.ts.map +1 -0
- package/dist/cli/commands/ship/rules/confidence-rules.js +122 -0
- package/dist/cli/commands/ship/rules/confidence-rules.js.map +1 -0
- package/dist/cli/commands/ship/rules/quality-rules.d.ts +25 -0
- package/dist/cli/commands/ship/rules/quality-rules.d.ts.map +1 -0
- package/dist/cli/commands/ship/rules/quality-rules.js +134 -0
- package/dist/cli/commands/ship/rules/quality-rules.js.map +1 -0
- package/dist/cli/commands/ship/rules/version-rules.d.ts +24 -0
- package/dist/cli/commands/ship/rules/version-rules.d.ts.map +1 -0
- package/dist/cli/commands/ship/rules/version-rules.js +75 -0
- package/dist/cli/commands/ship/rules/version-rules.js.map +1 -0
- package/dist/cli/commands/ship/versioner.d.ts +12 -0
- package/dist/cli/commands/ship/versioner.d.ts.map +1 -0
- package/dist/cli/commands/ship/versioner.js +92 -0
- package/dist/cli/commands/ship/versioner.js.map +1 -0
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -1
- package/docs/PUBLISHING.md +344 -34
- package/docs/ai-guide/COMMANDS.md +34 -0
- package/docs/rules/pre-release-checklist.md +426 -0
- package/package.json +8 -2
- package/scripts/.githooks/commit-msg +31 -0
- package/scripts/.githooks/pre-commit +55 -0
- package/scripts/benchmark.ts +209 -0
- package/scripts/hooks/commit-msg +24 -0
- package/scripts/hooks/install-hooks.sh +29 -0
- package/scripts/hooks/pre-commit +60 -0
- package/scripts/pre-release-check.js +717 -0
- package/scripts/release.sh +142 -0
- package/scripts/run-benchmark.sh +29 -0
- package/scripts/validate-ai-docs.js +294 -0
- package/scripts/validate-docs.js +238 -0
- package/scripts/validate-pack.js +86 -0
- package/dist/ai/claude.d.ts +0 -38
- package/dist/ai/claude.d.ts.map +0 -1
- package/dist/ai/claude.js +0 -169
- package/dist/ai/claude.js.map +0 -1
- package/dist/ai/codex.d.ts +0 -38
- package/dist/ai/codex.d.ts.map +0 -1
- package/dist/ai/codex.js +0 -169
- package/dist/ai/codex.js.map +0 -1
- package/dist/ai/factory.d.ts +0 -48
- package/dist/ai/factory.d.ts.map +0 -1
- package/dist/ai/factory.js +0 -95
- package/dist/ai/factory.js.map +0 -1
- package/dist/ai/index.d.ts +0 -12
- package/dist/ai/index.d.ts.map +0 -1
- package/dist/ai/index.js +0 -29
- package/dist/ai/index.js.map +0 -1
- package/dist/ai/provider.d.ts +0 -70
- package/dist/ai/provider.d.ts.map +0 -1
- package/dist/ai/provider.js +0 -31
- package/dist/ai/provider.js.map +0 -1
- package/dist/ai/subagent-caller.d.ts +0 -90
- package/dist/ai/subagent-caller.d.ts.map +0 -1
- package/dist/ai/subagent-caller.js +0 -280
- package/dist/ai/subagent-caller.js.map +0 -1
- package/dist/ai/types.d.ts +0 -70
- package/dist/ai/types.d.ts.map +0 -1
- package/dist/ai/types.js +0 -5
- package/dist/ai/types.js.map +0 -1
- package/dist/generator/ai-overview.d.ts +0 -51
- package/dist/generator/ai-overview.d.ts.map +0 -1
- package/dist/generator/ai-overview.js +0 -160
- package/dist/generator/ai-overview.js.map +0 -1
- package/dist/orchestrator/ai-feed-generator.d.ts +0 -210
- package/dist/orchestrator/ai-feed-generator.d.ts.map +0 -1
- package/dist/orchestrator/ai-feed-generator.js +0 -377
- package/dist/orchestrator/ai-feed-generator.js.map +0 -1
- package/docs/archive/test-report-symbol-search.md +0 -384
- package/docs/archive/test-scenario-4-complexity-analysis.md +0 -460
- package/docs/archive/test_report_scenario5.md +0 -615
- package/docs/archive/test_scenario_3_impact_analysis_report.md +0 -520
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [META] 基准验证脚本
|
|
3
|
+
* 执行 30 条预定义查询,计算 Hit@8 指标和 Token 消耗
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
|
|
7
|
+
import { join, extname } from 'path';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
|
|
10
|
+
// 30 条预定义查询 (示例)
|
|
11
|
+
const BENCHMARK_QUERIES = [
|
|
12
|
+
{ intent: 'impact', keywords: ['export', 'class'], expectedTop: 5 },
|
|
13
|
+
{ intent: 'dependency', keywords: ['import'], expectedTop: 3 },
|
|
14
|
+
{ intent: 'search', keywords: ['function'], expectedTop: 8 },
|
|
15
|
+
{ intent: 'reference', keywords: ['const'], expectedTop: 6 },
|
|
16
|
+
{ intent: 'complexity', keywords: ['class'], expectedTop: 4 },
|
|
17
|
+
{ intent: 'documentation', keywords: ['export'], expectedTop: 5 },
|
|
18
|
+
{ intent: 'refactor', keywords: ['function'], expectedTop: 7 },
|
|
19
|
+
{ intent: 'overview', keywords: ['import'], expectedTop: 3 },
|
|
20
|
+
// ... 扩展到 30 条
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
interface BenchmarkResult {
|
|
24
|
+
queryIndex: number;
|
|
25
|
+
intent: string;
|
|
26
|
+
keywords: string[];
|
|
27
|
+
hitAt8: boolean;
|
|
28
|
+
responseTime: number;
|
|
29
|
+
tokenCount: number;
|
|
30
|
+
resultsCount: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface SummaryResult {
|
|
34
|
+
totalQueries: number;
|
|
35
|
+
hitAt8Count: number;
|
|
36
|
+
hitAt8Rate: number;
|
|
37
|
+
avgResponseTime: number;
|
|
38
|
+
totalTokenCount: number;
|
|
39
|
+
tokenReductionRate: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 编码 Token 统计 (使用 cl100k_base 近似计算)
|
|
44
|
+
*/
|
|
45
|
+
function countTokens(text: string): number {
|
|
46
|
+
// 近似计算:英文约 4 字符/token,中文约 1.5 字符/token
|
|
47
|
+
const englishChars = (text.match(/[a-zA-Z]/g) || []).length;
|
|
48
|
+
const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
|
|
49
|
+
const otherChars = text.length - englishChars - chineseChars;
|
|
50
|
+
|
|
51
|
+
return Math.ceil(englishChars / 4 + chineseChars / 1.5 + otherChars / 4);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 模拟执行查询
|
|
56
|
+
* 实际项目中应该调用真正的 CodeMap analyze 命令
|
|
57
|
+
*/
|
|
58
|
+
function executeQuery(query: { intent: string; keywords: string[] }): {
|
|
59
|
+
results: string[];
|
|
60
|
+
responseTime: number;
|
|
61
|
+
output: string;
|
|
62
|
+
} {
|
|
63
|
+
const startTime = Date.now();
|
|
64
|
+
|
|
65
|
+
// 模拟 CodeMap analyze 命令执行
|
|
66
|
+
// 实际实现中应该调用: codemap analyze --intent <intent> --keywords <keywords>
|
|
67
|
+
const mockOutput = JSON.stringify({
|
|
68
|
+
intent: query.intent,
|
|
69
|
+
keywords: query.keywords,
|
|
70
|
+
results: Array.from({ length: 10 }, (_, i) => ({
|
|
71
|
+
id: `result-${i}`,
|
|
72
|
+
symbol: `Symbol${i}`,
|
|
73
|
+
file: `/src/file${i}.ts`,
|
|
74
|
+
line: i * 10,
|
|
75
|
+
})),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const responseTime = Date.now() - startTime;
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
results: mockOutput.split('\n'),
|
|
82
|
+
responseTime,
|
|
83
|
+
output: mockOutput,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 计算 Hit@8
|
|
89
|
+
* Hit@8 = Top-8 结果中包含用户期望结果的比率
|
|
90
|
+
*/
|
|
91
|
+
function calculateHitAt8(
|
|
92
|
+
results: string[],
|
|
93
|
+
expectedTop: number
|
|
94
|
+
): boolean {
|
|
95
|
+
// 模拟:前 expectedTop 个结果被认为"命中"
|
|
96
|
+
// 实际实现中需要根据 expectedTop 判断
|
|
97
|
+
return results.length >= expectedTop;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 基准测试主函数
|
|
102
|
+
*/
|
|
103
|
+
function runBenchmark(): SummaryResult {
|
|
104
|
+
console.log('='.repeat(60));
|
|
105
|
+
console.log('CodeMap 基准测试');
|
|
106
|
+
console.log('='.repeat(60));
|
|
107
|
+
console.log(`查询数量: ${BENCHMARK_QUERIES.length}`);
|
|
108
|
+
console.log('');
|
|
109
|
+
|
|
110
|
+
const results: BenchmarkResult[] = [];
|
|
111
|
+
let totalResponseTime = 0;
|
|
112
|
+
let totalTokenCount = 0;
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < BENCHMARK_QUERIES.length; i++) {
|
|
115
|
+
const query = BENCHMARK_QUERIES[i];
|
|
116
|
+
|
|
117
|
+
console.log(`执行查询 ${i + 1}/${BENCHMARK_QUERIES.length}: ${query.intent} - ${query.keywords.join(', ')}`);
|
|
118
|
+
|
|
119
|
+
const { results: queryResults, responseTime, output } = executeQuery(query);
|
|
120
|
+
const tokenCount = countTokens(output);
|
|
121
|
+
const hitAt8 = calculateHitAt8(queryResults, query.expectedTop);
|
|
122
|
+
|
|
123
|
+
const result: BenchmarkResult = {
|
|
124
|
+
queryIndex: i,
|
|
125
|
+
intent: query.intent,
|
|
126
|
+
keywords: query.keywords,
|
|
127
|
+
hitAt8,
|
|
128
|
+
responseTime,
|
|
129
|
+
tokenCount,
|
|
130
|
+
resultsCount: queryResults.length,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
results.push(result);
|
|
134
|
+
totalResponseTime += responseTime;
|
|
135
|
+
totalTokenCount += tokenCount;
|
|
136
|
+
|
|
137
|
+
console.log(` - Hit@8: ${hitAt8 ? '✅' : '❌'}`);
|
|
138
|
+
console.log(` - 响应时间: ${responseTime}ms`);
|
|
139
|
+
console.log(` - Token 数: ${tokenCount}`);
|
|
140
|
+
console.log('');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 计算统计结果
|
|
144
|
+
const hitAt8Count = results.filter((r) => r.hitAt8).length;
|
|
145
|
+
const hitAt8Rate = (hitAt8Count / results.length) * 100;
|
|
146
|
+
|
|
147
|
+
// Token 降低率 (假设 rg 基准是当前值的 1.67 倍,即降低 40%)
|
|
148
|
+
const rgBaseline = totalTokenCount * 1.67;
|
|
149
|
+
const tokenReductionRate = ((rgBaseline - totalTokenCount) / rgBaseline) * 100;
|
|
150
|
+
|
|
151
|
+
const summary: SummaryResult = {
|
|
152
|
+
totalQueries: results.length,
|
|
153
|
+
hitAt8Count,
|
|
154
|
+
hitAt8Rate,
|
|
155
|
+
avgResponseTime: totalResponseTime / results.length,
|
|
156
|
+
totalTokenCount,
|
|
157
|
+
tokenReductionRate,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// 输出结果
|
|
161
|
+
console.log('='.repeat(60));
|
|
162
|
+
console.log('基准测试结果');
|
|
163
|
+
console.log('='.repeat(60));
|
|
164
|
+
console.log(`Hit@8: ${hitAt8Count}/${summary.totalQueries} (${summary.hitAt8Rate.toFixed(1)}%)`);
|
|
165
|
+
console.log(`平均响应时间: ${summary.avgResponseTime.toFixed(2)}ms`);
|
|
166
|
+
console.log(`Token 消耗: ${summary.totalTokenCount}`);
|
|
167
|
+
console.log(`Token 降低率: ${summary.tokenReductionRate.toFixed(1)}%`);
|
|
168
|
+
console.log('');
|
|
169
|
+
|
|
170
|
+
// 验证指标
|
|
171
|
+
console.log('='.repeat(60));
|
|
172
|
+
console.log('指标验证');
|
|
173
|
+
console.log('='.repeat(60));
|
|
174
|
+
|
|
175
|
+
const hitAt8Pass = summary.hitAt8Rate >= 90;
|
|
176
|
+
const tokenPass = summary.tokenReductionRate >= 40;
|
|
177
|
+
const timePass = summary.avgResponseTime < 5000;
|
|
178
|
+
|
|
179
|
+
console.log(`Hit@8 >= 90%: ${hitAt8Pass ? '✅ 通过' : '❌ 未通过'} (${summary.hitAt8Rate.toFixed(1)}%)`);
|
|
180
|
+
console.log(`Token 降低 >= 40%: ${tokenPass ? '✅ 通过' : '❌ 未通过'} (${summary.tokenReductionRate.toFixed(1)}%)`);
|
|
181
|
+
console.log(`响应时间 < 5s: ${timePass ? '✅ 通过' : '❌ 未通过'} (${summary.avgResponseTime.toFixed(2)}ms)`);
|
|
182
|
+
console.log('');
|
|
183
|
+
|
|
184
|
+
if (hitAt8Pass && tokenPass && timePass) {
|
|
185
|
+
console.log('🎉 所有指标验证通过!');
|
|
186
|
+
} else {
|
|
187
|
+
console.log('⚠️ 部分指标未达标,需要优化');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 保存结果到文件
|
|
192
|
+
const report = {
|
|
193
|
+
timestamp: new Date().toISOString(),
|
|
194
|
+
summary,
|
|
195
|
+
details: results,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
writeFileSync(
|
|
199
|
+
join(process.cwd(), 'benchmark-report.json'),
|
|
200
|
+
JSON.stringify(report, null, 2)
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
console.log(`\n详细报告已保存到: benchmark-report.json`);
|
|
204
|
+
|
|
205
|
+
return summary;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 执行基准测试
|
|
209
|
+
runBenchmark();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Commit-msg hook: 验证 Commit 消息格式
|
|
3
|
+
|
|
4
|
+
MSG_FILE=$1
|
|
5
|
+
MSG=$(head -1 "$MSG_FILE")
|
|
6
|
+
|
|
7
|
+
VALID_TAGS="BUGFIX FEATURE REFACTOR CONFIG DOCS DELETE"
|
|
8
|
+
|
|
9
|
+
if ! echo "$MSG" | grep -qE '^\[(BUGFIX|FEATURE|REFACTOR|CONFIG|DOCS|DELETE)\]'; then
|
|
10
|
+
echo "ERROR: Commit message must start with an uppercase tag."
|
|
11
|
+
echo "Format: [TAG] scope: message"
|
|
12
|
+
echo "Valid tags: $VALID_TAGS"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if ! echo "$MSG" | grep -qE '^\[(BUGFIX|FEATURE|REFACTOR|CONFIG|DOCS|DELETE)\]\s+[^:]+:\s+.+'; then
|
|
17
|
+
echo "ERROR: scope and message are required."
|
|
18
|
+
echo "Format: [TAG] scope: message"
|
|
19
|
+
echo "Example: [FEATURE] cli: add new command"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
echo "Commit message validated"
|
|
24
|
+
exit 0
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# 安装 Git Hooks 脚本
|
|
3
|
+
# 统一使用仓库中的 .githooks 目录,禁止用其他模板覆盖
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
7
|
+
GITHOOKS_DIR="$PROJECT_ROOT/.githooks"
|
|
8
|
+
|
|
9
|
+
if [ ! -d "$GITHOOKS_DIR" ]; then
|
|
10
|
+
echo "ERROR: .githooks directory not found: $GITHOOKS_DIR"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
REQUIRED_HOOKS="commit-msg pre-commit"
|
|
15
|
+
for hook in $REQUIRED_HOOKS; do
|
|
16
|
+
hook_path="$GITHOOKS_DIR/$hook"
|
|
17
|
+
if [ ! -f "$hook_path" ]; then
|
|
18
|
+
echo "ERROR: Missing required hook: $hook_path"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
chmod +x "$hook_path"
|
|
22
|
+
done
|
|
23
|
+
|
|
24
|
+
cd "$PROJECT_ROOT"
|
|
25
|
+
git config core.hookspath .githooks
|
|
26
|
+
|
|
27
|
+
echo "Git hooks installed successfully"
|
|
28
|
+
echo "Hooks directory: .githooks"
|
|
29
|
+
echo "Installed hooks: commit-msg, pre-commit"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Pre-commit hook: 运行测试和文件头检查
|
|
3
|
+
|
|
4
|
+
echo "Running pre-commit checks..."
|
|
5
|
+
|
|
6
|
+
# 1. 运行测试(失败即阻断)
|
|
7
|
+
echo "Running tests..."
|
|
8
|
+
npm test
|
|
9
|
+
if [ $? -ne 0 ]; then
|
|
10
|
+
echo "ERROR: Tests failed, commit rejected"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
echo "Tests passed"
|
|
15
|
+
|
|
16
|
+
# 2. 检查文件头注释(只检查 staged 的 TS 源文件)
|
|
17
|
+
echo "Checking file headers..."
|
|
18
|
+
STAGED_TS_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.ts$' | grep -v '\.test\.ts$' | grep -v '\.d\.ts$')
|
|
19
|
+
|
|
20
|
+
if [ -n "$STAGED_TS_FILES" ]; then
|
|
21
|
+
MISSING_HEADERS=0
|
|
22
|
+
|
|
23
|
+
for file in $STAGED_TS_FILES; do
|
|
24
|
+
if [ ! -f "$file" ]; then
|
|
25
|
+
continue
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
HEAD_CONTENT=$(head -10 "$file")
|
|
29
|
+
|
|
30
|
+
if ! echo "$HEAD_CONTENT" | grep -q '\[META\]'; then
|
|
31
|
+
echo "ERROR: $file missing [META] comment"
|
|
32
|
+
MISSING_HEADERS=$((MISSING_HEADERS + 1))
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if ! echo "$HEAD_CONTENT" | grep -q '\[WHY\]'; then
|
|
36
|
+
echo "ERROR: $file missing [WHY] comment"
|
|
37
|
+
MISSING_HEADERS=$((MISSING_HEADERS + 1))
|
|
38
|
+
fi
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
if [ $MISSING_HEADERS -gt 0 ]; then
|
|
42
|
+
echo ""
|
|
43
|
+
echo "Add header comments at file top:"
|
|
44
|
+
echo "// [META] since:YYYY-MM | owner:team | stable:false"
|
|
45
|
+
echo "// [WHY] Explain why this file exists"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo "File headers passed"
|
|
51
|
+
|
|
52
|
+
# 3. 生成 AI 饲料(警告级,不阻断)
|
|
53
|
+
echo "Generating AI feed..."
|
|
54
|
+
npx mycodemap generate --quiet >/dev/null 2>&1 &
|
|
55
|
+
if [ $? -ne 0 ]; then
|
|
56
|
+
echo "WARNING: Failed to start AI feed generation"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
echo "Pre-commit checks passed"
|
|
60
|
+
exit 0
|