@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.
Files changed (107) hide show
  1. package/AGENTS.md +162 -0
  2. package/CLAUDE.md +458 -0
  3. package/README.md +306 -0
  4. package/bin/claude-flow-patch.mjs +148 -0
  5. package/check-patches.sh +176 -0
  6. package/lib/categories.json +15 -0
  7. package/lib/common.py +92 -0
  8. package/lib/discover.mjs +181 -0
  9. package/package.json +85 -0
  10. package/patch/010-CF-001-doctor-yaml/README.md +11 -0
  11. package/patch/010-CF-001-doctor-yaml/fix.py +20 -0
  12. package/patch/010-CF-001-doctor-yaml/sentinel +1 -0
  13. package/patch/020-CF-002-config-export-yaml/README.md +11 -0
  14. package/patch/020-CF-002-config-export-yaml/fix.py +130 -0
  15. package/patch/020-CF-002-config-export-yaml/sentinel +1 -0
  16. package/patch/030-DM-001-daemon-log-zero/README.md +12 -0
  17. package/patch/030-DM-001-daemon-log-zero/fix.py +37 -0
  18. package/patch/030-DM-001-daemon-log-zero/sentinel +1 -0
  19. package/patch/040-DM-002-cpu-load-threshold/README.md +11 -0
  20. package/patch/040-DM-002-cpu-load-threshold/fix.py +6 -0
  21. package/patch/040-DM-002-cpu-load-threshold/sentinel +1 -0
  22. package/patch/050-DM-003-macos-freemem/README.md +11 -0
  23. package/patch/050-DM-003-macos-freemem/fix.py +7 -0
  24. package/patch/050-DM-003-macos-freemem/sentinel +1 -0
  25. package/patch/060-DM-004-preload-worker-stub/README.md +11 -0
  26. package/patch/060-DM-004-preload-worker-stub/fix.py +34 -0
  27. package/patch/060-DM-004-preload-worker-stub/sentinel +1 -0
  28. package/patch/070-DM-005-consolidation-worker-stub/README.md +11 -0
  29. package/patch/070-DM-005-consolidation-worker-stub/fix.py +46 -0
  30. package/patch/070-DM-005-consolidation-worker-stub/sentinel +1 -0
  31. package/patch/080-EM-001-embedding-ignores-config/README.md +11 -0
  32. package/patch/080-EM-001-embedding-ignores-config/fix.py +111 -0
  33. package/patch/080-EM-001-embedding-ignores-config/sentinel +1 -0
  34. package/patch/090-EM-002-transformers-cache-eacces/README.md +11 -0
  35. package/patch/090-EM-002-transformers-cache-eacces/fix.sh +12 -0
  36. package/patch/090-EM-002-transformers-cache-eacces/sentinel +1 -0
  37. package/patch/100-GV-001-hnsw-ghost-vectors/README.md +11 -0
  38. package/patch/100-GV-001-hnsw-ghost-vectors/fix.py +34 -0
  39. package/patch/100-GV-001-hnsw-ghost-vectors/sentinel +1 -0
  40. package/patch/110-HK-001-post-edit-file-path/README.md +44 -0
  41. package/patch/110-HK-001-post-edit-file-path/fix.py +23 -0
  42. package/patch/110-HK-001-post-edit-file-path/sentinel +1 -0
  43. package/patch/120-HK-002-hooks-tools-stub/README.md +36 -0
  44. package/patch/120-HK-002-hooks-tools-stub/fix.py +155 -0
  45. package/patch/120-HK-002-hooks-tools-stub/sentinel +1 -0
  46. package/patch/130-HK-003-metrics-hardcoded/README.md +30 -0
  47. package/patch/130-HK-003-metrics-hardcoded/fix.py +82 -0
  48. package/patch/130-HK-003-metrics-hardcoded/sentinel +1 -0
  49. package/patch/140-HW-001-stdin-hang/README.md +11 -0
  50. package/patch/140-HW-001-stdin-hang/fix.py +6 -0
  51. package/patch/140-HW-001-stdin-hang/sentinel +1 -0
  52. package/patch/150-HW-002-failures-swallowed/README.md +11 -0
  53. package/patch/150-HW-002-failures-swallowed/fix.py +42 -0
  54. package/patch/150-HW-002-failures-swallowed/sentinel +1 -0
  55. package/patch/160-HW-003-aggressive-intervals/README.md +11 -0
  56. package/patch/160-HW-003-aggressive-intervals/fix.py +16 -0
  57. package/patch/160-HW-003-aggressive-intervals/sentinel +1 -0
  58. package/patch/170-IN-001-intelligence-stub/README.md +64 -0
  59. package/patch/170-IN-001-intelligence-stub/fix.py +70 -0
  60. package/patch/170-IN-001-intelligence-stub/sentinel +1 -0
  61. package/patch/180-MM-001-memory-persist-path/README.md +27 -0
  62. package/patch/180-MM-001-memory-persist-path/fix.py +54 -0
  63. package/patch/180-MM-001-memory-persist-path/sentinel +1 -0
  64. package/patch/190-NS-001-discovery-default-namespace/README.md +16 -0
  65. package/patch/190-NS-001-discovery-default-namespace/fix.py +68 -0
  66. package/patch/190-NS-001-discovery-default-namespace/sentinel +2 -0
  67. package/patch/200-NS-002-targeted-require-namespace/README.md +19 -0
  68. package/patch/200-NS-002-targeted-require-namespace/fix.py +158 -0
  69. package/patch/200-NS-002-targeted-require-namespace/sentinel +2 -0
  70. package/patch/210-NS-003-namespace-typo-pattern/README.md +15 -0
  71. package/patch/210-NS-003-namespace-typo-pattern/fix.py +23 -0
  72. package/patch/210-NS-003-namespace-typo-pattern/sentinel +1 -0
  73. package/patch/220-RS-001-better-sqlite3-node24/README.md +54 -0
  74. package/patch/220-RS-001-better-sqlite3-node24/fix.py +22 -0
  75. package/patch/220-RS-001-better-sqlite3-node24/rebuild.sh +31 -0
  76. package/patch/220-RS-001-better-sqlite3-node24/sentinel +2 -0
  77. package/patch/230-RV-001-force-learn-tick/README.md +31 -0
  78. package/patch/230-RV-001-force-learn-tick/fix.py +14 -0
  79. package/patch/230-RV-001-force-learn-tick/sentinel +2 -0
  80. package/patch/240-RV-002-trajectory-load/README.md +28 -0
  81. package/patch/240-RV-002-trajectory-load/fix.py +14 -0
  82. package/patch/240-RV-002-trajectory-load/sentinel +2 -0
  83. package/patch/250-RV-003-trajectory-stats-sync/README.md +31 -0
  84. package/patch/250-RV-003-trajectory-stats-sync/fix.py +18 -0
  85. package/patch/250-RV-003-trajectory-stats-sync/sentinel +2 -0
  86. package/patch/260-SG-001-init-settings/README.md +29 -0
  87. package/patch/260-SG-001-init-settings/fix.py +143 -0
  88. package/patch/260-SG-001-init-settings/sentinel +4 -0
  89. package/patch/270-SG-003-init-helpers-all-paths/README.md +60 -0
  90. package/patch/270-SG-003-init-helpers-all-paths/fix.py +164 -0
  91. package/patch/270-SG-003-init-helpers-all-paths/sentinel +3 -0
  92. package/patch/280-UI-001-intelligence-stats-crash/README.md +11 -0
  93. package/patch/280-UI-001-intelligence-stats-crash/fix.py +57 -0
  94. package/patch/280-UI-001-intelligence-stats-crash/sentinel +1 -0
  95. package/patch/290-UI-002-neural-status-not-loaded/README.md +11 -0
  96. package/patch/290-UI-002-neural-status-not-loaded/fix.py +19 -0
  97. package/patch/290-UI-002-neural-status-not-loaded/sentinel +1 -0
  98. package/patch/300-DM-006-log-rotation/README.md +11 -0
  99. package/patch/300-DM-006-log-rotation/fix.py +58 -0
  100. package/patch/300-DM-006-log-rotation/sentinel +1 -0
  101. package/patch/310-HW-004-runwithtimeout-orphan/README.md +11 -0
  102. package/patch/310-HW-004-runwithtimeout-orphan/fix.py +10 -0
  103. package/patch/310-HW-004-runwithtimeout-orphan/sentinel +1 -0
  104. package/patch-all.sh +203 -0
  105. package/repair-post-init.sh +245 -0
  106. package/scripts/update-docs.mjs +208 -0
  107. 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&nbsp;Issue |');
72
+ lines.push('|----|-------------|----------|--------------|');
73
+
74
+ for (const p of items) {
75
+ const idLink = `[${p.id.replace('-', '&#8209;')}](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('');