@uniqbit/mate-core 0.18.4 → 1.0.0-canary.0
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/claude-plugin/hooks/companion-instructions.mjs +9 -0
- package/claude-plugin/hooks/hooks.json +12 -2
- package/opencode-plugin/src/add-dir.ts +76 -10
- package/opencode-plugin/src/companion.ts +1 -8
- package/opencode-plugin/src/server.ts +2 -15
- package/package.json +10 -2
- package/src/brand/art.generated.ts +69 -0
- package/src/brand/index.ts +12 -0
- package/src/brand/palette.ts +19 -0
- package/src/brand/svg.generated.ts +8 -0
- package/src/brand/terminal.ts +83 -0
- package/src/brand/types.ts +33 -0
- package/src/cli/commands/artifact/artifact.ts +8 -0
- package/src/cli/commands/artifact/delivery/archive.ts +117 -0
- package/src/cli/commands/artifact/delivery/branch-flags.ts +55 -0
- package/src/cli/commands/artifact/delivery/environment.ts +104 -0
- package/src/cli/commands/artifact/delivery/link.ts +154 -0
- package/src/cli/commands/artifact/delivery/output.ts +58 -0
- package/src/cli/commands/artifact/finish/command.ts +33 -3
- package/src/cli/commands/artifact/finish/engine.ts +47 -3
- package/src/cli/commands/artifact/finish/finisher.ts +10 -0
- package/src/cli/commands/artifact/finish/openspec.ts +200 -129
- package/src/cli/commands/artifact/finish/secret-scan.ts +43 -0
- package/src/cli/commands/artifact/pending/command.ts +31 -3
- package/src/cli/commands/artifact/pending/discovery.ts +21 -57
- package/src/cli/commands/cap/graphify.ts +52 -3
- package/src/cli/commands/cap/index-cmd.ts +8 -1
- package/src/cli/commands/cap/openspec.ts +13 -25
- package/src/cli/commands/companion/open.ts +56 -39
- package/src/cli/commands/companion/update.ts +75 -42
- package/src/cli/commands/doctor.ts +257 -78
- package/src/cli/commands/install.ts +0 -8
- package/src/cli/commands/plugin/install.ts +9 -4
- package/src/cli/commands/plugin/plugin.ts +3 -2
- package/src/cli/commands/report/adapter.ts +11 -2
- package/src/cli/commands/report/contract.ts +21 -0
- package/src/cli/commands/report/highlight.ts +34 -4
- package/src/cli/commands/report/markdown.ts +110 -0
- package/src/cli/commands/report/palette.ts +118 -0
- package/src/cli/commands/report/renderer.ts +456 -153
- package/src/cli/commands/report/types.ts +16 -0
- package/src/cli/commands/setup.ts +7 -15
- package/src/cli/commands/studio/client-asset.ts +16 -0
- package/src/cli/commands/studio/openspec-cli.ts +10 -58
- package/src/cli/commands/studio/routes.ts +2 -0
- package/src/cli/commands/studio/selection.ts +19 -1
- package/src/cli/commands/studio/server.ts +42 -1
- package/src/cli/commands/studio/terminal-launch.ts +7 -16
- package/src/cli/commands/studio/views/client.ts +6 -616
- package/src/cli/commands/studio/views/dashboard/changes.tsx +5 -10
- package/src/cli/commands/studio/views/document.tsx +137 -69
- package/src/cli/commands/studio/views/markdown.tsx +250 -0
- package/src/cli/commands/studio/views/palette.ts +134 -0
- package/src/cli/commands/studio/views/reports/index.tsx +2 -7
- package/src/cli/commands/studio/views/skills/index.tsx +43 -62
- package/src/cli/commands/studio/views/specs/index.tsx +7 -15
- package/src/cli/commands/studio/views/studio-client/actions.ts +51 -0
- package/src/cli/commands/studio/views/studio-client/companion.ts +13 -0
- package/src/cli/commands/studio/views/studio-client/index.ts +32 -0
- package/src/cli/commands/studio/views/studio-client/keys.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/main.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/page.ts +146 -0
- package/src/cli/commands/studio/views/studio-client/panels.ts +57 -0
- package/src/cli/commands/studio/views/studio-client/reports.ts +122 -0
- package/src/cli/commands/studio/views/studio-client/theme.ts +63 -0
- package/src/cli/commands/studio/views/studio-client/vault-editor.ts +166 -0
- package/src/cli/commands/studio/views/studio-client/vault-pane.ts +202 -0
- package/src/cli/commands/studio/views/studio-client/vault-tree.ts +258 -0
- package/src/cli/commands/studio/views/studio-client.generated.ts +2 -0
- package/src/cli/commands/studio/views/styles.ts +217 -206
- package/src/cli/commands/studio/views/vault/state.ts +3 -5
- package/src/cli/commands/studio/views/workflow/delivery-path.tsx +370 -0
- package/src/cli/commands/studio/views/workflow/graph.ts +220 -0
- package/src/cli/commands/studio/views/workflow/index.tsx +2 -3
- package/src/cli/commands/studio/views/workflow/steps.ts +228 -287
- package/src/cli/commands/wrap.ts +9 -42
- package/src/cli/main.ts +4 -1
- package/src/cli/parse-flags.ts +25 -7
- package/src/cli/setup-selector.tsx +38 -162
- package/src/cli/usage.ts +5 -2
- package/src/hooks/companion-instructions.ts +190 -0
- package/src/hooks/hook-io.ts +63 -0
- package/src/hooks/session-banner.ts +21 -29
- package/src/hooks/session-guidance.ts +10 -36
- package/src/hooks/session-prepare.ts +115 -28
- package/src/hooks/validate-artifact-path.ts +82 -24
- package/src/index.ts +4 -0
- package/src/lib/change-layout.ts +154 -0
- package/src/lib/components/wizard-header.tsx +3 -1
- package/src/lib/context-mode-package.ts +24 -11
- package/src/lib/delivery/archive-change.ts +154 -0
- package/src/lib/delivery/branches.ts +108 -0
- package/src/lib/delivery/environment.ts +29 -0
- package/src/lib/delivery/forge.ts +279 -0
- package/src/lib/delivery/overlap.ts +202 -0
- package/src/lib/delivery/record-delivery.ts +196 -0
- package/src/lib/delivery/record.ts +343 -0
- package/src/lib/delivery/refresh.ts +174 -0
- package/src/lib/delivery/repository-access.ts +97 -0
- package/src/lib/delivery/stages.ts +111 -0
- package/src/lib/delivery/token.ts +58 -0
- package/src/lib/frontmatter.ts +18 -0
- package/src/lib/opencode-plugin-package.ts +5 -5
- package/src/lib/openspec-cli.ts +116 -0
- package/src/lib/openspec-scopes.ts +86 -0
- package/src/lib/orchestrator/adapters/claude-args.ts +50 -0
- package/src/lib/orchestrator/adapters/claude.ts +60 -162
- package/src/lib/orchestrator/adapters/opencode.ts +9 -24
- package/src/lib/orchestrator/config-store.ts +2 -70
- package/src/lib/orchestrator/editor.ts +9 -9
- package/src/lib/orchestrator/openspec-store.ts +14 -54
- package/src/lib/orchestrator/projection-claude-commands.ts +36 -0
- package/src/lib/orchestrator/projection-claude-links.ts +159 -0
- package/src/lib/orchestrator/projection-claude-skills.ts +19 -190
- package/src/lib/orchestrator/projection-entries.ts +45 -15
- package/src/lib/orchestrator/projection-runtime-documents.ts +15 -6
- package/src/lib/orchestrator/projection-types.ts +3 -2
- package/src/lib/orchestrator/refresh-working-claude-links.ts +87 -0
- package/src/lib/orchestrator/session-prepare.ts +49 -87
- package/src/lib/orchestrator/setup-compatibilities.ts +18 -75
- package/src/lib/orchestrator/types.ts +2 -2
- package/src/lib/orchestrator/wrap-pass.ts +22 -88
- package/src/lib/package-paths.ts +8 -0
- package/src/opencode/companion-hooks.ts +64 -5
- package/src/opencode/companion-policy.ts +37 -18
- package/src/opencode/projected-guidance.ts +4 -1
- package/src/opencode/session-prepare.ts +2 -1
- package/src/opencode/tui.tsx +4 -2
- package/src/runtime/artifact-refusal.ts +63 -0
- package/src/runtime/claude-delivery-plan.ts +89 -7
- package/src/runtime/commit-attribution-guard.ts +181 -0
- package/src/runtime/companion-guidance.ts +15 -3
- package/src/runtime/companion-instructions.ts +340 -0
- package/src/runtime/error-message.ts +4 -0
- package/src/runtime/guidance.ts +0 -17
- package/src/runtime/index.ts +5 -0
- package/src/runtime/managed-block.ts +117 -0
- package/src/runtime/markdown-scan.ts +266 -0
- package/src/runtime/policy.ts +16 -15
- package/src/runtime/projected-flag.ts +7 -0
- package/src/runtime/projected-guidance.ts +1 -0
- package/src/runtime/runtime-document-manifest.ts +62 -28
- package/src/runtime/secret-guard.ts +148 -0
- package/src/runtime/secret-patterns.ts +164 -0
- package/src/runtime/secret-scan.ts +90 -0
- package/src/runtime/session-kind.ts +13 -0
- package/src/runtime/shell-heredoc.ts +81 -0
- package/src/runtime/wrap-state.ts +36 -6
- package/src/runtime/write-atomic.ts +64 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +8 -6
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +7 -9
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +10 -12
- package/src/templates/mate-skills/THIRD-PARTY-NOTICES.md +40 -0
- package/src/templates/mate-skills/agents/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/agents/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/agents/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{claude/mate-openspec-backfill → agents/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/agents/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/agents/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/agents/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/agents/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/claude/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/claude/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/claude/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{agents/mate-openspec-backfill → claude/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/claude/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/claude/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/claude/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/claude/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/openspec-references.ts +140 -0
- package/src/templates/root/TEMPLATE_AGENTS.md +2 -2
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3727 -320
- package/src/tools/setup/capabilities/context-mode.ts +25 -5
- package/src/tools/setup/capabilities/graphify-gate.ts +31 -0
- package/src/tools/setup/capabilities/graphify-secrets.ts +255 -0
- package/src/tools/setup/capabilities/graphify.ts +12 -0
- package/src/tools/setup/capabilities/openspec.ts +273 -270
- package/src/tools/setup/capabilities/tokensave.ts +36 -3
- package/src/tools/setup/context-services.ts +9 -17
- package/src/tools/setup/dynamic-plugins/audiences.ts +7 -39
- package/src/tools/setup/dynamic-plugins/declarations.ts +1 -1
- package/src/tools/setup/dynamic-plugins/hydrate.ts +4 -7
- package/src/tools/setup/dynamic-plugins/install.ts +24 -28
- package/src/tools/setup/engine.ts +93 -148
- package/src/tools/setup/managed-output.ts +51 -0
- package/src/tools/setup/mate.ts +86 -11
- package/src/tools/setup/plugin.ts +37 -17
- package/src/tools/setup/plugins/gitignore.ts +1 -7
- package/src/tools/setup/plugins/guidance.ts +7 -4
- package/src/tools/setup/providers/agent-file-sections.ts +4 -2
- package/src/tools/setup/providers/claude-attribution.ts +65 -0
- package/src/tools/setup/providers/claude-plugin-folders.ts +625 -0
- package/src/tools/setup/providers/claude-plugin-hooks.ts +39 -19
- package/src/tools/setup/providers/claude.ts +285 -34
- package/src/tools/setup/providers/opencode.ts +180 -11
- package/src/tools/setup/registry.ts +2 -24
- package/src/tools/setup/surface-target.ts +4 -13
- package/src/tools/setup/utils.ts +14 -4
- package/src/tools/setup.ts +131 -18
- package/wrappers/bin/graphify +52 -4
- package/wrappers/bin/openspec +33 -9
- package/wrappers/lib/graphify-gate.mjs +8 -0
- package/opencode-plugin/src/audience-layer.ts +0 -91
- package/src/cli/commands/studio/views/workflow/transcript.tsx +0 -190
- package/src/runtime/audience-layer.ts +0 -84
- package/src/runtime/opencode-audience-layer.ts +0 -176
- package/src/tools/setup/audience-isolation.fixture.ts +0 -325
- package/src/tools/setup/audience-layer.ts +0 -207
- package/src/tools/setup/audience-registrations.ts +0 -52
- package/src/tools/setup/contribution-ledger.ts +0 -207
- package/src/tools/setup/isolation-diagnostics.ts +0 -139
- package/src/tools/setup/runtime-documents.ts +0 -291
- package/src/tools/setup/upgrade-sweep.ts +0 -153
|
@@ -4,6 +4,17 @@ import path from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { FRAMEWORK_NAME } from "../../../framework";
|
|
6
6
|
import { isLegacyMateOpenCodePluginReference } from "../../../lib/opencode-plugin-package";
|
|
7
|
+
import { resolveSecretPolicy } from "../../../runtime/policy";
|
|
8
|
+
import { runtimeOpenCodePluginRoot } from "../../../runtime/runtime-directory";
|
|
9
|
+
import {
|
|
10
|
+
OPENCODE_CONFIG_DOCUMENT,
|
|
11
|
+
OPENCODE_MCP_SERVERS_AT,
|
|
12
|
+
renderedDocument,
|
|
13
|
+
type ManagedRegion,
|
|
14
|
+
type RenderedRuntimeDocument,
|
|
15
|
+
type RenderedRuntimeDocuments,
|
|
16
|
+
} from "../../../runtime/runtime-document-manifest";
|
|
17
|
+
import { projectableSecretDenyPatterns } from "../../../runtime/secret-patterns";
|
|
7
18
|
import { refreshFromTemplate, stripGuidanceBlock } from "../plugins/guidance";
|
|
8
19
|
import { stripSectionFromFile, type RemoveHeadingSectionOptions } from "./agent-file-sections";
|
|
9
20
|
import { reconcileSharedSkillTree } from "./agents-skill-root";
|
|
@@ -416,16 +427,6 @@ export async function removeOpenCodeForeignPluginReferences(
|
|
|
416
427
|
|
|
417
428
|
const OPENCODE_CONTRIBUTION_CONFIG_FILES = ["opencode.json"];
|
|
418
429
|
|
|
419
|
-
/**
|
|
420
|
-
* The surface's reader for an audience layer it wrote: what the Mate OpenCode
|
|
421
|
-
* plugin applies. Defined under `runtime/` so the import-isolated plugin can
|
|
422
|
-
* reach it; this surface owns the format it reads.
|
|
423
|
-
*/
|
|
424
|
-
export {
|
|
425
|
-
readOpenCodeAudienceLayer,
|
|
426
|
-
type OpenCodeLayerContents,
|
|
427
|
-
} from "../../../runtime/opencode-audience-layer";
|
|
428
|
-
|
|
429
430
|
export async function reconcileOpenCodeContributions(
|
|
430
431
|
ctx: SetupContext,
|
|
431
432
|
inputs: CapabilityContributionInput[],
|
|
@@ -512,7 +513,7 @@ async function reconcileOpenCodeMcpContributions(
|
|
|
512
513
|
}
|
|
513
514
|
|
|
514
515
|
/** The Mate-managed V2 `mcp.servers` map of one pass, as a value. */
|
|
515
|
-
|
|
516
|
+
function renderManagedOpenCodeMcpServers(
|
|
516
517
|
contributions: CapabilityContributionInput[] = [],
|
|
517
518
|
): Record<string, unknown> {
|
|
518
519
|
const servers: Record<string, unknown> = {};
|
|
@@ -539,6 +540,174 @@ export function renderCompanionExternalDirectoryPermissions(
|
|
|
539
540
|
];
|
|
540
541
|
}
|
|
541
542
|
|
|
543
|
+
/**
|
|
544
|
+
* V2 deny rules for the default secret files: `read`, and `edit`, which covers
|
|
545
|
+
* write and patch. Only patterns that can't match an allow-listed file are
|
|
546
|
+
* rendered, as for Claude; the middleware enforces the rest of the set.
|
|
547
|
+
*/
|
|
548
|
+
export function renderSecretDenyPermissions(
|
|
549
|
+
companionPath: string,
|
|
550
|
+
): Array<{ action: string; resource: string; effect: string }> {
|
|
551
|
+
if (!resolveSecretPolicy(companionPath).guardrails) return [];
|
|
552
|
+
return projectableSecretDenyPatterns().flatMap((pattern) =>
|
|
553
|
+
["read", "edit"].map((action) => ({ action, resource: `**/${pattern}`, effect: "deny" })),
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* The working target's OpenCode document, rendered as a value, with the
|
|
559
|
+
* companion's `.opencode/opencode.json` folded in.
|
|
560
|
+
*
|
|
561
|
+
* The Mate plugin root is what makes an Unmanaged OpenCode session load the
|
|
562
|
+
* companion guidance at all: OpenCode has no config channel for a generated
|
|
563
|
+
* system prompt, so the plugin is the only injection point. It is named through
|
|
564
|
+
* the runtime directory, so a reinstall does not strand a wrapped repository.
|
|
565
|
+
* The managed launch carries no overlay of its own, so this document is the one
|
|
566
|
+
* place every OpenCode session in the repository loads it from.
|
|
567
|
+
*/
|
|
568
|
+
export async function renderOpenCodeWorkingDocuments(
|
|
569
|
+
ctx: SetupContext,
|
|
570
|
+
inputs: CapabilityContributionInput[],
|
|
571
|
+
): Promise<RenderedRuntimeDocuments> {
|
|
572
|
+
const documents = renderedDocument(OPENCODE_CONFIG_DOCUMENT, [
|
|
573
|
+
{
|
|
574
|
+
at: ["plugins"],
|
|
575
|
+
kind: "list",
|
|
576
|
+
values: [runtimeOpenCodePluginRoot()],
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
at: [...OPENCODE_MCP_SERVERS_AT],
|
|
580
|
+
kind: "map",
|
|
581
|
+
entries: renderManagedOpenCodeMcpServers(inputs),
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
at: ["permissions"],
|
|
585
|
+
kind: "list",
|
|
586
|
+
values: [
|
|
587
|
+
...renderCompanionExternalDirectoryPermissions(ctx.companionPath),
|
|
588
|
+
...renderSecretDenyPermissions(ctx.companionPath),
|
|
589
|
+
],
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
at: ["skills"],
|
|
593
|
+
kind: "list",
|
|
594
|
+
values: [path.join(ctx.companionPath, ".agents", "skills")],
|
|
595
|
+
},
|
|
596
|
+
{ at: ["references"], kind: "map", entries: { mate: ctx.companionPath } },
|
|
597
|
+
]);
|
|
598
|
+
if (ctx.target !== "working") return { documents, warnings: [] };
|
|
599
|
+
return mergeCompanionOpenCodeConfig(ctx.companionPath, documents);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/** Keys Mate renders its own regions for; the companion's values join those regions. */
|
|
603
|
+
const MATE_OPENCODE_KEYS = new Set(["plugins", "permissions", "skills", "mcp", "references"]);
|
|
604
|
+
|
|
605
|
+
function relativePathValues(value: unknown, at: string[]): string[] {
|
|
606
|
+
if (typeof value === "string") {
|
|
607
|
+
return value.startsWith("./") || value.startsWith("../") ? [at.join(".")] : [];
|
|
608
|
+
}
|
|
609
|
+
if (Array.isArray(value)) {
|
|
610
|
+
return value.flatMap((entry, index) => relativePathValues(entry, [...at, String(index)]));
|
|
611
|
+
}
|
|
612
|
+
if (isRecord(value)) {
|
|
613
|
+
return Object.entries(value).flatMap(([key, entry]) => relativePathValues(entry, [...at, key]));
|
|
614
|
+
}
|
|
615
|
+
return [];
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/** Replaces the region at `at` with what `merge` builds from it, or appends one. */
|
|
619
|
+
function upsertRegion(
|
|
620
|
+
regions: ManagedRegion[],
|
|
621
|
+
at: string[],
|
|
622
|
+
merge: (found: ManagedRegion | undefined) => ManagedRegion,
|
|
623
|
+
): void {
|
|
624
|
+
const key = JSON.stringify(at);
|
|
625
|
+
const index = regions.findIndex((region) => JSON.stringify(region.at) === key);
|
|
626
|
+
if (index === -1) regions.push(merge(undefined));
|
|
627
|
+
else regions[index] = merge(regions[index]);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/** Mate's own values stay; companion values it does not already list go first. */
|
|
631
|
+
function mergeListRegion(regions: ManagedRegion[], at: string[], extra: unknown[]): void {
|
|
632
|
+
upsertRegion(regions, at, (found) => {
|
|
633
|
+
const own = found?.kind === "list" ? found.values : [];
|
|
634
|
+
const seen = new Set(own.map((value) => JSON.stringify(value)));
|
|
635
|
+
const values = [...extra.filter((value) => !seen.has(JSON.stringify(value))), ...own];
|
|
636
|
+
return { at, kind: "list", values };
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/** Mate's own entries win over companion entries of the same name. */
|
|
641
|
+
function mergeMapRegion(
|
|
642
|
+
regions: ManagedRegion[],
|
|
643
|
+
at: string[],
|
|
644
|
+
extra: Record<string, unknown>,
|
|
645
|
+
): void {
|
|
646
|
+
upsertRegion(regions, at, (found) => {
|
|
647
|
+
const own = found?.kind === "map" ? found.entries : {};
|
|
648
|
+
return { at, kind: "map", entries: { ...extra, ...own } };
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/** Relative reference paths resolved against the companion's `.opencode/`. */
|
|
653
|
+
function resolveReferences(
|
|
654
|
+
references: Record<string, unknown>,
|
|
655
|
+
companionDir: string,
|
|
656
|
+
): Record<string, unknown> {
|
|
657
|
+
return Object.fromEntries(
|
|
658
|
+
Object.entries(references).map(([name, reference]) => [
|
|
659
|
+
name,
|
|
660
|
+
typeof reference === "string" && !path.isAbsolute(reference)
|
|
661
|
+
? path.resolve(companionDir, reference)
|
|
662
|
+
: reference,
|
|
663
|
+
]),
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Folds the companion's `.opencode/opencode.json` into the wrapped document.
|
|
669
|
+
* Mate's regions beat companion values; other top-level keys yield to the
|
|
670
|
+
* repository's. `references` resolve against the companion's `.opencode/`;
|
|
671
|
+
* other relative paths are reported, not rewritten.
|
|
672
|
+
*/
|
|
673
|
+
async function mergeCompanionOpenCodeConfig(
|
|
674
|
+
companionPath: string,
|
|
675
|
+
documents: RenderedRuntimeDocument[],
|
|
676
|
+
): Promise<RenderedRuntimeDocuments> {
|
|
677
|
+
const [rendered] = documents;
|
|
678
|
+
const companionConfigPath = path.join(companionPath, ".opencode", "opencode.json");
|
|
679
|
+
const companion = rendered ? await readOpenCodeConfig(companionConfigPath) : null;
|
|
680
|
+
if (!rendered || !companion?.present) return { documents, warnings: [] };
|
|
681
|
+
|
|
682
|
+
const regions = [...rendered.regions] as ManagedRegion[];
|
|
683
|
+
const warnings: string[] = [];
|
|
684
|
+
|
|
685
|
+
const companionDir = path.dirname(companionConfigPath);
|
|
686
|
+
for (const [key, value] of Object.entries(companion.config)) {
|
|
687
|
+
if (key === "$schema") continue;
|
|
688
|
+
if (key === "references" && isRecord(value)) {
|
|
689
|
+
mergeMapRegion(regions, ["references"], resolveReferences(value, companionDir));
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
for (const at of relativePathValues(value, [key])) {
|
|
693
|
+
warnings.push(
|
|
694
|
+
`${companionConfigPath}: ${at} is a relative path, which resolves against the working repository once merged into ${OPENCODE_CONFIG_DOCUMENT}; use an absolute path.`,
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
if (MATE_OPENCODE_KEYS.has(key)) {
|
|
698
|
+
if (key === "mcp" && isRecord(value) && isRecord(value.servers)) {
|
|
699
|
+
mergeMapRegion(regions, [...OPENCODE_MCP_SERVERS_AT], value.servers);
|
|
700
|
+
} else if (Array.isArray(value)) {
|
|
701
|
+
mergeListRegion(regions, [key], value);
|
|
702
|
+
}
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
regions.push({ at: [key], kind: "value", value, yields: true });
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return { documents: [{ path: OPENCODE_CONFIG_DOCUMENT, regions }], warnings };
|
|
709
|
+
}
|
|
710
|
+
|
|
542
711
|
/**
|
|
543
712
|
* Reconcile declared agent definition files under `.opencode/agents/`. Runs
|
|
544
713
|
* in both companion and hub scope — an agent definition is fully
|
|
@@ -3,22 +3,13 @@ import type { Plugin, PluginPolicy, PluginRegistration } from "./plugin";
|
|
|
3
3
|
export interface NormalizedRegistration {
|
|
4
4
|
plugin: Plugin;
|
|
5
5
|
policy: PluginPolicy;
|
|
6
|
-
/** The audience that declared the plugin; `null` for base. */
|
|
7
|
-
audience: string | null;
|
|
8
|
-
/** The declared package a dynamic plugin was loaded from; absent for compiled-in plugins. */
|
|
9
|
-
packageName?: string;
|
|
10
6
|
}
|
|
11
7
|
|
|
12
8
|
export function normalizeRegistration(entry: PluginRegistration): NormalizedRegistration {
|
|
13
9
|
if ("plugin" in entry && "policy" in entry) {
|
|
14
|
-
return {
|
|
15
|
-
plugin: entry.plugin,
|
|
16
|
-
policy: entry.policy,
|
|
17
|
-
audience: entry.audience ?? null,
|
|
18
|
-
...(entry.packageName ? { packageName: entry.packageName } : {}),
|
|
19
|
-
};
|
|
10
|
+
return { plugin: entry.plugin, policy: entry.policy };
|
|
20
11
|
}
|
|
21
|
-
return { plugin: entry, policy: entry.defaultSelected ? "default" : "optional"
|
|
12
|
+
return { plugin: entry, policy: entry.defaultSelected ? "default" : "optional" };
|
|
22
13
|
}
|
|
23
14
|
|
|
24
15
|
export class PluginRegistry {
|
|
@@ -41,19 +32,6 @@ export class PluginRegistry {
|
|
|
41
32
|
return this.entries.map((entry) => ({ ...entry }));
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
/**
|
|
45
|
-
* Compiled-in plugins and top-level declarations: the only registrations a
|
|
46
|
-
* pass that writes a persisted surface may reconcile.
|
|
47
|
-
*/
|
|
48
|
-
baseRegistrations(): NormalizedRegistration[] {
|
|
49
|
-
return this.getEntries().filter((entry) => entry.audience === null);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** The registrations one audience declared; only its launch layer renders them. */
|
|
53
|
-
audienceRegistrations(audience: string): NormalizedRegistration[] {
|
|
54
|
-
return this.getEntries().filter((entry) => entry.audience === audience);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
35
|
getPolicy(pluginId: string): PluginPolicy | undefined {
|
|
58
36
|
return this.entries.find((entry) => entry.plugin.id === pluginId)?.policy;
|
|
59
37
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
-
import { audienceLayerDir } from "../../runtime/audience-layer";
|
|
4
3
|
import type { ProjectionTarget, SetupContext } from "./plugin";
|
|
5
4
|
|
|
6
5
|
/**
|
|
@@ -15,9 +14,6 @@ export function projectionTarget(ctx: SetupContext): ProjectionTarget {
|
|
|
15
14
|
"a working-target reconcile needs a Working Repository in scope; none was supplied",
|
|
16
15
|
);
|
|
17
16
|
}
|
|
18
|
-
if (target === "audience-layer" && !ctx.audience) {
|
|
19
|
-
throw new Error("an audience-layer reconcile needs an audience; none was supplied");
|
|
20
|
-
}
|
|
21
17
|
return target;
|
|
22
18
|
}
|
|
23
19
|
|
|
@@ -42,18 +38,13 @@ export function projectionRepoRoot(ctx: SetupContext): string {
|
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
/**
|
|
45
|
-
* The root a Runtime Surface writes into
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* Surface ever computes a path inside one.
|
|
41
|
+
* The root a Runtime Surface writes into. Defined for the companion target
|
|
42
|
+
* only: a working-target pass renders its documents for the Projection Root to
|
|
43
|
+
* place, so no Runtime Surface ever computes a path inside a Working Repository.
|
|
49
44
|
*/
|
|
50
45
|
export function surfaceRoot(ctx: SetupContext): string {
|
|
51
|
-
|
|
52
|
-
if (target === "working") {
|
|
46
|
+
if (projectionTarget(ctx) === "working") {
|
|
53
47
|
throw new Error("a working-target reconcile renders its documents; it writes no surface root");
|
|
54
48
|
}
|
|
55
|
-
if (target === "audience-layer") {
|
|
56
|
-
return ctx.layerRoot ?? audienceLayerDir(ctx.companionPath, ctx.audience!);
|
|
57
|
-
}
|
|
58
49
|
return ctx.companionPath;
|
|
59
50
|
}
|
package/src/tools/setup/utils.ts
CHANGED
|
@@ -65,21 +65,31 @@ function waitForClose(child: ChildProcess, formatError: (code: number) => string
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
export interface RunCommandOptions {
|
|
69
|
+
cwd?: string;
|
|
70
|
+
/** Merged over `process.env` for the child only; Mate's own env is untouched. */
|
|
71
|
+
env?: Record<string, string>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function childEnv(options: RunCommandOptions): { env?: NodeJS.ProcessEnv } {
|
|
75
|
+
return options.env ? { env: { ...process.env, ...options.env } } : {};
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
export async function runCommand(
|
|
69
79
|
command: string,
|
|
70
80
|
args: string[],
|
|
71
|
-
options:
|
|
81
|
+
options: RunCommandOptions = {},
|
|
72
82
|
): Promise<void> {
|
|
73
|
-
const child = spawn(command, args, { cwd: options.cwd, stdio: "inherit" });
|
|
83
|
+
const child = spawn(command, args, { cwd: options.cwd, stdio: "inherit", ...childEnv(options) });
|
|
74
84
|
await waitForClose(child, (code) => `${command} ${args.join(" ")} exited with code ${code}`);
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
export async function runCommandSilently(
|
|
78
88
|
command: string,
|
|
79
89
|
args: string[],
|
|
80
|
-
options:
|
|
90
|
+
options: RunCommandOptions = {},
|
|
81
91
|
): Promise<void> {
|
|
82
|
-
const child = spawn(command, args, { cwd: options.cwd, stdio: "pipe" });
|
|
92
|
+
const child = spawn(command, args, { cwd: options.cwd, stdio: "pipe", ...childEnv(options) });
|
|
83
93
|
await waitForClose(child, (code) => `${command} ${args.join(" ")} exited with code ${code}`);
|
|
84
94
|
}
|
|
85
95
|
|
package/src/tools/setup.ts
CHANGED
|
@@ -18,11 +18,25 @@ import {
|
|
|
18
18
|
type CapabilityConfig,
|
|
19
19
|
type FrameworkConfig,
|
|
20
20
|
} from "../lib/orchestrator/types";
|
|
21
|
+
import { getClaudePluginRoot } from "../lib/package-paths";
|
|
22
|
+
import type { RenderedRuntimeDocuments } from "../runtime/runtime-document-manifest";
|
|
21
23
|
import {
|
|
22
24
|
collectManagedGitignoreEntries,
|
|
23
25
|
writeManagedGitignoreBlock,
|
|
24
26
|
} from "./setup/plugins/gitignore";
|
|
25
|
-
import
|
|
27
|
+
import { createManagedOutputCollector } from "./setup/managed-output";
|
|
28
|
+
import type {
|
|
29
|
+
ManagedOutputReport,
|
|
30
|
+
PluginRegistration,
|
|
31
|
+
SetupContext,
|
|
32
|
+
SetupScope,
|
|
33
|
+
} from "./setup/plugin";
|
|
34
|
+
import {
|
|
35
|
+
inspectClaudePluginFolders,
|
|
36
|
+
type ClaudePluginContributionFailure,
|
|
37
|
+
type ClaudePluginDirectoryReport,
|
|
38
|
+
type ClaudePluginFolders,
|
|
39
|
+
} from "./setup/providers/claude-plugin-folders";
|
|
26
40
|
import { getActiveDistribution } from "../distribution";
|
|
27
41
|
import { createUvPlugin } from "./setup/package-managers/uv";
|
|
28
42
|
import type { PackageManagerSetupDeps } from "./setup/package-managers/uv";
|
|
@@ -38,10 +52,12 @@ import { hydrateDynamicPlugins } from "./setup/dynamic-plugins/hydrate";
|
|
|
38
52
|
import { installDeclaredPlugins } from "./setup/dynamic-plugins/install";
|
|
39
53
|
import {
|
|
40
54
|
buildSetupInstallationPlan,
|
|
55
|
+
collectRuntimeContributionInputs,
|
|
41
56
|
executeSetupInstallationPlan,
|
|
57
|
+
publishRuntimeClaudePluginMarketplaces,
|
|
42
58
|
renderRuntimeDocuments,
|
|
43
|
-
type RenderedRuntimeDocuments,
|
|
44
59
|
type SetupInstallationOutcome,
|
|
60
|
+
type SetupInstallationPlan,
|
|
45
61
|
} from "./setup/engine";
|
|
46
62
|
|
|
47
63
|
export interface SetupInput {
|
|
@@ -57,42 +73,130 @@ export interface SetupDependencies {
|
|
|
57
73
|
globalConfigStore?: GlobalConfigStore;
|
|
58
74
|
}
|
|
59
75
|
|
|
76
|
+
export interface CompanionSyncOptions {
|
|
77
|
+
/** Regenerates OpenSpec CLI output; set only by `mate companion update`. */
|
|
78
|
+
regenerateOpenSpec?: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface CompanionSyncOutcome extends SetupInstallationOutcome {
|
|
82
|
+
/** The managed output this pass wrote or removed. */
|
|
83
|
+
managedOutput: ManagedOutputReport;
|
|
84
|
+
}
|
|
85
|
+
|
|
60
86
|
export async function applySetupCompatibilities(
|
|
61
87
|
companionPath: string,
|
|
62
88
|
config: FrameworkConfig,
|
|
63
89
|
mode: "setup" | "sync",
|
|
64
|
-
plugins: PluginRegistration[] = getActiveDistribution().registry.
|
|
90
|
+
plugins: PluginRegistration[] = getActiveDistribution().registry.getEntries(),
|
|
65
91
|
repoPath?: string,
|
|
66
92
|
scope: SetupScope = config.type === "hub" ? "hub" : "companion",
|
|
67
|
-
|
|
93
|
+
options: CompanionSyncOptions = {},
|
|
94
|
+
): Promise<CompanionSyncOutcome> {
|
|
68
95
|
const plan = buildSetupInstallationPlan(config, plugins);
|
|
69
96
|
const activeProviders = plan.activeProviders;
|
|
70
|
-
const
|
|
71
|
-
|
|
97
|
+
const managedOutput = createManagedOutputCollector(companionPath);
|
|
98
|
+
const ctx: SetupContext = {
|
|
99
|
+
companionPath,
|
|
100
|
+
config,
|
|
101
|
+
mode,
|
|
102
|
+
activeProviders,
|
|
103
|
+
repoPath,
|
|
104
|
+
scope,
|
|
105
|
+
...(options.regenerateOpenSpec ? { regenerateOpenSpec: true } : {}),
|
|
106
|
+
managedOutput: managedOutput.recorder,
|
|
107
|
+
};
|
|
108
|
+
const outcome = await executeSetupInstallationPlan(ctx, plugins, plan);
|
|
109
|
+
return { ...outcome, managedOutput: managedOutput.report() };
|
|
72
110
|
}
|
|
73
111
|
|
|
74
112
|
/**
|
|
75
113
|
* The active Agent Runtimes' documents for a Working Repository, rendered
|
|
76
|
-
* without a write
|
|
114
|
+
* without a write. Runs no plugin phase: the working target reconciles
|
|
77
115
|
* declarations only, so nothing is written to the companion on its behalf.
|
|
78
116
|
*/
|
|
79
117
|
export async function renderWorkingRuntimeDocuments(
|
|
80
118
|
companionPath: string,
|
|
81
119
|
config: FrameworkConfig,
|
|
82
120
|
repoPath: string,
|
|
83
|
-
plugins: PluginRegistration[] = getActiveDistribution().registry.
|
|
84
|
-
wrapped?: { audience: string; registrations: PluginRegistration[] },
|
|
121
|
+
plugins: PluginRegistration[] = getActiveDistribution().registry.getAll(),
|
|
85
122
|
): Promise<RenderedRuntimeDocuments> {
|
|
123
|
+
const { plan, ctx } = planSyncPass(companionPath, config, plugins, {
|
|
124
|
+
repoPath,
|
|
125
|
+
target: "working",
|
|
126
|
+
});
|
|
127
|
+
return renderRuntimeDocuments(ctx, plugins, plan);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Publishes the enabled Claude plugin folders for a Working Repository's wrap
|
|
132
|
+
* pass, before it renders; see {@link publishRuntimeClaudePluginMarketplaces}.
|
|
133
|
+
*/
|
|
134
|
+
export async function publishWorkingClaudePluginMarketplaces(
|
|
135
|
+
companionPath: string,
|
|
136
|
+
config: FrameworkConfig,
|
|
137
|
+
repoPath: string,
|
|
138
|
+
plugins: PluginRegistration[] = getActiveDistribution().registry.getAll(),
|
|
139
|
+
): Promise<ClaudePluginFolders> {
|
|
140
|
+
const { plan, ctx } = planSyncPass(companionPath, config, plugins, {
|
|
141
|
+
repoPath,
|
|
142
|
+
target: "working",
|
|
143
|
+
});
|
|
144
|
+
return publishRuntimeClaudePluginMarketplaces(ctx, plugins, plan);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A sync pass's plan and context, for a caller that runs no plugin phase. */
|
|
148
|
+
function planSyncPass(
|
|
149
|
+
companionPath: string,
|
|
150
|
+
config: FrameworkConfig,
|
|
151
|
+
plugins: PluginRegistration[],
|
|
152
|
+
extra: Pick<SetupContext, "repoPath" | "target"> = {},
|
|
153
|
+
): { plan: SetupInstallationPlan; ctx: SetupContext } {
|
|
86
154
|
const plan = buildSetupInstallationPlan(config, plugins);
|
|
87
155
|
const ctx: SetupContext = {
|
|
88
156
|
companionPath,
|
|
89
157
|
config,
|
|
90
158
|
mode: "sync",
|
|
91
159
|
activeProviders: plan.activeProviders,
|
|
92
|
-
|
|
93
|
-
|
|
160
|
+
...extra,
|
|
161
|
+
};
|
|
162
|
+
return { plan, ctx };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** What an operator sees of the enabled Capabilities' Claude plugin folders. */
|
|
166
|
+
export interface ClaudePluginDirectoryInspection {
|
|
167
|
+
plugins: ClaudePluginDirectoryReport[];
|
|
168
|
+
/** Capabilities whose contributions rejected; the other Capabilities are still listed. */
|
|
169
|
+
failures: ClaudePluginContributionFailure[];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Enabled Capabilities' declared Claude plugin folders and whether `mate claude`
|
|
174
|
+
* passes each, checked against the running install's bundled plugin. Read-only:
|
|
175
|
+
* runs no plugin phase and reconciles nothing.
|
|
176
|
+
*/
|
|
177
|
+
export async function inspectClaudePluginDirectories(
|
|
178
|
+
companionPath: string,
|
|
179
|
+
config: FrameworkConfig,
|
|
180
|
+
plugins: PluginRegistration[] = getActiveDistribution().registry.getEntries(),
|
|
181
|
+
): Promise<ClaudePluginDirectoryInspection> {
|
|
182
|
+
const { plan, ctx } = planSyncPass(companionPath, config, plugins);
|
|
183
|
+
const failures: ClaudePluginContributionFailure[] = [];
|
|
184
|
+
const inputs = await collectRuntimeContributionInputs(ctx, plugins, plan, (capability, error) =>
|
|
185
|
+
failures.push({
|
|
186
|
+
capability,
|
|
187
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
188
|
+
}),
|
|
189
|
+
);
|
|
190
|
+
return {
|
|
191
|
+
plugins: [
|
|
192
|
+
...(
|
|
193
|
+
await inspectClaudePluginFolders(inputs.get("claude") ?? [], {
|
|
194
|
+
bundledPluginRoot: getClaudePluginRoot(),
|
|
195
|
+
})
|
|
196
|
+
).report,
|
|
197
|
+
],
|
|
198
|
+
failures,
|
|
94
199
|
};
|
|
95
|
-
return renderRuntimeDocuments(ctx, plugins, plan, wrapped);
|
|
96
200
|
}
|
|
97
201
|
|
|
98
202
|
export async function updateProjectGitignore(
|
|
@@ -100,25 +204,34 @@ export async function updateProjectGitignore(
|
|
|
100
204
|
config: FrameworkConfig,
|
|
101
205
|
): Promise<void> {
|
|
102
206
|
const ctx: SetupContext = { companionPath, config, mode: "sync", activeProviders: [] };
|
|
103
|
-
const plugins = getActiveDistribution()
|
|
104
|
-
.registry.baseRegistrations()
|
|
105
|
-
.map((entry) => entry.plugin);
|
|
207
|
+
const plugins = getActiveDistribution().registry.getAll();
|
|
106
208
|
const entries = collectManagedGitignoreEntries(ctx, plugins);
|
|
107
209
|
await writeManagedGitignoreBlock(path.join(companionPath, ".gitignore"), FRAMEWORK_NAME, entries);
|
|
108
210
|
}
|
|
109
211
|
|
|
212
|
+
export interface CompanionSyncResult {
|
|
213
|
+
/** For a launch to pass and report; every other caller ignores them. */
|
|
214
|
+
claudePluginFolders: ClaudePluginFolders;
|
|
215
|
+
/** For `mate companion update` to sort changed paths; a launch ignores it. */
|
|
216
|
+
managedOutput: ManagedOutputReport;
|
|
217
|
+
}
|
|
218
|
+
|
|
110
219
|
export async function syncCompanionFiles(
|
|
111
220
|
companionPath: string,
|
|
112
221
|
config: FrameworkConfig,
|
|
113
222
|
repoPath?: string,
|
|
114
|
-
|
|
115
|
-
|
|
223
|
+
options: CompanionSyncOptions = {},
|
|
224
|
+
): Promise<CompanionSyncResult> {
|
|
225
|
+
const outcome = await applySetupCompatibilities(
|
|
116
226
|
companionPath,
|
|
117
227
|
config,
|
|
118
228
|
"sync",
|
|
119
|
-
getActiveDistribution().registry.
|
|
229
|
+
getActiveDistribution().registry.getAll(),
|
|
120
230
|
repoPath,
|
|
231
|
+
undefined,
|
|
232
|
+
options,
|
|
121
233
|
);
|
|
234
|
+
return { claudePluginFolders: outcome.claudePluginFolders, managedOutput: outcome.managedOutput };
|
|
122
235
|
}
|
|
123
236
|
|
|
124
237
|
export const setupToolDeps = {
|
package/wrappers/bin/graphify
CHANGED
|
@@ -69,6 +69,34 @@ _graphify_root="$(dirname "$_graphify_out")"
|
|
|
69
69
|
_graph_json="$_graphify_out/graph.json"
|
|
70
70
|
export GRAPHIFY_OUT="$_graphify_out"
|
|
71
71
|
|
|
72
|
+
# Secret gate: leaves secret files out of every extraction and prunes any
|
|
73
|
+
# secret that still reaches the output. It never stops graphify: when the gate
|
|
74
|
+
# can't run, the wrapper warns and runs graphify anyway.
|
|
75
|
+
_gate="$_this_dir/../lib/graphify-gate.mjs"
|
|
76
|
+
_secret_excludes=()
|
|
77
|
+
_secret_pre() {
|
|
78
|
+
local _out _p
|
|
79
|
+
if ! _out="$(node "$_gate" pre "$_companion" "$_repo_path")"; then
|
|
80
|
+
echo "graphify: warning: the secret gate did not run; secret files may be indexed" >&2
|
|
81
|
+
return
|
|
82
|
+
fi
|
|
83
|
+
while IFS= read -r _p; do
|
|
84
|
+
[ -n "$_p" ] && _secret_excludes+=(--exclude "$_p")
|
|
85
|
+
done <<< "$_out"
|
|
86
|
+
}
|
|
87
|
+
_secret_post() {
|
|
88
|
+
node "$_gate" post "$_companion" "$_graphify_out" ||
|
|
89
|
+
echo "graphify: warning: the graphify output was not checked for secrets" >&2
|
|
90
|
+
}
|
|
91
|
+
# Runs an extraction that accepts --exclude, then prunes its output.
|
|
92
|
+
_extract() {
|
|
93
|
+
_secret_pre
|
|
94
|
+
"$_real" "$@" "${_secret_excludes[@]}"
|
|
95
|
+
local _status=$?
|
|
96
|
+
_secret_post
|
|
97
|
+
exit "$_status"
|
|
98
|
+
}
|
|
99
|
+
|
|
72
100
|
_cmd="${1:-}"
|
|
73
101
|
case "$_cmd" in
|
|
74
102
|
build)
|
|
@@ -81,16 +109,32 @@ case "$_cmd" in
|
|
|
81
109
|
*) _rest+=("$_a") ;;
|
|
82
110
|
esac
|
|
83
111
|
done
|
|
84
|
-
|
|
112
|
+
_extract extract "$_repo_path" --out "$_graphify_root" "${_rest[@]}"
|
|
85
113
|
;;
|
|
86
114
|
update)
|
|
87
115
|
if [ "$#" -eq 1 ] || [[ "$2" == -* ]]; then
|
|
88
|
-
|
|
116
|
+
_extract "$_repo_path" --update "${@:2}"
|
|
117
|
+
else
|
|
118
|
+
_extract "$2" --update "${@:3}"
|
|
119
|
+
fi
|
|
120
|
+
;;
|
|
121
|
+
extract)
|
|
122
|
+
if [ "$#" -eq 1 ] || [[ "$2" == -* ]]; then
|
|
123
|
+
_extract extract "$_repo_path" "${@:2}"
|
|
124
|
+
else
|
|
125
|
+
_extract "$@"
|
|
126
|
+
fi
|
|
127
|
+
;;
|
|
128
|
+
watch)
|
|
129
|
+
# Long-running: only the ignore region applies; there is no output to prune.
|
|
130
|
+
_secret_pre
|
|
131
|
+
if [ "$#" -eq 1 ] || [[ "$2" == -* ]]; then
|
|
132
|
+
exec "$_real" watch "$_repo_path" "${@:2}"
|
|
89
133
|
else
|
|
90
|
-
exec "$_real" "
|
|
134
|
+
exec "$_real" "$@"
|
|
91
135
|
fi
|
|
92
136
|
;;
|
|
93
|
-
|
|
137
|
+
cluster-only|label|check-update)
|
|
94
138
|
if [ "$#" -eq 1 ] || [[ "$2" == -* ]]; then
|
|
95
139
|
exec "$_real" "$_cmd" "$_repo_path" "${@:2}"
|
|
96
140
|
else
|
|
@@ -120,6 +164,10 @@ case "$_cmd" in
|
|
|
120
164
|
fi
|
|
121
165
|
;;
|
|
122
166
|
*)
|
|
167
|
+
# A direct `graphify <path> --update` is an extraction too.
|
|
168
|
+
for _a in "$@"; do
|
|
169
|
+
[ "$_a" = "--update" ] && _extract "$@"
|
|
170
|
+
done
|
|
123
171
|
exec "$_real" "$@"
|
|
124
172
|
;;
|
|
125
173
|
esac
|