@moretea-labs/forge 1.5.0-rc.1 → 1.5.1
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/AGENTS.md +9 -11
- package/CHANGELOG.md +27 -1
- package/CLAUDE.md +8 -10
- package/DESIGN.md +278 -0
- package/README.md +70 -90
- package/README.zh-CN.md +52 -55
- package/SKILL.md +8 -11
- package/THIRD_PARTY_NOTICES.md +9 -3
- package/assets/branding/README.md +4 -2
- package/assets/hooks/lib/workflow-state.sh +19 -18
- package/assets/hooks/post-edit-guard.sh +6 -75
- package/assets/hooks/prompt-guard.sh +24 -12
- package/assets/hooks/session-start-context.sh +17 -17
- package/assets/hooks/stop-orchestrator.sh +2 -2
- package/assets/partials/04-project-structure.partial.md +6 -6
- package/assets/partials/05-workflow.partial.md +6 -6
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials/08-orchestration.partial.md +2 -2
- package/assets/partials-agents/02-operating-mode.partial.md +5 -5
- package/assets/partials-agents/03-orchestration.partial.md +3 -3
- package/assets/partials-agents/04-task-protocol.partial.md +3 -3
- package/assets/partials-agents/08-deep-docs.partial.md +1 -1
- package/assets/reference-configs/agentic-development-flow.md +49 -86
- package/assets/reference-configs/external-tooling.md +21 -61
- package/assets/reference-configs/handoff-protocol.md +25 -20
- package/assets/reference-configs/harness-overview.md +8 -8
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/archive-workflow.sh +0 -1
- package/assets/templates/helpers/capture-plan.sh +3 -3
- package/assets/templates/helpers/check-task-workflow.sh +2 -2
- package/assets/templates/helpers/codex-handoff-resume.sh +5 -5
- package/assets/templates/helpers/ensure-task-workflow.sh +37 -35
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +2 -2
- package/assets/templates/helpers/prepare-codex-handoff.sh +2 -2
- package/assets/templates/helpers/prepare-handoff.sh +9 -9
- package/assets/templates/helpers/refresh-current-status.sh +2 -2
- package/assets/templates/helpers/ship-worktrees.sh +1 -1
- package/assets/templates/helpers/sprint-backlog.sh +1 -1
- package/assets/templates/helpers/verify-sprint.sh +7 -7
- package/assets/templates/helpers/workstream-sync.sh +1 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/workflow-contract.v1.json +6 -3
- package/docs/architecture/current/README.md +10 -9
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +2 -2
- package/docs/architecture/current/architecture-invariants.md +2 -2
- package/docs/architecture/current/automation-and-schedule-engine.md +15 -15
- package/docs/architecture/current/codegraph-context-provider.md +2 -2
- package/docs/architecture/current/control-plane-authority-inventory.md +12 -0
- package/docs/architecture/current/dispatch-and-agent-strategy.md +11 -11
- package/docs/architecture/current/entity-model.md +0 -27
- package/docs/architecture/current/failure-recovery.md +0 -24
- package/docs/architecture/current/governance.md +1 -1
- package/docs/architecture/current/implementation-status.md +1 -1
- package/docs/architecture/current/legacy-governance.md +58 -0
- package/docs/architecture/current/multi-repository-execution.md +1 -1
- package/docs/architecture/current/peer-supercontroller-runtime.md +1 -1
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +4 -5
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -10
- package/docs/architecture/current/target-requirements-traceability.md +2 -2
- package/docs/architecture/current/thin-harness-governance-policy.md +1 -1
- package/docs/architecture/current/thin-harness-v1.md +9 -11
- package/docs/architecture/domains/runtime-harness.md +1 -1
- package/docs/forge-autonomous-goal-loop.md +3 -1
- package/docs/forge-chatgpt-mcp-setup.md +67 -176
- package/docs/images/forge-banner-cn.svg +26 -1
- package/docs/images/forge-banner.svg +26 -1
- package/docs/operations/features.md +1 -1
- package/docs/operations/homebrew.md +3 -3
- package/docs/operations/mcp-tool-exposure.md +5 -5
- package/docs/operations/platform-support.md +45 -37
- package/docs/operations/platform-support.zh-CN.md +40 -38
- package/docs/operations/releasing.md +3 -3
- package/docs/operations/releasing.zh-CN.md +3 -3
- package/docs/operations/troubleshooting.md +11 -6
- package/docs/operations/troubleshooting.zh-CN.md +11 -6
- package/docs/public-usage-guide.md +28 -21
- package/docs/public-usage-guide.zh-CN.md +28 -21
- package/docs/tutorials/01-install-and-start.md +58 -35
- package/docs/tutorials/01-install-and-start.zh-CN.md +58 -35
- package/docs/tutorials/02-connect-chatgpt.md +118 -46
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +119 -45
- package/docs/wiki/Architecture.md +1 -1
- package/docs/wiki/Installation.md +2 -2
- package/docs/wiki/Quick-Start.md +1 -1
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/install.ps1 +4 -5
- package/install.sh +4 -6
- package/package.json +15 -5
- package/scripts/archive-workflow.sh +0 -1
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +27 -14
- package/scripts/build-controller-ui.ts +28 -0
- package/scripts/capture-plan.sh +4 -4
- package/scripts/check-agent-tooling.sh +12 -1
- package/scripts/check-platform-support.mjs +2 -2
- package/scripts/check-public-docs.sh +16 -5
- package/scripts/check-release-readiness.sh +5 -11
- package/scripts/check-runtime-architecture.mjs +14 -22
- package/scripts/check-tarball-install-smoke.sh +3 -2
- package/scripts/check-task-workflow.sh +2 -2
- package/scripts/codex-handoff-resume.sh +5 -5
- package/scripts/create-project-dirs.sh +3 -3
- package/scripts/ensure-task-workflow.sh +45 -35
- package/scripts/init-project.sh +4 -4
- package/scripts/install-desktop-operator-registration.ts +2 -2
- package/scripts/lib/project-init-lib.sh +46 -44
- package/scripts/migrate-project-template.sh +7 -4
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +2 -2
- package/scripts/prepare-codex-handoff.sh +2 -2
- package/scripts/prepare-handoff.sh +9 -9
- package/scripts/refresh-current-status.sh +2 -2
- package/scripts/setup-plugins.sh +3 -3
- package/scripts/ship-worktrees.sh +1 -1
- package/scripts/sprint-backlog.sh +5 -5
- package/scripts/sync-codex-installed-copies.sh +2 -3
- package/scripts/verify-sprint.sh +7 -7
- package/scripts/workstream-sync.sh +1 -1
- package/src/cli/chatgpt-browser/bridge-extension.ts +1 -1
- package/src/cli/chatgpt-browser/bridge-provider.ts +29 -0
- package/src/cli/chatgpt-browser/file-policy.ts +3 -1
- package/src/cli/commands/chatgpt.ts +51 -0
- package/src/cli/commands/doctor.ts +1 -1
- package/src/cli/commands/init-hook.ts +213 -70
- package/src/cli/commands/init.ts +5 -6
- package/src/cli/commands/mcp.ts +31 -5
- package/src/cli/commands/runtime.ts +61 -8
- package/src/cli/commands/setup-profile.ts +472 -0
- package/src/cli/controller/check-runner.ts +20 -3
- package/src/cli/controller/codex-continuation.ts +4 -7
- package/src/cli/controller/context-pack.ts +118 -14
- package/src/cli/controller/operational-plan.ts +4 -4
- package/src/cli/controller/task-ledger.ts +10 -10
- package/src/cli/controller/work-mode.ts +6 -13
- package/src/cli/index.ts +14 -35
- package/src/cli/local-bridge/connector-freshness.ts +1 -1
- package/src/cli/local-bridge/console-assets.ts +17 -0
- package/src/cli/local-bridge/console-automations.ts +160 -0
- package/src/cli/local-bridge/dashboard.ts +8 -1303
- package/src/cli/local-bridge/facade-api.ts +16 -7
- package/src/cli/local-bridge/frontend/src/App.tsx +10 -0
- package/src/cli/local-bridge/frontend/src/components/Button.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/design/icons.tsx +11 -0
- package/src/cli/local-bridge/frontend/src/design/tokens.css +8 -0
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +4 -0
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +2 -0
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +2 -0
- package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +2 -0
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/lib/api.ts +23 -0
- package/src/cli/local-bridge/frontend/src/lib/format.ts +4 -0
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +7 -0
- package/src/cli/local-bridge/frontend/src/main.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +2 -0
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/styles.css +12 -0
- package/src/cli/local-bridge/frontend/src/types.ts +14 -0
- package/src/cli/local-bridge/frontend/src/vite-env.d.ts +1 -0
- package/src/cli/local-bridge/frontend/vite.config.ts +27 -0
- package/src/cli/local-bridge/server.ts +45 -0
- package/src/cli/local-bridge/ui-dist/app.css +1 -0
- package/src/cli/local-bridge/ui-dist/app.js +11094 -0
- package/src/cli/mcp/auth.ts +2 -0
- package/src/cli/mcp/instructions.ts +3 -3
- package/src/cli/mcp/legacy-tool-service.ts +27 -23
- package/src/cli/mcp/multi-repository.ts +2 -0
- package/src/cli/mcp/policy.ts +7 -5
- package/src/cli/mcp/repository-tools.ts +53 -14
- package/src/cli/mcp/server.ts +5 -10
- package/src/cli/mcp/setup.ts +220 -276
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-names.ts +1 -1
- package/src/cli/mcp/toolset.ts +0 -1
- package/src/cli/mcp/transports/http.ts +51 -4
- package/src/cli/repositories/branch-name-policy.ts +0 -7
- package/src/cli/repositories/controller-home.ts +110 -3
- package/src/cli/repositories/safe-patch.ts +1 -1
- package/src/cli/repositories/selected-path-actions.ts +26 -24
- package/src/core/adoption/gitignore-plan.ts +2 -3
- package/src/core/adoption/plan.ts +4 -1
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +556 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +128 -1
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +277 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +169 -0
- package/src/runtime/control-plane/facade/capability-registry.ts +0 -10
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +44 -25
- package/src/runtime/control-plane/facade/suggested-actions.ts +7 -1
- package/src/runtime/control-plane/facade/types.ts +0 -1
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +30 -8
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +114 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +175 -0
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +161 -0
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +64 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +117 -24
- package/src/runtime/control-plane/routing/route-policy.ts +14 -14
- package/src/runtime/control-plane/runtime-cleanup.ts +0 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/{workflow/campaigns/workspace.ts → execution/managed-workspace.ts} +41 -43
- package/src/runtime/execution/process-runtime/check-facade.ts +6 -0
- package/src/runtime/execution/process-runtime/check-result.ts +45 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -16
- package/src/runtime/execution/process-runtime/command-facade.ts +4 -2
- package/src/runtime/execution/process-runtime/gc.ts +1 -1
- package/src/runtime/execution/process-runtime/index.ts +3 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +14 -5
- package/src/runtime/execution/process-runtime/persisted-check.ts +285 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +30 -0
- package/src/runtime/execution/process-runtime/runtime.ts +18 -1
- package/src/runtime/execution/process-runtime/store.ts +1 -19
- package/src/runtime/execution/process-runtime/types.ts +4 -0
- package/src/runtime/execution/thin-harness/execution-router.ts +0 -1
- package/src/runtime/execution/thin-harness/fast-executor.ts +1 -1
- package/src/runtime/execution/thin-harness/lightweight-lanes.ts +1 -2
- package/src/runtime/execution/thin-harness/types.ts +0 -1
- package/src/runtime/execution/workers/executor.ts +1 -1
- package/src/runtime/gateway/mcp/execution-tools.ts +1 -1
- package/src/runtime/gateway/mcp/persisted-check-process.ts +3 -255
- package/src/runtime/gateway/mcp/process-tools.ts +16 -3
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -9
- package/src/runtime/gateway/mcp/router.ts +117 -182
- package/src/runtime/gateway/mcp/runtime-tools.ts +331 -409
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +1 -219
- package/src/runtime/plugins/browser-adapter.ts +5 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +1 -1
- package/src/runtime/plugins/store.ts +14 -6
- package/src/runtime/plugins/types.ts +1 -1
- package/src/runtime/projections/materialized-view.ts +1 -23
- package/src/runtime/recovery/maintenance-executor.ts +9 -0
- package/src/runtime/resources/ownership.ts +0 -1
- package/src/runtime/root/gateway-adapter.ts +2 -0
- package/src/runtime/root/mcp-transport.ts +10 -2
- package/src/runtime/root/package-connector-service.ts +136 -0
- package/src/runtime/root/package-runtime-release.ts +161 -0
- package/src/runtime/root/package-runtime-service.ts +185 -0
- package/src/runtime/root/release-materialize.ts +19 -0
- package/src/runtime/root/runtime.ts +2 -1
- package/src/runtime/root/scheduler.ts +5 -0
- package/src/runtime/root/service.ts +18 -5
- package/src/runtime/standalone-recovery/core.ts +65 -16
- package/src/runtime/workflow/findings/store.ts +145 -106
- package/src/runtime/workflow/findings/types.ts +22 -0
- package/src/runtime/workflow/schedules/engine.ts +132 -2
- package/src/runtime/workflow/schedules/pending-activation.ts +139 -0
- package/src/runtime/workflow/schedules/work-continuation.ts +218 -0
- package/docs/CHANGELOG.md +0 -978
- package/docs/README.zh-CN.md +0 -58
- package/scripts/smoke-chatgpt-supervised-automation.ts +0 -208
- package/src/cli/commands/controller.ts +0 -680
- package/src/runtime/workflow/campaigns/cleanup.ts +0 -263
- package/src/runtime/workflow/campaigns/engine.ts +0 -112
- package/src/runtime/workflow/campaigns/normalize.ts +0 -100
- package/src/runtime/workflow/campaigns/review.ts +0 -315
- package/src/runtime/workflow/campaigns/store.ts +0 -526
- package/src/runtime/workflow/campaigns/supervisor.ts +0 -112
- package/src/runtime/workflow/campaigns/types.ts +0 -290
- /package/src/runtime/{workflow/campaigns → execution}/workspace-agent.ts +0 -0
|
@@ -79,10 +79,10 @@ ${packet.allowedActions.map((action) => `- ${action}`).join('\n')}
|
|
|
79
79
|
${packet.stopConditions.map((condition) => `- ${condition}`).join('\n')}
|
|
80
80
|
|
|
81
81
|
## Instructions
|
|
82
|
-
1.
|
|
83
|
-
2.
|
|
82
|
+
1. Read current Runtime state through \`rh_status\`, pending decisions through \`rh_inbox\`, and bounded Work through \`rh_work\` before starting new work.
|
|
83
|
+
2. Use \`rh_work.verify/finalize/repair\` for current Work lifecycle changes; do not mutate legacy Issue/Task state.
|
|
84
84
|
3. For high/destructive work, prepare a concise review summary instead of auto-approving it.
|
|
85
|
-
4. If a command fails because of tool/platform limitations,
|
|
85
|
+
4. If a command fails because of tool/platform limitations, create a Runtime HandoffItem or bounded continuation note rather than retrying the same blocked path repeatedly.
|
|
86
86
|
5. Finish with checks, a short status summary, and explicit remaining blockers.
|
|
87
87
|
`;
|
|
88
88
|
}
|
|
@@ -101,7 +101,7 @@ export function prepareCodexContinuation(repoRoot: string, options: CodexContinu
|
|
|
101
101
|
controller: 'codex-cli',
|
|
102
102
|
allowedActions: [
|
|
103
103
|
'inspect compact backlog and stuck-state reports',
|
|
104
|
-
'finish
|
|
104
|
+
'finish validated bounded Work through rh_work.finalize when policy allows',
|
|
105
105
|
'prepare patches for remaining blocker classes',
|
|
106
106
|
'run declared checks and summarize failures',
|
|
107
107
|
],
|
|
@@ -123,9 +123,6 @@ export function prepareCodexContinuation(repoRoot: string, options: CodexContinu
|
|
|
123
123
|
recommendations: stuckStates.recommendations,
|
|
124
124
|
},
|
|
125
125
|
nextCommands: [
|
|
126
|
-
'forge controller completion-backlog --json',
|
|
127
|
-
'forge controller stuck-states --json',
|
|
128
|
-
...(backlog.finishableRunIds.length ? ['forge controller finish-ready-runs --apply --json'] : []),
|
|
129
126
|
'npm run check:type',
|
|
130
127
|
'npm run check:mcp-compatibility',
|
|
131
128
|
],
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type CodeGraphReadProviderResponse,
|
|
14
14
|
} from "../../runtime/context/codegraph-read-provider";
|
|
15
15
|
|
|
16
|
-
const CONTEXT_PACK_SCHEMA_VERSION =
|
|
16
|
+
const CONTEXT_PACK_SCHEMA_VERSION = 6;
|
|
17
17
|
const DEFAULT_MAX_FILES = 8;
|
|
18
18
|
const DEFAULT_MAX_SNIPPETS = 20;
|
|
19
19
|
const DEFAULT_SNIPPET_CONTEXT_BEFORE = 12;
|
|
@@ -38,6 +38,22 @@ const STOPWORDS = new Set([
|
|
|
38
38
|
]);
|
|
39
39
|
|
|
40
40
|
export type StructuralContextMode = "off" | "auto" | "required";
|
|
41
|
+
export type ControllerContextRetrievalMode = "implementation" | "plan" | "debug" | "review";
|
|
42
|
+
export const CONTROLLER_CONTEXT_IMPACT_DOMAINS = [
|
|
43
|
+
"persistence", "scheduler", "notification", "timeline", "events", "cache", "api", "concurrency",
|
|
44
|
+
] as const;
|
|
45
|
+
export type ControllerContextImpactDomain = (typeof CONTROLLER_CONTEXT_IMPACT_DOMAINS)[number];
|
|
46
|
+
|
|
47
|
+
const IMPACT_DOMAIN_TERMS: Record<ControllerContextImpactDomain, readonly string[]> = {
|
|
48
|
+
persistence: ["persist", "database", "repository"],
|
|
49
|
+
scheduler: ["scheduler", "schedule", "reminder"],
|
|
50
|
+
notification: ["notification", "notify", "push"],
|
|
51
|
+
timeline: ["timeline", "history", "activity"],
|
|
52
|
+
events: ["event", "publish", "subscribe"],
|
|
53
|
+
cache: ["cache", "invalidate", "memo"],
|
|
54
|
+
api: ["api", "dto", "controller"],
|
|
55
|
+
concurrency: ["transaction", "lock", "atomic"],
|
|
56
|
+
};
|
|
41
57
|
|
|
42
58
|
export interface ControllerContextPackOptions {
|
|
43
59
|
description?: string;
|
|
@@ -51,6 +67,9 @@ export interface ControllerContextPackOptions {
|
|
|
51
67
|
maxSnippets?: number;
|
|
52
68
|
maxCharsPerSnippet?: number;
|
|
53
69
|
structuralContext?: StructuralContextMode;
|
|
70
|
+
retrievalMode?: ControllerContextRetrievalMode;
|
|
71
|
+
/** GPT-selected cross-cutting evidence dimensions. Forge expands mechanically and never treats them as semantic completeness proof. */
|
|
72
|
+
impactDomains?: ControllerContextImpactDomain[];
|
|
54
73
|
}
|
|
55
74
|
|
|
56
75
|
export interface ControllerContextPackDependencies {
|
|
@@ -97,6 +116,15 @@ export interface ControllerContextPackProjection {
|
|
|
97
116
|
};
|
|
98
117
|
search: {
|
|
99
118
|
terms: string[];
|
|
119
|
+
impactDomains: ControllerContextImpactDomain[];
|
|
120
|
+
impactCoverage: Array<{
|
|
121
|
+
domain: ControllerContextImpactDomain;
|
|
122
|
+
queries: string[];
|
|
123
|
+
matchedFiles: string[];
|
|
124
|
+
selectedFiles: string[];
|
|
125
|
+
omittedFileCount: number;
|
|
126
|
+
status: "selected" | "matched_not_selected" | "no_evidence";
|
|
127
|
+
}>;
|
|
100
128
|
includeGlobs: string[];
|
|
101
129
|
excludeGlobs: string[];
|
|
102
130
|
scannedFiles: number;
|
|
@@ -132,7 +160,10 @@ export interface ControllerContextPackProjection {
|
|
|
132
160
|
};
|
|
133
161
|
contextContract: {
|
|
134
162
|
strategy: string;
|
|
135
|
-
|
|
163
|
+
retrievalMode: ControllerContextRetrievalMode;
|
|
164
|
+
semanticSufficiencyAuthority: "chatgpt";
|
|
165
|
+
rawCodeRequiredForImplementation: boolean;
|
|
166
|
+
expansionSignals: string[];
|
|
136
167
|
notes: string[];
|
|
137
168
|
};
|
|
138
169
|
next: string[];
|
|
@@ -364,6 +395,7 @@ export function buildControllerContextPack(
|
|
|
364
395
|
options: ControllerContextPackOptions = {},
|
|
365
396
|
dependencies: ControllerContextPackDependencies = {},
|
|
366
397
|
): ControllerContextPackProjection {
|
|
398
|
+
const retrievalMode = options.retrievalMode ?? "implementation";
|
|
367
399
|
const maxFiles = clamp(options.maxFiles, DEFAULT_MAX_FILES, 1, 30);
|
|
368
400
|
const maxSnippets = clamp(options.maxSnippets, DEFAULT_MAX_SNIPPETS, 1, 80);
|
|
369
401
|
const maxCharsPerSnippet = clamp(options.maxCharsPerSnippet, DEFAULT_MAX_CHARS_PER_SNIPPET, 500, 50_000);
|
|
@@ -395,6 +427,23 @@ export function buildControllerContextPack(
|
|
|
395
427
|
].filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0);
|
|
396
428
|
const goal = goalParts.join("\n").trim();
|
|
397
429
|
const terms = cleanList([...(options.searchTerms ?? []), ...textTokens(goal)]).slice(0, 14);
|
|
430
|
+
const impactDomains = cleanList(options.impactDomains)
|
|
431
|
+
.filter((domain): domain is ControllerContextImpactDomain => CONTROLLER_CONTEXT_IMPACT_DOMAINS.includes(domain as ControllerContextImpactDomain))
|
|
432
|
+
.slice(0, 6);
|
|
433
|
+
const impactTermDomains = new Map<string, ControllerContextImpactDomain[]>();
|
|
434
|
+
for (const domain of impactDomains) {
|
|
435
|
+
for (const term of IMPACT_DOMAIN_TERMS[domain]) {
|
|
436
|
+
const existing = impactTermDomains.get(term) ?? [];
|
|
437
|
+
if (!existing.includes(domain)) existing.push(domain);
|
|
438
|
+
impactTermDomains.set(term, existing);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const searchQueries = cleanList([
|
|
442
|
+
...(terms[0] ? [terms[0]] : []),
|
|
443
|
+
...impactDomains.flatMap((domain) => IMPACT_DOMAIN_TERMS[domain]),
|
|
444
|
+
...terms.slice(1),
|
|
445
|
+
]).slice(0, 32);
|
|
446
|
+
const impactCandidatePaths = new Map<ControllerContextImpactDomain, Set<string>>(impactDomains.map((domain) => [domain, new Set<string>()]));
|
|
398
447
|
const candidates = new Map<string, { reasons: Set<string>; lines: Set<number> }>();
|
|
399
448
|
const deniedPaths: Array<{ path: string; reason: string }> = [];
|
|
400
449
|
const omitted: Array<{ path: string; reason: string }> = [];
|
|
@@ -415,7 +464,7 @@ export function buildControllerContextPack(
|
|
|
415
464
|
};
|
|
416
465
|
|
|
417
466
|
if (structuralMode !== "off") {
|
|
418
|
-
const structuralQuery = goal || terms.join(" ");
|
|
467
|
+
const structuralQuery = [goal || terms.join(" "), ...impactDomains].filter(Boolean).join(" ");
|
|
419
468
|
if (!structuralQuery) {
|
|
420
469
|
structuralContext = {
|
|
421
470
|
...structuralContext,
|
|
@@ -504,9 +553,9 @@ export function buildControllerContextPack(
|
|
|
504
553
|
// Lexical retrieval is one bounded pass across candidate files for all terms.
|
|
505
554
|
// The old per-term loop reread the same source files up to 14 times even
|
|
506
555
|
// though inventory itself was cached.
|
|
507
|
-
if (
|
|
556
|
+
if (searchQueries.length > 0 && candidates.size < maxFiles * 3) {
|
|
508
557
|
const search = searchRepositoryMany(repoRoot, policy, {
|
|
509
|
-
queries:
|
|
558
|
+
queries: searchQueries,
|
|
510
559
|
includeGlobs: searchIncludeGlobs,
|
|
511
560
|
excludeGlobs,
|
|
512
561
|
maxResultsPerQuery: Math.max(maxFiles * 4, 12),
|
|
@@ -521,7 +570,16 @@ export function buildControllerContextPack(
|
|
|
521
570
|
searchTruncated = searchTruncated || search.truncated;
|
|
522
571
|
for (const hit of search.results) {
|
|
523
572
|
if (!coveredByGlob(hit.path, searchIncludeGlobs) || excludeGlobs.some((glob) => globMatches(glob, hit.path))) continue;
|
|
524
|
-
|
|
573
|
+
if (terms.includes(hit.query)) {
|
|
574
|
+
addReason(candidates, hit.path, `search:${hit.query}`, hit.line);
|
|
575
|
+
} else {
|
|
576
|
+
const domains = impactTermDomains.get(hit.query) ?? [];
|
|
577
|
+
if (domains.length === 0) addReason(candidates, hit.path, `search:${hit.query}`, hit.line);
|
|
578
|
+
for (const domain of domains) {
|
|
579
|
+
impactCandidatePaths.get(domain)?.add(hit.path);
|
|
580
|
+
addReason(candidates, hit.path, `impact:${domain}:${hit.query}`, hit.line);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
525
583
|
if (candidates.size >= maxFiles * 3) break;
|
|
526
584
|
}
|
|
527
585
|
}
|
|
@@ -541,6 +599,19 @@ export function buildControllerContextPack(
|
|
|
541
599
|
return left.path.localeCompare(right.path);
|
|
542
600
|
});
|
|
543
601
|
const selected = rankedCandidates.slice(0, maxFiles);
|
|
602
|
+
const selectedPaths = new Set(selected.map((entry) => entry.path));
|
|
603
|
+
const impactCoverage = impactDomains.map((domain) => {
|
|
604
|
+
const matchedFiles = [...(impactCandidatePaths.get(domain) ?? new Set<string>())].sort();
|
|
605
|
+
const selectedFiles = matchedFiles.filter((path) => selectedPaths.has(path));
|
|
606
|
+
return {
|
|
607
|
+
domain,
|
|
608
|
+
queries: [...IMPACT_DOMAIN_TERMS[domain]],
|
|
609
|
+
matchedFiles: matchedFiles.slice(0, 20),
|
|
610
|
+
selectedFiles: selectedFiles.slice(0, 20),
|
|
611
|
+
omittedFileCount: Math.max(0, matchedFiles.length - selectedFiles.length),
|
|
612
|
+
status: selectedFiles.length > 0 ? "selected" as const : matchedFiles.length > 0 ? "matched_not_selected" as const : "no_evidence" as const,
|
|
613
|
+
};
|
|
614
|
+
});
|
|
544
615
|
for (const entry of rankedCandidates.slice(maxFiles)) omitted.push({ path: entry.path, reason: "max_files" });
|
|
545
616
|
|
|
546
617
|
let remainingSnippets = maxSnippets;
|
|
@@ -595,6 +666,26 @@ export function buildControllerContextPack(
|
|
|
595
666
|
omitted.push({ path: "<none>", reason: "No description, search_terms, issue/task focus, or known_paths produced search terms." });
|
|
596
667
|
}
|
|
597
668
|
|
|
669
|
+
const rawSnippetTruncated = files.some((file) => file.snippets.some((snippet) => snippet.truncated));
|
|
670
|
+
const expansionSignals = [
|
|
671
|
+
...(files.length === 0 ? ["no_current_raw_source"] : []),
|
|
672
|
+
...(rawSnippetTruncated ? ["raw_snippet_truncated"] : []),
|
|
673
|
+
...(searchTruncated ? ["candidate_search_truncated"] : []),
|
|
674
|
+
...(structuralContext.truncated ? ["structural_context_truncated"] : []),
|
|
675
|
+
...(structuralContext.status === "stale" ? ["structural_context_stale"] : []),
|
|
676
|
+
...(structuralMode === "required" && !structuralContext.requiredSatisfied ? ["required_structural_context_unavailable"] : []),
|
|
677
|
+
...(policyDeniedFiles > 0 || deniedPaths.length > 0 ? ["policy_denied_candidates"] : []),
|
|
678
|
+
...(skippedLargeFiles > 0 ? ["large_candidates_skipped"] : []),
|
|
679
|
+
...impactCoverage.flatMap((coverage) => coverage.status === "no_evidence"
|
|
680
|
+
? [`impact_domain_without_evidence:${coverage.domain}`]
|
|
681
|
+
: coverage.status === "matched_not_selected"
|
|
682
|
+
? [`impact_domain_not_selected:${coverage.domain}`]
|
|
683
|
+
: []),
|
|
684
|
+
];
|
|
685
|
+
const rawCodeRequiredForImplementation = retrievalMode !== "implementation"
|
|
686
|
+
|| files.length === 0
|
|
687
|
+
|| rawSnippetTruncated;
|
|
688
|
+
|
|
598
689
|
return {
|
|
599
690
|
schemaVersion: CONTEXT_PACK_SCHEMA_VERSION,
|
|
600
691
|
generatedAt: new Date().toISOString(),
|
|
@@ -609,7 +700,9 @@ export function buildControllerContextPack(
|
|
|
609
700
|
goal,
|
|
610
701
|
git,
|
|
611
702
|
search: {
|
|
612
|
-
terms,
|
|
703
|
+
terms: searchQueries,
|
|
704
|
+
impactDomains,
|
|
705
|
+
impactCoverage,
|
|
613
706
|
includeGlobs: searchIncludeGlobs,
|
|
614
707
|
excludeGlobs,
|
|
615
708
|
scannedFiles,
|
|
@@ -628,13 +721,22 @@ export function buildControllerContextPack(
|
|
|
628
721
|
checks: taskChecks,
|
|
629
722
|
},
|
|
630
723
|
contextContract: {
|
|
631
|
-
strategy:
|
|
632
|
-
|
|
724
|
+
strategy: retrievalMode === "implementation"
|
|
725
|
+
? "Use the returned current raw snippets plus bounded impact evidence as the implementation decision input. ChatGPT owns semantic sufficiency; do not re-read source solely because it arrived through search."
|
|
726
|
+
: "Use this pack as investigation evidence and deliberately expand the evidence surface when the selected mode requires planning, debugging, or review.",
|
|
727
|
+
retrievalMode,
|
|
728
|
+
semanticSufficiencyAuthority: "chatgpt",
|
|
729
|
+
rawCodeRequiredForImplementation,
|
|
730
|
+
expansionSignals,
|
|
633
731
|
notes: [
|
|
634
|
-
|
|
732
|
+
retrievalMode === "implementation"
|
|
733
|
+
? "The pack already contains policy-approved current raw source snippets with file SHA identities. Request wider/exact ranges only when ChatGPT judges the impact surface ambiguous or an expansion signal makes the raw evidence mechanically incomplete."
|
|
734
|
+
: "Investigation modes may intentionally expand exact ranges, structural relationships, tests, and neighboring modules before any implementation decision.",
|
|
735
|
+
"Search/CodeGraph ranking is discovery evidence, not a business-semantics authority. Forge never decides that the returned files are the complete semantic impact surface.",
|
|
736
|
+
impactDomains.length > 0 ? `Impact domains were selected by ChatGPT and expanded mechanically in this same retrieval call: ${impactDomains.join(", ")}. Missing or omitted domain evidence is an ambiguity signal, not proof that the domain is irrelevant.` : "No explicit cross-cutting impact domains were requested; ChatGPT may add them when state, scheduling, notifications, events, caching, API, or concurrency could materially change the implementation.",
|
|
635
737
|
"CodeGraph structural evidence is discovery evidence. Raw source access still passes through Forge repository policy and current-file reads.",
|
|
636
738
|
structuralContext.status === "stale" ? "CodeGraph reports stale structural evidence. Treat graph relationships as hints and prefer the returned current raw source for changed files." : structuralContext.status === "unavailable" || structuralContext.status === "degraded" ? "Structural context was unavailable or degraded; bounded text search remains the fallback." : structuralContext.status === "ready" ? "CodeGraph structural context was queried read-only with index sync disabled." : "Structural context was not requested.",
|
|
637
|
-
"Run focused validation after
|
|
739
|
+
"Run focused validation after a coherent edit batch; expensive full-suite checks belong at candidate/release boundaries unless debugging specifically needs them earlier.",
|
|
638
740
|
taskChecks.length > 0 ? `Task checks advertised by the board: ${taskChecks.join(", ")}.` : "No task-specific checks were found in the compact ledger.",
|
|
639
741
|
],
|
|
640
742
|
},
|
|
@@ -643,10 +745,12 @@ export function buildControllerContextPack(
|
|
|
643
745
|
? ["Structural context was required but is not current/ready. Refresh CodeGraph explicitly or continue only with the degraded-plan warning visible."]
|
|
644
746
|
: []),
|
|
645
747
|
files.length > 0
|
|
646
|
-
?
|
|
748
|
+
? retrievalMode === "implementation"
|
|
749
|
+
? "ChatGPT should decide whether the impact coverage is sufficient; if yes, edit directly from the returned current raw snippets and SHA identities instead of issuing a mandatory second read."
|
|
750
|
+
: "Expand exact ranges or neighboring evidence when that investigation can materially change the plan, diagnosis, or review conclusion."
|
|
647
751
|
: "Provide known_paths or narrower search_terms before attempting implementation.",
|
|
648
|
-
"After editing, review
|
|
649
|
-
"Use targeted validation before
|
|
752
|
+
"After editing, review the bounded diff/evidence for the coherent edit batch.",
|
|
753
|
+
"Use targeted validation before acceptance; defer expensive full-suite validation to a stable candidate or release boundary unless the task specifically requires it earlier.",
|
|
650
754
|
],
|
|
651
755
|
};
|
|
652
756
|
}
|
|
@@ -71,7 +71,7 @@ export interface ControllerOperationalPlan {
|
|
|
71
71
|
source: "task-recovery-loop";
|
|
72
72
|
continuationState: string;
|
|
73
73
|
nextActions: string[];
|
|
74
|
-
|
|
74
|
+
recoveryArtifacts: string[];
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -173,7 +173,7 @@ export function buildControllerOperationalPlan(repoRoot: string, ledger: TaskLed
|
|
|
173
173
|
label: "Fresh-session recovery",
|
|
174
174
|
when: "A controller session restarts or loses conversation state.",
|
|
175
175
|
steps: ["load task ledger", "read operational plan", "select continuation state", "expand only needed context", "resume or ask for the one missing decision"],
|
|
176
|
-
requiredEvidence: ["taskLedger", "operationalPlan", "
|
|
176
|
+
requiredEvidence: ["taskLedger", "operationalPlan", "recoveryArtifacts"],
|
|
177
177
|
},
|
|
178
178
|
],
|
|
179
179
|
},
|
|
@@ -195,7 +195,7 @@ export function buildControllerOperationalPlan(repoRoot: string, ledger: TaskLed
|
|
|
195
195
|
},
|
|
196
196
|
mcpToolSchemaConvergence: {
|
|
197
197
|
source: "mcp-tool-schema-convergence",
|
|
198
|
-
readModels: ["controller_context", "controller_context_pack", "work_status_digest", "
|
|
198
|
+
readModels: ["controller_context", "controller_context_pack", "work_status_digest", "prepare_transfer_artifacts"],
|
|
199
199
|
commandModels: ["work_submit", "run_check", "dispatch_task", "verify_task", "accept_task", "repository_safe_patch_apply"],
|
|
200
200
|
compatibilityRules: [
|
|
201
201
|
"compact reads are default",
|
|
@@ -220,7 +220,7 @@ export function buildControllerOperationalPlan(repoRoot: string, ledger: TaskLed
|
|
|
220
220
|
source: "task-recovery-loop",
|
|
221
221
|
continuationState: ledger.status.kind,
|
|
222
222
|
nextActions: ledger.suggestedNextActions.slice(0, 6),
|
|
223
|
-
|
|
223
|
+
recoveryArtifacts: [".ai/harness/controller/task-ledger.json", ".ai/harness/projections/controller-task-ledger.md"],
|
|
224
224
|
},
|
|
225
225
|
};
|
|
226
226
|
}
|
|
@@ -7,7 +7,7 @@ import { resolveRepoPreferredControllerHome } from "../repositories/controller-h
|
|
|
7
7
|
import { buildRequirementBoard } from "../../runtime/control-plane/facade/requirement-board";
|
|
8
8
|
|
|
9
9
|
const LEDGER_JSON_PATH = ".ai/harness/controller/task-ledger.json";
|
|
10
|
-
const
|
|
10
|
+
const LEDGER_PROJECTION_PATH = ".ai/harness/projections/controller-task-ledger.md";
|
|
11
11
|
const LEDGER_SCHEMA_VERSION = 2;
|
|
12
12
|
|
|
13
13
|
export interface TaskLedgerTaskProjection {
|
|
@@ -539,7 +539,7 @@ function renderTaskRow(task: TaskLedgerTaskProjection): string {
|
|
|
539
539
|
return `| ${tableEscape(task.issueId)} | ${tableEscape(task.taskId)} | ${tableEscape(task.title)} | ${tableEscape(task.effectiveStatus)} | ${tableEscape(task.verificationStatus)} | ${tableEscape(task.latestRunStatus ?? task.activeRunStatus ?? "")} |`;
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
-
export function
|
|
542
|
+
export function renderControllerTaskLedgerProjection(projection: TaskLedgerProjection, reason: string): string {
|
|
543
543
|
const current = projection.currentIssueId
|
|
544
544
|
? projection.issues.find((issue) => issue.id === projection.currentIssueId)
|
|
545
545
|
: undefined;
|
|
@@ -547,7 +547,7 @@ export function renderControllerTaskLedgerHandoff(projection: TaskLedgerProjecti
|
|
|
547
547
|
? projection.attention.map(renderTaskRow)
|
|
548
548
|
: ["| - | - | No attention tasks | - | - | - |"];
|
|
549
549
|
return [
|
|
550
|
-
"# Controller Task Ledger
|
|
550
|
+
"# Controller Task Ledger Projection",
|
|
551
551
|
"",
|
|
552
552
|
`> **Generated**: ${projection.generatedAt}`,
|
|
553
553
|
`> **Reason**: ${reason}`,
|
|
@@ -608,25 +608,25 @@ function artifactPreview(repoRoot: string, path: string): TaskLedgerArtifactPrev
|
|
|
608
608
|
|
|
609
609
|
export function writeControllerTaskLedgerArtifacts(repoRoot: string, input: { reason?: unknown } = {}): WrittenTaskLedgerArtifacts {
|
|
610
610
|
if (legacyIssueCutoverState(repoRoot).retired) {
|
|
611
|
-
throw new Error("LEGACY_TASK_LEDGER_WRITES_RETIRED: task-ledger
|
|
611
|
+
throw new Error("LEGACY_TASK_LEDGER_WRITES_RETIRED: task-ledger projection files are frozen after SQLite cutover.");
|
|
612
612
|
}
|
|
613
613
|
const reason = String(input.reason ?? "manual").trim() || "manual";
|
|
614
614
|
const projection = buildControllerTaskLedgerProjection(repoRoot);
|
|
615
615
|
const jsonPath = join(repoRoot, LEDGER_JSON_PATH);
|
|
616
|
-
const
|
|
616
|
+
const projectionPath = join(repoRoot, LEDGER_PROJECTION_PATH);
|
|
617
617
|
mkdirSync(dirname(jsonPath), { recursive: true });
|
|
618
|
-
mkdirSync(dirname(
|
|
618
|
+
mkdirSync(dirname(projectionPath), { recursive: true });
|
|
619
619
|
writeFileSync(jsonPath, `${JSON.stringify(projection, null, 2)}\n`, "utf-8");
|
|
620
|
-
writeFileSync(
|
|
620
|
+
writeFileSync(projectionPath, renderControllerTaskLedgerProjection(projection, reason), "utf-8");
|
|
621
621
|
return {
|
|
622
622
|
projection,
|
|
623
623
|
artifacts: [
|
|
624
624
|
artifactPreview(repoRoot, LEDGER_JSON_PATH),
|
|
625
|
-
artifactPreview(repoRoot,
|
|
625
|
+
artifactPreview(repoRoot, LEDGER_PROJECTION_PATH),
|
|
626
626
|
],
|
|
627
627
|
};
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
-
export function controllerTaskLedgerArtifactPaths(): { json: string;
|
|
631
|
-
return { json: LEDGER_JSON_PATH,
|
|
630
|
+
export function controllerTaskLedgerArtifactPaths(): { json: string; projection: string } {
|
|
631
|
+
return { json: LEDGER_JSON_PATH, projection: LEDGER_PROJECTION_PATH };
|
|
632
632
|
}
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
type RoutePolicyInput,
|
|
7
7
|
} from '../../runtime/control-plane/routing/route-policy';
|
|
8
8
|
|
|
9
|
-
export type WorkMode = 'direct_edit' | 'bounded_work' | 'quick_agent' | 'issue_task'
|
|
10
|
-
export type ExecutionPathPreference = 'fast' | 'durable'
|
|
9
|
+
export type WorkMode = 'direct_edit' | 'bounded_work' | 'quick_agent' | 'issue_task';
|
|
10
|
+
export type ExecutionPathPreference = 'fast' | 'durable';
|
|
11
11
|
export type EffectiveTaskMode = ExplicitTaskMode | 'bounded';
|
|
12
12
|
|
|
13
13
|
export interface WorkModeAssessmentInput {
|
|
@@ -38,7 +38,6 @@ export interface WorkModeAssessment {
|
|
|
38
38
|
reasons: string[];
|
|
39
39
|
nextTools: string[];
|
|
40
40
|
issueRequired: boolean;
|
|
41
|
-
campaignRequired: boolean;
|
|
42
41
|
taskMode: EffectiveTaskMode;
|
|
43
42
|
explicitMode: ExplicitTaskMode | null;
|
|
44
43
|
modeBehavior: {
|
|
@@ -57,7 +56,7 @@ export function parseExplicitTaskMode(value: unknown): ExplicitTaskMode | undefi
|
|
|
57
56
|
if (typeof value !== 'string') return undefined;
|
|
58
57
|
if (!value) return undefined;
|
|
59
58
|
const normalized = value.replace(/^-/, '') as ExplicitTaskMode;
|
|
60
|
-
return ['direct', 'plan', 'debug', 'review', '
|
|
59
|
+
return ['direct', 'plan', 'debug', 'review', 'release', 'scale'].includes(normalized)
|
|
61
60
|
? normalized
|
|
62
61
|
: undefined;
|
|
63
62
|
}
|
|
@@ -80,10 +79,6 @@ function behaviorFor(mode: EffectiveTaskMode): WorkModeAssessment['modeBehavior'
|
|
|
80
79
|
structuralContext: 'off', mutationPhase: 'read_only', issueRequired: false, planRequired: false, worktreeRequired: false,
|
|
81
80
|
workflow: ['read diff and affected source', 'review correctness and regressions', 'review lifecycle, concurrency, and security', 'report test gaps without mutation'],
|
|
82
81
|
};
|
|
83
|
-
case 'campaign': return {
|
|
84
|
-
structuralContext: 'off', mutationPhase: 'coordinate', issueRequired: false, planRequired: false, worktreeRequired: true,
|
|
85
|
-
workflow: ['decompose independent deliverables', 'create campaign', 'schedule isolated Tasks', 'reconcile and review campaign evidence'],
|
|
86
|
-
};
|
|
87
82
|
case 'release': return {
|
|
88
83
|
structuralContext: 'off', mutationPhase: 'release_gate', issueRequired: false, planRequired: false, worktreeRequired: false,
|
|
89
84
|
workflow: ['verify checks and changelog', 'build immutable complete release', 'run integration and deployment gates', 'activate through Recovery', 'verify rollback evidence'],
|
|
@@ -100,7 +95,6 @@ function behaviorFor(mode: EffectiveTaskMode): WorkModeAssessment['modeBehavior'
|
|
|
100
95
|
}
|
|
101
96
|
|
|
102
97
|
function nextTools(decision: RouteDecision, investigation: boolean): string[] {
|
|
103
|
-
if (decision.workMode === 'campaign') return ['create_campaign', 'add_campaign_task', 'reconcile_campaign', 'get_campaign_review_packet'];
|
|
104
98
|
if (decision.workMode === 'issue_task') return ['inspect_issue_readiness', 'create_issue or append_task', 'dispatch_task', 'verify_task', 'accept_task'];
|
|
105
99
|
if (decision.workMode === 'quick_agent') return ['search_repository', 'rh_work(operation=delegate)', 'rh_work(operation=continue)', 'rh_work(operation=verify)'];
|
|
106
100
|
if (decision.workMode === 'bounded_work') return [
|
|
@@ -129,7 +123,7 @@ export function assessWorkMode(input: WorkModeAssessmentInput): WorkModeAssessme
|
|
|
129
123
|
const explicitMode = parseExplicitTaskMode(input.explicitMode);
|
|
130
124
|
const investigationMode = explicitMode === 'plan' || explicitMode === 'debug' || explicitMode === 'review';
|
|
131
125
|
const readonlyMode = investigationMode;
|
|
132
|
-
const parallelMode = explicitMode === '
|
|
126
|
+
const parallelMode = explicitMode === 'scale';
|
|
133
127
|
const routeDecision = decideRoute(input.routePolicyInput ?? {
|
|
134
128
|
intent: {
|
|
135
129
|
objective: input.description,
|
|
@@ -160,7 +154,7 @@ export function assessWorkMode(input: WorkModeAssessmentInput): WorkModeAssessme
|
|
|
160
154
|
},
|
|
161
155
|
});
|
|
162
156
|
const taskMode: EffectiveTaskMode = explicitMode
|
|
163
|
-
?? (routeDecision.workMode === '
|
|
157
|
+
?? (routeDecision.workMode === 'direct_edit' ? 'direct' : 'bounded');
|
|
164
158
|
const modeBehavior = behaviorFor(taskMode);
|
|
165
159
|
return {
|
|
166
160
|
recommendedMode: routeDecision.workMode,
|
|
@@ -174,10 +168,9 @@ export function assessWorkMode(input: WorkModeAssessmentInput): WorkModeAssessme
|
|
|
174
168
|
: explicitMode === 'release'
|
|
175
169
|
? ['run_check', 'request_release_gate', 'capability_recovery_plan', 'capability_recovery_apply', 'controller_ready']
|
|
176
170
|
: explicitMode === 'scale'
|
|
177
|
-
? ['
|
|
171
|
+
? ['rh_work(operation=plan_create)', 'rh_work(operation=start)', 'process_get', 'process_wait', 'benchmark evidence']
|
|
178
172
|
: nextTools(routeDecision, input.requiresInvestigation === true || investigationMode || (input.knownPaths?.length ?? 0) === 0),
|
|
179
173
|
issueRequired: routeDecision.workMode === 'issue_task',
|
|
180
|
-
campaignRequired: routeDecision.workMode === 'campaign',
|
|
181
174
|
taskMode,
|
|
182
175
|
explicitMode: explicitMode ?? null,
|
|
183
176
|
modeBehavior,
|
package/src/cli/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* Forge CLI entry
|
|
3
|
+
* Forge CLI entry. Public commands expose product workflows; compatibility and host-dispatch commands stay hidden.
|
|
4
4
|
*
|
|
5
5
|
* Wires commander.js to the global runtime bootstrap, repo-local update,
|
|
6
6
|
* hook adapter, status, doctor, migrate, security, and tool command bodies.
|
|
@@ -13,7 +13,7 @@ import { runInit, runInteractiveInit, type InitBrainMode } from './commands/init
|
|
|
13
13
|
import { runHook } from './commands/hook';
|
|
14
14
|
import { CLI_VERSION, formatStatus, runStatus } from './commands/status';
|
|
15
15
|
import { formatDoctor, runDoctor } from './commands/doctor';
|
|
16
|
-
import {
|
|
16
|
+
import { buildSetupCommand, formatInitHook, runInitHook } from './commands/init-hook';
|
|
17
17
|
import { formatMigratePlan, runMigrate } from './commands/migrate';
|
|
18
18
|
import { buildToolsCommand } from './commands/tools';
|
|
19
19
|
import { buildPluginCommand } from './commands/plugin';
|
|
@@ -23,7 +23,6 @@ import { buildDocsCommand } from './commands/docs';
|
|
|
23
23
|
import { buildMcpCommand } from './commands/mcp';
|
|
24
24
|
import { buildChatgptCommand } from './commands/chatgpt';
|
|
25
25
|
import { buildRunCommand } from './commands/run';
|
|
26
|
-
import { buildControllerCommand } from './commands/controller';
|
|
27
26
|
import { buildRepositoryCommand } from './commands/repository';
|
|
28
27
|
import { buildRuntimeCommand } from './commands/runtime';
|
|
29
28
|
import { buildRecoveryCommand } from './commands/recovery';
|
|
@@ -41,8 +40,6 @@ import { bootstrapManagedRuntimeEnv } from '../runtime/shared/managed-env';
|
|
|
41
40
|
bootstrapManagedRuntimeEnv();
|
|
42
41
|
|
|
43
42
|
export const SUBCOMMANDS = [
|
|
44
|
-
'init',
|
|
45
|
-
'init-hook',
|
|
46
43
|
'install',
|
|
47
44
|
'uninstall',
|
|
48
45
|
'hook',
|
|
@@ -76,7 +73,7 @@ interface GlobalRuntimeCommandOptions {
|
|
|
76
73
|
cli?: boolean;
|
|
77
74
|
syncSkill?: boolean;
|
|
78
75
|
hooks?: string | false;
|
|
79
|
-
|
|
76
|
+
withExternalSkills?: boolean;
|
|
80
77
|
codegraph?: boolean;
|
|
81
78
|
brainRoot?: string;
|
|
82
79
|
json?: boolean;
|
|
@@ -94,7 +91,7 @@ function runGlobalRuntimeBootstrap(commandName: 'init' | 'install', rawOpts: Glo
|
|
|
94
91
|
installCli: rawOpts.cli !== false,
|
|
95
92
|
syncSkill: rawOpts.syncSkill !== false,
|
|
96
93
|
hostAdapters: rawOpts.hooks !== false,
|
|
97
|
-
externalSkills: rawOpts.
|
|
94
|
+
externalSkills: rawOpts.withExternalSkills === true,
|
|
98
95
|
codegraph: rawOpts.codegraph !== false,
|
|
99
96
|
brainRoot: rawOpts.brainRoot,
|
|
100
97
|
});
|
|
@@ -114,21 +111,6 @@ export function buildProgram(): Command {
|
|
|
114
111
|
.addHelpText('after', '\nGlobal shortcuts:\n -V, --version output the version number')
|
|
115
112
|
.exitOverride();
|
|
116
113
|
|
|
117
|
-
program
|
|
118
|
-
.command('init')
|
|
119
|
-
.description('Install the Forge CLI, global hook adapters, and required runtime dependencies')
|
|
120
|
-
.option('--target <target>', `Host target for adapters and runtime skills: ${VALID_TARGETS.join('|')}`, 'both')
|
|
121
|
-
.option('--no-cli', 'Skip installing the Forge CLI globally')
|
|
122
|
-
.option('--no-sync-skill', 'Skip refreshing legacy forge skill aliases under host skill roots')
|
|
123
|
-
.option('--no-hooks', 'Skip global hook adapter installation')
|
|
124
|
-
.option('--no-external-skills', 'Skip Waza, Mermaid, and cross-review (codex-review/claude-review) skill bootstrap')
|
|
125
|
-
.option('--no-codegraph', 'Skip CodeGraph CLI/MCP configuration')
|
|
126
|
-
.option('--brain-root <path>', 'Brain vault root to persist for Forge brain commands')
|
|
127
|
-
.option('--refresh', 'Compatibility no-op; init already refreshes the idempotent user-level runtime')
|
|
128
|
-
.option('--json', 'Output JSON instead of human-readable text')
|
|
129
|
-
.action((rawOpts: GlobalRuntimeCommandOptions & { refresh?: boolean }) => {
|
|
130
|
-
runGlobalRuntimeBootstrap('init', rawOpts);
|
|
131
|
-
});
|
|
132
114
|
|
|
133
115
|
program
|
|
134
116
|
.command('update')
|
|
@@ -140,7 +122,7 @@ export function buildProgram(): Command {
|
|
|
140
122
|
.option('--check-updates', 'Include network-backed version update advisories in setup check output')
|
|
141
123
|
.option('--no-runtime-refresh', 'Skip runtime refresh and run the read-only setup check only')
|
|
142
124
|
.option('--no-cli', 'Skip installing the Forge CLI globally')
|
|
143
|
-
.option('--no-sync-skill', 'Skip refreshing
|
|
125
|
+
.option('--no-sync-skill', 'Skip refreshing Forge host skill aliases')
|
|
144
126
|
.option('--no-hooks', 'Skip global hook adapter installation')
|
|
145
127
|
.option('--with-external-skills', 'Also bootstrap third-party Waza, Mermaid, and cross-review skills')
|
|
146
128
|
.option('--no-external-skills', 'Compatibility no-op; update no longer bootstraps third-party skills by default')
|
|
@@ -164,7 +146,6 @@ export function buildProgram(): Command {
|
|
|
164
146
|
syncSkill?: boolean;
|
|
165
147
|
hooks?: string | false;
|
|
166
148
|
withExternalSkills?: boolean;
|
|
167
|
-
externalSkills?: boolean;
|
|
168
149
|
codegraph?: boolean;
|
|
169
150
|
configureCodegraph?: boolean;
|
|
170
151
|
brainRoot?: string;
|
|
@@ -220,7 +201,7 @@ export function buildProgram(): Command {
|
|
|
220
201
|
|
|
221
202
|
program
|
|
222
203
|
.command('adopt')
|
|
223
|
-
.description('Install or refresh the repo-local
|
|
204
|
+
.description('Install or refresh the repo-local Forge workflow in an existing repo')
|
|
224
205
|
.argument('[action]', 'Optional action: rollback')
|
|
225
206
|
.option('--repo <path>', 'Target repository path (defaults to cwd)')
|
|
226
207
|
.option('--archive <path>', 'Runtime reclaim archive to restore when action is rollback')
|
|
@@ -250,7 +231,7 @@ export function buildProgram(): Command {
|
|
|
250
231
|
target: string;
|
|
251
232
|
syncSkill?: boolean;
|
|
252
233
|
hostAdapters?: boolean;
|
|
253
|
-
|
|
234
|
+
withExternalSkills?: boolean;
|
|
254
235
|
verify?: boolean;
|
|
255
236
|
codegraph?: boolean;
|
|
256
237
|
reclaimRuntime?: boolean;
|
|
@@ -409,9 +390,9 @@ export function buildProgram(): Command {
|
|
|
409
390
|
.option('--target <target>', `Target host: ${VALID_TARGETS.join('|')}`, 'both')
|
|
410
391
|
.option('--location <location>', `Adapter-only install location: ${VALID_LOCATIONS.join('|')}`)
|
|
411
392
|
.option('--no-cli', 'Skip installing the Forge CLI globally')
|
|
412
|
-
.option('--no-sync-skill', 'Skip refreshing
|
|
393
|
+
.option('--no-sync-skill', 'Skip refreshing Forge host skill aliases')
|
|
413
394
|
.option('--no-hooks', 'Skip global hook adapter installation during full runtime install')
|
|
414
|
-
.option('--
|
|
395
|
+
.option('--with-external-skills', 'Also install optional third-party Waza/Mermaid skills and Forge cross-review helpers')
|
|
415
396
|
.option('--no-codegraph', 'Skip CodeGraph CLI/MCP configuration')
|
|
416
397
|
.option('--brain-root <path>', 'Brain vault root to persist for Forge brain commands')
|
|
417
398
|
.option('--json', 'Output JSON instead of human-readable text')
|
|
@@ -466,7 +447,7 @@ export function buildProgram(): Command {
|
|
|
466
447
|
});
|
|
467
448
|
|
|
468
449
|
program
|
|
469
|
-
.command('hook')
|
|
450
|
+
.command('hook', { hidden: true })
|
|
470
451
|
.description('Dispatch a hook event to opt-in repo .ai/hooks/<script>')
|
|
471
452
|
.argument('<event>', 'Hook event name')
|
|
472
453
|
.requiredOption('--route <route>', 'Route id (default, edit, bash, always)')
|
|
@@ -498,11 +479,10 @@ export function buildProgram(): Command {
|
|
|
498
479
|
process.exit(report.summary.fail > 0 ? 1 : 0);
|
|
499
480
|
});
|
|
500
481
|
|
|
501
|
-
program.addCommand(buildInitHookCommand());
|
|
502
482
|
program.addCommand(buildSetupCommand());
|
|
503
483
|
|
|
504
484
|
program
|
|
505
|
-
.command('migrate')
|
|
485
|
+
.command('migrate', { hidden: true })
|
|
506
486
|
.description('Migrate legacy project-level hook adapters to the global CLI (dry-run by default)')
|
|
507
487
|
.option('--apply', 'Commit changes (default is dry-run)')
|
|
508
488
|
.option('--json', 'Output JSON plan')
|
|
@@ -529,13 +509,12 @@ export function buildProgram(): Command {
|
|
|
529
509
|
|
|
530
510
|
program.addCommand(buildToolsCommand());
|
|
531
511
|
program.addCommand(buildPluginCommand());
|
|
532
|
-
program.addCommand(buildBrainCommand());
|
|
533
|
-
program.addCommand(buildCapabilityContextCommand());
|
|
512
|
+
program.addCommand(buildBrainCommand(), { hidden: true });
|
|
513
|
+
program.addCommand(buildCapabilityContextCommand(), { hidden: true });
|
|
534
514
|
program.addCommand(buildDocsCommand());
|
|
535
515
|
program.addCommand(buildMcpCommand());
|
|
536
516
|
program.addCommand(buildChatgptCommand());
|
|
537
|
-
program.addCommand(buildRunCommand());
|
|
538
|
-
program.addCommand(buildControllerCommand());
|
|
517
|
+
program.addCommand(buildRunCommand(), { hidden: true });
|
|
539
518
|
program.addCommand(buildRepositoryCommand());
|
|
540
519
|
program.addCommand(buildRuntimeCommand());
|
|
541
520
|
program.addCommand(buildRecoveryCommand());
|
|
@@ -28,7 +28,7 @@ export const EXPECTED_FACADE_TOOLS = [...PREFERRED_FACADE_TOOL_NAMES] as const;
|
|
|
28
28
|
/**
|
|
29
29
|
* Interactive development tools in the bounded default schema. The historical
|
|
30
30
|
* core and advanced labels expose the same bounded default surface; git / issue /
|
|
31
|
-
*
|
|
31
|
+
* legacy atomics live in the `full` compatibility profile.
|
|
32
32
|
*/
|
|
33
33
|
export const OPTIONAL_INTERACTIVE_DEVELOPMENT_TOOLS = [
|
|
34
34
|
'repository_safe_patch_apply',
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { dirname, join } from "path";
|
|
3
|
+
|
|
4
|
+
export type ConsoleAssetName = "app.js" | "app.css";
|
|
5
|
+
|
|
6
|
+
export function resolveConsoleAssetPath(
|
|
7
|
+
asset: ConsoleAssetName,
|
|
8
|
+
executablePath = process.execPath,
|
|
9
|
+
): string | URL {
|
|
10
|
+
const releaseAssetPath = join(dirname(executablePath), "ui-dist", asset);
|
|
11
|
+
if (existsSync(releaseAssetPath)) return releaseAssetPath;
|
|
12
|
+
return new URL(`./ui-dist/${asset}`, import.meta.url);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function readConsoleAsset(asset: ConsoleAssetName): string {
|
|
16
|
+
return readFileSync(resolveConsoleAssetPath(asset), "utf8");
|
|
17
|
+
}
|