@michaelfromyeg/loom-adapter-kit 0.1.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/LICENSE +21 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael DeMarco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Owner, Target, Plugin, Marketplace, Scope, Component } from '@michaelfromyeg/loom-schema';
|
|
2
|
+
export { homedir } from 'node:os';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Classifies an artifact for the trust summary and lockfile grouping. This is
|
|
6
|
+
* metadata only -- it never decides placement (relPath does that).
|
|
7
|
+
*/
|
|
8
|
+
type ArtifactKind = "skill" | "mcp" | "agent" | "command" | "hook" | "manifest" | "catalog" | "other";
|
|
9
|
+
/**
|
|
10
|
+
* One file an adapter emits. `relPath` is relative to the native plugin/output
|
|
11
|
+
* root the adapter targets (e.g. "skills/greet/SKILL.md",
|
|
12
|
+
* ".claude-plugin/plugin.json"). Core decides the absolute base per build mode
|
|
13
|
+
* and scope; the adapter never hard-codes absolute paths.
|
|
14
|
+
*/
|
|
15
|
+
interface CompiledArtifact {
|
|
16
|
+
relPath: string;
|
|
17
|
+
contents: string | Buffer;
|
|
18
|
+
kind?: ArtifactKind;
|
|
19
|
+
/** Passthrough executables. Placed DISABLED; activation is a separate opt-in (spec §11). */
|
|
20
|
+
executable?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Convenience builder so adapters read declaratively. */
|
|
23
|
+
declare function artifact(relPath: string, contents: string | Buffer, opts?: {
|
|
24
|
+
kind?: ArtifactKind;
|
|
25
|
+
executable?: boolean;
|
|
26
|
+
}): CompiledArtifact;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* One plugin's resolved metadata for a catalog entry. Core resolves each
|
|
30
|
+
* marketplace entry (loads the referenced plugin to learn its name/description)
|
|
31
|
+
* so the adapter just formats -- it never re-resolves sources itself.
|
|
32
|
+
*/
|
|
33
|
+
interface CatalogEntry {
|
|
34
|
+
name: string;
|
|
35
|
+
/** Where the catalog points for this plugin (e.g. "./plugins/<name>"). */
|
|
36
|
+
source: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
version?: string;
|
|
39
|
+
category?: string;
|
|
40
|
+
tags?: string[];
|
|
41
|
+
}
|
|
42
|
+
/** A marketplace with every entry already resolved to concrete metadata. */
|
|
43
|
+
interface ResolvedMarketplace {
|
|
44
|
+
name: string;
|
|
45
|
+
owner: Owner;
|
|
46
|
+
description?: string;
|
|
47
|
+
entries: CatalogEntry[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface ToolCall {
|
|
51
|
+
name: string;
|
|
52
|
+
args: unknown;
|
|
53
|
+
result?: unknown;
|
|
54
|
+
ts: number;
|
|
55
|
+
}
|
|
56
|
+
/** Normalized result of one headless harness run. */
|
|
57
|
+
interface Transcript {
|
|
58
|
+
finalText: string;
|
|
59
|
+
toolCalls: ToolCall[];
|
|
60
|
+
exitCode: number;
|
|
61
|
+
/** Raw stdout/json for debugging + baselines. */
|
|
62
|
+
raw: string;
|
|
63
|
+
/**
|
|
64
|
+
* True when this harness exposes no structured tool-call trace, so `toolCalls`
|
|
65
|
+
* is empty by limitation rather than because none happened. `trace` assertions
|
|
66
|
+
* degrade to `output` assertions in this case (spec §14).
|
|
67
|
+
*/
|
|
68
|
+
traceUnavailable?: boolean;
|
|
69
|
+
}
|
|
70
|
+
interface RunOptions {
|
|
71
|
+
prompt: string;
|
|
72
|
+
cwd: string;
|
|
73
|
+
config?: Record<string, string>;
|
|
74
|
+
timeoutMs?: number;
|
|
75
|
+
}
|
|
76
|
+
interface HarnessDriver {
|
|
77
|
+
readonly target: Target;
|
|
78
|
+
/** CLI installed AND headless-capable on this machine. Never throws. */
|
|
79
|
+
available(): Promise<boolean>;
|
|
80
|
+
run(opts: RunOptions): Promise<Transcript>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface ImportOptions {
|
|
84
|
+
/** Reverse-DNS namespace to assign (native plugins have no Loom namespace). */
|
|
85
|
+
namespace?: string;
|
|
86
|
+
}
|
|
87
|
+
/** An existing native plugin reverse-compiled into a Loom plugin + its files. */
|
|
88
|
+
interface ImportedPlugin {
|
|
89
|
+
kind: "plugin";
|
|
90
|
+
plugin: Plugin;
|
|
91
|
+
/** Component files to write under the output root (relPath relative to it). */
|
|
92
|
+
files: CompiledArtifact[];
|
|
93
|
+
}
|
|
94
|
+
/** An existing native marketplace reverse-compiled into a Loom marketplace. */
|
|
95
|
+
interface ImportedMarketplace {
|
|
96
|
+
kind: "marketplace";
|
|
97
|
+
marketplace: Marketplace;
|
|
98
|
+
}
|
|
99
|
+
type ImportResult = ImportedPlugin | ImportedMarketplace;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolved install directories for one harness at one scope. Category dirs serve
|
|
103
|
+
* adapters that scatter components into convention dirs; `plugins` serves adapters
|
|
104
|
+
* that drop a whole compiled plugin tree. `root` is the scope base.
|
|
105
|
+
*/
|
|
106
|
+
interface InstallPaths {
|
|
107
|
+
root: string;
|
|
108
|
+
plugins: string;
|
|
109
|
+
skills: string;
|
|
110
|
+
mcp: string;
|
|
111
|
+
agents: string;
|
|
112
|
+
commands: string;
|
|
113
|
+
hooks: string;
|
|
114
|
+
/** Where the per-harness marketplace catalog goes. */
|
|
115
|
+
catalog: string;
|
|
116
|
+
}
|
|
117
|
+
/** Expand a leading `~` to the user's home directory. */
|
|
118
|
+
declare function expandTilde(p: string): string;
|
|
119
|
+
/** Resolve a path that may be tilde-prefixed or relative to a base. */
|
|
120
|
+
declare function resolveUnder(base: string, p: string): string;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Read-only view of the fetched plugin passed to every adapter call. `read`
|
|
124
|
+
* returns file bytes from the plugin so adapters can store standards verbatim;
|
|
125
|
+
* `aliasFor` resolves a component's bare invocation name (spec §9.4).
|
|
126
|
+
*/
|
|
127
|
+
interface PluginCtx {
|
|
128
|
+
plugin: Plugin;
|
|
129
|
+
read(relPath: string): Buffer;
|
|
130
|
+
/** Recursively list files under a plugin directory, as paths relative to the plugin root. */
|
|
131
|
+
list(relDir: string): string[];
|
|
132
|
+
aliasFor(componentId: string): string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The seam between Loom's canonical model and one harness's native format
|
|
136
|
+
* (spec §7). Every harness-specific fact lives behind `targetSchema`, so an
|
|
137
|
+
* upstream schema change is a version bump here, not a change to any plugin.
|
|
138
|
+
*
|
|
139
|
+
* A community adapter implements this interface and is registered by its consumer
|
|
140
|
+
* (the CLI or an embedding app) -- it depends only on @michaelfromyeg/loom-adapter-kit + @michaelfromyeg/loom-schema.
|
|
141
|
+
*/
|
|
142
|
+
interface HarnessAdapter {
|
|
143
|
+
readonly target: Target;
|
|
144
|
+
/** This adapter package's own version (versioning axis 3, spec §5). */
|
|
145
|
+
readonly version: string;
|
|
146
|
+
/** The harness manifest schema version this adapter emits against. */
|
|
147
|
+
readonly targetSchema: string;
|
|
148
|
+
/** Resolve install directories for a scope on this machine. */
|
|
149
|
+
detect(scope: Scope, cwd: string): InstallPaths;
|
|
150
|
+
/** Compile one canonical component to its native artifacts. */
|
|
151
|
+
transform(component: Component, ctx: PluginCtx): CompiledArtifact[];
|
|
152
|
+
/**
|
|
153
|
+
* Emit the plugin-level native manifest(s) (e.g. Claude `plugin.json`).
|
|
154
|
+
* Returns `[]` for directory-convention harnesses that need none.
|
|
155
|
+
*/
|
|
156
|
+
emitManifest(plugin: Plugin, ctx: PluginCtx): CompiledArtifact[];
|
|
157
|
+
/**
|
|
158
|
+
* Emit the harness's native marketplace catalog from a fully-resolved
|
|
159
|
+
* marketplace. Used for both a curated `marketplace.yaml` (many plugins) and
|
|
160
|
+
* the single-plugin build (a synthetic one-entry marketplace).
|
|
161
|
+
*/
|
|
162
|
+
emitCatalog(marketplace: ResolvedMarketplace): CompiledArtifact[];
|
|
163
|
+
/** Present iff headless eval is supported on this harness. */
|
|
164
|
+
driver?: HarnessDriver;
|
|
165
|
+
/**
|
|
166
|
+
* Reverse-compile an existing native plugin or marketplace in `dir` into the
|
|
167
|
+
* canonical Loom model, so it can be cross-compiled to the other harnesses
|
|
168
|
+
* ("federate, don't wall off" applied to assets you already maintain). Returns
|
|
169
|
+
* null when `dir` is not this harness's format. Present iff the harness supports it.
|
|
170
|
+
*/
|
|
171
|
+
importNative?(dir: string, opts?: ImportOptions): ImportResult | null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
interface Frontmatter {
|
|
175
|
+
data: Record<string, unknown>;
|
|
176
|
+
body: string;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Split a Markdown document (SKILL.md, agent .md, rules .mdc) into its YAML
|
|
180
|
+
* frontmatter and body. Returns empty data when there is no frontmatter block.
|
|
181
|
+
*/
|
|
182
|
+
declare function parseFrontmatter(md: string): Frontmatter;
|
|
183
|
+
/** Re-emit a Markdown document with the given frontmatter data and body. */
|
|
184
|
+
declare function withFrontmatter(data: Record<string, unknown>, body: string): string;
|
|
185
|
+
|
|
186
|
+
export { type ArtifactKind, type CatalogEntry, type CompiledArtifact, type Frontmatter, type HarnessAdapter, type HarnessDriver, type ImportOptions, type ImportResult, type ImportedMarketplace, type ImportedPlugin, type InstallPaths, type PluginCtx, type ResolvedMarketplace, type RunOptions, type ToolCall, type Transcript, artifact, expandTilde, parseFrontmatter, resolveUnder, withFrontmatter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as YAML from 'yaml';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
export { homedir } from 'os';
|
|
4
|
+
import { join, isAbsolute } from 'path';
|
|
5
|
+
|
|
6
|
+
// src/artifact.ts
|
|
7
|
+
function artifact(relPath, contents, opts = {}) {
|
|
8
|
+
return { relPath, contents, ...opts };
|
|
9
|
+
}
|
|
10
|
+
var FM_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
11
|
+
function parseFrontmatter(md) {
|
|
12
|
+
const m = FM_RE.exec(md);
|
|
13
|
+
if (!m) return { data: {}, body: md };
|
|
14
|
+
const data = YAML.parse(m[1], { version: "1.2", schema: "core" }) ?? {};
|
|
15
|
+
return { data, body: md.slice(m[0].length) };
|
|
16
|
+
}
|
|
17
|
+
function withFrontmatter(data, body) {
|
|
18
|
+
const yaml = YAML.stringify(data, { version: "1.2" }).trimEnd();
|
|
19
|
+
const cleanBody = body.startsWith("\n") ? body.slice(1) : body;
|
|
20
|
+
return `---
|
|
21
|
+
${yaml}
|
|
22
|
+
---
|
|
23
|
+
${cleanBody}`;
|
|
24
|
+
}
|
|
25
|
+
function expandTilde(p) {
|
|
26
|
+
if (p === "~") return homedir();
|
|
27
|
+
if (p.startsWith("~/")) return join(homedir(), p.slice(2));
|
|
28
|
+
return p;
|
|
29
|
+
}
|
|
30
|
+
function resolveUnder(base, p) {
|
|
31
|
+
const expanded = expandTilde(p);
|
|
32
|
+
return isAbsolute(expanded) ? expanded : join(base, expanded);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { artifact, expandTilde, parseFrontmatter, resolveUnder, withFrontmatter };
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/artifact.ts","../src/frontmatter.ts","../src/paths.ts"],"names":[],"mappings":";;;;;;AA6BO,SAAS,QAAA,CACd,OAAA,EACA,QAAA,EACA,IAAA,GAAsD,EAAC,EACrC;AAClB,EAAA,OAAO,EAAE,OAAA,EAAS,QAAA,EAAU,GAAG,IAAA,EAAK;AACtC;AC5BA,IAAM,KAAA,GAAQ,mCAAA;AAMP,SAAS,iBAAiB,EAAA,EAAyB;AACxD,EAAA,MAAM,CAAA,GAAI,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA;AACvB,EAAA,IAAI,CAAC,GAAG,OAAO,EAAE,MAAM,EAAC,EAAG,MAAM,EAAA,EAAG;AACpC,EAAA,MAAM,IAAA,GAAa,IAAA,CAAA,KAAA,CAAM,CAAA,CAAE,CAAC,CAAA,EAAG,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,CAAA,IAAK,EAAC;AAIvE,EAAA,OAAO,EAAE,MAAM,IAAA,EAAM,EAAA,CAAG,MAAM,CAAA,CAAE,CAAC,CAAA,CAAE,MAAM,CAAA,EAAE;AAC7C;AAGO,SAAS,eAAA,CAAgB,MAA+B,IAAA,EAAsB;AACnF,EAAA,MAAM,IAAA,GAAY,eAAU,IAAA,EAAM,EAAE,SAAS,KAAA,EAAO,EAAE,OAAA,EAAQ;AAC9D,EAAA,MAAM,SAAA,GAAY,KAAK,UAAA,CAAW,IAAI,IAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA;AAC1D,EAAA,OAAO,CAAA;AAAA,EAAQ,IAAI;AAAA;AAAA,EAAU,SAAS,CAAA,CAAA;AACxC;ACPO,SAAS,YAAY,CAAA,EAAmB;AAC7C,EAAA,IAAI,CAAA,KAAM,GAAA,EAAK,OAAO,OAAA,EAAQ;AAC9B,EAAA,IAAI,CAAA,CAAE,UAAA,CAAW,IAAI,CAAA,EAAG,OAAO,IAAA,CAAK,OAAA,EAAQ,EAAG,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA;AACzD,EAAA,OAAO,CAAA;AACT;AAGO,SAAS,YAAA,CAAa,MAAc,CAAA,EAAmB;AAC5D,EAAA,MAAM,QAAA,GAAW,YAAY,CAAC,CAAA;AAC9B,EAAA,OAAO,WAAW,QAAQ,CAAA,GAAI,QAAA,GAAW,IAAA,CAAK,MAAM,QAAQ,CAAA;AAC9D","file":"index.js","sourcesContent":["/**\n * Classifies an artifact for the trust summary and lockfile grouping. This is\n * metadata only -- it never decides placement (relPath does that).\n */\nexport type ArtifactKind =\n | \"skill\"\n | \"mcp\"\n | \"agent\"\n | \"command\"\n | \"hook\"\n | \"manifest\"\n | \"catalog\"\n | \"other\";\n\n/**\n * One file an adapter emits. `relPath` is relative to the native plugin/output\n * root the adapter targets (e.g. \"skills/greet/SKILL.md\",\n * \".claude-plugin/plugin.json\"). Core decides the absolute base per build mode\n * and scope; the adapter never hard-codes absolute paths.\n */\nexport interface CompiledArtifact {\n relPath: string;\n contents: string | Buffer;\n kind?: ArtifactKind;\n /** Passthrough executables. Placed DISABLED; activation is a separate opt-in (spec §11). */\n executable?: boolean;\n}\n\n/** Convenience builder so adapters read declaratively. */\nexport function artifact(\n relPath: string,\n contents: string | Buffer,\n opts: { kind?: ArtifactKind; executable?: boolean } = {},\n): CompiledArtifact {\n return { relPath, contents, ...opts };\n}\n","import * as YAML from \"yaml\";\n\nexport interface Frontmatter {\n data: Record<string, unknown>;\n body: string;\n}\n\nconst FM_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\n\n/**\n * Split a Markdown document (SKILL.md, agent .md, rules .mdc) into its YAML\n * frontmatter and body. Returns empty data when there is no frontmatter block.\n */\nexport function parseFrontmatter(md: string): Frontmatter {\n const m = FM_RE.exec(md);\n if (!m) return { data: {}, body: md };\n const data = (YAML.parse(m[1], { version: \"1.2\", schema: \"core\" }) ?? {}) as Record<\n string,\n unknown\n >;\n return { data, body: md.slice(m[0].length) };\n}\n\n/** Re-emit a Markdown document with the given frontmatter data and body. */\nexport function withFrontmatter(data: Record<string, unknown>, body: string): string {\n const yaml = YAML.stringify(data, { version: \"1.2\" }).trimEnd();\n const cleanBody = body.startsWith(\"\\n\") ? body.slice(1) : body;\n return `---\\n${yaml}\\n---\\n${cleanBody}`;\n}\n","import { homedir } from \"node:os\";\nimport { isAbsolute, join } from \"node:path\";\n\n/**\n * Resolved install directories for one harness at one scope. Category dirs serve\n * adapters that scatter components into convention dirs; `plugins` serves adapters\n * that drop a whole compiled plugin tree. `root` is the scope base.\n */\nexport interface InstallPaths {\n root: string;\n plugins: string;\n skills: string;\n mcp: string;\n agents: string;\n commands: string;\n hooks: string;\n /** Where the per-harness marketplace catalog goes. */\n catalog: string;\n}\n\n/** Expand a leading `~` to the user's home directory. */\nexport function expandTilde(p: string): string {\n if (p === \"~\") return homedir();\n if (p.startsWith(\"~/\")) return join(homedir(), p.slice(2));\n return p;\n}\n\n/** Resolve a path that may be tilde-prefixed or relative to a base. */\nexport function resolveUnder(base: string, p: string): string {\n const expanded = expandTilde(p);\n return isAbsolute(expanded) ? expanded : join(base, expanded);\n}\n\nexport { homedir };\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaelfromyeg/loom-adapter-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Public HarnessAdapter / HarnessDriver interfaces and helpers. The contract a community adapter implements.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"yaml": "^2.6.1",
|
|
20
|
+
"@michaelfromyeg/loom-schema": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Michael DeMarco",
|
|
24
|
+
"homepage": "https://github.com/michaelfromyeg/loom#readme",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/michaelfromyeg/loom.git",
|
|
28
|
+
"directory": "packages/adapter-kit"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/michaelfromyeg/loom/issues"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"loom",
|
|
35
|
+
"coding-agent",
|
|
36
|
+
"ai-agent",
|
|
37
|
+
"claude-code",
|
|
38
|
+
"codex",
|
|
39
|
+
"cursor",
|
|
40
|
+
"copilot",
|
|
41
|
+
"opencode",
|
|
42
|
+
"mcp",
|
|
43
|
+
"plugin",
|
|
44
|
+
"skill",
|
|
45
|
+
"compiler"
|
|
46
|
+
],
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup"
|
|
52
|
+
}
|
|
53
|
+
}
|