@aiwg/cli 2026.10.3 → 2026.10.4
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/README.md +2 -2
- package/agentic/code/providers/model-capabilities.v1.json +96 -4
- package/bin/aiwg.mjs +50 -27
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +57 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +285 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +65 -0
- package/dist/src/artifacts/index-refresh.js +576 -0
- package/dist/src/artifacts/index-worker.mjs +9 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +19 -2
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +19 -12
- package/dist/src/cli/handlers/diagnose.js +4 -3
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +4 -1
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/models.js +104 -8
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/refresh.js +12 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +1 -2
- package/dist/src/cli/handlers/script-runner.js +3 -2
- package/dist/src/cli/handlers/session.js +19 -9
- package/dist/src/cli/handlers/sessions.js +98 -37
- package/dist/src/cli/handlers/setup-manifest.js +39 -44
- package/dist/src/cli/handlers/setup.js +41 -0
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +57 -2
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +294 -55
- package/dist/src/cli/handlers/utilities.js +26 -10
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +87 -37
- package/dist/src/config/cli.js +111 -5
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +2 -1
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +13 -8
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +2 -15
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +5 -13
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +131 -28
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/gates/providers/loader.js +61 -17
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +14 -2
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +78 -3
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +7 -1
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +36 -11
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +1 -1
- package/dist/src/mcp/helpers.mjs +3 -2
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/registry.js +3 -93
- package/dist/src/mcp/registry.mjs +3 -87
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +37 -375
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tools/decision.mjs +12 -11
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +12 -2
- package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
- package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
- package/dist/src/mission-protocol/codecs.js +8 -0
- package/dist/src/models/config-loader.js +7 -2
- package/dist/src/models/model-capabilities.v1.json +96 -4
- package/dist/src/models/model-discovery.js +76 -11
- package/dist/src/models/provider-policy.js +101 -7
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/resolver.js +12 -10
- package/dist/src/models/worker-direct-readiness.js +65 -44
- package/dist/src/models/worker-dispatch-offer.js +3 -3
- package/dist/src/models/worker-health-readiness.js +16 -3
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +135 -28
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +43 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +12 -4
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/mission-conductor.js +38 -12
- package/dist/src/serve/worker-invocation.js +30 -10
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/sessions/adapter-registry.js +23 -19
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +82 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +10 -10
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot.js +24 -13
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
- package/dist/src/sessions/adapters/muse.js +150 -93
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +29 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +16 -8
- package/dist/src/sessions/batch-import.js +51 -11
- package/dist/src/sessions/contracts.js +40 -2
- package/dist/src/sessions/coverage-preflight.js +1 -1
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +60 -38
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +133 -25
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +106 -0
- package/dist/src/sessions/repository.js +147 -52
- package/dist/src/sessions/workspace-discovery.js +221 -56
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/package.json +15 -2
- package/schemas/models/model-catalog.v1.schema.json +1 -1
- package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
- package/tools/_resolve-impl.mjs +13 -15
- package/tools/agents/deploy-agents.mjs +49 -21
- package/tools/agents/providers/base.mjs +104 -134
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/hermes.mjs +57 -47
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +6 -4
- package/tools/providers/omp-transport.mjs +4 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
- package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
- package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Context-finalization pass for generated provider context files.
|
|
3
3
|
*
|
|
4
4
|
* Templates provide the stable structure; this pass stitches in current
|
|
5
|
-
* workspace facts from `.aiwg/aiwg.config` and
|
|
5
|
+
* workspace facts from `.aiwg/aiwg.config` and rule authority pointers so
|
|
6
6
|
* provider context files do not remain template-only (#1365).
|
|
7
7
|
*/
|
|
8
8
|
import { promises as fs } from 'node:fs';
|
|
@@ -10,6 +10,7 @@ import * as path from 'node:path';
|
|
|
10
10
|
import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from '../../config/project-artifacts.js';
|
|
11
11
|
import { renderTrackerProtocol, resolveTrackerAuthority } from '../../tracker/capability-protocol.js';
|
|
12
12
|
import { readConfig, readGitRemoteUrls } from '../../tracker/project-inputs.js';
|
|
13
|
+
import { DECISION_COMPACT_ANCHOR, inspectDecisionOffloadActivation } from '../../decision/offload-activation.js';
|
|
13
14
|
import { buildExternalLinksSection, replaceOrAppendExternalLinksBlock, } from './external-links-section.js';
|
|
14
15
|
export const FINALIZATION_START = '<!-- aiwg-context-finalization:START -->';
|
|
15
16
|
export const FINALIZATION_END = '<!-- aiwg-context-finalization:END -->';
|
|
@@ -52,6 +53,7 @@ export async function buildContextFinalizationBlock(projectPath, documentPath =
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
const trackerAuthority = resolveTrackerAuthority(config, remoteUrls);
|
|
56
|
+
const decision = await inspectDecisionOffloadActivation({ cwd: projectPath });
|
|
55
57
|
const lines = [
|
|
56
58
|
FINALIZATION_START,
|
|
57
59
|
'## Context Finalization',
|
|
@@ -65,11 +67,9 @@ export async function buildContextFinalizationBlock(projectPath, documentPath =
|
|
|
65
67
|
`- Recorded deployments: ${formatList([...providerDeployments].sort())}`,
|
|
66
68
|
`- Normalized project context: \`${normalizedAiwgMdPath}\``,
|
|
67
69
|
'',
|
|
68
|
-
'###
|
|
70
|
+
'### Capability Routing',
|
|
69
71
|
'',
|
|
70
|
-
'
|
|
71
|
-
'',
|
|
72
|
-
'Also run `aiwg discover` before declining an AIWG request as out of scope or inventing a workflow from memory. The CLI ranks AIWG capabilities across the installed corpus and rebuilds the index from `$AIWG_ROOT` automatically, so a "no matches" for a command you know is deployed is a bug — not a signal it is absent. Commands AIWG deploys to your provider command directory (`.opencode/command/`, `.claude/commands/`, `~/.codex/prompts/`, …) ARE discoverable this way; fetch them with `aiwg show command <name>`. This prevents decline-without-search failures, ask-instead-of-act on new directives, and hallucinated skill or agent names. Full rule: `agentic/code/addons/aiwg-utils/rules/skill-discovery.md`.',
|
|
72
|
+
'Routing authority: `aiwg show rule skill-discovery` (kernel); capability phrases: `aiwg show skill aiwg-utils-quickref`.',
|
|
73
73
|
'',
|
|
74
74
|
'### Engagement Verification',
|
|
75
75
|
'',
|
|
@@ -84,6 +84,7 @@ export async function buildContextFinalizationBlock(projectPath, documentPath =
|
|
|
84
84
|
`- ${normalizedAiwgMdLabel} is the normalized project-local context entry point.`,
|
|
85
85
|
'- Root `AIWG.md` is the generated cross-provider companion loaded through `AGENTS.md` and provider twins.',
|
|
86
86
|
`- \`AGENTS.md\`, \`WARP.md\`, \`.hermes.md\`, and \`.github/copilot-instructions.md\` are provider-facing bridges, not replacements for ${normalizedAiwgMdLabel}.`,
|
|
87
|
+
...(decision.shouldAnchor ? ['', '## Compact Instructions', '', DECISION_COMPACT_ANCHOR] : []),
|
|
87
88
|
FINALIZATION_END,
|
|
88
89
|
'',
|
|
89
90
|
];
|
|
@@ -199,7 +199,24 @@ export async function isOverwriteSafe(filePath) {
|
|
|
199
199
|
*
|
|
200
200
|
* Per ADR-4 §4 atomic write requirement; on failure no partial state is persisted.
|
|
201
201
|
*/
|
|
202
|
-
async function atomicWrite(filePath, content) {
|
|
202
|
+
async function atomicWrite(filePath, content, provider) {
|
|
203
|
+
// Rule delivery owns this block. Context regeneration runs after deployment
|
|
204
|
+
// and must retain the selected kernel even when rebuilding the adapter.
|
|
205
|
+
const existing = await fs.readFile(filePath, 'utf8').catch(() => '');
|
|
206
|
+
for (const owner of ['rule-startup', 'hermes-context', 'hermes-routing', 'warp-context']) {
|
|
207
|
+
if (content.includes(`<!-- AIWG:${owner}:start -->`))
|
|
208
|
+
continue;
|
|
209
|
+
const managed = existing.match(new RegExp(`<!-- AIWG:${owner}:start -->[\\s\\S]*?<!-- AIWG:${owner}:end -->`));
|
|
210
|
+
if (managed)
|
|
211
|
+
content = `${content.trimEnd()}\n\n${managed[0]}\n`;
|
|
212
|
+
}
|
|
213
|
+
const bytes = Buffer.byteLength(content, 'utf8');
|
|
214
|
+
if (provider === 'codex' && path.basename(filePath) === 'AGENTS.md' && bytes >= 32768) {
|
|
215
|
+
throw new Error('Codex AGENTS.md exceeds its 32 KB hard cap');
|
|
216
|
+
}
|
|
217
|
+
if (provider === 'hermes' && ['AGENTS.md', '.hermes.md'].includes(path.basename(filePath)) && content.length > 19000) {
|
|
218
|
+
throw new Error(`Hermes ${path.basename(filePath)} exceeds hard cap of 19000 characters`);
|
|
219
|
+
}
|
|
203
220
|
const dir = path.dirname(filePath);
|
|
204
221
|
const tmp = path.join(dir, `.${path.basename(filePath)}.tmp.${process.pid}`);
|
|
205
222
|
await fs.mkdir(dir, { recursive: true });
|
|
@@ -212,6 +229,7 @@ async function atomicWrite(filePath, content) {
|
|
|
212
229
|
await fs.unlink(tmp).catch(() => undefined);
|
|
213
230
|
throw err;
|
|
214
231
|
}
|
|
232
|
+
return bytes;
|
|
215
233
|
}
|
|
216
234
|
/**
|
|
217
235
|
* Per-provider twin-file names for AGENTS.md emission per ADR-1 §4.
|
|
@@ -303,7 +321,7 @@ async function writeTwinFiles(provider, projectPath, agentsMdContent, opts, resu
|
|
|
303
321
|
throw err;
|
|
304
322
|
}
|
|
305
323
|
}
|
|
306
|
-
await atomicWrite(twinPath, twinContent);
|
|
324
|
+
await atomicWrite(twinPath, twinContent, provider);
|
|
307
325
|
result.twinPaths.push(twinPath);
|
|
308
326
|
}
|
|
309
327
|
}
|
|
@@ -408,9 +426,8 @@ export async function generate(opts) {
|
|
|
408
426
|
// write, and as the twin content downstream.
|
|
409
427
|
const built = await buildAgentsMd(opts);
|
|
410
428
|
if (canWrite) {
|
|
411
|
-
await atomicWrite(agentsMdPath, built.content);
|
|
429
|
+
result.agentsMdBytes = await atomicWrite(agentsMdPath, built.content, opts.provider);
|
|
412
430
|
result.agentsMdPath = agentsMdPath;
|
|
413
|
-
result.agentsMdBytes = Buffer.byteLength(built.content, 'utf8');
|
|
414
431
|
result.warnings.push(...built.warnings);
|
|
415
432
|
if (built.splitOccurred) {
|
|
416
433
|
await writeSpilloverBlock(opts.projectPath, built.spilloverContent, result);
|
|
@@ -232,26 +232,16 @@ export async function buildParallelismSection(projectPath, deps = {}) {
|
|
|
232
232
|
lines.push(`*Rationale*: ${resolved.rationale}`);
|
|
233
233
|
}
|
|
234
234
|
lines.push('');
|
|
235
|
-
lines.push('
|
|
236
|
-
lines.push('');
|
|
237
|
-
lines.push('For each non-trivial task, assess whether it contains independent, bounded subtasks that can run concurrently. When delegation is supported, prefer the deployed model-pinned wrappers by task characteristics and consequence:');
|
|
238
|
-
lines.push('');
|
|
239
|
-
lines.push('- `aiwg-model-efficiency-worker`: discovery, inventory, focused edits, and other bounded low-cost work.');
|
|
240
|
-
lines.push('- `aiwg-model-coding-worker`: implementation, tests, debugging, and routine technical delivery.');
|
|
241
|
-
lines.push('- `aiwg-model-reasoning-worker`: architecture, synthesis, difficult analysis, and high-consequence review.');
|
|
242
|
-
lines.push('');
|
|
243
|
-
lines.push('Do not delegate trivial work, tightly coupled changes, serial dependencies, or tasks likely to collide in shared state; also keep work local when coordination costs exceed the expected benefit. Parallelize only independent work, and take the MIN of provider limits, `max_parallel_subagents`, `AIWG_CONTEXT_WINDOW` budget, framework-specific caps (including the RLM 7-agent hard cap for RLM dispatches), and natural task decomposition. Bump the project cap via `aiwg config set --project parallelism.max_parallel_subagents N`.');
|
|
244
|
-
lines.push('');
|
|
245
|
-
lines.push('The primary agent retains orchestration, final integration, conflict resolution, validation, and user-facing accountability.');
|
|
235
|
+
lines.push('Delegation policy and model-worker selection: `aiwg show rule subagent-scoping`.');
|
|
236
|
+
lines.push('After changing project parallelism with `aiwg config set/reset --project`, follow the emitted provider-specific regeneration commands and run `aiwg workspace-context doctor`.');
|
|
246
237
|
lines.push('');
|
|
247
238
|
lines.push(delegationSupport(primary));
|
|
248
239
|
lines.push('');
|
|
249
240
|
lines.push(workerDispatchStateLine(cfg.parallelism, cfg.setup));
|
|
250
241
|
lines.push('');
|
|
251
242
|
// Group dispatch disabled (worker_dispatch unset/false): the resolver reports
|
|
252
|
-
// `disabled`, so no group block renders — the
|
|
253
|
-
//
|
|
254
|
-
// rendering.
|
|
243
|
+
// `disabled`, so no group block renders — the subagent-scoping pointer above
|
|
244
|
+
// remains available.
|
|
255
245
|
if (cfg.parallelism?.worker_dispatch === true && healthUnresolved) {
|
|
256
246
|
lines.push(PARALLELISM_HEALTH_UNRESOLVED_NOTE);
|
|
257
247
|
lines.push('');
|
|
@@ -273,13 +263,15 @@ export async function buildParallelismSection(projectPath, deps = {}) {
|
|
|
273
263
|
lines.push(`Configured groups: ${shown}${hidden > 0 ? ` (+${hidden} more)` : ''}`);
|
|
274
264
|
}
|
|
275
265
|
else {
|
|
276
|
-
lines.push('Configured groups: none — group dispatch is enabled but no groups are configured; the
|
|
266
|
+
lines.push('Configured groups: none — group dispatch is enabled but no groups are configured; the subagent-scoping rule applies.');
|
|
277
267
|
}
|
|
278
268
|
lines.push('');
|
|
279
269
|
lines.push('A group above shown as configured, with excluded members or an uncovered required role listed, is a static config read — not a live eligibility verdict. Eligibility is resolved per (group, member, effort) tuple against the current catalog and request; see `aiwg models resolve` below to check a specific role/task/group.');
|
|
280
270
|
lines.push('');
|
|
281
271
|
lines.push('Tier 0 direct handoff is the default: use your own shell or a qualified MCP worker tool. No Mission/MC session, scheduler, daemon, or activation marker is needed. Self-count active native + CLI + MCP workers, take the minimum of the project, group, CLI, model, and context caps, and launch each writing task once.');
|
|
282
272
|
lines.push('');
|
|
273
|
+
lines.push('Before resolving, qualify the installed CLIs and configured model roles with `aiwg doctor --probe-workers --qualify` (`--yes` for headless confirmation).');
|
|
274
|
+
lines.push('Agents pass `--authorize-premium` to `aiwg models resolve` only after the human explicitly confirmed the premium escalation (escalation-discipline rule); authorization applies to that resolution only.');
|
|
283
275
|
lines.push('Resolve each bounded brief with `aiwg models resolve --role <role> --task <class> --group <group> --brief <file> --workdir <tree> --final-output <file> --transport shell --format shell`. Copy the exact rendered command or the MCP server/tool/arguments byte-for-byte; never assemble vendor flags from memory. Re-resolve after any brief, profile, model, or workspace change. `--format shell` prints only the launch command: to see the resolved health windows for an attempt, use `--format json` (the default) and read `invocation.health` with its per-field `origins`.');
|
|
284
276
|
lines.push('');
|
|
285
277
|
lines.push(`Configured health windows (each followed by its origin — \`default\` or \`project config\`): check every ${check.value}s (${check.origin}); confirm startup within ${startup.value}s (${startup.origin}); quiet limit ${quiet.value}s (${quiet.origin}); stall limit ${stall.value}s (${stall.origin}); hard stop ${hard.value}s (${hard.origin}). Per-attempt windows scale quiet and stall by the single largest applicable role/effort/model factor, never their product (for example, reasoning at \`xhigh\` effort resolves 180s/600s from the 90s/300s defaults; an effective 1.25 scale gives 112.5s/375s). Only the hard deadline and its diagnostic/termination grace periods are capped by the remaining wall-clock and transport ceilings; startup, quiet, and stall keep their configured or scaled values, and \`truncatedWindows\` instead records when one of them would exceed that capped deadline (\`resolveWorkerEffectiveHealth()\` in \`src/serve/worker-invocation.ts\`). Resolved values win over these configured values.`);
|
|
@@ -161,7 +161,7 @@ function createManifest(serverId, serverName, analyzerResult, config) {
|
|
|
161
161
|
resources: [],
|
|
162
162
|
prompts: [],
|
|
163
163
|
dependencies: {
|
|
164
|
-
runtime: ['node >=
|
|
164
|
+
runtime: ['node >= 24'],
|
|
165
165
|
external: [
|
|
166
166
|
`${analyzerResult.metadata.sourceName}${analyzerResult.metadata.sourceVersion ? ' >= ' + analyzerResult.metadata.sourceVersion : ''}`
|
|
167
167
|
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export class RuntimeDiscovery {
|
|
2
|
+
basePath: string;
|
|
3
|
+
constructor(basePath?: string | null, options?: { cwd?: string; quiet?: boolean });
|
|
4
|
+
discover(options?: { force?: boolean }): Promise<any>;
|
|
5
|
+
verify(): Promise<any>;
|
|
6
|
+
getCatalog(): Promise<any>;
|
|
7
|
+
generateDefinition(): Promise<any>;
|
|
8
|
+
checkTool(toolName: string): Promise<any>;
|
|
9
|
+
getSummary(): Promise<any>;
|
|
10
|
+
}
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { boundedExecFileSync, boundedExecSync } from '../../process/bounded-child-process.mjs';
|
|
11
|
-
import { readFile, writeFile, mkdir, access } from 'fs/promises';
|
|
11
|
+
import { readFile, writeFile, mkdir, access, stat } from 'fs/promises';
|
|
12
12
|
import { existsSync, constants } from 'fs';
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
13
14
|
import { resolve, join, dirname } from 'path';
|
|
14
15
|
import { platform, arch, homedir, tmpdir, cpus, totalmem, freemem } from 'os';
|
|
15
16
|
import { fileURLToPath } from 'url';
|
|
@@ -22,26 +23,59 @@ const __dirname = dirname(__filename);
|
|
|
22
23
|
* RuntimeDiscovery - Discovers and catalogs installed system tools
|
|
23
24
|
*/
|
|
24
25
|
export class RuntimeDiscovery {
|
|
25
|
-
constructor(basePath = null) {
|
|
26
|
-
this.
|
|
26
|
+
constructor(basePath = null, { cwd = process.cwd(), quiet = false } = {}) {
|
|
27
|
+
this.cwd = cwd;
|
|
28
|
+
this.quiet = quiet;
|
|
29
|
+
this.basePath = basePath || projectAiwgPath(this.cwd, 'smiths', 'toolsmith');
|
|
27
30
|
this.knownTools = this.#loadKnownTools();
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
/**
|
|
31
|
-
*
|
|
34
|
+
* Refresh discovery, skipping version probes when inputs are unchanged.
|
|
32
35
|
* @returns {Promise<Object>} RuntimeCatalog
|
|
33
36
|
*/
|
|
34
|
-
async discover() {
|
|
35
|
-
console.log('Starting runtime discovery...');
|
|
37
|
+
async discover({ force = false } = {}) {
|
|
38
|
+
if (!this.quiet) console.log('Starting runtime discovery...');
|
|
39
|
+
|
|
40
|
+
const loaded = await this.#loadCatalog().catch(error => {
|
|
41
|
+
if (error instanceof SyntaxError) return null;
|
|
42
|
+
throw error;
|
|
43
|
+
});
|
|
44
|
+
const previous = Array.isArray(loaded?.tools) ? loaded : null;
|
|
45
|
+
const paths = new Map();
|
|
46
|
+
const inputs = [];
|
|
47
|
+
for (const name of this.#safeTools()) {
|
|
48
|
+
const toolPath = await this.#findToolPath(name);
|
|
49
|
+
paths.set(name, toolPath);
|
|
50
|
+
const metadata = toolPath ? await stat(toolPath).catch(() => null) : null;
|
|
51
|
+
inputs.push([name, toolPath, metadata && [metadata.mtimeMs, metadata.ctimeMs, metadata.size, metadata.mode]]);
|
|
52
|
+
}
|
|
53
|
+
const inputFingerprint = createHash('sha256').update(JSON.stringify([
|
|
54
|
+
1, this.knownTools, inputs, platform(), arch(), homedir(), this.cwd,
|
|
55
|
+
process.env.PATH, process.env.PATHEXT, process.env.SHELL, process.env.ComSpec,
|
|
56
|
+
])).digest('hex');
|
|
57
|
+
if (!force && previous?.inputFingerprint === inputFingerprint
|
|
58
|
+
&& existsSync(join(this.basePath, 'runtime-info.md'))) {
|
|
59
|
+
if (!this.quiet) console.log(`Discovery skipped: unchanged (${previous.tools.length} tools)`);
|
|
60
|
+
return previous;
|
|
61
|
+
}
|
|
36
62
|
|
|
37
63
|
const environment = await this.getEnvironment();
|
|
38
64
|
const resources = await this.getResources();
|
|
39
|
-
const tools = await this.#discoverTools();
|
|
65
|
+
const tools = await this.#discoverTools(paths);
|
|
66
|
+
const previousIds = new Set((previous?.tools || []).map(tool => tool.id));
|
|
67
|
+
const currentIds = new Set(tools.available.map(tool => tool.id));
|
|
68
|
+
const delta = {
|
|
69
|
+
added: [...currentIds].filter(id => !previousIds.has(id)),
|
|
70
|
+
removed: [...previousIds].filter(id => !currentIds.has(id)),
|
|
71
|
+
};
|
|
40
72
|
|
|
41
73
|
const catalog = {
|
|
42
74
|
$schema: 'https://aiwg.io/schemas/toolsmith/runtime-catalog-v1.json',
|
|
43
75
|
version: '1.0',
|
|
44
76
|
generated: new Date().toISOString(),
|
|
77
|
+
inputFingerprint,
|
|
78
|
+
delta,
|
|
45
79
|
environment,
|
|
46
80
|
resources,
|
|
47
81
|
tools: tools.available,
|
|
@@ -52,7 +86,11 @@ export class RuntimeDiscovery {
|
|
|
52
86
|
await this.#writeCatalog(catalog);
|
|
53
87
|
await this.#writeRuntimeInfo(catalog);
|
|
54
88
|
|
|
55
|
-
console.log(`Discovery complete: ${catalog.tools.length} tools found`);
|
|
89
|
+
if (!this.quiet) console.log(`Discovery complete: ${catalog.tools.length} tools found`);
|
|
90
|
+
if (!this.quiet) console.log(`Tool delta: +${delta.added.length} added, -${delta.removed.length} removed`);
|
|
91
|
+
if (delta.removed.length) {
|
|
92
|
+
if (!this.quiet) console.warn(`Previously cataloged tools no longer available: ${delta.removed.join(', ')}`);
|
|
93
|
+
}
|
|
56
94
|
return catalog;
|
|
57
95
|
}
|
|
58
96
|
|
|
@@ -146,7 +184,7 @@ export class RuntimeDiscovery {
|
|
|
146
184
|
osVersion = boundedExecSync('ver', 'quick', { encoding: 'utf-8' }).trim();
|
|
147
185
|
}
|
|
148
186
|
} catch (error) {
|
|
149
|
-
console.warn('Could not detect OS version:', error.message);
|
|
187
|
+
if (!this.quiet) console.warn('Could not detect OS version:', error.message);
|
|
150
188
|
}
|
|
151
189
|
|
|
152
190
|
return {
|
|
@@ -155,7 +193,7 @@ export class RuntimeDiscovery {
|
|
|
155
193
|
arch: arch(),
|
|
156
194
|
shell: process.env.SHELL || process.env.ComSpec || '/bin/sh',
|
|
157
195
|
homeDir: homedir(),
|
|
158
|
-
workingDir:
|
|
196
|
+
workingDir: this.cwd
|
|
159
197
|
};
|
|
160
198
|
}
|
|
161
199
|
|
|
@@ -184,7 +222,7 @@ export class RuntimeDiscovery {
|
|
|
184
222
|
diskFreeGb = Math.round((totalFree / (1024 ** 3)) * 10) / 10;
|
|
185
223
|
} else {
|
|
186
224
|
// Unix: use df
|
|
187
|
-
const output = boundedExecFileSync('df', ['-k', '.'], 'quick', { encoding: 'utf-8' });
|
|
225
|
+
const output = boundedExecFileSync('df', ['-k', '.'], 'quick', { encoding: 'utf-8', cwd: this.cwd });
|
|
188
226
|
const lines = output.trim().split('\n');
|
|
189
227
|
if (lines.length > 1) {
|
|
190
228
|
const parts = lines[1].trim().split(/\s+/);
|
|
@@ -199,7 +237,7 @@ export class RuntimeDiscovery {
|
|
|
199
237
|
memoryTotalGb = Math.round((totalMem / (1024 ** 3)) * 10) / 10;
|
|
200
238
|
memoryAvailableGb = Math.round((freeMem / (1024 ** 3)) * 10) / 10;
|
|
201
239
|
} catch (error) {
|
|
202
|
-
console.warn('Could not get resource info:', error.message);
|
|
240
|
+
if (!this.quiet) console.warn('Could not get resource info:', error.message);
|
|
203
241
|
}
|
|
204
242
|
|
|
205
243
|
return {
|
|
@@ -298,9 +336,9 @@ export class RuntimeDiscovery {
|
|
|
298
336
|
* @see https://git.integrolabs.net/roctinam/aiwg/issues/820
|
|
299
337
|
* @private
|
|
300
338
|
*/
|
|
301
|
-
|
|
339
|
+
#safeTools() {
|
|
302
340
|
// Curated list: knownTools database + common dev tools safe to probe
|
|
303
|
-
|
|
341
|
+
return new Set([
|
|
304
342
|
// From knownTools database
|
|
305
343
|
...Object.keys(this.knownTools),
|
|
306
344
|
// Aliases from knownTools
|
|
@@ -328,12 +366,13 @@ export class RuntimeDiscovery {
|
|
|
328
366
|
'yq', 'fx', 'miller',
|
|
329
367
|
'htop', 'ncdu', 'dust', 'procs', 'tokei',
|
|
330
368
|
]);
|
|
369
|
+
}
|
|
331
370
|
|
|
371
|
+
async #discoverTools(paths) {
|
|
332
372
|
const available = [];
|
|
333
373
|
const unavailable = [];
|
|
334
374
|
|
|
335
|
-
for (const name of
|
|
336
|
-
const toolPath = await this.#findToolPath(name);
|
|
375
|
+
for (const [name, toolPath] of paths) {
|
|
337
376
|
if (!toolPath) {
|
|
338
377
|
// Not installed — skip silently (not an error)
|
|
339
378
|
continue;
|
|
@@ -359,7 +398,7 @@ export class RuntimeDiscovery {
|
|
|
359
398
|
}
|
|
360
399
|
|
|
361
400
|
/**
|
|
362
|
-
*
|
|
401
|
+
* Search PATH first, then common user installation directories on Unix.
|
|
363
402
|
* @private
|
|
364
403
|
*/
|
|
365
404
|
async #findToolPath(toolName) {
|
|
@@ -370,8 +409,27 @@ export class RuntimeDiscovery {
|
|
|
370
409
|
.split('\n')[0];
|
|
371
410
|
return path;
|
|
372
411
|
} catch {
|
|
373
|
-
|
|
412
|
+
// Scheduled jobs often omit user installation directories from PATH.
|
|
413
|
+
}
|
|
414
|
+
if (platform() !== 'win32' && !toolName.includes('/') && !toolName.includes('\\')) {
|
|
415
|
+
const home = homedir();
|
|
416
|
+
const directories = new Set([
|
|
417
|
+
join(process.env.CARGO_HOME || join(home, '.cargo'), 'bin'),
|
|
418
|
+
join(home, '.local', 'bin'), join(home, 'bin'),
|
|
419
|
+
join(home, '.npm-global', 'bin'), join(home, 'go', 'bin'),
|
|
420
|
+
join(home, '.bun', 'bin'), join(home, '.deno', 'bin'),
|
|
421
|
+
]);
|
|
422
|
+
for (const directory of directories) {
|
|
423
|
+
const candidate = join(directory, toolName);
|
|
424
|
+
try {
|
|
425
|
+
await access(candidate, constants.X_OK);
|
|
426
|
+
if ((await stat(candidate)).isFile()) return candidate;
|
|
427
|
+
} catch {
|
|
428
|
+
// Continue to the next user directory.
|
|
429
|
+
}
|
|
430
|
+
}
|
|
374
431
|
}
|
|
432
|
+
return null;
|
|
375
433
|
}
|
|
376
434
|
|
|
377
435
|
/**
|
|
@@ -9,14 +9,17 @@
|
|
|
9
9
|
* @issue #934
|
|
10
10
|
* @issue #956
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { dirname, join, relative, resolve, sep } from 'path';
|
|
12
|
+
import { resolve, sep } from 'path';
|
|
13
|
+
import { pinContainedRoot, readContainedFile, writeContainedFile, appendContainedFile, deleteContainedFile, listContainedFiles } from '../../utils/contained-filesystem.mjs';
|
|
15
14
|
export class FilesystemAdapter {
|
|
16
15
|
/** Absolute path where this subsystem's content lives. */
|
|
17
16
|
root;
|
|
17
|
+
store;
|
|
18
18
|
constructor(root) {
|
|
19
19
|
this.root = resolve(root);
|
|
20
|
+
this.store = pinContainedRoot(this.root, undefined, {
|
|
21
|
+
createMissing: true, protectLocationPointer: false, rejectLinkedWrites: true,
|
|
22
|
+
});
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
22
25
|
* Path traversal guard. Rejects `..`, leading `/`, leading `~`, and
|
|
@@ -46,89 +49,28 @@ export class FilesystemAdapter {
|
|
|
46
49
|
return abs;
|
|
47
50
|
}
|
|
48
51
|
async read(path) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return null;
|
|
52
|
-
return readFile(abs, 'utf-8');
|
|
52
|
+
this.resolveSafe(path);
|
|
53
|
+
return readContainedFile(await this.store, path);
|
|
53
54
|
}
|
|
54
55
|
async write(path, content, _meta) {
|
|
55
|
-
|
|
56
|
-
await
|
|
57
|
-
await writeFile(abs, content, 'utf-8');
|
|
56
|
+
this.resolveSafe(path);
|
|
57
|
+
await writeContainedFile(await this.store, path, content);
|
|
58
58
|
}
|
|
59
|
-
/**
|
|
60
|
-
* Atomic append. Uses fs.appendFile, which opens with O_APPEND so the
|
|
61
|
-
* kernel guarantees atomicity for writes ≤ PIPE_BUF (4096 bytes on
|
|
62
|
-
* Linux). Concurrent appenders interleave at line granularity rather
|
|
63
|
-
* than racing read-then-write. See #976.
|
|
64
|
-
*/
|
|
59
|
+
/** Append through a verified O_APPEND handle, without read/replace races. */
|
|
65
60
|
async append(path, content) {
|
|
66
|
-
|
|
67
|
-
await
|
|
68
|
-
await appendFile(abs, content, 'utf-8');
|
|
61
|
+
this.resolveSafe(path);
|
|
62
|
+
await appendContainedFile(await this.store, path, content);
|
|
69
63
|
}
|
|
70
64
|
async list(prefix) {
|
|
71
|
-
|
|
72
|
-
// restricts to entries whose subsystem-relative path starts with it
|
|
73
|
-
// (after path-normalizing the prefix itself).
|
|
74
|
-
if (typeof prefix !== 'string') {
|
|
65
|
+
if (typeof prefix !== 'string')
|
|
75
66
|
throw new Error('storage(fs): list prefix must be a string');
|
|
76
|
-
|
|
77
|
-
if (prefix.length > 0) {
|
|
78
|
-
// resolveSafe also validates the prefix shape
|
|
67
|
+
if (prefix)
|
|
79
68
|
this.resolveSafe(prefix);
|
|
80
|
-
|
|
81
|
-
if (!existsSync(this.root))
|
|
82
|
-
return [];
|
|
83
|
-
const out = [];
|
|
84
|
-
await walkInto(this.root, this.root, prefix, out);
|
|
85
|
-
return out;
|
|
69
|
+
return listContainedFiles(await this.store, prefix);
|
|
86
70
|
}
|
|
87
71
|
async delete(path) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return;
|
|
91
|
-
await rm(abs, { force: true });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
async function walkInto(root, dir, prefix, out) {
|
|
95
|
-
let entries;
|
|
96
|
-
try {
|
|
97
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
for (const e of entries) {
|
|
103
|
-
const full = join(dir, e.name);
|
|
104
|
-
const rel = relative(root, full).split(sep).join('/');
|
|
105
|
-
if (e.isDirectory()) {
|
|
106
|
-
// Descend if the dir could contain matches
|
|
107
|
-
if (prefix === '' || rel.startsWith(prefix) || prefix.startsWith(rel + '/')) {
|
|
108
|
-
await walkInto(root, full, prefix, out);
|
|
109
|
-
}
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
if (!e.isFile())
|
|
113
|
-
continue;
|
|
114
|
-
if (prefix !== '' && !rel.startsWith(prefix))
|
|
115
|
-
continue;
|
|
116
|
-
let size;
|
|
117
|
-
let modifiedAt;
|
|
118
|
-
try {
|
|
119
|
-
const s = await stat(full);
|
|
120
|
-
size = s.size;
|
|
121
|
-
modifiedAt = s.mtime;
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
// ignore stat failures
|
|
125
|
-
}
|
|
126
|
-
const entry = { path: rel };
|
|
127
|
-
if (size !== undefined)
|
|
128
|
-
entry.size = size;
|
|
129
|
-
if (modifiedAt !== undefined)
|
|
130
|
-
entry.modifiedAt = modifiedAt;
|
|
131
|
-
out.push(entry);
|
|
72
|
+
this.resolveSafe(path);
|
|
73
|
+
await deleteContainedFile(await this.store, path);
|
|
132
74
|
}
|
|
133
75
|
}
|
|
134
76
|
//# sourceMappingURL=fs.js.map
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* `code`. Access material stays inside the transport: it is never part of an
|
|
14
14
|
* error, a result or a log line.
|
|
15
15
|
*/
|
|
16
|
+
import { signalChild } from '../process/signal-child.mjs';
|
|
16
17
|
import { spawn } from 'node:child_process';
|
|
17
18
|
export class ForgeReadError extends Error {
|
|
18
19
|
code;
|
|
@@ -115,7 +116,7 @@ function spawnCliRunner(binary, maxBytes) {
|
|
|
115
116
|
size += chunk.length;
|
|
116
117
|
if (size > maxBytes + 65_536) {
|
|
117
118
|
oversized = true;
|
|
118
|
-
child
|
|
119
|
+
signalChild(child);
|
|
119
120
|
}
|
|
120
121
|
else
|
|
121
122
|
chunks.push(chunk);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type InstallMode = 'npm' | 'web' | 'source';
|
|
2
|
+
|
|
3
|
+
export interface UpdateResult {
|
|
4
|
+
mode: InstallMode;
|
|
5
|
+
packageRoot: string;
|
|
6
|
+
packageName: string | null;
|
|
7
|
+
channel: string;
|
|
8
|
+
status: 'updated' | 'current' | 'manual' | 'dry-run' | 'unsupported-offline';
|
|
9
|
+
changed: boolean;
|
|
10
|
+
version?: string;
|
|
11
|
+
command?: string;
|
|
12
|
+
message: string;
|
|
13
|
+
identity?: import('../installation/manager.mjs').InstallationIdentity;
|
|
14
|
+
identityPersistent?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function detectInstallMode(options?: Record<string, unknown>): Promise<{
|
|
18
|
+
mode: InstallMode;
|
|
19
|
+
packageRoot: string;
|
|
20
|
+
packageName: string | null;
|
|
21
|
+
}>;
|
|
22
|
+
export function updateInstallation(options?: Record<string, unknown>): Promise<UpdateResult>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { BigIntStats } from 'node:fs';
|
|
2
|
+
import type { FileHandle } from 'node:fs/promises';
|
|
3
|
+
|
|
4
|
+
/** Opaque pinned filesystem boundary; configuration is captured only once. */
|
|
5
|
+
export interface ContainedRoot {
|
|
6
|
+
readonly configured: string;
|
|
7
|
+
readonly root: string;
|
|
8
|
+
readonly identity?: BigIntStats | null;
|
|
9
|
+
readonly error?: Error;
|
|
10
|
+
}
|
|
11
|
+
export interface ContainmentOptions {
|
|
12
|
+
/** Storage may create missing configured roots through a pinned ancestor. */
|
|
13
|
+
createMissing?: boolean;
|
|
14
|
+
/** Artifacts protect location pointers; ordinary storage metadata may opt out. */
|
|
15
|
+
protectLocationPointer?: boolean;
|
|
16
|
+
/** Storage refuses replacement of hardlinks; artifact atomic writes retain compatibility. */
|
|
17
|
+
rejectLinkedWrites?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface ContainedFile {
|
|
20
|
+
handle: FileHandle;
|
|
21
|
+
fullPath: string;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
commit(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export interface ContainedEntry {
|
|
26
|
+
path: string;
|
|
27
|
+
size: number;
|
|
28
|
+
modifiedAt: Date;
|
|
29
|
+
}
|
|
30
|
+
export function validateContainedPath(relative: string): void;
|
|
31
|
+
export function pinContainedRoot(configured: string, projectRoot?: string, options?: ContainmentOptions): Promise<ContainedRoot>;
|
|
32
|
+
export function openContainedFile(store: ContainedRoot, relative: string, options?: { write?: boolean }): Promise<ContainedFile>;
|
|
33
|
+
export function readContainedFile(store: ContainedRoot, relative: string): Promise<string | null>;
|
|
34
|
+
export function writeContainedFile(store: ContainedRoot, relative: string, content: string): Promise<void>;
|
|
35
|
+
export function appendContainedFile(store: ContainedRoot, relative: string, content: string): Promise<void>;
|
|
36
|
+
export function deleteContainedFile(store: ContainedRoot, relative: string): Promise<void>;
|
|
37
|
+
export function listContainedFiles(store: ContainedRoot, prefix?: string): Promise<ContainedEntry[]>;
|