deepflow 0.1.65 → 0.1.67
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.
|
@@ -1138,70 +1138,129 @@ generate_report() {
|
|
|
1138
1138
|
# Build report
|
|
1139
1139
|
# -----------------------------------------------------------------
|
|
1140
1140
|
{
|
|
1141
|
-
#
|
|
1142
|
-
echo "
|
|
1141
|
+
# Header
|
|
1142
|
+
echo "# deepflow auto report"
|
|
1143
1143
|
echo ""
|
|
1144
|
-
echo "Status
|
|
1144
|
+
echo "**Status:** ${overall_status} "
|
|
1145
|
+
echo "**Date:** $(date -u '+%Y-%m-%d %H:%M UTC')"
|
|
1145
1146
|
echo ""
|
|
1146
1147
|
|
|
1147
|
-
#
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1148
|
+
# Per-spec details
|
|
1149
|
+
for sname in "${all_spec_names[@]}"; do
|
|
1150
|
+
local s="$(_spec_get STATUS "$sname" "unknown")"
|
|
1151
|
+
local w="$(_spec_get WINNER "$sname")"
|
|
1152
|
+
|
|
1153
|
+
echo "---"
|
|
1154
|
+
echo ""
|
|
1155
|
+
echo "## ${sname}"
|
|
1156
|
+
echo ""
|
|
1157
|
+
echo "**Status:** ${s}"
|
|
1158
|
+
if [[ -n "$w" ]]; then
|
|
1159
|
+
echo "**Winner:** ${w}"
|
|
1160
|
+
fi
|
|
1161
|
+
echo ""
|
|
1162
|
+
|
|
1163
|
+
# Hypotheses
|
|
1164
|
+
local hyp_file="${PROJECT_ROOT}/.deepflow/hypotheses/${sname}-cycle-0.json"
|
|
1165
|
+
if [[ -f "$hyp_file" ]]; then
|
|
1166
|
+
echo "### Hypotheses"
|
|
1167
|
+
echo ""
|
|
1168
|
+
# Parse each hypothesis slug + description
|
|
1169
|
+
local slug_list hyp_list
|
|
1170
|
+
slug_list="$(grep -o '"slug"[[:space:]]*:[[:space:]]*"[^"]*"' "$hyp_file" | sed 's/.*"\([^"]*\)".*/\1/')" || slug_list=""
|
|
1171
|
+
hyp_list="$(grep -o '"hypothesis"[[:space:]]*:[[:space:]]*"[^"]*"' "$hyp_file" | sed 's/.*"hypothesis"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')" || hyp_list=""
|
|
1172
|
+
|
|
1173
|
+
paste <(echo "$slug_list") <(echo "$hyp_list") | while IFS=$'\t' read -r hslug hhyp; do
|
|
1174
|
+
[[ -z "$hslug" ]] && continue
|
|
1175
|
+
echo "- **${hslug}:** ${hhyp}"
|
|
1176
|
+
done
|
|
1177
|
+
echo ""
|
|
1178
|
+
fi
|
|
1179
|
+
|
|
1180
|
+
# Spike results
|
|
1181
|
+
local has_spikes=false
|
|
1182
|
+
for wt_dir in "${PROJECT_ROOT}/.deepflow/worktrees/${sname}-"*; do
|
|
1183
|
+
[[ -d "$wt_dir" ]] || continue
|
|
1184
|
+
local wt_slug
|
|
1185
|
+
wt_slug="$(basename "$wt_dir")"
|
|
1186
|
+
wt_slug="${wt_slug#${sname}-}"
|
|
1187
|
+
|
|
1188
|
+
local spike_yaml="${wt_dir}/.deepflow/results/spike-${wt_slug}.yaml"
|
|
1189
|
+
if [[ -f "$spike_yaml" ]]; then
|
|
1190
|
+
if [[ "$has_spikes" == "false" ]]; then
|
|
1191
|
+
echo "### Spike Results"
|
|
1192
|
+
echo ""
|
|
1193
|
+
has_spikes=true
|
|
1156
1194
|
fi
|
|
1157
|
-
|
|
1195
|
+
local spike_status spike_summary
|
|
1196
|
+
spike_status="$(grep -m1 '^status:' "$spike_yaml" | sed 's/^status:[[:space:]]*//')" || spike_status="unknown"
|
|
1197
|
+
spike_summary="$(grep -m1 '^summary:' "$spike_yaml" | sed 's/^summary:[[:space:]]*//')" || spike_summary=""
|
|
1198
|
+
local status_icon="✅"
|
|
1199
|
+
[[ "$spike_status" =~ fail ]] && status_icon="❌"
|
|
1200
|
+
echo "- ${status_icon} **${wt_slug}** — ${spike_summary}"
|
|
1158
1201
|
fi
|
|
1159
1202
|
done
|
|
1160
|
-
|
|
1161
|
-
|
|
1203
|
+
if [[ "$has_spikes" == "true" ]]; then
|
|
1204
|
+
echo ""
|
|
1205
|
+
fi
|
|
1162
1206
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
local w="$(_spec_get WINNER "$sname" "-")"
|
|
1169
|
-
echo "| ${sname} | ${s} | ${w} |"
|
|
1170
|
-
done
|
|
1171
|
-
echo ""
|
|
1207
|
+
# Selection rationale
|
|
1208
|
+
local winner_file="${PROJECT_ROOT}/.deepflow/selection/${sname}-winner.json"
|
|
1209
|
+
if [[ -f "$winner_file" ]]; then
|
|
1210
|
+
echo "### Selection Rationale"
|
|
1211
|
+
echo ""
|
|
1172
1212
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1213
|
+
# Parse rankings from winner file
|
|
1214
|
+
local rankings
|
|
1215
|
+
rankings="$(node -e "
|
|
1216
|
+
try {
|
|
1217
|
+
const d = JSON.parse(require('fs').readFileSync('${winner_file}','utf8'));
|
|
1218
|
+
if (d.selection_output && d.selection_output.rankings) {
|
|
1219
|
+
d.selection_output.rankings.forEach(r => {
|
|
1220
|
+
const icon = r.rank === 1 ? '🏆' : ' ';
|
|
1221
|
+
console.log(icon + ' **#' + r.rank + ' ' + r.slug + ':** ' + r.rationale);
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
} catch(e) {}
|
|
1225
|
+
" 2>/dev/null)" || rankings=""
|
|
1226
|
+
|
|
1227
|
+
if [[ -n "$rankings" ]]; then
|
|
1228
|
+
echo "$rankings"
|
|
1229
|
+
fi
|
|
1230
|
+
echo ""
|
|
1231
|
+
fi
|
|
1176
1232
|
|
|
1177
|
-
|
|
1178
|
-
for sname in "${all_spec_names[@]}"; do
|
|
1179
|
-
local w="$(_spec_get WINNER "$sname")"
|
|
1233
|
+
# Changes (git diff stat)
|
|
1180
1234
|
if [[ -n "$w" ]]; then
|
|
1181
|
-
has_changes=true
|
|
1182
1235
|
local branch_name="df/${sname}-${w}"
|
|
1183
|
-
echo "###
|
|
1236
|
+
echo "### Changes"
|
|
1184
1237
|
echo ""
|
|
1185
1238
|
echo '```'
|
|
1186
|
-
git diff --stat "main...${branch_name}" 2>/dev/null || echo "(branch ${branch_name} not found)"
|
|
1239
|
+
git -C "$PROJECT_ROOT" diff --stat "main...${branch_name}" 2>/dev/null || echo "(branch ${branch_name} not found)"
|
|
1187
1240
|
echo '```'
|
|
1188
1241
|
echo ""
|
|
1189
1242
|
fi
|
|
1190
1243
|
done
|
|
1191
|
-
if [[ "$has_changes" == "false" ]]; then
|
|
1192
|
-
echo "No changes selected"
|
|
1193
|
-
echo ""
|
|
1194
|
-
fi
|
|
1195
1244
|
|
|
1196
|
-
#
|
|
1197
|
-
echo "
|
|
1245
|
+
# Next steps
|
|
1246
|
+
echo "---"
|
|
1198
1247
|
echo ""
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
if [[
|
|
1202
|
-
|
|
1248
|
+
echo "## Next Steps"
|
|
1249
|
+
echo ""
|
|
1250
|
+
if [[ "$overall_status" == "converged" ]]; then
|
|
1251
|
+
for sname in "${all_spec_names[@]}"; do
|
|
1252
|
+
local w="$(_spec_get WINNER "$sname")"
|
|
1253
|
+
if [[ -n "$w" ]]; then
|
|
1254
|
+
echo "To merge the winner:"
|
|
1255
|
+
echo '```bash'
|
|
1256
|
+
echo "git merge df/${sname}-${w}"
|
|
1257
|
+
echo '```'
|
|
1258
|
+
fi
|
|
1259
|
+
done
|
|
1260
|
+
elif [[ "$overall_status" == "in-progress" ]]; then
|
|
1261
|
+
echo "Run \`deepflow auto --continue\` to resume."
|
|
1203
1262
|
else
|
|
1204
|
-
echo "
|
|
1263
|
+
echo "Review the spec and run \`deepflow auto\` again."
|
|
1205
1264
|
fi
|
|
1206
1265
|
echo ""
|
|
1207
1266
|
} > "$report_file"
|
package/bin/install.js
CHANGED
|
@@ -10,11 +10,21 @@ const os = require('os');
|
|
|
10
10
|
const readline = require('readline');
|
|
11
11
|
const { execFileSync } = require('child_process');
|
|
12
12
|
|
|
13
|
-
// Subcommand routing: `deepflow auto [...]` ->
|
|
13
|
+
// Subcommand routing: `deepflow auto [...]` -> claude --agent .claude/agents/deepflow-auto.md
|
|
14
14
|
if (process.argv[2] === 'auto') {
|
|
15
|
-
|
|
15
|
+
if (!process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS) {
|
|
16
|
+
console.error('Error: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS environment variable is not set.');
|
|
17
|
+
console.error('');
|
|
18
|
+
console.error('The `deepflow auto` command now uses Claude Code Agent Teams.');
|
|
19
|
+
console.error('To enable it, set the environment variable before running:');
|
|
20
|
+
console.error('');
|
|
21
|
+
console.error(' export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1');
|
|
22
|
+
console.error(' deepflow auto');
|
|
23
|
+
console.error('');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
16
26
|
try {
|
|
17
|
-
execFileSync('
|
|
27
|
+
execFileSync('claude', ['--agent', '.claude/agents/deepflow-auto.md', ...process.argv.slice(3)], { stdio: 'inherit' });
|
|
18
28
|
} catch (e) {
|
|
19
29
|
process.exit(e.status || 1);
|
|
20
30
|
}
|
|
@@ -150,12 +160,8 @@ async function main() {
|
|
|
150
160
|
}
|
|
151
161
|
}
|
|
152
162
|
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
if (fs.existsSync(autoScript)) {
|
|
156
|
-
fs.chmodSync(autoScript, 0o755);
|
|
157
|
-
log('deepflow-auto.sh marked executable');
|
|
158
|
-
}
|
|
163
|
+
// deepflow-auto.sh has been archived; auto mode now uses Agent Teams
|
|
164
|
+
// via `claude --agent .claude/agents/deepflow-auto.md`
|
|
159
165
|
|
|
160
166
|
// Get version from package.json (single source of truth)
|
|
161
167
|
const packageJson = require(path.join(PACKAGE_DIR, 'package.json'));
|
package/hooks/df-spec-lint.js
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
11
11
|
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
12
15
|
// Each entry: [canonical name, ...aliases that also satisfy the requirement]
|
|
13
16
|
const REQUIRED_SECTIONS = [
|
|
14
17
|
['Objective', 'overview', 'goal', 'goals', 'summary'],
|
|
@@ -27,7 +30,7 @@ const REQUIRED_SECTIONS = [
|
|
|
27
30
|
* @param {'interactive'|'auto'} opts.mode
|
|
28
31
|
* @returns {{ hard: string[], advisory: string[] }}
|
|
29
32
|
*/
|
|
30
|
-
function validateSpec(content, { mode = 'interactive' } = {}) {
|
|
33
|
+
function validateSpec(content, { mode = 'interactive', specsDir = null } = {}) {
|
|
31
34
|
const hard = [];
|
|
32
35
|
const advisory = [];
|
|
33
36
|
|
|
@@ -135,6 +138,24 @@ function validateSpec(content, { mode = 'interactive' } = {}) {
|
|
|
135
138
|
advisory.push(`Too many requirements (${seenIds.size}, limit 20)`);
|
|
136
139
|
}
|
|
137
140
|
|
|
141
|
+
// (adv-e) Dependencies reference existing specs
|
|
142
|
+
const depsSection = extractSection(content, 'Dependencies');
|
|
143
|
+
if (depsSection !== null) {
|
|
144
|
+
const depLines = depsSection.split('\n');
|
|
145
|
+
for (const line of depLines) {
|
|
146
|
+
const depMatch = line.match(/depends_on:\s*(.+)/);
|
|
147
|
+
if (depMatch) {
|
|
148
|
+
const specName = depMatch[1].trim();
|
|
149
|
+
if (specsDir) {
|
|
150
|
+
const specPath = path.join(specsDir, `${specName}.md`);
|
|
151
|
+
if (!fs.existsSync(specPath)) {
|
|
152
|
+
advisory.push(`Dependency not found: "${specName}" (no file specs/${specName}.md)`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
138
159
|
// ── Auto-mode escalation ─────────────────────────────────────────────
|
|
139
160
|
if (mode === 'auto') {
|
|
140
161
|
hard.push(...advisory.splice(0, advisory.length));
|
|
@@ -180,8 +201,6 @@ function extractSection(content, sectionName) {
|
|
|
180
201
|
|
|
181
202
|
// ── CLI entry point ──────────────────────────────────────────────────────
|
|
182
203
|
if (require.main === module) {
|
|
183
|
-
const fs = require('fs');
|
|
184
|
-
|
|
185
204
|
const filePath = process.argv[2];
|
|
186
205
|
if (!filePath) {
|
|
187
206
|
console.error('Usage: df-spec-lint.js <spec-file.md>');
|
|
@@ -190,7 +209,8 @@ if (require.main === module) {
|
|
|
190
209
|
|
|
191
210
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
192
211
|
const mode = process.argv.includes('--auto') ? 'auto' : 'interactive';
|
|
193
|
-
const
|
|
212
|
+
const specsDir = path.resolve(path.dirname(filePath));
|
|
213
|
+
const result = validateSpec(content, { mode, specsDir });
|
|
194
214
|
|
|
195
215
|
if (result.hard.length > 0) {
|
|
196
216
|
console.error('HARD invariant failures:');
|
package/package.json
CHANGED
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
- [Constraint 1: e.g., "Max file size 10MB"]
|
|
16
16
|
- [Constraint 2: e.g., "Must work offline"]
|
|
17
17
|
|
|
18
|
+
## Dependencies
|
|
19
|
+
|
|
20
|
+
<!-- Optional. List specs that must be completed before this one. -->
|
|
21
|
+
<!-- - depends_on: doing-other-spec-name -->
|
|
22
|
+
|
|
18
23
|
## Out of Scope
|
|
19
24
|
|
|
20
25
|
- [Explicitly excluded: e.g., "Video upload is NOT included"]
|