@try-works/dsh-recursive-mode 0.1.2 → 0.1.4
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/cordis.patch.yml +15 -10
- package/lib/bootstrap.d.ts +9 -2
- package/lib/client/apply-guard.d.ts +14 -0
- package/lib/client/board.d.ts +5 -4
- package/lib/client/contract.d.ts +57 -24
- package/lib/client/host-api.d.ts +26 -0
- package/lib/client/index.d.ts +16 -9
- package/lib/client/inspector.d.ts +3 -3
- package/lib/client/slots.d.ts +4 -6
- package/lib/client/strip.d.ts +4 -3
- package/lib/client/use-live.d.ts +9 -0
- package/lib/client.js +252 -174
- package/lib/index.d.ts +11 -11
- package/lib/index.js +972 -4504
- package/lib/init-templates.d.ts +41 -0
- package/lib/live-route.d.ts +69 -0
- package/lib/phase-rules.d.ts +34 -0
- package/lib/runtime.d.ts +24 -17
- package/lib/snapshot.d.ts +11 -0
- package/package.json +4 -3
- package/preset/recursive/agent.cordis.yml +7 -4
- package/preset/recursive/preset.yml +1 -1
- package/references/agents-block.md +95 -0
- package/references/artifact-template.md +2574 -0
- package/references/bodies/claude.md +7 -0
- package/references/bodies/codex-agents.md +95 -0
- package/references/bodies/copilot.md +7 -0
- package/references/bodies/cursorrules.md +7 -0
- package/references/bodies/decisions.md +5 -0
- package/references/bodies/delegated-verification.md +49 -0
- package/references/bodies/memory-router.md +45 -0
- package/references/bodies/phase8-skill-memory.md +48 -0
- package/references/bodies/plans-bridge.md +43 -0
- package/references/bodies/recursive-agents-router.md +64 -0
- package/references/bodies/skill-discovery.md +51 -0
- package/references/bodies/skill-memory-router.md +42 -0
- package/references/bodies/state.md +5 -0
- package/references/bootstrap/RECURSIVE.md +2362 -0
- package/references/scripts/__pycache__/recursive_phase_rules.cpython-314.pyc +0 -0
- package/references/scripts/lint-recursive-run.ps1 +25 -0
- package/references/scripts/lint-recursive-run.py +2870 -0
- package/references/scripts/recursive-closeout.ps1 +38 -0
- package/references/scripts/recursive-closeout.py +541 -0
- package/references/scripts/recursive-init.ps1 +515 -0
- package/references/scripts/recursive-init.py +356 -0
- package/references/scripts/recursive-lock.ps1 +26 -0
- package/references/scripts/recursive-lock.py +302 -0
- package/references/scripts/recursive-review-bundle.ps1 +58 -0
- package/references/scripts/recursive-review-bundle.py +503 -0
- package/references/scripts/recursive-router-cli-configure.ps1 +2 -0
- package/references/scripts/recursive-router-cli-configure.py +9 -0
- package/references/scripts/recursive-router-cli-init.ps1 +2 -0
- package/references/scripts/recursive-router-cli-init.py +9 -0
- package/references/scripts/recursive-router-cli-invoke.ps1 +2 -0
- package/references/scripts/recursive-router-cli-invoke.py +9 -0
- package/references/scripts/recursive-router-cli-probe.ps1 +2 -0
- package/references/scripts/recursive-router-cli-probe.py +9 -0
- package/references/scripts/recursive-router-cli-resolve.ps1 +2 -0
- package/references/scripts/recursive-router-cli-resolve.py +9 -0
- package/references/scripts/recursive-router-cli-validate.ps1 +2 -0
- package/references/scripts/recursive-router-cli-validate.py +9 -0
- package/references/scripts/recursive-router-configure.ps1 +27 -0
- package/references/scripts/recursive-router-configure.py +74 -0
- package/references/scripts/recursive-router-init.ps1 +17 -0
- package/references/scripts/recursive-router-init.py +29 -0
- package/references/scripts/recursive-router-invoke.ps1 +47 -0
- package/references/scripts/recursive-router-invoke.py +103 -0
- package/references/scripts/recursive-router-probe.ps1 +25 -0
- package/references/scripts/recursive-router-probe.py +44 -0
- package/references/scripts/recursive-router-resolve.ps1 +26 -0
- package/references/scripts/recursive-router-resolve.py +46 -0
- package/references/scripts/recursive-router-validate.ps1 +17 -0
- package/references/scripts/recursive-router-validate.py +27 -0
- package/references/scripts/recursive-status.ps1 +23 -0
- package/references/scripts/recursive-status.py +2124 -0
- package/references/scripts/recursive-subagent-action.ps1 +98 -0
- package/references/scripts/recursive-subagent-action.py +197 -0
- package/references/scripts/recursive-training-extract.ps1 +23 -0
- package/references/scripts/recursive-training-extract.py +99 -0
- package/references/scripts/recursive-training-grpo.ps1 +52 -0
- package/references/scripts/recursive-training-grpo.py +1341 -0
- package/references/scripts/recursive-training-loader.ps1 +78 -0
- package/references/scripts/recursive-training-loader.py +558 -0
- package/references/scripts/recursive-training-mcp.ps1 +29 -0
- package/references/scripts/recursive-training-mcp.py +261 -0
- package/references/scripts/recursive-training-phase8-trigger.ps1 +55 -0
- package/references/scripts/recursive-training-phase8-trigger.py +137 -0
- package/references/scripts/recursive-training-sync.ps1 +29 -0
- package/references/scripts/recursive-training-sync.py +183 -0
- package/references/scripts/recursive_phase_rules.py +367 -0
- package/references/scripts/recursive_router_cli_lib.py +2 -0
- package/references/scripts/recursive_router_lib.py +2282 -0
- package/references/scripts/verify-locks.ps1 +25 -0
- package/references/scripts/verify-locks.py +353 -0
- package/scripts/__pycache__/lint-recursive-run.cpython-314.pyc +0 -0
- package/scripts/__pycache__/recursive_phase_rules.cpython-314.pyc +0 -0
- package/scripts/install-preset.cmd +7 -0
- package/scripts/install-preset.js +101 -0
- package/scripts/lint-recursive-run.py +7 -5
- package/scripts/test-recursive-mode-smoke.ts +29 -28
- package/src/bootstrap.ts +253 -39
- package/src/client/apply-guard.ts +23 -0
- package/src/client/board.tsx +0 -0
- package/src/client/contract.ts +72 -24
- package/src/client/host-api.ts +90 -0
- package/src/client/index.ts +22 -11
- package/src/client/inspector.tsx +7 -9
- package/src/client/slots.ts +70 -26
- package/src/client/strip.tsx +9 -9
- package/src/client/use-live.ts +52 -0
- package/src/index.ts +89 -146
- package/src/init-templates.ts +276 -0
- package/src/live-route.ts +163 -0
- package/src/phase-rules.ts +262 -0
- package/src/policy.ts +28 -4
- package/src/runtime.ts +129 -77
- package/src/snapshot.ts +104 -0
- package/lib/client/node.d.ts +0 -71
- package/lib/events.d.ts +0 -173
- package/lib/projection.d.ts +0 -29
- package/src/client/node.ts +0 -156
- package/src/events.ts +0 -173
- package/src/projection.ts +0 -237
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
[CmdletBinding()]
|
|
2
|
+
param(
|
|
3
|
+
[Parameter(Mandatory = $true)][string]$RunId,
|
|
4
|
+
[Parameter(Mandatory = $true)][string]$SubagentId,
|
|
5
|
+
[Parameter(Mandatory = $true)][string]$Phase,
|
|
6
|
+
[Parameter(Mandatory = $true)][string]$Purpose,
|
|
7
|
+
[Parameter(Mandatory = $true)][string]$ExecutionMode,
|
|
8
|
+
[string]$ArtifactPath = "",
|
|
9
|
+
[string]$RepoRoot = (Get-Location).Path,
|
|
10
|
+
[string[]]$UpstreamArtifact = @(),
|
|
11
|
+
[string[]]$Addendum = @(),
|
|
12
|
+
[string]$ReviewBundle = "",
|
|
13
|
+
[string]$DiffBasis = "",
|
|
14
|
+
[string[]]$CodeRef = @(),
|
|
15
|
+
[string[]]$MemoryRef = @(),
|
|
16
|
+
[string[]]$AuditQuestion = @(),
|
|
17
|
+
[string[]]$ActionTaken = @(),
|
|
18
|
+
[string[]]$CreatedFile = @(),
|
|
19
|
+
[string[]]$ModifiedFile = @(),
|
|
20
|
+
[string[]]$ReviewedFile = @(),
|
|
21
|
+
[string[]]$UntouchedFile = @(),
|
|
22
|
+
[string[]]$ArtifactRead = @(),
|
|
23
|
+
[string[]]$ArtifactUpdated = @(),
|
|
24
|
+
[string[]]$EvidenceUsed = @(),
|
|
25
|
+
[string[]]$Finding = @(),
|
|
26
|
+
[string[]]$VerificationPath = @(),
|
|
27
|
+
[string[]]$VerificationItem = @(),
|
|
28
|
+
[string]$RouterUsed = "",
|
|
29
|
+
[string]$RoutedRole = "",
|
|
30
|
+
[string]$RoutedCli = "",
|
|
31
|
+
[string]$RoutedModel = "",
|
|
32
|
+
[string]$RoutingConfigPath = "",
|
|
33
|
+
[string]$RoutingDiscoveryPath = "",
|
|
34
|
+
[string]$RoutingResolutionBasis = "",
|
|
35
|
+
[string]$RoutingFallbackReason = "",
|
|
36
|
+
[string]$CliProbeSummary = "",
|
|
37
|
+
[string]$PromptBundlePath = "",
|
|
38
|
+
[string]$InvocationExitCode = "",
|
|
39
|
+
[string[]]$OutputCapturePath = @(),
|
|
40
|
+
[string]$OutputName = ""
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
Set-StrictMode -Version Latest
|
|
44
|
+
$ErrorActionPreference = "Stop"
|
|
45
|
+
|
|
46
|
+
$python = if ($env:PYTHON) { $env:PYTHON } elseif (Get-Command python -ErrorAction SilentlyContinue) { "python" } else { $null }
|
|
47
|
+
if (-not $python) {
|
|
48
|
+
Write-Host "[FAIL] Python executable not found in PATH."
|
|
49
|
+
exit 1
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$scriptPath = Join-Path $PSScriptRoot "recursive-subagent-action.py"
|
|
53
|
+
$argsList = @(
|
|
54
|
+
$scriptPath,
|
|
55
|
+
"--repo-root", $RepoRoot,
|
|
56
|
+
"--run-id", $RunId,
|
|
57
|
+
"--subagent-id", $SubagentId,
|
|
58
|
+
"--phase", $Phase,
|
|
59
|
+
"--purpose", $Purpose,
|
|
60
|
+
"--execution-mode", $ExecutionMode
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if ($ArtifactPath) { $argsList += @("--artifact-path", $ArtifactPath) }
|
|
64
|
+
if ($ReviewBundle) { $argsList += @("--review-bundle", $ReviewBundle) }
|
|
65
|
+
if ($DiffBasis) { $argsList += @("--diff-basis", $DiffBasis) }
|
|
66
|
+
if ($OutputName) { $argsList += @("--output-name", $OutputName) }
|
|
67
|
+
if ($RouterUsed) { $argsList += @("--router-used", $RouterUsed) }
|
|
68
|
+
if ($RoutedRole) { $argsList += @("--routed-role", $RoutedRole) }
|
|
69
|
+
if ($RoutedCli) { $argsList += @("--routed-cli", $RoutedCli) }
|
|
70
|
+
if ($RoutedModel) { $argsList += @("--routed-model", $RoutedModel) }
|
|
71
|
+
if ($RoutingConfigPath) { $argsList += @("--routing-config-path", $RoutingConfigPath) }
|
|
72
|
+
if ($RoutingDiscoveryPath) { $argsList += @("--routing-discovery-path", $RoutingDiscoveryPath) }
|
|
73
|
+
if ($RoutingResolutionBasis) { $argsList += @("--routing-resolution-basis", $RoutingResolutionBasis) }
|
|
74
|
+
if ($RoutingFallbackReason) { $argsList += @("--routing-fallback-reason", $RoutingFallbackReason) }
|
|
75
|
+
if ($CliProbeSummary) { $argsList += @("--cli-probe-summary", $CliProbeSummary) }
|
|
76
|
+
if ($PromptBundlePath) { $argsList += @("--prompt-bundle-path", $PromptBundlePath) }
|
|
77
|
+
if ($InvocationExitCode) { $argsList += @("--invocation-exit-code", $InvocationExitCode) }
|
|
78
|
+
|
|
79
|
+
foreach ($value in @($UpstreamArtifact)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--upstream-artifact", $piece.Trim()) } } }
|
|
80
|
+
foreach ($value in @($Addendum)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--addendum", $piece.Trim()) } } }
|
|
81
|
+
foreach ($value in @($CodeRef)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--code-ref", $piece.Trim()) } } }
|
|
82
|
+
foreach ($value in @($MemoryRef)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--memory-ref", $piece.Trim()) } } }
|
|
83
|
+
foreach ($value in @($AuditQuestion)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--audit-question", $piece.Trim()) } } }
|
|
84
|
+
foreach ($value in @($ActionTaken)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--action-taken", $piece.Trim()) } } }
|
|
85
|
+
foreach ($value in @($CreatedFile)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--created-file", $piece.Trim()) } } }
|
|
86
|
+
foreach ($value in @($ModifiedFile)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--modified-file", $piece.Trim()) } } }
|
|
87
|
+
foreach ($value in @($ReviewedFile)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--reviewed-file", $piece.Trim()) } } }
|
|
88
|
+
foreach ($value in @($UntouchedFile)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--untouched-file", $piece.Trim()) } } }
|
|
89
|
+
foreach ($value in @($ArtifactRead)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--artifact-read", $piece.Trim()) } } }
|
|
90
|
+
foreach ($value in @($ArtifactUpdated)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--artifact-updated", $piece.Trim()) } } }
|
|
91
|
+
foreach ($value in @($EvidenceUsed)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--evidence-used", $piece.Trim()) } } }
|
|
92
|
+
foreach ($value in @($Finding)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--finding", $piece.Trim()) } } }
|
|
93
|
+
foreach ($value in @($VerificationPath)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--verification-path", $piece.Trim()) } } }
|
|
94
|
+
foreach ($value in @($VerificationItem)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--verification-item", $piece.Trim()) } } }
|
|
95
|
+
foreach ($value in @($OutputCapturePath)) { foreach ($piece in ($value -split ",")) { if ($piece.Trim()) { $argsList += @("--output-capture-path", $piece.Trim()) } } }
|
|
96
|
+
|
|
97
|
+
& $python @argsList
|
|
98
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Generate a recursive-mode subagent action record scaffold.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import hashlib
|
|
10
|
+
import re
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def normalize_repo_path(raw_path: str) -> str:
|
|
16
|
+
return raw_path.replace("\\", "/").strip().lstrip("/")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def slugify(value: str) -> str:
|
|
20
|
+
lowered = value.strip().lower()
|
|
21
|
+
slug = re.sub(r"[^a-z0-9]+", "-", lowered).strip("-")
|
|
22
|
+
return slug or "action"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def render_path_list(title: str, values: list[str]) -> list[str]:
|
|
26
|
+
lines = [title]
|
|
27
|
+
if not values:
|
|
28
|
+
lines.append("- none")
|
|
29
|
+
return lines
|
|
30
|
+
lines.extend(f"- `{value}`" for value in values)
|
|
31
|
+
return lines
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def render_text_list(title: str, values: list[str]) -> list[str]:
|
|
35
|
+
lines = [title]
|
|
36
|
+
if not values:
|
|
37
|
+
lines.append("- none")
|
|
38
|
+
return lines
|
|
39
|
+
lines.extend(f"- {value}" for value in values)
|
|
40
|
+
return lines
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def content_sha256(content: str) -> str:
|
|
44
|
+
normalized = content.replace("\r\n", "\n").replace("\r", "\n")
|
|
45
|
+
return hashlib.sha256(normalized.encode("utf-8")).hexdigest()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main() -> int:
|
|
49
|
+
parser = argparse.ArgumentParser(description="Generate a recursive-mode subagent action record scaffold.")
|
|
50
|
+
parser.add_argument("--repo-root", default=".", help="Repository root path.")
|
|
51
|
+
parser.add_argument("--run-id", required=True, help="Run ID under .recursive/run/.")
|
|
52
|
+
parser.add_argument("--subagent-id", required=True, help="Subagent identifier.")
|
|
53
|
+
parser.add_argument("--phase", required=True, help="Phase name for the action record.")
|
|
54
|
+
parser.add_argument("--purpose", required=True, help="Invocation purpose.")
|
|
55
|
+
parser.add_argument("--execution-mode", required=True, help="Execution mode, e.g. review, audit, implementer.")
|
|
56
|
+
parser.add_argument("--artifact-path", default="", help="Repo-relative current artifact path.")
|
|
57
|
+
parser.add_argument("--upstream-artifact", action="append", default=[], help="Repo-relative upstream artifact path.")
|
|
58
|
+
parser.add_argument("--addendum", action="append", default=[], help="Repo-relative addendum path.")
|
|
59
|
+
parser.add_argument("--review-bundle", default="", help="Repo-relative review bundle path if used.")
|
|
60
|
+
parser.add_argument("--diff-basis", default="", help="Explicit diff basis summary if not inferred.")
|
|
61
|
+
parser.add_argument("--code-ref", action="append", default=[], help="Repo-relative code reference path.")
|
|
62
|
+
parser.add_argument("--memory-ref", action="append", default=[], help="Repo-relative memory doc path.")
|
|
63
|
+
parser.add_argument("--audit-question", action="append", default=[], help="Audit/task question passed to the subagent.")
|
|
64
|
+
parser.add_argument("--action-taken", action="append", default=[], help="Concrete delegated action taken by the subagent.")
|
|
65
|
+
parser.add_argument("--created-file", action="append", default=[], help="Repo-relative created file path.")
|
|
66
|
+
parser.add_argument("--modified-file", action="append", default=[], help="Repo-relative modified file path.")
|
|
67
|
+
parser.add_argument("--reviewed-file", action="append", default=[], help="Repo-relative reviewed file path.")
|
|
68
|
+
parser.add_argument("--untouched-file", action="append", default=[], help="Repo-relative relevant but untouched file path.")
|
|
69
|
+
parser.add_argument("--artifact-read", action="append", default=[], help="Repo-relative recursive artifact read by the subagent.")
|
|
70
|
+
parser.add_argument("--artifact-updated", action="append", default=[], help="Repo-relative recursive artifact updated by the subagent.")
|
|
71
|
+
parser.add_argument("--evidence-used", action="append", default=[], help="Repo-relative evidence path used by the subagent.")
|
|
72
|
+
parser.add_argument("--finding", action="append", default=[], help="Claimed finding or unresolved point.")
|
|
73
|
+
parser.add_argument("--verification-path", action="append", default=[], help="Repo-relative file or artifact path the controller should inspect first.")
|
|
74
|
+
parser.add_argument("--verification-item", action="append", default=[], help="Main-agent verification handoff item.")
|
|
75
|
+
parser.add_argument("--router-used", default="", help="Router identifier used for delegated dispatch, e.g. recursive-router.")
|
|
76
|
+
parser.add_argument("--routed-role", default="", help="Canonical routed role resolved for this action.")
|
|
77
|
+
parser.add_argument("--routed-cli", default="", help="Resolved external CLI id if any.")
|
|
78
|
+
parser.add_argument("--routed-model", default="", help="Resolved model id if any.")
|
|
79
|
+
parser.add_argument("--routing-config-path", default="", help="Repo-relative routing policy path.")
|
|
80
|
+
parser.add_argument("--routing-discovery-path", default="", help="Repo-relative routing discovery path.")
|
|
81
|
+
parser.add_argument("--routing-resolution-basis", default="", help="Short explanation of how the route was resolved.")
|
|
82
|
+
parser.add_argument("--routing-fallback-reason", default="", help="Why execution fell back from routed delegation, if applicable.")
|
|
83
|
+
parser.add_argument("--cli-probe-summary", default="", help="Compact CLI probe summary for the action record.")
|
|
84
|
+
parser.add_argument("--prompt-bundle-path", default="", help="Repo-relative routed prompt bundle path if used.")
|
|
85
|
+
parser.add_argument("--invocation-exit-code", default="", help="External CLI invocation exit code if applicable.")
|
|
86
|
+
parser.add_argument("--output-capture-path", action="append", default=[], help="Repo-relative captured output path from the routed CLI.")
|
|
87
|
+
parser.add_argument("--output-name", default="", help="Optional action record filename under subagents/.")
|
|
88
|
+
args = parser.parse_args()
|
|
89
|
+
|
|
90
|
+
repo_root = Path(args.repo_root).resolve()
|
|
91
|
+
run_dir = repo_root / ".recursive" / "run" / args.run_id.strip()
|
|
92
|
+
if not run_dir.exists():
|
|
93
|
+
print(f"[FAIL] Run directory not found: {run_dir}")
|
|
94
|
+
return 1
|
|
95
|
+
|
|
96
|
+
subagents_dir = run_dir / "subagents"
|
|
97
|
+
subagents_dir.mkdir(parents=True, exist_ok=True)
|
|
98
|
+
|
|
99
|
+
bundle_path = normalize_repo_path(args.review_bundle) if args.review_bundle.strip() else ""
|
|
100
|
+
artifact_path = normalize_repo_path(args.artifact_path) if args.artifact_path.strip() else ""
|
|
101
|
+
artifact_hash = ""
|
|
102
|
+
if artifact_path and (repo_root / artifact_path).exists():
|
|
103
|
+
artifact_hash = content_sha256((repo_root / artifact_path).read_text(encoding="utf-8"))
|
|
104
|
+
|
|
105
|
+
diff_basis = args.diff_basis.strip() or "See /.recursive/run/<run-id>/00-worktree.md for the normalized diff basis used."
|
|
106
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
107
|
+
output_name = args.output_name.strip()
|
|
108
|
+
if not output_name:
|
|
109
|
+
output_name = f"{timestamp.replace(':', '').replace('-', '')}-{slugify(args.subagent_id)}-action.md"
|
|
110
|
+
if not output_name.lower().endswith(".md"):
|
|
111
|
+
output_name = f"{output_name}.md"
|
|
112
|
+
output_path = subagents_dir / output_name
|
|
113
|
+
output_rel = normalize_repo_path(str(output_path.relative_to(repo_root)))
|
|
114
|
+
|
|
115
|
+
upstream_artifacts = sorted(set(normalize_repo_path(value) for value in args.upstream_artifact if value.strip()))
|
|
116
|
+
addenda = sorted(set(normalize_repo_path(value) for value in args.addendum if value.strip()))
|
|
117
|
+
code_refs = sorted(set(normalize_repo_path(value) for value in args.code_ref if value.strip()))
|
|
118
|
+
memory_refs = sorted(set(normalize_repo_path(value) for value in args.memory_ref if value.strip()))
|
|
119
|
+
created_files = sorted(set(normalize_repo_path(value) for value in args.created_file if value.strip()))
|
|
120
|
+
modified_files = sorted(set(normalize_repo_path(value) for value in args.modified_file if value.strip()))
|
|
121
|
+
reviewed_files = sorted(set(normalize_repo_path(value) for value in args.reviewed_file if value.strip()))
|
|
122
|
+
untouched_files = sorted(set(normalize_repo_path(value) for value in args.untouched_file if value.strip()))
|
|
123
|
+
artifacts_read = sorted(set(normalize_repo_path(value) for value in args.artifact_read if value.strip()))
|
|
124
|
+
artifacts_updated = sorted(set(normalize_repo_path(value) for value in args.artifact_updated if value.strip()))
|
|
125
|
+
evidence_used = sorted(set(normalize_repo_path(value) for value in args.evidence_used if value.strip()))
|
|
126
|
+
actions_taken = [value.strip() for value in args.action_taken if value.strip()]
|
|
127
|
+
verification_paths = sorted(set(normalize_repo_path(value) for value in args.verification_path if value.strip()))
|
|
128
|
+
output_capture_paths = sorted(set(normalize_repo_path(value) for value in args.output_capture_path if value.strip()))
|
|
129
|
+
routing_config_path = normalize_repo_path(args.routing_config_path) if args.routing_config_path.strip() else ""
|
|
130
|
+
routing_discovery_path = normalize_repo_path(args.routing_discovery_path) if args.routing_discovery_path.strip() else ""
|
|
131
|
+
prompt_bundle_path = normalize_repo_path(args.prompt_bundle_path) if args.prompt_bundle_path.strip() else ""
|
|
132
|
+
|
|
133
|
+
lines: list[str] = [
|
|
134
|
+
"# Subagent Action Record",
|
|
135
|
+
"",
|
|
136
|
+
"## Metadata",
|
|
137
|
+
f"- Subagent ID: `{args.subagent_id.strip()}`",
|
|
138
|
+
f"- Run ID: `{args.run_id.strip()}`",
|
|
139
|
+
f"- Phase: `{args.phase.strip()}`",
|
|
140
|
+
f"- Purpose: `{args.purpose.strip()}`",
|
|
141
|
+
f"- Execution Mode: `{args.execution_mode.strip()}`",
|
|
142
|
+
f"- Timestamp: `{timestamp}`",
|
|
143
|
+
f"- Action Record Path: `/{output_rel}`",
|
|
144
|
+
"",
|
|
145
|
+
"## Inputs Provided",
|
|
146
|
+
f"- Current Artifact: `{('/' + artifact_path) if artifact_path else 'none'}`",
|
|
147
|
+
f"- Artifact Content Hash: `{artifact_hash or 'UNKNOWN'}`",
|
|
148
|
+
]
|
|
149
|
+
lines.extend(render_path_list("- Upstream Artifacts:", [f"/{value}" for value in upstream_artifacts]))
|
|
150
|
+
lines.extend(render_path_list("- Addenda:", [f"/{value}" for value in addenda]))
|
|
151
|
+
lines.append(f"- Review Bundle: `{('/' + bundle_path) if bundle_path else 'none'}`")
|
|
152
|
+
lines.append(f"- Diff Basis: `{diff_basis}`")
|
|
153
|
+
lines.extend(render_path_list("- Code Refs:", [f"/{value}" for value in code_refs]))
|
|
154
|
+
lines.extend(render_path_list("- Memory Refs:", [f"/{value}" for value in memory_refs]))
|
|
155
|
+
lines.extend(render_text_list("- Audit / Task Questions:", args.audit_question))
|
|
156
|
+
lines.append("")
|
|
157
|
+
lines.append("## Routing")
|
|
158
|
+
lines.append(f"- Router Used: `{args.router_used.strip() or 'none'}`")
|
|
159
|
+
lines.append(f"- Routed Role: `{args.routed_role.strip() or 'none'}`")
|
|
160
|
+
lines.append(f"- Routed CLI: `{args.routed_cli.strip() or 'none'}`")
|
|
161
|
+
lines.append(f"- Routed Model: `{args.routed_model.strip() or 'none'}`")
|
|
162
|
+
lines.append(f"- Routing Config Path: `{('/' + routing_config_path) if routing_config_path else 'none'}`")
|
|
163
|
+
lines.append(f"- Routing Discovery Path: `{('/' + routing_discovery_path) if routing_discovery_path else 'none'}`")
|
|
164
|
+
lines.append(f"- Routing Resolution Basis: `{args.routing_resolution_basis.strip() or 'none'}`")
|
|
165
|
+
lines.append(f"- Routing Fallback Reason: `{args.routing_fallback_reason.strip() or 'none'}`")
|
|
166
|
+
lines.append(f"- CLI Probe Summary: `{args.cli_probe_summary.strip() or 'none'}`")
|
|
167
|
+
lines.append(f"- Prompt Bundle Path: `{('/' + prompt_bundle_path) if prompt_bundle_path else 'none'}`")
|
|
168
|
+
lines.append(f"- Invocation Exit Code: `{args.invocation_exit_code.strip() or 'none'}`")
|
|
169
|
+
lines.extend(render_path_list("- Output Capture Paths:", [f"/{value}" for value in output_capture_paths]))
|
|
170
|
+
lines.append("")
|
|
171
|
+
lines.extend(render_text_list("## Claimed Actions Taken", actions_taken))
|
|
172
|
+
lines.append("")
|
|
173
|
+
lines.append("## Claimed File Impact")
|
|
174
|
+
lines.extend(render_path_list("### Created", [f"/{value}" for value in created_files]))
|
|
175
|
+
lines.extend(render_path_list("### Modified", [f"/{value}" for value in modified_files]))
|
|
176
|
+
lines.extend(render_path_list("### Reviewed", [f"/{value}" for value in reviewed_files]))
|
|
177
|
+
lines.extend(render_path_list("### Relevant but Untouched", [f"/{value}" for value in untouched_files]))
|
|
178
|
+
lines.append("")
|
|
179
|
+
lines.append("## Claimed Artifact Impact")
|
|
180
|
+
lines.extend(render_path_list("### Read", [f"/{value}" for value in artifacts_read]))
|
|
181
|
+
lines.extend(render_path_list("### Updated", [f"/{value}" for value in artifacts_updated]))
|
|
182
|
+
lines.extend(render_path_list("### Evidence Used", [f"/{value}" for value in evidence_used]))
|
|
183
|
+
lines.append("")
|
|
184
|
+
lines.extend(render_text_list("## Claimed Findings", args.finding))
|
|
185
|
+
lines.append("")
|
|
186
|
+
lines.append("## Verification Handoff")
|
|
187
|
+
lines.extend(render_path_list("- Inspect first:", [f"/{value}" if value.startswith(".recursive/") else value for value in verification_paths]))
|
|
188
|
+
lines.extend(render_text_list("- Notes:", args.verification_item))
|
|
189
|
+
lines.append("")
|
|
190
|
+
|
|
191
|
+
output_path.write_text("\n".join(lines), encoding="utf-8", newline="\n")
|
|
192
|
+
print(f"[OK] Wrote subagent action record: /{output_rel}")
|
|
193
|
+
return 0
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if __name__ == "__main__":
|
|
197
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[CmdletBinding()]
|
|
2
|
+
param(
|
|
3
|
+
[Parameter(Mandatory = $true)][string]$RepoRoot,
|
|
4
|
+
[Parameter(Mandatory = $true)][string]$PromptFile,
|
|
5
|
+
[Parameter()][string]$ResponseFile = ""
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
Set-StrictMode -Version Latest
|
|
9
|
+
$ErrorActionPreference = "Stop"
|
|
10
|
+
|
|
11
|
+
$python = if ($env:PYTHON) { $env:PYTHON } elseif (Get-Command python -ErrorAction SilentlyContinue) { "python" } else { $null }
|
|
12
|
+
if (-not $python) {
|
|
13
|
+
Write-Host "[FAIL] Python executable not found in PATH."
|
|
14
|
+
exit 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
$scriptPath = Join-Path $PSScriptRoot "recursive-training-extract.py"
|
|
18
|
+
$argsList = @("--repo-root", $RepoRoot, "--prompt-file", $PromptFile)
|
|
19
|
+
if ($ResponseFile) {
|
|
20
|
+
$argsList += @("--response-file", $ResponseFile)
|
|
21
|
+
}
|
|
22
|
+
& $python $scriptPath @argsList
|
|
23
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Companion extractor for recursive-training prompt evaluation.
|
|
4
|
+
|
|
5
|
+
The training scripts build extraction prompts and hand them to this script.
|
|
6
|
+
Supported evaluation modes (first match wins):
|
|
7
|
+
|
|
8
|
+
1. --response-file PATH — print the file contents (agent/offline JSON)
|
|
9
|
+
2. Sibling <prompt-file>.response.json — same as response-file when present
|
|
10
|
+
3. RECURSIVE_TRAINING_EXTRACTOR_CMD — shell command; `{prompt_file}` and
|
|
11
|
+
`{repo_root}` are substituted; stdout is the extraction result
|
|
12
|
+
4. Otherwise exit 2 (unavailable)
|
|
13
|
+
|
|
14
|
+
Exit codes:
|
|
15
|
+
0: extraction text printed to stdout
|
|
16
|
+
1: invalid arguments / missing prompt
|
|
17
|
+
2: no extractor wired
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import os
|
|
24
|
+
import shlex
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _run_external_command(command: str, repo_root: Path, prompt_path: Path) -> int:
|
|
31
|
+
rendered = command.format(prompt_file=str(prompt_path), repo_root=str(repo_root))
|
|
32
|
+
# Windows-friendly: run via shell when the command is a single string template
|
|
33
|
+
result = subprocess.run(
|
|
34
|
+
rendered if os.name == "nt" else shlex.split(rendered),
|
|
35
|
+
check=False,
|
|
36
|
+
capture_output=True,
|
|
37
|
+
text=True,
|
|
38
|
+
shell=(os.name == "nt"),
|
|
39
|
+
)
|
|
40
|
+
if result.returncode != 0:
|
|
41
|
+
err = (result.stderr or result.stdout or "extractor command failed").strip()
|
|
42
|
+
print(err, file=sys.stderr)
|
|
43
|
+
return result.returncode if result.returncode != 2 else 1
|
|
44
|
+
sys.stdout.write(result.stdout)
|
|
45
|
+
if result.stdout and not result.stdout.endswith("\n"):
|
|
46
|
+
sys.stdout.write("\n")
|
|
47
|
+
return 0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def main() -> int:
|
|
51
|
+
parser = argparse.ArgumentParser(description="Companion extractor for recursive-training")
|
|
52
|
+
parser.add_argument("--repo-root", required=True, help="Repository root path")
|
|
53
|
+
parser.add_argument("--prompt-file", required=True, help="Path to the prompt file to evaluate")
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--response-file",
|
|
56
|
+
default="",
|
|
57
|
+
help="Optional precomputed JSON/text response to emit instead of calling a model",
|
|
58
|
+
)
|
|
59
|
+
args = parser.parse_args()
|
|
60
|
+
|
|
61
|
+
repo_root = Path(args.repo_root).resolve()
|
|
62
|
+
prompt_path = Path(args.prompt_file).resolve()
|
|
63
|
+
if not prompt_path.exists():
|
|
64
|
+
print("ERROR: Prompt file not found for recursive-training extraction.", file=sys.stderr)
|
|
65
|
+
return 1
|
|
66
|
+
|
|
67
|
+
response_path = Path(args.response_file).resolve() if args.response_file.strip() else None
|
|
68
|
+
if response_path is None:
|
|
69
|
+
sibling = prompt_path.with_suffix(prompt_path.suffix + ".response.json")
|
|
70
|
+
if not sibling.exists():
|
|
71
|
+
sibling = Path(str(prompt_path) + ".response.json")
|
|
72
|
+
if sibling.exists():
|
|
73
|
+
response_path = sibling
|
|
74
|
+
|
|
75
|
+
if response_path is not None:
|
|
76
|
+
if not response_path.exists():
|
|
77
|
+
print(f"ERROR: Response file not found: {response_path}", file=sys.stderr)
|
|
78
|
+
return 1
|
|
79
|
+
payload = response_path.read_text(encoding="utf-8")
|
|
80
|
+
sys.stdout.write(payload)
|
|
81
|
+
if payload and not payload.endswith("\n"):
|
|
82
|
+
sys.stdout.write("\n")
|
|
83
|
+
return 0
|
|
84
|
+
|
|
85
|
+
extractor_cmd = os.environ.get("RECURSIVE_TRAINING_EXTRACTOR_CMD", "").strip()
|
|
86
|
+
if extractor_cmd:
|
|
87
|
+
return _run_external_command(extractor_cmd, repo_root, prompt_path)
|
|
88
|
+
|
|
89
|
+
print(
|
|
90
|
+
"Training extractor is not available for recursive-training in this environment.\n"
|
|
91
|
+
"Wire one of: --response-file, <prompt>.response.json, or "
|
|
92
|
+
"RECURSIVE_TRAINING_EXTRACTOR_CMD.",
|
|
93
|
+
file=sys.stderr,
|
|
94
|
+
)
|
|
95
|
+
return 2
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
Repository-local Training-Free GRPO for recursive-mode runs (PowerShell wrapper).
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Thin wrapper that calls recursive-training-grpo.py with the same arguments.
|
|
8
|
+
Extraction is delegated to recursive-training-extract.py (response-file or
|
|
9
|
+
RECURSIVE_TRAINING_EXTRACTOR_CMD). This wrapper does not select an LLM provider.
|
|
10
|
+
#>
|
|
11
|
+
|
|
12
|
+
[CmdletBinding()]
|
|
13
|
+
param(
|
|
14
|
+
[Parameter(Mandatory = $true)]
|
|
15
|
+
[string]$RepoRoot,
|
|
16
|
+
|
|
17
|
+
[Parameter()]
|
|
18
|
+
[switch]$Incremental,
|
|
19
|
+
|
|
20
|
+
[Parameter()]
|
|
21
|
+
[string]$RunId,
|
|
22
|
+
|
|
23
|
+
[Parameter()]
|
|
24
|
+
[int]$WinnerOnlyThreshold = 0
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
$python = Get-Command python -ErrorAction SilentlyContinue
|
|
28
|
+
if (-not $python) {
|
|
29
|
+
$python = Get-Command python3 -ErrorAction SilentlyContinue
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (-not $python) {
|
|
33
|
+
Write-Error "Python is required but not found. Please install Python 3.10+."
|
|
34
|
+
exit 1
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
38
|
+
$pyScript = Join-Path $scriptDir "recursive-training-grpo.py"
|
|
39
|
+
|
|
40
|
+
$argsList = @("--repo-root", $RepoRoot)
|
|
41
|
+
if ($Incremental) {
|
|
42
|
+
$argsList += "--incremental"
|
|
43
|
+
}
|
|
44
|
+
if ($RunId) {
|
|
45
|
+
$argsList += @("--run-id", $RunId)
|
|
46
|
+
}
|
|
47
|
+
if ($WinnerOnlyThreshold -gt 0) {
|
|
48
|
+
$argsList += @("--winner-only-threshold", "$WinnerOnlyThreshold")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
& $python.Source $pyScript @argsList
|
|
52
|
+
exit $LASTEXITCODE
|