@aiwg/cli 2026.10.4 → 2026.10.6
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 +3 -1
- package/dist/src/artifacts/browser-export.js +1 -0
- package/dist/src/artifacts/citation-parser.js +8 -1
- package/dist/src/artifacts/cli.js +72 -9
- 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/graph-query.js +18 -8
- package/dist/src/artifacts/index-adapters.js +339 -0
- package/dist/src/artifacts/index-builder.js +146 -16
- package/dist/src/artifacts/index-files.js +15 -4
- package/dist/src/artifacts/index-preparation.js +91 -26
- package/dist/src/artifacts/index-reader.js +44 -0
- package/dist/src/artifacts/index-refresh.js +130 -56
- package/dist/src/artifacts/index-status.js +20 -5
- package/dist/src/artifacts/index-worker.mjs +9 -7
- 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/cli/handlers/decision.js +19 -1
- 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/local-executor.js +5 -3
- 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 +40 -6
- package/dist/src/cli/handlers/setup.js +428 -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 +279 -93
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/prompt-utils.js +8 -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/native-sandbox-warnings.js +25 -0
- package/dist/src/contributors/validation.js +6 -5
- package/dist/src/decision/admission.js +7 -3
- package/dist/src/decision/benchmark.js +469 -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 +18 -15
- package/dist/src/decision/qualification/load-staged.js +539 -0
- package/dist/src/decision/qualification/load.js +109 -33
- package/dist/src/decision/qualification/runner.js +5 -2
- 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 +233 -0
- package/dist/src/discovery/plan-apply.js +571 -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/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-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/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/mcp/cli.mjs +5 -6
- package/dist/src/mcp/config-file.mjs +6 -2
- package/dist/src/mcp/config-layers.mjs +23 -7
- 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 +17 -4
- 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/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/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 +7 -0
- package/dist/src/models/worker-installation.mjs +169 -37
- package/dist/src/models/wrapper-route.js +1 -1
- package/dist/src/output-modes/registry.js +8 -7
- package/dist/src/plugin/metadata-validator.js +1 -1
- 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 +114 -15
- package/dist/src/sessions/adapters/claude.js +19 -18
- package/dist/src/sessions/adapters/codex.js +33 -19
- 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/batch-contracts.js +1 -1
- package/dist/src/sessions/batch-import.js +202 -2
- package/dist/src/sessions/candidates.js +7 -6
- package/dist/src/sessions/contracts.js +73 -54
- package/dist/src/sessions/coverage-preflight.js +26 -1
- package/dist/src/sessions/output-registration.js +11 -10
- package/dist/src/sessions/policy.js +18 -7
- package/dist/src/sessions/refresh.js +2 -3
- package/dist/src/sessions/repository.js +4 -2
- package/dist/src/sessions/workspace-discovery.js +92 -26
- package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +42 -6
- package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +106 -10
- package/dist/src/update/checker.d.mts +4 -0
- package/dist/src/update/checker.mjs +14 -0
- 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 +19 -13
- package/tools/agents/providers/claude.mjs +1 -1
- package/tools/agents/providers/codex.mjs +50 -56
- package/tools/agents/providers/copilot.mjs +8 -2
- 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 +7 -13
- package/tools/agents/providers/muse-hooks.mjs +21 -14
- package/tools/agents/providers/muse.mjs +7 -2
- package/tools/agents/providers/omp.mjs +28 -4
- 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 +263 -71
- package/tools/agents/providers/windsurf.mjs +7 -0
- 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/muse-adapter.mjs +12 -2
- 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 +7 -4
- package/tools/ralph-external/session-launcher.mjs +280 -32
- package/tools/skills/deploy-skills-codex.mjs +11 -7
- package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
|
@@ -2,12 +2,25 @@ import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
|
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
3
|
import { emptyConfig, FORCE_PUSH_POLICY_ALIAS_NOTE, normalizeForcePushPolicy as normalizeForcePushPolicyShared, getConfigPath, getProjectDir, readAiwgConfig, resolveRemoteProvider, VALID_PROVIDERS, writeAiwgConfig, } from '../../config/aiwg-config.js';
|
|
4
4
|
import { AiwgError, EXIT_CODES } from '../errors.js';
|
|
5
|
-
import { askChoice, askConsent, askString, askYesNo, createPromptInterface } from '../prompt-utils.js';
|
|
5
|
+
import { askChoice, askConsent, askString, askYesNo, createPromptInterface, createSecretPromptInterface } from '../prompt-utils.js';
|
|
6
6
|
import * as ui from '../ui.js';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { validateJevOptions } from './guided-setup.js';
|
|
9
|
+
import { validateParallelismConfig } from '../../config/aiwg-config.js';
|
|
10
|
+
import { readFile } from 'node:fs/promises';
|
|
11
|
+
import { discoverInventory } from '../../discovery/index.js';
|
|
12
|
+
import { applyGuidedPlan, configPathFor, credentialReferenceFor, defaultPlanReviewIO, projectRootForWorkspace, renderHumanReview, reviewGuidedPlan, } from '../../discovery/plan-apply.js';
|
|
13
|
+
import { readSetupPlan, writeSetupPlan } from '../../discovery/plan-store.js';
|
|
14
|
+
import { workspaceSlug } from '../../discovery/interview.js';
|
|
15
|
+
import { registerProjectMemory } from '../../memory/project-registry.js';
|
|
16
|
+
import { verifyProviderDeployment } from '../services/deployment-verification.js';
|
|
7
17
|
import { projectAiwgPath } from '../../config/project-artifacts.js';
|
|
18
|
+
import { useHandler } from './use.js';
|
|
8
19
|
import { normalizeProviderDefinitionId } from '../../providers/provider-definitions.js';
|
|
9
20
|
import { isInteractive } from '../env.js';
|
|
10
21
|
import { requireFlagValue as strictFlagValue, MissingFlagValueError } from '../flag-parsing.js';
|
|
22
|
+
import { parseGuidedSetupFlags, runGuidedSetup } from './guided-setup.js';
|
|
23
|
+
import { DEFAULT_SCRIPTS, parseInitArgInputs } from './init.js';
|
|
11
24
|
import { detectInstalledWorkerClis, evaluateWorkerDispatchOffer, workerDispatchOfferHintWithArg, workerDispatchOfferQuestion, WORKER_DISPATCH_ARG_VALUES, } from '../../models/worker-dispatch-offer.js';
|
|
12
25
|
const DELIVERY_MODES = ['pr-required', 'feature-branch', 'direct'];
|
|
13
26
|
const FORCE_PUSH_POLICIES = ['never', 'own-branch-only', 'allowed'];
|
|
@@ -470,6 +483,313 @@ function printPlan(plan) {
|
|
|
470
483
|
ui.warn(warning);
|
|
471
484
|
}
|
|
472
485
|
}
|
|
486
|
+
// ---------------------------------------------------------------------------
|
|
487
|
+
// Guided plan review and apply (#3449 A4).
|
|
488
|
+
//
|
|
489
|
+
// `--review` renders a saved guided plan per workspace and provider without
|
|
490
|
+
// writing anything. `--apply` executes `remainingPlanActions` in dependency
|
|
491
|
+
// order through the existing owners: project policy through
|
|
492
|
+
// `buildSetupProjectPlan` + `writeAiwgConfig` (the setup/init config owners),
|
|
493
|
+
// framework deployment through the `use` handler (consuming its
|
|
494
|
+
// `aiwg.use.result.v1` verification), and project registration through the
|
|
495
|
+
// project registry owner. Nothing here re-implements those paths.
|
|
496
|
+
// ---------------------------------------------------------------------------
|
|
497
|
+
/** Legacy policy flags: when any is present, `--dry-run` keeps its legacy preview meaning. */
|
|
498
|
+
const LEGACY_SETUP_POLICY_FLAGS = [
|
|
499
|
+
'--primary', '--issue-tracker', '--customer-issue-tracker', '--ci',
|
|
500
|
+
'--issue-provider', '--customer-issue-provider', '--delivery-mode', '--default-branch',
|
|
501
|
+
'--require-ci-green', '--auto-close-issues', '--issue-comment-on-cycle', '--force-push-policy',
|
|
502
|
+
'--require-signed-commits', '--committer-name', '--committer-email', '--signing-format',
|
|
503
|
+
'--signing-key', '--signing-enforce', '--tracker-actor-login', '--tracker-actor-via',
|
|
504
|
+
'--customer-tracker-actor-login', '--customer-tracker-actor-via', '--worker-dispatch', '--providers',
|
|
505
|
+
];
|
|
506
|
+
function hasLegacySetupPolicyFlags(args) {
|
|
507
|
+
return LEGACY_SETUP_POLICY_FLAGS.some(flag => args.includes(flag) || args.some(arg => arg.startsWith(`${flag}=`)));
|
|
508
|
+
}
|
|
509
|
+
const GUIDED_INTENT_FRAMEWORK = {
|
|
510
|
+
code: 'sdlc', docs: 'writing', ops: 'ops', mixed: 'sdlc',
|
|
511
|
+
};
|
|
512
|
+
function guidedAnswer(plan, workspace, block, topic, provider = 'shared') {
|
|
513
|
+
const projectRoot = projectRootForWorkspace(plan, workspace) ?? workspace;
|
|
514
|
+
return plan.answers[`${block}.${topic}.ws-${workspaceSlug(projectRoot)}.${provider}`]?.value;
|
|
515
|
+
}
|
|
516
|
+
function guidedProviders(plan, workspace) {
|
|
517
|
+
const projectRoot = projectRootForWorkspace(plan, workspace) ?? workspace;
|
|
518
|
+
const prefix = `tools.provider.ws-${workspaceSlug(projectRoot)}.`;
|
|
519
|
+
return Object.entries(plan.answers)
|
|
520
|
+
.filter(([id, answer]) => id.startsWith(prefix) && answer.value === true)
|
|
521
|
+
.map(([id]) => id.slice(prefix.length))
|
|
522
|
+
.sort();
|
|
523
|
+
}
|
|
524
|
+
async function writeGuidedProjectConfig(plan, action) {
|
|
525
|
+
const asRecord = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
526
|
+
const delivery = asRecord(guidedAnswer(plan, action.workspace, 'capabilities', 'delivery'));
|
|
527
|
+
const signing = guidedAnswer(plan, action.workspace, 'capabilities', 'signing');
|
|
528
|
+
const tracker = asRecord(guidedAnswer(plan, action.workspace, 'capabilities', 'tracker'));
|
|
529
|
+
const team = guidedAnswer(plan, action.workspace, 'intent', 'team');
|
|
530
|
+
const string = (value) => typeof value === 'string' ? value : undefined;
|
|
531
|
+
const boolean = (value) => typeof value === 'boolean' ? value : undefined;
|
|
532
|
+
const built = await buildSetupProjectPlan({
|
|
533
|
+
projectDir: action.workspace,
|
|
534
|
+
providers: guidedProviders(plan, action.workspace),
|
|
535
|
+
...(string(delivery.mode) ? { deliveryMode: string(delivery.mode) } : {}),
|
|
536
|
+
...(string(delivery.default_branch) ? { defaultBranch: string(delivery.default_branch) } : {}),
|
|
537
|
+
...(boolean(delivery.require_ci_green) ? { requireCiGreen: boolean(delivery.require_ci_green) } : {}),
|
|
538
|
+
...(boolean(delivery.auto_close_issues) ? { autoCloseIssues: boolean(delivery.auto_close_issues) } : {}),
|
|
539
|
+
...(boolean(delivery.issue_comment_on_cycle) ? { issueCommentOnCycle: boolean(delivery.issue_comment_on_cycle) } : {}),
|
|
540
|
+
...(!delivery.force_push_policy && team === 'shared' ? { forcePushPolicy: 'never' } : {}),
|
|
541
|
+
...(string(delivery.force_push_policy) ? { forcePushPolicy: string(delivery.force_push_policy) } : {}),
|
|
542
|
+
...(signing === false ? { requireSignedCommits: false } : {}),
|
|
543
|
+
...(typeof signing?.required === 'boolean'
|
|
544
|
+
? { requireSignedCommits: signing.required } : {}),
|
|
545
|
+
...(string(signing?.format) ? { signingFormat: string(signing.format) } : {}),
|
|
546
|
+
...(string(signing?.key) ? { signingKey: string(signing.key) } : {}),
|
|
547
|
+
...(string(signing?.enforce) ? { signingEnforce: string(signing.enforce) } : {}),
|
|
548
|
+
...(string(tracker.primary) ? { primary: string(tracker.primary) } : {}),
|
|
549
|
+
...(string(tracker.issue_tracker) ? { issueTracker: string(tracker.issue_tracker) } : {}),
|
|
550
|
+
...(string(tracker.issue_provider) ? { issueProvider: string(tracker.issue_provider) } : {}),
|
|
551
|
+
...(string(tracker.customer_issue_tracker) ? { customerIssueTracker: string(tracker.customer_issue_tracker) } : {}),
|
|
552
|
+
...(string(tracker.customer_issue_provider) ? { customerIssueProvider: string(tracker.customer_issue_provider) } : {}),
|
|
553
|
+
...(string(tracker.ci) ? { ci: string(tracker.ci) } : {}),
|
|
554
|
+
...(string(tracker.tracker_actor_login) ? { trackerActorLogin: string(tracker.tracker_actor_login) } : {}),
|
|
555
|
+
...(string(tracker.tracker_actor_via) ? { trackerActorVia: string(tracker.tracker_actor_via) } : {}),
|
|
556
|
+
...(string(tracker.customer_tracker_actor_login) ? { customerTrackerActorLogin: string(tracker.customer_tracker_actor_login) } : {}),
|
|
557
|
+
...(string(tracker.customer_tracker_actor_via) ? { customerTrackerActorVia: string(tracker.customer_tracker_actor_via) } : {}),
|
|
558
|
+
});
|
|
559
|
+
// Interview script answers merge on top of the preserved config: init owns
|
|
560
|
+
// the default-script set, setup owns the policy merge above.
|
|
561
|
+
const scripts = asRecord(guidedAnswer(plan, action.workspace, 'tools', 'scripts'));
|
|
562
|
+
let next = built.next;
|
|
563
|
+
if (Object.keys(scripts).length > 0) {
|
|
564
|
+
const merged = { ...(next.scripts ?? {}) };
|
|
565
|
+
if (scripts.defaultScripts === true) {
|
|
566
|
+
for (const [name, command] of Object.entries(DEFAULT_SCRIPTS)) {
|
|
567
|
+
if (!merged[name])
|
|
568
|
+
merged[name] = command;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const custom = scripts.scripts;
|
|
572
|
+
if (custom && typeof custom === 'object' && !Array.isArray(custom))
|
|
573
|
+
Object.assign(merged, custom);
|
|
574
|
+
next = { ...next, scripts: merged };
|
|
575
|
+
}
|
|
576
|
+
await writeAiwgConfig(action.workspace, next);
|
|
577
|
+
return `merged guided policy into ${getConfigPath(action.workspace)}`;
|
|
578
|
+
}
|
|
579
|
+
// configure-provider writes no provider files: provider config files belong
|
|
580
|
+
// to the existing deployment owner (`aiwg use`, the `deploy-framework`
|
|
581
|
+
// action), which `PLAN_APPLY_KIND_ORDER` runs after every
|
|
582
|
+
// configure-provider. An existing file is user-authored and left untouched
|
|
583
|
+
// (applied, preserved); a missing one defers to deploy-framework. The
|
|
584
|
+
// credential reference was already resolved at apply time by the apply engine
|
|
585
|
+
// and is never written anywhere.
|
|
586
|
+
async function writeGuidedProviderConfig(plan, action, configPath) {
|
|
587
|
+
if (!configPath)
|
|
588
|
+
throw new Error(`Cannot apply '${action.id}': no provider config path was recorded for ${action.provider}.`);
|
|
589
|
+
try {
|
|
590
|
+
await readFile(configPath, 'utf8');
|
|
591
|
+
return `preserved existing provider file ${configPath} (left untouched)`;
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
if (error?.code !== 'ENOENT')
|
|
595
|
+
throw error;
|
|
596
|
+
}
|
|
597
|
+
const deploy = plan.actions.find(other => other.workspace === action.workspace &&
|
|
598
|
+
other.provider === action.provider && other.kind === 'deploy-framework');
|
|
599
|
+
const owner = deploy ? `'${deploy.id}'` : 'deploy-framework';
|
|
600
|
+
return `provider files for ${action.provider} in ${action.workspace} are owned by ${owner}; nothing written`;
|
|
601
|
+
}
|
|
602
|
+
async function verifyGuidedProvider(plan, fresh, action, reference) {
|
|
603
|
+
const executable = action.dependsOn.find(fact => fact.kind === 'tool')?.executable ?? action.provider;
|
|
604
|
+
const tool = fresh.tools.find(entry => entry.executable === executable);
|
|
605
|
+
if (!tool || tool.installed !== 'yes') {
|
|
606
|
+
throw new Error(`Provider tool '${executable}' is not installed; install it and re-run discovery before verifying '${action.id}'.`);
|
|
607
|
+
}
|
|
608
|
+
const configPath = action.dependsOn.find(fact => fact.kind === 'path')?.path
|
|
609
|
+
?? configPathFor(plan, action.workspace, action.provider);
|
|
610
|
+
let configNote = 'no config path recorded';
|
|
611
|
+
if (configPath) {
|
|
612
|
+
try {
|
|
613
|
+
await readFile(configPath, 'utf8');
|
|
614
|
+
configNote = `config present at ${configPath}`;
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
configNote = `config missing at ${configPath}`;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const version = tool.version ? `version ${tool.version}` : 'version unknown';
|
|
621
|
+
// Hyphenated wording: receipt messages persist through the plan store,
|
|
622
|
+
// whose redaction gate rejects `credential <value>` phrasing.
|
|
623
|
+
const credential = reference ? `credential-ref ${reference.source}:${reference.name} readable at apply time` : 'no credential-reference recorded';
|
|
624
|
+
return `verified ${action.provider} (${executable} installed, ${version}; ${configNote}; ${credential}; read-back only)`;
|
|
625
|
+
}
|
|
626
|
+
async function deployGuidedFramework(plan, action, frameworkRoot, signal) {
|
|
627
|
+
const intent = guidedAnswer(plan, action.workspace, 'intent', 'use');
|
|
628
|
+
const framework = GUIDED_INTENT_FRAMEWORK[typeof intent === 'string' ? intent : 'code'] ?? 'sdlc';
|
|
629
|
+
const args = [framework, '--provider', action.provider, ...(action.scope === 'user' ? ['--scope', 'user'] : [])];
|
|
630
|
+
const deployed = await useHandler.execute({ cwd: action.workspace, args, rawArgs: args, frameworkRoot, dryRun: false, signal });
|
|
631
|
+
if (deployed.exitCode !== 0) {
|
|
632
|
+
throw new Error(`aiwg use ${framework} --provider ${action.provider} failed${deployed.message ? `: ${deployed.message}` : ` (exit ${deployed.exitCode})`}.`);
|
|
633
|
+
}
|
|
634
|
+
// Consume the aiwg.use.result.v1 verification for this provider and scope.
|
|
635
|
+
const verification = await verifyProviderDeployment({
|
|
636
|
+
projectRoot: action.workspace,
|
|
637
|
+
frameworkRoot,
|
|
638
|
+
provider: action.provider,
|
|
639
|
+
scope: action.scope,
|
|
640
|
+
requestedBundles: [framework],
|
|
641
|
+
});
|
|
642
|
+
const blocking = verification.findings.filter(finding => finding.severity === 'blocking');
|
|
643
|
+
if (verification.outcome === 'failed' || blocking.length > 0) {
|
|
644
|
+
throw new Error(`Deployment verification failed for ${action.provider}: ${blocking[0]?.message ?? verification.outcome}.`);
|
|
645
|
+
}
|
|
646
|
+
return `deployed ${framework} for ${action.provider} (verification: ${verification.outcome})`;
|
|
647
|
+
}
|
|
648
|
+
function defaultGuidedApplyIO(plan, fresh, ctx, projectDir, frameworkRoot, canPrompt) {
|
|
649
|
+
return {
|
|
650
|
+
...defaultPlanReviewIO(),
|
|
651
|
+
readEnv: name => process.env[name],
|
|
652
|
+
// Headless apply can consume redirected stdin. Interactive apply uses the prompt below.
|
|
653
|
+
...(!canPrompt ? { readStdinCredential: async () => {
|
|
654
|
+
const chunks = [];
|
|
655
|
+
for await (const chunk of process.stdin)
|
|
656
|
+
chunks.push(Buffer.from(chunk));
|
|
657
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
658
|
+
} } : {}),
|
|
659
|
+
readCredentialFile: async (path) => readFile(path, 'utf8'),
|
|
660
|
+
// stdin-at-apply prompts for the value without echoing it anywhere: the
|
|
661
|
+
// label names the consumer only, and the value never reaches output.
|
|
662
|
+
...(canPrompt ? { promptCredential: async (label) => {
|
|
663
|
+
const rl = createSecretPromptInterface();
|
|
664
|
+
try {
|
|
665
|
+
process.stdout.write(label);
|
|
666
|
+
return await askString(rl, '', '', ctx.signal);
|
|
667
|
+
}
|
|
668
|
+
finally {
|
|
669
|
+
rl.close();
|
|
670
|
+
}
|
|
671
|
+
} } : {}),
|
|
672
|
+
now: () => new Date().toISOString(),
|
|
673
|
+
save: async (next) => {
|
|
674
|
+
await writeSetupPlan(projectDir, next);
|
|
675
|
+
},
|
|
676
|
+
owners: {
|
|
677
|
+
configureJev: async ({ action, token, options, credential }) => {
|
|
678
|
+
if (options != null)
|
|
679
|
+
validateJevOptions(options);
|
|
680
|
+
const settings = (options ?? {});
|
|
681
|
+
const args = ['decision', 'setup', 'jev', ...(settings.remove === true ? ['--remove'] : ['--token-stdin']),
|
|
682
|
+
...(typeof settings.region === 'string' ? ['--region', settings.region] : []),
|
|
683
|
+
...(typeof settings.endpoint === 'string' ? ['--endpoint', settings.endpoint] : []),
|
|
684
|
+
...(settings.verify === true ? ['--verify'] : [])];
|
|
685
|
+
const environment = { ...process.env };
|
|
686
|
+
delete environment.JEV_API_KEY;
|
|
687
|
+
delete environment.AIWG_DECISION_JEV_API_KEY;
|
|
688
|
+
if (credential?.source === 'env')
|
|
689
|
+
delete environment[credential.name];
|
|
690
|
+
const child = boundedSpawnSync(process.execPath, [join(frameworkRoot, 'bin', 'aiwg.mjs'), ...args], 'network', {
|
|
691
|
+
cwd: action.workspace, env: environment, input: token ? `${token}\n` : '', encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
692
|
+
maxBuffer: 1024 * 1024,
|
|
693
|
+
});
|
|
694
|
+
if (child.error || child.status !== 0)
|
|
695
|
+
throw new Error('Jev setup failed.');
|
|
696
|
+
if (settings.verify === true && settings.remove !== true && JSON.parse(child.stdout).verified !== true) {
|
|
697
|
+
throw new Error('Jev verification incomplete.');
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
configureWorkerDispatch: async ({ action }) => {
|
|
701
|
+
const built = await buildSetupProjectPlan({ projectDir: action.workspace, workerDispatchChoice: 'enable' });
|
|
702
|
+
const settings = guidedAnswer(plan, action.workspace, 'capabilities', 'worker-config');
|
|
703
|
+
let next = built.next;
|
|
704
|
+
if (settings && typeof settings === 'object' && !Array.isArray(settings)) {
|
|
705
|
+
if (Object.keys(settings).some(key => !['clis', 'groups', 'admission', 'budgets'].includes(key)))
|
|
706
|
+
throw new Error('Invalid worker settings.');
|
|
707
|
+
next = { ...next, parallelism: { ...next.parallelism, ...settings } };
|
|
708
|
+
}
|
|
709
|
+
if (validateParallelismConfig(next.parallelism).length)
|
|
710
|
+
throw new Error('Invalid worker settings.');
|
|
711
|
+
await writeAiwgConfig(action.workspace, next);
|
|
712
|
+
const child = boundedSpawnSync(process.execPath, [join(frameworkRoot, 'bin', 'aiwg.mjs'),
|
|
713
|
+
'doctor', '--probe-workers', '--qualify', '--yes', '--json'], 'long', {
|
|
714
|
+
cwd: action.workspace, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 1024 * 1024,
|
|
715
|
+
});
|
|
716
|
+
if (child.error || child.status !== 0)
|
|
717
|
+
throw new Error('Worker qualification failed.');
|
|
718
|
+
const report = JSON.parse(child.stdout);
|
|
719
|
+
if (report.qualification?.status !== 'recorded' || !report.qualification?.results?.length || report.qualification.results.some((row) => row.status !== 'qualified'))
|
|
720
|
+
throw new Error('Worker qualification incomplete.');
|
|
721
|
+
},
|
|
722
|
+
configureProvider: async ({ action, configPath }) => writeGuidedProviderConfig(plan, action, configPath),
|
|
723
|
+
verifyProvider: async ({ action }) => verifyGuidedProvider(plan, fresh, action, credentialReferenceFor(plan, action.workspace, action.provider)),
|
|
724
|
+
writeProjectConfig: async ({ action }) => writeGuidedProjectConfig(plan, action),
|
|
725
|
+
deployFramework: async ({ action }) => deployGuidedFramework(plan, action, frameworkRoot, ctx.signal),
|
|
726
|
+
registerProject: async ({ action }) => {
|
|
727
|
+
const entry = await registerProjectMemory({ workspaceRoot: action.workspace });
|
|
728
|
+
return `registered project '${entry.id}'`;
|
|
729
|
+
},
|
|
730
|
+
recordDecline: async ({ action }) => {
|
|
731
|
+
const built = await buildSetupProjectPlan({ projectDir: action.workspace, workerDispatchChoice: 'decline' });
|
|
732
|
+
await writeAiwgConfig(action.workspace, built.next);
|
|
733
|
+
return `recorded worker-dispatch decline in ${getConfigPath(action.workspace)}`;
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
async function loadGuidedPlan(projectDir, planId) {
|
|
739
|
+
const fresh = await discoverInventory({ workspaceRoot: projectDir });
|
|
740
|
+
const workspaceRoot = fresh.projects.find(project => project.sources.includes('workspace'))?.root ?? projectDir;
|
|
741
|
+
const id = planId ?? `setup-${workspaceSlug(workspaceRoot)}`;
|
|
742
|
+
let plan;
|
|
743
|
+
try {
|
|
744
|
+
plan = await readSetupPlan(projectDir, id);
|
|
745
|
+
}
|
|
746
|
+
catch {
|
|
747
|
+
throw new AiwgError({
|
|
748
|
+
code: 'ERR_SETUP_PLAN_NOT_FOUND',
|
|
749
|
+
message: `No guided setup plan '${id}' for ${projectDir}.`,
|
|
750
|
+
hint: 'Record one first with `aiwg setup --scope project|user` (or `aiwg init --scope ...`).',
|
|
751
|
+
exitCode: EXIT_CODES.GENERAL,
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
return { plan, fresh };
|
|
755
|
+
}
|
|
756
|
+
async function runGuidedReview(_ctx, projectDir, planId, json) {
|
|
757
|
+
const { plan, fresh } = await loadGuidedPlan(projectDir, planId);
|
|
758
|
+
// Review is read-only: findings come from revalidation plus filesystem
|
|
759
|
+
// reads, and nothing here writes (not even receipts).
|
|
760
|
+
const review = await reviewGuidedPlan(plan, fresh, defaultPlanReviewIO());
|
|
761
|
+
if (json)
|
|
762
|
+
return { exitCode: 0, rawOutput: true, message: JSON.stringify(review, null, 2) };
|
|
763
|
+
return { exitCode: 0, message: renderHumanReview(review) };
|
|
764
|
+
}
|
|
765
|
+
async function runGuidedApply(ctx, projectDir, planId, options) {
|
|
766
|
+
const { plan, fresh } = await loadGuidedPlan(projectDir, planId);
|
|
767
|
+
// The existing --yes/confirmation gate and TTY rules: a write that can
|
|
768
|
+
// never be confirmed is refused before any owner runs.
|
|
769
|
+
if (!options.yes && !options.canPrompt) {
|
|
770
|
+
throw new AiwgError({
|
|
771
|
+
code: 'ERR_NON_INTERACTIVE_CONFIRMATION_REQUIRED',
|
|
772
|
+
message: 'Refusing to apply the guided setup plan in a non-interactive context without --yes.',
|
|
773
|
+
hint: 'Re-run with `aiwg setup --apply --yes` or use `--review`/`--dry-run` to preview only.',
|
|
774
|
+
exitCode: EXIT_CODES.USAGE,
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
if (!options.yes && options.canPrompt) {
|
|
778
|
+
const rl = createPromptInterface();
|
|
779
|
+
try {
|
|
780
|
+
const confirmed = await askYesNo(rl, `Apply the guided setup plan '${plan.id}'? [y/N]: `, false, ctx.signal);
|
|
781
|
+
if (!confirmed)
|
|
782
|
+
return { exitCode: 0, message: 'Guided setup apply cancelled.' };
|
|
783
|
+
}
|
|
784
|
+
finally {
|
|
785
|
+
rl.close();
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
const { result } = await applyGuidedPlan(plan, fresh, defaultGuidedApplyIO(plan, fresh, ctx, projectDir, options.frameworkRoot, options.canPrompt));
|
|
789
|
+
if (options.json)
|
|
790
|
+
return { exitCode: result.exitCode, rawOutput: true, message: JSON.stringify(result, null, 2) };
|
|
791
|
+
return { exitCode: result.exitCode, message: result.message };
|
|
792
|
+
}
|
|
473
793
|
export const setupHandler = {
|
|
474
794
|
id: 'setup',
|
|
475
795
|
name: 'Setup',
|
|
@@ -484,8 +804,21 @@ export const setupHandler = {
|
|
|
484
804
|
|
|
485
805
|
Options:
|
|
486
806
|
--target <dir> Project directory
|
|
807
|
+
--scope project|user Record guided plan only (no config writes)
|
|
808
|
+
--force Recreate guided interview answers
|
|
809
|
+
--workspace <root> Observed workspace to configure (repeatable)
|
|
810
|
+
--monorepo root|package Monorepo target selection
|
|
811
|
+
--package <root> Observed package root (with --monorepo package)
|
|
812
|
+
--scan-roots <dir> Opt in to bounded discovery (repeatable)
|
|
813
|
+
--intent code|docs|ops|mixed Workspace usage intent
|
|
814
|
+
--jev enable|decline|defer Optional external decision service (default off)
|
|
815
|
+
--region <r> --endpoint <url> --verify --remove --token-stdin Existing Jev options
|
|
816
|
+
--credential <provider>=env:NAME|file:PATH|stdin Credential reference only
|
|
487
817
|
--yes Confirm writes without prompting
|
|
488
818
|
--dry-run Preview the setup plan
|
|
819
|
+
--review Render the saved guided plan per workspace/provider (writes nothing)
|
|
820
|
+
--apply Apply the saved guided plan through the existing owners
|
|
821
|
+
--plan <id> Guided plan ID (default: setup plan for the target directory)
|
|
489
822
|
--non-interactive Disable prompts
|
|
490
823
|
--json Emit the plan as JSON
|
|
491
824
|
--primary <remote> Primary repository remote
|
|
@@ -524,12 +857,106 @@ Options:
|
|
|
524
857
|
};
|
|
525
858
|
}
|
|
526
859
|
const args = subcommand === 'project' ? ctx.args.slice(1) : ctx.args;
|
|
860
|
+
let guidedFlags;
|
|
861
|
+
try {
|
|
862
|
+
guidedFlags = parseGuidedSetupFlags(args, 'setup');
|
|
863
|
+
}
|
|
864
|
+
catch (error) {
|
|
865
|
+
throw new AiwgError({ code: 'ERR_INVALID_VALUE', exitCode: EXIT_CODES.USAGE,
|
|
866
|
+
message: error instanceof Error ? error.message : String(error) });
|
|
867
|
+
}
|
|
527
868
|
const options = parseSetupProjectOptions({ ...ctx, args });
|
|
528
869
|
// #3062 review item 12: the one shared interactive decision (TTY, TERM,
|
|
529
870
|
// CI, DEBIAN_FRONTEND) — this used to check only `process.stdin.isTTY &&
|
|
530
871
|
// process.stdout.isTTY`, so a CI job with a pseudo-terminal could still
|
|
531
872
|
// reach these prompts.
|
|
532
873
|
const canPrompt = isInteractive(options.nonInteractive);
|
|
874
|
+
// #3449 A4: operate on a saved guided plan. `--review` renders it
|
|
875
|
+
// read-only; `--apply` executes it through the existing owners behind the
|
|
876
|
+
// same --yes/confirmation gate as legacy writes. `--dry-run` on a guided
|
|
877
|
+
// plan (no planning or policy flags) previews the same review and writes
|
|
878
|
+
// nothing. `--review` and `--apply` together is a usage error.
|
|
879
|
+
const wantsReview = boolFlag(args, '--review');
|
|
880
|
+
const wantsApply = boolFlag(args, '--apply');
|
|
881
|
+
if (wantsReview && wantsApply) {
|
|
882
|
+
throw new AiwgError({
|
|
883
|
+
code: 'ERR_USAGE_CONFLICTING_FLAGS',
|
|
884
|
+
message: 'Cannot combine --review with --apply; use --review to preview or --apply to write.',
|
|
885
|
+
exitCode: EXIT_CODES.USAGE,
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
if (wantsReview || wantsApply || (options.dryRun && !guidedFlags.enabled && !hasLegacySetupPolicyFlags(args))) {
|
|
889
|
+
let planId;
|
|
890
|
+
try {
|
|
891
|
+
planId = flagValue(args, '--plan');
|
|
892
|
+
}
|
|
893
|
+
catch (error) {
|
|
894
|
+
if (error instanceof MissingFlagValueError) {
|
|
895
|
+
throw new AiwgError({ code: 'ERR_USAGE_MISSING_FLAG_VALUE', message: `${error.message}.`, exitCode: EXIT_CODES.USAGE });
|
|
896
|
+
}
|
|
897
|
+
throw error;
|
|
898
|
+
}
|
|
899
|
+
// A bare `--dry-run` without a saved plan keeps its legacy preview
|
|
900
|
+
// meaning; only an existing plan switches it to the guided review.
|
|
901
|
+
if (!wantsReview && !wantsApply && options.dryRun) {
|
|
902
|
+
try {
|
|
903
|
+
return await runGuidedReview({ ...ctx, args }, options.projectDir, planId, options.json ?? false);
|
|
904
|
+
}
|
|
905
|
+
catch (error) {
|
|
906
|
+
if (error instanceof AiwgError && error.code === 'ERR_SETUP_PLAN_NOT_FOUND') {
|
|
907
|
+
// Fall through to the legacy setup preview below.
|
|
908
|
+
}
|
|
909
|
+
else
|
|
910
|
+
throw error;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
else if (wantsReview || options.dryRun) {
|
|
914
|
+
return runGuidedReview({ ...ctx, args }, options.projectDir, planId, options.json ?? false);
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
return runGuidedApply({ ...ctx, args }, options.projectDir, planId, { yes: options.yes ?? false, json: options.json ?? false, canPrompt, frameworkRoot: ctx.frameworkRoot });
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (guidedFlags.enabled) {
|
|
921
|
+
const target = flagValue(args, '--target');
|
|
922
|
+
const projectDir = target ?? options.projectDir;
|
|
923
|
+
let inputs;
|
|
924
|
+
try {
|
|
925
|
+
inputs = parseInitArgInputs(args);
|
|
926
|
+
}
|
|
927
|
+
catch (error) {
|
|
928
|
+
throw new AiwgError({ code: 'ERR_INVALID_VALUE', exitCode: EXIT_CODES.USAGE,
|
|
929
|
+
message: error instanceof Error ? error.message : String(error) });
|
|
930
|
+
}
|
|
931
|
+
const selected = (entries) => {
|
|
932
|
+
const present = entries.filter((entry) => entry[1] !== undefined);
|
|
933
|
+
return present.length ? Object.fromEntries(present) : undefined;
|
|
934
|
+
};
|
|
935
|
+
const tracker = selected([
|
|
936
|
+
['primary', options.primary], ['issue_tracker', options.issueTracker], ['issue_provider', options.issueProvider],
|
|
937
|
+
['customer_issue_tracker', options.customerIssueTracker], ['customer_issue_provider', options.customerIssueProvider],
|
|
938
|
+
['ci', options.ci], ['tracker_actor_login', options.trackerActorLogin], ['tracker_actor_via', options.trackerActorVia],
|
|
939
|
+
['customer_tracker_actor_login', options.customerTrackerActorLogin], ['customer_tracker_actor_via', options.customerTrackerActorVia],
|
|
940
|
+
]);
|
|
941
|
+
const delivery = selected([
|
|
942
|
+
['mode', options.deliveryMode], ['default_branch', options.defaultBranch], ['require_ci_green', options.requireCiGreen],
|
|
943
|
+
['auto_close_issues', options.autoCloseIssues], ['issue_comment_on_cycle', options.issueCommentOnCycle], ['force_push_policy', options.forcePushPolicy],
|
|
944
|
+
]);
|
|
945
|
+
const signing = options.requireSignedCommits === false ? false : selected([
|
|
946
|
+
['required', options.requireSignedCommits], ['format', options.signingFormat], ['key', options.signingKey], ['enforce', options.signingEnforce],
|
|
947
|
+
]);
|
|
948
|
+
const hasScripts = inputs.defaultScriptsArg !== undefined || inputs.customScriptsArg !== undefined || Object.keys(inputs.scriptPairs).length > 0;
|
|
949
|
+
const remotes = detectGitRemotes(projectDir);
|
|
950
|
+
const primary = options.primary ?? choosePrimary(remotes);
|
|
951
|
+
return runGuidedSetup({ ...ctx, args }, guidedFlags, { providers: inputs.providersArg,
|
|
952
|
+
workerDispatch: options.workerDispatchChoice, tracker, delivery, signing,
|
|
953
|
+
team: options.forcePushPolicy === undefined ? undefined : options.forcePushPolicy === 'never' ? 'shared' : 'solo',
|
|
954
|
+
...(hasScripts ? { scripts: { defaultScripts: inputs.defaultScriptsArg ?? true,
|
|
955
|
+
customScripts: inputs.customScriptsArg ?? Object.keys(inputs.scriptPairs).length > 0, scripts: inputs.scriptPairs } } : {}),
|
|
956
|
+
}, canPrompt, projectDir, remotes.find(r => r.name === primary)?.provider === 'unknown', remotes.length ? { primary, issue_tracker: primary, ci: primary,
|
|
957
|
+
...(remotes.find(r => r.name === primary)?.provider !== 'unknown'
|
|
958
|
+
? { issue_provider: remotes.find(r => r.name === primary).provider } : {}) } : undefined);
|
|
959
|
+
}
|
|
533
960
|
// #3062 second review, finding 6: refuse before any child process runs
|
|
534
961
|
// — `buildSetupProjectPlan()` below makes four `git` calls on its own,
|
|
535
962
|
// 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
|
/**
|