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
- # Section 1: Resultado
1142
- echo "## Resultado"
1141
+ # Header
1142
+ echo "# deepflow auto report"
1143
1143
  echo ""
1144
- echo "Status: ${overall_status}"
1144
+ echo "**Status:** ${overall_status} "
1145
+ echo "**Date:** $(date -u '+%Y-%m-%d %H:%M UTC')"
1145
1146
  echo ""
1146
1147
 
1147
- # Winner info (if converged)
1148
- if [[ "$overall_status" == "converged" ]]; then
1149
- for sname in "${all_spec_names[@]}"; do
1150
- local w="$(_spec_get WINNER "$sname")"
1151
- if [[ -n "$w" ]]; then
1152
- local summary=""
1153
- local winner_file="${PROJECT_ROOT}/.deepflow/selection/${sname}-winner.json"
1154
- if [[ -f "$winner_file" ]]; then
1155
- summary="$(grep -o '"winner"[[:space:]]*:[[:space:]]*"[^"]*"' "$winner_file" | sed 's/.*"\([^"]*\)".*/\1/')" || summary=""
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
- echo "Winner: ${w} (spec: ${sname})"
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
- echo ""
1161
- fi
1203
+ if [[ "$has_spikes" == "true" ]]; then
1204
+ echo ""
1205
+ fi
1162
1206
 
1163
- # Per-spec status table
1164
- echo "| Spec | Status | Winner |"
1165
- echo "|------|--------|--------|"
1166
- for sname in "${all_spec_names[@]}"; do
1167
- local s="$(_spec_get STATUS "$sname" "unknown")"
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
- # Section 2: Mudancas
1174
- echo "## Mudancas"
1175
- echo ""
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
- local has_changes=false
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 "### ${sname} (winner: ${w})"
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
- # Section 3: Decisoes
1197
- echo "## Decisoes"
1245
+ # Next steps
1246
+ echo "---"
1198
1247
  echo ""
1199
-
1200
- local decisions_log="${PROJECT_ROOT}/.deepflow/auto-decisions.log"
1201
- if [[ -f "$decisions_log" ]]; then
1202
- cat "$decisions_log"
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 "No decisions logged"
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 [...]` -> bin/deepflow-auto.sh
13
+ // Subcommand routing: `deepflow auto [...]` -> claude --agent .claude/agents/deepflow-auto.md
14
14
  if (process.argv[2] === 'auto') {
15
- const scriptPath = path.join(__dirname, 'deepflow-auto.sh');
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('bash', [scriptPath, ...process.argv.slice(3)], { stdio: 'inherit' });
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
- // Ensure deepflow-auto.sh is executable
154
- const autoScript = path.join(PACKAGE_DIR, 'bin', 'deepflow-auto.sh');
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'));
@@ -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 result = validateSpec(content, { mode });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepflow",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "description": "Stay in flow state - lightweight spec-driven task orchestration for Claude Code",
5
5
  "keywords": [
6
6
  "claude",
@@ -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"]