@aiwg/cli 2026.10.5 → 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 +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/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-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/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 +1 -0
- 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 +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 +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
|
@@ -27,6 +27,7 @@ let fs;
|
|
|
27
27
|
try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
|
|
28
28
|
import path from 'path';
|
|
29
29
|
import {
|
|
30
|
+
withRuleDeployment,
|
|
30
31
|
ensureDir,
|
|
31
32
|
listMdFiles,
|
|
32
33
|
listMdFilesRecursive,
|
|
@@ -429,6 +430,7 @@ export function deploySkills(skillDirs, targetDir, opts) {
|
|
|
429
430
|
* Deploy rules to .github/instructions/ as .instructions.md files
|
|
430
431
|
*/
|
|
431
432
|
export function deployRules(ruleFiles, targetDir, opts) {
|
|
433
|
+
if (opts.ruleDelivery) return [];
|
|
432
434
|
const destDir = path.join(targetDir, paths.rules);
|
|
433
435
|
ensureDir(destDir, opts.dryRun);
|
|
434
436
|
cleanupOldRuleFiles(destDir, opts);
|
|
@@ -495,8 +497,8 @@ export function createCopilotInstructions(target, srcRoot, dryRun) {
|
|
|
495
497
|
export async function postDeploy(targetDir, opts) {
|
|
496
498
|
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
497
499
|
|
|
498
|
-
//
|
|
499
|
-
createCopilotInstructions(targetDir, opts.srcRoot, opts.dryRun);
|
|
500
|
+
// copilot-instructions.md is Copilot's context file: --no-context-files leaves it alone.
|
|
501
|
+
if (opts.contextFiles !== false) createCopilotInstructions(targetDir, opts.srcRoot, opts.dryRun);
|
|
500
502
|
}
|
|
501
503
|
|
|
502
504
|
// ============================================================================
|
|
@@ -517,6 +519,10 @@ export function getFileExtension(type) {
|
|
|
517
519
|
// ============================================================================
|
|
518
520
|
|
|
519
521
|
export async function deploy(opts) {
|
|
522
|
+
return withRuleDeployment(opts, { name, paths, kernelSkillsPath }, deploySelected);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function deploySelected(opts) {
|
|
520
526
|
const {
|
|
521
527
|
srcRoot,
|
|
522
528
|
target,
|
|
@@ -414,7 +414,7 @@ export async function postDeploy(targetDir, opts) {
|
|
|
414
414
|
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
415
415
|
|
|
416
416
|
if (opts.createAgentsMd) {
|
|
417
|
-
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
417
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
// PUW-019 (#1120): wire environment.json + worktrees.json templates.
|
|
@@ -46,7 +46,7 @@ export function deployCordisPatch(target, opts) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export async function postDeploy(target, opts) {
|
|
49
|
-
if (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)) createAgentsMd(target, opts.srcRoot, opts.dryRun);
|
|
49
|
+
if (opts.contextFiles !== false && (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly))) createAgentsMd(target, opts.srcRoot, opts.dryRun);
|
|
50
50
|
if (!opts.quiet) console.log('DeepSeek Harness: runtime credentials remain environment-only; use a reviewed ephemeral route patch.');
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -25,6 +25,7 @@ let fs;
|
|
|
25
25
|
try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
|
|
26
26
|
import path from 'path';
|
|
27
27
|
import {
|
|
28
|
+
withRuleDeployment,
|
|
28
29
|
ensureDir,
|
|
29
30
|
listMdFiles,
|
|
30
31
|
listMdFilesRecursive,
|
|
@@ -576,6 +577,7 @@ export function deploySkills(skillDirs, targetDir, opts) {
|
|
|
576
577
|
* Deploy rules to .factory/rules/
|
|
577
578
|
*/
|
|
578
579
|
export function deployRules(ruleFiles, targetDir, opts) {
|
|
580
|
+
if (opts.ruleDelivery) return [];
|
|
579
581
|
const destDir = path.join(targetDir, paths.rules);
|
|
580
582
|
ensureDir(destDir, opts.dryRun);
|
|
581
583
|
cleanupOldRuleFiles(destDir, opts);
|
|
@@ -816,7 +818,7 @@ export async function postDeploy(targetDir, opts) {
|
|
|
816
818
|
|
|
817
819
|
// Create/update AGENTS.md if requested
|
|
818
820
|
if (opts.createAgentsMd) {
|
|
819
|
-
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
821
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
820
822
|
}
|
|
821
823
|
}
|
|
822
824
|
|
|
@@ -945,6 +947,10 @@ export function getFileExtension(type) {
|
|
|
945
947
|
* Main deployment function for Factory provider
|
|
946
948
|
*/
|
|
947
949
|
export async function deploy(opts) {
|
|
950
|
+
return withRuleDeployment(opts, { name, paths, kernelSkillsPath, transformSkillMd: transformSkillFrontmatter }, deploySelected);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
async function deploySelected(opts) {
|
|
948
954
|
const {
|
|
949
955
|
srcRoot,
|
|
950
956
|
target,
|
|
@@ -16,6 +16,8 @@ import fs from 'node:fs';
|
|
|
16
16
|
import { createHash, randomUUID } from 'node:crypto';
|
|
17
17
|
import yaml from 'js-yaml';
|
|
18
18
|
import {
|
|
19
|
+
withRuleDeployment,
|
|
20
|
+
replaceBoundRulesBlock,
|
|
19
21
|
createAgentsMdFromTemplate,
|
|
20
22
|
deploySkillsWithKernelRouting,
|
|
21
23
|
collectFrameworkArtifacts,
|
|
@@ -224,7 +226,17 @@ function matchesSkillSource(source, target) {
|
|
|
224
226
|
if (relative.endsWith(path.sep)) return true;
|
|
225
227
|
let content = fs.readFileSync(path.join(source, relative), 'utf8');
|
|
226
228
|
if (relative === 'SKILL.md') content = injectPlatformInContent(content, 'grok-build');
|
|
227
|
-
|
|
229
|
+
const deployed = fs.readFileSync(path.join(target, relative), 'utf8');
|
|
230
|
+
if (content === deployed) return true;
|
|
231
|
+
if (relative !== 'SKILL.md' || !deployed.includes('<!-- AIWG:bound-rules:start -->')) return false;
|
|
232
|
+
// Activation projection appends generated bindings. Verify the complete
|
|
233
|
+
// deployed bytes against the receipt before comparing the authored body;
|
|
234
|
+
// an operator edit (including inside the binding block) must survive.
|
|
235
|
+
const receipt = JSON.parse(fs.readFileSync(path.join(target, '.aiwg-manifest.json'), 'utf8'));
|
|
236
|
+
const digest = `sha256:${createHash('sha256').update(deployed).digest('hex')}`;
|
|
237
|
+
return receipt.skillSource === path.resolve(source)
|
|
238
|
+
&& receipt.managed?.[relative]?.hash === digest
|
|
239
|
+
&& replaceBoundRulesBlock(content, []).trimEnd() === replaceBoundRulesBlock(deployed, []).trimEnd();
|
|
228
240
|
});
|
|
229
241
|
} catch { return false; }
|
|
230
242
|
}
|
|
@@ -326,7 +338,7 @@ export function uninstall(target, opts = {}) {
|
|
|
326
338
|
|
|
327
339
|
export async function postDeploy(target, opts = {}) {
|
|
328
340
|
if (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)) {
|
|
329
|
-
createAgentsMd(target, opts.srcRoot, opts.dryRun);
|
|
341
|
+
if (opts.contextFiles !== false) createAgentsMd(target, opts.srcRoot, opts.dryRun);
|
|
330
342
|
}
|
|
331
343
|
if (!opts.quiet) {
|
|
332
344
|
const home = resolveGrokHome(opts.env || process.env);
|
|
@@ -344,6 +356,10 @@ export function getFileExtension() {
|
|
|
344
356
|
}
|
|
345
357
|
|
|
346
358
|
export async function deploy(opts) {
|
|
359
|
+
return withRuleDeployment(opts, { name, paths, kernelSkillsPath }, deploySelected);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function deploySelected(opts) {
|
|
347
363
|
const mode = normalizeDeploymentMode(opts.mode);
|
|
348
364
|
if (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)) {
|
|
349
365
|
assertSafeAgentsMd(opts.target);
|
|
@@ -171,7 +171,7 @@ export async function postDeploy(targetDir, opts = {}) {
|
|
|
171
171
|
opts.createAgentsMd ||
|
|
172
172
|
(!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)
|
|
173
173
|
) {
|
|
174
|
-
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
174
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
175
175
|
}
|
|
176
176
|
if (!opts.quiet) {
|
|
177
177
|
const root = resolveGrokbotSkillsDir(opts.env || process.env);
|
|
@@ -224,9 +224,9 @@ export function assertContextBudget(content, name = 'AGENTS.md', rules = []) {
|
|
|
224
224
|
* priming on every Hermes turn (#1318 / S7; skill-discovery added #1347).
|
|
225
225
|
*
|
|
226
226
|
* The full rule bodies are reachable without MCP via `aiwg discover` and
|
|
227
|
-
* `aiwg show rule <name
|
|
227
|
+
* `aiwg show rule <name> --body`; MCP exposes the same path through `rule-list` and
|
|
228
228
|
* `rule-show` when configured (#1320). The bodies exceed Hermes's 20K context
|
|
229
|
-
* cap if inlined verbatim (anti-laziness alone
|
|
229
|
+
* cap if inlined verbatim (anti-laziness alone was 32K chars; it now ships as a compact core body, #3450), so we inline
|
|
230
230
|
* compressed directives only and point to CLI/MCP for the full text.
|
|
231
231
|
*
|
|
232
232
|
* Selection criteria: rules tagged CRITICAL/HIGH enforcement level whose
|
|
@@ -240,7 +240,7 @@ const CRITICAL_RULE_DIRECTIVES = `## CRITICAL Rules (always apply)
|
|
|
240
240
|
These are the highest-enforcement AIWG rules. Full bodies are reachable without
|
|
241
241
|
MCP:
|
|
242
242
|
- Find rules: \`aiwg discover "rule <topic>" --type rule\`
|
|
243
|
-
- Fetch a rule: \`aiwg show rule <name
|
|
243
|
+
- Fetch a rule: \`aiwg show rule <name> --body\`
|
|
244
244
|
|
|
245
245
|
If the optional MCP sidecar is configured, the same surface is available through
|
|
246
246
|
\`mcp_aiwg_rule_list\` and \`mcp_aiwg_rule_show\`.
|
|
@@ -260,13 +260,6 @@ Never add AI-tool attribution to commits, PRs, code, or docs. No \`Co-Authored-B
|
|
|
260
260
|
no "Generated with", no "Written by [AI tool]". The AI is a tool; tools don't sign
|
|
261
261
|
their output. Applies to ALL platforms (Claude, Codex, Copilot, Cursor, etc.).
|
|
262
262
|
|
|
263
|
-
### Rule: anti-laziness
|
|
264
|
-
Never delete tests to make them pass. Never skip/disable tests. Never remove
|
|
265
|
-
features instead of fixing them. Never weaken assertions to be meaningless.
|
|
266
|
-
Never suppress CI/pipeline signals (\`continue-on-error\`, \`|| true\`, \`set +e\`).
|
|
267
|
-
If stuck after 3 honest attempts: escalate with full context, don't shortcut.
|
|
268
|
-
Within scope: leave nothing half-done — code + tests + docs + verification.
|
|
269
|
-
|
|
270
263
|
### Rule: citation-policy
|
|
271
264
|
Never fabricate citations, DOIs, URLs, or page numbers. Only cite sources that
|
|
272
265
|
exist in the research corpus (.aiwg/research/sources/). Match claim strength
|
|
@@ -331,6 +324,7 @@ function mergeHermesContext(file, generated, kind) {
|
|
|
331
324
|
}
|
|
332
325
|
|
|
333
326
|
export function generateAgentsMd(agentCount, skillCount, targetDir, opts) {
|
|
327
|
+
if (opts.contextFiles === false) return;
|
|
334
328
|
const { dryRun } = opts;
|
|
335
329
|
|
|
336
330
|
// Startup is reserved for rule contracts. Workflow routing, artifact catalogs,
|
|
@@ -339,7 +333,7 @@ export function generateAgentsMd(agentCount, skillCount, targetDir, opts) {
|
|
|
339
333
|
const header = `# AIWG Integration
|
|
340
334
|
|
|
341
335
|
Skills: \`$HERMES_HOME/skills/\` (kernel), \`$HERMES_HOME/skills/.aiwg/\` (standard).
|
|
342
|
-
Find: \`aiwg discover "rule <topic>" --type rule\`. Fetch: \`aiwg show rule <name
|
|
336
|
+
Find: \`aiwg discover "rule <topic>" --type rule\`. Fetch: \`aiwg show rule <name> --body\`.
|
|
343
337
|
MCP is optional; \`mcp_aiwg_rule_show\` exposes the same rule catalog.
|
|
344
338
|
`;
|
|
345
339
|
|
|
@@ -533,7 +527,7 @@ async function deploySelected(opts) {
|
|
|
533
527
|
// Generate AGENTS.md (with inlined CRITICAL rule priming, #1318/#1532)
|
|
534
528
|
// AND .hermes.md thin pointer (resolves #1319 doc-debt, was claimed in
|
|
535
529
|
// CHANGELOG but not previously implemented).
|
|
536
|
-
if (!skillsOnly && !opts.commandsOnly && !opts.rulesOnly) {
|
|
530
|
+
if (opts.contextFiles !== false && !skillsOnly && !opts.commandsOnly && !opts.rulesOnly) {
|
|
537
531
|
const artifacts = collectFrameworkArtifacts(srcRoot, normalizedMode, {
|
|
538
532
|
includeAgents: true,
|
|
539
533
|
includeCommands: false,
|
|
@@ -560,7 +554,7 @@ async function deploySelected(opts) {
|
|
|
560
554
|
if (!opts.quiet) {
|
|
561
555
|
console.log('');
|
|
562
556
|
console.log(`Skills root: ${kernelSkillsPath}`);
|
|
563
|
-
console.log('Rules are in AGENTS.md as compressed directives; full bodies via `aiwg show rule <name
|
|
557
|
+
console.log('Rules are in AGENTS.md as compressed directives; full bodies via `aiwg show rule <name> --body`.');
|
|
564
558
|
console.log('Optional: configure config.yaml to connect AIWG MCP server.');
|
|
565
559
|
console.log('See: docs/integrations/hermes-quickstart.md (optional MCP setup)');
|
|
566
560
|
}
|
|
@@ -43,9 +43,7 @@
|
|
|
43
43
|
* Servers inherit the operator's process environment; pass `MUSE_SESSION_ID`
|
|
44
44
|
* through at runtime instead of hardcoding a session id into `env`.
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
* muse-paths.mjs only) so provider-policy-style import graphs never pull it
|
|
48
|
-
* in transitively.
|
|
46
|
+
* MCP writes use the shared config writer, resolved for source and packaged installs.
|
|
49
47
|
*
|
|
50
48
|
* @issue #228
|
|
51
49
|
*/
|
|
@@ -53,9 +51,13 @@
|
|
|
53
51
|
import fs from 'node:fs';
|
|
54
52
|
import os from 'node:os';
|
|
55
53
|
import path from 'node:path';
|
|
54
|
+
import { randomUUID } from 'node:crypto';
|
|
55
|
+
import { importImpl } from '../../_resolve-impl.mjs';
|
|
56
56
|
import { stripJsonComments } from './base.mjs';
|
|
57
57
|
import { resolveMuseXdgSkillsDir, museXdgSkillsDirRemediation } from './muse-paths.mjs';
|
|
58
58
|
|
|
59
|
+
const { assertConfigDestination, writeConfigAtomic, writeConfigTransaction } = await importImpl(import.meta.url, 'mcp/config-file.mjs');
|
|
60
|
+
|
|
59
61
|
// ---------------------------------------------------------------------------
|
|
60
62
|
// Shared JSON helpers
|
|
61
63
|
// ---------------------------------------------------------------------------
|
|
@@ -631,7 +633,7 @@ export function mergeMcpProfile(existingDoc, opts = {}) {
|
|
|
631
633
|
* preserving unknown keys, timestamped backup before writing, atomic write,
|
|
632
634
|
* dry-run diff with zero writes, fail closed on unparseable settings.
|
|
633
635
|
*/
|
|
634
|
-
export function deployMuseMcp(opts = {}) {
|
|
636
|
+
export async function deployMuseMcp(opts = {}) {
|
|
635
637
|
const { dryRun = false, quiet = false, mcp = false, env = process.env, userHome = os.homedir() } = opts;
|
|
636
638
|
const log = quiet ? () => {} : (msg) => console.log(msg);
|
|
637
639
|
|
|
@@ -642,6 +644,8 @@ export function deployMuseMcp(opts = {}) {
|
|
|
642
644
|
|
|
643
645
|
const settingsPath = assertMuseUserSettingsPath(env, userHome);
|
|
644
646
|
const configDir = path.dirname(settingsPath);
|
|
647
|
+
await assertConfigDestination(settingsPath);
|
|
648
|
+
await assertConfigDestination(path.join(configDir, MUSE_MCP_SIDECAR_NAME));
|
|
645
649
|
const { doc: existingDoc, raw: existingRaw, existed } = readSettingsDoc(settingsPath);
|
|
646
650
|
const sidecar = readMcpSidecar(configDir);
|
|
647
651
|
const profile = aiwgMcpServerProfile();
|
|
@@ -680,14 +684,15 @@ export function deployMuseMcp(opts = {}) {
|
|
|
680
684
|
return { settingsPath, changed: true, plan, wrote: false, warnings };
|
|
681
685
|
}
|
|
682
686
|
|
|
683
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
684
687
|
let backupPath = null;
|
|
685
688
|
if (existed) {
|
|
686
|
-
backupPath = `${settingsPath}.aiwg-backup-${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
687
|
-
|
|
689
|
+
backupPath = `${settingsPath}.aiwg-backup-${new Date().toISOString().replace(/[:.]/g, '-')}-${randomUUID()}`;
|
|
690
|
+
await writeConfigAtomic(backupPath, existingRaw, { userScope: true });
|
|
688
691
|
}
|
|
689
|
-
|
|
690
|
-
|
|
692
|
+
await writeConfigTransaction([
|
|
693
|
+
{ file: settingsPath, content: afterText, options: { userScope: true } },
|
|
694
|
+
{ file: sidecar.path, content: canonicalJson({ version: 1, servers: ['aiwg'] }), options: { userScope: true } },
|
|
695
|
+
]);
|
|
691
696
|
|
|
692
697
|
log(`Muse MCP: merged AIWG stdio profile into ${settingsPath}`);
|
|
693
698
|
for (const step of plan) log(` ${step}`);
|
|
@@ -700,11 +705,13 @@ export function deployMuseMcp(opts = {}) {
|
|
|
700
705
|
* Removal path: drop the AIWG-managed `aiwg` server key (only when recorded
|
|
701
706
|
* in the sidecar) and delete the sidecar. Operator servers are never touched.
|
|
702
707
|
*/
|
|
703
|
-
export function pruneMuseMcp(opts = {}) {
|
|
708
|
+
export async function pruneMuseMcp(opts = {}) {
|
|
704
709
|
const { dryRun = false, quiet = false, env = process.env, userHome = os.homedir() } = opts;
|
|
705
710
|
const log = quiet ? () => {} : (msg) => console.log(msg);
|
|
706
711
|
const settingsPath = assertMuseUserSettingsPath(env, userHome);
|
|
707
712
|
const configDir = path.dirname(settingsPath);
|
|
713
|
+
await assertConfigDestination(settingsPath);
|
|
714
|
+
await assertConfigDestination(path.join(configDir, MUSE_MCP_SIDECAR_NAME));
|
|
708
715
|
|
|
709
716
|
if (!fs.existsSync(settingsPath)) {
|
|
710
717
|
log(`Muse MCP: no ${settingsPath}; nothing to prune.`);
|
|
@@ -731,10 +738,10 @@ export function pruneMuseMcp(opts = {}) {
|
|
|
731
738
|
return { settingsPath, pruned: true, wrote: false };
|
|
732
739
|
}
|
|
733
740
|
|
|
734
|
-
const backupPath = `${settingsPath}.aiwg-backup-${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
if (
|
|
741
|
+
const backupPath = `${settingsPath}.aiwg-backup-${new Date().toISOString().replace(/[:.]/g, '-')}-${randomUUID()}`;
|
|
742
|
+
await writeConfigAtomic(backupPath, existingRaw, { userScope: true });
|
|
743
|
+
await writeConfigAtomic(settingsPath, canonicalJson(merged), { userScope: true });
|
|
744
|
+
if (await assertConfigDestination(sidecar.path)) fs.unlinkSync(sidecar.path);
|
|
738
745
|
log(`Muse MCP: removed mcp_servers.aiwg from ${settingsPath} (backup: ${backupPath}).`);
|
|
739
746
|
return { settingsPath, pruned: true, wrote: true, backupPath };
|
|
740
747
|
}
|
|
@@ -35,6 +35,7 @@ import fs from 'node:fs';
|
|
|
35
35
|
import os from 'node:os';
|
|
36
36
|
import path from 'node:path';
|
|
37
37
|
import {
|
|
38
|
+
withRuleDeployment,
|
|
38
39
|
collectFrameworkArtifacts,
|
|
39
40
|
createAgentsMdFromTemplate,
|
|
40
41
|
deploySkillsWithKernelRouting,
|
|
@@ -226,7 +227,7 @@ export async function postDeploy(targetDir, opts = {}) {
|
|
|
226
227
|
// bridge, managed hooks, and an opted-in MCP profile must not be skipped.
|
|
227
228
|
const fullDeploy = !opts.commandsOnly && !opts.rulesOnly && (!opts.skillsOnly || opts.kernelOnly === true);
|
|
228
229
|
if (opts.createAgentsMd || fullDeploy) {
|
|
229
|
-
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
230
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
230
231
|
}
|
|
231
232
|
|
|
232
233
|
// #228 — managed project hooks (.muse/hooks.json). Default on, mirroring
|
|
@@ -240,7 +241,7 @@ export async function postDeploy(targetDir, opts = {}) {
|
|
|
240
241
|
// #228 — optional MCP settings profile. Explicit opt-in only (--mcp);
|
|
241
242
|
// default `aiwg use --provider muse` never touches user settings.
|
|
242
243
|
if (fullDeploy && opts.mcp === true) {
|
|
243
|
-
deployMuseMcp(opts);
|
|
244
|
+
await deployMuseMcp(opts);
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
if (!opts.quiet) {
|
|
@@ -265,6 +266,10 @@ export async function postDeploy(targetDir, opts = {}) {
|
|
|
265
266
|
}
|
|
266
267
|
|
|
267
268
|
export async function deploy(opts = {}) {
|
|
269
|
+
return withRuleDeployment(opts, { name, paths, kernelSkillsPath }, deploySelected);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function deploySelected(opts) {
|
|
268
273
|
const {
|
|
269
274
|
srcRoot,
|
|
270
275
|
target,
|
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import YAML from 'yaml';
|
|
6
6
|
import { importImpl } from '../../_resolve-impl.mjs';
|
|
7
|
-
import { collectFrameworkArtifacts, getAddonFiles, isKernelSkill, listMdFiles, listSkillDirs, normalizeDeploymentMode, resolveAiwgRoot } from './base.mjs';
|
|
7
|
+
import { withRuleDeployment, collectFrameworkArtifacts, getAddonFiles, isKernelSkill, listMdFiles, listSkillDirs, normalizeDeploymentMode, resolveAiwgRoot } from './base.mjs';
|
|
8
8
|
export const name = 'omp';
|
|
9
9
|
export const aliases = ['oh-my-pi'];
|
|
10
10
|
export const paths = { agents: '.omp/agents', commands: '.omp/prompts', skills: '.agents/skills', rules: '.omp/rules' };
|
|
@@ -131,7 +131,7 @@ function deployType(files, target, opts, type, transform) {
|
|
|
131
131
|
}
|
|
132
132
|
export const deployAgents = (files, target, opts = {}) => deployType(files, target, opts, 'agents', transformAgent);
|
|
133
133
|
export const deployCommands = (files, target, opts = {}) => deployType(files, target, opts, 'prompts', transformCommand);
|
|
134
|
-
export const deployRules = (files, target, opts = {}) => deployType(files, target, opts, 'rules', transformRule);
|
|
134
|
+
export const deployRules = (files, target, opts = {}) => opts.ruleDelivery ? 0 : deployType(files, target, opts, 'rules', transformRule);
|
|
135
135
|
export function deploySkills(dirs, target, opts = {}) {
|
|
136
136
|
let count = 0; const seen = new Map(); const root = roots(target, opts);
|
|
137
137
|
for (const dir of [...new Set(dirs)]) {
|
|
@@ -223,10 +223,20 @@ export function deployExtensionBridge(target, opts = {}) {
|
|
|
223
223
|
return writeOwned(path.join(roots(target, opts).native, 'extensions/aiwg-bridge.ts'), fs.readFileSync(src, 'utf8'), src, opts, 'omp-extension');
|
|
224
224
|
}
|
|
225
225
|
export async function postDeploy(target, opts) {
|
|
226
|
-
if (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)) createAgentsMd(target, opts.srcRoot, opts.dryRun);
|
|
226
|
+
if (opts.contextFiles !== false && (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly))) createAgentsMd(target, opts.srcRoot, opts.dryRun);
|
|
227
227
|
}
|
|
228
228
|
export const getFileExtension = () => '.md';
|
|
229
229
|
export async function deploy(opts) {
|
|
230
|
+
return withRuleDeployment(opts, { name, paths, kernelSkillsPath }, async staged => {
|
|
231
|
+
let count = await deploySelected(staged);
|
|
232
|
+
for (const srcRoot of staged.additionalSources || []) {
|
|
233
|
+
count += await deploySelected({ ...staged, srcRoot });
|
|
234
|
+
}
|
|
235
|
+
return count;
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function deploySelected(opts) {
|
|
230
240
|
const mode = normalizeDeploymentMode(opts.mode);
|
|
231
241
|
const include = { includeAgents: true, includeCommands: opts.deployCommands || opts.commandsOnly, includeSkills: opts.deploySkills || opts.skillsOnly, includeRules: opts.deployRules || opts.rulesOnly };
|
|
232
242
|
const directSource = ['agents', 'commands', 'skills', 'rules'].some(type => fs.existsSync(path.join(opts.srcRoot, type)));
|
|
@@ -251,7 +261,21 @@ export async function deploy(opts) {
|
|
|
251
261
|
let count = 0;
|
|
252
262
|
if (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly) count += deployAgents(selected('agents'), opts.target, opts);
|
|
253
263
|
if (include.includeCommands && !opts.skillsOnly && !opts.rulesOnly) count += deployCommands(selected('commands'), opts.target, opts);
|
|
254
|
-
if (include.includeSkills && !opts.commandsOnly && !opts.rulesOnly)
|
|
264
|
+
if (include.includeSkills && !opts.commandsOnly && !opts.rulesOnly) {
|
|
265
|
+
// Different frameworks may index same-named standard skills. With copying
|
|
266
|
+
// disabled they share only a stale-copy cleanup destination, not a native
|
|
267
|
+
// definition slot. Keep one cleanup pass; retain collision checks for every
|
|
268
|
+
// kernel skill and for explicitly copied standard skills.
|
|
269
|
+
const lazyNames = new Set();
|
|
270
|
+
const skills = selected('skills').filter(dir => {
|
|
271
|
+
if (opts.copyStandardSkills || isKernelSkill(dir)) return true;
|
|
272
|
+
const basename = path.basename(dir);
|
|
273
|
+
if (lazyNames.has(basename)) return false;
|
|
274
|
+
lazyNames.add(basename);
|
|
275
|
+
return true;
|
|
276
|
+
});
|
|
277
|
+
count += deploySkills(skills, opts.target, opts);
|
|
278
|
+
}
|
|
255
279
|
if (include.includeRules && !opts.commandsOnly && !opts.skillsOnly) count += deployRules(selected('rules'), opts.target, opts);
|
|
256
280
|
if (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly) count += deployExtensionBridge(opts.target, opts);
|
|
257
281
|
await postDeploy(opts.target, opts); return count;
|
|
@@ -23,7 +23,9 @@ const staticModelCatalog = _require('../../../agentic/code/providers/model-catal
|
|
|
23
23
|
let fs;
|
|
24
24
|
try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
|
|
25
25
|
import path from 'path';
|
|
26
|
+
import { importImpl } from '../../_resolve-impl.mjs';
|
|
26
27
|
import {
|
|
28
|
+
withRuleDeployment,
|
|
27
29
|
ensureDir,
|
|
28
30
|
listMdFiles,
|
|
29
31
|
listMdFilesRecursive,
|
|
@@ -50,6 +52,7 @@ import {
|
|
|
50
52
|
filterCommandsAgainstSkills,
|
|
51
53
|
deploySoulCompanions
|
|
52
54
|
} from './base.mjs';
|
|
55
|
+
const { assertConfigDestination, writeConfigAtomic } = await importImpl(import.meta.url, 'mcp/config-file.mjs');
|
|
53
56
|
const modelCatalog = loadRuntimeModelCatalog(staticModelCatalog);
|
|
54
57
|
|
|
55
58
|
// ============================================================================
|
|
@@ -375,6 +378,7 @@ export function deploySkills(skillDirs, targetDir, opts) {
|
|
|
375
378
|
* Deploy rules to .opencode/rule/
|
|
376
379
|
*/
|
|
377
380
|
export function deployRules(ruleFiles, targetDir, opts) {
|
|
381
|
+
if (opts.ruleDelivery) return [];
|
|
378
382
|
const destDir = path.join(targetDir, paths.rules);
|
|
379
383
|
ensureDir(destDir, opts.dryRun);
|
|
380
384
|
cleanupOldRuleFiles(destDir, opts);
|
|
@@ -394,9 +398,9 @@ export function deployRules(ruleFiles, targetDir, opts) {
|
|
|
394
398
|
* Non-destructive: a malformed existing opencode.json is left untouched (warn,
|
|
395
399
|
* skip) rather than overwritten.
|
|
396
400
|
*
|
|
397
|
-
* @returns {boolean} true if the config was changed (or would be, in dry-run)
|
|
401
|
+
* @returns {Promise<boolean>} true if the config was changed (or would be, in dry-run)
|
|
398
402
|
*/
|
|
399
|
-
export function mergeOpenCodeInstructions(targetDir, opts = {}) {
|
|
403
|
+
export async function mergeOpenCodeInstructions(targetDir, opts = {}) {
|
|
400
404
|
const RULE_GLOB = `${paths.rules}*.md`; // .opencode/rule/*.md — opencode supports globs in instructions (verified in sst/opencode session/instruction.ts)
|
|
401
405
|
// opencode auto-loads AGENTS.md/CLAUDE.md/CONTEXT.md but NOT AIWG.md, and the
|
|
402
406
|
// generated AGENTS.md is a thin link-index that points to AIWG.md. The
|
|
@@ -407,6 +411,7 @@ export function mergeOpenCodeInstructions(targetDir, opts = {}) {
|
|
|
407
411
|
const AIWG = 'AIWG.md';
|
|
408
412
|
const wanted = [RULE_GLOB, AGENTS, AIWG];
|
|
409
413
|
const configPath = path.join(targetDir, 'opencode.json');
|
|
414
|
+
await assertConfigDestination(configPath, targetDir);
|
|
410
415
|
|
|
411
416
|
let config = {};
|
|
412
417
|
if (fs.existsSync(configPath)) {
|
|
@@ -433,7 +438,7 @@ export function mergeOpenCodeInstructions(targetDir, opts = {}) {
|
|
|
433
438
|
|
|
434
439
|
// Preserve operator-authored entries; append only what's missing, deduped.
|
|
435
440
|
config.instructions = [...existing, ...missing];
|
|
436
|
-
|
|
441
|
+
await writeConfigAtomic(configPath, `${JSON.stringify(config, null, 2)}\n`, { projectRoot: targetDir });
|
|
437
442
|
if (opts.verbose) {
|
|
438
443
|
console.log(`Wired ${missing.length} entr${missing.length === 1 ? 'y' : 'ies'} into opencode.json instructions[]: ${missing.join(', ')}`);
|
|
439
444
|
}
|
|
@@ -503,7 +508,7 @@ export async function postDeploy(targetDir, opts) {
|
|
|
503
508
|
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
504
509
|
|
|
505
510
|
if (opts.createAgentsMd) {
|
|
506
|
-
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
511
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
507
512
|
}
|
|
508
513
|
|
|
509
514
|
// PUW-035 (#1136): emit SDLC primary roles as TUI modes.
|
|
@@ -527,6 +532,10 @@ export function getFileExtension(type) {
|
|
|
527
532
|
// ============================================================================
|
|
528
533
|
|
|
529
534
|
export async function deploy(opts) {
|
|
535
|
+
return withRuleDeployment(opts, { name, paths, kernelSkillsPath }, deploySelected);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async function deploySelected(opts) {
|
|
530
539
|
const {
|
|
531
540
|
srcRoot,
|
|
532
541
|
target,
|
|
@@ -637,7 +646,7 @@ export async function deploy(opts) {
|
|
|
637
646
|
// #1548: rules on disk are inert unless referenced from opencode.json
|
|
638
647
|
// instructions[]. Wire them so opencode actually loads them.
|
|
639
648
|
try {
|
|
640
|
-
mergeOpenCodeInstructions(target, opts);
|
|
649
|
+
await mergeOpenCodeInstructions(target, opts);
|
|
641
650
|
} catch (err) {
|
|
642
651
|
console.warn(`Warning: could not wire opencode.json instructions[]: ${err && err.message ? err.message : err}`);
|
|
643
652
|
}
|
|
@@ -173,7 +173,7 @@ export async function postDeploy(targetDir, opts) {
|
|
|
173
173
|
opts.createAgentsMd ||
|
|
174
174
|
(!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)
|
|
175
175
|
) {
|
|
176
|
-
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
176
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
@@ -128,7 +128,7 @@ export function createAgentsMd(target, srcRoot, dryRun) {
|
|
|
128
128
|
|
|
129
129
|
export async function postDeploy(targetDir, opts) {
|
|
130
130
|
if (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)) {
|
|
131
|
-
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
131
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
132
132
|
}
|
|
133
133
|
if (!opts.quiet && (opts.deployCommands || opts.deploySkills || opts.commandsOnly || opts.skillsOnly)) {
|
|
134
134
|
console.log('Pi project resources are trust-gated; approve the project with `/trust` (restart required) or use `pi --approve` for a reviewed one-shot run.');
|
|
@@ -81,6 +81,10 @@ function prepareInChild({ job, attempt, sessionRoot, poolDescriptor, workerPath,
|
|
|
81
81
|
let outcome;
|
|
82
82
|
let killTimer;
|
|
83
83
|
let timedOut = false;
|
|
84
|
+
let exited = false;
|
|
85
|
+
let disconnected = false;
|
|
86
|
+
let exitCode;
|
|
87
|
+
let exitSignal;
|
|
84
88
|
const finish = () => {
|
|
85
89
|
if (settled) return;
|
|
86
90
|
settled = true;
|
|
@@ -107,19 +111,30 @@ function prepareInChild({ job, attempt, sessionRoot, poolDescriptor, workerPath,
|
|
|
107
111
|
timer.unref?.();
|
|
108
112
|
child.once('error', error => {
|
|
109
113
|
// A spawn failure has no operating-system child to reap. Every error
|
|
110
|
-
// after a pid is assigned still waits for
|
|
114
|
+
// after a pid is assigned still waits for exit and IPC disconnect below.
|
|
111
115
|
if (!child.pid) { outcome = { error }; finish(); }
|
|
112
116
|
else stop(error);
|
|
113
117
|
});
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
118
|
+
// Exit confirms termination; disconnect confirms pending IPC messages have
|
|
119
|
+
// drained. Either event can arrive first. Keep workers active until both,
|
|
120
|
+
// including workers that respond and retain an unexpected live handle.
|
|
121
|
+
const finishAfterDisconnectAndExit = () => {
|
|
122
|
+
if (!exited || !disconnected) return;
|
|
123
|
+
if (outcome?.value && (exitCode !== 0 || exitSignal)) {
|
|
124
|
+
outcome = { error: new Error(`Provider preparation worker exited ${exitSignal || exitCode || 'without status'} after responding for ${job.provider}`) };
|
|
120
125
|
}
|
|
121
|
-
if (!outcome) outcome = { error: new Error(`${timedOut ? 'Provider preparation timed out' : `Provider preparation worker exited ${
|
|
126
|
+
if (!outcome) outcome = { error: new Error(`${timedOut ? 'Provider preparation timed out' : `Provider preparation worker exited ${exitCode ?? 'without status'}`} for ${job.provider}${stderrBytes ? `: ${Buffer.concat(stderr).toString('utf8').trim()}` : ''}`) };
|
|
122
127
|
finish();
|
|
128
|
+
};
|
|
129
|
+
child.once('exit', (code, signal) => {
|
|
130
|
+
exited = true;
|
|
131
|
+
exitCode = code;
|
|
132
|
+
exitSignal = signal;
|
|
133
|
+
finishAfterDisconnectAndExit();
|
|
134
|
+
});
|
|
135
|
+
child.once('disconnect', () => {
|
|
136
|
+
disconnected = true;
|
|
137
|
+
finishAfterDisconnectAndExit();
|
|
123
138
|
});
|
|
124
139
|
child.once('message', response => {
|
|
125
140
|
try {
|
|
@@ -137,7 +152,7 @@ function prepareInChild({ job, attempt, sessionRoot, poolDescriptor, workerPath,
|
|
|
137
152
|
if (response.type !== 'prepared') throw new Error('Invalid provider preparation worker response type');
|
|
138
153
|
outcome = { value: { detached: readPlan(response, expectedPlan), scratch, attempt } };
|
|
139
154
|
// Closing the IPC channel lets the single-request worker exit normally;
|
|
140
|
-
// the exit
|
|
155
|
+
// the exit/disconnect handlers confirm termination and IPC drainage.
|
|
141
156
|
if (child.connected) { try { child.disconnect(); } catch {} }
|
|
142
157
|
} catch (error) { stop(error); }
|
|
143
158
|
});
|
|
@@ -200,6 +215,10 @@ export async function prepareProviderDeployments({
|
|
|
200
215
|
removeSession();
|
|
201
216
|
};
|
|
202
217
|
const prepareOne = async (index, attempt = 0) => {
|
|
218
|
+
// Never start a worker once the session is closed: it would outlive
|
|
219
|
+
// cleanup, and anything it writes (e.g. the shared binding-index cache)
|
|
220
|
+
// would land after its owner removed it.
|
|
221
|
+
if (closed) throw new Error('Provider preparation session is closed');
|
|
203
222
|
const raw = await prepareInChild({ job: jobs[index], attempt, sessionRoot, poolDescriptor, workerPath, workerEnv, activeChildren, timeoutMs });
|
|
204
223
|
const plan = transaction.adopt(raw.detached, {
|
|
205
224
|
scratch: raw.scratch,
|
|
@@ -212,6 +231,7 @@ export async function prepareProviderDeployments({
|
|
|
212
231
|
try {
|
|
213
232
|
await Promise.all(Array.from({ length: concurrency }, async () => {
|
|
214
233
|
for (;;) {
|
|
234
|
+
if (closed) return;
|
|
215
235
|
const index = next++;
|
|
216
236
|
if (index >= jobs.length) return;
|
|
217
237
|
prepared[index] = await prepareOne(index);
|