@nseng-ai/areg 0.1.1 → 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.
- package/package.json +7 -6
- package/src/cli.ts +0 -33
- package/src/context.ts +3 -29
- package/src/fake-gateways.ts +108 -380
- package/src/gateways/errors.ts +2 -2
- package/src/gateways/fs-utils.ts +2 -2
- package/src/gateways/project-fs.ts +29 -41
- package/src/gateways/project-gateway.ts +98 -31
- package/src/gateways.ts +34 -150
- package/src/index.ts +2 -28
- package/src/operations/check.ts +33 -20
- package/src/operations/doctor-skills.ts +71 -7
- package/src/operations/file-state.ts +4 -4
- package/src/operations/manifest-source-findings.ts +45 -0
- package/src/operations/manifest-sources.ts +119 -0
- package/src/operations/pi-settings.ts +2 -5
- package/src/operations/project-inspection.ts +32 -28
- package/src/operations/project-mutations.ts +5 -21
- package/src/operations/project-resolution.ts +2 -2
- package/src/operations/skill-find.ts +23 -3
- package/src/operations/skill-kind-apply-plan.ts +2 -2
- package/src/operations/skill-kind-frontmatter.ts +1 -1
- package/src/operations/skill-kind-inference.ts +30 -14
- package/src/operations/skill-kind.ts +34 -12
- package/src/sort.ts +3 -3
- package/src/gateways/command-constants.ts +0 -1
- package/src/gateways/github-gateway.ts +0 -111
- package/src/gateways/host-gateway.ts +0 -35
- package/src/gateways/mutation-policy.ts +0 -94
- package/src/gateways/npx-skills-gateway.ts +0 -53
- package/src/gateways/prompt-gateway.ts +0 -21
- package/src/gateways/skillx-workspace-gateway.ts +0 -220
- package/src/operations/frontmatter.ts +0 -151
- package/src/operations/init.ts +0 -548
- package/src/operations/lockfile.ts +0 -107
- package/src/operations/managed-markdown-block.ts +0 -47
- package/src/operations/project-agents.ts +0 -115
- package/src/operations/skill-mirror-conventions.ts +0 -126
- package/src/operations/skillx.ts +0 -305
- package/src/operations/toml-section.ts +0 -53
- package/src/operations/update-skills.ts +0 -325
- package/src/real-gateways.ts +0 -6
- package/src/skill-lookup.ts +0 -254
package/src/gateways.ts
CHANGED
|
@@ -1,135 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SKILL_LOOKUP_ROOT_DESCRIPTORS,
|
|
3
3
|
skillLookupDescriptorForSourceType,
|
|
4
|
-
} from "
|
|
4
|
+
} from "@nseng-ai/foundation/skill-lookup";
|
|
5
5
|
import type {
|
|
6
6
|
SkillLookupRoot,
|
|
7
7
|
SkillLookupRootDescriptor,
|
|
8
8
|
SkillLookupSourceType,
|
|
9
|
-
} from "
|
|
9
|
+
} from "@nseng-ai/foundation/skill-lookup";
|
|
10
|
+
import type { GitGateway } from "@nseng-ai/capability-kit/git";
|
|
10
11
|
import type { ErrorInfo, Result } from "@nseng-ai/foundation/result";
|
|
12
|
+
import type { PathState, TextFileState } from "@nseng-ai/harness-artifacts/api";
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
export type AregHostToolName = (typeof AREG_HOST_TOOL_NAMES)[number];
|
|
14
|
+
import type { AregManifestSkillSourcesInspection } from "./operations/manifest-sources.ts";
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export type AregOperationResult = Result<undefined, AregErrorInfo>;
|
|
20
|
-
|
|
21
|
-
export type AregToolCheckResult =
|
|
22
|
-
| { type: "found"; tool: AregHostToolName; path: string }
|
|
23
|
-
| { type: "missing"; tool: AregHostToolName; message: string };
|
|
24
|
-
|
|
25
|
-
export interface AregHostGateway {
|
|
26
|
-
checkTool(options: {
|
|
27
|
-
tool: AregHostToolName;
|
|
28
|
-
cwd: string;
|
|
29
|
-
env: NodeJS.ProcessEnv;
|
|
30
|
-
}): Promise<AregToolCheckResult>;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type AregGithubSkillListResult =
|
|
34
|
-
| { type: "ok"; skillNames: readonly string[] }
|
|
35
|
-
| { type: "missing"; message: string }
|
|
36
|
-
| { type: "auth-error"; message: string }
|
|
37
|
-
| { type: "error"; error: AregErrorInfo };
|
|
38
|
-
|
|
39
|
-
export type AregGithubSkillFileResult =
|
|
40
|
-
| { type: "found" }
|
|
41
|
-
| { type: "missing"; message: string }
|
|
42
|
-
| { type: "auth-error"; message: string }
|
|
43
|
-
| { type: "error"; error: AregErrorInfo };
|
|
44
|
-
|
|
45
|
-
export interface AregGithubGateway {
|
|
46
|
-
listSkillDirectoryNames(options: {
|
|
47
|
-
repo: string;
|
|
48
|
-
ref?: string;
|
|
49
|
-
env: NodeJS.ProcessEnv;
|
|
50
|
-
}): Promise<AregGithubSkillListResult>;
|
|
51
|
-
checkSkillFile(options: {
|
|
52
|
-
repo: string;
|
|
53
|
-
path: string;
|
|
54
|
-
ref?: string;
|
|
55
|
-
env: NodeJS.ProcessEnv;
|
|
56
|
-
}): Promise<AregGithubSkillFileResult>;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface AregNpxSkillsAddRequest {
|
|
60
|
-
sourceRepo: string;
|
|
61
|
-
/** Empty means install all skills from the source repository. */
|
|
62
|
-
skillNames: readonly string[];
|
|
63
|
-
targetAgents: readonly string[];
|
|
64
|
-
cwd: string;
|
|
65
|
-
env: NodeJS.ProcessEnv;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export type AregNpxSkillsAddResult = { type: "ok" } | { type: "error"; error: AregErrorInfo };
|
|
69
|
-
|
|
70
|
-
export interface AregNpxSkillsGateway {
|
|
71
|
-
addSkills(request: AregNpxSkillsAddRequest): Promise<AregNpxSkillsAddResult>;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface AregPromptGateway {
|
|
75
|
-
confirm(request: { message: string; defaultValue: boolean }): Promise<boolean>;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface AregSkillxInstalledSkill {
|
|
79
|
-
name: string;
|
|
80
|
-
directory: string;
|
|
81
|
-
skillFile: string;
|
|
82
|
-
relativeFiles: readonly string[];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export interface AregSkillxWorkspaceInstall {
|
|
86
|
-
workspaceRoot: string;
|
|
87
|
-
installedSkills: readonly AregSkillxInstalledSkill[];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface AregSkillxInstallRequest {
|
|
91
|
-
sourceRepo: string;
|
|
92
|
-
skillName?: string;
|
|
93
|
-
cwd: string;
|
|
94
|
-
env: NodeJS.ProcessEnv;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface AregSkillxWorkspaceCleanupRequest {
|
|
98
|
-
workspaceRoot: string;
|
|
99
|
-
}
|
|
16
|
+
// The git methods areg consumes: resolve the repo root and materialize
|
|
17
|
+
// worktree-relative git paths (for example `info/exclude`). A full `GitGateway`
|
|
18
|
+
// is assignable to this narrowed surface.
|
|
19
|
+
export type AregGitGateway = Pick<GitGateway, "optionalRepoRoot" | "gitPath">;
|
|
100
20
|
|
|
101
|
-
export type
|
|
102
|
-
| { type: "ok"; workspace: AregSkillxWorkspaceInstall }
|
|
103
|
-
| { type: "error"; error: AregErrorInfo };
|
|
21
|
+
export type AregErrorInfo = ErrorInfo;
|
|
104
22
|
|
|
105
|
-
export
|
|
106
|
-
installIntoWorkspace(request: AregSkillxInstallRequest): Promise<AregSkillxInstallResult>;
|
|
107
|
-
cleanupWorkspace(request: AregSkillxWorkspaceCleanupRequest): Promise<AregOperationResult>;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export type AregPathState =
|
|
111
|
-
| { type: "missing" }
|
|
112
|
-
| { type: "file" }
|
|
113
|
-
| { type: "directory" }
|
|
114
|
-
| { type: "symlink"; target: string }
|
|
115
|
-
| { type: "other" };
|
|
116
|
-
|
|
117
|
-
export type AregTextFileState =
|
|
118
|
-
| { type: "missing" }
|
|
119
|
-
| { type: "file"; text: string }
|
|
120
|
-
| { type: "directory" }
|
|
121
|
-
| { type: "symlink"; target: string }
|
|
122
|
-
| { type: "other" }
|
|
123
|
-
| { type: "unreadable"; message: string };
|
|
23
|
+
export type AregOperationResult = Result<undefined, AregErrorInfo>;
|
|
124
24
|
|
|
125
25
|
export interface AregCheckSkillInspection {
|
|
126
26
|
name: string;
|
|
127
|
-
skillsPath:
|
|
128
|
-
agentsPath:
|
|
129
|
-
claudePath:
|
|
130
|
-
localSkillMd:
|
|
131
|
-
remoteSkillMd:
|
|
132
|
-
openaiPolicy:
|
|
27
|
+
skillsPath: PathState;
|
|
28
|
+
agentsPath: PathState;
|
|
29
|
+
claudePath: PathState;
|
|
30
|
+
localSkillMd: TextFileState;
|
|
31
|
+
remoteSkillMd: TextFileState;
|
|
32
|
+
openaiPolicy: TextFileState;
|
|
133
33
|
}
|
|
134
34
|
|
|
135
35
|
export function missingCheckSkillInspection(name: string): AregCheckSkillInspection {
|
|
@@ -178,8 +78,8 @@ export interface AregSkillFindSkillInspection {
|
|
|
178
78
|
root: SkillLookupRoot;
|
|
179
79
|
sourceType: SkillLookupSourceType;
|
|
180
80
|
baseRelativePath: string;
|
|
181
|
-
skillDir:
|
|
182
|
-
skillMd:
|
|
81
|
+
skillDir: PathState;
|
|
82
|
+
skillMd: TextFileState;
|
|
183
83
|
}
|
|
184
84
|
|
|
185
85
|
export interface AregSkillFindRootsInspection {
|
|
@@ -190,11 +90,11 @@ export interface AregSkillKindSkillInspection {
|
|
|
190
90
|
name: string;
|
|
191
91
|
sourceType: AregSkillKindSourceType;
|
|
192
92
|
baseRelativePath: string;
|
|
193
|
-
skillDir:
|
|
194
|
-
skillMd:
|
|
195
|
-
openaiPolicy:
|
|
196
|
-
agentsPath:
|
|
197
|
-
claudePath:
|
|
93
|
+
skillDir: PathState;
|
|
94
|
+
skillMd: TextFileState;
|
|
95
|
+
openaiPolicy: TextFileState;
|
|
96
|
+
agentsPath: PathState;
|
|
97
|
+
claudePath: PathState;
|
|
198
98
|
}
|
|
199
99
|
|
|
200
100
|
export interface AregReplacementInspection {
|
|
@@ -220,22 +120,15 @@ export interface AregSkillInspectionRequest {
|
|
|
220
120
|
|
|
221
121
|
export interface AregProjectBaseInspection {
|
|
222
122
|
projectDir: string;
|
|
223
|
-
projectPathState:
|
|
224
|
-
lockfile:
|
|
225
|
-
nsToml:
|
|
226
|
-
aregJson:
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export interface AregInstructionFilesInspection {
|
|
230
|
-
agentsMd: AregTextFileState;
|
|
231
|
-
claudeMd: AregTextFileState;
|
|
232
|
-
claudeDir: AregPathState;
|
|
233
|
-
claudeSettings: AregTextFileState;
|
|
123
|
+
projectPathState: PathState;
|
|
124
|
+
lockfile: TextFileState;
|
|
125
|
+
nsToml: TextFileState;
|
|
126
|
+
aregJson: TextFileState;
|
|
234
127
|
}
|
|
235
128
|
|
|
236
129
|
export interface AregPiArtifactsInspection {
|
|
237
|
-
piDir:
|
|
238
|
-
piSettings:
|
|
130
|
+
piDir: PathState;
|
|
131
|
+
piSettings: TextFileState;
|
|
239
132
|
replacement: AregReplacementInspection;
|
|
240
133
|
}
|
|
241
134
|
|
|
@@ -263,15 +156,12 @@ export type AregSkillKindResolveResult =
|
|
|
263
156
|
| { type: "ok"; skillName: string }
|
|
264
157
|
| { type: "error"; error: AregErrorInfo };
|
|
265
158
|
|
|
266
|
-
export type AregProjectMutationPolicy = "init" | "skill-kind";
|
|
267
|
-
|
|
268
159
|
export interface AregProjectTextWriteRequest {
|
|
269
160
|
projectDir: string;
|
|
270
161
|
relativePath: string;
|
|
271
162
|
content: string;
|
|
272
163
|
description: string;
|
|
273
164
|
createParent: boolean;
|
|
274
|
-
policy: AregProjectMutationPolicy;
|
|
275
165
|
env: NodeJS.ProcessEnv;
|
|
276
166
|
}
|
|
277
167
|
|
|
@@ -279,7 +169,6 @@ export interface AregProjectManagedTargetRequest {
|
|
|
279
169
|
projectDir: string;
|
|
280
170
|
relativePath: string;
|
|
281
171
|
description: string;
|
|
282
|
-
policy: "skill-kind";
|
|
283
172
|
env: NodeJS.ProcessEnv;
|
|
284
173
|
}
|
|
285
174
|
|
|
@@ -298,15 +187,17 @@ export type AregProjectRemoveEmptyDirResult =
|
|
|
298
187
|
* This is intentionally domain-oriented: it exposes named areg project facts and
|
|
299
188
|
* project-scoped safe mutation primitives, not a generic filesystem API. Add new
|
|
300
189
|
* reads here only when they represent stable areg project concepts, and route new
|
|
301
|
-
* writes/deletes through project-scoped primitives
|
|
190
|
+
* writes/deletes through project-scoped primitives. Do not
|
|
302
191
|
* add an unrestricted filesystem gateway to areg as a convenience for operation code.
|
|
303
192
|
*/
|
|
304
193
|
export interface AregProjectGateway {
|
|
305
194
|
inspectProjectBase(request: AregProjectInspectionRequest): Promise<AregProjectBaseInspection>;
|
|
306
|
-
inspectInstructionFiles(request: AregProjectDirRequest): Promise<AregInstructionFilesInspection>;
|
|
307
195
|
inspectPiArtifacts(request: AregProjectDirRequest): Promise<AregPiArtifactsInspection>;
|
|
308
196
|
inspectPiSkillInventory(request: AregProjectDirRequest): Promise<AregPiSkillInventoryInspection>;
|
|
309
197
|
inspectSkillNameInventory(request: AregProjectDirRequest): Promise<AregSkillNameInventory>;
|
|
198
|
+
inspectManifestSkillSources(
|
|
199
|
+
request: AregProjectDirRequest,
|
|
200
|
+
): Promise<AregManifestSkillSourcesInspection>;
|
|
310
201
|
inspectSkillFindRoots(request: AregProjectDirRequest): Promise<AregSkillFindRootsInspection>;
|
|
311
202
|
inspectCheckSkill(request: AregSkillInspectionRequest): Promise<AregCheckSkillInspection>;
|
|
312
203
|
inspectSkillKindSkill(request: AregSkillInspectionRequest): Promise<AregSkillKindSkillInspection>;
|
|
@@ -331,13 +222,6 @@ export interface AregProjectGateway {
|
|
|
331
222
|
): Promise<AregProjectRemoveEmptyDirResult>;
|
|
332
223
|
}
|
|
333
224
|
|
|
334
|
-
export interface AregInitTextWritePlan {
|
|
335
|
-
relativePath: "ns.toml" | "AGENTS.md" | "CLAUDE.md" | ".claude/settings.local.json";
|
|
336
|
-
content: string;
|
|
337
|
-
description: string;
|
|
338
|
-
createParent: boolean;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
225
|
export interface AregSkillKindTextWritePlan {
|
|
342
226
|
relativePath: string;
|
|
343
227
|
content: string;
|
package/src/index.ts
CHANGED
|
@@ -5,44 +5,18 @@ export type {
|
|
|
5
5
|
AregCheckSkillInspection,
|
|
6
6
|
AregErrorInfo,
|
|
7
7
|
AregReplacementInspection,
|
|
8
|
-
AregGithubGateway,
|
|
9
|
-
AregGithubSkillListResult,
|
|
10
|
-
AregHostGateway,
|
|
11
|
-
AregHostToolName,
|
|
12
|
-
AregNpxSkillsAddRequest,
|
|
13
|
-
AregNpxSkillsAddResult,
|
|
14
|
-
AregNpxSkillsGateway,
|
|
15
8
|
AregOperationResult,
|
|
16
|
-
AregPathState,
|
|
17
9
|
AregProjectBaseInspection,
|
|
18
10
|
AregProjectDirRequest,
|
|
19
11
|
AregProjectGateway,
|
|
20
12
|
AregProjectInspectionRequest,
|
|
21
|
-
AregProjectMutationPolicy,
|
|
22
13
|
AregProjectMutationResult,
|
|
23
14
|
AregProjectRemoveEmptyDirResult,
|
|
24
15
|
AregProjectTextWriteRequest,
|
|
25
|
-
AregPromptGateway,
|
|
26
16
|
AregSkillInspectionRequest,
|
|
27
17
|
AregSkillKindResolveRequest,
|
|
28
18
|
AregSkillKindResolveResult,
|
|
29
19
|
AregSkillKindSkillInspection,
|
|
30
|
-
AregSkillxInstallRequest,
|
|
31
|
-
AregSkillxInstallResult,
|
|
32
|
-
AregSkillxInstalledSkill,
|
|
33
|
-
AregSkillxWorkspaceCleanupRequest,
|
|
34
|
-
AregSkillxWorkspaceGateway,
|
|
35
|
-
AregSkillxWorkspaceInstall,
|
|
36
|
-
AregTextFileState,
|
|
37
|
-
AregToolCheckResult,
|
|
38
20
|
} from "./gateways.ts";
|
|
39
|
-
export {
|
|
40
|
-
export {
|
|
41
|
-
export {
|
|
42
|
-
buildNpxSkillsAddArgs,
|
|
43
|
-
RealAregGithubGateway,
|
|
44
|
-
RealAregHostGateway,
|
|
45
|
-
RealAregNpxSkillsGateway,
|
|
46
|
-
RealAregProjectGateway,
|
|
47
|
-
RealAregSkillxWorkspaceGateway,
|
|
48
|
-
} from "./real-gateways.ts";
|
|
21
|
+
export { parseSkillFrontmatterText } from "./operations/check.ts";
|
|
22
|
+
export { RealAregProjectGateway } from "./gateways/project-gateway.ts";
|
package/src/operations/check.ts
CHANGED
|
@@ -2,26 +2,25 @@ import { failure, ok, negative, type ClinkrExit } from "@nseng-ai/clinkr";
|
|
|
2
2
|
import type { Result } from "@nseng-ai/foundation/result";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
-
import type { AregCliContext } from "../context.ts";
|
|
6
|
-
import { missingCheckSkillInspection } from "../gateways.ts";
|
|
7
|
-
import type { AregCheckSkillInspection } from "../gateways.ts";
|
|
8
|
-
import { sortStrings } from "../sort.ts";
|
|
9
|
-
import { isPathStateError } from "./file-state.ts";
|
|
10
|
-
import { parseSkillFrontmatterBlock } from "./frontmatter.ts";
|
|
11
5
|
import {
|
|
6
|
+
expectedAgentsSkillSymlinkTarget,
|
|
7
|
+
expectedClaudeSkillSymlinkTarget,
|
|
8
|
+
isAgentsSkillMirror,
|
|
9
|
+
isClaudeSkillMirror,
|
|
12
10
|
parseInspectedLockfile,
|
|
13
|
-
|
|
11
|
+
parseSkillFrontmatterBlock,
|
|
14
12
|
type LockfileSkill,
|
|
15
13
|
type SkillsLockfile,
|
|
16
|
-
} from "
|
|
14
|
+
} from "@nseng-ai/harness-artifacts/api";
|
|
15
|
+
|
|
16
|
+
import type { AregCliContext } from "../context.ts";
|
|
17
|
+
import { missingCheckSkillInspection } from "../gateways.ts";
|
|
18
|
+
import { sortStringsLocaleAware } from "../sort.ts";
|
|
19
|
+
import { manifestSourceFinding } from "./manifest-source-findings.ts";
|
|
20
|
+
import type { AregCheckSkillInspection } from "../gateways.ts";
|
|
21
|
+
import { isPathStateError } from "./file-state.ts";
|
|
17
22
|
import { verifyPiReplacement } from "./pi-replacement.ts";
|
|
18
23
|
import { parsePiSettings } from "./pi-settings.ts";
|
|
19
|
-
import {
|
|
20
|
-
expectedAgentsSkillSymlinkTarget,
|
|
21
|
-
expectedClaudeSkillSymlinkTarget,
|
|
22
|
-
isAgentsSkillMirror,
|
|
23
|
-
isClaudeSkillMirror,
|
|
24
|
-
} from "./skill-mirror-conventions.ts";
|
|
25
24
|
import {
|
|
26
25
|
inferSkillKindRecord,
|
|
27
26
|
inspectSkillFrontmatter,
|
|
@@ -58,6 +57,9 @@ const CHECK_ISSUE_CODES = [
|
|
|
58
57
|
"agents-md-missing-peer",
|
|
59
58
|
"claude-md-missing-agents-ref",
|
|
60
59
|
"pi-settings-unusable",
|
|
60
|
+
"invalid-install-manifest",
|
|
61
|
+
"manifest-skill-target-missing",
|
|
62
|
+
"manifest-skill-md-missing",
|
|
61
63
|
] as const;
|
|
62
64
|
|
|
63
65
|
const MAX_SKILL_DESCRIPTION_CHARS = 1024;
|
|
@@ -71,8 +73,6 @@ interface CheckIssue {
|
|
|
71
73
|
message: string;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
export { parseLockfileData };
|
|
75
|
-
|
|
76
76
|
const checkIssueSchema = z.object({
|
|
77
77
|
skill: z.string(),
|
|
78
78
|
code: z.enum(CHECK_ISSUE_CODES),
|
|
@@ -151,6 +151,7 @@ export function buildCheckReport(
|
|
|
151
151
|
issues.push(...checkSkillInvocationKind(entry, inspected, record));
|
|
152
152
|
}
|
|
153
153
|
issues.push(...checkLockfileHashes(lockfile));
|
|
154
|
+
issues.push(...checkManifestSkillSources(inspection));
|
|
154
155
|
issues.push(...checkOrphansAndDangling(lockfile, inspection));
|
|
155
156
|
issues.push(...checkPairing(inspection));
|
|
156
157
|
return {
|
|
@@ -175,7 +176,7 @@ export function formatCheckReport(report: Pick<CheckReport, "issues">): string {
|
|
|
175
176
|
grouped.set(issue.skill, existing);
|
|
176
177
|
}
|
|
177
178
|
const lines: string[] = [];
|
|
178
|
-
for (const skill of
|
|
179
|
+
for (const skill of sortStringsLocaleAware([...grouped.keys()])) {
|
|
179
180
|
lines.push("", `${skill}:`);
|
|
180
181
|
for (const issue of grouped.get(skill) ?? []) lines.push(` ${issue.message}`);
|
|
181
182
|
}
|
|
@@ -183,6 +184,18 @@ export function formatCheckReport(report: Pick<CheckReport, "issues">): string {
|
|
|
183
184
|
return lines.join("\n");
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
function checkManifestSkillSources(inspection: CheckProjectInspection): CheckIssue[] {
|
|
188
|
+
const issues: CheckIssue[] = [];
|
|
189
|
+
for (const error of inspection.manifestSkillSources.errors) {
|
|
190
|
+
issues.push(issue("project", "invalid-install-manifest", error.message));
|
|
191
|
+
}
|
|
192
|
+
for (const source of inspection.manifestSkillSources.sources) {
|
|
193
|
+
const finding = manifestSourceFinding(source);
|
|
194
|
+
if (finding !== undefined) issues.push(issue(source.skillName, finding.code, finding.message));
|
|
195
|
+
}
|
|
196
|
+
return issues;
|
|
197
|
+
}
|
|
198
|
+
|
|
186
199
|
function checkLocalSkill(
|
|
187
200
|
entry: LockfileSkill,
|
|
188
201
|
inspected: AregCheckSkillInspection,
|
|
@@ -470,7 +483,7 @@ function checkOrphansAndDangling(
|
|
|
470
483
|
const excluded = new Set(inspection.excludedSkillNames);
|
|
471
484
|
const byName = new Map(inspection.skills.map((skill) => [skill.name, skill]));
|
|
472
485
|
const issues: CheckIssue[] = [];
|
|
473
|
-
for (const name of
|
|
486
|
+
for (const name of sortStringsLocaleAware(inspection.skillsDirectoryNames)) {
|
|
474
487
|
if (!lockNames.has(name) && !excluded.has(name))
|
|
475
488
|
issues.push(
|
|
476
489
|
issue(
|
|
@@ -480,7 +493,7 @@ function checkOrphansAndDangling(
|
|
|
480
493
|
),
|
|
481
494
|
);
|
|
482
495
|
}
|
|
483
|
-
for (const name of
|
|
496
|
+
for (const name of sortStringsLocaleAware(inspection.agentsSkillNames)) {
|
|
484
497
|
if (!lockNames.has(name) && !excluded.has(name))
|
|
485
498
|
issues.push(
|
|
486
499
|
issue(
|
|
@@ -490,7 +503,7 @@ function checkOrphansAndDangling(
|
|
|
490
503
|
),
|
|
491
504
|
);
|
|
492
505
|
}
|
|
493
|
-
for (const name of
|
|
506
|
+
for (const name of sortStringsLocaleAware([...lockNames])) {
|
|
494
507
|
const inspected = byName.get(name) ?? missingCheckSkillInspection(name);
|
|
495
508
|
if (
|
|
496
509
|
inspected.skillsPath.type === "missing" &&
|
|
@@ -4,16 +4,25 @@ import { optionalEntries, optionalEntry } from "@nseng-ai/foundation/primitives"
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import type { AregCliContext } from "../context.ts";
|
|
7
|
+
import type { PathState, TextFileState } from "@nseng-ai/harness-artifacts/api";
|
|
7
8
|
import type {
|
|
8
9
|
AregCheckSkillInspection,
|
|
9
|
-
AregPathState,
|
|
10
10
|
AregPiSkillInventoryInspection,
|
|
11
11
|
AregProjectBaseInspection,
|
|
12
12
|
AregReplacementInspection,
|
|
13
13
|
AregSkillKindSkillInspection,
|
|
14
14
|
AregSkillNameInventory,
|
|
15
|
-
AregTextFileState,
|
|
16
15
|
} from "../gateways.ts";
|
|
16
|
+
import {
|
|
17
|
+
MANIFEST_FAILURE_CODE,
|
|
18
|
+
MANIFEST_FAILURE_REMEDIATION,
|
|
19
|
+
manifestSourceFinding,
|
|
20
|
+
} from "./manifest-source-findings.ts";
|
|
21
|
+
import {
|
|
22
|
+
manifestSkillKindNames,
|
|
23
|
+
toManifestSkillSourceView,
|
|
24
|
+
type AregManifestSkillSourcesInspection,
|
|
25
|
+
} from "./manifest-sources.ts";
|
|
17
26
|
import { uniqueSortedStrings } from "../sort.ts";
|
|
18
27
|
import { renderDoctorSkills } from "./doctor-skills-report.ts";
|
|
19
28
|
import {
|
|
@@ -24,7 +33,7 @@ import {
|
|
|
24
33
|
import { parsePiSettings } from "./pi-settings.ts";
|
|
25
34
|
import { collectCheckSkillInspections, collectSkillKindInspections } from "./project-inspection.ts";
|
|
26
35
|
import { buildSkillKindRecords, type SkillKindRecord } from "./skill-kind-inference.ts";
|
|
27
|
-
import { isAgentsSkillMirror, isClaudeSkillMirror } from "
|
|
36
|
+
import { isAgentsSkillMirror, isClaudeSkillMirror } from "@nseng-ai/harness-artifacts/api";
|
|
28
37
|
|
|
29
38
|
const doctorStatusValues = ["ok", "warning", "error"] as const;
|
|
30
39
|
|
|
@@ -57,8 +66,9 @@ export interface DoctorSkillsInspection {
|
|
|
57
66
|
skillInventory: AregSkillNameInventory;
|
|
58
67
|
piSkillInventory: AregPiSkillInventoryInspection;
|
|
59
68
|
replacement: AregReplacementInspection;
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
manifestSkillSources: AregManifestSkillSourcesInspection;
|
|
70
|
+
piDir: PathState;
|
|
71
|
+
piSettings: TextFileState;
|
|
62
72
|
checkSkills: readonly AregCheckSkillInspection[];
|
|
63
73
|
skillKindSkills: readonly AregSkillKindSkillInspection[];
|
|
64
74
|
}
|
|
@@ -162,11 +172,13 @@ export function buildDoctorSkillFindings(
|
|
|
162
172
|
piDir: inspection.piDir,
|
|
163
173
|
piSettings: inspection.piSettings,
|
|
164
174
|
replacement: inspection.replacement,
|
|
175
|
+
manifestSkillSources: inspection.manifestSkillSources,
|
|
165
176
|
skills: inspection.skillKindSkills,
|
|
166
177
|
});
|
|
167
178
|
const records = skillKindRecords.ok ? skillKindRecords.value : [];
|
|
168
179
|
const findings: DoctorSkillFinding[] = [];
|
|
169
180
|
findings.push(...filesystemFindings(inspection));
|
|
181
|
+
findings.push(...manifestFindings(inspection));
|
|
170
182
|
findings.push(...piInventoryFindings(inspection));
|
|
171
183
|
findings.push(...replacementFindings(records, piSettings.value.exclusions));
|
|
172
184
|
if (!skillKindRecords.ok) {
|
|
@@ -206,9 +218,21 @@ async function inspectDoctorSkillsProject(
|
|
|
206
218
|
const piArtifacts = await ctx.project.inspectPiArtifacts({ projectDir, env: ctx.env });
|
|
207
219
|
const skillInventory = await ctx.project.inspectSkillNameInventory({ projectDir, env: ctx.env });
|
|
208
220
|
const piSkillInventory = await ctx.project.inspectPiSkillInventory({ projectDir, env: ctx.env });
|
|
209
|
-
const
|
|
221
|
+
const manifestSkillSources = await ctx.project.inspectManifestSkillSources({
|
|
222
|
+
projectDir,
|
|
223
|
+
env: ctx.env,
|
|
224
|
+
});
|
|
225
|
+
const baseSkillNames = allKnownSkillNames(skillInventory, { includeSkillKindNames: true });
|
|
226
|
+
const skillNames = uniqueSortedStrings([
|
|
227
|
+
...baseSkillNames,
|
|
228
|
+
...manifestSkillSources.sources.map((source) => source.skillName),
|
|
229
|
+
]);
|
|
210
230
|
const checkSkills = await collectCheckSkillInspections(ctx, projectDir, skillNames);
|
|
211
|
-
const
|
|
231
|
+
const skillKindNames = uniqueSortedStrings([
|
|
232
|
+
...baseSkillNames,
|
|
233
|
+
...manifestSkillKindNames(manifestSkillSources.sources),
|
|
234
|
+
]);
|
|
235
|
+
const skillKindSkills = await collectSkillKindInspections(ctx, projectDir, skillKindNames);
|
|
212
236
|
return {
|
|
213
237
|
type: "ok",
|
|
214
238
|
value: {
|
|
@@ -217,6 +241,7 @@ async function inspectDoctorSkillsProject(
|
|
|
217
241
|
skillInventory,
|
|
218
242
|
piSkillInventory,
|
|
219
243
|
replacement: piArtifacts.replacement,
|
|
244
|
+
manifestSkillSources,
|
|
220
245
|
piDir: piArtifacts.piDir,
|
|
221
246
|
piSettings: piArtifacts.piSettings,
|
|
222
247
|
checkSkills,
|
|
@@ -283,6 +308,45 @@ function filesystemFindings(inspection: DoctorSkillsInspection): readonly Doctor
|
|
|
283
308
|
return findings;
|
|
284
309
|
}
|
|
285
310
|
|
|
311
|
+
function manifestFindings(inspection: DoctorSkillsInspection): readonly DoctorSkillFinding[] {
|
|
312
|
+
const findings: DoctorSkillFinding[] = [];
|
|
313
|
+
for (const error of inspection.manifestSkillSources.errors) {
|
|
314
|
+
findings.push({
|
|
315
|
+
code: MANIFEST_FAILURE_CODE,
|
|
316
|
+
severity: "error",
|
|
317
|
+
message: error.message,
|
|
318
|
+
remediation: MANIFEST_FAILURE_REMEDIATION,
|
|
319
|
+
path: error.type === "manifest" ? error.manifestPath : error.harness,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
for (const source of inspection.manifestSkillSources.sources) {
|
|
323
|
+
const evidence = toManifestSkillSourceView(source);
|
|
324
|
+
const finding = manifestSourceFinding(source);
|
|
325
|
+
if (finding !== undefined) {
|
|
326
|
+
findings.push({
|
|
327
|
+
code: finding.code,
|
|
328
|
+
severity: "warning",
|
|
329
|
+
skill: source.skillName,
|
|
330
|
+
path: finding.path,
|
|
331
|
+
message: finding.message,
|
|
332
|
+
remediation: finding.remediation,
|
|
333
|
+
evidence,
|
|
334
|
+
});
|
|
335
|
+
} else {
|
|
336
|
+
findings.push({
|
|
337
|
+
code: "manifest-skill-source",
|
|
338
|
+
severity: "info",
|
|
339
|
+
skill: source.skillName,
|
|
340
|
+
path: source.targetSkillRelativePath,
|
|
341
|
+
message: `${source.skillName} is tracked by shared manifest entry ${source.manifestKey} from ${source.provenance.packageName}@${source.provenance.version}.`,
|
|
342
|
+
remediation: "No action required unless this manifest provenance is stale.",
|
|
343
|
+
evidence,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return findings;
|
|
348
|
+
}
|
|
349
|
+
|
|
286
350
|
function piInventoryFindings(inspection: DoctorSkillsInspection): readonly DoctorSkillFinding[] {
|
|
287
351
|
const findings: DoctorSkillFinding[] = [];
|
|
288
352
|
const expected = new Set(inspection.skillInventory.skillKindNames);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { optionalEntry } from "@nseng-ai/foundation/primitives";
|
|
2
2
|
import { resultErr, type Result } from "@nseng-ai/foundation/result";
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { PathState, TextFileState } from "@nseng-ai/harness-artifacts/api";
|
|
5
5
|
|
|
6
|
-
type NonUsableTextFileState = Exclude<
|
|
7
|
-
type NonUsableDirectoryState = Exclude<
|
|
6
|
+
type NonUsableTextFileState = Exclude<TextFileState, { type: "file" } | { type: "missing" }>;
|
|
7
|
+
type NonUsableDirectoryState = Exclude<PathState, { type: "directory" } | { type: "missing" }>;
|
|
8
8
|
|
|
9
9
|
export function isPathStateError(error: { code: string }): boolean {
|
|
10
10
|
return (
|
|
@@ -16,7 +16,7 @@ export function isPathStateError(error: { code: string }): boolean {
|
|
|
16
16
|
|
|
17
17
|
export function validateOptionalDirectoryState(options: {
|
|
18
18
|
pathLabel: string;
|
|
19
|
-
state:
|
|
19
|
+
state: PathState;
|
|
20
20
|
action: string;
|
|
21
21
|
symlinkSubject?: string;
|
|
22
22
|
}): Result<undefined> {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyManifestSkillSource,
|
|
3
|
+
type AregManifestSkillSourceInspection,
|
|
4
|
+
} from "./manifest-sources.ts";
|
|
5
|
+
|
|
6
|
+
export interface ManifestSourceFinding {
|
|
7
|
+
code: "manifest-skill-target-missing" | "manifest-skill-md-missing";
|
|
8
|
+
message: string;
|
|
9
|
+
path: string;
|
|
10
|
+
remediation: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const MANIFEST_FAILURE_CODE = "invalid-install-manifest";
|
|
14
|
+
export const MANIFEST_FAILURE_REMEDIATION =
|
|
15
|
+
"Fix the shared harness artifact manifest or run ns update to reconcile manifest-tracked artifacts.";
|
|
16
|
+
export const MANIFEST_SOURCE_REMEDIATION =
|
|
17
|
+
"Run ns update to reconcile manifest-tracked harness artifacts, or remove/fix the stale manifest entry through the owning provisioning workflow.";
|
|
18
|
+
|
|
19
|
+
export function manifestSourceLabel(source: AregManifestSkillSourceInspection): string {
|
|
20
|
+
return `Shared manifest entry ${source.manifestKey} from ${source.provenance.packageName}@${source.provenance.version}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function manifestSourceFinding(
|
|
24
|
+
source: AregManifestSkillSourceInspection,
|
|
25
|
+
): ManifestSourceFinding | undefined {
|
|
26
|
+
const label = manifestSourceLabel(source);
|
|
27
|
+
const status = classifyManifestSkillSource(source);
|
|
28
|
+
if (status === "target-missing") {
|
|
29
|
+
return {
|
|
30
|
+
code: "manifest-skill-target-missing",
|
|
31
|
+
path: source.targetSkillRelativePath,
|
|
32
|
+
message: `${label} targets ${source.targetSkillRelativePath}, but the skill directory is missing.`,
|
|
33
|
+
remediation: MANIFEST_SOURCE_REMEDIATION,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (status === "md-missing") {
|
|
37
|
+
return {
|
|
38
|
+
code: "manifest-skill-md-missing",
|
|
39
|
+
path: `${source.targetSkillRelativePath}/SKILL.md`,
|
|
40
|
+
message: `${label} targets ${source.targetSkillRelativePath}, but SKILL.md is missing.`,
|
|
41
|
+
remediation: MANIFEST_SOURCE_REMEDIATION,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|