forge-workflow 0.0.2 → 0.0.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/.claude/commands/plan.md +93 -4
- package/.cline/workflows/dev.md +311 -0
- package/.cline/workflows/plan.md +475 -0
- package/.cline/workflows/premerge.md +176 -0
- package/.cline/workflows/research.md +39 -0
- package/.cline/workflows/review.md +439 -0
- package/.cline/workflows/rollback.md +718 -0
- package/.cline/workflows/ship.md +131 -0
- package/.cline/workflows/sonarcloud.md +146 -0
- package/.cline/workflows/status.md +74 -0
- package/.cline/workflows/validate.md +234 -0
- package/.cline/workflows/verify.md +218 -0
- package/.codex/config.toml +11 -0
- package/.codex/skills/dev/SKILL.md +314 -0
- package/.codex/skills/plan/SKILL.md +478 -0
- package/.codex/skills/premerge/SKILL.md +179 -0
- package/.codex/skills/research/SKILL.md +42 -0
- package/.codex/skills/review/SKILL.md +442 -0
- package/.codex/skills/rollback/SKILL.md +721 -0
- package/.codex/skills/ship/SKILL.md +134 -0
- package/.codex/skills/sonarcloud/SKILL.md +149 -0
- package/.codex/skills/status/SKILL.md +77 -0
- package/.codex/skills/validate/SKILL.md +237 -0
- package/.codex/skills/verify/SKILL.md +221 -0
- package/.cursor/commands/dev.md +311 -0
- package/.cursor/commands/plan.md +475 -0
- package/.cursor/commands/premerge.md +176 -0
- package/.cursor/commands/research.md +39 -0
- package/.cursor/commands/review.md +439 -0
- package/.cursor/commands/rollback.md +718 -0
- package/.cursor/commands/ship.md +131 -0
- package/.cursor/commands/sonarcloud.md +146 -0
- package/.cursor/commands/status.md +74 -0
- package/.cursor/commands/validate.md +234 -0
- package/.cursor/commands/verify.md +218 -0
- package/.cursor/rules/permissions-guidance.mdc +37 -0
- package/.github/prompts/dev.prompt.md +316 -0
- package/.github/prompts/plan.prompt.md +480 -0
- package/.github/prompts/premerge.prompt.md +181 -0
- package/.github/prompts/research.prompt.md +44 -0
- package/.github/prompts/review.prompt.md +444 -0
- package/.github/prompts/rollback.prompt.md +723 -0
- package/.github/prompts/ship.prompt.md +136 -0
- package/.github/prompts/sonarcloud.prompt.md +151 -0
- package/.github/prompts/status.prompt.md +79 -0
- package/.github/prompts/validate.prompt.md +239 -0
- package/.github/prompts/verify.prompt.md +223 -0
- package/.kilocode/workflows/dev.md +315 -0
- package/.kilocode/workflows/plan.md +479 -0
- package/.kilocode/workflows/premerge.md +180 -0
- package/.kilocode/workflows/research.md +43 -0
- package/.kilocode/workflows/review.md +443 -0
- package/.kilocode/workflows/rollback.md +722 -0
- package/.kilocode/workflows/ship.md +135 -0
- package/.kilocode/workflows/sonarcloud.md +150 -0
- package/.kilocode/workflows/status.md +78 -0
- package/.kilocode/workflows/validate.md +238 -0
- package/.kilocode/workflows/verify.md +222 -0
- package/.opencode/commands/dev.md +314 -0
- package/.opencode/commands/plan.md +478 -0
- package/.opencode/commands/premerge.md +179 -0
- package/.opencode/commands/research.md +42 -0
- package/.opencode/commands/review.md +442 -0
- package/.opencode/commands/rollback.md +721 -0
- package/.opencode/commands/ship.md +134 -0
- package/.opencode/commands/sonarcloud.md +149 -0
- package/.opencode/commands/status.md +77 -0
- package/.opencode/commands/validate.md +237 -0
- package/.opencode/commands/verify.md +221 -0
- package/.roo/commands/dev.md +315 -0
- package/.roo/commands/plan.md +479 -0
- package/.roo/commands/premerge.md +180 -0
- package/.roo/commands/research.md +43 -0
- package/.roo/commands/review.md +443 -0
- package/.roo/commands/rollback.md +722 -0
- package/.roo/commands/ship.md +135 -0
- package/.roo/commands/sonarcloud.md +150 -0
- package/.roo/commands/status.md +78 -0
- package/.roo/commands/validate.md +238 -0
- package/.roo/commands/verify.md +222 -0
- package/LICENSE +21 -21
- package/docs/ENHANCED_ONBOARDING.md +2 -2
- package/docs/TOOLCHAIN.md +15 -234
- package/install.sh +32 -36
- package/lib/commands/plan.js +11 -15
- package/lib/commands/recommend.js +2 -2
- package/lib/dep-guard/analyzer.js +294 -0
- package/lib/dep-guard/behavior-detector.js +98 -0
- package/lib/dep-guard/contract-detector.js +162 -0
- package/lib/dep-guard/import-detector.js +498 -0
- package/lib/dep-guard/path-utils.js +13 -0
- package/lib/dep-guard/rubric.js +120 -0
- package/lib/dep-guard/task-parser.js +318 -0
- package/lib/plugin-catalog.js +18 -28
- package/lib/workflow-profiles.js +5 -11
- package/package.json +15 -4
- package/skills/parallel-deep-research/SKILL.md +108 -0
- package/skills/parallel-deep-research/evals/README.md +27 -0
- package/skills/parallel-deep-research/evals/evals.json +62 -0
- package/skills/sonarcloud-analysis/SKILL.md +171 -0
- package/skills/sonarcloud-analysis/evals/README.md +27 -0
- package/skills/sonarcloud-analysis/evals/evals.json +50 -0
- package/skills/sonarcloud-analysis/references/api-reference.md +466 -0
- package/docs/planning/PROGRESS.md +0 -396
- package/docs/plans/.gitkeep +0 -0
- package/docs/plans/2026-02-27-forge-test-suite-v2-decisions.md +0 -21
- package/docs/plans/2026-02-27-forge-test-suite-v2-design.md +0 -362
- package/docs/plans/2026-02-27-forge-test-suite-v2-tasks.md +0 -343
- package/docs/plans/2026-03-02-superpowers-gaps-decisions.md +0 -26
- package/docs/plans/2026-03-02-superpowers-gaps-design.md +0 -239
- package/docs/plans/2026-03-02-superpowers-gaps-tasks.md +0 -260
- package/docs/plans/2026-03-04-agent-command-parity-design.md +0 -163
- package/docs/plans/2026-03-04-verify-worktree-cleanup-decisions.md +0 -7
- package/docs/plans/2026-03-04-verify-worktree-cleanup-design.md +0 -165
- package/docs/plans/2026-03-05-forge-uto-decisions.md +0 -6
- package/docs/plans/2026-03-05-forge-uto-design.md +0 -116
- package/docs/plans/2026-03-05-forge-uto-tasks.md +0 -244
- package/docs/plans/2026-03-10-command-creator-and-eval-decisions.md +0 -52
- package/docs/plans/2026-03-10-command-creator-and-eval-design.md +0 -350
- package/docs/plans/2026-03-10-command-creator-and-eval-tasks.md +0 -426
- package/docs/plans/2026-03-10-stale-workflow-refs-decisions.md +0 -8
- package/docs/plans/2026-03-10-stale-workflow-refs-design.md +0 -80
- package/docs/plans/2026-03-10-stale-workflow-refs-tasks.md +0 -90
- package/docs/plans/2026-03-14-beads-plan-context-decisions.md +0 -9
- package/docs/plans/2026-03-14-beads-plan-context-design.md +0 -171
- package/docs/plans/2026-03-14-beads-plan-context-tasks.md +0 -160
- package/docs/plans/2026-03-14-skill-eval-loop-decisions.md +0 -33
- package/docs/plans/2026-03-14-skill-eval-loop-design.md +0 -118
- package/docs/plans/2026-03-14-skill-eval-loop-results.md +0 -78
- package/docs/plans/2026-03-14-skill-eval-loop-tasks.md +0 -160
- package/docs/plans/2026-03-15-agent-command-parity-v2-decisions.md +0 -11
- package/docs/plans/2026-03-15-agent-command-parity-v2-design.md +0 -145
- package/docs/plans/2026-03-15-agent-command-parity-v2-tasks.md +0 -211
- package/docs/research/TEMPLATE.md +0 -292
- package/docs/research/advanced-testing.md +0 -297
- package/docs/research/agent-permissions.md +0 -167
- package/docs/research/dependency-chain.md +0 -328
- package/docs/research/forge-workflow-v2.md +0 -550
- package/docs/research/plugin-architecture.md +0 -772
- package/docs/research/pr4-cli-automation.md +0 -326
- package/docs/research/premerge-verify-restructure.md +0 -205
- package/docs/research/skills-restructure.md +0 -508
- package/docs/research/sonarcloud-perfection-plan.md +0 -166
- package/docs/research/sonarcloud-quality-gate.md +0 -184
- package/docs/research/superpowers-integration.md +0 -403
- package/docs/research/superpowers.md +0 -319
- package/docs/research/test-environment.md +0 -519
package/install.sh
CHANGED
|
@@ -342,7 +342,7 @@ create_link() {
|
|
|
342
342
|
# Universal SKILL.md content
|
|
343
343
|
SKILL_CONTENT='---
|
|
344
344
|
name: forge-workflow
|
|
345
|
-
description:
|
|
345
|
+
description: 7-stage TDD-first workflow for feature development. Use when building features, fixing bugs, or shipping PRs.
|
|
346
346
|
category: Development Workflow
|
|
347
347
|
tags: [tdd, workflow, pr, git, testing]
|
|
348
348
|
tools: [Bash, Read, Write, Edit, Grep, Glob]
|
|
@@ -360,24 +360,23 @@ Automatically invoke this skill when the user wants to:
|
|
|
360
360
|
- Create a pull request
|
|
361
361
|
- Run the development workflow
|
|
362
362
|
|
|
363
|
-
##
|
|
363
|
+
## 7 Stages
|
|
364
364
|
|
|
365
365
|
| Stage | Command | Description |
|
|
366
366
|
|-------|---------|-------------|
|
|
367
|
-
|
|
|
368
|
-
|
|
|
369
|
-
|
|
|
370
|
-
|
|
|
371
|
-
|
|
|
372
|
-
|
|
|
373
|
-
|
|
|
374
|
-
|
|
|
375
|
-
| 9 | `/verify` | Final documentation verification |
|
|
367
|
+
| utility | `/status` | Check current context, active work, recent completions |
|
|
368
|
+
| 1 | `/plan` | Design intent -> research -> branch + worktree + task list |
|
|
369
|
+
| 2 | `/dev` | TDD development (implementer -> spec review -> quality review) |
|
|
370
|
+
| 3 | `/validate` | Type check, lint, security, tests - all fresh output |
|
|
371
|
+
| 4 | `/ship` | Push branch and create PR with full documentation |
|
|
372
|
+
| 5 | `/review` | Address ALL PR feedback (GitHub Actions, Greptile, SonarCloud) |
|
|
373
|
+
| 6 | `/premerge` | Update docs, hand off PR to user |
|
|
374
|
+
| 7 | `/verify` | Post-merge health check (CI on main, close Beads) |
|
|
376
375
|
|
|
377
376
|
## Workflow Flow
|
|
378
377
|
|
|
379
378
|
```
|
|
380
|
-
/status -> /
|
|
379
|
+
/status -> /plan -> /dev -> /validate -> /ship -> /review -> /premerge -> /verify
|
|
381
380
|
```
|
|
382
381
|
|
|
383
382
|
## Core Principles
|
|
@@ -398,10 +397,10 @@ if [ "$INSTALL_CLAUDE" = true ]; then
|
|
|
398
397
|
mkdir -p .claude/commands .claude/rules .claude/skills/forge-workflow .claude/scripts
|
|
399
398
|
|
|
400
399
|
# Download commands
|
|
401
|
-
for cmd in status
|
|
400
|
+
for cmd in status plan dev validate ship review premerge verify research rollback sonarcloud; do
|
|
402
401
|
curl -fsSL "$BASE_URL/.claude/commands/$cmd.md" -o ".claude/commands/$cmd.md" 2>/dev/null || true
|
|
403
402
|
done
|
|
404
|
-
echo -e " ${GREEN}Downloaded:
|
|
403
|
+
echo -e " ${GREEN}Downloaded: 11 workflow commands${NC}"
|
|
405
404
|
|
|
406
405
|
# Download rules
|
|
407
406
|
curl -fsSL "$BASE_URL/.claude/rules/workflow.md" -o ".claude/rules/workflow.md" 2>/dev/null || true
|
|
@@ -458,14 +457,13 @@ alwaysApply: true
|
|
|
458
457
|
Use these commands via `/command-name`:
|
|
459
458
|
|
|
460
459
|
1. `/status` - Check current context, active work, recent completions
|
|
461
|
-
2. `/
|
|
462
|
-
3. `/
|
|
463
|
-
4. `/
|
|
464
|
-
5. `/
|
|
465
|
-
6. `/
|
|
466
|
-
7. `/
|
|
467
|
-
8. `/
|
|
468
|
-
9. `/verify` - Final documentation verification
|
|
460
|
+
2. `/plan` - Design intent -> research -> branch + worktree + task list
|
|
461
|
+
3. `/dev` - TDD development (implementer -> spec review -> quality review)
|
|
462
|
+
4. `/validate` - Type check, lint, security, tests - all fresh output
|
|
463
|
+
5. `/ship` - Push branch and create PR with full documentation
|
|
464
|
+
6. `/review` - Address ALL PR feedback (GitHub Actions, Greptile, SonarCloud)
|
|
465
|
+
7. `/premerge` - Update docs, hand off PR to user
|
|
466
|
+
8. `/verify` - Post-merge health check (CI on main, close Beads)
|
|
469
467
|
|
|
470
468
|
See AGENTS.md for full workflow details.
|
|
471
469
|
MDC_EOF
|
|
@@ -495,7 +493,7 @@ if [ "$INSTALL_WINDSURF" = true ]; then
|
|
|
495
493
|
filename=$(basename "$cmd")
|
|
496
494
|
strip_frontmatter "$cmd" > ".windsurf/workflows/$filename" 2>/dev/null || cp "$cmd" ".windsurf/workflows/$filename"
|
|
497
495
|
done
|
|
498
|
-
echo -e " ${GREEN}Converted:
|
|
496
|
+
echo -e " ${GREEN}Converted: 11 workflow commands${NC}"
|
|
499
497
|
|
|
500
498
|
# Copy rules
|
|
501
499
|
cp .claude/rules/workflow.md .windsurf/rules/workflow.md 2>/dev/null || true
|
|
@@ -525,7 +523,7 @@ if [ "$INSTALL_KILOCODE" = true ]; then
|
|
|
525
523
|
filename=$(basename "$cmd")
|
|
526
524
|
strip_frontmatter "$cmd" > ".kilocode/workflows/$filename" 2>/dev/null || cp "$cmd" ".kilocode/workflows/$filename"
|
|
527
525
|
done
|
|
528
|
-
echo -e " ${GREEN}Converted:
|
|
526
|
+
echo -e " ${GREEN}Converted: 11 workflow commands${NC}"
|
|
529
527
|
|
|
530
528
|
# Copy rules
|
|
531
529
|
cp .claude/rules/workflow.md .kilocode/rules/workflow.md 2>/dev/null || true
|
|
@@ -552,7 +550,7 @@ if [ "$INSTALL_ANTIGRAVITY" = true ]; then
|
|
|
552
550
|
filename=$(basename "$cmd")
|
|
553
551
|
strip_frontmatter "$cmd" > ".agent/workflows/$filename" 2>/dev/null || cp "$cmd" ".agent/workflows/$filename"
|
|
554
552
|
done
|
|
555
|
-
echo -e " ${GREEN}Converted:
|
|
553
|
+
echo -e " ${GREEN}Converted: 11 workflow commands${NC}"
|
|
556
554
|
|
|
557
555
|
# Copy rules
|
|
558
556
|
cp .claude/rules/workflow.md .agent/rules/workflow.md 2>/dev/null || true
|
|
@@ -583,7 +581,7 @@ if [ "$INSTALL_COPILOT" = true ]; then
|
|
|
583
581
|
filename="${basename_noext}.prompt.md"
|
|
584
582
|
strip_frontmatter "$cmd" > ".github/prompts/$filename" 2>/dev/null || cp "$cmd" ".github/prompts/$filename"
|
|
585
583
|
done
|
|
586
|
-
echo -e " ${GREEN}Converted:
|
|
584
|
+
echo -e " ${GREEN}Converted: 11 workflow prompts${NC}"
|
|
587
585
|
fi
|
|
588
586
|
|
|
589
587
|
# Link copilot-instructions.md -> AGENTS.md
|
|
@@ -615,7 +613,7 @@ if [ "$INSTALL_CONTINUE" = true ]; then
|
|
|
615
613
|
strip_frontmatter "$cmd" 2>/dev/null || cat "$cmd"
|
|
616
614
|
} > ".continue/prompts/$filename"
|
|
617
615
|
done
|
|
618
|
-
echo -e " ${GREEN}Converted:
|
|
616
|
+
echo -e " ${GREEN}Converted: 11 workflow prompts${NC}"
|
|
619
617
|
fi
|
|
620
618
|
|
|
621
619
|
# Create SKILL.md
|
|
@@ -660,7 +658,7 @@ if [ "$INSTALL_OPENCODE" = true ]; then
|
|
|
660
658
|
# Copy commands as-is (same YAML format)
|
|
661
659
|
if [ "$INSTALL_CLAUDE" = true ]; then
|
|
662
660
|
cp .claude/commands/*.md .opencode/commands/ 2>/dev/null || true
|
|
663
|
-
echo -e " ${GREEN}Copied:
|
|
661
|
+
echo -e " ${GREEN}Copied: 11 workflow commands${NC}"
|
|
664
662
|
fi
|
|
665
663
|
|
|
666
664
|
# Create SKILL.md
|
|
@@ -701,7 +699,7 @@ if [ "$INSTALL_ROO" = true ]; then
|
|
|
701
699
|
filename=$(basename "$cmd")
|
|
702
700
|
strip_frontmatter "$cmd" > ".roo/commands/$filename" 2>/dev/null || cp "$cmd" ".roo/commands/$filename"
|
|
703
701
|
done
|
|
704
|
-
echo -e " ${GREEN}Converted:
|
|
702
|
+
echo -e " ${GREEN}Converted: 11 workflow commands${NC}"
|
|
705
703
|
fi
|
|
706
704
|
|
|
707
705
|
# Link .roorules -> AGENTS.md
|
|
@@ -1043,20 +1041,18 @@ echo -e " GET STARTED"
|
|
|
1043
1041
|
echo -e "==============================================${NC}"
|
|
1044
1042
|
echo ""
|
|
1045
1043
|
echo " /status - Check current context"
|
|
1046
|
-
echo " /
|
|
1047
|
-
echo " /
|
|
1048
|
-
echo " /
|
|
1049
|
-
echo " /check - Run validation"
|
|
1044
|
+
echo " /plan - Design intent, research, branch + task list"
|
|
1045
|
+
echo " /dev - TDD development"
|
|
1046
|
+
echo " /validate - Type check, lint, tests, security"
|
|
1050
1047
|
echo " /ship - Create pull request"
|
|
1051
1048
|
echo " /review - Address PR feedback"
|
|
1052
|
-
echo " /
|
|
1053
|
-
echo " /verify -
|
|
1049
|
+
echo " /premerge - Update docs, hand off PR"
|
|
1050
|
+
echo " /verify - Post-merge health check"
|
|
1054
1051
|
echo ""
|
|
1055
1052
|
echo " Full guide: docs/WORKFLOW.md"
|
|
1056
1053
|
echo ""
|
|
1057
1054
|
echo "Optional tools:"
|
|
1058
1055
|
echo " - Beads: $PKG_MANAGER install -g @beads/bd && bd init"
|
|
1059
|
-
echo " - OpenSpec: $PKG_MANAGER install -g @fission-ai/openspec"
|
|
1060
1056
|
echo ""
|
|
1061
1057
|
echo -e "${CYAN}Package manager detected: $PKG_MANAGER${NC}"
|
|
1062
1058
|
echo ""
|
package/lib/commands/plan.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Plan Command -
|
|
2
|
+
* Plan Command - Beads Integration
|
|
3
3
|
* Creates implementation plan after research is complete
|
|
4
4
|
*
|
|
5
5
|
* Security: Uses execFileSync instead of exec/execSync to prevent command injection
|
|
@@ -123,18 +123,18 @@ function readResearchDoc(featureSlug) {
|
|
|
123
123
|
* 3. Default to tactical if no clear signals
|
|
124
124
|
*
|
|
125
125
|
* @param {string} researchContent - Research document markdown content
|
|
126
|
-
* @returns {{type: 'tactical'|'strategic',
|
|
126
|
+
* @returns {{type: 'tactical'|'strategic', requiresDesignDoc: boolean, reason: string}} Scope analysis result
|
|
127
127
|
* @example
|
|
128
128
|
* const scope = detectScope(researchMarkdown);
|
|
129
129
|
* if (scope.type === 'strategic') {
|
|
130
|
-
* console.log('Requires
|
|
130
|
+
* console.log('Requires design doc:', scope.reason);
|
|
131
131
|
* }
|
|
132
132
|
*/
|
|
133
133
|
function detectScope(researchContent) {
|
|
134
134
|
if (!researchContent || typeof researchContent !== 'string') {
|
|
135
135
|
return {
|
|
136
136
|
type: 'tactical',
|
|
137
|
-
|
|
137
|
+
requiresDesignDoc: false,
|
|
138
138
|
reason: 'Invalid research content (defaulting to tactical)',
|
|
139
139
|
};
|
|
140
140
|
}
|
|
@@ -166,7 +166,7 @@ function detectScope(researchContent) {
|
|
|
166
166
|
const type = scopeMatch[1].toLowerCase();
|
|
167
167
|
return {
|
|
168
168
|
type,
|
|
169
|
-
|
|
169
|
+
requiresDesignDoc: type === 'strategic',
|
|
170
170
|
reason: `Explicit scope declaration: ${scopeMatch[1]}`,
|
|
171
171
|
};
|
|
172
172
|
}
|
|
@@ -190,7 +190,7 @@ function detectScope(researchContent) {
|
|
|
190
190
|
if (foundKeywords.length > 0) {
|
|
191
191
|
return {
|
|
192
192
|
type: 'strategic',
|
|
193
|
-
|
|
193
|
+
requiresDesignDoc: true,
|
|
194
194
|
reason: `Strategic keywords detected: ${foundKeywords.join(', ')}`,
|
|
195
195
|
};
|
|
196
196
|
}
|
|
@@ -198,7 +198,7 @@ function detectScope(researchContent) {
|
|
|
198
198
|
// Default to tactical (safe fallback)
|
|
199
199
|
return {
|
|
200
200
|
type: 'tactical',
|
|
201
|
-
|
|
201
|
+
requiresDesignDoc: false,
|
|
202
202
|
reason: 'No strategic indicators found (tactical by default)',
|
|
203
203
|
};
|
|
204
204
|
}
|
|
@@ -244,7 +244,7 @@ function createBeadsIssue(featureName, researchPath, scope) {
|
|
|
244
244
|
if (!slugValidation.valid) {
|
|
245
245
|
return { success: false, error: `Cannot generate valid slug from feature name: ${slugValidation.error}` };
|
|
246
246
|
}
|
|
247
|
-
description += `\n\
|
|
247
|
+
description += `\n\nDesign: docs/plans/${featureSlug}-design.md`;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
// Execute bd create command using execFileSync for safety (OWASP A03)
|
|
@@ -585,12 +585,9 @@ function applyYAGNIFilter({ task, tasks, designDoc } = {}) {
|
|
|
585
585
|
* Strategic workflow (architecture changes, >1 day):
|
|
586
586
|
* 1. Read research document
|
|
587
587
|
* 2. Detect scope (strategic)
|
|
588
|
-
* 3. Create Beads issue with
|
|
588
|
+
* 3. Create Beads issue with design doc link
|
|
589
589
|
* 4. Create feature branch
|
|
590
|
-
*
|
|
591
|
-
* 6. Commit and push proposal
|
|
592
|
-
* 7. Create proposal PR
|
|
593
|
-
* → Next: Wait for proposal approval
|
|
590
|
+
* → Next: Create design doc, then /dev command
|
|
594
591
|
*
|
|
595
592
|
* @param {string} featureName - Feature name (human-readable, e.g., "Payment Integration")
|
|
596
593
|
* @returns {Promise<{
|
|
@@ -598,8 +595,7 @@ function applyYAGNIFilter({ task, tasks, designDoc } = {}) {
|
|
|
598
595
|
* scope?: 'tactical'|'strategic',
|
|
599
596
|
* beadsIssueId?: string,
|
|
600
597
|
* branchName?: string,
|
|
601
|
-
*
|
|
602
|
-
* proposalPR?: {url: string, number: number},
|
|
598
|
+
*
|
|
603
599
|
* summary?: string,
|
|
604
600
|
* nextCommand?: string,
|
|
605
601
|
* error?: string
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
const { scoreImportDependencies } = require('./import-detector.js');
|
|
2
|
+
const { parseTaskFile } = require('./task-parser.js');
|
|
3
|
+
const { scoreContractDependencies } = require('./contract-detector.js');
|
|
4
|
+
const { scoreBehavioralDependencies } = require('./behavior-detector.js');
|
|
5
|
+
const { WEIGHTS: RUBRIC_WEIGHTS, aggregateRubricScores, needsUserEscalation } = require('./rubric.js');
|
|
6
|
+
const { normalizeRepoPath } = require('./path-utils.js');
|
|
7
|
+
|
|
8
|
+
const CONFIDENCE_THRESHOLD = 0.7;
|
|
9
|
+
const DETECTOR_KEYS = Object.freeze([
|
|
10
|
+
'importCallChain',
|
|
11
|
+
'contractDependencies',
|
|
12
|
+
'behavioralDependencies',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
function createZeroedDetectorMap() {
|
|
16
|
+
return DETECTOR_KEYS.reduce((result, key) => {
|
|
17
|
+
result[key] = 0;
|
|
18
|
+
return result;
|
|
19
|
+
}, {});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createEmptyDetectorEvidence() {
|
|
23
|
+
return DETECTOR_KEYS.reduce((result, key) => {
|
|
24
|
+
result[key] = [];
|
|
25
|
+
return result;
|
|
26
|
+
}, {});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeContractToken(contract) {
|
|
30
|
+
if (typeof contract !== 'string') {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const separatorIndex = contract.indexOf(':');
|
|
35
|
+
if (separatorIndex === -1) {
|
|
36
|
+
return contract;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return `${normalizeRepoPath(contract.slice(0, separatorIndex))}${contract.slice(separatorIndex)}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizeContractNotes(notes = '') {
|
|
43
|
+
return notes
|
|
44
|
+
.split(/\r?\n/)
|
|
45
|
+
.map((line) => {
|
|
46
|
+
const marker = ': ';
|
|
47
|
+
if (!line.startsWith('contracts@') || !line.includes(marker)) {
|
|
48
|
+
return line;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const separatorIndex = line.indexOf(marker);
|
|
52
|
+
const prefix = line.slice(0, separatorIndex + marker.length);
|
|
53
|
+
const payload = line.slice(separatorIndex + marker.length);
|
|
54
|
+
const normalizedPayload = payload
|
|
55
|
+
.split(/\s+/)
|
|
56
|
+
.map(normalizeContractToken)
|
|
57
|
+
.join(' ');
|
|
58
|
+
return `${prefix}${normalizedPayload}`;
|
|
59
|
+
})
|
|
60
|
+
.join('\n');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function normalizeIssue(issue, label) {
|
|
64
|
+
if (!issue || typeof issue !== 'object') {
|
|
65
|
+
throw new Error(`${label} is required`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!issue.id || typeof issue.id !== 'string') {
|
|
69
|
+
throw new Error(`${label} id is required`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
id: issue.id,
|
|
74
|
+
title: typeof issue.title === 'string' ? issue.title : '',
|
|
75
|
+
description: typeof issue.description === 'string' ? issue.description : '',
|
|
76
|
+
status: typeof issue.status === 'string' ? issue.status : '',
|
|
77
|
+
contracts: Array.isArray(issue.contracts)
|
|
78
|
+
? issue.contracts.map(normalizeContractToken).filter((contract) => typeof contract === 'string')
|
|
79
|
+
: [],
|
|
80
|
+
files: Array.isArray(issue.files)
|
|
81
|
+
? issue.files
|
|
82
|
+
.filter((file) => typeof file === 'string')
|
|
83
|
+
.map((file) => normalizeRepoPath(file))
|
|
84
|
+
: [],
|
|
85
|
+
notes: typeof issue.notes === 'string' ? normalizeContractNotes(issue.notes) : '',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizePhase3Input(input = {}) {
|
|
90
|
+
const currentIssue = normalizeIssue(input.currentIssue, 'Current issue');
|
|
91
|
+
const openIssues = Array.isArray(input.openIssues)
|
|
92
|
+
? input.openIssues.map((issue) => normalizeIssue(issue, 'Open issue'))
|
|
93
|
+
: [];
|
|
94
|
+
const taskContext = input.taskContext ?? parseTaskFile(input.taskFile);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
currentIssue,
|
|
98
|
+
openIssues,
|
|
99
|
+
taskContext,
|
|
100
|
+
repositoryRoot: typeof input.repositoryRoot === 'string' && input.repositoryRoot
|
|
101
|
+
? input.repositoryRoot
|
|
102
|
+
: process.cwd(),
|
|
103
|
+
thresholds: {
|
|
104
|
+
confidence: CONFIDENCE_THRESHOLD,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function createScaffoldResult(normalizedInput) {
|
|
110
|
+
const detectorScores = createZeroedDetectorMap();
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
currentIssue: normalizedInput.currentIssue,
|
|
114
|
+
taskContext: normalizedInput.taskContext,
|
|
115
|
+
issues: [],
|
|
116
|
+
scores: {
|
|
117
|
+
...detectorScores,
|
|
118
|
+
rubric: 0,
|
|
119
|
+
},
|
|
120
|
+
rubric: {
|
|
121
|
+
score: 0,
|
|
122
|
+
threshold: normalizedInput.thresholds.confidence,
|
|
123
|
+
summary: 'No detector findings yet.',
|
|
124
|
+
weights: RUBRIC_WEIGHTS,
|
|
125
|
+
reasons: [],
|
|
126
|
+
},
|
|
127
|
+
detectorEvidence: createEmptyDetectorEvidence(),
|
|
128
|
+
detectorConflicts: [],
|
|
129
|
+
importCallChain: {
|
|
130
|
+
score: 0,
|
|
131
|
+
evidence: [],
|
|
132
|
+
findings: [],
|
|
133
|
+
},
|
|
134
|
+
contractDependencies: {
|
|
135
|
+
score: 0,
|
|
136
|
+
evidence: [],
|
|
137
|
+
findings: [],
|
|
138
|
+
},
|
|
139
|
+
behavioralDependencies: {
|
|
140
|
+
score: 0,
|
|
141
|
+
evidence: [],
|
|
142
|
+
findings: [],
|
|
143
|
+
uncertain: false,
|
|
144
|
+
},
|
|
145
|
+
confidence: {
|
|
146
|
+
score: 1,
|
|
147
|
+
threshold: normalizedInput.thresholds.confidence,
|
|
148
|
+
belowThreshold: false,
|
|
149
|
+
reasons: [],
|
|
150
|
+
},
|
|
151
|
+
proposals: [],
|
|
152
|
+
needsUserDecision: false,
|
|
153
|
+
metadata: {
|
|
154
|
+
analyzerVersion: 'phase3-v1-scaffold',
|
|
155
|
+
parser: '@babel/parser',
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function inferDetectorType(finding) {
|
|
161
|
+
if (finding.behavioralSignal) {
|
|
162
|
+
return 'behavioralDependencies';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const firstEvidenceType = finding.evidence?.[0]?.type;
|
|
166
|
+
if (firstEvidenceType === 'contract') {
|
|
167
|
+
return 'contractDependencies';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return 'importCallChain';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function formatDetectorList(detectorTypes) {
|
|
174
|
+
const labels = detectorTypes.map((type) => {
|
|
175
|
+
if (type === 'importCallChain') {
|
|
176
|
+
return 'import/call-chain evidence';
|
|
177
|
+
}
|
|
178
|
+
if (type === 'contractDependencies') {
|
|
179
|
+
return 'contract evidence';
|
|
180
|
+
}
|
|
181
|
+
return 'behavioral evidence';
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return labels.join(', ');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function buildDependencyProposals(normalizedInput, findings, rubricResult) {
|
|
188
|
+
if (findings.length === 0) {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const groupedFindings = new Map();
|
|
193
|
+
for (const finding of findings) {
|
|
194
|
+
const entry = groupedFindings.get(finding.targetIssueId) ?? {
|
|
195
|
+
targetIssueId: finding.targetIssueId,
|
|
196
|
+
detectorTypes: new Set(),
|
|
197
|
+
hasBehavioralUncertainty: false,
|
|
198
|
+
};
|
|
199
|
+
entry.detectorTypes.add(inferDetectorType(finding));
|
|
200
|
+
entry.hasBehavioralUncertainty = entry.hasBehavioralUncertainty || Boolean(finding.uncertain);
|
|
201
|
+
groupedFindings.set(finding.targetIssueId, entry);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return Array.from(groupedFindings.values()).map((entry) => {
|
|
205
|
+
const detectorTypes = Array.from(entry.detectorTypes);
|
|
206
|
+
const reasons = [
|
|
207
|
+
`${entry.targetIssueId} appears to consume or rely on logic being changed in ${normalizedInput.currentIssue.id} through ${formatDetectorList(detectorTypes)}.`,
|
|
208
|
+
];
|
|
209
|
+
const cons = [
|
|
210
|
+
'Adds coordination overhead and may delay the dependent issue.',
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
if (entry.hasBehavioralUncertainty) {
|
|
214
|
+
reasons.push('Behavioral evidence is heuristic and should be confirmed before applying the dependency.');
|
|
215
|
+
cons.push('Behavioral evidence is heuristic and may be noisy until a human confirms the dependency.');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (rubricResult.confidence.belowThreshold) {
|
|
219
|
+
reasons.push(`Overall confidence ${rubricResult.confidence.score.toFixed(2)} is below the review threshold.`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
action: 'add-dependency',
|
|
224
|
+
dependentIssueId: entry.targetIssueId,
|
|
225
|
+
dependsOnIssueId: normalizedInput.currentIssue.id,
|
|
226
|
+
detectorTypes,
|
|
227
|
+
requiresApproval: true,
|
|
228
|
+
confidence: rubricResult.confidence.score,
|
|
229
|
+
reasons,
|
|
230
|
+
pros: [
|
|
231
|
+
'Preserves issue independence claims by sequencing affected work explicitly.',
|
|
232
|
+
'Reduces merge-order surprises for downstream logic consumers.',
|
|
233
|
+
],
|
|
234
|
+
cons,
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function analyzePhase3Dependencies(input = {}) {
|
|
240
|
+
const normalizedInput = normalizePhase3Input(input);
|
|
241
|
+
const result = createScaffoldResult(normalizedInput);
|
|
242
|
+
const importResult = scoreImportDependencies(normalizedInput);
|
|
243
|
+
const contractResult = scoreContractDependencies(normalizedInput);
|
|
244
|
+
const behavioralResult = scoreBehavioralDependencies(normalizedInput);
|
|
245
|
+
const rubricResult = aggregateRubricScores({
|
|
246
|
+
importCallChain: importResult,
|
|
247
|
+
contractDependencies: contractResult,
|
|
248
|
+
behavioralDependencies: behavioralResult,
|
|
249
|
+
}, {
|
|
250
|
+
confidenceThreshold: normalizedInput.thresholds.confidence,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
result.issues = [
|
|
254
|
+
...importResult.findings,
|
|
255
|
+
...contractResult.findings,
|
|
256
|
+
...behavioralResult.findings,
|
|
257
|
+
];
|
|
258
|
+
result.scores.importCallChain = importResult.score;
|
|
259
|
+
result.scores.contractDependencies = contractResult.score;
|
|
260
|
+
result.scores.behavioralDependencies = behavioralResult.score;
|
|
261
|
+
result.scores.rubric = rubricResult.total;
|
|
262
|
+
result.rubric = rubricResult;
|
|
263
|
+
result.detectorEvidence.importCallChain = importResult.evidence;
|
|
264
|
+
result.detectorEvidence.contractDependencies = contractResult.evidence;
|
|
265
|
+
result.detectorEvidence.behavioralDependencies = behavioralResult.evidence;
|
|
266
|
+
result.detectorConflicts = rubricResult.detectorConflicts;
|
|
267
|
+
result.confidence = rubricResult.confidence;
|
|
268
|
+
result.needsUserDecision = needsUserEscalation(rubricResult);
|
|
269
|
+
result.proposals = buildDependencyProposals(normalizedInput, result.issues, rubricResult);
|
|
270
|
+
result.importCallChain = {
|
|
271
|
+
score: importResult.score,
|
|
272
|
+
evidence: importResult.evidence,
|
|
273
|
+
findings: importResult.findings,
|
|
274
|
+
};
|
|
275
|
+
result.contractDependencies = {
|
|
276
|
+
score: contractResult.score,
|
|
277
|
+
evidence: contractResult.evidence,
|
|
278
|
+
findings: contractResult.findings,
|
|
279
|
+
};
|
|
280
|
+
result.behavioralDependencies = {
|
|
281
|
+
score: behavioralResult.score,
|
|
282
|
+
evidence: behavioralResult.evidence,
|
|
283
|
+
findings: behavioralResult.findings,
|
|
284
|
+
uncertain: behavioralResult.uncertain,
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
module.exports = {
|
|
291
|
+
analyzePhase3Dependencies,
|
|
292
|
+
DETECTOR_KEYS,
|
|
293
|
+
normalizePhase3Input,
|
|
294
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const STOP_WORDS = new Set([
|
|
2
|
+
'the', 'a', 'an', 'and', 'or', 'is', 'in', 'to', 'for', 'of', 'with', 'on',
|
|
3
|
+
'at', 'by', 'from', 'add', 'fix', 'update', 'implement', 'create', 'remove',
|
|
4
|
+
'delete', 'make', 'use', 'get', 'set', 'run', 'test', 'check', 'this', 'that',
|
|
5
|
+
'it', 'be', 'as', 'not', 'no', 'but', 'if', 'do', 'we', 'they', 'are', 'was',
|
|
6
|
+
'were', 'been', 'have', 'has', 'had', 'will', 'would', 'could', 'should',
|
|
7
|
+
'may', 'can', 'each', 'every', 'both', 'also', 'into', 'than', 'then', 'when',
|
|
8
|
+
'where', 'which', 'what', 'how', 'why', 'who', 'its', 'new', 'first', 'last',
|
|
9
|
+
'same', 'other',
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const BEHAVIOR_TERMS = new Set([
|
|
13
|
+
'approval', 'behavior', 'confidence', 'default', 'manual', 'ordering',
|
|
14
|
+
'output', 'policy', 'review', 'rules', 'rule', 'threshold', 'validation',
|
|
15
|
+
'workflow', 'state',
|
|
16
|
+
]);
|
|
17
|
+
const STRONG_SIGNAL_TERMS = new Set([
|
|
18
|
+
'approval',
|
|
19
|
+
'confidence',
|
|
20
|
+
'manual',
|
|
21
|
+
'policy',
|
|
22
|
+
'review',
|
|
23
|
+
'rule',
|
|
24
|
+
'rules',
|
|
25
|
+
'threshold',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
function tokenize(text) {
|
|
29
|
+
return Array.from(new Set(
|
|
30
|
+
text
|
|
31
|
+
.toLowerCase()
|
|
32
|
+
.split(/[^a-z0-9]+/)
|
|
33
|
+
.filter((term) => term.length >= 3 && !STOP_WORDS.has(term)),
|
|
34
|
+
));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function scoreBehavioralDependencies(normalizedInput) {
|
|
38
|
+
const taskText = normalizedInput.taskContext.tasks
|
|
39
|
+
.map((task) => task.whatToImplement)
|
|
40
|
+
.join(' ');
|
|
41
|
+
const taskTerms = tokenize(taskText).filter((term) => BEHAVIOR_TERMS.has(term));
|
|
42
|
+
|
|
43
|
+
if (taskTerms.length === 0) {
|
|
44
|
+
return {
|
|
45
|
+
score: 0,
|
|
46
|
+
findings: [],
|
|
47
|
+
evidence: [],
|
|
48
|
+
uncertain: false,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const findings = [];
|
|
53
|
+
const evidence = [];
|
|
54
|
+
|
|
55
|
+
for (const issue of normalizedInput.openIssues) {
|
|
56
|
+
const issueText = [issue.title, issue.description, issue.notes].filter(Boolean).join(' ');
|
|
57
|
+
const issueTerms = new Set(tokenize(issueText));
|
|
58
|
+
const sharedTerms = taskTerms.filter((term) => issueTerms.has(term));
|
|
59
|
+
|
|
60
|
+
if (sharedTerms.length < 2) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const strongSignalCount = sharedTerms.filter((term) => STRONG_SIGNAL_TERMS.has(term)).length;
|
|
65
|
+
const isUncertain = strongSignalCount < 2 || sharedTerms.length < 3;
|
|
66
|
+
const signal = sharedTerms.some((term) => ['rule', 'rules', 'policy', 'approval'].includes(term))
|
|
67
|
+
? 'rule-change-overlap'
|
|
68
|
+
: 'behavior-change-overlap';
|
|
69
|
+
const issueEvidence = sharedTerms.map((term) => ({
|
|
70
|
+
type: 'behavior',
|
|
71
|
+
targetIssueId: issue.id,
|
|
72
|
+
sharedTerms: sharedTerms,
|
|
73
|
+
term,
|
|
74
|
+
scoreContribution: 1,
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
findings.push({
|
|
78
|
+
sourceIssueId: normalizedInput.currentIssue.id,
|
|
79
|
+
targetIssueId: issue.id,
|
|
80
|
+
score: sharedTerms.length,
|
|
81
|
+
behavioralSignal: signal,
|
|
82
|
+
uncertain: isUncertain,
|
|
83
|
+
evidence: issueEvidence,
|
|
84
|
+
});
|
|
85
|
+
evidence.push(...issueEvidence);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
score: findings.reduce((total, finding) => total + finding.score, 0),
|
|
90
|
+
findings,
|
|
91
|
+
evidence,
|
|
92
|
+
uncertain: findings.some((finding) => finding.uncertain),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
scoreBehavioralDependencies,
|
|
98
|
+
};
|