@agentxm/workspace-lint 0.28.4-bootstrap.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.
Files changed (100) hide show
  1. package/LICENSE +110 -0
  2. package/dist/src/catalog/index.d.ts +31 -0
  3. package/dist/src/catalog/index.js +60 -0
  4. package/dist/src/catalog/workspace/agents-detected-declared.d.ts +23 -0
  5. package/dist/src/catalog/workspace/agents-detected-declared.js +61 -0
  6. package/dist/src/catalog/workspace/agents-recognized.d.ts +23 -0
  7. package/dist/src/catalog/workspace/agents-recognized.js +64 -0
  8. package/dist/src/catalog/workspace/axm-skill-compatible.d.ts +4 -0
  9. package/dist/src/catalog/workspace/axm-skill-compatible.js +51 -0
  10. package/dist/src/catalog/workspace/configured-but-not-installed.d.ts +4 -0
  11. package/dist/src/catalog/workspace/configured-but-not-installed.js +88 -0
  12. package/dist/src/catalog/workspace/desired-state-reconcilable.d.ts +4 -0
  13. package/dist/src/catalog/workspace/desired-state-reconcilable.js +96 -0
  14. package/dist/src/catalog/workspace/display-paths.d.ts +7 -0
  15. package/dist/src/catalog/workspace/display-paths.js +4 -0
  16. package/dist/src/catalog/workspace/helpers/empty.d.ts +18 -0
  17. package/dist/src/catalog/workspace/helpers/empty.js +16 -0
  18. package/dist/src/catalog/workspace/helpers/finding.d.ts +10 -0
  19. package/dist/src/catalog/workspace/helpers/finding.js +11 -0
  20. package/dist/src/catalog/workspace/helpers/source-categorize.d.ts +16 -0
  21. package/dist/src/catalog/workspace/helpers/source-categorize.js +51 -0
  22. package/dist/src/catalog/workspace/hook-ownership-ambiguous.d.ts +4 -0
  23. package/dist/src/catalog/workspace/hook-ownership-ambiguous.js +25 -0
  24. package/dist/src/catalog/workspace/initialized.d.ts +22 -0
  25. package/dist/src/catalog/workspace/initialized.js +58 -0
  26. package/dist/src/catalog/workspace/instructions-agent-supported.d.ts +4 -0
  27. package/dist/src/catalog/workspace/instructions-agent-supported.js +38 -0
  28. package/dist/src/catalog/workspace/instructions-gitignore-current.d.ts +4 -0
  29. package/dist/src/catalog/workspace/instructions-gitignore-current.js +49 -0
  30. package/dist/src/catalog/workspace/instructions-source-present.d.ts +4 -0
  31. package/dist/src/catalog/workspace/instructions-source-present.js +41 -0
  32. package/dist/src/catalog/workspace/instructions-target-current.d.ts +4 -0
  33. package/dist/src/catalog/workspace/instructions-target-current.js +51 -0
  34. package/dist/src/catalog/workspace/instructions-target-stale.d.ts +9 -0
  35. package/dist/src/catalog/workspace/instructions-target-stale.js +41 -0
  36. package/dist/src/catalog/workspace/instructions-target-unowned.d.ts +10 -0
  37. package/dist/src/catalog/workspace/instructions-target-unowned.js +59 -0
  38. package/dist/src/catalog/workspace/knowledge-state-valid.d.ts +5 -0
  39. package/dist/src/catalog/workspace/knowledge-state-valid.js +65 -0
  40. package/dist/src/catalog/workspace/lockfile-valid.d.ts +5 -0
  41. package/dist/src/catalog/workspace/lockfile-valid.js +63 -0
  42. package/dist/src/catalog/workspace/managed-file-unowned.d.ts +4 -0
  43. package/dist/src/catalog/workspace/managed-file-unowned.js +25 -0
  44. package/dist/src/catalog/workspace/mcps-agent-drift.d.ts +4 -0
  45. package/dist/src/catalog/workspace/mcps-agent-drift.js +139 -0
  46. package/dist/src/catalog/workspace/mcps-agent-orphaned.d.ts +4 -0
  47. package/dist/src/catalog/workspace/mcps-agent-orphaned.js +64 -0
  48. package/dist/src/catalog/workspace/mcps-no-secret-literal.d.ts +4 -0
  49. package/dist/src/catalog/workspace/mcps-no-secret-literal.js +67 -0
  50. package/dist/src/catalog/workspace/mcps-shared-target-compatible.d.ts +4 -0
  51. package/dist/src/catalog/workspace/mcps-shared-target-compatible.js +58 -0
  52. package/dist/src/catalog/workspace/mcps-transport-exclusivity.d.ts +4 -0
  53. package/dist/src/catalog/workspace/mcps-transport-exclusivity.js +67 -0
  54. package/dist/src/catalog/workspace/packs-declarations-valid.d.ts +19 -0
  55. package/dist/src/catalog/workspace/packs-declarations-valid.js +110 -0
  56. package/dist/src/catalog/workspace/packs-dependencies-resolved.d.ts +5 -0
  57. package/dist/src/catalog/workspace/packs-dependencies-resolved.js +49 -0
  58. package/dist/src/catalog/workspace/projections-current.d.ts +15 -0
  59. package/dist/src/catalog/workspace/projections-current.js +35 -0
  60. package/dist/src/catalog/workspace/settings-keys-recognized.d.ts +17 -0
  61. package/dist/src/catalog/workspace/settings-keys-recognized.js +90 -0
  62. package/dist/src/catalog/workspace/settings-schema-valid.d.ts +27 -0
  63. package/dist/src/catalog/workspace/settings-schema-valid.js +68 -0
  64. package/dist/src/catalog/workspace/skills-artifacts-correct.d.ts +28 -0
  65. package/dist/src/catalog/workspace/skills-artifacts-correct.js +143 -0
  66. package/dist/src/catalog/workspace/skills-declarations-valid.d.ts +27 -0
  67. package/dist/src/catalog/workspace/skills-declarations-valid.js +102 -0
  68. package/dist/src/catalog/workspace/skills-integrity-valid.d.ts +28 -0
  69. package/dist/src/catalog/workspace/skills-integrity-valid.js +95 -0
  70. package/dist/src/catalog/workspace/skills-lockfile-aligned.d.ts +5 -0
  71. package/dist/src/catalog/workspace/skills-lockfile-aligned.js +64 -0
  72. package/dist/src/catalog/workspace/source-endpoints-aligned.d.ts +5 -0
  73. package/dist/src/catalog/workspace/source-endpoints-aligned.js +75 -0
  74. package/dist/src/catalog/workspace-fixtures/fixture-state.d.ts +8 -0
  75. package/dist/src/catalog/workspace-fixtures/fixture-state.js +120 -0
  76. package/dist/src/catalog/workspace-fixtures/interpret-ops.d.ts +11 -0
  77. package/dist/src/catalog/workspace-fixtures/interpret-ops.js +9 -0
  78. package/dist/src/catalog/workspace-read-model/lint-workspace.d.ts +191 -0
  79. package/dist/src/catalog/workspace-read-model/lint-workspace.js +651 -0
  80. package/dist/src/catalog/workspace-read-model/manifest-json.d.ts +14 -0
  81. package/dist/src/catalog/workspace-read-model/manifest-json.js +27 -0
  82. package/dist/src/catalog/workspace.d.ts +58 -0
  83. package/dist/src/catalog/workspace.js +164 -0
  84. package/dist/src/catalog-contexts.d.ts +106 -0
  85. package/dist/src/catalog-contexts.js +95 -0
  86. package/dist/src/cli.d.ts +239 -0
  87. package/dist/src/cli.js +1077 -0
  88. package/dist/src/index.d.ts +10 -0
  89. package/dist/src/index.js +21 -0
  90. package/dist/src/json-schema.d.ts +121 -0
  91. package/dist/src/json-schema.js +99 -0
  92. package/dist/src/run/errors.d.ts +23 -0
  93. package/dist/src/run/errors.js +21 -0
  94. package/dist/src/run/settings.d.ts +63 -0
  95. package/dist/src/run/settings.js +139 -0
  96. package/dist/src/run/staged-workspace.d.ts +31 -0
  97. package/dist/src/run/staged-workspace.js +273 -0
  98. package/dist/src/workspace-context.d.ts +129 -0
  99. package/dist/src/workspace-context.js +12 -0
  100. package/package.json +55 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * `workspace/skills-integrity-valid` — each installed skill listed in the
