@uniqbit/mate-core 0.15.5 → 0.16.0-canary.1
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/.claude-plugin/plugin.json +2 -2
- package/claude-plugin/hooks/hooks.json +3 -6
- package/claude-plugin/hooks/session-guidance.mjs +8 -0
- package/claude-plugin/hooks/ts-loader.mjs +19 -0
- package/package.json +7 -5
- package/src/cli/commands/artifact/artifact.ts +19 -3
- package/src/cli/commands/artifact/finish/command.ts +183 -57
- package/src/cli/commands/artifact/finish/engine.ts +80 -91
- package/src/cli/commands/artifact/finish/finisher.ts +26 -33
- package/src/cli/commands/artifact/finish/git.ts +34 -23
- package/src/cli/commands/artifact/finish/index.ts +9 -3
- package/src/cli/commands/artifact/finish/openspec.ts +225 -97
- package/src/cli/commands/artifact/pending/command.ts +175 -0
- package/src/cli/commands/artifact/pending/discovery.ts +249 -0
- package/src/cli/commands/artifact/pending/index.ts +17 -0
- package/src/cli/commands/cap/index-cmd.ts +9 -1
- package/src/cli/commands/cap/index.ts +2 -6
- package/src/cli/commands/cap/tokensave.ts +4 -4
- package/src/cli/commands/companion/companion.ts +5 -1
- package/src/cli/commands/companion/link.ts +2 -2
- package/src/cli/commands/companion/sync.ts +92 -0
- package/src/cli/commands/doctor.ts +0 -3
- package/src/cli/commands/launch/shared.ts +23 -5
- package/src/cli/commands/report/collector.ts +72 -78
- package/src/cli/commands/report/contract.ts +40 -1
- package/src/cli/commands/report/highlight.ts +27 -0
- package/src/cli/commands/report/index.ts +11 -18
- package/src/cli/commands/report/renderer.ts +199 -2
- package/src/cli/commands/report/types.ts +26 -1
- package/src/cli/commands/shared/companion-selection.ts +107 -10
- package/src/cli/commands/studio/areas.ts +68 -0
- package/src/cli/commands/studio/index.ts +69 -0
- package/src/cli/commands/studio/inventory.ts +55 -0
- package/src/cli/commands/studio/mate-inventory.ts +43 -0
- package/src/cli/commands/studio/openspec-cli.ts +198 -0
- package/src/cli/commands/studio/payload.ts +184 -0
- package/src/cli/commands/studio/routes.ts +2 -0
- package/src/cli/commands/studio/selection.ts +61 -0
- package/src/cli/commands/studio/server.ts +201 -0
- package/src/cli/commands/studio/snapshot.ts +63 -0
- package/src/cli/commands/studio/topology.ts +199 -0
- package/src/cli/commands/studio/views/client.ts +197 -0
- package/src/cli/commands/studio/views/companion-picker.tsx +91 -0
- package/src/cli/commands/studio/views/companion-selector.tsx +90 -0
- package/src/cli/commands/studio/views/dashboard/changes.tsx +107 -0
- package/src/cli/commands/studio/views/dashboard/index.tsx +19 -0
- package/src/cli/commands/studio/views/document.tsx +256 -0
- package/src/cli/commands/studio/views/error.tsx +20 -0
- package/src/cli/commands/studio/views/model.ts +34 -0
- package/src/cli/commands/studio/views/pairings.tsx +38 -0
- package/src/cli/commands/studio/views/skills/index.tsx +87 -0
- package/src/cli/commands/studio/views/specs/index.tsx +101 -0
- package/src/cli/commands/studio/views/styles.ts +389 -0
- package/src/cli/commands/studio/views/warnings.tsx +21 -0
- package/src/cli/commands/studio/views/workflow/index.tsx +21 -0
- package/src/cli/commands/studio/views/workflow/steps.ts +329 -0
- package/src/cli/commands/studio/views/workflow/transcript.tsx +190 -0
- package/src/cli/commands/unwrap.ts +70 -0
- package/src/cli/commands/wrap.ts +164 -0
- package/src/cli/main.ts +68 -19
- package/src/cli/parse-flags.ts +36 -11
- package/src/cli/usage.ts +12 -3
- package/src/framework.ts +1 -7
- package/src/hooks/session-banner.ts +64 -11
- package/src/hooks/session-guidance.ts +40 -0
- package/src/hooks/validate-artifact-path.ts +108 -35
- package/src/lib/fs-utils.ts +9 -0
- package/src/lib/install.ts +33 -0
- package/src/lib/orchestrator/adapters/base.ts +14 -125
- package/src/lib/orchestrator/adapters/claude.ts +0 -11
- package/src/lib/orchestrator/adapters/opencode.ts +2 -32
- package/src/lib/orchestrator/companion-git-sync.ts +94 -84
- package/src/lib/orchestrator/config-store.ts +2 -21
- package/src/lib/orchestrator/editor.ts +12 -22
- package/src/lib/orchestrator/framework-context.ts +17 -6
- package/src/lib/orchestrator/global-config-store.ts +1 -1
- package/src/lib/orchestrator/launcher.ts +97 -7
- package/src/lib/orchestrator/opencode-guidance.ts +4 -56
- package/src/lib/orchestrator/projection-claude-entry.ts +198 -0
- package/src/lib/orchestrator/projection-claude-skills.ts +120 -0
- package/src/lib/orchestrator/projection-companion-link.ts +62 -0
- package/src/lib/orchestrator/projection-entries.ts +377 -0
- package/src/lib/orchestrator/projection-record.ts +56 -0
- package/src/lib/orchestrator/projection-runtime-documents.ts +424 -0
- package/src/lib/orchestrator/projection-types.ts +169 -0
- package/src/lib/orchestrator/repo-local-registry.ts +37 -133
- package/src/lib/orchestrator/repo-local-store.ts +96 -0
- package/src/lib/orchestrator/setup-compatibilities.ts +1 -9
- package/src/lib/orchestrator/types.ts +1 -0
- package/src/lib/orchestrator/working-repo-projection.ts +366 -0
- package/src/lib/orchestrator/workspace-inventory.ts +1 -1
- package/src/lib/package-paths.ts +11 -1
- package/src/lib/public-npm.ts +2 -1
- package/src/lib/update-checker.ts +15 -9
- package/src/opencode/companion-hooks.ts +89 -245
- package/src/opencode/companion-policy.ts +35 -10
- package/src/opencode/index.ts +1 -0
- package/src/opencode/projected-guidance.ts +56 -0
- package/src/opencode/tui.tsx +13 -4
- package/src/playbooks/companion-guidance.ts +32 -116
- package/src/plugins.ts +0 -1
- package/src/runtime/companion-git-state.ts +156 -0
- package/src/runtime/companion-git.ts +203 -0
- package/src/runtime/companion-guidance.ts +222 -0
- package/src/runtime/companion-sync.ts +298 -0
- package/src/runtime/env-names.ts +30 -0
- package/src/runtime/env.ts +67 -35
- package/src/runtime/framework.ts +10 -0
- package/src/runtime/freshness.ts +58 -0
- package/src/runtime/index.ts +104 -0
- package/src/runtime/install.ts +30 -0
- package/src/runtime/policy.ts +66 -0
- package/src/runtime/projected-guidance.ts +45 -0
- package/src/runtime/projection.ts +224 -0
- package/src/runtime/repo-local.ts +64 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +56 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +48 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/tasks.md +22 -0
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +31 -90
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/design.md +3 -0
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/explore-brief.md +6 -6
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/tasks.md +3 -0
- package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +30 -0
- package/src/templates/capabilities/react-doctor/claude/hooks/react-doctor.sh +2 -2
- package/src/templates/mate-skills/agents/mate-artifact-publish/SKILL.md +185 -0
- package/src/templates/mate-skills/agents/mate-artifact-publish/references/openspec.md +227 -0
- package/src/templates/mate-skills/agents/mate-domain-modeling/SKILL.md +68 -0
- package/src/templates/mate-skills/agents/mate-domain-modeling/references/ADR-FORMAT.md +9 -0
- package/src/templates/mate-skills/agents/mate-domain-modeling/references/CONTEXT-FORMAT.md +18 -0
- package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +14 -0
- package/src/templates/mate-skills/agents/mate-grill-with-docs/SKILL.md +29 -0
- package/src/templates/mate-skills/agents/mate-grilling/SKILL.md +49 -0
- package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +156 -0
- package/src/templates/{capabilities/openspec-cap/mate-skills → mate-skills}/agents/mate-openspec-backfill/SKILL.md +4 -2
- package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +139 -0
- package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +503 -0
- package/src/templates/mate-skills/claude/mate-artifact-publish/SKILL.md +185 -0
- package/src/templates/mate-skills/claude/mate-artifact-publish/references/openspec.md +227 -0
- package/src/templates/mate-skills/claude/mate-domain-modeling/SKILL.md +68 -0
- package/src/templates/mate-skills/claude/mate-domain-modeling/references/ADR-FORMAT.md +9 -0
- package/src/templates/mate-skills/claude/mate-domain-modeling/references/CONTEXT-FORMAT.md +18 -0
- package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +14 -0
- package/src/templates/mate-skills/claude/mate-grill-with-docs/SKILL.md +29 -0
- package/src/templates/mate-skills/claude/mate-grilling/SKILL.md +49 -0
- package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +156 -0
- package/src/templates/mate-skills/claude/mate-openspec-backfill/SKILL.md +67 -0
- package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +139 -0
- package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +503 -0
- package/src/templates/report-assets/README.md +32 -0
- package/src/templates/report-assets/mermaid.LICENSE +21 -0
- package/src/templates/report-assets/mermaid.min.js +4376 -0
- package/src/templates/root/TEMPLATE_CLAUDE.md +1 -9
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +1476 -197
- package/src/tools/setup/capabilities/graphify.ts +16 -7
- package/src/tools/setup/capabilities/openspec.ts +63 -56
- package/src/tools/setup/capabilities/tokensave.ts +116 -2
- package/src/tools/setup/engine.ts +34 -6
- package/src/tools/setup/mate.ts +42 -13
- package/src/tools/setup/plugin.ts +9 -0
- package/src/tools/setup/plugins/guidance.ts +11 -1
- package/src/tools/setup/providers/claude-format.ts +49 -4
- package/src/tools/setup/providers/claude-plugin-hooks.ts +117 -0
- package/src/tools/setup/providers/claude.ts +55 -220
- package/src/tools/setup/providers/opencode.ts +41 -14
- package/src/tools/setup/runtime-documents.ts +174 -0
- package/src/tools/setup/surface-target.ts +50 -0
- package/src/tools/setup/working-repo-cleanup.ts +33 -26
- package/src/tools/setup/working-repo-local-state.ts +21 -1
- package/src/tools/setup.ts +25 -3
- package/wrappers/bin/graphify +57 -8
- package/wrappers/bin/openspec +50 -3
- package/claude-plugin/hooks/artifact-finish-nudge.mjs +0 -8
- package/src/cli/commands/cap/headroom.ts +0 -52
- package/src/cli/commands/workspace/list.ts +0 -25
- package/src/cli/commands/workspace/materialize.ts +0 -46
- package/src/cli/commands/workspace/workspace.ts +0 -22
- package/src/hooks/artifact-finish-nudge.ts +0 -244
- package/src/lib/orchestrator/headroom/proxy.ts +0 -116
- package/src/lib/orchestrator/workspace-materialize.ts +0 -80
- package/src/templates/capabilities/openspec-cap/mate-skills/agents/mate-artifact-finish/SKILL.md +0 -51
- package/src/templates/capabilities/openspec-cap/mate-skills/agents/mate-artifact-finish/references/openspec.md +0 -134
- package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/SKILL.md +0 -58
- package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/references/openspec.md +0 -139
- package/src/tools/setup/capabilities/headroom.ts +0 -57
- /package/src/cli/commands/{workspace → companion}/open.ts +0 -0
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
upsertManagedBlock,
|
|
19
19
|
} from "../context-services";
|
|
20
20
|
import type { CapabilityContributionInput, ProviderPlugin, SetupContext } from "../plugin";
|
|
21
|
+
import { surfaceRoot } from "../surface-target";
|
|
21
22
|
import { mergeDir, pruneEmptyAncestors } from "../utils";
|
|
22
23
|
import {
|
|
23
24
|
getOpenCodePluginReferences,
|
|
@@ -424,22 +425,23 @@ export async function reconcileOpenCodeContributions(
|
|
|
424
425
|
ctx: SetupContext,
|
|
425
426
|
inputs: CapabilityContributionInput[],
|
|
426
427
|
): Promise<void> {
|
|
427
|
-
|
|
428
|
+
/** Resolved once per pass; every write site below is relative to it. */
|
|
429
|
+
const root = surfaceRoot(ctx);
|
|
428
430
|
|
|
429
431
|
if (ctx.scope === "hub") {
|
|
430
|
-
await reconcileOpenCodeMcpContributions(
|
|
431
|
-
await reconcileOpenCodeAgentDefinitionContributions(
|
|
432
|
+
await reconcileOpenCodeMcpContributions(root, inputs);
|
|
433
|
+
await reconcileOpenCodeAgentDefinitionContributions(root, inputs);
|
|
432
434
|
return;
|
|
433
435
|
}
|
|
434
436
|
|
|
435
|
-
await reconcileOpenCodeMcpContributions(
|
|
436
|
-
await reconcileOpenCodeAgentDefinitionContributions(
|
|
437
|
+
await reconcileOpenCodeMcpContributions(root, inputs);
|
|
438
|
+
await reconcileOpenCodeAgentDefinitionContributions(root, inputs);
|
|
437
439
|
|
|
438
440
|
for (const input of inputs) {
|
|
439
441
|
for (const pluginReference of input.contributions.pluginReferences ?? []) {
|
|
440
442
|
const configFiles = pluginReference.configFiles ?? OPENCODE_CONTRIBUTION_CONFIG_FILES;
|
|
441
443
|
for (const name of configFiles) {
|
|
442
|
-
const configPath = path.join(
|
|
444
|
+
const configPath = path.join(root, ".opencode", name);
|
|
443
445
|
const { present, config } = await readOpenCodeConfig(configPath);
|
|
444
446
|
if (!input.enabled && !present) continue;
|
|
445
447
|
const preserved = getOpenCodePluginReferences(config).filter(
|
|
@@ -456,7 +458,7 @@ export async function reconcileOpenCodeContributions(
|
|
|
456
458
|
// Guidance sections are managed blocks in AGENTS.md. Capability disable
|
|
457
459
|
// strips them here; runtime teardown of the shared AGENTS.md is guarded in
|
|
458
460
|
// the provider teardown paths (kept while another active runtime uses it).
|
|
459
|
-
const guidancePath = path.join(
|
|
461
|
+
const guidancePath = path.join(root, "AGENTS.md");
|
|
460
462
|
const sections = input.enabled ? (input.contributions.guidanceSections ?? []) : [];
|
|
461
463
|
const keepKeys = new Set<string>();
|
|
462
464
|
for (const section of sections) {
|
|
@@ -469,12 +471,12 @@ export async function reconcileOpenCodeContributions(
|
|
|
469
471
|
}
|
|
470
472
|
|
|
471
473
|
for (const skillTree of input.contributions.skillTrees ?? []) {
|
|
472
|
-
const skillDir = path.join(
|
|
474
|
+
const skillDir = path.join(root, ".opencode", "skills", skillTree.name);
|
|
473
475
|
if (input.enabled) {
|
|
474
476
|
await mergeDir(skillTree.sourceDir, skillDir);
|
|
475
477
|
} else {
|
|
476
478
|
await fs.rm(skillDir, { recursive: true, force: true });
|
|
477
|
-
await pruneEmptyAncestors(path.join(
|
|
479
|
+
await pruneEmptyAncestors(path.join(root, ".opencode", "skills"), root);
|
|
478
480
|
}
|
|
479
481
|
}
|
|
480
482
|
}
|
|
@@ -482,13 +484,13 @@ export async function reconcileOpenCodeContributions(
|
|
|
482
484
|
|
|
483
485
|
/** Reconcile only MCP entries without touching OpenCode plugins or guidance. */
|
|
484
486
|
async function reconcileOpenCodeMcpContributions(
|
|
485
|
-
|
|
487
|
+
root: string,
|
|
486
488
|
inputs: CapabilityContributionInput[],
|
|
487
489
|
): Promise<void> {
|
|
488
490
|
for (const input of inputs) {
|
|
489
491
|
for (const descriptor of input.contributions.mcpServers ?? []) {
|
|
490
492
|
await updateOpenCodeMcpServer(
|
|
491
|
-
getCompanionOpenCodeConfigPath(
|
|
493
|
+
getCompanionOpenCodeConfigPath(root),
|
|
492
494
|
descriptor.name,
|
|
493
495
|
input.enabled ? toOpenCodeMcpEntry(descriptor) : null,
|
|
494
496
|
);
|
|
@@ -496,6 +498,31 @@ async function reconcileOpenCodeMcpContributions(
|
|
|
496
498
|
}
|
|
497
499
|
}
|
|
498
500
|
|
|
501
|
+
/** The Mate-managed `mcp` map of one pass, as a value. */
|
|
502
|
+
export function renderManagedOpenCodeMcpServers(
|
|
503
|
+
contributions: CapabilityContributionInput[] = [],
|
|
504
|
+
): Record<string, unknown> {
|
|
505
|
+
const servers: Record<string, unknown> = {};
|
|
506
|
+
for (const input of contributions) {
|
|
507
|
+
if (!input.enabled) continue;
|
|
508
|
+
for (const descriptor of input.contributions.mcpServers ?? []) {
|
|
509
|
+
servers[descriptor.name] = toOpenCodeMcpEntry(descriptor);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return servers;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* The Mate-managed `permission.external_directory` map, as a value. Rendered
|
|
517
|
+
* once for both destinations a session can reach it through — the projected
|
|
518
|
+
* document and the launch environment — so the two cannot drift.
|
|
519
|
+
*/
|
|
520
|
+
export function renderCompanionExternalDirectoryPermissions(
|
|
521
|
+
companionPath: string,
|
|
522
|
+
): Record<string, string> {
|
|
523
|
+
return { [companionPath]: "allow", [`${companionPath}/**`]: "allow" };
|
|
524
|
+
}
|
|
525
|
+
|
|
499
526
|
/**
|
|
500
527
|
* Reconcile declared agent definition files under `.opencode/agents/`. Runs
|
|
501
528
|
* in both companion and hub scope — an agent definition is fully
|
|
@@ -503,10 +530,10 @@ async function reconcileOpenCodeMcpContributions(
|
|
|
503
530
|
* surface.
|
|
504
531
|
*/
|
|
505
532
|
async function reconcileOpenCodeAgentDefinitionContributions(
|
|
506
|
-
|
|
533
|
+
root: string,
|
|
507
534
|
inputs: CapabilityContributionInput[],
|
|
508
535
|
): Promise<void> {
|
|
509
|
-
const agentsDir = path.join(
|
|
536
|
+
const agentsDir = path.join(root, ".opencode", "agents");
|
|
510
537
|
for (const input of inputs) {
|
|
511
538
|
for (const agent of input.contributions.agentDefinitions ?? []) {
|
|
512
539
|
const agentPath = path.join(agentsDir, `${agent.name}.md`);
|
|
@@ -515,7 +542,7 @@ async function reconcileOpenCodeAgentDefinitionContributions(
|
|
|
515
542
|
await fs.writeFile(agentPath, agent.content, "utf8");
|
|
516
543
|
} else {
|
|
517
544
|
await fs.rm(agentPath, { force: true });
|
|
518
|
-
await pruneEmptyAncestors(agentsDir,
|
|
545
|
+
await pruneEmptyAncestors(agentsDir, root);
|
|
519
546
|
}
|
|
520
547
|
}
|
|
521
548
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLAUDE_LOCAL_CONFIG_DOCUMENT,
|
|
3
|
+
CLAUDE_MCP_DOCUMENT,
|
|
4
|
+
CLAUDE_SETTINGS_DOCUMENT,
|
|
5
|
+
OPENCODE_CONFIG_DOCUMENT,
|
|
6
|
+
} from "../../lib/orchestrator/projection-runtime-documents";
|
|
7
|
+
import type {
|
|
8
|
+
ManagedRegion,
|
|
9
|
+
RenderedRuntimeDocument,
|
|
10
|
+
} from "../../lib/orchestrator/projection-types";
|
|
11
|
+
import { getOpenCodePluginPackageReference } from "../../lib/opencode-plugin-package";
|
|
12
|
+
import type { CapabilityContributionInput, SetupContext } from "./plugin";
|
|
13
|
+
import {
|
|
14
|
+
mateBannerHookGroups,
|
|
15
|
+
mateGuardHookGroups,
|
|
16
|
+
mateGuidanceHookGroups,
|
|
17
|
+
} from "./providers/claude-plugin-hooks";
|
|
18
|
+
import {
|
|
19
|
+
renderManagedClaudeMcpServers,
|
|
20
|
+
renderManagedClaudePermissionEntries,
|
|
21
|
+
} from "./providers/claude";
|
|
22
|
+
import {
|
|
23
|
+
renderCompanionExternalDirectoryPermissions,
|
|
24
|
+
renderManagedOpenCodeMcpServers,
|
|
25
|
+
} from "./providers/opencode";
|
|
26
|
+
import { projectionRepoRoot } from "./surface-target";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The working target's documents, rendered as values. Each is a document the
|
|
30
|
+
* runtime finds by walking up from the current directory, and every path inside
|
|
31
|
+
* one still names the Companion Repository — only where the document is placed
|
|
32
|
+
* differs from the companion target.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
CLAUDE_LOCAL_CONFIG_DOCUMENT,
|
|
37
|
+
CLAUDE_MCP_DOCUMENT,
|
|
38
|
+
CLAUDE_SETTINGS_DOCUMENT,
|
|
39
|
+
OPENCODE_CONFIG_DOCUMENT,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function nonEmpty(region: ManagedRegion): boolean {
|
|
43
|
+
if (region.kind === "list") return region.values.length > 0;
|
|
44
|
+
if (region.kind === "map") return Object.keys(region.entries).length > 0;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function document(documentPath: string, regions: ManagedRegion[]): RenderedRuntimeDocument[] {
|
|
49
|
+
const kept = regions.filter(nonEmpty);
|
|
50
|
+
return kept.length > 0 ? [{ path: documentPath, regions: kept }] : [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The Mate plugin reference is what makes an Unmanaged OpenCode session load
|
|
55
|
+
* the companion guidance at all: OpenCode has no config channel for a generated
|
|
56
|
+
* system prompt, so the plugin is the only injection point, and a bare session
|
|
57
|
+
* loads no companion config to find it in.
|
|
58
|
+
*
|
|
59
|
+
* A managed launch does load the plugin twice — its `OPENCODE_CONFIG_DIR` names
|
|
60
|
+
* the companion, whose config carries the same reference. That is inert rather
|
|
61
|
+
* than doubled: the plugin skips its own emission when the companion-policy
|
|
62
|
+
* marker is already in the prompt.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Capability hook groups stay companion-only. A managed session loads this
|
|
67
|
+
* document as its `local` source *alongside* the companion's, so anything here
|
|
68
|
+
* that the launch also delivers runs twice; the guard, the guidance hook and
|
|
69
|
+
* the banner are the contributions whose absence is a correctness hole and
|
|
70
|
+
* whose double run is inert — the guard because its verdict depends only on the
|
|
71
|
+
* tool input, the guidance hook because it yields nothing under a launch
|
|
72
|
+
* environment, and the banner because the copy carried here is flagged
|
|
73
|
+
* `--projected` and defers to the one the launch's own plugin prints.
|
|
74
|
+
*
|
|
75
|
+
* `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` is what a managed launch sets
|
|
76
|
+
* beside `--add-dir`. Without it the companion directories this document
|
|
77
|
+
* already permits are reachable but their `CLAUDE.md` is not loaded, so the
|
|
78
|
+
* permission alone delivers no guidance.
|
|
79
|
+
*/
|
|
80
|
+
function claudeSettingsRegions(
|
|
81
|
+
ctx: SetupContext,
|
|
82
|
+
inputs: CapabilityContributionInput[],
|
|
83
|
+
): ManagedRegion[] {
|
|
84
|
+
return [
|
|
85
|
+
{ at: ["hooks", "PreToolUse"], kind: "list", values: mateGuardHookGroups() },
|
|
86
|
+
{
|
|
87
|
+
at: ["hooks", "SessionStart"],
|
|
88
|
+
kind: "list",
|
|
89
|
+
values: [...mateBannerHookGroups(), ...mateGuidanceHookGroups()],
|
|
90
|
+
},
|
|
91
|
+
/**
|
|
92
|
+
* `stripPrefix` heals a repository an older Mate wrapped. That version wrote
|
|
93
|
+
* the whole launch environment here, and Claude Code applies `env` to the
|
|
94
|
+
* session, so every hook in a bare session inherited `MATE_*` and read the
|
|
95
|
+
* repository as a Managed Session — silently, since looking managed is
|
|
96
|
+
* exactly what makes the guidance hook emit nothing. Guidance must be the
|
|
97
|
+
* projection's to give in an Unmanaged Session, so no `MATE_*` key may
|
|
98
|
+
* survive here.
|
|
99
|
+
*/
|
|
100
|
+
{
|
|
101
|
+
at: ["env"],
|
|
102
|
+
kind: "map",
|
|
103
|
+
entries: { CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD: "1" },
|
|
104
|
+
stripPrefix: "MATE_",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
at: ["permissions", "allow"],
|
|
108
|
+
kind: "list",
|
|
109
|
+
values: renderManagedClaudePermissionEntries(ctx.companionPath, inputs),
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function renderDocumentsForTarget(
|
|
115
|
+
ctx: SetupContext,
|
|
116
|
+
inputsByRuntime: Map<string, CapabilityContributionInput[]>,
|
|
117
|
+
): RenderedRuntimeDocument[] {
|
|
118
|
+
const repoRoot = projectionRepoRoot(ctx);
|
|
119
|
+
|
|
120
|
+
const documents: RenderedRuntimeDocument[] = [];
|
|
121
|
+
if (ctx.activeProviders.includes("claude")) {
|
|
122
|
+
const inputs = inputsByRuntime.get("claude") ?? [];
|
|
123
|
+
documents.push(...document(CLAUDE_SETTINGS_DOCUMENT, claudeSettingsRegions(ctx, inputs)));
|
|
124
|
+
/**
|
|
125
|
+
* Local scope, not `.mcp.json`: a project-scoped server is inert until a
|
|
126
|
+
* human approves it in a session, which an Unmanaged Session is exactly the
|
|
127
|
+
* case that cannot rely on. The repo path is the key Claude Code files a
|
|
128
|
+
* project under, resolved so a symlinked working directory still matches.
|
|
129
|
+
*
|
|
130
|
+
* Confirmed against a real `~/.claude.json`: the keys there are the
|
|
131
|
+
* directories sessions started in, not repository roots — sibling
|
|
132
|
+
* directories of one checkout each hold their own entry, as do temporary
|
|
133
|
+
* directories. So this reaches exactly one session: the one started at the
|
|
134
|
+
* repository root. A session started in a subdirectory of a wrapped
|
|
135
|
+
* repository has its own key, finds no `mcpServers` under it, and sees none
|
|
136
|
+
* of these servers — the Unmanaged Session case this document exists for,
|
|
137
|
+
* missed. That limit is deliberate. The directories a session might start
|
|
138
|
+
* in are unbounded, and every one of them would be a fresh entry in the
|
|
139
|
+
* user's global configuration that nothing later reclaims; keying the
|
|
140
|
+
* repository root only keeps the projection's footprint there to a single
|
|
141
|
+
* key. A subdirectory session still gets the rest of the projection — the
|
|
142
|
+
* documents inside the repository are found by the runtime's own upward
|
|
143
|
+
* walk — it loses only the MCP servers.
|
|
144
|
+
*/
|
|
145
|
+
documents.push(
|
|
146
|
+
...document(CLAUDE_LOCAL_CONFIG_DOCUMENT, [
|
|
147
|
+
{
|
|
148
|
+
at: ["projects", repoRoot, "mcpServers"],
|
|
149
|
+
kind: "map",
|
|
150
|
+
entries: renderManagedClaudeMcpServers(inputs),
|
|
151
|
+
},
|
|
152
|
+
]),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (ctx.activeProviders.includes("opencode")) {
|
|
156
|
+
const inputs = inputsByRuntime.get("opencode") ?? [];
|
|
157
|
+
documents.push(
|
|
158
|
+
...document(OPENCODE_CONFIG_DOCUMENT, [
|
|
159
|
+
{
|
|
160
|
+
at: ["plugin"],
|
|
161
|
+
kind: "list",
|
|
162
|
+
values: [getOpenCodePluginPackageReference()],
|
|
163
|
+
},
|
|
164
|
+
{ at: ["mcp"], kind: "map", entries: renderManagedOpenCodeMcpServers(inputs) },
|
|
165
|
+
{
|
|
166
|
+
at: ["permission", "external_directory"],
|
|
167
|
+
kind: "map",
|
|
168
|
+
entries: renderCompanionExternalDirectoryPermissions(ctx.companionPath),
|
|
169
|
+
},
|
|
170
|
+
]),
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return documents;
|
|
174
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import type { ProjectionTarget, SetupContext } from "./plugin";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The projection target of a reconcile pass, resolved once. A `working` pass
|
|
7
|
+
* without a Working Repository in scope refuses rather than falling back to the
|
|
8
|
+
* companion: writing the other root is the one outcome the caller cannot detect.
|
|
9
|
+
*/
|
|
10
|
+
export function projectionTarget(ctx: SetupContext): ProjectionTarget {
|
|
11
|
+
const target = ctx.target ?? "companion";
|
|
12
|
+
if (target === "working" && !ctx.repoPath) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"a working-target reconcile needs a Working Repository in scope; none was supplied",
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return target;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The Working Repository a rendered document is addressed by, resolved so a
|
|
22
|
+
* symlinked working directory still matches what the runtime reports. Every
|
|
23
|
+
* document a reconcile pass renders is for a Working Repository, so a pass with
|
|
24
|
+
* none in scope refuses here rather than resolving an absent path: `path.resolve`
|
|
25
|
+
* answers the current working directory for an empty one, and a document keyed
|
|
26
|
+
* by wherever the process happened to start would file a Runtime Surface's MCP
|
|
27
|
+
* servers under a stranger's project in the user's global Claude config — a
|
|
28
|
+
* write no caller asked for and none would notice.
|
|
29
|
+
*/
|
|
30
|
+
export function projectionRepoRoot(ctx: SetupContext): string {
|
|
31
|
+
const target = projectionTarget(ctx);
|
|
32
|
+
if (!ctx.repoPath) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`a ${target}-target render needs a Working Repository in scope to address its documents by; none was supplied`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return path.resolve(ctx.repoPath);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
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.
|
|
44
|
+
*/
|
|
45
|
+
export function surfaceRoot(ctx: SetupContext): string {
|
|
46
|
+
if (projectionTarget(ctx) === "working") {
|
|
47
|
+
throw new Error("a working-target reconcile renders its documents; it writes no surface root");
|
|
48
|
+
}
|
|
49
|
+
return ctx.companionPath;
|
|
50
|
+
}
|
|
@@ -1,40 +1,47 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { repoLocalDirPath } from "../../lib/orchestrator/repo-local-registry";
|
|
4
|
-
import { cleanupWorkingRepoClaudeSettings } from "./providers/claude";
|
|
5
|
-
import { removeWorkingRepoLocalExcludes } from "./working-repo-local-state";
|
|
1
|
+
import { unproject } from "../../lib/orchestrator/working-repo-projection";
|
|
2
|
+
import type { ProjectionEntryId } from "../../lib/orchestrator/projection-types";
|
|
6
3
|
|
|
7
4
|
export interface WorkingRepoCleanupResult {
|
|
8
5
|
changed: boolean;
|
|
9
6
|
removed: string[];
|
|
10
7
|
updated: string[];
|
|
8
|
+
/** Entries `unproject` deliberately left in place, named rather than omitted. */
|
|
9
|
+
retained: string[];
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
12
|
+
/**
|
|
13
|
+
* How an entry is reported. Presentation only — cleanup holds no list of paths,
|
|
14
|
+
* so a newly declared entry is removed without touching this map.
|
|
15
|
+
*/
|
|
16
|
+
const REPORTED_AS: Partial<
|
|
17
|
+
Record<ProjectionEntryId, { as: "removed" | "updated"; label: string }>
|
|
18
|
+
> = {
|
|
19
|
+
"projection-root": { as: "removed", label: ".mate" },
|
|
20
|
+
"git-excludes": { as: "removed", label: "git-excludes" },
|
|
21
|
+
"claude-working-settings": { as: "updated", label: "claude-settings" },
|
|
22
|
+
"legacy-tokensave-claude-md": { as: "updated", label: "claude-settings" },
|
|
23
|
+
};
|
|
21
24
|
|
|
22
25
|
export async function cleanupWorkingRepository(
|
|
23
26
|
repoPath: string,
|
|
24
|
-
registeredCompanionPaths: string[],
|
|
27
|
+
registeredCompanionPaths: string[] = [],
|
|
25
28
|
): Promise<WorkingRepoCleanupResult> {
|
|
26
|
-
const
|
|
29
|
+
const { outcomes } = await unproject({ repoPath, registeredCompanionPaths });
|
|
27
30
|
const removed: string[] = [];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
const updated: string[] = [];
|
|
32
|
+
const retained: string[] = [];
|
|
33
|
+
|
|
34
|
+
for (const entry of outcomes) {
|
|
35
|
+
if (entry.state === "retained") {
|
|
36
|
+
retained.push(entry.id);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (entry.state !== "removed") continue;
|
|
40
|
+
const reported = REPORTED_AS[entry.id];
|
|
41
|
+
if (!reported) continue;
|
|
42
|
+
const bucket = reported.as === "removed" ? removed : updated;
|
|
43
|
+
if (!bucket.includes(reported.label)) bucket.push(reported.label);
|
|
38
44
|
}
|
|
39
|
-
|
|
45
|
+
|
|
46
|
+
return { changed: removed.length > 0 || updated.length > 0, removed, updated, retained };
|
|
40
47
|
}
|
|
@@ -5,7 +5,8 @@ import { resolveGitInfoExcludePath } from "./git-utils";
|
|
|
5
5
|
|
|
6
6
|
const MANAGED_BLOCK_START = "# mate managed: start";
|
|
7
7
|
const MANAGED_BLOCK_END = "# mate managed: end";
|
|
8
|
-
|
|
8
|
+
/** `.mcp.json` is the one working-target document no directory entry covers. */
|
|
9
|
+
const CORE_EXCLUDE_ENTRIES = ["/.mate/", "/.claude/", "/.opencode/", "/.agents/", "/.mcp.json"];
|
|
9
10
|
const LEGACY_CORE_EXCLUDE_ENTRIES = new Set([".mate/", ".claude/settings.local.json"]);
|
|
10
11
|
|
|
11
12
|
async function readExclude(excludePath: string): Promise<string> {
|
|
@@ -90,6 +91,25 @@ export async function reconcileWorkingRepoCapabilityExcludes(
|
|
|
90
91
|
return writeExclude(excludePath, existing, lines);
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
/** Whether the managed block is on disk; the block, not the exclude file. */
|
|
95
|
+
export async function managedWorkingRepoExcludesPresent(repoPath: string): Promise<boolean> {
|
|
96
|
+
const excludePath = await resolveGitInfoExcludePath(repoPath);
|
|
97
|
+
if (!excludePath) return false;
|
|
98
|
+
const lines = contentLines(await readExclude(excludePath));
|
|
99
|
+
return lines.some((line) => line.trim() === MANAGED_BLOCK_START);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Whether any Capability-owned exclusion is present outside the managed block. */
|
|
103
|
+
export async function workingRepoCapabilityExcludesPresent(
|
|
104
|
+
repoPath: string,
|
|
105
|
+
candidateEntries: string[],
|
|
106
|
+
): Promise<boolean> {
|
|
107
|
+
const excludePath = await resolveGitInfoExcludePath(repoPath);
|
|
108
|
+
if (!excludePath) return false;
|
|
109
|
+
const lines = withoutManagedBlocks(contentLines(await readExclude(excludePath)));
|
|
110
|
+
return candidateEntries.some((entry) => lines.some((line) => line.trim() === entry));
|
|
111
|
+
}
|
|
112
|
+
|
|
93
113
|
export async function removeWorkingRepoLocalExcludes(repoPath: string): Promise<boolean> {
|
|
94
114
|
const excludePath = await resolveGitInfoExcludePath(repoPath);
|
|
95
115
|
if (!excludePath) return false;
|
package/src/tools/setup.ts
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
type CapabilityConfig,
|
|
20
20
|
type FrameworkConfig,
|
|
21
21
|
} from "../lib/orchestrator/types";
|
|
22
|
-
import { syncWorkingRepoClaudeSettings } from "./setup/providers/claude";
|
|
23
22
|
import {
|
|
24
23
|
collectManagedGitignoreEntries,
|
|
25
24
|
writeManagedGitignoreBlock,
|
|
@@ -35,10 +34,10 @@ import { installDeclaredPlugins } from "./setup/dynamic-plugins/install";
|
|
|
35
34
|
import {
|
|
36
35
|
buildSetupInstallationPlan,
|
|
37
36
|
executeSetupInstallationPlan,
|
|
37
|
+
renderRuntimeDocuments,
|
|
38
38
|
type SetupInstallationOutcome,
|
|
39
39
|
} from "./setup/engine";
|
|
40
|
-
|
|
41
|
-
export { syncWorkingRepoClaudeSettings };
|
|
40
|
+
import type { RenderedRuntimeDocument } from "../lib/orchestrator/projection-types";
|
|
42
41
|
|
|
43
42
|
export interface SetupInput {
|
|
44
43
|
allowedAgents?: string[];
|
|
@@ -67,6 +66,29 @@ export async function applySetupCompatibilities(
|
|
|
67
66
|
return executeSetupInstallationPlan(ctx, plugins, plan);
|
|
68
67
|
}
|
|
69
68
|
|
|
69
|
+
/**
|
|
70
|
+
* The active Agent Runtimes' documents for a Working Repository, rendered
|
|
71
|
+
* without a write. Runs no plugin phase: the working target reconciles
|
|
72
|
+
* declarations only, so nothing is written to the companion on its behalf.
|
|
73
|
+
*/
|
|
74
|
+
export async function renderWorkingRuntimeDocuments(
|
|
75
|
+
companionPath: string,
|
|
76
|
+
config: FrameworkConfig,
|
|
77
|
+
repoPath: string,
|
|
78
|
+
plugins: PluginRegistration[] = getActiveDistribution().registry.getAll(),
|
|
79
|
+
): Promise<RenderedRuntimeDocument[]> {
|
|
80
|
+
const plan = buildSetupInstallationPlan(config, plugins);
|
|
81
|
+
const ctx: SetupContext = {
|
|
82
|
+
companionPath,
|
|
83
|
+
config,
|
|
84
|
+
mode: "sync",
|
|
85
|
+
activeProviders: plan.activeProviders,
|
|
86
|
+
repoPath,
|
|
87
|
+
target: "working",
|
|
88
|
+
};
|
|
89
|
+
return renderRuntimeDocuments(ctx, plugins, plan);
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
export async function updateProjectGitignore(
|
|
71
93
|
companionPath: string,
|
|
72
94
|
config: FrameworkConfig,
|
package/wrappers/bin/graphify
CHANGED
|
@@ -1,8 +1,59 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
[ -n "$MATE_ARTIFACT_PATH" ] || { echo "graphify: must be run via mate claude or mate opencode (MATE_ARTIFACT_PATH not set)" >&2; exit 1; }
|
|
3
|
-
[ -n "$MATE_REPO_PATH" ] || { echo "graphify: must be run via mate claude or mate opencode (MATE_REPO_PATH not set)" >&2; exit 1; }
|
|
4
|
-
|
|
5
2
|
_this_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
3
|
+
|
|
4
|
+
# Mate context: the launch environment first, the Working Repository's
|
|
5
|
+
# Projection Root second. Projected values stay in private shell variables and
|
|
6
|
+
# no MATE_-prefixed variable is exported: the projection carries paths and
|
|
7
|
+
# never predicates, so a partial MATE_* environment would read to any child as
|
|
8
|
+
# authoritative with every predicate disabled.
|
|
9
|
+
_mate_projection_file() {
|
|
10
|
+
local dir="$PWD"
|
|
11
|
+
while :; do
|
|
12
|
+
if [ -f "$dir/.mate/config/registry.yaml" ]; then
|
|
13
|
+
[ -f "$dir/.mate/projection.env" ] && printf '%s' "$dir/.mate/projection.env"
|
|
14
|
+
return
|
|
15
|
+
fi
|
|
16
|
+
[ "$dir" = "/" ] && return
|
|
17
|
+
dir="$(dirname "$dir")"
|
|
18
|
+
done
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_mate_projected() {
|
|
22
|
+
sed -n "s/^$2='\\(.*\\)'\$/\\1/p" "$1" | head -n 1 | sed "s/'\\\\''/'/g"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_mate_same_dir() {
|
|
26
|
+
local left="$1" right="$2"
|
|
27
|
+
[ -d "$left" ] && left="$(cd "$left" && pwd)"
|
|
28
|
+
[ -d "$right" ] && right="$(cd "$right" && pwd)"
|
|
29
|
+
[ "$left" = "$right" ]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_companion="$MATE_ARTIFACT_PATH"
|
|
33
|
+
_repo_path="$MATE_REPO_PATH"
|
|
34
|
+
_graphify_out="$GRAPHIFY_OUT"
|
|
35
|
+
if [ -z "$_companion" ]; then
|
|
36
|
+
_projection="$(_mate_projection_file)"
|
|
37
|
+
if [ -n "$_projection" ]; then
|
|
38
|
+
_companion="$(_mate_projected "$_projection" MATE_ARTIFACT_PATH)"
|
|
39
|
+
_repo_path="$(_mate_projected "$_projection" MATE_REPO_PATH)"
|
|
40
|
+
_graphify_out="$(_mate_projected "$_projection" GRAPHIFY_OUT)"
|
|
41
|
+
_projected_wrapper_dir="$(_mate_projected "$_projection" MATE_WRAPPER_BIN_PATH)"
|
|
42
|
+
if [ -n "$_companion" ] && ! _mate_same_dir "$_projected_wrapper_dir" "$_this_dir"; then
|
|
43
|
+
{
|
|
44
|
+
echo "graphify: this repository was wrapped by a different mate install."
|
|
45
|
+
echo " projected wrappers: $_projected_wrapper_dir"
|
|
46
|
+
echo " running wrapper: $_this_dir"
|
|
47
|
+
echo " re-run \`mate wrap\` with this install."
|
|
48
|
+
} >&2
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
[ -n "$_companion" ] || { echo "graphify: no mate context here — run \`mate wrap\` in this repository" >&2; exit 1; }
|
|
54
|
+
[ -n "$_repo_path" ] || { echo "graphify: no working repository in the mate context — run \`mate wrap\` in this repository" >&2; exit 1; }
|
|
55
|
+
[ -n "$_graphify_out" ] || { echo "graphify: no graphify output path in the mate context — run \`mate wrap\` in this repository" >&2; exit 1; }
|
|
56
|
+
|
|
6
57
|
_real=""
|
|
7
58
|
IFS=:
|
|
8
59
|
for _dir in $PATH; do
|
|
@@ -14,8 +65,6 @@ done
|
|
|
14
65
|
unset IFS
|
|
15
66
|
[ -n "$_real" ] || { echo "graphify wrapper: real graphify binary not found in PATH" >&2; exit 1; }
|
|
16
67
|
|
|
17
|
-
_repo_id="${MATE_REPO_ID:-unknown}"
|
|
18
|
-
_graphify_out="$MATE_ARTIFACT_PATH/.graphify/$_repo_id/graphify-out"
|
|
19
68
|
_graphify_root="$(dirname "$_graphify_out")"
|
|
20
69
|
_graph_json="$_graphify_out/graph.json"
|
|
21
70
|
export GRAPHIFY_OUT="$_graphify_out"
|
|
@@ -32,18 +81,18 @@ case "$_cmd" in
|
|
|
32
81
|
*) _rest+=("$_a") ;;
|
|
33
82
|
esac
|
|
34
83
|
done
|
|
35
|
-
exec "$_real" extract "$
|
|
84
|
+
exec "$_real" extract "$_repo_path" --out "$_graphify_root" "${_rest[@]}"
|
|
36
85
|
;;
|
|
37
86
|
update)
|
|
38
87
|
if [ "$#" -eq 1 ] || [[ "$2" == -* ]]; then
|
|
39
|
-
exec "$_real" "$
|
|
88
|
+
exec "$_real" "$_repo_path" --update "${@:2}"
|
|
40
89
|
else
|
|
41
90
|
exec "$_real" "$2" --update "${@:3}"
|
|
42
91
|
fi
|
|
43
92
|
;;
|
|
44
93
|
extract|watch|cluster-only|label|check-update)
|
|
45
94
|
if [ "$#" -eq 1 ] || [[ "$2" == -* ]]; then
|
|
46
|
-
exec "$_real" "$_cmd" "$
|
|
95
|
+
exec "$_real" "$_cmd" "$_repo_path" "${@:2}"
|
|
47
96
|
else
|
|
48
97
|
exec "$_real" "$@"
|
|
49
98
|
fi
|