@sparkleideas/claude-flow-patch 3.1.0-alpha.44.patch.3
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/AGENTS.md +162 -0
- package/CLAUDE.md +458 -0
- package/README.md +306 -0
- package/bin/claude-flow-patch.mjs +148 -0
- package/check-patches.sh +176 -0
- package/lib/categories.json +15 -0
- package/lib/common.py +92 -0
- package/lib/discover.mjs +181 -0
- package/package.json +85 -0
- package/patch/010-CF-001-doctor-yaml/README.md +11 -0
- package/patch/010-CF-001-doctor-yaml/fix.py +20 -0
- package/patch/010-CF-001-doctor-yaml/sentinel +1 -0
- package/patch/020-CF-002-config-export-yaml/README.md +11 -0
- package/patch/020-CF-002-config-export-yaml/fix.py +130 -0
- package/patch/020-CF-002-config-export-yaml/sentinel +1 -0
- package/patch/030-DM-001-daemon-log-zero/README.md +12 -0
- package/patch/030-DM-001-daemon-log-zero/fix.py +37 -0
- package/patch/030-DM-001-daemon-log-zero/sentinel +1 -0
- package/patch/040-DM-002-cpu-load-threshold/README.md +11 -0
- package/patch/040-DM-002-cpu-load-threshold/fix.py +6 -0
- package/patch/040-DM-002-cpu-load-threshold/sentinel +1 -0
- package/patch/050-DM-003-macos-freemem/README.md +11 -0
- package/patch/050-DM-003-macos-freemem/fix.py +7 -0
- package/patch/050-DM-003-macos-freemem/sentinel +1 -0
- package/patch/060-DM-004-preload-worker-stub/README.md +11 -0
- package/patch/060-DM-004-preload-worker-stub/fix.py +34 -0
- package/patch/060-DM-004-preload-worker-stub/sentinel +1 -0
- package/patch/070-DM-005-consolidation-worker-stub/README.md +11 -0
- package/patch/070-DM-005-consolidation-worker-stub/fix.py +46 -0
- package/patch/070-DM-005-consolidation-worker-stub/sentinel +1 -0
- package/patch/080-EM-001-embedding-ignores-config/README.md +11 -0
- package/patch/080-EM-001-embedding-ignores-config/fix.py +111 -0
- package/patch/080-EM-001-embedding-ignores-config/sentinel +1 -0
- package/patch/090-EM-002-transformers-cache-eacces/README.md +11 -0
- package/patch/090-EM-002-transformers-cache-eacces/fix.sh +12 -0
- package/patch/090-EM-002-transformers-cache-eacces/sentinel +1 -0
- package/patch/100-GV-001-hnsw-ghost-vectors/README.md +11 -0
- package/patch/100-GV-001-hnsw-ghost-vectors/fix.py +34 -0
- package/patch/100-GV-001-hnsw-ghost-vectors/sentinel +1 -0
- package/patch/110-HK-001-post-edit-file-path/README.md +44 -0
- package/patch/110-HK-001-post-edit-file-path/fix.py +23 -0
- package/patch/110-HK-001-post-edit-file-path/sentinel +1 -0
- package/patch/120-HK-002-hooks-tools-stub/README.md +36 -0
- package/patch/120-HK-002-hooks-tools-stub/fix.py +155 -0
- package/patch/120-HK-002-hooks-tools-stub/sentinel +1 -0
- package/patch/130-HK-003-metrics-hardcoded/README.md +30 -0
- package/patch/130-HK-003-metrics-hardcoded/fix.py +82 -0
- package/patch/130-HK-003-metrics-hardcoded/sentinel +1 -0
- package/patch/140-HW-001-stdin-hang/README.md +11 -0
- package/patch/140-HW-001-stdin-hang/fix.py +6 -0
- package/patch/140-HW-001-stdin-hang/sentinel +1 -0
- package/patch/150-HW-002-failures-swallowed/README.md +11 -0
- package/patch/150-HW-002-failures-swallowed/fix.py +42 -0
- package/patch/150-HW-002-failures-swallowed/sentinel +1 -0
- package/patch/160-HW-003-aggressive-intervals/README.md +11 -0
- package/patch/160-HW-003-aggressive-intervals/fix.py +16 -0
- package/patch/160-HW-003-aggressive-intervals/sentinel +1 -0
- package/patch/170-IN-001-intelligence-stub/README.md +64 -0
- package/patch/170-IN-001-intelligence-stub/fix.py +70 -0
- package/patch/170-IN-001-intelligence-stub/sentinel +1 -0
- package/patch/180-MM-001-memory-persist-path/README.md +27 -0
- package/patch/180-MM-001-memory-persist-path/fix.py +54 -0
- package/patch/180-MM-001-memory-persist-path/sentinel +1 -0
- package/patch/190-NS-001-discovery-default-namespace/README.md +16 -0
- package/patch/190-NS-001-discovery-default-namespace/fix.py +68 -0
- package/patch/190-NS-001-discovery-default-namespace/sentinel +2 -0
- package/patch/200-NS-002-targeted-require-namespace/README.md +19 -0
- package/patch/200-NS-002-targeted-require-namespace/fix.py +158 -0
- package/patch/200-NS-002-targeted-require-namespace/sentinel +2 -0
- package/patch/210-NS-003-namespace-typo-pattern/README.md +15 -0
- package/patch/210-NS-003-namespace-typo-pattern/fix.py +23 -0
- package/patch/210-NS-003-namespace-typo-pattern/sentinel +1 -0
- package/patch/220-RS-001-better-sqlite3-node24/README.md +54 -0
- package/patch/220-RS-001-better-sqlite3-node24/fix.py +22 -0
- package/patch/220-RS-001-better-sqlite3-node24/rebuild.sh +31 -0
- package/patch/220-RS-001-better-sqlite3-node24/sentinel +2 -0
- package/patch/230-RV-001-force-learn-tick/README.md +31 -0
- package/patch/230-RV-001-force-learn-tick/fix.py +14 -0
- package/patch/230-RV-001-force-learn-tick/sentinel +2 -0
- package/patch/240-RV-002-trajectory-load/README.md +28 -0
- package/patch/240-RV-002-trajectory-load/fix.py +14 -0
- package/patch/240-RV-002-trajectory-load/sentinel +2 -0
- package/patch/250-RV-003-trajectory-stats-sync/README.md +31 -0
- package/patch/250-RV-003-trajectory-stats-sync/fix.py +18 -0
- package/patch/250-RV-003-trajectory-stats-sync/sentinel +2 -0
- package/patch/260-SG-001-init-settings/README.md +29 -0
- package/patch/260-SG-001-init-settings/fix.py +143 -0
- package/patch/260-SG-001-init-settings/sentinel +4 -0
- package/patch/270-SG-003-init-helpers-all-paths/README.md +60 -0
- package/patch/270-SG-003-init-helpers-all-paths/fix.py +164 -0
- package/patch/270-SG-003-init-helpers-all-paths/sentinel +3 -0
- package/patch/280-UI-001-intelligence-stats-crash/README.md +11 -0
- package/patch/280-UI-001-intelligence-stats-crash/fix.py +57 -0
- package/patch/280-UI-001-intelligence-stats-crash/sentinel +1 -0
- package/patch/290-UI-002-neural-status-not-loaded/README.md +11 -0
- package/patch/290-UI-002-neural-status-not-loaded/fix.py +19 -0
- package/patch/290-UI-002-neural-status-not-loaded/sentinel +1 -0
- package/patch/300-DM-006-log-rotation/README.md +11 -0
- package/patch/300-DM-006-log-rotation/fix.py +58 -0
- package/patch/300-DM-006-log-rotation/sentinel +1 -0
- package/patch/310-HW-004-runwithtimeout-orphan/README.md +11 -0
- package/patch/310-HW-004-runwithtimeout-orphan/fix.py +10 -0
- package/patch/310-HW-004-runwithtimeout-orphan/sentinel +1 -0
- package/patch-all.sh +203 -0
- package/repair-post-init.sh +245 -0
- package/scripts/update-docs.mjs +208 -0
- package/scripts/upstream-log.mjs +257 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# repair-post-init.sh
|
|
3
|
+
# Post-init remediation for projects initialized before patch-all.sh.
|
|
4
|
+
#
|
|
5
|
+
# What it does:
|
|
6
|
+
# 1) Finds a patched @claude-flow/cli helper source (local or global npx cache)
|
|
7
|
+
# 2) Backs up target .claude/helpers (default)
|
|
8
|
+
# 3) Rehydrates helper files into target project
|
|
9
|
+
# 4) Preserves/installs a guidance-aware hook-handler when available
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
TARGET_DIR="$(pwd)"
|
|
15
|
+
SOURCE_SCOPE="auto" # auto|local|global
|
|
16
|
+
DO_BACKUP=1
|
|
17
|
+
DRY_RUN=0
|
|
18
|
+
RUN_CHECK=1
|
|
19
|
+
|
|
20
|
+
usage() {
|
|
21
|
+
cat <<'EOF'
|
|
22
|
+
Usage:
|
|
23
|
+
bash repair-post-init.sh [options]
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--target <dir> Target project directory (default: current working directory)
|
|
27
|
+
--source <mode> Source mode: auto|local|global (default: auto)
|
|
28
|
+
--no-backup Skip .claude/helpers backup
|
|
29
|
+
--dry-run Print actions without writing files
|
|
30
|
+
--skip-check Skip check-patches.sh preflight
|
|
31
|
+
-h, --help Show help
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
bash repair-post-init.sh --target ~/src/my-project
|
|
35
|
+
bash repair-post-init.sh --target ~/src/my-project --source global
|
|
36
|
+
bash repair-post-init.sh --target ~/src/my-project --dry-run
|
|
37
|
+
EOF
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fail() {
|
|
41
|
+
echo "[repair-post-init] ERROR: $*" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
log() {
|
|
46
|
+
echo "[repair-post-init] $*"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
run_cmd() {
|
|
50
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
51
|
+
echo "[dry-run] $*"
|
|
52
|
+
return 0
|
|
53
|
+
fi
|
|
54
|
+
"$@"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
while [[ $# -gt 0 ]]; do
|
|
58
|
+
case "$1" in
|
|
59
|
+
--target)
|
|
60
|
+
TARGET_DIR="${2:-}"
|
|
61
|
+
shift 2
|
|
62
|
+
;;
|
|
63
|
+
--source)
|
|
64
|
+
SOURCE_SCOPE="${2:-}"
|
|
65
|
+
shift 2
|
|
66
|
+
;;
|
|
67
|
+
--no-backup)
|
|
68
|
+
DO_BACKUP=0
|
|
69
|
+
shift
|
|
70
|
+
;;
|
|
71
|
+
--dry-run)
|
|
72
|
+
DRY_RUN=1
|
|
73
|
+
shift
|
|
74
|
+
;;
|
|
75
|
+
--skip-check)
|
|
76
|
+
RUN_CHECK=0
|
|
77
|
+
shift
|
|
78
|
+
;;
|
|
79
|
+
-h|--help)
|
|
80
|
+
usage
|
|
81
|
+
exit 0
|
|
82
|
+
;;
|
|
83
|
+
*)
|
|
84
|
+
fail "Unknown option: $1"
|
|
85
|
+
;;
|
|
86
|
+
esac
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
if [[ ! "$SOURCE_SCOPE" =~ ^(auto|local|global)$ ]]; then
|
|
90
|
+
fail "Invalid --source value: $SOURCE_SCOPE (expected auto|local|global)"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
TARGET_DIR="$(cd "$TARGET_DIR" && pwd)"
|
|
94
|
+
[ -d "$TARGET_DIR" ] || fail "Target directory not found: $TARGET_DIR"
|
|
95
|
+
|
|
96
|
+
if [ "$RUN_CHECK" -eq 1 ]; then
|
|
97
|
+
if [ -x "$SCRIPT_DIR/check-patches.sh" ]; then
|
|
98
|
+
log "Running preflight patch check..."
|
|
99
|
+
bash "$SCRIPT_DIR/check-patches.sh" >/dev/null || {
|
|
100
|
+
log "Patch check failed; applying patches..."
|
|
101
|
+
bash "$SCRIPT_DIR/patch-all.sh" --global >/dev/null
|
|
102
|
+
bash "$SCRIPT_DIR/check-patches.sh" >/dev/null || fail "Patch verification failed"
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
log "check-patches.sh not found; skipping preflight"
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
find_local_helpers() {
|
|
110
|
+
local base="$1"
|
|
111
|
+
for d in "$base" "$base/.." "$base/../.." "$base/../../.."; do
|
|
112
|
+
if [ -d "$d/node_modules/@claude-flow/cli/.claude/helpers" ]; then
|
|
113
|
+
(cd "$d/node_modules/@claude-flow/cli/.claude/helpers" && pwd)
|
|
114
|
+
return 0
|
|
115
|
+
fi
|
|
116
|
+
done
|
|
117
|
+
return 1
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
find_global_helpers() {
|
|
121
|
+
ls -td ~/.npm/_npx/*/node_modules/@claude-flow/cli/.claude/helpers 2>/dev/null | head -1 || true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
SRC_HELPERS=""
|
|
125
|
+
case "$SOURCE_SCOPE" in
|
|
126
|
+
local)
|
|
127
|
+
SRC_HELPERS="$(find_local_helpers "$TARGET_DIR" || true)"
|
|
128
|
+
;;
|
|
129
|
+
global)
|
|
130
|
+
SRC_HELPERS="$(find_global_helpers)"
|
|
131
|
+
;;
|
|
132
|
+
auto)
|
|
133
|
+
SRC_HELPERS="$(find_local_helpers "$TARGET_DIR" || true)"
|
|
134
|
+
if [ -z "$SRC_HELPERS" ]; then
|
|
135
|
+
SRC_HELPERS="$(find_global_helpers)"
|
|
136
|
+
fi
|
|
137
|
+
;;
|
|
138
|
+
esac
|
|
139
|
+
|
|
140
|
+
[ -n "$SRC_HELPERS" ] || fail "Could not locate @claude-flow/cli/.claude/helpers (source=$SOURCE_SCOPE)"
|
|
141
|
+
[ -d "$SRC_HELPERS" ] || fail "Source helpers directory does not exist: $SRC_HELPERS"
|
|
142
|
+
[ -f "$SRC_HELPERS/intelligence.cjs" ] || fail "Source missing intelligence.cjs: $SRC_HELPERS"
|
|
143
|
+
|
|
144
|
+
TARGET_HELPERS="$TARGET_DIR/.claude/helpers"
|
|
145
|
+
BACKUP_PATH="$TARGET_DIR/.claude/helpers.backup.$(date +%Y%m%d-%H%M%S)"
|
|
146
|
+
|
|
147
|
+
log "Target: $TARGET_DIR"
|
|
148
|
+
log "Source helpers: $SRC_HELPERS"
|
|
149
|
+
log "Target helpers: $TARGET_HELPERS"
|
|
150
|
+
|
|
151
|
+
run_cmd mkdir -p "$TARGET_HELPERS"
|
|
152
|
+
|
|
153
|
+
if [ "$DO_BACKUP" -eq 1 ] && [ -d "$TARGET_HELPERS" ]; then
|
|
154
|
+
if [ "$(ls -A "$TARGET_HELPERS" 2>/dev/null || true)" ]; then
|
|
155
|
+
log "Backing up existing helpers -> $BACKUP_PATH"
|
|
156
|
+
run_cmd cp -a "$TARGET_HELPERS" "$BACKUP_PATH"
|
|
157
|
+
fi
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Copy all helpers except hook-handler.cjs first.
|
|
161
|
+
copied=0
|
|
162
|
+
for src in "$SRC_HELPERS"/*; do
|
|
163
|
+
[ -e "$src" ] || continue
|
|
164
|
+
base="$(basename "$src")"
|
|
165
|
+
if [ "$base" = "hook-handler.cjs" ]; then
|
|
166
|
+
continue
|
|
167
|
+
fi
|
|
168
|
+
run_cmd cp -a "$src" "$TARGET_HELPERS/$base"
|
|
169
|
+
copied=$((copied + 1))
|
|
170
|
+
done
|
|
171
|
+
|
|
172
|
+
# Prefer guidance-aware hook-handler when installed in target project.
|
|
173
|
+
GUIDANCE_HANDLER="$TARGET_DIR/node_modules/claude-flow-guidance-implementation/scaffold/.claude/helpers/hook-handler.cjs"
|
|
174
|
+
HOOK_SRC="$SRC_HELPERS/hook-handler.cjs"
|
|
175
|
+
HOOK_REASON="@claude-flow/cli helper template"
|
|
176
|
+
if [ -f "$GUIDANCE_HANDLER" ]; then
|
|
177
|
+
HOOK_SRC="$GUIDANCE_HANDLER"
|
|
178
|
+
HOOK_REASON="guidance implementation hook-handler"
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
if [ -f "$HOOK_SRC" ]; then
|
|
182
|
+
log "Installing hook-handler from: $HOOK_REASON"
|
|
183
|
+
run_cmd cp -a "$HOOK_SRC" "$TARGET_HELPERS/hook-handler.cjs"
|
|
184
|
+
copied=$((copied + 1))
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
# Make guidance hook-handler path resolution work when copied into project .claude/helpers.
|
|
188
|
+
if [ "$DRY_RUN" -eq 0 ] && [ -f "$TARGET_HELPERS/hook-handler.cjs" ]; then
|
|
189
|
+
python3 - "$TARGET_HELPERS/hook-handler.cjs" <<'PY'
|
|
190
|
+
import pathlib
|
|
191
|
+
import sys
|
|
192
|
+
|
|
193
|
+
path = pathlib.Path(sys.argv[1])
|
|
194
|
+
text = path.read_text()
|
|
195
|
+
|
|
196
|
+
start = text.find("function getBundledScriptPath(scriptName)")
|
|
197
|
+
end = text.find("function getGuidanceScriptPath()", start if start >= 0 else 0)
|
|
198
|
+
|
|
199
|
+
if start >= 0 and end > start and "claude-flow-guidance-implementation" not in text:
|
|
200
|
+
replacement = """function getBundledScriptPath(scriptName) {
|
|
201
|
+
return path.join(
|
|
202
|
+
getProjectDir(),
|
|
203
|
+
'node_modules',
|
|
204
|
+
'claude-flow-guidance-implementation',
|
|
205
|
+
'scaffold',
|
|
206
|
+
'scripts',
|
|
207
|
+
scriptName
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function resolveGuidanceScriptPath(scriptName) {
|
|
212
|
+
const localPath = path.join(getProjectDir(), 'scripts', scriptName);
|
|
213
|
+
if (fs.existsSync(localPath)) return localPath;
|
|
214
|
+
|
|
215
|
+
const bundledPath = getBundledScriptPath(scriptName);
|
|
216
|
+
if (fs.existsSync(bundledPath)) return bundledPath;
|
|
217
|
+
|
|
218
|
+
// Keep compatibility for handler copies that live under scaffold/.claude/helpers.
|
|
219
|
+
const relativeBundledPath = path.resolve(__dirname, '..', '..', 'scripts', scriptName);
|
|
220
|
+
if (fs.existsSync(relativeBundledPath)) return relativeBundledPath;
|
|
221
|
+
|
|
222
|
+
return localPath;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
"""
|
|
226
|
+
text = text[:start] + replacement + text[end:]
|
|
227
|
+
path.write_text(text)
|
|
228
|
+
PY
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
if [ "$DRY_RUN" -eq 0 ] && [ -f "$TARGET_HELPERS/hook-handler.cjs" ]; then
|
|
232
|
+
chmod +x "$TARGET_HELPERS/hook-handler.cjs" || true
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
log "Copied/updated helper files: $copied"
|
|
236
|
+
|
|
237
|
+
if [ "$DRY_RUN" -eq 0 ] && [ -f "$TARGET_HELPERS/hook-handler.cjs" ]; then
|
|
238
|
+
if node "$TARGET_HELPERS/hook-handler.cjs" status >/dev/null 2>&1; then
|
|
239
|
+
log "Smoke check: hook-handler status OK"
|
|
240
|
+
else
|
|
241
|
+
log "WARN: hook-handler status returned non-zero"
|
|
242
|
+
fi
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
log "Done."
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/update-docs.mjs — Regenerate documentation from dynamic patch discovery.
|
|
3
|
+
// Updates: README.md, CLAUDE.md, npm/README.md, npm/config.json
|
|
4
|
+
//
|
|
5
|
+
// Usage: node scripts/update-docs.mjs [--check]
|
|
6
|
+
// --check Exit 1 if docs are out of date (for CI), don't write.
|
|
7
|
+
|
|
8
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { resolve, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { discover } from '../lib/discover.mjs';
|
|
12
|
+
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const ROOT = resolve(__dirname, '..');
|
|
15
|
+
const checkOnly = process.argv.includes('--check');
|
|
16
|
+
|
|
17
|
+
const data = discover();
|
|
18
|
+
const { patches, categories, stats } = data;
|
|
19
|
+
|
|
20
|
+
// ── Helpers ──
|
|
21
|
+
|
|
22
|
+
/** Group patches by prefix, preserving sort order. */
|
|
23
|
+
function groupByPrefix(patches) {
|
|
24
|
+
const groups = new Map();
|
|
25
|
+
for (const p of patches) {
|
|
26
|
+
if (!groups.has(p.prefix)) groups.set(p.prefix, []);
|
|
27
|
+
groups.get(p.prefix).push(p);
|
|
28
|
+
}
|
|
29
|
+
return groups;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Replace content between marker comments in a file.
|
|
34
|
+
* Returns true if content changed.
|
|
35
|
+
*/
|
|
36
|
+
function replaceMarkerSection(filePath, markerName, newContent) {
|
|
37
|
+
const beginMarker = `<!-- GENERATED:${markerName}:begin -->`;
|
|
38
|
+
const endMarker = `<!-- GENERATED:${markerName}:end -->`;
|
|
39
|
+
|
|
40
|
+
const text = readFileSync(filePath, 'utf-8');
|
|
41
|
+
const beginIdx = text.indexOf(beginMarker);
|
|
42
|
+
const endIdx = text.indexOf(endMarker);
|
|
43
|
+
|
|
44
|
+
if (beginIdx < 0 || endIdx < 0) {
|
|
45
|
+
console.error(`ERROR: Markers ${beginMarker} / ${endMarker} not found in ${filePath}`);
|
|
46
|
+
console.error(' Add them around the section that should be auto-generated.');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const before = text.slice(0, beginIdx + beginMarker.length);
|
|
51
|
+
const after = text.slice(endIdx);
|
|
52
|
+
const updated = `${before}\n${newContent}\n${after}`;
|
|
53
|
+
|
|
54
|
+
if (updated === text) return false;
|
|
55
|
+
|
|
56
|
+
if (!checkOnly) writeFileSync(filePath, updated);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Generate README.md defect index ──
|
|
61
|
+
|
|
62
|
+
function generateReadmeIndex() {
|
|
63
|
+
const groups = groupByPrefix(patches);
|
|
64
|
+
const lines = [`${stats.total} defects across ${stats.categories} categories.`];
|
|
65
|
+
|
|
66
|
+
for (const [prefix, items] of groups) {
|
|
67
|
+
const catLabel = categories[prefix] ?? prefix;
|
|
68
|
+
lines.push('');
|
|
69
|
+
lines.push(`### ${prefix} -- ${catLabel}`);
|
|
70
|
+
lines.push('');
|
|
71
|
+
lines.push('| ID | Description <img width="500" height="1" /> | Severity | GitHub Issue |');
|
|
72
|
+
lines.push('|----|-------------|----------|--------------|');
|
|
73
|
+
|
|
74
|
+
for (const p of items) {
|
|
75
|
+
const idLink = `[${p.id.replace('-', '‑')}](patch/${p.dir}/)`;
|
|
76
|
+
const ghLink = p.githubUrl ? `[${p.github}](${p.githubUrl})` : p.github;
|
|
77
|
+
lines.push(`| ${idLink} | ${p.title} | ${p.severity} | ${ghLink} |`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return lines.join('\n');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Generate CLAUDE.md defect tables ──
|
|
85
|
+
|
|
86
|
+
function generateClaudeTables() {
|
|
87
|
+
const groups = groupByPrefix(patches);
|
|
88
|
+
const lines = [];
|
|
89
|
+
|
|
90
|
+
// Category summary table
|
|
91
|
+
lines.push('| Prefix | Category | Count |');
|
|
92
|
+
lines.push('|--------|----------|-------|');
|
|
93
|
+
for (const [prefix, items] of groups) {
|
|
94
|
+
const catLabel = categories[prefix] ?? prefix;
|
|
95
|
+
lines.push(`| ${prefix} | ${catLabel} | ${items.length} |`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Full defect list
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push(`## All ${stats.total} Defects`);
|
|
101
|
+
lines.push('');
|
|
102
|
+
lines.push('| ID | GitHub Issue | Severity |');
|
|
103
|
+
lines.push('|----|-------------|----------|');
|
|
104
|
+
for (const p of patches) {
|
|
105
|
+
const ghText = p.github ? `${p.github} ${p.title}` : p.title;
|
|
106
|
+
const ghLink = p.githubUrl ? `[${ghText}](${p.githubUrl})` : ghText;
|
|
107
|
+
lines.push(`| ${p.id} | ${ghLink} | ${p.severity} |`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return lines.join('\n');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Generate npm/README.md defect list ──
|
|
114
|
+
|
|
115
|
+
const REPO_URL = 'https://github.com/sparkling/claude-flow-patch';
|
|
116
|
+
|
|
117
|
+
function generateNpmDefectList() {
|
|
118
|
+
const groups = groupByPrefix(patches);
|
|
119
|
+
const lines = [
|
|
120
|
+
`${stats.total} tracked defects across ${stats.categories} categories.`,
|
|
121
|
+
'',
|
|
122
|
+
'| Defect | Description | GitHub Issue |',
|
|
123
|
+
'|--------|-------------|-------------|',
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
for (const [, items] of groups) {
|
|
127
|
+
for (const p of items) {
|
|
128
|
+
const defectLink = `[${p.id}](${REPO_URL}/tree/master/patch/${p.dir})`;
|
|
129
|
+
const ghLink = p.githubUrl ? `[${p.github}](${p.githubUrl})` : p.github;
|
|
130
|
+
lines.push(`| ${defectLink} | ${p.title} | ${ghLink} |`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function updateNpmReadme() {
|
|
138
|
+
return replaceMarkerSection(
|
|
139
|
+
resolve(ROOT, 'npm', 'README.md'),
|
|
140
|
+
'npm-defects',
|
|
141
|
+
generateNpmDefectList()
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ── Update npm/config.json counts ──
|
|
146
|
+
|
|
147
|
+
function updateNpmConfig() {
|
|
148
|
+
const filePath = resolve(ROOT, 'npm', 'config.json');
|
|
149
|
+
const config = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
150
|
+
|
|
151
|
+
let changed = false;
|
|
152
|
+
if (config.defects?.total !== stats.total) {
|
|
153
|
+
config.defects.total = stats.total;
|
|
154
|
+
changed = true;
|
|
155
|
+
}
|
|
156
|
+
if (config.defects?.categories !== stats.categories) {
|
|
157
|
+
config.defects.categories = stats.categories;
|
|
158
|
+
changed = true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (changed && !checkOnly) {
|
|
162
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n');
|
|
163
|
+
}
|
|
164
|
+
return changed;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ── Main ──
|
|
168
|
+
|
|
169
|
+
let anyChanged = false;
|
|
170
|
+
|
|
171
|
+
const readmeChanged = replaceMarkerSection(
|
|
172
|
+
resolve(ROOT, 'README.md'),
|
|
173
|
+
'defect-index',
|
|
174
|
+
generateReadmeIndex()
|
|
175
|
+
);
|
|
176
|
+
if (readmeChanged) {
|
|
177
|
+
anyChanged = true;
|
|
178
|
+
console.log(checkOnly ? 'STALE: README.md' : 'Updated: README.md');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const claudeChanged = replaceMarkerSection(
|
|
182
|
+
resolve(ROOT, 'CLAUDE.md'),
|
|
183
|
+
'defect-tables',
|
|
184
|
+
generateClaudeTables()
|
|
185
|
+
);
|
|
186
|
+
if (claudeChanged) {
|
|
187
|
+
anyChanged = true;
|
|
188
|
+
console.log(checkOnly ? 'STALE: CLAUDE.md' : 'Updated: CLAUDE.md');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const npmReadmeChanged = updateNpmReadme();
|
|
192
|
+
if (npmReadmeChanged) {
|
|
193
|
+
anyChanged = true;
|
|
194
|
+
console.log(checkOnly ? 'STALE: npm/README.md' : 'Updated: npm/README.md');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const npmConfigChanged = updateNpmConfig();
|
|
198
|
+
if (npmConfigChanged) {
|
|
199
|
+
anyChanged = true;
|
|
200
|
+
console.log(checkOnly ? 'STALE: npm/config.json' : 'Updated: npm/config.json');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!anyChanged) {
|
|
204
|
+
console.log('All docs are up to date.');
|
|
205
|
+
} else if (checkOnly) {
|
|
206
|
+
console.log('\nDocs are out of date. Run: npm run update-docs');
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/upstream-log.mjs — Show recent @claude-flow/cli releases with changes.
|
|
3
|
+
//
|
|
4
|
+
// Usage:
|
|
5
|
+
// node scripts/upstream-log.mjs [count] # default: 10
|
|
6
|
+
// node scripts/upstream-log.mjs 20 # last 20 releases
|
|
7
|
+
// node scripts/upstream-log.mjs --diff # also diff deps against our baseline
|
|
8
|
+
// node scripts/upstream-log.mjs --full # show complete GitHub issue bodies
|
|
9
|
+
//
|
|
10
|
+
// Requires: npm, gh (optional, for commit messages)
|
|
11
|
+
|
|
12
|
+
import { execSync } from 'node:child_process';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
import { resolve, dirname } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const ROOT = resolve(__dirname, '..');
|
|
19
|
+
|
|
20
|
+
// ── Args ──
|
|
21
|
+
|
|
22
|
+
const flags = ['--diff', '--full'];
|
|
23
|
+
const args = process.argv.slice(2).filter(a => !flags.includes(a));
|
|
24
|
+
const showDiff = process.argv.includes('--diff');
|
|
25
|
+
const fullDetail = process.argv.includes('--full');
|
|
26
|
+
const count = parseInt(args[0], 10) || 10;
|
|
27
|
+
|
|
28
|
+
// ── Our baseline + latest from npm ──
|
|
29
|
+
|
|
30
|
+
let baseline = null;
|
|
31
|
+
try {
|
|
32
|
+
const readme = readFileSync(resolve(ROOT, 'README.md'), 'utf-8');
|
|
33
|
+
const match = readme.match(/@claude-flow\/cli.*?\*\*v?(3\.\d+\.\d+-alpha\.\d+)\*\*/);
|
|
34
|
+
baseline = match?.[1] ?? null;
|
|
35
|
+
} catch { /* no README */ }
|
|
36
|
+
|
|
37
|
+
const latest = run('npm view @claude-flow/cli@latest version').trim() || null;
|
|
38
|
+
|
|
39
|
+
// ── Helpers ──
|
|
40
|
+
|
|
41
|
+
function run(cmd) {
|
|
42
|
+
try {
|
|
43
|
+
return execSync(cmd, { encoding: 'utf-8', timeout: 30000 }).trim();
|
|
44
|
+
} catch {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── Fetch npm version timeline ──
|
|
50
|
+
|
|
51
|
+
const timeJson = run('npm view @claude-flow/cli time --json');
|
|
52
|
+
if (!timeJson) {
|
|
53
|
+
console.error('ERROR: Could not fetch version data from npm.');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const times = JSON.parse(timeJson);
|
|
58
|
+
delete times.created;
|
|
59
|
+
delete times.modified;
|
|
60
|
+
|
|
61
|
+
// Sort by publish date descending, take last N (+1 for window boundary)
|
|
62
|
+
const allVersions = Object.entries(times)
|
|
63
|
+
.sort((a, b) => new Date(b[1]) - new Date(a[1]));
|
|
64
|
+
|
|
65
|
+
const versions = allVersions.slice(0, count);
|
|
66
|
+
|
|
67
|
+
// ── Fetch GitHub commits with timestamps ──
|
|
68
|
+
|
|
69
|
+
const commits = []; // { time: Date, title: string, body: string }
|
|
70
|
+
|
|
71
|
+
function fetchCommits() {
|
|
72
|
+
// Fetch as JSON to preserve full multi-line commit messages
|
|
73
|
+
const raw = run('gh api repos/ruvnet/claude-flow/commits?per_page=100');
|
|
74
|
+
if (!raw) return;
|
|
75
|
+
try {
|
|
76
|
+
const data = JSON.parse(raw);
|
|
77
|
+
for (const item of data) {
|
|
78
|
+
const time = new Date(item.commit?.author?.date);
|
|
79
|
+
const fullMsg = item.commit?.message ?? '';
|
|
80
|
+
const lines = fullMsg.split('\n');
|
|
81
|
+
const title = lines[0] || '';
|
|
82
|
+
// Body is everything after the first blank line
|
|
83
|
+
const blankIdx = lines.findIndex((l, i) => i > 0 && l.trim() === '');
|
|
84
|
+
const body = blankIdx >= 0
|
|
85
|
+
? lines.slice(blankIdx + 1).filter(l => l.trim()).join('\n').trim()
|
|
86
|
+
: '';
|
|
87
|
+
if (!isNaN(time.getTime()) && title) {
|
|
88
|
+
commits.push({ time, title, body });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch { /* gh not available or parse error */ }
|
|
92
|
+
// Sort oldest first for window matching
|
|
93
|
+
commits.sort((a, b) => a.time - b.time);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fetchCommits();
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Find commits between prevTime (exclusive) and thisTime (inclusive).
|
|
100
|
+
* Skip version-bump-only and checkpoint commits.
|
|
101
|
+
*/
|
|
102
|
+
function commitsForWindow(thisTime, prevTime) {
|
|
103
|
+
const start = prevTime ? new Date(prevTime) : new Date(0);
|
|
104
|
+
const end = new Date(thisTime);
|
|
105
|
+
return commits
|
|
106
|
+
.filter(c => c.time > start && c.time <= end)
|
|
107
|
+
.filter(c => !/^Bump to 3\.\d/.test(c.title))
|
|
108
|
+
.filter(c => !/^Checkpoint:/.test(c.title));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── GitHub issue details ──
|
|
112
|
+
|
|
113
|
+
const issueCache = new Map();
|
|
114
|
+
|
|
115
|
+
function fetchIssue(num) {
|
|
116
|
+
if (issueCache.has(num)) return issueCache.get(num);
|
|
117
|
+
const raw = run(`gh api repos/ruvnet/claude-flow/issues/${num}`);
|
|
118
|
+
if (!raw) { issueCache.set(num, null); return null; }
|
|
119
|
+
try {
|
|
120
|
+
const data = JSON.parse(raw);
|
|
121
|
+
const rawBody = (data.body ?? '').trim();
|
|
122
|
+
const labels = (data.labels ?? []).map(l => l.name);
|
|
123
|
+
|
|
124
|
+
// Summary: filtered lines for compact view
|
|
125
|
+
const summary = rawBody.split('\n')
|
|
126
|
+
.filter(l => l.trim())
|
|
127
|
+
.filter(l => !l.startsWith('##'))
|
|
128
|
+
.filter(l => !l.startsWith('```'))
|
|
129
|
+
.filter(l => !l.startsWith('Fixes #'))
|
|
130
|
+
.filter(l => !l.startsWith('Co-Authored'))
|
|
131
|
+
.filter(l => !l.startsWith('🤖'))
|
|
132
|
+
.slice(0, 6)
|
|
133
|
+
.map(l => l.trim());
|
|
134
|
+
|
|
135
|
+
const result = { title: data.title, state: data.state, labels, summary, fullBody: rawBody };
|
|
136
|
+
issueCache.set(num, result);
|
|
137
|
+
return result;
|
|
138
|
+
} catch { issueCache.set(num, null); return null; }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Extract GitHub issue numbers from a commit title, e.g. "(#1165)" -> [1165]
|
|
143
|
+
*/
|
|
144
|
+
function extractIssueRefs(title) {
|
|
145
|
+
const matches = [...title.matchAll(/#(\d+)/g)];
|
|
146
|
+
return matches.map(m => parseInt(m[1], 10));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Dep diff helper ──
|
|
150
|
+
|
|
151
|
+
function getDeps(version) {
|
|
152
|
+
const raw = run(`npm view @claude-flow/cli@${version} dependencies --json`);
|
|
153
|
+
return raw ? JSON.parse(raw) : {};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Output ──
|
|
157
|
+
|
|
158
|
+
console.log(`Last ${versions.length} releases of @claude-flow/cli`);
|
|
159
|
+
if (latest) console.log(`Latest on npm: ${latest}`);
|
|
160
|
+
if (baseline) console.log(`Patch baseline: ${baseline}`);
|
|
161
|
+
if (latest && baseline && latest !== baseline) {
|
|
162
|
+
const ahead = allVersions.findIndex(([v]) => v === baseline) - allVersions.findIndex(([v]) => v === latest);
|
|
163
|
+
if (ahead > 0) console.log(`Upstream is ${ahead} version${ahead > 1 ? 's' : ''} ahead of baseline`);
|
|
164
|
+
}
|
|
165
|
+
console.log('');
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < versions.length; i++) {
|
|
168
|
+
const [version, time] = versions[i];
|
|
169
|
+
const date = time.slice(0, 10);
|
|
170
|
+
const isBaseline = version === baseline;
|
|
171
|
+
const marker = isBaseline ? ' <-- patch baseline' : '';
|
|
172
|
+
|
|
173
|
+
console.log(` ${version} ${date}${marker}`);
|
|
174
|
+
|
|
175
|
+
// Previous version's time is the window boundary
|
|
176
|
+
const prevTime = versions[i + 1]?.[1] ?? allVersions[allVersions.indexOf(versions[i]) + 1]?.[1];
|
|
177
|
+
const windowCommits = commitsForWindow(time, prevTime);
|
|
178
|
+
|
|
179
|
+
const seenIssues = new Set();
|
|
180
|
+
for (const c of windowCommits.slice(0, 5)) {
|
|
181
|
+
console.log(` - ${c.title}`);
|
|
182
|
+
if (c.body) {
|
|
183
|
+
const bodyLines = c.body.split('\n')
|
|
184
|
+
.filter(l => l.trim())
|
|
185
|
+
.filter(l => !l.startsWith('Co-Authored-By'))
|
|
186
|
+
.filter(l => !l.startsWith('Published:'));
|
|
187
|
+
for (const bl of (fullDetail ? bodyLines : bodyLines.slice(0, 6))) {
|
|
188
|
+
console.log(` ${bl.trim()}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Fetch linked GitHub issues for additional context
|
|
192
|
+
for (const issueNum of extractIssueRefs(c.title)) {
|
|
193
|
+
if (seenIssues.has(issueNum)) continue;
|
|
194
|
+
seenIssues.add(issueNum);
|
|
195
|
+
const issue = fetchIssue(issueNum);
|
|
196
|
+
if (!issue) continue;
|
|
197
|
+
|
|
198
|
+
if (fullDetail && issue.fullBody) {
|
|
199
|
+
// --full: show complete issue body
|
|
200
|
+
console.log(` Issue #${issueNum}: ${issue.title} [${issue.state}]`);
|
|
201
|
+
if (issue.labels.length > 0) {
|
|
202
|
+
console.log(` Labels: ${issue.labels.join(', ')}`);
|
|
203
|
+
}
|
|
204
|
+
for (const bl of issue.fullBody.split('\n')) {
|
|
205
|
+
console.log(` ${bl}`);
|
|
206
|
+
}
|
|
207
|
+
} else if (issue.summary.length > 0) {
|
|
208
|
+
// Compact: show summary lines not already in the commit body
|
|
209
|
+
const extra = issue.summary.filter(l => !c.body?.includes(l));
|
|
210
|
+
if (extra.length > 0) {
|
|
211
|
+
console.log(` Issue #${issueNum}: ${issue.title} [${issue.state}]`);
|
|
212
|
+
for (const bl of extra.slice(0, 4)) {
|
|
213
|
+
console.log(` ${bl}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (windowCommits.length > 5) {
|
|
220
|
+
console.log(` ... and ${windowCommits.length - 5} more`);
|
|
221
|
+
}
|
|
222
|
+
if (windowCommits.length === 0 && commits.length > 0) {
|
|
223
|
+
console.log(' (version bump only)');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ── Dep diff against baseline ──
|
|
228
|
+
|
|
229
|
+
if (showDiff && baseline) {
|
|
230
|
+
const latest = versions[0]?.[0];
|
|
231
|
+
if (latest && latest !== baseline) {
|
|
232
|
+
console.log('');
|
|
233
|
+
console.log(`Dependency diff: ${baseline} -> ${latest}`);
|
|
234
|
+
console.log('');
|
|
235
|
+
|
|
236
|
+
const oldDeps = getDeps(baseline);
|
|
237
|
+
const newDeps = getDeps(latest);
|
|
238
|
+
|
|
239
|
+
const allKeys = new Set([...Object.keys(oldDeps), ...Object.keys(newDeps)]);
|
|
240
|
+
let anyChange = false;
|
|
241
|
+
|
|
242
|
+
for (const key of [...allKeys].sort()) {
|
|
243
|
+
const oldV = oldDeps[key];
|
|
244
|
+
const newV = newDeps[key];
|
|
245
|
+
if (oldV !== newV) {
|
|
246
|
+
anyChange = true;
|
|
247
|
+
if (!oldV) console.log(` + ${key}: ${newV}`);
|
|
248
|
+
else if (!newV) console.log(` - ${key}: ${oldV}`);
|
|
249
|
+
else console.log(` ~ ${key}: ${oldV} -> ${newV}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!anyChange) console.log(' (no dependency changes)');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
console.log('');
|