3
+ * lockfile is present on disk.
4
+ *
5
+ * For each accepted skill lock entry, verify that its canonical acquired
6
+ * package is present in the scope's canonical root. Missing-package entries
7
+ * each emit one finding.
8
+ * Configured findings are autofixable via `install-skill` with
9
+ * `force: true`; pack-provided implicit findings are advisory because the
10
+ * repair is a pack-level reinstall.
11
+ *
12
+ * This rule owns only the missing-package arm. Package-tree integrity is
13
+ * evaluated by canonical observations against `treeIntegrity`; local edits,
14
+ * including formatter changes, are drift and block affected reads and
15
+ * mutation closures until explicit recovery.
16
+ *
17
+ * A skill whose `sourceHash` is undefined in the lockfile (e.g., git-hosted
18
+ * sources without pinned hash) is not checked.
19
+ *
20
+ * One finding per affected entity.
21
+ *
22
+ * @experimental This API is unstable and may change without notice.
23
+ * @packageDocumentation
24
+ */
25
+ import * as Effect from "effect/Effect";
26
+ import * as Option from "effect/Option";
27
+ import * as Result from "effect/Result";
28
+ import {} from "@agentxm/workspace-state";
29
+ import { EMPTY_LINT_FINDINGS } from "./helpers/empty.js";
30
+ import { lockfileDisplayPath } from "./display-paths.js";
31
+ const RULE_ID = "workspace/skills-integrity-valid";
32
+ const simpleName = (name) => name.split("/").at(-1) ?? name;
33
+ const hasSourceActual = (actual, name) => actual.some((occurrence) => occurrence.key.name === simpleName(name) &&
34
+ (occurrence.origin._tag === "canonical-axm-skill" ||
35
+ occurrence.origin._tag === "external-axm-skill"));
36
+ const integrityFinding = (name, reason, lockfilePath) => ({
37
+ kind: "advisory",
38
+ ruleId: RULE_ID,
39
+ severity: "error",
40
+ message: `Skill '${name}' has an accepted resolution, but ${reason}.`,
41
+ location: { file: lockfilePath },
42
+ });
43
+ /**
44
+ * Presence probe for the skill's acquired package tree. Integrity drift is a
45
+ * separate canonical-observation fact, so this helper owns only absence.
46
+ */
47
+ const integrityReason = (lockEntry, probeExists) => {
48
+ if (!probeExists) {
49
+ return Option.some("its installed source directory is missing");
50
+ }
51
+ return Option.none();
52
+ };
53
+ const collectIntegrityFindings = (desiredNames, probes, lockfilePath) => {
54
+ const findings = [];
55
+ for (const { name, entry, exists } of probes) {
56
+ if (!desiredNames.has(name))
57
+ continue;
58
+ const reason = integrityReason(entry, exists);
59
+ if (Option.isNone(reason)) {
60
+ continue;
61
+ }
62
+ findings.push(integrityFinding(name, reason.value, lockfilePath));
63
+ }
64
+ return findings;
65
+ };
66
+ export const skillsIntegrityValidRule = {
67
+ id: RULE_ID,
68
+ description: "Installed skills listed in the lockfile are present on disk.",
69
+ kind: "advisory",
70
+ severity: "error",
71
+ check: (context) => Effect.gen(function* () {
72
+ const scoped = context.workspace;
73
+ const lockfileResult = yield* Effect.result(scoped.state.lockfile);
74
+ if (Result.isFailure(lockfileResult) || context.health === undefined) {
75
+ return EMPTY_LINT_FINDINGS;
76
+ }
77
+ const graphResult = yield* Effect.result(context.health.desiredState);
78
+ if (Result.isFailure(graphResult)) {
79
+ return EMPTY_LINT_FINDINGS;
80
+ }
81
+ const lockOption = lockfileResult.success;
82
+ if (Option.isNone(lockOption)) {
83
+ return EMPTY_LINT_FINDINGS;
84
+ }
85
+ const actual = yield* scoped.skills.actual;
86
+ const probes = Object.entries(lockOption.value.skills).map(([name, entry]) => ({
87
+ name,
88
+ entry,
89
+ exists: hasSourceActual(actual, name),
90
+ }));
91
+ const desiredNames = new Set(graphResult.success.nodes.filter((node) => node.type === "skill").map((node) => node.name));
92
+ return collectIntegrityFindings(desiredNames, probes, lockfileDisplayPath(context.subject.scope));
93
+ }),
94
+ };
95
+ //# sourceMappingURL=skills-integrity-valid.js.map
@@ -0,0 +1,5 @@
1
+ /** Reports alignment between desired external Skills and accepted resolutions. */
2
+ import type { WorkspaceRuleContext } from "../../workspace-context.js";
3
+ import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
4
+ export declare const skillsLockfileAlignedRule: AdvisoryRule<WorkspaceRuleContext>;
5
+ //# sourceMappingURL=skills-lockfile-aligned.d.ts.map
@@ -0,0 +1,64 @@
1
+ /** Reports alignment between desired external Skills and accepted resolutions. */
2
+ import * as Effect from "effect/Effect";
3
+ import * as Option from "effect/Option";
4
+ import * as Result from "effect/Result";
5
+ import { parseRegistrySourceRef } from "@agentxm/extension-model/unstable/extensions/registry-source";
6
+ import {} from "@agentxm/workspace-state";
7
+ import { isWorkspaceSourceLocator } from "@agentxm/extension-model/unstable/sources/workspace";
8
+ import { versionSatisfiesRange } from "@agentxm/extension-model/unstable/version-constraints";
9
+ import { lockfileDisplayPath } from "./display-paths.js";
10
+ const RULE_ID = "workspace/skills-lockfile-aligned";
11
+ const finding = (message, path) => ({
12
+ kind: "advisory",
13
+ ruleId: RULE_ID,
14
+ severity: "error",
15
+ message,
16
+ location: { file: path },
17
+ });
18
+ const externalSkills = (nodes) => nodes.filter((node) => node.type === "skill" && node.source !== undefined && !isWorkspaceSourceLocator(node.source));
19
+ const collectFindings = (nodes, lockfile, lockfilePath) => {
20
+ const desired = externalSkills(nodes);
21
+ const desiredNames = new Set(desired.map((node) => node.name));
22
+ const findings = [];
23
+ for (const node of desired) {
24
+ if (node.source === undefined)
25
+ continue;
26
+ const accepted = lockfile.skills[node.name];
27
+ if (accepted === undefined) {
28
+ findings.push(finding(`Skill '${node.name}' has desired external content but no accepted resolution.`, lockfilePath));
29
+ continue;
30
+ }
31
+ if (accepted.type !== "registry")
32
+ continue;
33
+ const parsed = parseRegistrySourceRef(node.source);
34
+ const constraint = parsed?.versionRange;
35
+ if (constraint !== undefined && !versionSatisfiesRange(accepted.resolvedVersion, constraint)) {
36
+ findings.push(finding(`Skill '${node.name}' accepts Registry version ${accepted.resolvedVersion}, which does not satisfy desired constraint ${constraint}.`, lockfilePath));
37
+ }
38
+ }
39
+ for (const name of Object.keys(lockfile.skills)) {
40
+ if (!desiredNames.has(name)) {
41
+ findings.push(finding(`Skill '${name}' has an accepted resolution but is not desired.`, lockfilePath));
42
+ }
43
+ }
44
+ return findings;
45
+ };
46
+ export const skillsLockfileAlignedRule = {
47
+ id: RULE_ID,
48
+ description: "Desired external Skills and accepted resolutions stay aligned.",
49
+ kind: "advisory",
50
+ severity: "error",
51
+ check: (context) => Effect.gen(function* () {
52
+ if (context.health === undefined)
53
+ return [];
54
+ const graph = yield* Effect.result(context.health.desiredState);
55
+ const lockfile = yield* Effect.result(context.workspace.state.lockfile);
56
+ if (Result.isFailure(graph) ||
57
+ Result.isFailure(lockfile) ||
58
+ Option.isNone(lockfile.success)) {
59
+ return [];
60
+ }
61
+ return collectFindings(graph.success.nodes, lockfile.success.value, lockfileDisplayPath(context.subject.scope));
62
+ }),
63
+ };
64
+ //# sourceMappingURL=skills-lockfile-aligned.js.map
@@ -0,0 +1,5 @@
1
+ /** Reports configured source hosts that diverge from accepted lock authority. */
2
+ import type { WorkspaceRuleContext } from "../../workspace-context.js";
3
+ import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
4
+ export declare const sourceEndpointsAlignedRule: AdvisoryRule<WorkspaceRuleContext>;
5
+ //# sourceMappingURL=source-endpoints-aligned.d.ts.map
@@ -0,0 +1,75 @@
1
+ /** Reports configured source hosts that diverge from accepted lock authority. */
2
+ import * as Effect from "effect/Effect";
3
+ import * as Option from "effect/Option";
4
+ import * as Result from "effect/Result";
5
+ import { settingsDisplayPath } from "./display-paths.js";
6
+ const RULE_ID = "workspace/source-endpoints-aligned";
7
+ const configuredEndpoint = (source) => source.type === "registry" ? source.location : source.url;
8
+ const lockedEntries = (lockfile) => [
9
+ ...Object.entries(lockfile.skills).map(([name, entry]) => ({
10
+ extension: `skill:${name}`,
11
+ entry,
12
+ })),
13
+ ...Object.entries(lockfile.mcpServers ?? {}).map(([name, entry]) => ({
14
+ extension: `mcp-server:${name}`,
15
+ entry,
16
+ })),
17
+ ...Object.entries(lockfile.subagents ?? {}).map(([name, entry]) => ({
18
+ extension: `subagent:${name}`,
19
+ entry,
20
+ })),
21
+ ...Object.entries(lockfile.rules ?? {}).map(([name, entry]) => ({
22
+ extension: `rule:${name}`,
23
+ entry,
24
+ })),
25
+ ...Object.entries(lockfile.hooks ?? {}).map(([name, entry]) => ({
26
+ extension: `hook:${name}`,
27
+ entry,
28
+ })),
29
+ ...Object.entries(lockfile.knowledge ?? {}).map(([name, entry]) => ({
30
+ extension: `knowledge:${name}`,
31
+ entry,
32
+ })),
33
+ ...Object.entries(lockfile.packs ?? {}).map(([name, entry]) => ({
34
+ extension: `pack:${name}`,
35
+ entry,
36
+ })),
37
+ ];
38
+ const collectFindings = (lockfile, configuredSources, settingsPath) => {
39
+ const findings = [];
40
+ for (const { extension, entry } of lockedEntries(lockfile)) {
41
+ if (entry.type === "local" || entry.type === "git")
42
+ continue;
43
+ const configured = configuredSources.find((source) => source.name === entry.sourceName);
44
+ if (configured === undefined)
45
+ continue;
46
+ const endpoint = configuredEndpoint(configured);
47
+ if (configured.type === entry.sourceType && endpoint.href === entry.endpoint.href)
48
+ continue;
49
+ findings.push({
50
+ kind: "advisory",
51
+ ruleId: RULE_ID,
52
+ severity: "error",
53
+ message: `Accepted resolution '${extension}' binds source '${entry.sourceName}' to ${entry.sourceType} ${entry.endpoint.href}, but the configured source now resolves to ${configured.type} ${endpoint.href}. Use an explicit source transition before syncing.`,
54
+ location: { file: settingsPath },
55
+ });
56
+ }
57
+ return findings;
58
+ };
59
+ export const sourceEndpointsAlignedRule = {
60
+ id: RULE_ID,
61
+ description: "Configured source endpoints remain aligned with accepted lock authority.",
62
+ kind: "advisory",
63
+ severity: "error",
64
+ check: (context) => Effect.gen(function* () {
65
+ const lockfile = yield* Effect.result(context.workspace.state.lockfile);
66
+ const sources = yield* Effect.result(context.workspace.sourceHosts.declared);
67
+ if (Result.isFailure(lockfile) ||
68
+ Option.isNone(lockfile.success) ||
69
+ Result.isFailure(sources)) {
70
+ return [];
71
+ }
72
+ return collectFindings(lockfile.success.value, sources.success, settingsDisplayPath(context.subject.scope));
73
+ }),
74
+ };
75
+ //# sourceMappingURL=source-endpoints-aligned.js.map
@@ -0,0 +1,8 @@
1
+ import type { FixtureSpec, ScopeFiles } from "@agentxm/workspace-state";
2
+ import type { WorkspaceState } from "./interpret-ops.js";
3
+ export declare const scopeFilesFromWorkspaceState: (state: WorkspaceState) => ScopeFiles;
4
+ export declare const fixtureSpecFromWorkspaceState: (state: WorkspaceState, scope: "project" | "user", roots: {
5
+ readonly workspaceRoot: string;
6
+ readonly userHome: string;
7
+ }) => FixtureSpec;
8
+ //# sourceMappingURL=fixture-state.d.ts.map
@@ -0,0 +1,120 @@
1
+ import { AGENTS } from "@agentxm/extension-model/unstable/agents/registry";
2
+ const pathSegments = (relativePath) => relativePath.split("/").filter((segment) => segment.length > 0);
3
+ const firstPathSegment = (relativePath) => pathSegments(relativePath)[0];
4
+ const agentRootForSkillsDir = (skillsDir) => firstPathSegment(skillsDir);
5
+ const relativeUnderAgentRoot = (agentRoot, relativePath) => {
6
+ if (relativePath === agentRoot)
7
+ return "";
8
+ const prefix = `${agentRoot}/`;
9
+ if (!relativePath.startsWith(prefix))
10
+ return undefined;
11
+ return relativePath.slice(prefix.length);
12
+ };
13
+ const skillNameFromPath = (skillsDir, relativePath) => {
14
+ const prefix = `${skillsDir}/`;
15
+ if (!relativePath.startsWith(prefix))
16
+ return undefined;
17
+ const afterSkillsDir = relativePath.slice(prefix.length);
18
+ return firstPathSegment(afterSkillsDir);
19
+ };
20
+ const literalFileFor = (relativePath) => {
21
+ const segments = pathSegments(relativePath);
22
+ const name = segments.at(-2) ?? segments.at(-1) ?? "fixture";
23
+ const lower = relativePath.toLowerCase();
24
+ if (lower.endsWith(".md"))
25
+ return `# ${name}\n`;
26
+ if (lower.endsWith(".json"))
27
+ return "{}\n";
28
+ if (lower.endsWith(".yaml") || lower.endsWith(".yml"))
29
+ return "{}\n";
30
+ return "";
31
+ };
32
+ const addTreeFile = (tree, relativePath, contents = literalFileFor(relativePath)) => {
33
+ tree[relativePath] = contents;
34
+ };
35
+ const addAgentSkillArtifact = (agentDirs, agentId, relativePath) => {
36
+ const agent = AGENTS[agentId];
37
+ const skills = agent.skills;
38
+ if (skills === undefined)
39
+ return;
40
+ const agentRoot = agentRootForSkillsDir(skills.dir);
41
+ if (agentRoot === undefined)
42
+ return;
43
+ const relative = relativeUnderAgentRoot(agentRoot, relativePath);
44
+ if (relative === undefined)
45
+ return;
46
+ const skillName = skillNameFromPath(skills.dir, relativePath);
47
+ const materializedPath = skillName === undefined
48
+ ? relative
49
+ : `${skills.dir.slice(agentRoot.length + 1)}/${skillName}/SKILL.md`;
50
+ if (materializedPath.length === 0)
51
+ return;
52
+ const tree = agentDirs[agentId] ?? {};
53
+ agentDirs[agentId] = tree;
54
+ addTreeFile(tree, materializedPath);
55
+ };
56
+ const isKnownAgentId = (id) => Object.hasOwn(AGENTS, id);
57
+ const fileSpecFor = (raw) => {
58
+ if (raw === undefined)
59
+ return undefined;
60
+ if (typeof raw !== "object" || raw === null) {
61
+ throw new Error(`Fixture state expected an object for settings/lockfile; got ${typeof raw}`);
62
+ }
63
+ return { _tag: "valid", contents: raw };
64
+ };
65
+ export const scopeFilesFromWorkspaceState = (state) => {
66
+ const axmExtensions = {};
67
+ const agentDirs = {};
68
+ const agentSettings = {};
69
+ const agents = Object.values(AGENTS);
70
+ const addExistingPath = (relativePath) => {
71
+ const acquiredPrefix = "agent_extensions/";
72
+ if (relativePath.startsWith(acquiredPrefix)) {
73
+ addTreeFile(axmExtensions, relativePath.slice(acquiredPrefix.length));
74
+ return;
75
+ }
76
+ const axmPrefix = "agent_extensions/";
77
+ if (relativePath.startsWith(axmPrefix)) {
78
+ addTreeFile(axmExtensions, relativePath.slice(axmPrefix.length));
79
+ return;
80
+ }
81
+ for (const agent of agents) {
82
+ if (agent.skills !== undefined &&
83
+ skillNameFromPath(agent.skills.dir, relativePath) !== undefined) {
84
+ addAgentSkillArtifact(agentDirs, agent.id, relativePath);
85
+ }
86
+ }
87
+ };
88
+ for (const relativePath of state.existingPaths) {
89
+ addExistingPath(relativePath);
90
+ }
91
+ for (const [dir, entries] of state.listings) {
92
+ for (const entry of entries) {
93
+ addExistingPath(`${dir}/${entry}`);
94
+ }
95
+ }
96
+ for (const agentId of state.detectedProjectAgents) {
97
+ if (isKnownAgentId(agentId)) {
98
+ agentSettings[agentId] = { _tag: "valid", contents: {} };
99
+ }
100
+ }
101
+ const settings = fileSpecFor(state.settings);
102
+ const lockfile = fileSpecFor(state.lockfile);
103
+ return {
104
+ ...(settings === undefined ? {} : { settings }),
105
+ ...(lockfile === undefined ? {} : { lockfile }),
106
+ ...(Object.keys(axmExtensions).length === 0 ? {} : { axmExtensions }),
107
+ ...(Object.keys(agentDirs).length === 0 ? {} : { agentDirs }),
108
+ ...(Object.keys(agentSettings).length === 0 ? {} : { agentSettings }),
109
+ };
110
+ };
111
+ export const fixtureSpecFromWorkspaceState = (state, scope, roots) => {
112
+ const scopeFiles = scopeFilesFromWorkspaceState(state);
113
+ return {
114
+ workspaceRoot: roots.workspaceRoot,
115
+ userHome: roots.userHome,
116
+ ...(scope === "project" ? { project: scopeFiles } : {}),
117
+ ...(scope === "user" ? { user: scopeFiles } : {}),
118
+ };
119
+ };
120
+ //# sourceMappingURL=fixture-state.js.map
@@ -0,0 +1,11 @@
1
+ /** Mutable workspace-state fixture shared by read-only lint rule tests. */
2
+ export interface WorkspaceState {
3
+ settings: unknown;
4
+ lockfile: unknown;
5
+ readonly existingPaths: Set<string>;
6
+ readonly writablePaths: Set<string>;
7
+ readonly listings: Map<string, Array<string>>;
8
+ readonly detectedProjectAgents: Set<string>;
9
+ }
10
+ export declare const emptyWorkspaceState: () => WorkspaceState;
11
+ //# sourceMappingURL=interpret-ops.d.ts.map
@@ -0,0 +1,9 @@
1
+ export const emptyWorkspaceState = () => ({
2
+ settings: undefined,
3
+ lockfile: undefined,
4
+ existingPaths: new Set(),
5
+ writablePaths: new Set(),
6
+ listings: new Map(),
7
+ detectedProjectAgents: new Set(),
8
+ });
9
+ //# sourceMappingURL=interpret-ops.js.map
@@ -0,0 +1,191 @@
1
+ /**
2
+ * `LintWorkspace` — single helper that produces both the per-rule
3
+ * `WorkspaceRuleContext` and the flat `LintWorkspaceView` projection a lint
4
+ * run needs, sharing one `WorkspaceReadModelLive` setup.
5
+ *
6
+ * `LintWorkspaceView` exposes `installedSkills: InstalledSkillInfo[]` and
7
+ * `installedPacks: InstalledPackInfo[]` with per-provenance `displayRoot`s:
8
+ *
9
+ * Registry-installed native skill: `agent_extensions/<source>/<@owner>/skills/<name>/src/`
10
+ * Portable acquired skill: `agent_extensions/<source>/<source-full-name>/`
11
+ * Registry pack: `agent_extensions/<source>/<@owner>/packs/<name>/`
12
+ * (NO `src/` — matches the on-disk layout.)
13
+ *
14
+ * The companion `WorkspaceRuleContext` is scoped to project or user.
15
+ * User-scope resolution uses `.axm/workspace/` under `$AXM_USER_HOME` when set,
16
+ * otherwise under `$HOME`; a follow-up owns the broader XDG story.
17
+ *
18
+ * @experimental This API is unstable and may change without notice.
19
+ * @packageDocumentation
20
+ */
21
+ import * as Effect from "effect/Effect";
22
+ import * as FileSystem from "effect/FileSystem";
23
+ import * as Option from "effect/Option";
24
+ import * as Path from "effect/Path";
25
+ import type { LockfileReadError, SettingsReadError, WorkspaceRootEscape } from "@agentxm/workspace-state";
26
+ import type { HookRuleContext, KnowledgeRuleContext, McpServerRuleContext, RuleRuleContext, SubagentRuleContext } from "@agentxm/registry-protocol/unstable/lint/context";
27
+ import type { WorkspaceProjectionsAccessor, WorkspaceRuleContext } from "../../workspace-context.js";
28
+ import type { InstalledSkillInfo } from "@agentxm/registry-protocol/unstable/lint/catalog/skill-accessor/contexts";
29
+ import type { InstalledPackInfo } from "@agentxm/registry-protocol/unstable/lint/catalog/pack-accessor/contexts";
30
+ import type { SkillLockEntry } from "@agentxm/workspace-state";
31
+ import type { KnowledgeInspection } from "@agentxm/registry-protocol/unstable/knowledge/okf";
32
+ import type { AxmSkillCompatibilityPolicyService } from "@agentxm/extension-workspace";
33
+ import type { Handle } from "@agentxm/extension-model/unstable/extensions/handle";
34
+ /**
35
+ * Flat projection of the workspace read model that the lint accessor builders
36
+ * consume. `buildSkillRuleContexts` reads `installedSkills`;
37
+ * `buildPackRuleContexts` reads `installedPacks`.
38
+ *
39
+ * @experimental This API is unstable and may change without notice.
40
+ */
41
+ export interface LintWorkspaceView {
42
+ readonly installedSkills: ReadonlyArray<InstalledSkillInfo>;
43
+ readonly installedPacks: ReadonlyArray<InstalledPackInfo>;
44
+ readonly subagentContexts: ReadonlyArray<SubagentRuleContext>;
45
+ readonly mcpServerContexts: ReadonlyArray<McpServerRuleContext>;
46
+ readonly hookContexts: ReadonlyArray<HookRuleContext>;
47
+ readonly ruleContexts: ReadonlyArray<RuleRuleContext>;
48
+ readonly knowledgeContexts: ReadonlyArray<KnowledgeRuleContext>;
49
+ }
50
+ /**
51
+ * Argument shape for `buildLintWorkspace`.
52
+ *
53
+ * @experimental This API is unstable and may change without notice.
54
+ */
55
+ export interface BuildLintWorkspaceArgs {
56
+ readonly platform: {
57
+ readonly fs: FileSystem.FileSystem;
58
+ readonly path: Path.Path;
59
+ };
60
+ readonly workspaceRoot: string;
61
+ /**
62
+ * User home directory used by {@link makeWorkspaceReadModel} to validate
63
+ * the alternate scope's root against `allowedRoot`. Supplied even when
64
+ * only one scope is queried so root-escape checks stay symmetric.
65
+ */
66
+ readonly userHome: string;
67
+ readonly scope: "project" | "user";
68
+ /** The filesystem is a materialized Git-index snapshot, not the live workspace. */
69
+ readonly gitIndexView?: boolean;
70
+ /**
71
+ * Optional `displayRoot` override for the `WorkspaceRuleContext`. Defaults
72
+ * to `""` (accessor-relative paths render under the workspace root).
73
+ */
74
+ readonly displayRoot?: string;
75
+ /** Runtime-pinned evaluator used by status and the compatibility lint rule. */
76
+ readonly axmSkillCompatibilityPolicy?: AxmSkillCompatibilityPolicyService;
77
+ /** Caller-bound effective workspace owner accessor. */
78
+ readonly owner?: Effect.Effect<Option.Option<Handle>>;
79
+ /** Caller-bound read-back currency for aggregate managed output units. */
80
+ readonly projections?: WorkspaceProjectionsAccessor;
81
+ /** Test seam for proving one package inspection per selected bundle. */
82
+ readonly inspectKnowledge?: (packageRoot: string) => Effect.Effect<{
83
+ readonly inspection: KnowledgeInspection;
84
+ }, unknown, FileSystem.FileSystem | Path.Path>;
85
+ }
86
+ /**
87
+ * The combined output of `buildLintWorkspace`: the per-rule
88
+ * `WorkspaceRuleContext` plus the flat `LintWorkspaceView`.
89
+ *
90
+ * @experimental This API is unstable and may change without notice.
91
+ */
92
+ export interface LintWorkspace {
93
+ readonly rule: WorkspaceRuleContext;
94
+ readonly view: LintWorkspaceView;
95
+ }
96
+ /**
97
+ * Build the per-rule `WorkspaceRuleContext` and the flat `LintWorkspaceView`
98
+ * via a single {@link makeWorkspaceReadModel} invocation.
99
+ *
100
+ * `WorkspaceRootEscape` is surfaced in the error channel by the factory when
101
+ * `workspaceRoot` or `userHome` escape the filesystem root.
102
+ *
103
+ * @experimental This API is unstable and may change without notice.
104
+ */
105
+ export declare const buildLintWorkspace: (args: BuildLintWorkspaceArgs) => Effect.Effect<LintWorkspace, WorkspaceRootEscape | SettingsReadError | LockfileReadError>;
106
+ /**
107
+ * Compute the `displayRoot` for a registry-installed native skill.
108
+ *
109
+ * @experimental This API is unstable and may change without notice.
110
+ */
111
+ export declare const registryNativeSkillDisplayRoot: (scope: "project" | "user", sourceName: string, owner: string, name: string) => string;
112
+ /**
113
+ * Compute the content `displayRoot` for a source-qualified acquired skill.
114
+ *
115
+ * @experimental This API is unstable and may change without notice.
116
+ */
117
+ export declare const acquiredSkillDisplayRoot: (scope: "project" | "user", entry: SkillLockEntry, name: string) => string;
118
+ /**
119
+ * Compute the `displayRoot` for a registry-installed pack.
120
+ *
121
+ * **No `src/` segment** — matches the on-disk layout at
122
+ * `packages/workspace-state/src/workspace/pack-paths.ts#computePackPathsForLayout`.
123
+ *
124
+ * @experimental This API is unstable and may change without notice.
125
+ */
126
+ export declare const registryPackDisplayRoot: (scope: "project" | "user", sourceName: string, owner: string, name: string) => string;
127
+ /**
128
+ * Options for building an `InstalledSkillInfo` for a registry-installed
129
+ * native skill. `owner` and `name` pin the on-disk layout.
130
+ */
131
+ export interface BuildInstalledSkillInfoNativeArgs {
132
+ readonly platform: {
133
+ readonly fs: FileSystem.FileSystem;
134
+ readonly path: Path.Path;
135
+ };
136
+ readonly workspaceRoot: string;
137
+ readonly scope: "project" | "user";
138
+ readonly sourceName: string;
139
+ readonly owner: string;
140
+ readonly name: string;
141
+ readonly skillJson: unknown;
142
+ }
143
+ /**
144
+ * Build an `InstalledSkillInfo` rooted at `agent_extensions/<owner>/skills/<name>/src/`.
145
+ *
146
+ * @experimental This API is unstable and may change without notice.
147
+ */
148
+ export declare const buildNativeInstalledSkillInfo: (args: BuildInstalledSkillInfoNativeArgs) => InstalledSkillInfo;
149
+ /**
150
+ * Options for building an `InstalledSkillInfo` from an accepted acquired skill.
151
+ */
152
+ export interface BuildAcquiredInstalledSkillInfoArgs {
153
+ readonly platform: {
154
+ readonly fs: FileSystem.FileSystem;
155
+ readonly path: Path.Path;
156
+ };
157
+ readonly workspaceRoot: string;
158
+ readonly scope: "project" | "user";
159
+ readonly lockEntry: SkillLockEntry;
160
+ readonly name: string;
161
+ }
162
+ /**
163
+ * Build an `InstalledSkillInfo` rooted at its exact accepted source-qualified path.
164
+ *
165
+ * @experimental This API is unstable and may change without notice.
166
+ */
167
+ export declare const buildAcquiredInstalledSkillInfo: (args: BuildAcquiredInstalledSkillInfoArgs) => InstalledSkillInfo;
168
+ /**
169
+ * Options for building an `InstalledPackInfo` for a registry-installed pack.
170
+ */
171
+ export interface BuildInstalledPackInfoArgs {
172
+ readonly platform: {
173
+ readonly fs: FileSystem.FileSystem;
174
+ readonly path: Path.Path;
175
+ };
176
+ readonly workspaceRoot: string;
177
+ readonly scope: "project" | "user";
178
+ readonly sourceName: string;
179
+ readonly owner: string;
180
+ readonly name: string;
181
+ readonly packJson: unknown;
182
+ }
183
+ /**
184
+ * Build an `InstalledPackInfo` rooted at `agent_extensions/<owner>/packs/<name>/`.
185
+ *
186
+ * **No `src/` segment** — matches the on-disk pack layout.
187
+ *
188
+ * @experimental This API is unstable and may change without notice.
189
+ */
190
+ export declare const buildInstalledPackInfo: (args: BuildInstalledPackInfoArgs) => InstalledPackInfo;
191
+ //# sourceMappingURL=lint-workspace.d.ts.map