@aiwg/cli 2026.10.5 → 2026.10.7
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/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
- package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
- package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
- package/agentic/code/providers/model-capabilities.v1.json +74 -50
- package/bin/aiwg.mjs +7 -3
- package/dist/src/a2a/agent-card.js +11 -0
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/browser-export.js +1 -0
- package/dist/src/artifacts/citation-parser.js +8 -1
- package/dist/src/artifacts/cli.js +57 -6
- package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
- package/dist/src/artifacts/fortemi-core-sync.js +26 -8
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
- package/dist/src/artifacts/fortemi-shard-export.js +284 -0
- package/dist/src/artifacts/graph-query.js +18 -8
- package/dist/src/artifacts/index-adapters.js +339 -0
- package/dist/src/artifacts/index-builder.js +143 -14
- package/dist/src/artifacts/index-files.js +15 -4
- package/dist/src/artifacts/index-preparation.js +2 -0
- package/dist/src/artifacts/index-reader.js +44 -0
- package/dist/src/artifacts/index-refresh.js +114 -55
- package/dist/src/artifacts/index-status.js +20 -5
- package/dist/src/artifacts/memory-maintenance.js +503 -0
- package/dist/src/artifacts/prebuilt-relations.js +3 -1
- package/dist/src/artifacts/query-engine.js +263 -108
- package/dist/src/artifacts/relations-builder.js +2 -2
- package/dist/src/artifacts/source-graph.js +2 -2
- package/dist/src/artifacts/types.js +9 -1
- package/dist/src/auth/client.js +5 -1
- package/dist/src/auth/credential-store.js +5 -1
- package/dist/src/auth/sensitive-input.js +151 -0
- package/dist/src/channel/manager.mjs +3 -0
- package/dist/src/cli/handlers/agentcard.js +47 -25
- package/dist/src/cli/handlers/auth.js +22 -2
- package/dist/src/cli/handlers/decision.js +22 -3
- package/dist/src/cli/handlers/effect.js +4 -84
- package/dist/src/cli/handlers/guided-setup.js +470 -0
- package/dist/src/cli/handlers/help.js +2 -2
- package/dist/src/cli/handlers/housekeeper.js +1110 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/init.js +41 -6
- package/dist/src/cli/handlers/install.js +6 -0
- package/dist/src/cli/handlers/local-executor.js +5 -3
- package/dist/src/cli/handlers/marketplace.js +15 -4
- package/dist/src/cli/handlers/mc.js +509 -53
- package/dist/src/cli/handlers/models.js +32 -2
- package/dist/src/cli/handlers/ralph-launcher.js +76 -4
- package/dist/src/cli/handlers/rules.js +1 -1
- package/dist/src/cli/handlers/script-runner.js +73 -2
- package/dist/src/cli/handlers/serve.js +5 -3
- package/dist/src/cli/handlers/sessions.js +1 -0
- package/dist/src/cli/handlers/setup.js +432 -1
- package/dist/src/cli/handlers/skill-lint.js +1 -1
- package/dist/src/cli/handlers/subcommands.js +118 -8
- package/dist/src/cli/handlers/use.js +335 -107
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/prompt-utils.js +81 -0
- package/dist/src/cli/router.js +16 -1
- package/dist/src/config/aiwg-config.js +42 -0
- package/dist/src/config/cli.js +7 -3
- package/dist/src/config/decision-config.js +14 -1
- package/dist/src/config/gitignore.js +4 -0
- package/dist/src/config/native-sandbox-warnings.js +25 -0
- package/dist/src/config/workspace.js +2 -1
- package/dist/src/contributors/validation.js +6 -5
- package/dist/src/decision/adapters/jev.js +20 -31
- package/dist/src/decision/admission.js +7 -3
- package/dist/src/decision/ask.js +6 -1
- package/dist/src/decision/benchmark.js +482 -0
- package/dist/src/decision/ledger.js +22 -3
- package/dist/src/decision/pricing.js +147 -0
- package/dist/src/decision/qualification/gate-evidence.js +23 -18
- package/dist/src/decision/qualification/load-staged.js +576 -0
- package/dist/src/decision/qualification/load.js +109 -33
- package/dist/src/decision/qualification/runner.js +12 -5
- package/dist/src/decision/review/service.js +6 -4
- package/dist/src/decision/review/validate.js +23 -2
- package/dist/src/decision/savings.js +8 -3
- package/dist/src/discovery/index.js +492 -0
- package/dist/src/discovery/interview.js +238 -0
- package/dist/src/discovery/plan-apply.js +567 -0
- package/dist/src/discovery/plan-store.js +171 -0
- package/dist/src/discovery/plan-types.js +2 -0
- package/dist/src/discovery/plan-validation.js +139 -0
- package/dist/src/discovery/types.js +2 -0
- package/dist/src/effects/host.js +91 -0
- package/dist/src/extensions/commands/definitions.js +49 -9
- package/dist/src/extensions/manifest.js +71 -44
- package/dist/src/extensions/project-local-discovery.js +1 -1
- package/dist/src/extensions/project-local-gitignore.js +2 -0
- package/dist/src/extensions/project-quickref.js +18 -17
- package/dist/src/extensions/validation.js +32 -29
- package/dist/src/housekeeper/daemon-config.js +148 -0
- package/dist/src/housekeeper/ipc.js +84 -0
- package/dist/src/housekeeper/lifecycle.js +148 -0
- package/dist/src/housekeeper/platform.js +35 -0
- package/dist/src/housekeeper/status.js +70 -0
- package/dist/src/housekeeper/units.js +114 -0
- package/dist/src/issues/cli.js +16 -5
- package/dist/src/issues/live.js +44 -12
- package/dist/src/jobs/flow.js +22 -21
- package/dist/src/lint/rule-activation.js +20 -5
- package/dist/src/lint/runner.js +1 -1
- package/dist/src/marketplace/exchange.js +18 -6
- package/dist/src/marketplace/provenance.js +3 -2
- package/dist/src/mcp/adapters/codex-runtime.js +46 -76
- package/dist/src/mcp/cli.mjs +22 -96
- package/dist/src/mcp/config-file.mjs +6 -2
- package/dist/src/mcp/config-layers.mjs +23 -7
- package/dist/src/mcp/grok-build-config.mjs +2 -1
- package/dist/src/mcp/muse-native-config.mjs +2 -1
- package/dist/src/mcp/profiles.js +18 -31
- package/dist/src/mcp/profiles.mjs +18 -32
- package/dist/src/mcp/registry.js +7 -27
- package/dist/src/mcp/registry.mjs +7 -28
- package/dist/src/mcp/server.mjs +34 -1
- package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
- package/dist/src/mcp/tools/command-run.mjs +3 -2
- package/dist/src/mcp/tools/decision.mjs +49 -48
- package/dist/src/mcp/tools/discovery.mjs +46 -21
- package/dist/src/mcp/tools/orchestration.mjs +22 -22
- package/dist/src/mcp/tools/subsystems.mjs +9 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +30 -6
- package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
- package/dist/src/memory/audit.js +719 -0
- package/dist/src/memory/canonical-context.js +392 -13
- package/dist/src/memory/canonical-maintenance.js +95 -0
- package/dist/src/memory/cli-result.js +22 -0
- package/dist/src/memory/cli.js +176 -0
- package/dist/src/memory/context-pack.js +57 -6
- package/dist/src/memory/cost.js +512 -0
- package/dist/src/memory/firewall-classification.d.mts +7 -0
- package/dist/src/memory/firewall-classification.mjs +32 -0
- package/dist/src/memory/index.js +8 -0
- package/dist/src/memory/line-maintenance.js +68 -0
- package/dist/src/memory/maintenance-cli.js +286 -0
- package/dist/src/memory/maintenance-contract.js +136 -0
- package/dist/src/memory/maintenance-index.js +18 -0
- package/dist/src/memory/maintenance-owner.js +2 -0
- package/dist/src/memory/maintenance-record.js +138 -0
- package/dist/src/memory/maintenance.js +254 -0
- package/dist/src/memory/project-registry.js +2 -1
- package/dist/src/memory/provider-memory-adapter.js +154 -0
- package/dist/src/memory/recommend-cli.js +83 -0
- package/dist/src/memory/recommend.js +269 -0
- package/dist/src/memory/scheduled.js +81 -0
- package/dist/src/memory/taxonomy.js +44 -0
- package/dist/src/mission-protocol/codecs.js +19 -9
- package/dist/src/mission-protocol/worker-status.js +91 -1
- package/dist/src/models/config-loader.js +11 -1
- package/dist/src/models/model-capabilities.v1.json +74 -50
- package/dist/src/models/provider-policy.js +125 -62
- package/dist/src/models/resolver.js +45 -8
- package/dist/src/models/worker-installation.d.mts +8 -1
- package/dist/src/models/worker-installation.mjs +231 -38
- package/dist/src/models/wrapper-route.js +1 -1
- package/dist/src/output-modes/registry.js +8 -7
- package/dist/src/packages/adapters/git.js +99 -23
- package/dist/src/packages/adapters/gitea.js +8 -27
- package/dist/src/packages/adapters/github.js +7 -25
- package/dist/src/packages/registry.js +4 -4
- package/dist/src/plugin/metadata-validator.js +1 -1
- package/dist/src/process/cli-command.d.mts +5 -0
- package/dist/src/process/cli-command.mjs +42 -0
- package/dist/src/providers/deploy-version.d.mts +1 -0
- package/dist/src/providers/deploy-version.mjs +17 -0
- package/dist/src/providers/provider-definitions.js +19 -16
- package/dist/src/providers/skill-ownership.d.mts +11 -2
- package/dist/src/providers/skill-ownership.mjs +35 -17
- package/dist/src/rules/activation.d.mts +11 -2
- package/dist/src/rules/activation.mjs +82 -6
- package/dist/src/rules/inventory.js +1 -1
- package/dist/src/rules/rule-frontmatter.schema.json +2 -0
- package/dist/src/schema/zod-validation.d.mts +6 -0
- package/dist/src/schema/zod-validation.mjs +50 -0
- package/dist/src/serve/executor-registry.js +297 -20
- package/dist/src/serve/executor-token.d.mts +1 -0
- package/dist/src/serve/executor-token.mjs +8 -0
- package/dist/src/serve/shared-host-scheduler.js +1102 -31
- package/dist/src/serve/worker-invocation.js +124 -15
- package/dist/src/sessions/adapters/claude.js +19 -18
- package/dist/src/sessions/adapters/codex.js +10 -9
- package/dist/src/sessions/adapters/copilot.js +14 -13
- package/dist/src/sessions/adapters/cursor.js +19 -18
- package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
- package/dist/src/sessions/adapters/factory.js +11 -10
- package/dist/src/sessions/adapters/generic.js +32 -23
- package/dist/src/sessions/adapters/grokbot.js +35 -35
- package/dist/src/sessions/adapters/hermes.js +20 -19
- package/dist/src/sessions/adapters/muse.js +50 -49
- package/dist/src/sessions/adapters/omp.js +5 -4
- package/dist/src/sessions/adapters/openclaw.js +30 -29
- package/dist/src/sessions/adapters/opencode.js +26 -26
- package/dist/src/sessions/adapters/openhuman.js +24 -23
- package/dist/src/sessions/adapters/pi.js +5 -4
- package/dist/src/sessions/adapters/windsurf.js +8 -8
- package/dist/src/sessions/analytics.js +10 -9
- package/dist/src/sessions/candidates.js +7 -6
- package/dist/src/sessions/contracts.js +63 -53
- package/dist/src/sessions/output-registration.js +11 -10
- package/dist/src/sessions/workspace-discovery.js +3 -4
- package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +46 -10
- package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
- package/dist/src/smiths/context-pipeline/finalization.js +3 -2
- package/dist/src/smiths/context-pipeline/generator.js +7 -19
- package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
- package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
- package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +110 -22
- package/dist/src/tracker/project-inputs.js +2 -0
- package/dist/src/update/checker.d.mts +4 -0
- package/dist/src/update/checker.mjs +14 -0
- package/dist/src/update/notifier.mjs +2 -4
- package/dist/src/writing/exemplar-selection.js +6 -6
- package/dist/src/writing/voice-evaluation.js +24 -23
- package/dist/src/writing/writer-migration.js +14 -13
- package/dist/src/writing/writer-profile.js +19 -18
- package/dist/src/writing/writing-brief.js +18 -17
- package/dist/src/writing/writing-receipt.js +47 -46
- package/package.json +5 -2
- package/schemas/decision/DecisionReview.schema.json +78 -1
- package/schemas/mission-v1.schema.json +3 -0
- package/tools/agents/deploy-agents.mjs +67 -39
- package/tools/agents/providers/antigravity.mjs +6 -1
- package/tools/agents/providers/base.mjs +24 -14
- package/tools/agents/providers/claude.mjs +6 -3
- package/tools/agents/providers/codex.mjs +54 -57
- package/tools/agents/providers/context-write.mjs +28 -0
- package/tools/agents/providers/copilot.mjs +13 -4
- package/tools/agents/providers/cursor.mjs +1 -1
- package/tools/agents/providers/deepseek-harness.mjs +1 -1
- package/tools/agents/providers/factory.mjs +7 -1
- package/tools/agents/providers/grok-build.mjs +18 -2
- package/tools/agents/providers/grokbot.mjs +1 -1
- package/tools/agents/providers/hermes.mjs +12 -15
- package/tools/agents/providers/muse-hooks.mjs +21 -14
- package/tools/agents/providers/muse.mjs +7 -2
- package/tools/agents/providers/omp.mjs +31 -5
- package/tools/agents/providers/opencode.mjs +14 -5
- package/tools/agents/providers/openhuman.mjs +1 -1
- package/tools/agents/providers/pi.mjs +1 -1
- package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
- package/tools/agents/providers/rule-delivery.mjs +289 -80
- package/tools/agents/providers/warp.mjs +4 -1
- package/tools/agents/providers/windsurf.mjs +13 -3
- package/tools/cli/workspace-status.mjs +5 -0
- package/tools/commands/deploy-prompts-codex.mjs +11 -8
- package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
- package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
- package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
- package/tools/ralph-external/process-monitor.mjs +49 -8
- package/tools/ralph-external/session-launcher.mjs +405 -35
- package/tools/skills/deploy-skills-codex.mjs +11 -7
- package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
|
|
2
|
+
import { helperEnvironment, HELPER_ENV_ALLOWLIST } from '../../process/cli-command.mjs';
|
|
3
|
+
import { readPrivateValueFile } from '../../auth/sensitive-input.js';
|
|
4
|
+
import { setupJev } from '../../decision/driver.js';
|
|
2
5
|
import { existsSync } from 'fs';
|
|
3
6
|
import { emptyConfig, FORCE_PUSH_POLICY_ALIAS_NOTE, normalizeForcePushPolicy as normalizeForcePushPolicyShared, getConfigPath, getProjectDir, readAiwgConfig, resolveRemoteProvider, VALID_PROVIDERS, writeAiwgConfig, } from '../../config/aiwg-config.js';
|
|
4
7
|
import { AiwgError, EXIT_CODES } from '../errors.js';
|
|
5
|
-
import { askChoice, askConsent, askString, askYesNo, createPromptInterface } from '../prompt-utils.js';
|
|
8
|
+
import { askChoice, askConsent, askString, askYesNo, createPromptInterface, createSecretPromptInterface } from '../prompt-utils.js';
|
|
6
9
|
import * as ui from '../ui.js';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { validateJevOptions } from './guided-setup.js';
|
|
12
|
+
import { validateParallelismConfig } from '../../config/aiwg-config.js';
|
|
13
|
+
import { readFile } from 'node:fs/promises';
|
|
14
|
+
import { discoverInventory } from '../../discovery/index.js';
|
|
15
|
+
import { applyGuidedPlan, configPathFor, credentialReferenceFor, defaultPlanReviewIO, projectRootForWorkspace, renderHumanReview, reviewGuidedPlan, } from '../../discovery/plan-apply.js';
|
|
16
|
+
import { readSetupPlan, writeSetupPlan } from '../../discovery/plan-store.js';
|
|
17
|
+
import { workspaceSlug } from '../../discovery/interview.js';
|
|
18
|
+
import { registerProjectMemory } from '../../memory/project-registry.js';
|
|
19
|
+
import { verifyProviderDeployment } from '../services/deployment-verification.js';
|
|
7
20
|
import { projectAiwgPath } from '../../config/project-artifacts.js';
|
|
21
|
+
import { useHandler } from './use.js';
|
|
8
22
|
import { normalizeProviderDefinitionId } from '../../providers/provider-definitions.js';
|
|
9
23
|
import { isInteractive } from '../env.js';
|
|
10
24
|
import { requireFlagValue as strictFlagValue, MissingFlagValueError } from '../flag-parsing.js';
|
|
25
|
+
import { parseGuidedSetupFlags, runGuidedSetup } from './guided-setup.js';
|
|
26
|
+
import { DEFAULT_SCRIPTS, parseInitArgInputs } from './init.js';
|
|
11
27
|
import { detectInstalledWorkerClis, evaluateWorkerDispatchOffer, workerDispatchOfferHintWithArg, workerDispatchOfferQuestion, WORKER_DISPATCH_ARG_VALUES, } from '../../models/worker-dispatch-offer.js';
|
|
12
28
|
const DELIVERY_MODES = ['pr-required', 'feature-branch', 'direct'];
|
|
13
29
|
const FORCE_PUSH_POLICIES = ['never', 'own-branch-only', 'allowed'];
|
|
@@ -120,6 +136,7 @@ export function parseSetupProjectOptions(ctx) {
|
|
|
120
136
|
}
|
|
121
137
|
export function detectGitRemotes(projectDir) {
|
|
122
138
|
const result = boundedSpawnSync('git', ['-C', projectDir, 'remote', '-v'], 'quick', {
|
|
139
|
+
env: helperEnvironment(),
|
|
123
140
|
encoding: 'utf-8',
|
|
124
141
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
125
142
|
});
|
|
@@ -143,6 +160,7 @@ export function detectGitRemotes(projectDir) {
|
|
|
143
160
|
}
|
|
144
161
|
function detectDefaultBranch(projectDir) {
|
|
145
162
|
const symbolic = boundedSpawnSync('git', ['-C', projectDir, 'symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'], 'quick', {
|
|
163
|
+
env: helperEnvironment(),
|
|
146
164
|
encoding: 'utf-8',
|
|
147
165
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
148
166
|
});
|
|
@@ -150,6 +168,7 @@ function detectDefaultBranch(projectDir) {
|
|
|
150
168
|
if (symbolic.status === 0 && fromOrigin)
|
|
151
169
|
return fromOrigin;
|
|
152
170
|
const branch = boundedSpawnSync('git', ['-C', projectDir, 'branch', '--show-current'], 'quick', {
|
|
171
|
+
env: helperEnvironment(),
|
|
153
172
|
encoding: 'utf-8',
|
|
154
173
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
155
174
|
});
|
|
@@ -157,6 +176,7 @@ function detectDefaultBranch(projectDir) {
|
|
|
157
176
|
}
|
|
158
177
|
function gitConfig(projectDir, key) {
|
|
159
178
|
const result = boundedSpawnSync('git', ['-C', projectDir, 'config', '--get', key], 'quick', {
|
|
179
|
+
env: helperEnvironment(),
|
|
160
180
|
encoding: 'utf-8',
|
|
161
181
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
162
182
|
});
|
|
@@ -470,6 +490,310 @@ function printPlan(plan) {
|
|
|
470
490
|
ui.warn(warning);
|
|
471
491
|
}
|
|
472
492
|
}
|
|
493
|
+
// ---------------------------------------------------------------------------
|
|
494
|
+
// Guided plan review and apply (#3449 A4).
|
|
495
|
+
//
|
|
496
|
+
// `--review` renders a saved guided plan per workspace and provider without
|
|
497
|
+
// writing anything. `--apply` executes `remainingPlanActions` in dependency
|
|
498
|
+
// order through the existing owners: project policy through
|
|
499
|
+
// `buildSetupProjectPlan` + `writeAiwgConfig` (the setup/init config owners),
|
|
500
|
+
// framework deployment through the `use` handler (consuming its
|
|
501
|
+
// `aiwg.use.result.v1` verification), and project registration through the
|
|
502
|
+
// project registry owner. Nothing here re-implements those paths.
|
|
503
|
+
// ---------------------------------------------------------------------------
|
|
504
|
+
/** Legacy policy flags: when any is present, `--dry-run` keeps its legacy preview meaning. */
|
|
505
|
+
const LEGACY_SETUP_POLICY_FLAGS = [
|
|
506
|
+
'--primary', '--issue-tracker', '--customer-issue-tracker', '--ci',
|
|
507
|
+
'--issue-provider', '--customer-issue-provider', '--delivery-mode', '--default-branch',
|
|
508
|
+
'--require-ci-green', '--auto-close-issues', '--issue-comment-on-cycle', '--force-push-policy',
|
|
509
|
+
'--require-signed-commits', '--committer-name', '--committer-email', '--signing-format',
|
|
510
|
+
'--signing-key', '--signing-enforce', '--tracker-actor-login', '--tracker-actor-via',
|
|
511
|
+
'--customer-tracker-actor-login', '--customer-tracker-actor-via', '--worker-dispatch', '--providers',
|
|
512
|
+
];
|
|
513
|
+
function hasLegacySetupPolicyFlags(args) {
|
|
514
|
+
return LEGACY_SETUP_POLICY_FLAGS.some(flag => args.includes(flag) || args.some(arg => arg.startsWith(`${flag}=`)));
|
|
515
|
+
}
|
|
516
|
+
const GUIDED_INTENT_FRAMEWORK = {
|
|
517
|
+
code: 'sdlc', docs: 'writing', ops: 'ops', mixed: 'sdlc',
|
|
518
|
+
};
|
|
519
|
+
function guidedAnswer(plan, workspace, block, topic, provider = 'shared') {
|
|
520
|
+
const projectRoot = projectRootForWorkspace(plan, workspace) ?? workspace;
|
|
521
|
+
return plan.answers[`${block}.${topic}.ws-${workspaceSlug(projectRoot)}.${provider}`]?.value;
|
|
522
|
+
}
|
|
523
|
+
function guidedProviders(plan, workspace) {
|
|
524
|
+
const projectRoot = projectRootForWorkspace(plan, workspace) ?? workspace;
|
|
525
|
+
const prefix = `tools.provider.ws-${workspaceSlug(projectRoot)}.`;
|
|
526
|
+
return Object.entries(plan.answers)
|
|
527
|
+
.filter(([id, answer]) => id.startsWith(prefix) && answer.value === true)
|
|
528
|
+
.map(([id]) => id.slice(prefix.length))
|
|
529
|
+
.sort();
|
|
530
|
+
}
|
|
531
|
+
async function writeGuidedProjectConfig(plan, action) {
|
|
532
|
+
const asRecord = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
533
|
+
const delivery = asRecord(guidedAnswer(plan, action.workspace, 'capabilities', 'delivery'));
|
|
534
|
+
const signing = guidedAnswer(plan, action.workspace, 'capabilities', 'signing');
|
|
535
|
+
const tracker = asRecord(guidedAnswer(plan, action.workspace, 'capabilities', 'tracker'));
|
|
536
|
+
const team = guidedAnswer(plan, action.workspace, 'intent', 'team');
|
|
537
|
+
const string = (value) => typeof value === 'string' ? value : undefined;
|
|
538
|
+
const boolean = (value) => typeof value === 'boolean' ? value : undefined;
|
|
539
|
+
const built = await buildSetupProjectPlan({
|
|
540
|
+
projectDir: action.workspace,
|
|
541
|
+
providers: guidedProviders(plan, action.workspace),
|
|
542
|
+
...(string(delivery.mode) ? { deliveryMode: string(delivery.mode) } : {}),
|
|
543
|
+
...(string(delivery.default_branch) ? { defaultBranch: string(delivery.default_branch) } : {}),
|
|
544
|
+
...(boolean(delivery.require_ci_green) ? { requireCiGreen: boolean(delivery.require_ci_green) } : {}),
|
|
545
|
+
...(boolean(delivery.auto_close_issues) ? { autoCloseIssues: boolean(delivery.auto_close_issues) } : {}),
|
|
546
|
+
...(boolean(delivery.issue_comment_on_cycle) ? { issueCommentOnCycle: boolean(delivery.issue_comment_on_cycle) } : {}),
|
|
547
|
+
...(!delivery.force_push_policy && team === 'shared' ? { forcePushPolicy: 'never' } : {}),
|
|
548
|
+
...(string(delivery.force_push_policy) ? { forcePushPolicy: string(delivery.force_push_policy) } : {}),
|
|
549
|
+
...(signing === false ? { requireSignedCommits: false } : {}),
|
|
550
|
+
...(typeof signing?.required === 'boolean'
|
|
551
|
+
? { requireSignedCommits: signing.required } : {}),
|
|
552
|
+
...(string(signing?.format) ? { signingFormat: string(signing.format) } : {}),
|
|
553
|
+
...(string(signing?.key) ? { signingKey: string(signing.key) } : {}),
|
|
554
|
+
...(string(signing?.enforce) ? { signingEnforce: string(signing.enforce) } : {}),
|
|
555
|
+
...(string(tracker.primary) ? { primary: string(tracker.primary) } : {}),
|
|
556
|
+
...(string(tracker.issue_tracker) ? { issueTracker: string(tracker.issue_tracker) } : {}),
|
|
557
|
+
...(string(tracker.issue_provider) ? { issueProvider: string(tracker.issue_provider) } : {}),
|
|
558
|
+
...(string(tracker.customer_issue_tracker) ? { customerIssueTracker: string(tracker.customer_issue_tracker) } : {}),
|
|
559
|
+
...(string(tracker.customer_issue_provider) ? { customerIssueProvider: string(tracker.customer_issue_provider) } : {}),
|
|
560
|
+
...(string(tracker.ci) ? { ci: string(tracker.ci) } : {}),
|
|
561
|
+
...(string(tracker.tracker_actor_login) ? { trackerActorLogin: string(tracker.tracker_actor_login) } : {}),
|
|
562
|
+
...(string(tracker.tracker_actor_via) ? { trackerActorVia: string(tracker.tracker_actor_via) } : {}),
|
|
563
|
+
...(string(tracker.customer_tracker_actor_login) ? { customerTrackerActorLogin: string(tracker.customer_tracker_actor_login) } : {}),
|
|
564
|
+
...(string(tracker.customer_tracker_actor_via) ? { customerTrackerActorVia: string(tracker.customer_tracker_actor_via) } : {}),
|
|
565
|
+
});
|
|
566
|
+
// Interview script answers merge on top of the preserved config: init owns
|
|
567
|
+
// the default-script set, setup owns the policy merge above.
|
|
568
|
+
const scripts = asRecord(guidedAnswer(plan, action.workspace, 'tools', 'scripts'));
|
|
569
|
+
let next = built.next;
|
|
570
|
+
if (Object.keys(scripts).length > 0) {
|
|
571
|
+
const merged = { ...(next.scripts ?? {}) };
|
|
572
|
+
if (scripts.defaultScripts === true) {
|
|
573
|
+
for (const [name, command] of Object.entries(DEFAULT_SCRIPTS)) {
|
|
574
|
+
if (!merged[name])
|
|
575
|
+
merged[name] = command;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const custom = scripts.scripts;
|
|
579
|
+
if (custom && typeof custom === 'object' && !Array.isArray(custom))
|
|
580
|
+
Object.assign(merged, custom);
|
|
581
|
+
next = { ...next, scripts: merged };
|
|
582
|
+
}
|
|
583
|
+
await writeAiwgConfig(action.workspace, next);
|
|
584
|
+
return `merged guided policy into ${getConfigPath(action.workspace)}`;
|
|
585
|
+
}
|
|
586
|
+
// configure-provider writes no provider files: provider config files belong
|
|
587
|
+
// to the existing deployment owner (`aiwg use`, the `deploy-framework`
|
|
588
|
+
// action), which `PLAN_APPLY_KIND_ORDER` runs after every
|
|
589
|
+
// configure-provider. An existing file is user-authored and left untouched
|
|
590
|
+
// (applied, preserved); a missing one defers to deploy-framework. The
|
|
591
|
+
// credential reference was already resolved at apply time by the apply engine
|
|
592
|
+
// and is never written anywhere.
|
|
593
|
+
async function writeGuidedProviderConfig(plan, action, configPath) {
|
|
594
|
+
if (!configPath)
|
|
595
|
+
throw new Error(`Cannot apply '${action.id}': no provider config path was recorded for ${action.provider}.`);
|
|
596
|
+
try {
|
|
597
|
+
await readFile(configPath, 'utf8');
|
|
598
|
+
return `preserved existing provider file ${configPath} (left untouched)`;
|
|
599
|
+
}
|
|
600
|
+
catch (error) {
|
|
601
|
+
if (error?.code !== 'ENOENT')
|
|
602
|
+
throw error;
|
|
603
|
+
}
|
|
604
|
+
const deploy = plan.actions.find(other => other.workspace === action.workspace &&
|
|
605
|
+
other.provider === action.provider && other.kind === 'deploy-framework');
|
|
606
|
+
const owner = deploy ? `'${deploy.id}'` : 'deploy-framework';
|
|
607
|
+
return `provider files for ${action.provider} in ${action.workspace} are owned by ${owner}; nothing written`;
|
|
608
|
+
}
|
|
609
|
+
async function verifyGuidedProvider(plan, fresh, action, reference) {
|
|
610
|
+
const executable = action.dependsOn.find(fact => fact.kind === 'tool')?.executable ?? action.provider;
|
|
611
|
+
const tool = fresh.tools.find(entry => entry.executable === executable);
|
|
612
|
+
if (!tool || tool.installed !== 'yes') {
|
|
613
|
+
throw new Error(`Provider tool '${executable}' is not installed; install it and re-run discovery before verifying '${action.id}'.`);
|
|
614
|
+
}
|
|
615
|
+
const configPath = action.dependsOn.find(fact => fact.kind === 'path')?.path
|
|
616
|
+
?? configPathFor(plan, action.workspace, action.provider);
|
|
617
|
+
let configNote = 'no config path recorded';
|
|
618
|
+
if (configPath) {
|
|
619
|
+
try {
|
|
620
|
+
await readFile(configPath, 'utf8');
|
|
621
|
+
configNote = `config present at ${configPath}`;
|
|
622
|
+
}
|
|
623
|
+
catch {
|
|
624
|
+
configNote = `config missing at ${configPath}`;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
const version = tool.version ? `version ${tool.version}` : 'version unknown';
|
|
628
|
+
// Hyphenated wording: receipt messages persist through the plan store,
|
|
629
|
+
// whose redaction gate rejects `credential <value>` phrasing.
|
|
630
|
+
const credential = reference ? `credential-ref ${reference.source}:${reference.name} readable at apply time` : 'no credential-reference recorded';
|
|
631
|
+
return `verified ${action.provider} (${executable} installed, ${version}; ${configNote}; ${credential}; read-back only)`;
|
|
632
|
+
}
|
|
633
|
+
async function deployGuidedFramework(plan, action, frameworkRoot, signal) {
|
|
634
|
+
const intent = guidedAnswer(plan, action.workspace, 'intent', 'use');
|
|
635
|
+
const framework = GUIDED_INTENT_FRAMEWORK[typeof intent === 'string' ? intent : 'code'] ?? 'sdlc';
|
|
636
|
+
const args = [framework, '--provider', action.provider, ...(action.scope === 'user' ? ['--scope', 'user'] : [])];
|
|
637
|
+
const deployed = await useHandler.execute({ cwd: action.workspace, args, rawArgs: args, frameworkRoot, dryRun: false, signal });
|
|
638
|
+
if (deployed.exitCode !== 0) {
|
|
639
|
+
throw new Error(`aiwg use ${framework} --provider ${action.provider} failed${deployed.message ? `: ${deployed.message}` : ` (exit ${deployed.exitCode})`}.`);
|
|
640
|
+
}
|
|
641
|
+
// Consume the aiwg.use.result.v1 verification for this provider and scope.
|
|
642
|
+
const verification = await verifyProviderDeployment({
|
|
643
|
+
projectRoot: action.workspace,
|
|
644
|
+
frameworkRoot,
|
|
645
|
+
provider: action.provider,
|
|
646
|
+
scope: action.scope,
|
|
647
|
+
requestedBundles: [framework],
|
|
648
|
+
});
|
|
649
|
+
const blocking = verification.findings.filter(finding => finding.severity === 'blocking');
|
|
650
|
+
if (verification.outcome === 'failed' || blocking.length > 0) {
|
|
651
|
+
throw new Error(`Deployment verification failed for ${action.provider}: ${blocking[0]?.message ?? verification.outcome}.`);
|
|
652
|
+
}
|
|
653
|
+
return `deployed ${framework} for ${action.provider} (verification: ${verification.outcome})`;
|
|
654
|
+
}
|
|
655
|
+
function defaultGuidedApplyIO(plan, fresh, ctx, projectDir, frameworkRoot, canPrompt) {
|
|
656
|
+
return {
|
|
657
|
+
...defaultPlanReviewIO(),
|
|
658
|
+
readEnv: name => process.env[name],
|
|
659
|
+
// Headless apply can consume redirected stdin. Interactive apply uses the prompt below.
|
|
660
|
+
...(!canPrompt ? { readStdinCredential: async () => {
|
|
661
|
+
const chunks = [];
|
|
662
|
+
for await (const chunk of process.stdin)
|
|
663
|
+
chunks.push(Buffer.from(chunk));
|
|
664
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
665
|
+
} } : {}),
|
|
666
|
+
readCredentialFile: async (path) => readPrivateValueFile(path, '--credential'),
|
|
667
|
+
// stdin-at-apply prompts for the value without echoing it anywhere: the
|
|
668
|
+
// label names the consumer only, and the value never reaches output.
|
|
669
|
+
...(canPrompt ? { promptCredential: async (label) => {
|
|
670
|
+
const rl = createSecretPromptInterface();
|
|
671
|
+
try {
|
|
672
|
+
process.stdout.write(label);
|
|
673
|
+
return await askString(rl, '', '', ctx.signal);
|
|
674
|
+
}
|
|
675
|
+
finally {
|
|
676
|
+
rl.close();
|
|
677
|
+
}
|
|
678
|
+
} } : {}),
|
|
679
|
+
now: () => new Date().toISOString(),
|
|
680
|
+
save: async (next) => {
|
|
681
|
+
await writeSetupPlan(projectDir, next);
|
|
682
|
+
},
|
|
683
|
+
owners: {
|
|
684
|
+
configureJev: async ({ action, token, options }) => {
|
|
685
|
+
if (options != null)
|
|
686
|
+
validateJevOptions(options);
|
|
687
|
+
const settings = (options ?? {});
|
|
688
|
+
// Invoke the existing owner directly. spawnSync's input option is
|
|
689
|
+
// rendered by NODE_DEBUG=child_process before the child can read it.
|
|
690
|
+
const result = await setupJev({
|
|
691
|
+
token,
|
|
692
|
+
...(typeof settings.region === 'string' ? { region: settings.region } : {}),
|
|
693
|
+
...(typeof settings.endpoint === 'string' ? { endpoint: settings.endpoint } : {}),
|
|
694
|
+
verify: settings.verify === true,
|
|
695
|
+
remove: settings.remove === true,
|
|
696
|
+
}, { env: process.env, cwd: action.workspace });
|
|
697
|
+
if (settings.verify === true && settings.remove !== true && result.verified !== true) {
|
|
698
|
+
throw new Error('Jev verification incomplete.');
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
configureWorkerDispatch: async ({ action }) => {
|
|
702
|
+
const built = await buildSetupProjectPlan({ projectDir: action.workspace, workerDispatchChoice: 'enable' });
|
|
703
|
+
const settings = guidedAnswer(plan, action.workspace, 'capabilities', 'worker-config');
|
|
704
|
+
let next = built.next;
|
|
705
|
+
if (settings && typeof settings === 'object' && !Array.isArray(settings)) {
|
|
706
|
+
if (Object.keys(settings).some(key => !['clis', 'groups', 'admission', 'budgets'].includes(key)))
|
|
707
|
+
throw new Error('Invalid worker settings.');
|
|
708
|
+
next = { ...next, parallelism: { ...next.parallelism, ...settings } };
|
|
709
|
+
}
|
|
710
|
+
if (validateParallelismConfig(next.parallelism).length)
|
|
711
|
+
throw new Error('Invalid worker settings.');
|
|
712
|
+
await writeAiwgConfig(action.workspace, next);
|
|
713
|
+
const child = boundedSpawnSync(process.execPath, [join(frameworkRoot, 'bin', 'aiwg.mjs'),
|
|
714
|
+
'doctor', '--probe-workers', '--qualify', '--yes', '--json'], 'long', {
|
|
715
|
+
cwd: action.workspace, env: helperEnvironment(process.env, [...HELPER_ENV_ALLOWLIST,
|
|
716
|
+
'AIWG_ROOT', 'AIWG_HOME', 'AIWG_CONFIG', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME',
|
|
717
|
+
'AIWG_ARTIFACTS_PATH', 'AIWG_PROJECT_ARTIFACTS_PATH', 'AIWG_PROJECT_AIWG_DIR',
|
|
718
|
+
'AIWG_PROJECT_MEMORY_HOME', 'NO_UPDATE_NOTIFIER', 'NO_COLOR', 'CI']), encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 1024 * 1024,
|
|
719
|
+
});
|
|
720
|
+
if (child.error || child.status !== 0)
|
|
721
|
+
throw new Error('Worker qualification failed.');
|
|
722
|
+
const report = JSON.parse(child.stdout);
|
|
723
|
+
if (report.qualification?.status !== 'recorded' || !report.qualification?.results?.length || report.qualification.results.some((row) => row.status !== 'qualified'))
|
|
724
|
+
throw new Error('Worker qualification incomplete.');
|
|
725
|
+
},
|
|
726
|
+
configureProvider: async ({ action, configPath }) => writeGuidedProviderConfig(plan, action, configPath),
|
|
727
|
+
verifyProvider: async ({ action }) => verifyGuidedProvider(plan, fresh, action, credentialReferenceFor(plan, action.workspace, action.provider)),
|
|
728
|
+
writeProjectConfig: async ({ action }) => writeGuidedProjectConfig(plan, action),
|
|
729
|
+
deployFramework: async ({ action }) => deployGuidedFramework(plan, action, frameworkRoot, ctx.signal),
|
|
730
|
+
registerProject: async ({ action }) => {
|
|
731
|
+
const entry = await registerProjectMemory({ workspaceRoot: action.workspace });
|
|
732
|
+
return `registered project '${entry.id}'`;
|
|
733
|
+
},
|
|
734
|
+
recordDecline: async ({ action }) => {
|
|
735
|
+
const built = await buildSetupProjectPlan({ projectDir: action.workspace, workerDispatchChoice: 'decline' });
|
|
736
|
+
await writeAiwgConfig(action.workspace, built.next);
|
|
737
|
+
return `recorded worker-dispatch decline in ${getConfigPath(action.workspace)}`;
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
async function loadGuidedPlan(projectDir, planId) {
|
|
743
|
+
const fresh = await discoverInventory({ workspaceRoot: projectDir });
|
|
744
|
+
const workspaceRoot = fresh.projects.find(project => project.sources.includes('workspace'))?.root ?? projectDir;
|
|
745
|
+
const id = planId ?? `setup-${workspaceSlug(workspaceRoot)}`;
|
|
746
|
+
let plan;
|
|
747
|
+
try {
|
|
748
|
+
plan = await readSetupPlan(projectDir, id);
|
|
749
|
+
}
|
|
750
|
+
catch {
|
|
751
|
+
throw new AiwgError({
|
|
752
|
+
code: 'ERR_SETUP_PLAN_NOT_FOUND',
|
|
753
|
+
message: `No guided setup plan '${id}' for ${projectDir}.`,
|
|
754
|
+
hint: 'Record one first with `aiwg setup --scope project|user` (or `aiwg init --scope ...`).',
|
|
755
|
+
exitCode: EXIT_CODES.GENERAL,
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
return { plan, fresh };
|
|
759
|
+
}
|
|
760
|
+
async function runGuidedReview(_ctx, projectDir, planId, json) {
|
|
761
|
+
const { plan, fresh } = await loadGuidedPlan(projectDir, planId);
|
|
762
|
+
// Review is read-only: findings come from revalidation plus filesystem
|
|
763
|
+
// reads, and nothing here writes (not even receipts).
|
|
764
|
+
const review = await reviewGuidedPlan(plan, fresh, defaultPlanReviewIO());
|
|
765
|
+
if (json)
|
|
766
|
+
return { exitCode: 0, rawOutput: true, message: JSON.stringify(review, null, 2) };
|
|
767
|
+
return { exitCode: 0, message: renderHumanReview(review) };
|
|
768
|
+
}
|
|
769
|
+
async function runGuidedApply(ctx, projectDir, planId, options) {
|
|
770
|
+
const { plan, fresh } = await loadGuidedPlan(projectDir, planId);
|
|
771
|
+
// The existing --yes/confirmation gate and TTY rules: a write that can
|
|
772
|
+
// never be confirmed is refused before any owner runs.
|
|
773
|
+
if (!options.yes && !options.canPrompt) {
|
|
774
|
+
throw new AiwgError({
|
|
775
|
+
code: 'ERR_NON_INTERACTIVE_CONFIRMATION_REQUIRED',
|
|
776
|
+
message: 'Refusing to apply the guided setup plan in a non-interactive context without --yes.',
|
|
777
|
+
hint: 'Re-run with `aiwg setup --apply --yes` or use `--review`/`--dry-run` to preview only.',
|
|
778
|
+
exitCode: EXIT_CODES.USAGE,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
if (!options.yes && options.canPrompt) {
|
|
782
|
+
const rl = createPromptInterface();
|
|
783
|
+
try {
|
|
784
|
+
const confirmed = await askYesNo(rl, `Apply the guided setup plan '${plan.id}'? [y/N]: `, false, ctx.signal);
|
|
785
|
+
if (!confirmed)
|
|
786
|
+
return { exitCode: 0, message: 'Guided setup apply cancelled.' };
|
|
787
|
+
}
|
|
788
|
+
finally {
|
|
789
|
+
rl.close();
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
const { result } = await applyGuidedPlan(plan, fresh, defaultGuidedApplyIO(plan, fresh, ctx, projectDir, options.frameworkRoot, options.canPrompt));
|
|
793
|
+
if (options.json)
|
|
794
|
+
return { exitCode: result.exitCode, rawOutput: true, message: JSON.stringify(result, null, 2) };
|
|
795
|
+
return { exitCode: result.exitCode, message: result.message };
|
|
796
|
+
}
|
|
473
797
|
export const setupHandler = {
|
|
474
798
|
id: 'setup',
|
|
475
799
|
name: 'Setup',
|
|
@@ -484,8 +808,21 @@ export const setupHandler = {
|
|
|
484
808
|
|
|
485
809
|
Options:
|
|
486
810
|
--target <dir> Project directory
|
|
811
|
+
--scope project|user Record guided plan only (no config writes)
|
|
812
|
+
--force Recreate guided interview answers
|
|
813
|
+
--workspace <root> Observed workspace to configure (repeatable)
|
|
814
|
+
--monorepo root|package Monorepo target selection
|
|
815
|
+
--package <root> Observed package root (with --monorepo package)
|
|
816
|
+
--scan-roots <dir> Opt in to bounded discovery (repeatable)
|
|
817
|
+
--intent code|docs|ops|mixed Workspace usage intent
|
|
818
|
+
--jev enable|decline|defer Optional external decision service (default off)
|
|
819
|
+
--region <r> --endpoint <url> --verify --remove --token-stdin Existing Jev options
|
|
820
|
+
--credential <provider>=stdin|file:PATH|store:NAME|env:NAME Credential reference only
|
|
487
821
|
--yes Confirm writes without prompting
|
|
488
822
|
--dry-run Preview the setup plan
|
|
823
|
+
--review Render the saved guided plan per workspace/provider (writes nothing)
|
|
824
|
+
--apply Apply the saved guided plan through the existing owners
|
|
825
|
+
--plan <id> Guided plan ID (default: setup plan for the target directory)
|
|
489
826
|
--non-interactive Disable prompts
|
|
490
827
|
--json Emit the plan as JSON
|
|
491
828
|
--primary <remote> Primary repository remote
|
|
@@ -524,12 +861,106 @@ Options:
|
|
|
524
861
|
};
|
|
525
862
|
}
|
|
526
863
|
const args = subcommand === 'project' ? ctx.args.slice(1) : ctx.args;
|
|
864
|
+
let guidedFlags;
|
|
865
|
+
try {
|
|
866
|
+
guidedFlags = parseGuidedSetupFlags(args, 'setup');
|
|
867
|
+
}
|
|
868
|
+
catch (error) {
|
|
869
|
+
throw new AiwgError({ code: 'ERR_INVALID_VALUE', exitCode: EXIT_CODES.USAGE,
|
|
870
|
+
message: error instanceof Error ? error.message : String(error) });
|
|
871
|
+
}
|
|
527
872
|
const options = parseSetupProjectOptions({ ...ctx, args });
|
|
528
873
|
// #3062 review item 12: the one shared interactive decision (TTY, TERM,
|
|
529
874
|
// CI, DEBIAN_FRONTEND) — this used to check only `process.stdin.isTTY &&
|
|
530
875
|
// process.stdout.isTTY`, so a CI job with a pseudo-terminal could still
|
|
531
876
|
// reach these prompts.
|
|
532
877
|
const canPrompt = isInteractive(options.nonInteractive);
|
|
878
|
+
// #3449 A4: operate on a saved guided plan. `--review` renders it
|
|
879
|
+
// read-only; `--apply` executes it through the existing owners behind the
|
|
880
|
+
// same --yes/confirmation gate as legacy writes. `--dry-run` on a guided
|
|
881
|
+
// plan (no planning or policy flags) previews the same review and writes
|
|
882
|
+
// nothing. `--review` and `--apply` together is a usage error.
|
|
883
|
+
const wantsReview = boolFlag(args, '--review');
|
|
884
|
+
const wantsApply = boolFlag(args, '--apply');
|
|
885
|
+
if (wantsReview && wantsApply) {
|
|
886
|
+
throw new AiwgError({
|
|
887
|
+
code: 'ERR_USAGE_CONFLICTING_FLAGS',
|
|
888
|
+
message: 'Cannot combine --review with --apply; use --review to preview or --apply to write.',
|
|
889
|
+
exitCode: EXIT_CODES.USAGE,
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
if (wantsReview || wantsApply || (options.dryRun && !guidedFlags.enabled && !hasLegacySetupPolicyFlags(args))) {
|
|
893
|
+
let planId;
|
|
894
|
+
try {
|
|
895
|
+
planId = flagValue(args, '--plan');
|
|
896
|
+
}
|
|
897
|
+
catch (error) {
|
|
898
|
+
if (error instanceof MissingFlagValueError) {
|
|
899
|
+
throw new AiwgError({ code: 'ERR_USAGE_MISSING_FLAG_VALUE', message: `${error.message}.`, exitCode: EXIT_CODES.USAGE });
|
|
900
|
+
}
|
|
901
|
+
throw error;
|
|
902
|
+
}
|
|
903
|
+
// A bare `--dry-run` without a saved plan keeps its legacy preview
|
|
904
|
+
// meaning; only an existing plan switches it to the guided review.
|
|
905
|
+
if (!wantsReview && !wantsApply && options.dryRun) {
|
|
906
|
+
try {
|
|
907
|
+
return await runGuidedReview({ ...ctx, args }, options.projectDir, planId, options.json ?? false);
|
|
908
|
+
}
|
|
909
|
+
catch (error) {
|
|
910
|
+
if (error instanceof AiwgError && error.code === 'ERR_SETUP_PLAN_NOT_FOUND') {
|
|
911
|
+
// Fall through to the legacy setup preview below.
|
|
912
|
+
}
|
|
913
|
+
else
|
|
914
|
+
throw error;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
else if (wantsReview || options.dryRun) {
|
|
918
|
+
return runGuidedReview({ ...ctx, args }, options.projectDir, planId, options.json ?? false);
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
return runGuidedApply({ ...ctx, args }, options.projectDir, planId, { yes: options.yes ?? false, json: options.json ?? false, canPrompt, frameworkRoot: ctx.frameworkRoot });
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (guidedFlags.enabled) {
|
|
925
|
+
const target = flagValue(args, '--target');
|
|
926
|
+
const projectDir = target ?? options.projectDir;
|
|
927
|
+
let inputs;
|
|
928
|
+
try {
|
|
929
|
+
inputs = parseInitArgInputs(args);
|
|
930
|
+
}
|
|
931
|
+
catch (error) {
|
|
932
|
+
throw new AiwgError({ code: 'ERR_INVALID_VALUE', exitCode: EXIT_CODES.USAGE,
|
|
933
|
+
message: error instanceof Error ? error.message : String(error) });
|
|
934
|
+
}
|
|
935
|
+
const selected = (entries) => {
|
|
936
|
+
const present = entries.filter((entry) => entry[1] !== undefined);
|
|
937
|
+
return present.length ? Object.fromEntries(present) : undefined;
|
|
938
|
+
};
|
|
939
|
+
const tracker = selected([
|
|
940
|
+
['primary', options.primary], ['issue_tracker', options.issueTracker], ['issue_provider', options.issueProvider],
|
|
941
|
+
['customer_issue_tracker', options.customerIssueTracker], ['customer_issue_provider', options.customerIssueProvider],
|
|
942
|
+
['ci', options.ci], ['tracker_actor_login', options.trackerActorLogin], ['tracker_actor_via', options.trackerActorVia],
|
|
943
|
+
['customer_tracker_actor_login', options.customerTrackerActorLogin], ['customer_tracker_actor_via', options.customerTrackerActorVia],
|
|
944
|
+
]);
|
|
945
|
+
const delivery = selected([
|
|
946
|
+
['mode', options.deliveryMode], ['default_branch', options.defaultBranch], ['require_ci_green', options.requireCiGreen],
|
|
947
|
+
['auto_close_issues', options.autoCloseIssues], ['issue_comment_on_cycle', options.issueCommentOnCycle], ['force_push_policy', options.forcePushPolicy],
|
|
948
|
+
]);
|
|
949
|
+
const signing = options.requireSignedCommits === false ? false : selected([
|
|
950
|
+
['required', options.requireSignedCommits], ['format', options.signingFormat], ['key', options.signingKey], ['enforce', options.signingEnforce],
|
|
951
|
+
]);
|
|
952
|
+
const hasScripts = inputs.defaultScriptsArg !== undefined || inputs.customScriptsArg !== undefined || Object.keys(inputs.scriptPairs).length > 0;
|
|
953
|
+
const remotes = detectGitRemotes(projectDir);
|
|
954
|
+
const primary = options.primary ?? choosePrimary(remotes);
|
|
955
|
+
return runGuidedSetup({ ...ctx, args }, guidedFlags, { providers: inputs.providersArg,
|
|
956
|
+
workerDispatch: options.workerDispatchChoice, tracker, delivery, signing,
|
|
957
|
+
team: options.forcePushPolicy === undefined ? undefined : options.forcePushPolicy === 'never' ? 'shared' : 'solo',
|
|
958
|
+
...(hasScripts ? { scripts: { defaultScripts: inputs.defaultScriptsArg ?? true,
|
|
959
|
+
customScripts: inputs.customScriptsArg ?? Object.keys(inputs.scriptPairs).length > 0, scripts: inputs.scriptPairs } } : {}),
|
|
960
|
+
}, canPrompt, projectDir, remotes.find(r => r.name === primary)?.provider === 'unknown', remotes.length ? { primary, issue_tracker: primary, ci: primary,
|
|
961
|
+
...(remotes.find(r => r.name === primary)?.provider !== 'unknown'
|
|
962
|
+
? { issue_provider: remotes.find(r => r.name === primary).provider } : {}) } : undefined);
|
|
963
|
+
}
|
|
533
964
|
// #3062 second review, finding 6: refuse before any child process runs
|
|
534
965
|
// — `buildSetupProjectPlan()` below makes four `git` calls on its own,
|
|
535
966
|
// and this exact refusal used to happen only after building (and
|
|
@@ -47,7 +47,7 @@ function scoreSchema(frontmatter, diagnostics) {
|
|
|
47
47
|
const result = validateSkillFrontmatter(frontmatter);
|
|
48
48
|
if (result.success)
|
|
49
49
|
return scoreDimension(true);
|
|
50
|
-
const notes = result.errors.
|
|
50
|
+
const notes = result.errors.issues.map((e) => `${e.path.join('.') || '<root>'}: ${e.message}`);
|
|
51
51
|
return scoreDimension(false, notes);
|
|
52
52
|
}
|
|
53
53
|
/**
|