@agentxm/extension-authoring 0.28.12 → 0.28.13
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/dist/src/adopt/adopt-extension.d.ts +71 -0
- package/dist/src/adopt/adopt-extension.js +174 -0
- package/dist/src/authored-declaration.d.ts +49 -0
- package/dist/src/authored-declaration.js +86 -0
- package/dist/src/authored-package-errors.d.ts +91 -0
- package/dist/src/authored-package-errors.js +36 -0
- package/dist/src/create/authoring-owner.d.ts +54 -0
- package/dist/src/create/authoring-owner.js +93 -0
- package/dist/src/create/create-extension.d.ts +127 -0
- package/dist/src/create/create-extension.js +440 -0
- package/dist/src/create/errors.d.ts +59 -0
- package/dist/src/create/errors.js +54 -0
- package/dist/src/create/scaffold-name.d.ts +15 -0
- package/dist/src/create/scaffold-name.js +16 -0
- package/dist/src/create/scaffolds/hook.d.ts +19 -0
- package/dist/src/create/scaffolds/hook.js +118 -0
- package/dist/src/create/scaffolds/knowledge.d.ts +14 -0
- package/dist/src/create/scaffolds/knowledge.js +45 -0
- package/dist/src/create/scaffolds/mcp-server.d.ts +17 -0
- package/dist/src/create/scaffolds/mcp-server.js +57 -0
- package/dist/src/create/scaffolds/pack.d.ts +13 -0
- package/dist/src/create/scaffolds/pack.js +37 -0
- package/dist/src/create/scaffolds/rule.d.ts +14 -0
- package/dist/src/create/scaffolds/rule.js +46 -0
- package/dist/src/create/scaffolds/scaffold.d.ts +51 -0
- package/dist/src/create/scaffolds/scaffold.js +45 -0
- package/dist/src/create/scaffolds/skill.d.ts +12 -0
- package/dist/src/create/scaffolds/skill.js +47 -0
- package/dist/src/create/scaffolds/subagent.d.ts +12 -0
- package/dist/src/create/scaffolds/subagent.js +47 -0
- package/dist/src/create-preflight.d.ts +3 -2
- package/dist/src/create-preflight.js +2 -1
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js +1 -1
- package/dist/src/fork/fork-extension.d.ts +87 -0
- package/dist/src/fork/fork-extension.js +253 -0
- package/dist/src/fork-package.d.ts +2 -2
- package/dist/src/fork-package.js +4 -4
- package/dist/src/import/import-native-extension.d.ts +119 -0
- package/dist/src/import/import-native-extension.js +310 -0
- package/dist/src/import-native-package.d.ts +2 -2
- package/dist/src/import-native-package.js +4 -4
- package/dist/src/index.d.ts +20 -11
- package/dist/src/index.js +26 -13
- package/dist/src/packs/add-to-pack.d.ts +2 -2
- package/dist/src/packs/add-to-pack.js +4 -3
- package/dist/src/packs/change-pack-membership.d.ts +56 -0
- package/dist/src/packs/change-pack-membership.js +246 -0
- package/dist/src/packs/configured-pack-selector.d.ts +24 -0
- package/dist/src/packs/configured-pack-selector.js +53 -0
- package/dist/src/packs/glob.d.ts +14 -0
- package/dist/src/packs/glob.js +44 -0
- package/dist/src/packs/membership-errors.d.ts +122 -0
- package/dist/src/packs/membership-errors.js +47 -0
- package/dist/src/packs/remove-from-pack.d.ts +2 -2
- package/dist/src/packs/remove-from-pack.js +4 -3
- package/dist/src/step-failure.d.ts +31 -0
- package/dist/src/step-failure.js +253 -0
- package/dist/src/testing.d.ts +68 -0
- package/dist/src/testing.js +135 -0
- package/dist/src/version/change-authored-version.d.ts +73 -0
- package/dist/src/version/change-authored-version.js +224 -0
- package/dist/src/version/errors.d.ts +65 -0
- package/dist/src/version/errors.js +73 -0
- package/package.json +23 -8
- package/dist/src/desired-identity.d.ts +0 -19
- package/dist/src/desired-identity.js +0 -26
- package/dist/src/failure-adapter.d.ts +0 -31
- package/dist/src/failure-adapter.js +0 -23
- package/dist/src/hooks/new-hook.d.ts +0 -46
- package/dist/src/hooks/new-hook.js +0 -164
- package/dist/src/packs/new-pack.d.ts +0 -37
- package/dist/src/packs/new-pack.js +0 -90
- package/dist/src/skills/new-skill.d.ts +0 -38
- package/dist/src/skills/new-skill.js +0 -102
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creating a new authored extension.
|
|
3
|
+
*
|
|
4
|
+
* One use case for every extension type: the owner the package is created
|
|
5
|
+
* under, the name it may carry, the canonical location it occupies, the
|
|
6
|
+
* create-only refusal that protects existing content, the desired-state
|
|
7
|
+
* entry that declares it, and the projection that makes it observable are all
|
|
8
|
+
* decided here. What differs per type — the manifest and starter body — is a
|
|
9
|
+
* scaffold; what differs per command — flags, wording, exit codes — stays in
|
|
10
|
+
* the application.
|
|
11
|
+
*
|
|
12
|
+
* `prepare` decides everything and writes nothing: it settles ownership,
|
|
13
|
+
* naming, and collisions, then freezes an execution candidate. `previewOrApply`
|
|
14
|
+
* resolves that same candidate, so preview and apply describe one decision
|
|
15
|
+
* rather than two.
|
|
16
|
+
*
|
|
17
|
+
* @experimental This API is unstable and may change without notice.
|
|
18
|
+
*/
|
|
19
|
+
import * as Effect from "effect/Effect";
|
|
20
|
+
import type * as FileSystem from "effect/FileSystem";
|
|
21
|
+
import * as Option from "effect/Option";
|
|
22
|
+
import * as Path from "effect/Path";
|
|
23
|
+
import { ExtensionManagers, type ManagerRequirements, type RecipeRequirements } from "@agentxm/extension-materialization";
|
|
24
|
+
import { McpSecretStore } from "@agentxm/extension-materialization";
|
|
25
|
+
import { type Handle } from "@agentxm/extension-model/unstable/extensions";
|
|
26
|
+
import type { HookEvent, HookRuntime } from "@agentxm/extension-model/unstable/hooks/manifest-schema";
|
|
27
|
+
import { type ExecutionCandidate, type PlanExecution } from "@agentxm/workspace-operations";
|
|
28
|
+
import { CodingAgentRepository } from "@agentxm/workspace-projection";
|
|
29
|
+
import type { CredentialStore } from "@agentxm/registry-auth";
|
|
30
|
+
import type { RegistryUrl } from "@agentxm/registry-client";
|
|
31
|
+
import type { FqnInvalidError } from "@agentxm/extension-model/unstable/extensions";
|
|
32
|
+
import type { CandidateFingerprintFailed } from "@agentxm/workspace-operations";
|
|
33
|
+
import { WorkspaceMutations, type ConfiguredAgentOutcomesProvider } from "@agentxm/workspace-state";
|
|
34
|
+
import { type AuthoringStepFailure } from "../step-failure.js";
|
|
35
|
+
import { AuthoringScopeUnsupported, ScaffoldNameInvalid, type AuthoringOwnerMismatch, type AuthoringOwnerRequired } from "./errors.js";
|
|
36
|
+
interface CreateRequestBase {
|
|
37
|
+
/** Name without owner, as the person typed it. */
|
|
38
|
+
readonly name: string;
|
|
39
|
+
/** An explicitly requested owner, if the person named one. */
|
|
40
|
+
readonly owner: Option.Option<string>;
|
|
41
|
+
}
|
|
42
|
+
export interface CreateSkillRequest extends CreateRequestBase {
|
|
43
|
+
readonly type: "skill";
|
|
44
|
+
}
|
|
45
|
+
export interface CreateSubagentRequest extends CreateRequestBase {
|
|
46
|
+
readonly type: "subagent";
|
|
47
|
+
}
|
|
48
|
+
export interface CreateRuleRequest extends CreateRequestBase {
|
|
49
|
+
readonly type: "rule";
|
|
50
|
+
readonly title: Option.Option<string>;
|
|
51
|
+
}
|
|
52
|
+
export interface CreateHookRequest extends CreateRequestBase {
|
|
53
|
+
readonly type: "hook";
|
|
54
|
+
readonly runtime: HookRuntime;
|
|
55
|
+
readonly event: HookEvent;
|
|
56
|
+
readonly matcher: Option.Option<string>;
|
|
57
|
+
}
|
|
58
|
+
export interface CreateKnowledgeRequest extends CreateRequestBase {
|
|
59
|
+
readonly type: "knowledge";
|
|
60
|
+
readonly description: Option.Option<string>;
|
|
61
|
+
}
|
|
62
|
+
export interface CreatePackRequest extends CreateRequestBase {
|
|
63
|
+
readonly type: "pack";
|
|
64
|
+
}
|
|
65
|
+
export interface CreateMcpServerRequest extends CreateRequestBase {
|
|
66
|
+
readonly type: "mcp-server";
|
|
67
|
+
readonly description: Option.Option<string>;
|
|
68
|
+
/**
|
|
69
|
+
* Whether the invoking surface can prompt for a connection input the
|
|
70
|
+
* manifest requires. A new server scaffolds a placeholder package with no
|
|
71
|
+
* required inputs, so this only decides how a hand-edited manifest behaves
|
|
72
|
+
* on a second run.
|
|
73
|
+
*/
|
|
74
|
+
readonly nonInteractive: boolean;
|
|
75
|
+
}
|
|
76
|
+
/** What an author asked to create. */
|
|
77
|
+
export type CreateExtensionRequest = CreateSkillRequest | CreateSubagentRequest | CreateRuleRequest | CreateHookRequest | CreateKnowledgeRequest | CreatePackRequest | CreateMcpServerRequest;
|
|
78
|
+
/** The extension types `CreateExtension` scaffolds. */
|
|
79
|
+
export type CreatableExtensionType = CreateExtensionRequest["type"];
|
|
80
|
+
/** What every step in this creation may require when it runs. */
|
|
81
|
+
export type CreateExtensionRequirements = ManagerRequirements | RecipeRequirements | WorkspaceMutations | CodingAgentRepository | McpSecretStore;
|
|
82
|
+
/**
|
|
83
|
+
* A settled creation: every decision is made and nothing is written. The
|
|
84
|
+
* execution candidate carries the frozen plan; the identity fields answer
|
|
85
|
+
* what the application renders around it.
|
|
86
|
+
*/
|
|
87
|
+
export interface CreateExtensionCandidate {
|
|
88
|
+
readonly type: CreatableExtensionType;
|
|
89
|
+
/** Owner-qualified identity of the package being created. */
|
|
90
|
+
readonly fqn: string;
|
|
91
|
+
readonly owner: Handle;
|
|
92
|
+
readonly name: string;
|
|
93
|
+
/** Workspace-relative directory the package occupies. */
|
|
94
|
+
readonly authoredPath: string;
|
|
95
|
+
/** Workspace-relative file the author edits next. */
|
|
96
|
+
readonly entryPath: string;
|
|
97
|
+
/** Workspace-relative settings file the declaration is written to. */
|
|
98
|
+
readonly settingsPath: string;
|
|
99
|
+
readonly execution: ExecutionCandidate<CreateExtensionRequirements>;
|
|
100
|
+
}
|
|
101
|
+
/** Every failure settling a creation can surface before anything is written. */
|
|
102
|
+
export type CreateExtensionFailure = AuthoringStepFailure | AuthoringOwnerRequired | AuthoringOwnerMismatch | ScaffoldNameInvalid | AuthoringScopeUnsupported | CandidateFingerprintFailed | FqnInvalidError;
|
|
103
|
+
/** Everything settling a creation reads before it freezes a candidate. */
|
|
104
|
+
export type PrepareCreateExtensionRequirements = FileSystem.FileSystem | Path.Path | CredentialStore | RegistryUrl | WorkspaceMutations | CodingAgentRepository | ConfiguredAgentOutcomesProvider | ExtensionManagers;
|
|
105
|
+
/**
|
|
106
|
+
* The plan a creation of this type resolves. It names the operation in
|
|
107
|
+
* previews, in machine output, and in an interruption report, so the feature
|
|
108
|
+
* that builds the plan owns it rather than each command restating it.
|
|
109
|
+
*/
|
|
110
|
+
export declare const createExtensionPlanName: (type: CreatableExtensionType) => string;
|
|
111
|
+
/**
|
|
112
|
+
* Settle a creation without writing anything.
|
|
113
|
+
*
|
|
114
|
+
* The create-only refusal runs here so a preview refuses an occupied name,
|
|
115
|
+
* and again inside the transaction so a candidate that went stale between
|
|
116
|
+
* preview and apply writes nothing.
|
|
117
|
+
*/
|
|
118
|
+
export declare const prepareCreateExtension: (request: CreateExtensionRequest) => Effect.Effect<CreateExtensionCandidate, CreateExtensionFailure, PrepareCreateExtensionRequirements>;
|
|
119
|
+
/** Preview or apply a settled creation, resolving to one operation outcome. */
|
|
120
|
+
export declare const previewOrApplyCreateExtension: (candidate: CreateExtensionCandidate, execution: PlanExecution) => Effect.Effect<import("@agentxm/workspace-operations").OperationResolution<never>, import("@agentxm/workspace-state").WorkspaceSettingsReadFailure | import("@agentxm/workspace-operations").ApprovalRecoveryMissing | import("@agentxm/workspace-operations").PlanInteractionFailed | import("@agentxm/workspace-transactions").WorkspaceTransitionAcquireFailure, CreateExtensionRequirements | ConfiguredAgentOutcomesProvider | import("@agentxm/workspace-operations").OperationJournal | import("@agentxm/workspace-operations").ResolvePlanInteraction | import("@agentxm/workspace-transactions").FootprintRecorder>;
|
|
121
|
+
/** The creation use case: settle a request, then preview or apply it. */
|
|
122
|
+
export declare const CreateExtension: {
|
|
123
|
+
readonly prepare: (request: CreateExtensionRequest) => Effect.Effect<CreateExtensionCandidate, CreateExtensionFailure, PrepareCreateExtensionRequirements>;
|
|
124
|
+
readonly previewOrApply: (candidate: CreateExtensionCandidate, execution: PlanExecution) => Effect.Effect<import("@agentxm/workspace-operations").OperationResolution<never>, import("@agentxm/workspace-state").WorkspaceSettingsReadFailure | import("@agentxm/workspace-operations").ApprovalRecoveryMissing | import("@agentxm/workspace-operations").PlanInteractionFailed | import("@agentxm/workspace-transactions").WorkspaceTransitionAcquireFailure, CreateExtensionRequirements | ConfiguredAgentOutcomesProvider | import("@agentxm/workspace-operations").OperationJournal | import("@agentxm/workspace-operations").ResolvePlanInteraction | import("@agentxm/workspace-transactions").FootprintRecorder>;
|
|
125
|
+
};
|
|
126
|
+
export {};
|
|
127
|
+
//# sourceMappingURL=create-extension.d.ts.map
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creating a new authored extension.
|
|
3
|
+
*
|
|
4
|
+
* One use case for every extension type: the owner the package is created
|
|
5
|
+
* under, the name it may carry, the canonical location it occupies, the
|
|
6
|
+
* create-only refusal that protects existing content, the desired-state
|
|
7
|
+
* entry that declares it, and the projection that makes it observable are all
|
|
8
|
+
* decided here. What differs per type — the manifest and starter body — is a
|
|
9
|
+
* scaffold; what differs per command — flags, wording, exit codes — stays in
|
|
10
|
+
* the application.
|
|
11
|
+
*
|
|
12
|
+
* `prepare` decides everything and writes nothing: it settles ownership,
|
|
13
|
+
* naming, and collisions, then freezes an execution candidate. `previewOrApply`
|
|
14
|
+
* resolves that same candidate, so preview and apply describe one decision
|
|
15
|
+
* rather than two.
|
|
16
|
+
*
|
|
17
|
+
* @experimental This API is unstable and may change without notice.
|
|
18
|
+
*/
|
|
19
|
+
import * as Effect from "effect/Effect";
|
|
20
|
+
import * as Option from "effect/Option";
|
|
21
|
+
import * as Path from "effect/Path";
|
|
22
|
+
import { ExtensionManagers, buildAuthoredExtensionStep, buildNewExtensionStep, createCanonicalDirectory, groupInstallTargetsByDirectory, recoverCanonicalDirectory, artifactAgentIdsFromTargets, artifactTargetAgentIds, } from "@agentxm/extension-materialization";
|
|
23
|
+
import { McpSecretStore, materializeAuthoredMcpServer } from "@agentxm/extension-materialization";
|
|
24
|
+
import { CONFIGURABLE_AGENTS_BY_ID } from "@agentxm/extension-model/unstable/agent-capabilities";
|
|
25
|
+
import { toExtensionTypePlural, formatFqn, decodeExtensionNameSync, } from "@agentxm/extension-model/unstable/extensions";
|
|
26
|
+
import { operationPresentation, prepareExecutionCandidate, resolveExecutionCandidate, } from "@agentxm/workspace-operations";
|
|
27
|
+
import { CodingAgentRepository } from "@agentxm/workspace-projection";
|
|
28
|
+
import { WorkspaceMutations, computeExtensionPathsForLayout, computePackPathsForLayout, computeSourceHash, } from "@agentxm/workspace-state";
|
|
29
|
+
import { authoredDeclaration } from "../authored-declaration.js";
|
|
30
|
+
import { preflightCreateOnly } from "../create-preflight.js";
|
|
31
|
+
import { authoringStepFailure } from "../step-failure.js";
|
|
32
|
+
import { resolveAuthoringOwner, settingsRelativePath } from "./authoring-owner.js";
|
|
33
|
+
import { AuthoringScopeUnsupported, ScaffoldNameInvalid, } from "./errors.js";
|
|
34
|
+
import { hookScaffold } from "./scaffolds/hook.js";
|
|
35
|
+
import { knowledgeScaffold } from "./scaffolds/knowledge.js";
|
|
36
|
+
import { mcpServerScaffold } from "./scaffolds/mcp-server.js";
|
|
37
|
+
import { packScaffold } from "./scaffolds/pack.js";
|
|
38
|
+
import { ruleScaffold } from "./scaffolds/rule.js";
|
|
39
|
+
import { skillScaffold } from "./scaffolds/skill.js";
|
|
40
|
+
import { subagentScaffold } from "./scaffolds/subagent.js";
|
|
41
|
+
import { SCAFFOLD_NAME_MAX_LENGTH, SCAFFOLD_NAME_PATTERN, isValidScaffoldName, } from "./scaffold-name.js";
|
|
42
|
+
// -----------------------------------------------------------------------------
|
|
43
|
+
// Per-type facts
|
|
44
|
+
// -----------------------------------------------------------------------------
|
|
45
|
+
/** How each type reads its declarations and writes the one it creates. */
|
|
46
|
+
const declaration = (ws, type, name) => {
|
|
47
|
+
const target = authoredDeclaration(ws, type, name);
|
|
48
|
+
return {
|
|
49
|
+
isConfigured: target.read.pipe(Effect.map((current) => current.configured)),
|
|
50
|
+
declare: target.declare({ enabled: true }),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const scaffoldFor = (request, owner) => {
|
|
54
|
+
switch (request.type) {
|
|
55
|
+
case "skill":
|
|
56
|
+
return skillScaffold({ name: request.name, owner });
|
|
57
|
+
case "subagent":
|
|
58
|
+
return subagentScaffold({ name: request.name, owner });
|
|
59
|
+
case "rule":
|
|
60
|
+
return ruleScaffold({ name: request.name, owner, title: request.title });
|
|
61
|
+
case "hook":
|
|
62
|
+
return hookScaffold({
|
|
63
|
+
name: request.name,
|
|
64
|
+
owner,
|
|
65
|
+
runtime: request.runtime,
|
|
66
|
+
event: request.event,
|
|
67
|
+
matcher: request.matcher,
|
|
68
|
+
});
|
|
69
|
+
case "knowledge":
|
|
70
|
+
return knowledgeScaffold({ name: request.name, owner, description: request.description });
|
|
71
|
+
case "pack":
|
|
72
|
+
return packScaffold({ name: request.name, owner });
|
|
73
|
+
case "mcp-server":
|
|
74
|
+
return mcpServerScaffold({
|
|
75
|
+
name: request.name,
|
|
76
|
+
owner,
|
|
77
|
+
description: Option.getOrElse(request.description, () => ""),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
/** The subject and command route a refusal names, per type. */
|
|
82
|
+
const route = (type) => {
|
|
83
|
+
switch (type) {
|
|
84
|
+
case "skill":
|
|
85
|
+
return { subject: "skill", command: "skills new" };
|
|
86
|
+
case "subagent":
|
|
87
|
+
return { subject: "subagent", command: "subagents new" };
|
|
88
|
+
case "rule":
|
|
89
|
+
return { subject: "rule", command: "rules new" };
|
|
90
|
+
case "hook":
|
|
91
|
+
return { subject: "hook", command: "hooks new" };
|
|
92
|
+
case "knowledge":
|
|
93
|
+
return { subject: "knowledge bundle", command: "knowledge new" };
|
|
94
|
+
case "pack":
|
|
95
|
+
return { subject: "pack", command: "packs new" };
|
|
96
|
+
case "mcp-server":
|
|
97
|
+
return { subject: "MCP server", command: "mcps new" };
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* The plan a creation of this type resolves. It names the operation in
|
|
102
|
+
* previews, in machine output, and in an interruption report, so the feature
|
|
103
|
+
* that builds the plan owns it rather than each command restating it.
|
|
104
|
+
*/
|
|
105
|
+
export const createExtensionPlanName = (type) => `New ${route(type).subject}`;
|
|
106
|
+
/** The canonical directory an authored package of this type occupies. */
|
|
107
|
+
const canonicalLocation = (path, ws, type, owner, name) => type === "pack"
|
|
108
|
+
? computePackPathsForLayout(path.join, ws.layout, "workspace", owner, name).canonicalPath
|
|
109
|
+
: computeExtensionPathsForLayout(path.join, ws.layout, { refType: "workspace", owner }, toExtensionTypePlural(type), name).canonicalPath;
|
|
110
|
+
/**
|
|
111
|
+
* Where a skill becomes observable: the universal location and every
|
|
112
|
+
* configured agent that can represent one. Preview and apply read the same
|
|
113
|
+
* function, so a preview lists exactly the locations an apply writes.
|
|
114
|
+
*/
|
|
115
|
+
const skillTargetLocations = Effect.fn("CreateExtension.skillTargetLocations")(function* () {
|
|
116
|
+
const ws = yield* WorkspaceMutations;
|
|
117
|
+
const path = yield* Path.Path;
|
|
118
|
+
const agents = yield* (yield* CodingAgentRepository).getMaterializationAgents();
|
|
119
|
+
const resolved = yield* Effect.forEach(agents, (agent) => agent
|
|
120
|
+
.resolveEffectiveSkillsDir({ workspaceRoot: ws.baseDir })
|
|
121
|
+
.pipe(Effect.map((outcome) => ({ agentId: agent.id, outcome }))), { concurrency: "unbounded" });
|
|
122
|
+
const installable = [];
|
|
123
|
+
for (const { agentId, outcome } of resolved) {
|
|
124
|
+
if (outcome._tag === "supported") {
|
|
125
|
+
installable.push({ agentId, targetDir: path.normalize(outcome.dir) });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { installable, locations: yield* groupInstallTargetsByDirectory(installable, ws.baseDir) };
|
|
129
|
+
});
|
|
130
|
+
/**
|
|
131
|
+
* Where a newly declared MCP connection becomes observable: the MCP config
|
|
132
|
+
* every configured agent reads for the selected scope. Preview and apply read
|
|
133
|
+
* the same forecast, so a preview lists exactly the files an apply writes.
|
|
134
|
+
*/
|
|
135
|
+
const mcpAgentConfigTargets = Effect.fn("CreateExtension.mcpAgentConfigTargets")(function* () {
|
|
136
|
+
const ws = yield* WorkspaceMutations;
|
|
137
|
+
const path = yield* Path.Path;
|
|
138
|
+
const configuredAgentIds = yield* ws.getConfiguredAgents();
|
|
139
|
+
const catalogAgents = Object.values(CONFIGURABLE_AGENTS_BY_ID);
|
|
140
|
+
const agentsByConfigPath = new Map();
|
|
141
|
+
for (const agentId of configuredAgentIds) {
|
|
142
|
+
const agent = catalogAgents.find((candidate) => candidate.id === agentId);
|
|
143
|
+
const capability = agent?.capabilities["mcp-server"];
|
|
144
|
+
if (capability === undefined || capability.axm.writer === null)
|
|
145
|
+
continue;
|
|
146
|
+
for (const target of capability.axm.writer.config.targets) {
|
|
147
|
+
if (target.scope !== ws.scope)
|
|
148
|
+
continue;
|
|
149
|
+
const configPath = path.relative(ws.baseDir, path.resolve(ws.baseDir, target.path));
|
|
150
|
+
const agentIds = agentsByConfigPath.get(configPath) ?? new Set();
|
|
151
|
+
agentIds.add(agentId);
|
|
152
|
+
agentsByConfigPath.set(configPath, agentIds);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return Array.from(agentsByConfigPath.entries())
|
|
156
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
157
|
+
.map(([configPath, agentIds]) => ({
|
|
158
|
+
path: configPath,
|
|
159
|
+
change: "created",
|
|
160
|
+
agentIds: Array.from(agentIds).sort(),
|
|
161
|
+
}));
|
|
162
|
+
});
|
|
163
|
+
/**
|
|
164
|
+
* Build the authored-creation step for a type whose canonical content is
|
|
165
|
+
* realized by an installer rather than by its own manager.
|
|
166
|
+
*/
|
|
167
|
+
const authoredPackageStep = (manager, args) => buildAuthoredExtensionStep(manager, args);
|
|
168
|
+
/**
|
|
169
|
+
* Build the authored-creation step with the requirements this use case keeps
|
|
170
|
+
* in `R`. The recipe pins one requirement set across the manager and the
|
|
171
|
+
* closures, so naming it here is what lets the workspace facade and the agent
|
|
172
|
+
* repository stay requirements instead of captured values.
|
|
173
|
+
*/
|
|
174
|
+
const authoredStep = (manager, args) => buildNewExtensionStep(manager, args);
|
|
175
|
+
// -----------------------------------------------------------------------------
|
|
176
|
+
// prepare
|
|
177
|
+
// -----------------------------------------------------------------------------
|
|
178
|
+
/**
|
|
179
|
+
* Settle a creation without writing anything.
|
|
180
|
+
*
|
|
181
|
+
* The create-only refusal runs here so a preview refuses an occupied name,
|
|
182
|
+
* and again inside the transaction so a candidate that went stale between
|
|
183
|
+
* preview and apply writes nothing.
|
|
184
|
+
*/
|
|
185
|
+
export const prepareCreateExtension = Effect.fn("CreateExtension.prepare")(function* (request) {
|
|
186
|
+
const ws = yield* WorkspaceMutations;
|
|
187
|
+
const path = yield* Path.Path;
|
|
188
|
+
const managers = yield* ExtensionManagers;
|
|
189
|
+
const { subject, command } = route(request.type);
|
|
190
|
+
const { owner, establish } = yield* resolveAuthoringOwner({ subject, command, name: request.name }, request.owner);
|
|
191
|
+
if (!isValidScaffoldName(request.name)) {
|
|
192
|
+
return yield* new ScaffoldNameInvalid({
|
|
193
|
+
subject,
|
|
194
|
+
name: request.name,
|
|
195
|
+
pattern: SCAFFOLD_NAME_PATTERN.source,
|
|
196
|
+
maxLength: SCAFFOLD_NAME_MAX_LENGTH,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
// Authored packages live in a project workspace; the user scope holds
|
|
200
|
+
// acquired content only.
|
|
201
|
+
if (ws.layout.scope !== "project") {
|
|
202
|
+
return yield* new AuthoringScopeUnsupported({ subject, scope: ws.layout.scope });
|
|
203
|
+
}
|
|
204
|
+
const name = request.name;
|
|
205
|
+
const scaffold = scaffoldFor(request, owner);
|
|
206
|
+
const location = canonicalLocation(path, ws, request.type, owner, name);
|
|
207
|
+
const authoredPath = path.relative(ws.baseDir, location);
|
|
208
|
+
const settingsPath = settingsRelativePath(path, ws);
|
|
209
|
+
const extensionName = decodeExtensionNameSync(name);
|
|
210
|
+
const fqn = formatFqn({ owner, type: request.type, name: extensionName });
|
|
211
|
+
const { isConfigured, declare } = declaration(ws, request.type, name);
|
|
212
|
+
const createOnly = Effect.gen(function* () {
|
|
213
|
+
yield* preflightCreateOnly({
|
|
214
|
+
subject: scaffold.subject,
|
|
215
|
+
name,
|
|
216
|
+
configured: yield* isConfigured,
|
|
217
|
+
destinations: [location],
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
yield* createOnly;
|
|
221
|
+
const contentTargets = scaffold.contentFiles.map((file) => ({
|
|
222
|
+
path: path.join(authoredPath, ...file.split("/")),
|
|
223
|
+
change: "created",
|
|
224
|
+
}));
|
|
225
|
+
const settingsTarget = { path: settingsPath, change: "created" };
|
|
226
|
+
const projected = request.type === "skill" ? yield* skillTargetLocations() : { installable: [], locations: [] };
|
|
227
|
+
const projectionTargets = projected.locations.map((target) => {
|
|
228
|
+
const agentIds = artifactTargetAgentIds(target.agentIds);
|
|
229
|
+
return {
|
|
230
|
+
path: path.relative(ws.baseDir, path.join(target.targetDir, name)),
|
|
231
|
+
change: "created",
|
|
232
|
+
...(agentIds.length > 0 ? { agentIds } : {}),
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
const agentConfigTargets = request.type === "mcp-server" ? yield* mcpAgentConfigTargets() : [];
|
|
236
|
+
const plannedArtifact = {
|
|
237
|
+
path: authoredPath,
|
|
238
|
+
scope: ws.scope,
|
|
239
|
+
version: scaffold.version,
|
|
240
|
+
change: "created",
|
|
241
|
+
fileCount: scaffold.contentFiles.length,
|
|
242
|
+
targets: [...contentTargets, settingsTarget, ...projectionTargets, ...agentConfigTargets],
|
|
243
|
+
};
|
|
244
|
+
const common = {
|
|
245
|
+
toStepFailure: authoringStepFailure,
|
|
246
|
+
versionRange: Option.none(),
|
|
247
|
+
label: fqn,
|
|
248
|
+
message: `Created ${subject} ${fqn}`,
|
|
249
|
+
plannedArtifact,
|
|
250
|
+
preflight: Effect.gen(function* () {
|
|
251
|
+
yield* recoverCanonicalDirectory({ baseDir: ws.baseDir, canonicalPath: location });
|
|
252
|
+
yield* createOnly;
|
|
253
|
+
}),
|
|
254
|
+
scaffold: createCanonicalDirectory({
|
|
255
|
+
baseDir: ws.baseDir,
|
|
256
|
+
canonicalPath: location,
|
|
257
|
+
subject: scaffold.subject,
|
|
258
|
+
requiredFiles: scaffold.contentFiles,
|
|
259
|
+
populate: scaffold.populate,
|
|
260
|
+
}),
|
|
261
|
+
markAuthored: establish.pipe(Effect.andThen(declare)),
|
|
262
|
+
};
|
|
263
|
+
const sourceRef = {
|
|
264
|
+
refType: "workspace",
|
|
265
|
+
source: { type: "workspace", owner, extensionType: request.type, name: extensionName },
|
|
266
|
+
scope: ws.scope,
|
|
267
|
+
owner,
|
|
268
|
+
name: extensionName,
|
|
269
|
+
version: scaffold.version,
|
|
270
|
+
sourceHash: computeSourceHash("scaffold"),
|
|
271
|
+
location,
|
|
272
|
+
};
|
|
273
|
+
const step = (() => {
|
|
274
|
+
switch (request.type) {
|
|
275
|
+
case "skill":
|
|
276
|
+
return authoredStep(managers.skill, {
|
|
277
|
+
...common,
|
|
278
|
+
ref: {
|
|
279
|
+
...sourceRef,
|
|
280
|
+
type: "skill",
|
|
281
|
+
source: { ...sourceRef.source, extensionType: "skill" },
|
|
282
|
+
skill: { name: extensionName, description: Option.none(), metadata: Option.none() },
|
|
283
|
+
},
|
|
284
|
+
target: { type: "skill", name },
|
|
285
|
+
buildArtifact: ({ installedBefore }) => Effect.gen(function* () {
|
|
286
|
+
const change = installedBefore ? "updated" : "created";
|
|
287
|
+
const { installable, locations } = yield* skillTargetLocations();
|
|
288
|
+
const agents = artifactAgentIdsFromTargets(installable);
|
|
289
|
+
return {
|
|
290
|
+
path: authoredPath,
|
|
291
|
+
scope: ws.scope,
|
|
292
|
+
...(agents.length > 0 ? { agents } : {}),
|
|
293
|
+
version: scaffold.version,
|
|
294
|
+
change,
|
|
295
|
+
targets: [
|
|
296
|
+
{ path: authoredPath, change },
|
|
297
|
+
{ path: settingsPath, change },
|
|
298
|
+
...locations.map((target) => {
|
|
299
|
+
const agentIds = artifactTargetAgentIds(target.agentIds);
|
|
300
|
+
return {
|
|
301
|
+
path: path.relative(ws.baseDir, path.join(target.targetDir, name)),
|
|
302
|
+
change,
|
|
303
|
+
...(agentIds.length > 0 ? { agentIds } : {}),
|
|
304
|
+
};
|
|
305
|
+
}),
|
|
306
|
+
],
|
|
307
|
+
};
|
|
308
|
+
}),
|
|
309
|
+
});
|
|
310
|
+
case "subagent":
|
|
311
|
+
return authoredStep(managers.subagent, {
|
|
312
|
+
...common,
|
|
313
|
+
ref: {
|
|
314
|
+
...sourceRef,
|
|
315
|
+
type: "subagent",
|
|
316
|
+
source: { ...sourceRef.source, extensionType: "subagent" },
|
|
317
|
+
subagent: { name: extensionName, description: Option.none() },
|
|
318
|
+
},
|
|
319
|
+
target: { type: "subagent", name },
|
|
320
|
+
buildArtifact: () => Effect.succeed(plannedArtifact),
|
|
321
|
+
});
|
|
322
|
+
case "rule":
|
|
323
|
+
return authoredStep(managers.rule, {
|
|
324
|
+
...common,
|
|
325
|
+
ref: {
|
|
326
|
+
...sourceRef,
|
|
327
|
+
type: "rule",
|
|
328
|
+
source: { ...sourceRef.source, extensionType: "rule" },
|
|
329
|
+
rule: { name: extensionName },
|
|
330
|
+
},
|
|
331
|
+
target: { type: "rule", name },
|
|
332
|
+
buildArtifact: () => Effect.succeed(plannedArtifact),
|
|
333
|
+
});
|
|
334
|
+
case "hook":
|
|
335
|
+
return authoredStep(managers.hook, {
|
|
336
|
+
...common,
|
|
337
|
+
ref: {
|
|
338
|
+
...sourceRef,
|
|
339
|
+
type: "hook",
|
|
340
|
+
source: { ...sourceRef.source, extensionType: "hook" },
|
|
341
|
+
hook: { name: extensionName },
|
|
342
|
+
},
|
|
343
|
+
target: { type: "hook", name },
|
|
344
|
+
buildArtifact: () => Effect.gen(function* () {
|
|
345
|
+
// A hook becomes observable through the shared agent hook
|
|
346
|
+
// configurations, so its realized targets are the aggregate
|
|
347
|
+
// projection's, not the package's own files.
|
|
348
|
+
const observation = yield* managers.hook.aggregateProjectionObservation;
|
|
349
|
+
const targets = observation.targets.map((target) => ({
|
|
350
|
+
...target,
|
|
351
|
+
change: "created",
|
|
352
|
+
}));
|
|
353
|
+
return {
|
|
354
|
+
path: authoredPath,
|
|
355
|
+
scope: ws.scope,
|
|
356
|
+
version: scaffold.version,
|
|
357
|
+
change: "created",
|
|
358
|
+
...(targets.length === 0 ? {} : { fileCount: targets.length, targets }),
|
|
359
|
+
};
|
|
360
|
+
}),
|
|
361
|
+
});
|
|
362
|
+
case "knowledge":
|
|
363
|
+
return authoredStep(managers.knowledge, {
|
|
364
|
+
...common,
|
|
365
|
+
ref: {
|
|
366
|
+
...sourceRef,
|
|
367
|
+
type: "knowledge",
|
|
368
|
+
source: { ...sourceRef.source, extensionType: "knowledge" },
|
|
369
|
+
knowledge: { name: extensionName },
|
|
370
|
+
},
|
|
371
|
+
target: { type: "knowledge", name },
|
|
372
|
+
buildArtifact: () => Effect.succeed(plannedArtifact),
|
|
373
|
+
});
|
|
374
|
+
case "pack":
|
|
375
|
+
return authoredStep(managers.pack, {
|
|
376
|
+
...common,
|
|
377
|
+
ref: {
|
|
378
|
+
...sourceRef,
|
|
379
|
+
type: "pack",
|
|
380
|
+
source: { ...sourceRef.source, extensionType: "pack" },
|
|
381
|
+
pack: { name: extensionName, dependencies: {} },
|
|
382
|
+
},
|
|
383
|
+
target: { type: "pack", owner, name },
|
|
384
|
+
buildArtifact: () => Effect.succeed(plannedArtifact),
|
|
385
|
+
});
|
|
386
|
+
case "mcp-server": {
|
|
387
|
+
// An MCP server's canonical content is realized by the install
|
|
388
|
+
// operation rather than by its manager, so the authored closure names
|
|
389
|
+
// that realization instead of the manager's default.
|
|
390
|
+
const nonInteractive = request.nonInteractive;
|
|
391
|
+
return authoredPackageStep(managers["mcp-server"], {
|
|
392
|
+
...common,
|
|
393
|
+
target: { type: "mcp-server", name },
|
|
394
|
+
location,
|
|
395
|
+
enabled: true,
|
|
396
|
+
materializeInstall: (ref) => materializeAuthoredMcpServer({ ref, nonInteractive }),
|
|
397
|
+
buildArtifact: () => Effect.succeed(plannedArtifact),
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
})();
|
|
402
|
+
// A description is the one piece of package content a creation flag
|
|
403
|
+
// controls, so the operation says what it will record rather than leaving
|
|
404
|
+
// the author to read it back out of the written manifest.
|
|
405
|
+
const authoredSummary = request.type === "knowledge" || request.type === "mcp-server"
|
|
406
|
+
? request.description
|
|
407
|
+
: Option.none();
|
|
408
|
+
const plan = {
|
|
409
|
+
_tag: "Plan",
|
|
410
|
+
name: createExtensionPlanName(request.type),
|
|
411
|
+
description: Option.some(Option.match(authoredSummary, {
|
|
412
|
+
onNone: () => `Create ${fqn}`,
|
|
413
|
+
onSome: (summary) => `Create ${fqn}: ${summary}`,
|
|
414
|
+
})),
|
|
415
|
+
presentation: operationPresentation({ imperative: "create", past: "Created", gerund: "Creating" }, request.type),
|
|
416
|
+
jobs: [{ concurrency: 1, steps: [step] }],
|
|
417
|
+
};
|
|
418
|
+
const execution = yield* prepareExecutionCandidate(plan);
|
|
419
|
+
return {
|
|
420
|
+
type: request.type,
|
|
421
|
+
fqn,
|
|
422
|
+
owner,
|
|
423
|
+
name,
|
|
424
|
+
authoredPath,
|
|
425
|
+
entryPath: path.join(authoredPath, ...scaffold.entryFile.split("/")),
|
|
426
|
+
settingsPath,
|
|
427
|
+
execution,
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
// -----------------------------------------------------------------------------
|
|
431
|
+
// previewOrApply
|
|
432
|
+
// -----------------------------------------------------------------------------
|
|
433
|
+
/** Preview or apply a settled creation, resolving to one operation outcome. */
|
|
434
|
+
export const previewOrApplyCreateExtension = (candidate, execution) => resolveExecutionCandidate(candidate.execution, execution);
|
|
435
|
+
/** The creation use case: settle a request, then preview or apply it. */
|
|
436
|
+
export const CreateExtension = {
|
|
437
|
+
prepare: prepareCreateExtension,
|
|
438
|
+
previewOrApply: previewOrApplyCreateExtension,
|
|
439
|
+
};
|
|
440
|
+
//# sourceMappingURL=create-extension.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed refusals the authoring request phase can settle with.
|
|
3
|
+
*
|
|
4
|
+
* Each one carries the facts a person needs to recover — the owners they
|
|
5
|
+
* could have meant, the settings file that records ownership, the name rule
|
|
6
|
+
* their name broke — and none of them carries rendered prose. The
|
|
7
|
+
* application boundary owns the sentence; the feature owns the decision.
|
|
8
|
+
*
|
|
9
|
+
* @experimental This API is unstable and may change without notice.
|
|
10
|
+
*/
|
|
11
|
+
import * as Schema from "effect/Schema";
|
|
12
|
+
declare const AuthoringOwnerRequired_base: Schema.Class<AuthoringOwnerRequired, Schema.TaggedStruct<"AuthoringOwnerRequired", {
|
|
13
|
+
/** The thing being authored, e.g. `skill`. */
|
|
14
|
+
readonly subject: Schema.String;
|
|
15
|
+
/** The command route, so a recovery action is a runnable command. */
|
|
16
|
+
readonly command: Schema.String;
|
|
17
|
+
/** The name the person asked to create. */
|
|
18
|
+
readonly name: Schema.String;
|
|
19
|
+
/** Owners the person could have meant, most local first. */
|
|
20
|
+
readonly candidates: Schema.$Array<Schema.String>;
|
|
21
|
+
/** Workspace-relative settings file that would record the owner. */
|
|
22
|
+
readonly settingsPath: Schema.String;
|
|
23
|
+
}>, import("effect/Cause").YieldableError>;
|
|
24
|
+
/**
|
|
25
|
+
* No owner is configured for the selected workspace and none was named, so
|
|
26
|
+
* the package has no authorship to carry. `candidates` are the owners the
|
|
27
|
+
* person could plausibly have meant, most local first; it may be empty.
|
|
28
|
+
*/
|
|
29
|
+
export declare class AuthoringOwnerRequired extends AuthoringOwnerRequired_base {
|
|
30
|
+
}
|
|
31
|
+
declare const AuthoringOwnerMismatch_base: Schema.Class<AuthoringOwnerMismatch, Schema.TaggedStruct<"AuthoringOwnerMismatch", {
|
|
32
|
+
readonly requested: Schema.String;
|
|
33
|
+
readonly configured: Schema.String;
|
|
34
|
+
}>, import("effect/Cause").YieldableError>;
|
|
35
|
+
/** An explicitly named owner disagrees with the one the workspace records. */
|
|
36
|
+
export declare class AuthoringOwnerMismatch extends AuthoringOwnerMismatch_base {
|
|
37
|
+
}
|
|
38
|
+
declare const ScaffoldNameInvalid_base: Schema.Class<ScaffoldNameInvalid, Schema.TaggedStruct<"ScaffoldNameInvalid", {
|
|
39
|
+
readonly subject: Schema.String;
|
|
40
|
+
readonly name: Schema.String;
|
|
41
|
+
/** The pattern source every authored name matches. */
|
|
42
|
+
readonly pattern: Schema.String;
|
|
43
|
+
readonly maxLength: Schema.Number;
|
|
44
|
+
}>, import("effect/Cause").YieldableError>;
|
|
45
|
+
/** The requested name is not a name an authored package can carry. */
|
|
46
|
+
export declare class ScaffoldNameInvalid extends ScaffoldNameInvalid_base {
|
|
47
|
+
}
|
|
48
|
+
declare const AuthoringScopeUnsupported_base: Schema.Class<AuthoringScopeUnsupported, Schema.TaggedStruct<"AuthoringScopeUnsupported", {
|
|
49
|
+
readonly subject: Schema.String;
|
|
50
|
+
readonly scope: Schema.String;
|
|
51
|
+
}>, import("effect/Cause").YieldableError>;
|
|
52
|
+
/**
|
|
53
|
+
* Authoring was requested in a workspace scope that does not hold authored
|
|
54
|
+
* packages. Only the project workspace does.
|
|
55
|
+
*/
|
|
56
|
+
export declare class AuthoringScopeUnsupported extends AuthoringScopeUnsupported_base {
|
|
57
|
+
}
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=errors.d.ts.map
|