@moretea-labs/forge 1.8.1 → 1.8.2
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 +5 -2
- package/CHANGELOG.md +20 -0
- package/README.en.md +24 -3
- package/README.md +10 -6
- package/README.zh-CN.md +5 -3
- package/THIRD_PARTY_NOTICES.md +1 -2
- package/adapters/chatgpt/browser-delivery-runtime.ts +5 -3
- package/adapters/chatgpt/provider-delivery.ts +1 -1
- package/adapters/chatgpt/work-conversation-binding-store.ts +20 -1
- package/adapters/computer/chatgpt-extension-target.ts +295 -50
- package/adapters/computer/chrome-extension/background.js +227 -71
- package/adapters/computer/chrome-extension/content.js +181 -157
- package/adapters/computer/chrome-extension/manifest.json +3 -1
- package/adapters/computer/desktop-operator-provider.ts +18 -6
- package/adapters/computer/interaction-target-authority.ts +39 -9
- package/adapters/mcp/access-mode.ts +18 -39
- package/adapters/mcp/auth.ts +2 -6
- package/adapters/mcp/instructions.ts +2 -2
- package/adapters/mcp/multi-repository.ts +7 -28
- package/adapters/mcp/runtime-gateway/context-adapter.ts +79 -40
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +7 -15
- package/adapters/mcp/runtime-gateway/execution-tools.ts +7 -13
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +16 -3
- package/adapters/mcp/runtime-gateway/process-tools.ts +3 -21
- package/adapters/mcp/runtime-gateway/router.ts +3 -5
- package/adapters/mcp/runtime-gateway/routing-policy.ts +17 -10
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1 -1
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +14 -9
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +1 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +36 -1
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +58 -4
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +7 -3
- package/adapters/mcp/runtime-gateway/work-adapter.ts +16 -16
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +0 -17
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +69 -12
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +26 -2
- package/adapters/mcp/server.ts +24 -3
- package/adapters/mcp/setup.ts +4 -10
- package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +672 -2935
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +1 -1
- package/adapters/mcp/tool-mapping/repository-tools.ts +52 -86
- package/adapters/mcp/toolset-names.ts +11 -42
- package/adapters/mcp/toolset.ts +16 -90
- package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
- package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
- package/adapters/mcp/transports/http-observation.ts +18 -6
- package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
- package/adapters/mcp/transports/http.ts +54 -14
- package/adapters/mcp/types.ts +0 -6
- package/assets/hooks/lib/session-state.sh +0 -38
- package/assets/hooks/post-bash.sh +1 -1
- package/assets/hooks/post-tool-observer.sh +0 -5
- package/assets/hooks/prompt-guard.sh +0 -55
- package/assets/partials/04-project-structure.partial.md +1 -1
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -1
- package/assets/partials-agents/08-deep-docs.partial.md +1 -1
- package/assets/reference-configs/agentic-development-flow.md +1 -1
- package/assets/reference-configs/external-tooling.md +0 -76
- package/assets/reference-configs/release-deploy.md +1 -1
- package/assets/templates/helpers/architecture-queue.sh +1 -1
- package/assets/templates/helpers/check-agent-tooling.sh +1 -327
- package/assets/templates/helpers/check-task-workflow.sh +1 -1
- package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
- package/assets/templates/helpers/migrate-project-template.sh +2 -2
- package/assets/workflow-contract.v1.json +0 -2
- package/docs/README.md +5 -2
- package/docs/architecture/CURRENT.md +114 -30
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +42 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +15 -2
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +4 -2
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +109 -4
- package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
- package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
- package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
- package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
- package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
- package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
- package/docs/architecture/domains/public-surface.md +3 -5
- package/docs/architecture/domains/verification.md +5 -5
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +2 -2
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
- package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
- package/docs/forge-plugin-management.md +14 -2
- package/docs/operations/controller-browser-plugin.md +23 -0
- package/docs/operations/features.md +6 -7
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/github-repository.md +4 -4
- package/docs/operations/github-semantic-release-projection.md +20 -0
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +13 -54
- package/docs/operations/runtime-performance-diagnostics.md +8 -0
- package/docs/operations/standalone-disaster-recovery.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/03-first-repository-task.md +2 -0
- package/docs/wiki/Computer-and-Plugins.md +78 -0
- package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
- package/docs/wiki/Home.md +16 -2
- package/docs/wiki/Implementation.md +24 -12
- package/docs/wiki/Installation.md +28 -32
- package/docs/wiki/Integrations.md +4 -0
- package/docs/wiki/Quick-Start.md +35 -22
- package/docs/wiki/Releases-and-Upgrades.md +15 -25
- package/docs/wiki/_Sidebar.md +5 -1
- package/package.json +1 -12
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +63 -38
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +10 -0
- package/packages/kernel/scheduler/application/eligibility.ts +4 -1
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +13 -10
- package/packages/kernel/work/application/work-service.ts +1 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
- package/packages/kernel/work/domain/state-machine.ts +9 -2
- package/packages/kernel/work/infrastructure/work-contract-store.ts +28 -1
- package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
- package/packages/plugin-runtime/computer/conversation-target.ts +17 -7
- package/scripts/assemble-template.ts +11 -72
- package/scripts/check-mcp-compatibility.ts +55 -74
- package/scripts/check-package-identity.mjs +0 -5
- package/scripts/check-runtime-architecture.mjs +305 -28
- package/scripts/init-project.sh +0 -46
- package/scripts/install-ios-hid-toolchain.sh +60 -0
- package/scripts/install-standalone-recovery.ts +1 -1
- package/scripts/lib/project-init-lib.sh +3 -3
- package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
- package/scripts/open-source-audit-allowlist.txt +0 -1
- package/scripts/run-governed-gate.ts +2 -0
- package/scripts/smoke-ios-physical-device.ts +38 -10
- package/scripts/smoke-mcp-http-runtime.ts +7 -8
- package/scripts/smoke-mcp-tool-surface.ts +27 -41
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +6 -3
- package/scripts/sync-generated-authority.mjs +0 -1
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/agent-jobs/integration.ts +2 -2
- package/src/cli/chatgpt-browser/file-policy.ts +2 -2
- package/src/cli/commands/doctor.ts +1 -143
- package/src/cli/commands/global-runtime.ts +3 -196
- package/src/cli/commands/init-hook.ts +6 -23
- package/src/cli/commands/init.ts +3 -252
- package/src/cli/commands/mcp.ts +0 -5
- package/src/cli/commands/prompt-guard-decision.ts +0 -2
- package/src/cli/commands/recovery.ts +2 -14
- package/src/cli/commands/repository.ts +17 -1
- package/src/cli/commands/setup-execution.ts +41 -0
- package/src/cli/controller/completion-orchestrator.ts +1 -1
- package/src/cli/controller/composite-operations.ts +1 -1
- package/src/cli/controller/context/instruction-context.ts +1 -1
- package/src/cli/controller/context/known-paths.ts +2 -2
- package/src/cli/controller/context/query-planning.ts +11 -4
- package/src/cli/controller/context/rule-resolution.ts +2 -2
- package/src/cli/controller/context/source-materializer.ts +2 -2
- package/src/cli/controller/context/structural-provider.ts +84 -0
- package/src/cli/controller/context/structural.ts +6 -6
- package/src/cli/controller/context/types.ts +8 -9
- package/src/cli/controller/context-pack.ts +97 -76
- package/src/cli/controller/operational-plan.ts +1 -1
- package/src/cli/controller/systemd-user.ts +4 -10
- package/src/cli/editing/edit-session.ts +17 -5
- package/src/cli/github/github.ts +4 -4
- package/src/cli/github/semantic-projection.ts +174 -0
- package/src/cli/hook/prompt-intents.ts +0 -9
- package/src/cli/index.ts +1 -43
- package/src/cli/local-bridge/connector-freshness.ts +1 -12
- package/src/cli/local-bridge/console-automations.ts +1 -1
- package/src/cli/local-bridge/console-view-models.ts +1 -1
- package/src/cli/local-bridge/facade-api.ts +0 -2
- package/src/cli/repositories/command-classifier.ts +0 -9
- package/src/cli/repositories/command-scope.ts +2 -2
- package/src/cli/repositories/locks.ts +20 -0
- package/src/cli/repositories/registry.ts +2 -1
- package/src/cli/repositories/safe-patch.ts +2 -12
- package/src/cli/repositories/structured-git.ts +39 -3
- package/src/cli/repository/inspector.ts +185 -343
- package/src/effects/process-runner.ts +17 -1
- package/src/runtime/context/generic-lsp-provider.ts +1 -1
- package/src/runtime/context/semantic-navigation-contract.ts +7 -0
- package/src/runtime/context/semantic-navigation.ts +1 -1
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +4 -4
- package/src/runtime/control-plane/execution/work-delivery-service.ts +150 -31
- package/src/runtime/control-plane/execution/work-handle-authority.ts +12 -4
- package/src/runtime/control-plane/execution/work-handle-store.ts +53 -1
- package/src/runtime/control-plane/execution/work-operation-service.ts +20 -10
- package/src/runtime/control-plane/execution/work-preparation-service.ts +108 -4
- package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +28 -13
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +5 -1
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +1 -1
- package/src/runtime/control-plane/facade/capability-registry.ts +2 -0
- package/src/runtime/control-plane/facade/work-continuation.ts +1 -1
- package/src/runtime/control-plane/generated-cache-retention.ts +114 -63
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +20 -8
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -11
- package/src/runtime/control-plane/governance/external-effects.ts +1 -1
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +1 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +12 -93
- package/src/runtime/evidence/sensitive-output.ts +22 -2
- package/src/runtime/execution/jobs/child-reference.ts +11 -0
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
- package/src/runtime/execution/process-runtime/command-facade.ts +3 -3
- package/src/runtime/execution/process-runtime/runtime.ts +20 -16
- package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
- package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
- package/src/runtime/execution/workers/executor.ts +1 -2
- package/src/runtime/maintenance/cleanup.ts +41 -5
- package/src/runtime/plugins/action-application.ts +12 -1
- package/src/runtime/plugins/app-store-connect-adapter.ts +252 -4
- package/src/runtime/plugins/browser-adapter.ts +381 -313
- package/src/runtime/plugins/browser-macos-bridge.ts +114 -112
- package/src/runtime/plugins/browser-manifest-surface.ts +2 -1
- package/src/runtime/plugins/browser-registration.ts +2 -0
- package/src/runtime/plugins/browser-runtime.ts +26 -35
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +11 -2
- package/src/runtime/plugins/computer-registration.ts +76 -9
- package/src/runtime/plugins/github-adapter.ts +30 -3
- package/src/runtime/plugins/gmail-adapter.ts +166 -1
- package/src/runtime/plugins/google-shared.ts +10 -5
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
- package/src/runtime/plugins/ios-adapter.ts +45 -40
- package/src/runtime/plugins/ios-agent-device.ts +54 -4
- package/src/runtime/plugins/ios-device-ownership.ts +103 -0
- package/src/runtime/plugins/ios-physical-device.ts +497 -79
- package/src/runtime/plugins/local-system-adapter.ts +1 -99
- package/src/runtime/plugins/store.ts +80 -7
- package/src/runtime/plugins/types.ts +4 -0
- package/src/runtime/projections/git-status-sampler.ts +2 -19
- package/src/runtime/recovery/actions.ts +1 -1
- package/src/runtime/recovery/lifecycle-inventory.ts +1 -11
- package/src/runtime/recovery/probe.ts +2 -2
- package/src/runtime/release/release-coordinator.ts +20 -4
- package/src/runtime/root/controller-progression-composition.ts +67 -5
- package/src/runtime/root/package-connector-service.ts +123 -11
- package/src/runtime/root/package-runtime-release.ts +6 -18
- package/src/runtime/root/package-runtime-service.ts +7 -13
- package/src/runtime/root/release-execution-canary.ts +18 -5
- package/src/runtime/root/release-manifest.ts +0 -34
- package/src/runtime/root/release-materialize.ts +0 -78
- package/src/runtime/root/runtime.ts +1 -2
- package/src/runtime/root/scheduled-controller-composition.ts +2 -1
- package/src/runtime/root/service.ts +90 -11
- package/src/runtime/root/types.ts +0 -6
- package/src/runtime/root/workflow-supervisor-composition.ts +207 -19
- package/src/runtime/root/workflow-supervisor-runtime.ts +365 -10
- package/src/runtime/safe-tooling/google-credential-store.ts +1 -11
- package/src/runtime/safe-tooling/ios-development.ts +22 -8
- package/src/runtime/safe-tooling/job-summary.ts +1 -1
- package/src/runtime/safe-tooling/workspace-auth.ts +4 -1
- package/src/runtime/shared/atomic-text-file.ts +21 -0
- package/src/runtime/shared/json-files.ts +8 -3
- package/src/runtime/standalone-recovery/core.ts +189 -174
- package/src/runtime/standalone-recovery/entry.ts +62 -30
- package/src/runtime/standalone-recovery/operation-lock.ts +11 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -0
- package/src/runtime/workflow/schedules/engine.ts +36 -14
- package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +51 -6
- package/supervisor/client.ts +31 -14
- package/supervisor/control-plane.ts +210 -115
- package/supervisor/forge-validators.ts +88 -21
- package/supervisor/native-browser-adapter.ts +393 -28
- package/supervisor/native-messaging/host.ts +21 -2
- package/supervisor/protocol.ts +74 -26
- package/supervisor/server.ts +16 -8
- package/supervisor/store.ts +476 -57
- package/supervisor/types.ts +19 -13
- package/SKILL.md +0 -427
- package/adapters/computer/chatgpt-target-router.ts +0 -103
- package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +0 -87
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
- package/adapters/mcp/toolset-selection.ts +0 -41
- package/agents/openai.yaml +0 -4
- package/assets/skill-hooks.json +0 -41
- package/assets/skill-version.json +0 -189
- package/assets/skills/claude-review/SKILL.md +0 -231
- package/assets/skills/codex-review/SKILL.md +0 -103
- package/assets/templates/helpers/check-skill-version.ts +0 -193
- package/scripts/check-skill-version.ts +0 -5
- package/scripts/ensure-codegraph.sh +0 -16
- package/scripts/run-skill-hook.ts +0 -244
- package/scripts/sync-codex-installed-copies.sh +0 -182
- package/src/cli/commands/tools.ts +0 -167
- package/src/cli/mcp/access-mode.ts +0 -2
- package/src/cli/mcp/access-tools.ts +0 -2
- package/src/cli/mcp/audit.ts +0 -2
- package/src/cli/mcp/auth.ts +0 -2
- package/src/cli/mcp/instructions.ts +0 -2
- package/src/cli/mcp/legacy-context.ts +0 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -2
- package/src/cli/mcp/multi-repository.ts +0 -2
- package/src/cli/mcp/oauth.ts +0 -2
- package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
- package/src/cli/mcp/paths.ts +0 -2
- package/src/cli/mcp/policy.ts +0 -2
- package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
- package/src/cli/mcp/proxy-env.ts +0 -2
- package/src/cli/mcp/readiness-projection.ts +0 -2
- package/src/cli/mcp/redaction.ts +0 -2
- package/src/cli/mcp/repo.ts +0 -2
- package/src/cli/mcp/repository-tools.ts +0 -2
- package/src/cli/mcp/server.ts +0 -2
- package/src/cli/mcp/setup.ts +0 -2
- package/src/cli/mcp/tool-capability-cache.ts +0 -2
- package/src/cli/mcp/tool-contract.ts +0 -2
- package/src/cli/mcp/tools.ts +0 -2
- package/src/cli/mcp/toolset-names.ts +0 -2
- package/src/cli/mcp/toolset-selection.ts +0 -2
- package/src/cli/mcp/toolset.ts +0 -2
- package/src/cli/mcp/transports/http.ts +0 -2
- package/src/cli/mcp/transports/session-registry.ts +0 -2
- package/src/cli/mcp/transports/stdio.ts +0 -2
- package/src/cli/mcp/types.ts +0 -2
- package/src/cli/mcp/umbrella-tools.ts +0 -2
- package/src/cli/tools/codegraph.ts +0 -695
- package/src/core/adoption/helper-wrapper-plan.ts +0 -159
- package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
- package/src/runtime/context/codegraph-read-provider.ts +0 -611
- package/src/runtime/context/codegraph-sidecar.cjs +0 -248
- package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
- package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
- package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
- package/src/runtime/gateway/mcp/process-tools.ts +0 -2
- package/src/runtime/gateway/mcp/router.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
- package/src/runtime/plugins/browser-provider-registry.ts +0 -2
- package/src/runtime/plugins/computer-chatgpt-macos-target.ts +0 -891
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
|
|
2
|
+
import { tmpdir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { listRepositories } from '../repositories/registry';
|
|
5
|
+
import { resolveRepoPreferredControllerHome } from '../repositories/controller-home';
|
|
6
|
+
import { getWorkContract, listWorkContracts, type WorkContract } from '../../../packages/kernel/work/api/index';
|
|
7
|
+
import { getPlanContract, planSemanticView } from '../../runtime/control-plane/facade/plan-contract-store';
|
|
8
|
+
import { readRequirement } from '../../runtime/control-plane/persistence/requirement-store';
|
|
9
|
+
import type { GitHubIssueLink } from '../controller/types';
|
|
10
|
+
import { gh, createGitHubIssue, updateGitHubIssue, addProjectItem, resolveGitHubRepository } from './github';
|
|
11
|
+
|
|
12
|
+
interface RemoteIssue { number: number; url: string; title: string; body: string }
|
|
13
|
+
export interface SemanticReleaseProjectionInput {
|
|
14
|
+
releaseWorkId: string;
|
|
15
|
+
repository?: string;
|
|
16
|
+
projectOwner?: string;
|
|
17
|
+
projectNumber?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface SemanticReleaseProjectionResult {
|
|
20
|
+
releaseIssue: string;
|
|
21
|
+
children: Array<{ workId: string; issue: string }>;
|
|
22
|
+
created: number;
|
|
23
|
+
updated: number;
|
|
24
|
+
unchanged: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function safeIdentity(value: string): string {
|
|
28
|
+
if (!/^[A-Za-z0-9_.:-]{1,200}$/.test(value)) throw new Error('GITHUB_SEMANTIC_ID_INVALID');
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function marker(workId: string): string {
|
|
33
|
+
return '<!-- forge-semantic-work:' + safeIdentity(workId) + ' -->';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function lines(values: readonly string[] | undefined, prefix = '- '): string {
|
|
37
|
+
return values?.length ? values.map((line) => prefix + line).join('\n') : '- 尚无记录';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function workDisplayTitle(work: WorkContract, home: string): string {
|
|
41
|
+
const requirement = work.requirementId
|
|
42
|
+
? readRequirement({ controllerHome: home }, work.requirementId)?.value
|
|
43
|
+
: undefined;
|
|
44
|
+
return (requirement?.title || work.objective).replace(/\s+/g, ' ').slice(0, 96);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function semanticWorkText(work: WorkContract, home: string): string {
|
|
48
|
+
const requirement = work.requirementId
|
|
49
|
+
? readRequirement({ controllerHome: home }, work.requirementId)?.value
|
|
50
|
+
: undefined;
|
|
51
|
+
const plan = work.planId
|
|
52
|
+
? getPlanContract({ controllerHome: home, repoId: work.repoId }, work.planId)
|
|
53
|
+
: undefined;
|
|
54
|
+
const planView = plan ? planSemanticView(plan) : undefined;
|
|
55
|
+
const text = [
|
|
56
|
+
marker(work.workId),
|
|
57
|
+
'> Forge 生成视图(单向投影)。Requirement / Plan / Work 的正式状态只由 Forge 持有。GitHub 上的勾选或评论不改变执行授权。',
|
|
58
|
+
'',
|
|
59
|
+
'## 工作目标', work.objective,
|
|
60
|
+
'',
|
|
61
|
+
'## Forge 当前事实',
|
|
62
|
+
'- Work: `' + work.workId + '`',
|
|
63
|
+
'- 语义状态: `' + work.semanticState + '`',
|
|
64
|
+
'- Requirement: `' + (work.requirementId || '未绑定') + '`',
|
|
65
|
+
'- Plan: `' + (work.planId || '未绑定') + '`',
|
|
66
|
+
'- 父 Work: `' + (work.semanticParentWorkId || '无') + '`',
|
|
67
|
+
'- 最近语义更新: ' + (work.semanticUpdatedAt || work.updatedAt),
|
|
68
|
+
'',
|
|
69
|
+
'## 已选择的交付结果引用',
|
|
70
|
+
lines(work.semanticResultRefs),
|
|
71
|
+
];
|
|
72
|
+
if (requirement) {
|
|
73
|
+
text.push('', '## 原始需求:' + requirement.title, requirement.outcomeStatement, '',
|
|
74
|
+
'### 验收标准', lines(requirement.acceptanceCriteria));
|
|
75
|
+
if (requirement.needsAttention && requirement.attentionSummary) {
|
|
76
|
+
text.push('', '### Forge 记录的需关注事项', requirement.attentionSummary);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (planView) {
|
|
80
|
+
text.push('', '## 当前方案', planView.goal, '',
|
|
81
|
+
'### 方案决定', lines(planView.resolvedDecisions), '',
|
|
82
|
+
'### 方案项(描述性工作记忆,不是自动审批状态)',
|
|
83
|
+
lines(planView.items.map((item) => item.id + ' — ' + item.objective)));
|
|
84
|
+
}
|
|
85
|
+
text.push('', '> 未明确记录的用户产品审批、测试、发布均不能推断为通过。');
|
|
86
|
+
return text.join('\n') + '\n';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function syncGitHubSemanticRelease(
|
|
90
|
+
repoRoot: string,
|
|
91
|
+
input: SemanticReleaseProjectionInput,
|
|
92
|
+
): SemanticReleaseProjectionResult {
|
|
93
|
+
const home = resolveRepoPreferredControllerHome(repoRoot);
|
|
94
|
+
const repositoryRecord = listRepositories(home).find((record) =>
|
|
95
|
+
record.canonicalRoot === repoRoot || record.localRoot === repoRoot ||
|
|
96
|
+
record.checkouts.some((checkout) => checkout.canonicalRoot === repoRoot || checkout.localRoot === repoRoot));
|
|
97
|
+
if (!repositoryRecord) throw new Error('GITHUB_SEMANTIC_REPOSITORY_NOT_REGISTERED');
|
|
98
|
+
const repoId = repositoryRecord.repoId;
|
|
99
|
+
const store = { controllerHome: home, repoId };
|
|
100
|
+
const releaseWorkId = safeIdentity(input.releaseWorkId);
|
|
101
|
+
const parent = getWorkContract(store, releaseWorkId);
|
|
102
|
+
if (!parent || parent.lifecycleRole === 'execution_child') throw new Error('GITHUB_SEMANTIC_RELEASE_WORK_NOT_FOUND');
|
|
103
|
+
if (parent.semanticParentWorkId) throw new Error('GITHUB_SEMANTIC_RELEASE_MUST_BE_ROOT');
|
|
104
|
+
const all: WorkContract[] = [];
|
|
105
|
+
for (let offset = 0; offset < 10_000; offset += 100) {
|
|
106
|
+
const page = listWorkContracts({ ...store, state: 'all', limit: 100, offset });
|
|
107
|
+
all.push(...page);
|
|
108
|
+
if (page.length < 100) break;
|
|
109
|
+
if (offset + 100 >= 10_000) throw new Error('GITHUB_SEMANTIC_WORK_INVENTORY_TOO_LARGE');
|
|
110
|
+
}
|
|
111
|
+
const children = all.filter((work) => work.semanticParentWorkId === releaseWorkId &&
|
|
112
|
+
work.lifecycleRole !== 'execution_child').sort((a, b) => a.workId.localeCompare(b.workId));
|
|
113
|
+
|
|
114
|
+
// The GitHub Issue is a projection; never import remote status back into Work.
|
|
115
|
+
if (Boolean(input.projectOwner) !== Boolean(input.projectNumber)) throw new Error('GITHUB_SEMANTIC_PROJECT_CONFIG_INCOMPLETE');
|
|
116
|
+
const repo = resolveGitHubRepository(repoRoot, input.repository);
|
|
117
|
+
const inventory = gh(repoRoot,
|
|
118
|
+
['issue', 'list', '--repo', repo.nameWithOwner, '--state', 'all', '--limit', '1000',
|
|
119
|
+
'--json', 'number,url,title,body'], undefined, 4 * 1024 * 1024);
|
|
120
|
+
if (!inventory.ok) throw new Error('GITHUB_SEMANTIC_ISSUE_INVENTORY_FAILED: ' + (inventory.error || inventory.stderr));
|
|
121
|
+
const remote = JSON.parse(inventory.stdout) as RemoteIssue[];
|
|
122
|
+
if (!Array.isArray(remote) || remote.length === 1000) {
|
|
123
|
+
throw new Error('GITHUB_SEMANTIC_ISSUE_INVENTORY_UNBOUNDED');
|
|
124
|
+
}
|
|
125
|
+
let created = 0;
|
|
126
|
+
let updated = 0;
|
|
127
|
+
let unchanged = 0;
|
|
128
|
+
const tmp = mkdtempSync(join(tmpdir(), 'forge-semantic-github-'));
|
|
129
|
+
function ensureIssue(workId: string, title: string, body: string): string {
|
|
130
|
+
const matches = remote.filter((issue) => issue.body?.includes(marker(workId)));
|
|
131
|
+
if (matches.length > 1) throw new Error('GITHUB_SEMANTIC_DUPLICATE_REMOTE_IDENTITY: ' + workId);
|
|
132
|
+
const file = join(tmp, 'issue.md');
|
|
133
|
+
writeFileSync(file, body, 'utf8');
|
|
134
|
+
let link: GitHubIssueLink;
|
|
135
|
+
if (matches.length === 1) {
|
|
136
|
+
const current = matches[0]!;
|
|
137
|
+
link = { owner: repo.owner, repo: repo.repo, number: current.number, url: current.url, syncedAt: '' };
|
|
138
|
+
if (current.title === title && current.body === body) unchanged++;
|
|
139
|
+
else { link = updateGitHubIssue(repoRoot, repo, link, title, file); updated++; }
|
|
140
|
+
} else {
|
|
141
|
+
link = createGitHubIssue(repoRoot, repo, title, file, []);
|
|
142
|
+
remote.push({ number: link.number, title, body, url: link.url });
|
|
143
|
+
created++;
|
|
144
|
+
}
|
|
145
|
+
if (input.projectOwner && input.projectNumber) {
|
|
146
|
+
addProjectItem(repoRoot, link, input.projectOwner, input.projectNumber);
|
|
147
|
+
}
|
|
148
|
+
return link.url;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const linked = children.map((work) => ({
|
|
152
|
+
workId: work.workId,
|
|
153
|
+
title: workDisplayTitle(work, home),
|
|
154
|
+
issue: ensureIssue(work.workId, workDisplayTitle(work, home),
|
|
155
|
+
semanticWorkText(work, home)),
|
|
156
|
+
}));
|
|
157
|
+
const counts = {
|
|
158
|
+
open: children.filter((work) => work.semanticState === 'open').length,
|
|
159
|
+
completed: children.filter((work) => work.semanticState === 'completed').length,
|
|
160
|
+
cancelled: children.filter((work) => work.semanticState === 'cancelled').length,
|
|
161
|
+
};
|
|
162
|
+
const releaseBody = semanticWorkText(parent, home) + '\n## 版本关联工作\n' +
|
|
163
|
+
(linked.length
|
|
164
|
+
? linked.map((child) => '- [' + child.title + '](' + child.issue + ') — \`' + child.workId + '\`').join('\n')
|
|
165
|
+
: '- 暂无已关联子 Work') +
|
|
166
|
+
'\n\n## 版本进度(真实 Work 事实,不是提交/发布证明)\n' +
|
|
167
|
+
'- 关联:' + linked.length + '\n- Open:' + counts.open +
|
|
168
|
+
'\n- Completed:' + counts.completed + '\n- Cancelled:' + counts.cancelled + '\n';
|
|
169
|
+
const releaseIssue = ensureIssue(releaseWorkId, workDisplayTitle(parent, home), releaseBody);
|
|
170
|
+
return { releaseIssue, children: linked, created, updated, unchanged };
|
|
171
|
+
} finally {
|
|
172
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -465,15 +465,6 @@ export function isAgenticPackagingIntent(ctx: PromptIntentContext): boolean {
|
|
|
465
465
|
return AGENTIC_PACKAGING.test(ctx.text);
|
|
466
466
|
}
|
|
467
467
|
|
|
468
|
-
const CODEGRAPH_ROUTE = re(
|
|
469
|
-
String.raw`(who calls|what calls|callers|callees|impact|impact radius|trace\s+(flow|path|call)|where\s.*(defined|definition)|definition of|symbol named|调用关系|谁调用|调用了谁|哪里定义|定义在哪|影响面|调用链|追踪(路径|调用|链)|从.*到.*怎么走)`,
|
|
470
|
-
);
|
|
471
|
-
|
|
472
|
-
export function isCodegraphRouteIntent(ctx: PromptIntentContext): boolean {
|
|
473
|
-
if (isTriggerQuestionPrompt(ctx)) return false;
|
|
474
|
-
return CODEGRAPH_ROUTE.test(ctx.text);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
468
|
const GIT_STATUS_LINE = re(
|
|
478
469
|
String.raw`^${SP}*(git\s+(status|log|show|diff|push|pull|commit)|status|commit|push|merge|提交|推送|合并|看状态|看看状态)(${SP}|$)`,
|
|
479
470
|
);
|
package/src/cli/index.ts
CHANGED
|
@@ -15,7 +15,6 @@ import { CLI_VERSION, formatStatus, runStatus } from './commands/status';
|
|
|
15
15
|
import { formatDoctor, runDoctor } from './commands/doctor';
|
|
16
16
|
import { buildSetupCommand, formatInitHook, runInitHook } from './commands/init-hook';
|
|
17
17
|
import { formatMigratePlan, runMigrate } from './commands/migrate';
|
|
18
|
-
import { buildToolsCommand } from './commands/tools';
|
|
19
18
|
import { buildPluginCommand } from './commands/plugin';
|
|
20
19
|
import { buildComputerCommand } from './commands/computer';
|
|
21
20
|
import { buildBrainCommand } from './commands/brain';
|
|
@@ -73,10 +72,7 @@ const VALID_LOCATIONS: readonly Location[] = ['global', 'local'];
|
|
|
73
72
|
interface GlobalRuntimeCommandOptions {
|
|
74
73
|
target: string;
|
|
75
74
|
cli?: boolean;
|
|
76
|
-
syncSkill?: boolean;
|
|
77
75
|
hooks?: string | false;
|
|
78
|
-
withExternalSkills?: boolean;
|
|
79
|
-
codegraph?: boolean;
|
|
80
76
|
brainRoot?: string;
|
|
81
77
|
json?: boolean;
|
|
82
78
|
}
|
|
@@ -91,10 +87,7 @@ function runGlobalRuntimeBootstrap(commandName: 'init' | 'install', rawOpts: Glo
|
|
|
91
87
|
const result = runGlobalRuntimeSetup({
|
|
92
88
|
target: rawOpts.target as InstallTargetSpec,
|
|
93
89
|
installCli: rawOpts.cli !== false,
|
|
94
|
-
syncSkill: rawOpts.syncSkill !== false,
|
|
95
90
|
hostAdapters: rawOpts.hooks !== false,
|
|
96
|
-
externalSkills: rawOpts.withExternalSkills === true,
|
|
97
|
-
codegraph: rawOpts.codegraph !== false,
|
|
98
91
|
brainRoot: rawOpts.brainRoot,
|
|
99
92
|
});
|
|
100
93
|
if (rawOpts.json === true) {
|
|
@@ -117,19 +110,14 @@ export function buildProgram(): Command {
|
|
|
117
110
|
program
|
|
118
111
|
.command('update')
|
|
119
112
|
.description('Update the global Forge CLI and user-level managed runtime')
|
|
120
|
-
.option('--target <target>', `Host target for adapters
|
|
113
|
+
.option('--target <target>', `Host target for adapters: ${VALID_TARGETS.join('|')}`, 'both')
|
|
121
114
|
.option('--version <version>', 'Install a specific Forge package version')
|
|
122
115
|
.option('--channel <channel>', 'Install package channel: latest|next')
|
|
123
116
|
.option('--check', 'Run the read-only setup check without refreshing runtime')
|
|
124
117
|
.option('--check-updates', 'Include network-backed version update advisories in setup check output')
|
|
125
118
|
.option('--no-runtime-refresh', 'Skip runtime refresh and run the read-only setup check only')
|
|
126
119
|
.option('--no-cli', 'Skip installing the Forge CLI globally')
|
|
127
|
-
.option('--no-sync-skill', 'Skip refreshing Forge host skill aliases')
|
|
128
120
|
.option('--no-hooks', 'Skip global hook adapter installation')
|
|
129
|
-
.option('--with-external-skills', 'Also bootstrap third-party Waza, Mermaid, and cross-review skills')
|
|
130
|
-
.option('--no-external-skills', 'Compatibility no-op; update no longer bootstraps third-party skills by default')
|
|
131
|
-
.option('--configure-codegraph', 'Also configure CodeGraph CLI/MCP during runtime refresh')
|
|
132
|
-
.option('--no-codegraph', 'Compatibility no-op; update no longer configures CodeGraph by default')
|
|
133
121
|
.option('--brain-root <path>', 'Brain vault root for manifest sync')
|
|
134
122
|
.option('--repo <path>', 'Deprecated: use forge adopt --repo <path>')
|
|
135
123
|
.option('--dry-run', 'Deprecated: use forge adopt --dry-run for repo-level planning')
|
|
@@ -145,11 +133,7 @@ export function buildProgram(): Command {
|
|
|
145
133
|
checkUpdates?: boolean;
|
|
146
134
|
runtimeRefresh?: boolean;
|
|
147
135
|
cli?: boolean;
|
|
148
|
-
syncSkill?: boolean;
|
|
149
136
|
hooks?: string | false;
|
|
150
|
-
withExternalSkills?: boolean;
|
|
151
|
-
codegraph?: boolean;
|
|
152
|
-
configureCodegraph?: boolean;
|
|
153
137
|
brainRoot?: string;
|
|
154
138
|
interactive?: boolean;
|
|
155
139
|
json?: boolean;
|
|
@@ -187,10 +171,7 @@ export function buildProgram(): Command {
|
|
|
187
171
|
target: rawOpts.target as InstallTargetSpec,
|
|
188
172
|
installCli: rawOpts.cli !== false,
|
|
189
173
|
installSpec,
|
|
190
|
-
syncSkill: rawOpts.syncSkill !== false,
|
|
191
174
|
hostAdapters: rawOpts.hooks !== false,
|
|
192
|
-
externalSkills: rawOpts.withExternalSkills === true,
|
|
193
|
-
codegraph: rawOpts.configureCodegraph === true,
|
|
194
175
|
brainRoot: rawOpts.brainRoot,
|
|
195
176
|
});
|
|
196
177
|
if (rawOpts.json === true) {
|
|
@@ -210,16 +191,11 @@ export function buildProgram(): Command {
|
|
|
210
191
|
.option('--transaction <path>', 'Adoption transaction manifest to restore when action is rollback')
|
|
211
192
|
.option('--dry-run', 'Plan Forge changes without applying them')
|
|
212
193
|
.option('--target <target>', `Host target for readiness checks and optional global bootstrap: ${VALID_TARGETS.join('|')}`, 'both')
|
|
213
|
-
.option('--no-sync-skill', 'Compatibility no-op; adopt never refreshes user-level skill aliases')
|
|
214
194
|
.option('--no-host-adapters', 'Compatibility no-op; adopt never writes global Codex/Claude hook adapters')
|
|
215
|
-
.option('--no-external-skills', 'Compatibility no-op; adopt never bootstraps user-level external skills')
|
|
216
195
|
.option('--no-verify', 'Skip repo workflow verification after apply')
|
|
217
|
-
.option('--no-codegraph', 'Skip building the CodeGraph index and MCP readiness check')
|
|
218
196
|
.option('--reclaim-runtime', 'Reclaim generated repo-local hook/helper runtime copies after replacement paths verify')
|
|
219
197
|
.option('--compact', 'Compact repo surface; includes --reclaim-runtime plus package script rewrite')
|
|
220
198
|
.option('--mode <mode>', 'Adoption mode: minimal|standard|self-host', 'standard')
|
|
221
|
-
.option('--configure-codegraph', 'Deprecated: user-level MCP config belongs to Forge update/setup')
|
|
222
|
-
.option('--sync-codegraph', 'Sync the CodeGraph index after ensure')
|
|
223
199
|
.option('--brain-root <path>', 'Deprecated: user-level brain config belongs to Forge update/setup')
|
|
224
200
|
.option('--brain-mode <mode>', 'Deprecated: adopt does not perform user-level brain sync', 'skip')
|
|
225
201
|
.option('--interactive', 'Run the numbered interactive install planner')
|
|
@@ -231,16 +207,11 @@ export function buildProgram(): Command {
|
|
|
231
207
|
transaction?: string;
|
|
232
208
|
dryRun?: boolean;
|
|
233
209
|
target: string;
|
|
234
|
-
syncSkill?: boolean;
|
|
235
210
|
hostAdapters?: boolean;
|
|
236
|
-
withExternalSkills?: boolean;
|
|
237
211
|
verify?: boolean;
|
|
238
|
-
codegraph?: boolean;
|
|
239
212
|
reclaimRuntime?: boolean;
|
|
240
213
|
compact?: boolean;
|
|
241
214
|
mode?: string;
|
|
242
|
-
configureCodegraph?: boolean;
|
|
243
|
-
syncCodegraph?: boolean;
|
|
244
215
|
brainRoot?: string;
|
|
245
216
|
brainMode?: string;
|
|
246
217
|
interactive?: boolean;
|
|
@@ -294,10 +265,6 @@ export function buildProgram(): Command {
|
|
|
294
265
|
console.error('forge adopt: invalid --mode (expected: minimal, standard, self-host)');
|
|
295
266
|
process.exit(2);
|
|
296
267
|
}
|
|
297
|
-
if (rawOpts.configureCodegraph === true) {
|
|
298
|
-
console.error('forge adopt: --configure-codegraph writes user-level MCP config; run forge update instead');
|
|
299
|
-
process.exit(2);
|
|
300
|
-
}
|
|
301
268
|
if (rawOpts.brainRoot || rawOpts.brainMode !== 'skip') {
|
|
302
269
|
console.error('forge adopt: brain configuration writes user-level state; run forge update instead');
|
|
303
270
|
process.exit(2);
|
|
@@ -347,13 +314,8 @@ export function buildProgram(): Command {
|
|
|
347
314
|
repo: rawOpts.repo,
|
|
348
315
|
apply: rawOpts.dryRun !== true,
|
|
349
316
|
target: rawOpts.target as InstallTargetSpec,
|
|
350
|
-
syncSkill: false,
|
|
351
317
|
hostAdapters: false,
|
|
352
|
-
externalSkills: false,
|
|
353
318
|
verify: rawOpts.verify !== false,
|
|
354
|
-
codegraph: rawOpts.codegraph !== false,
|
|
355
|
-
configureCodegraphMcp: false,
|
|
356
|
-
syncCodegraph: rawOpts.syncCodegraph === true,
|
|
357
319
|
brainRoot: rawOpts.brainRoot,
|
|
358
320
|
brainMode: rawOpts.brainMode as InitBrainMode,
|
|
359
321
|
};
|
|
@@ -392,10 +354,7 @@ export function buildProgram(): Command {
|
|
|
392
354
|
.option('--target <target>', `Target host: ${VALID_TARGETS.join('|')}`, 'both')
|
|
393
355
|
.option('--location <location>', `Adapter-only install location: ${VALID_LOCATIONS.join('|')}`)
|
|
394
356
|
.option('--no-cli', 'Skip installing the Forge CLI globally')
|
|
395
|
-
.option('--no-sync-skill', 'Skip refreshing Forge host skill aliases')
|
|
396
357
|
.option('--no-hooks', 'Skip global hook adapter installation during full runtime install')
|
|
397
|
-
.option('--with-external-skills', 'Also install optional third-party Waza/Mermaid skills and Forge cross-review helpers')
|
|
398
|
-
.option('--no-codegraph', 'Skip CodeGraph CLI/MCP configuration')
|
|
399
358
|
.option('--brain-root <path>', 'Brain vault root to persist for Forge brain commands')
|
|
400
359
|
.option('--json', 'Output JSON instead of human-readable text')
|
|
401
360
|
.action((rawOpts: GlobalRuntimeCommandOptions & { location?: string }) => {
|
|
@@ -509,7 +468,6 @@ export function buildProgram(): Command {
|
|
|
509
468
|
process.exit(rawOpts.strict === true && strictFailure ? 1 : 0);
|
|
510
469
|
});
|
|
511
470
|
|
|
512
|
-
program.addCommand(buildToolsCommand());
|
|
513
471
|
program.addCommand(buildPluginCommand());
|
|
514
472
|
program.addCommand(buildComputerCommand());
|
|
515
473
|
program.addCommand(buildBrainCommand(), { hidden: true });
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
loadMcpLocalConfig,
|
|
20
20
|
loadMcpServiceLocalConfig,
|
|
21
21
|
} from '../../../adapters/mcp/auth';
|
|
22
|
-
import { PREFERRED_FACADE_TOOL_NAMES
|
|
22
|
+
import { PREFERRED_FACADE_TOOL_NAMES } from '../../../adapters/mcp/toolset-names';
|
|
23
23
|
import { resolveRepoPreferredControllerHome } from '../repositories/controller-home';
|
|
24
24
|
import type { PlainStatusTone } from './console-view-models';
|
|
25
25
|
|
|
@@ -37,8 +37,6 @@ export const OPTIONAL_INTERACTIVE_DEVELOPMENT_TOOLS = [
|
|
|
37
37
|
'process_wait',
|
|
38
38
|
] as const;
|
|
39
39
|
|
|
40
|
-
/** @deprecated Prefer ADVANCED_CONTROLLER_TOOL_NAMES; kept for import stability. */
|
|
41
|
-
export const CORE_SURFACE_INTERACTIVE_TOOLS = OPTIONAL_INTERACTIVE_DEVELOPMENT_TOOLS;
|
|
42
40
|
|
|
43
41
|
export type ConnectorFreshnessStatus =
|
|
44
42
|
| 'local_mcp_updated'
|
|
@@ -479,15 +477,6 @@ function finalize(
|
|
|
479
477
|
return report;
|
|
480
478
|
}
|
|
481
479
|
|
|
482
|
-
/**
|
|
483
|
-
* Build local tool names from the in-process controller registry (not ChatGPT).
|
|
484
|
-
* Uses expectedTools when provided; otherwise falls back to the stable default exposure.
|
|
485
|
-
*/
|
|
486
|
-
export function localControllerToolNames(expectedTools?: readonly string[]): string[] {
|
|
487
|
-
if (expectedTools && expectedTools.length > 0) return uniqueSorted(expectedTools);
|
|
488
|
-
return uniqueSorted([...DEFAULT_CONTROLLER_TOOL_NAMES]);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
480
|
export function buildLocalConnectorStatus(input: {
|
|
492
481
|
expectedTools: readonly string[];
|
|
493
482
|
connectorToolNames?: readonly string[] | null;
|
|
@@ -138,7 +138,7 @@ function occurrenceHistory(occurrences: ScheduleOccurrence[]): ConsoleAutomation
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
function scheduleMode(schedule: RepositorySchedule): ConsoleAutomationMode {
|
|
141
|
-
if (schedule.action.operation === 'external_controller_wake') return 'continuation';
|
|
141
|
+
if (schedule.action.operation === 'external_controller_wake' || schedule.action.operation === 'controller_progression') return 'continuation';
|
|
142
142
|
if (schedule.action.operation === 'browser_probe') return schedule.action.arguments?.keepalive_only === true ? 'browser_keepalive' : 'browser_watch';
|
|
143
143
|
return 'schedule';
|
|
144
144
|
}
|
|
@@ -243,7 +243,7 @@ export interface AccessStateViewModel {
|
|
|
243
243
|
configuredAccessModeLabel: string;
|
|
244
244
|
effectiveAccessMode: 'request' | 'full_access';
|
|
245
245
|
effectiveAccessModeLabel: string;
|
|
246
|
-
effectiveToolset: '
|
|
246
|
+
effectiveToolset: 'full';
|
|
247
247
|
exposureRevision: number;
|
|
248
248
|
lastAppliedAt?: string;
|
|
249
249
|
source: string;
|
|
@@ -598,8 +598,6 @@ function controllerAccessStateView(ctx: ConsoleFacadeContext): AccessStateViewMo
|
|
|
598
598
|
const effective = resolveControllerAccessState({
|
|
599
599
|
controllerHome: ctx.controllerHome,
|
|
600
600
|
repoRoot: ctx.repository.canonicalRoot,
|
|
601
|
-
toolsetOverride: toolCtx.toolset,
|
|
602
|
-
toolsetLocked: toolCtx.toolsetLocked,
|
|
603
601
|
});
|
|
604
602
|
const repositoryPolicy = readRepositoryAccessPolicy(ctx.controllerHome, ctx.repository.repoId);
|
|
605
603
|
const runtimeGeneration = readRuntimeGeneration(ctx.controllerHome);
|
|
@@ -255,11 +255,6 @@ function readOnlyGitSegment(words: string[], _segment: string): boolean {
|
|
|
255
255
|
return isReadOnlyGitCommand(words);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
function isReadOnlyCodegraphCommand(words: string[]): boolean {
|
|
259
|
-
const program = words[0]?.split(/[\\/]/).at(-1)?.toLowerCase();
|
|
260
|
-
return program === 'codegraph' && words[1]?.toLowerCase() === 'status';
|
|
261
|
-
}
|
|
262
|
-
|
|
263
258
|
function githubCliApiMethod(words: string[]): string | undefined {
|
|
264
259
|
for (let index = 2; index < words.length; index += 1) {
|
|
265
260
|
const word = words[index]?.toLowerCase();
|
|
@@ -315,7 +310,6 @@ function isReadOnlySegment(segment: string): boolean {
|
|
|
315
310
|
const program = words[0]?.toLowerCase();
|
|
316
311
|
if (!program) return false;
|
|
317
312
|
if (program === 'git') return readOnlyGitSegment(words, segment);
|
|
318
|
-
if (isReadOnlyCodegraphCommand(words)) return true;
|
|
319
313
|
if (program === 'curl') return isReadOnlyLoopbackCurl(words);
|
|
320
314
|
if (program === 'sqlite3') return isSafeReadOnlySqliteSegment(segment);
|
|
321
315
|
if (program === 'find') return !/(?:-delete|-exec|-execdir|-ok|-okdir)\b/.test(segment);
|
|
@@ -621,9 +615,6 @@ function classifyArgvCommand(
|
|
|
621
615
|
if (program === 'git' && isReadOnlyGitCommand(argv)) {
|
|
622
616
|
return { risk: 'readonly', confirmation: 'none', reasons: ['the argv command is a recognized repository-local read operation'] };
|
|
623
617
|
}
|
|
624
|
-
if (isReadOnlyCodegraphCommand(argv)) {
|
|
625
|
-
return { risk: 'readonly', confirmation: 'none', reasons: ['CodeGraph status only observes the local semantic index'] };
|
|
626
|
-
}
|
|
627
618
|
if (program === 'curl' && isReadOnlyLoopbackCurl(argv)) {
|
|
628
619
|
return { risk: 'readonly', confirmation: 'none', reasons: ['curl performs a read-only loopback HTTP observation without writing response data to disk'] };
|
|
629
620
|
}
|
|
@@ -184,13 +184,13 @@ function assertNoUnmanagedControllerWorktreeLifecycle(command: CanonicalReposito
|
|
|
184
184
|
};
|
|
185
185
|
if (command.kind === 'argv') {
|
|
186
186
|
if (isGitWorktreeAdd([command.executable!, ...(command.args ?? [])])) {
|
|
187
|
-
throw new Error('MANAGED_WORKSPACE_REQUIRED:
|
|
187
|
+
throw new Error('MANAGED_WORKSPACE_REQUIRED: execute_command must not create an unmanaged temporary git worktree; use rh_work so Forge persists checkout/worktree ownership and terminal cleanup authority');
|
|
188
188
|
}
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
191
191
|
for (const segment of shellSegments(command.shellCommand ?? '')) {
|
|
192
192
|
if (isGitWorktreeAdd(shellWords(segment))) {
|
|
193
|
-
throw new Error('MANAGED_WORKSPACE_REQUIRED:
|
|
193
|
+
throw new Error('MANAGED_WORKSPACE_REQUIRED: execute_command must not create an unmanaged temporary git worktree; use rh_work so Forge persists checkout/worktree ownership and terminal cleanup authority');
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
}
|
|
@@ -150,6 +150,26 @@ export function readControllerLock(controllerHome: string, key: ControllerLockKe
|
|
|
150
150
|
return record;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
export function renewControllerLock(
|
|
154
|
+
controllerHome: string,
|
|
155
|
+
key: ControllerLockKey,
|
|
156
|
+
lockId: string,
|
|
157
|
+
ttlMs: number,
|
|
158
|
+
): ControllerLockRecord {
|
|
159
|
+
const current = readControllerLock(controllerHome, key);
|
|
160
|
+
if (!current) throw new Error(`LOCK_NOT_FOUND: ${lockId}`);
|
|
161
|
+
if (current.lockId !== lockId) throw new Error(`LOCK_OWNERSHIP_MISMATCH: ${current.lockId}`);
|
|
162
|
+
const normalizedTtlMs = Math.max(5_000, Math.trunc(ttlMs));
|
|
163
|
+
const heartbeatAt = new Date().toISOString();
|
|
164
|
+
const next: ControllerLockRecord = {
|
|
165
|
+
...current,
|
|
166
|
+
heartbeatAt,
|
|
167
|
+
expiresAt: new Date(Date.now() + normalizedTtlMs).toISOString(),
|
|
168
|
+
};
|
|
169
|
+
writeFileSync(current.path, `${JSON.stringify(next, null, 2)}\n`, 'utf-8');
|
|
170
|
+
return next;
|
|
171
|
+
}
|
|
172
|
+
|
|
153
173
|
function metric(key: ControllerLockKey, waitedMs: number, outcome: 'success' | 'contention' | 'timeout'): void {
|
|
154
174
|
recordGatewayLatency({
|
|
155
175
|
repoId: key.repoId,
|
|
@@ -93,7 +93,8 @@ function now(): string {
|
|
|
93
93
|
|
|
94
94
|
function comparablePath(value: string): string {
|
|
95
95
|
const resolved = resolve(value);
|
|
96
|
-
|
|
96
|
+
// macOS /tmp and /var are symlinks into /private; compare both aliases canonically.
|
|
97
|
+
return process.platform === 'darwin' && (/^\/(?:tmp|var)(?:\/|$)/.test(resolved))
|
|
97
98
|
? `/private${resolved}`
|
|
98
99
|
: resolved;
|
|
99
100
|
}
|
|
@@ -4,6 +4,7 @@ import { join, relative, resolve } from 'path';
|
|
|
4
4
|
import {
|
|
5
5
|
adoptEditSessionSuccessorHead,
|
|
6
6
|
applyEditOperations,
|
|
7
|
+
findOccurrence,
|
|
7
8
|
beginEditSession,
|
|
8
9
|
getEditSession,
|
|
9
10
|
type EditSession,
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
type EditSessionPatchError,
|
|
12
13
|
} from '../editing/edit-session';
|
|
13
14
|
import { normalizeEditOperation, type EditOperation } from '../editing/edit-operation-contract';
|
|
14
|
-
import { getMcpPolicy } from '
|
|
15
|
+
import { getMcpPolicy } from '../../../adapters/mcp/policy';
|
|
15
16
|
import type { RepositoryRecord } from './types';
|
|
16
17
|
import { ensureRepositoryRuntimeStorageBinding } from './runtime-storage';
|
|
17
18
|
|
|
@@ -189,17 +190,6 @@ function snippetAround(content: string, needle: string): SafePatchFailureContext
|
|
|
189
190
|
return { line: lineIndex + 1, before, focus, after };
|
|
190
191
|
}
|
|
191
192
|
|
|
192
|
-
function findOccurrence(value: string, anchor: string, occurrence: number): number {
|
|
193
|
-
let from = 0;
|
|
194
|
-
for (let current = 1; current <= occurrence; current += 1) {
|
|
195
|
-
const index = value.indexOf(anchor, from);
|
|
196
|
-
if (index < 0) return -1;
|
|
197
|
-
if (current === occurrence) return index;
|
|
198
|
-
from = index + anchor.length;
|
|
199
|
-
}
|
|
200
|
-
return -1;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
193
|
function applyTextPreview(operation: Exclude<EditOperation, { type: 'create' | 'write' | 'delete' }>, before: string): string {
|
|
204
194
|
if (operation.type === 'replace') {
|
|
205
195
|
let after = before;
|
|
@@ -154,7 +154,7 @@ export function resolveRepositoryGitCommitScope(
|
|
|
154
154
|
return { source: 'staged_index', paths: repositoryGitStagedPaths(repository) };
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
function
|
|
157
|
+
export function splitRepositoryGitStatus(porcelain: string): { staged: string[]; unstaged: string[]; untracked: string[] } {
|
|
158
158
|
const staged: string[] = [];
|
|
159
159
|
const unstaged: string[] = [];
|
|
160
160
|
const untracked: string[] = [];
|
|
@@ -174,7 +174,7 @@ function splitStatus(porcelain: string): { staged: string[]; unstaged: string[];
|
|
|
174
174
|
export function repositoryGitStatus(repository: RepositoryRecord): RepositoryGitStatusSnapshot {
|
|
175
175
|
const observedAt = new Date().toISOString();
|
|
176
176
|
const porcelain = runGit(repository, ['status', '--porcelain=v1', '--branch', '--untracked-files=all', '--', '.', ':(exclude).ai/harness/**']).stdout;
|
|
177
|
-
const split =
|
|
177
|
+
const split = splitRepositoryGitStatus(porcelain);
|
|
178
178
|
return {
|
|
179
179
|
repoId: repository.repoId,
|
|
180
180
|
checkoutId: repository.activeCheckoutId,
|
|
@@ -185,7 +185,9 @@ export function repositoryGitStatus(repository: RepositoryRecord): RepositoryGit
|
|
|
185
185
|
head: gitText(repository, ['rev-parse', '--verify', 'HEAD']),
|
|
186
186
|
upstream: gitText(repository, ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']),
|
|
187
187
|
porcelain,
|
|
188
|
-
|
|
188
|
+
// Git porcelain v1 with --branch uses the same short-status layout. Reuse
|
|
189
|
+
// the authoritative status snapshot instead of spawning Git a second time.
|
|
190
|
+
shortStatus: porcelain,
|
|
189
191
|
...split,
|
|
190
192
|
clean: split.staged.length === 0 && split.unstaged.length === 0 && split.untracked.length === 0,
|
|
191
193
|
};
|
|
@@ -214,6 +216,40 @@ export function repositoryGitDiff(repository: RepositoryRecord, input: { staged?
|
|
|
214
216
|
};
|
|
215
217
|
}
|
|
216
218
|
|
|
219
|
+
/**
|
|
220
|
+
* One Git-index/worktree candidate view for Work inspection and check selection.
|
|
221
|
+
* Untracked paths are explicit evidence; Git cannot provide their patch before add.
|
|
222
|
+
* Keep both index slices visible while bounding the aggregate patch size.
|
|
223
|
+
*/
|
|
224
|
+
export function repositoryGitWorkspaceChanges(
|
|
225
|
+
repository: RepositoryRecord,
|
|
226
|
+
maxBytes = 64 * 1024,
|
|
227
|
+
): {
|
|
228
|
+
status: RepositoryGitStatusSnapshot;
|
|
229
|
+
staged: RepositoryGitDiffResult;
|
|
230
|
+
unstaged: RepositoryGitDiffResult;
|
|
231
|
+
changedPaths: string[];
|
|
232
|
+
diff: { nameOnly: string[]; stat: string; patch: string; truncated: boolean };
|
|
233
|
+
} {
|
|
234
|
+
const status = repositoryGitStatus(repository);
|
|
235
|
+
const sliceBytes = Math.max(1024, Math.floor(boundedOutputBytes(maxBytes) / 2));
|
|
236
|
+
const staged = repositoryGitDiff(repository, { staged: true, maxBytes: sliceBytes });
|
|
237
|
+
const unstaged = repositoryGitDiff(repository, { maxBytes: sliceBytes });
|
|
238
|
+
const changedPaths = [...new Set([...staged.nameOnly, ...unstaged.nameOnly, ...status.untracked])].sort();
|
|
239
|
+
return {
|
|
240
|
+
status,
|
|
241
|
+
staged,
|
|
242
|
+
unstaged,
|
|
243
|
+
changedPaths,
|
|
244
|
+
diff: {
|
|
245
|
+
nameOnly: changedPaths,
|
|
246
|
+
stat: [staged.stat, unstaged.stat].filter(Boolean).join('\n'),
|
|
247
|
+
patch: [staged.patch, unstaged.patch].filter(Boolean).join('\n'),
|
|
248
|
+
truncated: staged.truncated || unstaged.truncated,
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
217
253
|
export function repositoryGitCreateBranch(controllerHome: string, repository: RepositoryRecord, input: { branch: unknown; startPoint?: unknown; switchTo?: unknown }): { branch: string; execution: RepositoryGitExecution } {
|
|
218
254
|
const branch = assertSafeBranchName(input.branch);
|
|
219
255
|
const startPoint = typeof input.startPoint === 'string' && input.startPoint.trim() ? input.startPoint.trim() : undefined;
|