@nseng-ai/harness-artifacts 0.1.2

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.
@@ -0,0 +1,416 @@
1
+ import { join, resolve } from "node:path";
2
+ import process from "node:process";
3
+
4
+ import { formatErrorMessage, isPathInside, optionalEntry } from "@nseng-ai/foundation/primitives";
5
+ import { requireXdgPath, resolveNsXdgPath } from "@nseng-ai/foundation/xdg-path";
6
+ import { z } from "zod";
7
+
8
+ import type { SkillHarnessArtifactEntry } from "./artifact-catalog.ts";
9
+ import { sortDiagnosticsByKey } from "./diagnostic-sort.ts";
10
+ import {
11
+ nodeHarnessArtifactFileSystemGateway,
12
+ type HarnessArtifactFileSystemErrorInfo,
13
+ type HarnessArtifactModuleDiscoveryGateway,
14
+ type ModuleDiscoveryDirectoryEntry,
15
+ type ModuleDiscoveryDirectoryState,
16
+ type ModuleDiscoveryPathState,
17
+ } from "./filesystem.ts";
18
+ import { sortStrings } from "./sort.ts";
19
+ import {
20
+ MODULE_ARTIFACT_DECLARATION_DIAGNOSTIC_CODES,
21
+ parseModuleArtifactDeclaration,
22
+ type ModuleArtifactDeclarationDiagnostic,
23
+ } from "./module-artifact-declaration.ts";
24
+
25
+ export interface DiscoverExtensionModuleHarnessArtifactsRequest {
26
+ projectRoot: string;
27
+ homeDir?: string;
28
+ env?: Record<string, string | undefined>;
29
+ gateway?: HarnessArtifactModuleDiscoveryGateway;
30
+ }
31
+
32
+ export interface ResolvedNpmModuleHarnessArtifactCatalog {
33
+ type: "npm-module-catalog";
34
+ moduleRoot: string;
35
+ packageName: string;
36
+ version: string;
37
+ artifacts: readonly SkillHarnessArtifactEntry[];
38
+ diagnostics: readonly ModuleArtifactDiscoveryDiagnostic[];
39
+ }
40
+
41
+ export interface DiscoverExtensionModuleHarnessArtifactsResult {
42
+ catalogs: readonly ResolvedNpmModuleHarnessArtifactCatalog[];
43
+ diagnostics: readonly ModuleArtifactDiscoveryDiagnostic[];
44
+ }
45
+
46
+ export type {
47
+ HarnessArtifactFileSystemErrorInfo,
48
+ HarnessArtifactModuleDiscoveryGateway,
49
+ ModuleDiscoveryDirectoryEntry,
50
+ ModuleDiscoveryDirectoryState,
51
+ ModuleDiscoveryPathState,
52
+ };
53
+
54
+ export const MODULE_ARTIFACT_DISCOVERY_DIAGNOSTIC_CODES = [
55
+ ...MODULE_ARTIFACT_DECLARATION_DIAGNOSTIC_CODES,
56
+ "module_artifact_extension_root_unavailable",
57
+ "module_artifact_extension_root_not_directory",
58
+ "module_artifact_extension_root_unreadable",
59
+ "module_artifact_skill_path_escapes",
60
+ "module_artifact_skill_entry_missing",
61
+ "module_artifact_skill_entry_not_directory",
62
+ "module_artifact_duplicate_id",
63
+ "module_artifact_duplicate_target_name",
64
+ ] as const;
65
+
66
+ export type ModuleArtifactDiscoveryDiagnosticCode =
67
+ (typeof MODULE_ARTIFACT_DISCOVERY_DIAGNOSTIC_CODES)[number];
68
+
69
+ export interface ModuleArtifactDiscoveryDiagnostic {
70
+ code: ModuleArtifactDiscoveryDiagnosticCode;
71
+ message: string;
72
+ path?: string;
73
+ packageName?: string;
74
+ artifactId?: string;
75
+ artifactName?: string;
76
+ }
77
+
78
+ const moduleArtifactDiscoveryDiagnosticCodeSchema = z.enum(
79
+ MODULE_ARTIFACT_DISCOVERY_DIAGNOSTIC_CODES,
80
+ );
81
+
82
+ export const moduleArtifactDiscoveryDiagnosticSchema: z.ZodType<ModuleArtifactDiscoveryDiagnostic> =
83
+ z
84
+ .object({
85
+ code: moduleArtifactDiscoveryDiagnosticCodeSchema,
86
+ message: z.string(),
87
+ path: z.string().optional(),
88
+ packageName: z.string().optional(),
89
+ artifactId: z.string().optional(),
90
+ artifactName: z.string().optional(),
91
+ })
92
+ .transform((diagnostic) => ({
93
+ code: diagnostic.code,
94
+ message: diagnostic.message,
95
+ ...optionalEntry("path", diagnostic.path),
96
+ ...optionalEntry("packageName", diagnostic.packageName),
97
+ ...optionalEntry("artifactId", diagnostic.artifactId),
98
+ ...optionalEntry("artifactName", diagnostic.artifactName),
99
+ }));
100
+
101
+ export async function discoverExtensionModuleHarnessArtifacts(
102
+ request: DiscoverExtensionModuleHarnessArtifactsRequest,
103
+ ): Promise<DiscoverExtensionModuleHarnessArtifactsResult> {
104
+ const gateway = request.gateway ?? nodeHarnessArtifactFileSystemGateway;
105
+ const rootResolution = extensionArtifactRoots(request);
106
+ const catalogs: ResolvedNpmModuleHarnessArtifactCatalog[] = [];
107
+ const diagnostics: ModuleArtifactDiscoveryDiagnostic[] = [...rootResolution.diagnostics];
108
+ for (const root of rootResolution.roots) {
109
+ const rootResult = await discoverExtensionRoot({ root, gateway });
110
+ catalogs.push(...rootResult.catalogs);
111
+ diagnostics.push(...rootResult.diagnostics);
112
+ }
113
+ const duplicateDiagnostics = duplicateArtifactDiagnostics(catalogs);
114
+ const catalogsWithDuplicateDiagnostics = catalogs.map((catalog) => ({
115
+ ...catalog,
116
+ diagnostics: sortDiscoveryDiagnostics([
117
+ ...catalog.diagnostics,
118
+ ...duplicateDiagnostics.filter((diagnostic) => diagnostic.path === catalog.moduleRoot),
119
+ ]),
120
+ }));
121
+ return {
122
+ catalogs: catalogsWithDuplicateDiagnostics,
123
+ diagnostics: sortDiscoveryDiagnostics([
124
+ ...diagnostics,
125
+ ...catalogsWithDuplicateDiagnostics.flatMap((catalog) => catalog.diagnostics),
126
+ ]),
127
+ };
128
+ }
129
+
130
+ function extensionArtifactRoots(request: DiscoverExtensionModuleHarnessArtifactsRequest): {
131
+ roots: readonly string[];
132
+ diagnostics: readonly ModuleArtifactDiscoveryDiagnostic[];
133
+ } {
134
+ const env = {
135
+ ...process.env,
136
+ ...(request.env ?? {}),
137
+ ...(request.homeDir === undefined ? {} : { HOME: request.homeDir }),
138
+ };
139
+ const diagnostics: ModuleArtifactDiscoveryDiagnostic[] = [];
140
+ const roots = [join(request.projectRoot, ".ns", "extensions")];
141
+ try {
142
+ roots.push(requireXdgPath(resolveNsXdgPath({ kind: "data", env, segments: ["extensions"] })));
143
+ } catch (error) {
144
+ diagnostics.push({
145
+ code: "module_artifact_extension_root_unavailable",
146
+ message: `Could not resolve global extension root for harness artifact discovery: ${formatErrorMessage(error)}`,
147
+ });
148
+ }
149
+ return { roots: sortStrings(roots), diagnostics };
150
+ }
151
+
152
+ async function discoverExtensionRoot(options: {
153
+ root: string;
154
+ gateway: HarnessArtifactModuleDiscoveryGateway;
155
+ }): Promise<DiscoverExtensionModuleHarnessArtifactsResult> {
156
+ const root = await options.gateway.readDirectory(options.root);
157
+ if (!root.ok) {
158
+ return {
159
+ catalogs: [],
160
+ diagnostics: [discoveryFileSystemDiagnostic(root.error)],
161
+ };
162
+ }
163
+ if (root.value.type === "missing") return { catalogs: [], diagnostics: [] };
164
+ if (root.value.type !== "directory") {
165
+ return {
166
+ catalogs: [],
167
+ diagnostics: [
168
+ {
169
+ code: "module_artifact_extension_root_not_directory",
170
+ message: `Extension root must be a directory for harness artifact discovery: ${options.root}.`,
171
+ path: options.root,
172
+ },
173
+ ],
174
+ };
175
+ }
176
+ const catalogs: ResolvedNpmModuleHarnessArtifactCatalog[] = [];
177
+ const diagnostics: ModuleArtifactDiscoveryDiagnostic[] = [];
178
+ for (const entry of [...root.value.entries].sort((left, right) =>
179
+ left.name.localeCompare(right.name),
180
+ )) {
181
+ if (entry.type !== "directory") continue;
182
+ const moduleRoot = join(options.root, entry.name);
183
+ const packageCatalog = await discoverExtensionPackage({ moduleRoot, gateway: options.gateway });
184
+ if (packageCatalog.type === "catalog") catalogs.push(packageCatalog.catalog);
185
+ else diagnostics.push(...packageCatalog.diagnostics);
186
+ }
187
+ return { catalogs, diagnostics: sortDiscoveryDiagnostics(diagnostics) };
188
+ }
189
+
190
+ async function discoverExtensionPackage(options: {
191
+ moduleRoot: string;
192
+ gateway: HarnessArtifactModuleDiscoveryGateway;
193
+ }): Promise<
194
+ | { type: "catalog"; catalog: ResolvedNpmModuleHarnessArtifactCatalog }
195
+ | { type: "diagnostics"; diagnostics: readonly ModuleArtifactDiscoveryDiagnostic[] }
196
+ > {
197
+ const packageJsonPath = join(options.moduleRoot, "package.json");
198
+ const packageText = await options.gateway.readOptionalTextFile(packageJsonPath);
199
+ if (!packageText.ok) {
200
+ return { type: "diagnostics", diagnostics: [discoveryFileSystemDiagnostic(packageText.error)] };
201
+ }
202
+ if (packageText.value.type === "missing") return { type: "diagnostics", diagnostics: [] };
203
+ const parsed = parseModuleArtifactDeclaration(packageText.value.text);
204
+ if (!parsed.ok) {
205
+ return {
206
+ type: "diagnostics",
207
+ diagnostics: parsed.diagnostics.map((diagnostic) =>
208
+ declarationDiagnostic(diagnostic, packageJsonPath),
209
+ ),
210
+ };
211
+ }
212
+ const artifactResults = await validateDiscoveredArtifacts({
213
+ moduleRoot: options.moduleRoot,
214
+ packageName: parsed.packageName,
215
+ artifacts: parsed.artifacts,
216
+ gateway: options.gateway,
217
+ });
218
+ return {
219
+ type: "catalog",
220
+ catalog: {
221
+ type: "npm-module-catalog",
222
+ moduleRoot: options.moduleRoot,
223
+ packageName: parsed.packageName,
224
+ version: parsed.version,
225
+ artifacts: artifactResults.artifacts,
226
+ diagnostics: sortDiscoveryDiagnostics([
227
+ ...parsed.diagnostics.map((diagnostic) =>
228
+ declarationDiagnostic(diagnostic, packageJsonPath),
229
+ ),
230
+ ...artifactResults.diagnostics,
231
+ ]),
232
+ },
233
+ };
234
+ }
235
+
236
+ async function validateDiscoveredArtifacts(options: {
237
+ moduleRoot: string;
238
+ packageName: string;
239
+ artifacts: readonly SkillHarnessArtifactEntry[];
240
+ gateway: HarnessArtifactModuleDiscoveryGateway;
241
+ }): Promise<{
242
+ artifacts: readonly SkillHarnessArtifactEntry[];
243
+ diagnostics: readonly ModuleArtifactDiscoveryDiagnostic[];
244
+ }> {
245
+ const artifacts: SkillHarnessArtifactEntry[] = [];
246
+ const diagnostics: ModuleArtifactDiscoveryDiagnostic[] = [];
247
+ for (const artifact of options.artifacts) {
248
+ const artifactRoot = resolve(options.moduleRoot, artifact.source.relativePath);
249
+ if (!isPathInside(options.moduleRoot, artifactRoot)) {
250
+ diagnostics.push(
251
+ artifactDiagnostic({
252
+ code: "module_artifact_skill_path_escapes",
253
+ message: `Skill harness artifact path must remain inside its package: ${artifact.source.relativePath}.`,
254
+ path: artifact.source.relativePath,
255
+ artifact,
256
+ packageName: options.packageName,
257
+ }),
258
+ );
259
+ continue;
260
+ }
261
+ const state = await options.gateway.pathState(join(artifactRoot, "SKILL.md"));
262
+ if (!state.ok) {
263
+ diagnostics.push(discoveryFileSystemDiagnostic(state.error, artifact));
264
+ continue;
265
+ }
266
+ if (state.value.type === "missing") {
267
+ diagnostics.push(
268
+ artifactDiagnostic({
269
+ code: "module_artifact_skill_entry_missing",
270
+ message: `Declared skill harness artifact must contain SKILL.md: ${artifact.source.relativePath}.`,
271
+ path: join(artifact.source.relativePath, "SKILL.md"),
272
+ artifact,
273
+ packageName: options.packageName,
274
+ }),
275
+ );
276
+ continue;
277
+ }
278
+ if (state.value.type !== "file") {
279
+ diagnostics.push(
280
+ artifactDiagnostic({
281
+ code: "module_artifact_skill_entry_not_directory",
282
+ message: `Declared skill harness artifact SKILL.md path must be a file: ${artifact.source.relativePath}.`,
283
+ path: join(artifact.source.relativePath, "SKILL.md"),
284
+ artifact,
285
+ packageName: options.packageName,
286
+ }),
287
+ );
288
+ continue;
289
+ }
290
+ artifacts.push(artifact);
291
+ }
292
+ return {
293
+ artifacts: artifacts.sort((left, right) => left.id.localeCompare(right.id)),
294
+ diagnostics: sortDiscoveryDiagnostics(diagnostics),
295
+ };
296
+ }
297
+
298
+ interface ArtifactDiagnosticOptions {
299
+ code: Extract<
300
+ ModuleArtifactDiscoveryDiagnosticCode,
301
+ | "module_artifact_skill_path_escapes"
302
+ | "module_artifact_skill_entry_missing"
303
+ | "module_artifact_skill_entry_not_directory"
304
+ >;
305
+ message: string;
306
+ path: string;
307
+ artifact: SkillHarnessArtifactEntry;
308
+ packageName: string;
309
+ }
310
+
311
+ function artifactDiagnostic(options: ArtifactDiagnosticOptions): ModuleArtifactDiscoveryDiagnostic {
312
+ return {
313
+ code: options.code,
314
+ message: options.message,
315
+ path: options.path,
316
+ packageName: options.packageName,
317
+ artifactId: options.artifact.id,
318
+ artifactName: options.artifact.skillName,
319
+ };
320
+ }
321
+
322
+ function duplicateArtifactDiagnostics(
323
+ catalogs: readonly ResolvedNpmModuleHarnessArtifactCatalog[],
324
+ ): readonly ModuleArtifactDiscoveryDiagnostic[] {
325
+ const artifacts = catalogs.flatMap((catalog) =>
326
+ catalog.artifacts.map((artifact) => ({ catalog, artifact })),
327
+ );
328
+ return [
329
+ ...duplicateDiagnosticsForKey(
330
+ artifacts,
331
+ (item) => item.artifact.id,
332
+ "module_artifact_duplicate_id",
333
+ ),
334
+ ...duplicateDiagnosticsForKey(
335
+ artifacts,
336
+ (item) => item.artifact.skillName,
337
+ "module_artifact_duplicate_target_name",
338
+ ),
339
+ ];
340
+ }
341
+
342
+ function duplicateDiagnosticsForKey(
343
+ items: readonly {
344
+ catalog: ResolvedNpmModuleHarnessArtifactCatalog;
345
+ artifact: SkillHarnessArtifactEntry;
346
+ }[],
347
+ keyForItem: (item: {
348
+ catalog: ResolvedNpmModuleHarnessArtifactCatalog;
349
+ artifact: SkillHarnessArtifactEntry;
350
+ }) => string,
351
+ code: Extract<
352
+ ModuleArtifactDiscoveryDiagnosticCode,
353
+ "module_artifact_duplicate_id" | "module_artifact_duplicate_target_name"
354
+ >,
355
+ ): readonly ModuleArtifactDiscoveryDiagnostic[] {
356
+ const counts = new Map<string, number>();
357
+ for (const item of items) counts.set(keyForItem(item), (counts.get(keyForItem(item)) ?? 0) + 1);
358
+ const diagnostics: ModuleArtifactDiscoveryDiagnostic[] = [];
359
+ for (const item of items) {
360
+ const key = keyForItem(item);
361
+ if ((counts.get(key) ?? 0) < 2) continue;
362
+ diagnostics.push({
363
+ code,
364
+ message:
365
+ code === "module_artifact_duplicate_id"
366
+ ? `Duplicate harness artifact id discovered: ${key}.`
367
+ : `Duplicate target skill name discovered: ${key}.`,
368
+ path: item.catalog.moduleRoot,
369
+ packageName: item.catalog.packageName,
370
+ artifactId: item.artifact.id,
371
+ artifactName: item.artifact.skillName,
372
+ });
373
+ }
374
+ return diagnostics;
375
+ }
376
+
377
+ function declarationDiagnostic(
378
+ diagnostic: ModuleArtifactDeclarationDiagnostic,
379
+ packageJsonPath: string,
380
+ ): ModuleArtifactDiscoveryDiagnostic {
381
+ return {
382
+ ...diagnostic,
383
+ path: diagnostic.path ?? packageJsonPath,
384
+ };
385
+ }
386
+
387
+ function discoveryFileSystemDiagnostic(
388
+ error: HarnessArtifactFileSystemErrorInfo,
389
+ artifact?: SkillHarnessArtifactEntry,
390
+ ): ModuleArtifactDiscoveryDiagnostic {
391
+ return {
392
+ code: "module_artifact_extension_root_unreadable",
393
+ message: error.message,
394
+ path: error.details.path,
395
+ ...(artifact === undefined
396
+ ? {}
397
+ : {
398
+ packageName: artifact.source.packageName,
399
+ artifactId: artifact.id,
400
+ artifactName: artifact.skillName,
401
+ }),
402
+ };
403
+ }
404
+
405
+ function sortDiscoveryDiagnostics(
406
+ diagnostics: readonly ModuleArtifactDiscoveryDiagnostic[],
407
+ ): readonly ModuleArtifactDiscoveryDiagnostic[] {
408
+ return sortDiagnosticsByKey(diagnostics, (diagnostic) => [
409
+ diagnostic.path,
410
+ diagnostic.packageName,
411
+ diagnostic.artifactId,
412
+ diagnostic.artifactName,
413
+ diagnostic.code,
414
+ diagnostic.message,
415
+ ]);
416
+ }
@@ -0,0 +1,32 @@
1
+ import { createNsGitGateway } from "@nseng-ai/capability-kit/git";
2
+ import {
3
+ createNsDomainCommand,
4
+ type NsDomainCommandOptions,
5
+ } from "@nseng-ai/capability-kit/ns-command";
6
+ import { optionalEntry } from "@nseng-ai/foundation/primitives";
7
+ import type { NsCommand, NsCommandSchema } from "@nseng-ai/kernel/sdk";
8
+
9
+ import type { SkillsCommandContext } from "./skills-shared.ts";
10
+
11
+ type HarnessArtifactsNsCommandOptions<S extends NsCommandSchema, T> = Omit<
12
+ NsDomainCommandOptions<S, T, SkillsCommandContext>,
13
+ "createContext"
14
+ >;
15
+
16
+ export function harnessArtifactsNsCommand<S extends NsCommandSchema, T>(
17
+ options: HarnessArtifactsNsCommandOptions<S, T>,
18
+ ): NsCommand<S, T> {
19
+ return createNsDomainCommand({
20
+ ...options,
21
+ createContext: async (ctx) => {
22
+ const repoRootResult = await createNsGitGateway(ctx).optionalRepoRoot({ cwd: ctx.cwd });
23
+ const projectRoot = repoRootResult.type === "found" ? repoRootResult.value : ctx.cwd;
24
+ return {
25
+ cwd: ctx.cwd,
26
+ projectRoot,
27
+ ...optionalEntry("homeDir", ctx.homeDir),
28
+ env: ctx.env,
29
+ };
30
+ },
31
+ });
32
+ }
@@ -0,0 +1,31 @@
1
+ import { defineExtension } from "@nseng-ai/kernel/sdk";
2
+
3
+ import { harnessArtifactsNsCommand } from "../command.ts";
4
+ import {
5
+ renderSkillsInstallHuman,
6
+ runSkillsInstall,
7
+ skillsInstallCommandResultSchema,
8
+ skillsInstallRequestSchema,
9
+ } from "../skills-install.ts";
10
+
11
+ export const skillsInstallNsCommand = harnessArtifactsNsCommand({
12
+ name: "install",
13
+ summary: "Provision an ns skill into a harness.",
14
+ description:
15
+ "Deterministically preview or provision a first-party ns skill into a selected harness and scope.",
16
+ schema: skillsInstallRequestSchema,
17
+ positionals: { skill: { position: 0 } },
18
+ options: {
19
+ harness: { short: "-H" },
20
+ scope: { short: "-s" },
21
+ dryRun: { short: "-n" },
22
+ force: { short: "-f" },
23
+ },
24
+ resultSchema: skillsInstallCommandResultSchema,
25
+ handler: runSkillsInstall,
26
+ renderHuman: renderSkillsInstallHuman,
27
+ });
28
+
29
+ export default defineExtension({
30
+ commands: [skillsInstallNsCommand],
31
+ });
@@ -0,0 +1,23 @@
1
+ import { defineExtension } from "@nseng-ai/kernel/sdk";
2
+
3
+ import { harnessArtifactsNsCommand } from "../command.ts";
4
+ import {
5
+ renderSkillsListHuman,
6
+ runSkillsList,
7
+ skillsListRequestSchema,
8
+ skillsListResultSchema,
9
+ } from "../skills-list.ts";
10
+
11
+ export const skillsListNsCommand = harnessArtifactsNsCommand({
12
+ name: "list",
13
+ summary: "List first-party ns skills.",
14
+ description: "List first-party ns-owned skills available for harness provisioning.",
15
+ schema: skillsListRequestSchema,
16
+ resultSchema: skillsListResultSchema,
17
+ handler: () => runSkillsList(),
18
+ renderHuman: renderSkillsListHuman,
19
+ });
20
+
21
+ export default defineExtension({
22
+ commands: [skillsListNsCommand],
23
+ });
@@ -0,0 +1,26 @@
1
+ import { defineExtension } from "@nseng-ai/kernel/sdk";
2
+
3
+ import { harnessArtifactsNsCommand } from "../command.ts";
4
+ import {
5
+ renderSkillsPathHuman,
6
+ runSkillsPath,
7
+ skillsPathRequestSchema,
8
+ skillsPathResultSchema,
9
+ } from "../skills-path.ts";
10
+
11
+ export const skillsPathNsCommand = harnessArtifactsNsCommand({
12
+ name: "path",
13
+ summary: "Show where an ns skill provisions for a harness.",
14
+ description:
15
+ "Show the resolved target root and artifact path for a first-party ns skill in a selected harness and scope.",
16
+ schema: skillsPathRequestSchema,
17
+ positionals: { skill: { position: 0 } },
18
+ options: { harness: { short: "-H" }, scope: { short: "-s" } },
19
+ resultSchema: skillsPathResultSchema,
20
+ handler: runSkillsPath,
21
+ renderHuman: renderSkillsPathHuman,
22
+ });
23
+
24
+ export default defineExtension({
25
+ commands: [skillsPathNsCommand],
26
+ });
@@ -0,0 +1,28 @@
1
+ import { defineExtension } from "@nseng-ai/kernel/sdk";
2
+
3
+ import { harnessArtifactsNsCommand } from "../command.ts";
4
+ import {
5
+ nsUpdateRequestSchema,
6
+ nsUpdateResultSchema,
7
+ renderNsUpdateHuman,
8
+ runNsUpdate,
9
+ } from "../update.ts";
10
+
11
+ export const nsUpdateCommand = harnessArtifactsNsCommand({
12
+ name: "update",
13
+ summary: "Update ns harness artifacts from selected harnesses.",
14
+ description:
15
+ "Preview or apply updates for manifest-tracked ns harness artifacts and artifacts selected by ns.toml.",
16
+ schema: nsUpdateRequestSchema,
17
+ options: {
18
+ dryRun: { short: "-n" },
19
+ force: { short: "-f" },
20
+ },
21
+ resultSchema: nsUpdateResultSchema,
22
+ handler: runNsUpdate,
23
+ renderHuman: renderNsUpdateHuman,
24
+ });
25
+
26
+ export default defineExtension({
27
+ commands: [nsUpdateCommand],
28
+ });
@@ -0,0 +1,26 @@
1
+ import {
2
+ repoLocalNsCommandDescriptorToPreinstalledCatalogEntry,
3
+ repoLocalNsExtensionToPreinstalledCatalog,
4
+ type PreinstalledNsCommandCatalogEntry,
5
+ } from "@nseng-ai/kernel/cli";
6
+ import { repoLocalNsCommandDescriptor } from "@nseng-ai/kernel/sdk";
7
+
8
+ import { nsUpdateCommand } from "./commands/update.ts";
9
+ import {
10
+ HARNESS_ARTIFACT_NS_COMMAND_EXPORT_PREFIX,
11
+ skillsRepoLocalNsExtension,
12
+ } from "./repo-local-ns-extension.ts";
13
+
14
+ const nsUpdateCommandDescriptor = repoLocalNsCommandDescriptor({
15
+ command: nsUpdateCommand,
16
+ packageExportPrefix: HARNESS_ARTIFACT_NS_COMMAND_EXPORT_PREFIX,
17
+ });
18
+
19
+ export const skillsPreinstalledNsCommandCatalog = [
20
+ ...repoLocalNsExtensionToPreinstalledCatalog(skillsRepoLocalNsExtension),
21
+ repoLocalNsCommandDescriptorToPreinstalledCatalogEntry(nsUpdateCommandDescriptor),
22
+ ] satisfies readonly PreinstalledNsCommandCatalogEntry[];
23
+
24
+ export function listSkillsPreinstalledNsCommandCatalogEntries(): readonly PreinstalledNsCommandCatalogEntry[] {
25
+ return skillsPreinstalledNsCommandCatalog;
26
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ defineRepoLocalNsExtensionDescriptor,
3
+ repoLocalNsCommandDescriptor,
4
+ } from "@nseng-ai/kernel/sdk";
5
+
6
+ import { skillsInstallNsCommand } from "./commands/install.ts";
7
+ import { skillsListNsCommand } from "./commands/list.ts";
8
+ import { skillsPathNsCommand } from "./commands/path.ts";
9
+
10
+ export const HARNESS_ARTIFACT_NS_COMMAND_EXPORT_PREFIX = "@nseng-ai/harness-artifacts/ns/commands";
11
+
12
+ export const skillsRepoLocalNsExtension = defineRepoLocalNsExtensionDescriptor({
13
+ group: "skills",
14
+ description: "List and provision ns-owned skills into assistant harnesses.",
15
+ commands: [skillsListNsCommand, skillsPathNsCommand, skillsInstallNsCommand].map((command) =>
16
+ repoLocalNsCommandDescriptor({
17
+ command,
18
+ packageExportPrefix: HARNESS_ARTIFACT_NS_COMMAND_EXPORT_PREFIX,
19
+ }),
20
+ ),
21
+ });