@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,273 @@
1
+ /**
2
+ * Materialize the repository index as an isolated, read-only lint workspace.
3
+ *
4
+ * Git checkout commands apply working-tree filters and line-ending conversion,
5
+ * so they cannot represent the index byte-for-byte. This module enumerates
6
+ * stage-0 entries, reads every unique blob through one `git cat-file --batch`
7
+ * process, and recreates the indexed modes in a scoped temporary directory.
8
+ */
9
+ import * as Effect from "effect/Effect";
10
+ import * as FileSystem from "effect/FileSystem";
11
+ import * as Option from "effect/Option";
12
+ import * as Path from "effect/Path";
13
+ import * as Stream from "effect/Stream";
14
+ import { ChildProcess } from "effect/unstable/process";
15
+ import { createHash } from "node:crypto";
16
+ import { LintStagingFailed } from "./errors.js";
17
+ import { findGitRoot } from "@agentxm/extension-sources";
18
+ // Deliberately duplicated from the CLI-destined environment module: a feature
19
+ // package may not depend on application utilities, and this snapshot helper is
20
+ // within the sanctioned duplication budget for small pure functions.
21
+ // eslint-disable-next-line no-restricted-properties -- Centralized env var access point; callers filter the snapshot before passing it to child processes
22
+ const readEnvironment = () => ({ ...process.env });
23
+ const GIT_REPOSITORY_ENVIRONMENT_VARIABLES = new Set([
24
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
25
+ "GIT_COMMON_DIR",
26
+ "GIT_CONFIG",
27
+ "GIT_CONFIG_COUNT",
28
+ "GIT_CONFIG_PARAMETERS",
29
+ "GIT_DIR",
30
+ "GIT_GRAFT_FILE",
31
+ "GIT_IMPLICIT_WORK_TREE",
32
+ "GIT_INDEX_FILE",
33
+ "GIT_NO_REPLACE_OBJECTS",
34
+ "GIT_OBJECT_DIRECTORY",
35
+ "GIT_PREFIX",
36
+ "GIT_REPLACE_REF_BASE",
37
+ "GIT_SHALLOW_FILE",
38
+ "GIT_WORK_TREE",
39
+ ]);
40
+ /** Remove repository-local variables inherited from Git hook processes. */
41
+ export const isolatedGitEnvironment = () => ({
42
+ ...Object.fromEntries(Object.entries(readEnvironment()).filter((entry) => entry[1] !== undefined && !GIT_REPOSITORY_ENVIRONMENT_VARIABLES.has(entry[0]))),
43
+ GIT_TERMINAL_PROMPT: "0",
44
+ GIT_LFS_SKIP_SMUDGE: "1",
45
+ });
46
+ const stagedSnapshotError = (args) => new LintStagingFailed({
47
+ category: "validation",
48
+ title: args.title ?? "Git index snapshot failed",
49
+ detail: args.detail,
50
+ ...(args.cause === undefined ? {} : { cause: args.cause }),
51
+ });
52
+ const parseIndexEntries = (raw) => Effect.gen(function* () {
53
+ const entries = [];
54
+ for (const record of raw.split("\0")) {
55
+ if (record.length === 0)
56
+ continue;
57
+ const separator = record.indexOf("\t");
58
+ if (separator < 0) {
59
+ return yield* stagedSnapshotError({ detail: "Git returned an invalid index entry" });
60
+ }
61
+ const [mode, objectId, stage] = record.slice(0, separator).split(" ");
62
+ const entryPath = record.slice(separator + 1);
63
+ if (mode === undefined ||
64
+ objectId === undefined ||
65
+ stage === undefined ||
66
+ entryPath.length === 0 ||
67
+ !/^[0-9a-f]+$/.test(objectId)) {
68
+ return yield* stagedSnapshotError({ detail: "Git returned an invalid index entry" });
69
+ }
70
+ if (stage !== "0") {
71
+ return yield* stagedSnapshotError({
72
+ detail: "The Git index contains unmerged entries. Resolve the merge conflicts and stage the result before running axm lint --view git-index.",
73
+ });
74
+ }
75
+ entries.push({ mode, objectId, path: entryPath });
76
+ }
77
+ return entries;
78
+ });
79
+ const collectBytes = (stream) => Effect.gen(function* () {
80
+ const chunks = yield* Stream.runCollect(stream);
81
+ const totalLength = chunks.reduce((total, chunk) => total + chunk.length, 0);
82
+ const result = new Uint8Array(totalLength);
83
+ let offset = 0;
84
+ for (const chunk of chunks) {
85
+ result.set(chunk, offset);
86
+ offset += chunk.length;
87
+ }
88
+ return result;
89
+ });
90
+ const collectText = (stream) => collectBytes(stream).pipe(Effect.map((bytes) => new TextDecoder().decode(bytes)));
91
+ const parseBatchBlobs = (bytes, expectedObjectIds) => Effect.gen(function* () {
92
+ const blobs = new Map();
93
+ const decoder = new TextDecoder();
94
+ let cursor = 0;
95
+ while (cursor < bytes.length) {
96
+ const headerEnd = bytes.indexOf(10, cursor);
97
+ if (headerEnd < 0) {
98
+ return yield* stagedSnapshotError({ detail: "Git returned an incomplete blob header" });
99
+ }
100
+ const header = decoder.decode(bytes.slice(cursor, headerEnd));
101
+ const [objectId, objectType, sizeText] = header.split(" ");
102
+ const size = sizeText === undefined ? Number.NaN : Number.parseInt(sizeText, 10);
103
+ if (objectId === undefined ||
104
+ objectType !== "blob" ||
105
+ !expectedObjectIds.has(objectId) ||
106
+ !Number.isSafeInteger(size) ||
107
+ size < 0) {
108
+ return yield* stagedSnapshotError({
109
+ detail: `Git returned an invalid blob header: ${header}`,
110
+ });
111
+ }
112
+ const contentStart = headerEnd + 1;
113
+ const contentEnd = contentStart + size;
114
+ if (contentEnd >= bytes.length || bytes[contentEnd] !== 10) {
115
+ return yield* stagedSnapshotError({ detail: "Git returned incomplete staged blob data" });
116
+ }
117
+ blobs.set(objectId, bytes.slice(contentStart, contentEnd));
118
+ cursor = contentEnd + 1;
119
+ }
120
+ if (blobs.size !== expectedObjectIds.size) {
121
+ return yield* stagedSnapshotError({ detail: "Git did not return every staged blob" });
122
+ }
123
+ return blobs;
124
+ });
125
+ const readIndexEntries = (gitRoot) => Effect.gen(function* () {
126
+ const handle = yield* ChildProcess.make("git", ["ls-files", "--stage", "-z"], {
127
+ cwd: gitRoot,
128
+ env: isolatedGitEnvironment(),
129
+ extendEnv: false,
130
+ });
131
+ const result = yield* Effect.all({
132
+ stdout: collectBytes(handle.stdout),
133
+ stderr: collectText(handle.stderr),
134
+ exitCode: handle.exitCode,
135
+ }, { concurrency: "unbounded" });
136
+ if (result.exitCode !== 0) {
137
+ return yield* stagedSnapshotError({
138
+ detail: `AXM could not enumerate the Git index${result.stderr.trim().length === 0 ? "" : `: ${result.stderr.trim()}`}`,
139
+ });
140
+ }
141
+ const fingerprint = `sha256:${createHash("sha256").update(result.stdout).digest("hex")}`;
142
+ const raw = new TextDecoder().decode(result.stdout);
143
+ return { entries: yield* parseIndexEntries(raw), fingerprint };
144
+ }).pipe(Effect.mapError((cause) => cause._tag === "LintStagingFailed"
145
+ ? cause
146
+ : stagedSnapshotError({
147
+ detail: "AXM could not enumerate the Git index. Confirm Git can read the repository.",
148
+ cause,
149
+ })));
150
+ const readIndexBlobs = (args) => {
151
+ if (args.objectIds.length === 0) {
152
+ return Effect.succeed(new Map());
153
+ }
154
+ const expectedObjectIds = new Set(args.objectIds);
155
+ const input = new TextEncoder().encode(`${args.objectIds.join("\n")}\n`);
156
+ const process = Effect.gen(function* () {
157
+ const handle = yield* ChildProcess.make("git", ["cat-file", "--batch"], {
158
+ cwd: args.gitRoot,
159
+ env: isolatedGitEnvironment(),
160
+ extendEnv: false,
161
+ stdin: Stream.make(input),
162
+ });
163
+ return yield* Effect.all({
164
+ stdout: collectBytes(handle.stdout),
165
+ stderr: collectText(handle.stderr),
166
+ exitCode: handle.exitCode,
167
+ }, { concurrency: "unbounded" });
168
+ }).pipe(Effect.mapError((cause) => stagedSnapshotError({ detail: "Git could not read the staged blob data", cause })));
169
+ return Effect.gen(function* () {
170
+ const result = yield* process;
171
+ if (result.exitCode !== 0) {
172
+ return yield* stagedSnapshotError({
173
+ detail: `Git could not read the staged blob data${result.stderr.trim().length === 0 ? "" : `: ${result.stderr.trim()}`}`,
174
+ });
175
+ }
176
+ return yield* parseBatchBlobs(result.stdout, expectedObjectIds);
177
+ });
178
+ };
179
+ const writeIndexEntry = (args) => Effect.gen(function* () {
180
+ const fs = yield* FileSystem.FileSystem;
181
+ const path = yield* Path.Path;
182
+ const destination = path.resolve(args.workspaceRoot, args.entry.path);
183
+ const relative = path.relative(args.workspaceRoot, destination);
184
+ if (relative.length === 0 || relative.startsWith("..") || path.isAbsolute(relative)) {
185
+ return yield* stagedSnapshotError({
186
+ detail: `Git index path escapes the staged workspace: ${args.entry.path}`,
187
+ });
188
+ }
189
+ const mapFileError = (cause) => new LintStagingFailed({
190
+ category: "internal",
191
+ detail: `Failed to materialize staged path: ${args.entry.path}`,
192
+ cause,
193
+ });
194
+ if (args.entry.mode === "160000") {
195
+ yield* fs.makeDirectory(destination, { recursive: true }).pipe(Effect.mapError(mapFileError));
196
+ return;
197
+ }
198
+ const blob = args.blobs.get(args.entry.objectId);
199
+ if (blob === undefined) {
200
+ return yield* stagedSnapshotError({
201
+ detail: `Git did not return the staged blob for ${args.entry.path}`,
202
+ });
203
+ }
204
+ yield* fs
205
+ .makeDirectory(path.dirname(destination), { recursive: true })
206
+ .pipe(Effect.mapError(mapFileError));
207
+ if (args.entry.mode === "120000") {
208
+ const target = yield* Effect.try({
209
+ try: () => new TextDecoder("utf-8", { fatal: true }).decode(blob),
210
+ catch: (cause) => stagedSnapshotError({
211
+ detail: `Staged symbolic-link target is not valid UTF-8: ${args.entry.path}`,
212
+ cause,
213
+ }),
214
+ });
215
+ yield* fs.symlink(target, destination).pipe(Effect.mapError(mapFileError));
216
+ return;
217
+ }
218
+ if (args.entry.mode !== "100644" && args.entry.mode !== "100755") {
219
+ return yield* stagedSnapshotError({
220
+ detail: `Unsupported Git index mode '${args.entry.mode}' for ${args.entry.path}`,
221
+ });
222
+ }
223
+ yield* fs
224
+ .writeFile(destination, blob, { mode: args.entry.mode === "100755" ? 0o755 : 0o644 })
225
+ .pipe(Effect.mapError(mapFileError));
226
+ });
227
+ export const materializeGitIndexWorkspace = Effect.fn("Lint.materializeGitIndexWorkspace")(function* (startPath, options = {}) {
228
+ const fs = yield* FileSystem.FileSystem;
229
+ const path = yield* Path.Path;
230
+ const resolvedStartPath = path.resolve(startPath);
231
+ const gitRoot = yield* findGitRoot(resolvedStartPath);
232
+ if (Option.isNone(gitRoot)) {
233
+ return yield* stagedSnapshotError({
234
+ title: "Git index unavailable",
235
+ detail: `axm lint --view git-index requires a Git repository; no .git entry was found from '${resolvedStartPath}'`,
236
+ });
237
+ }
238
+ const displayWorkspaceRoot = options.selectRepositoryRoot ? gitRoot.value : resolvedStartPath;
239
+ const selectedRelative = path.relative(gitRoot.value, displayWorkspaceRoot);
240
+ if (selectedRelative.startsWith("..") || path.isAbsolute(selectedRelative)) {
241
+ return yield* stagedSnapshotError({
242
+ detail: `Lint path '${displayWorkspaceRoot}' is outside Git repository '${gitRoot.value}'`,
243
+ });
244
+ }
245
+ const snapshotRoot = yield* fs.makeTempDirectoryScoped({ prefix: "axm-lint-git-index-" }).pipe(Effect.mapError((cause) => new LintStagingFailed({
246
+ category: "internal",
247
+ detail: "Failed to create the temporary Git-index snapshot",
248
+ cause,
249
+ })));
250
+ const { entries, fingerprint } = yield* readIndexEntries(gitRoot.value);
251
+ const objectIds = [
252
+ ...new Set(entries.filter((entry) => entry.mode !== "160000").map((entry) => entry.objectId)),
253
+ ];
254
+ const blobs = yield* readIndexBlobs({ gitRoot: gitRoot.value, objectIds });
255
+ yield* Effect.forEach(entries, (entry) => writeIndexEntry({ entry, blobs, workspaceRoot: snapshotRoot }), {
256
+ concurrency: 32,
257
+ discard: true,
258
+ });
259
+ const workspaceRoot = selectedRelative.length === 0 ? snapshotRoot : path.resolve(snapshotRoot, selectedRelative);
260
+ yield* fs.makeDirectory(workspaceRoot, { recursive: true }).pipe(Effect.mapError((cause) => new LintStagingFailed({
261
+ category: "internal",
262
+ detail: "Failed to select Git-index workspace",
263
+ cause,
264
+ })));
265
+ return {
266
+ gitRoot: gitRoot.value,
267
+ snapshotRoot,
268
+ workspaceRoot,
269
+ displayWorkspaceRoot,
270
+ fingerprint,
271
+ };
272
+ });
273
+ //# sourceMappingURL=staged-workspace.js.map
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Workspace lint rule contexts and accessors.
3
+ *
4
+ * These context types bind the workspace read model, desired-state health,
5
+ * and projection facts to `workspace/*` rules. They complement the
6
+ * contract-level rule contexts owned by `@agentxm/registry-protocol`.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ * @packageDocumentation
10
+ */
11
+ import type * as Effect from "effect/Effect";
12
+ import type * as Option from "effect/Option";
13
+ import type { ExtensionType } from "@agentxm/extension-model/unstable/extensions/common";
14
+ import type { Handle } from "@agentxm/extension-model/unstable/extensions/handle";
15
+ import type { PackDependencyReachability, AxmSkillCompatibility, WorkspaceOwnershipIssue, InstructionProjectionSnapshot } from "@agentxm/extension-workspace";
16
+ import type { ProjectionInvariantFact } from "@agentxm/extension-workspace";
17
+ import type { CanonicalObservation } from "@agentxm/workspace-state";
18
+ import type { DesiredExtensionNode, DesiredStateGraph } from "@agentxm/workspace-state";
19
+ import type { LockfileReadError, SettingsReadError } from "@agentxm/workspace-state";
20
+ import type { WorkspaceReadModel } from "@agentxm/workspace-state";
21
+ /**
22
+ * Context passed to `workspace/*` rules.
23
+ *
24
+ * `subject.scope` is `"project"` (default) or `"user"` (user-level `.axm/`).
25
+ * Rules whose invariants apply at only one scope early-return `[]` via the
26
+ * `check` body.
27
+ *
28
+ * `subject.root` is an absolute filesystem path pinning the workspace root;
29
+ * typed as `string` in Phase 2 so the rule primitives don't depend on a
30
+ * platform-specific path brand.
31
+ *
32
+ * @experimental This API is unstable and may change without notice.
33
+ */
34
+ /**
35
+ * Opaque failure carried by the caller-supplied health effects. Rules probe
36
+ * these effects with `Effect.result` and never inspect the failure value, so
37
+ * only a tagged shape is required.
38
+ */
39
+ export type WorkspaceHealthFailure = {
40
+ readonly _tag: string;
41
+ };
42
+ export interface WorkspaceRuleContext {
43
+ readonly subject: WorkspaceSubject;
44
+ readonly workspace: WorkspaceReadModel;
45
+ readonly axmDirExists: Effect.Effect<boolean>;
46
+ readonly instructions?: WorkspaceInstructionAccessor;
47
+ /** Read-back currency for aggregate managed output units. */
48
+ readonly projections?: WorkspaceProjectionsAccessor;
49
+ readonly ownership?: Effect.Effect<ReadonlyArray<WorkspaceOwnershipIssue>>;
50
+ /**
51
+ * Installed non-pack extension manifests, keyed by nothing — rules walk the
52
+ * list. Landed for `workspace/recommended-packs-retained`, which needs the
53
+ * `standalone` / `recommendedPacks` pair that the lockfile does not carry.
54
+ *
55
+ * Optional so callers that build a context by hand (tests, and any caller
56
+ * predating the accessor) stay valid; rules that need it early-return `[]`
57
+ * when it is absent.
58
+ */
59
+ readonly installedExtensions?: WorkspaceInstalledExtensionAccessor;
60
+ /** Pre-joined, offline pack/member version reachability for workspace rules. */
61
+ readonly packDependencyReachability?: Effect.Effect<ReadonlyArray<PackDependencyReachability>>;
62
+ /** Effective configured owner (project, then user scope), when available. */
63
+ readonly owner?: Effect.Effect<Option.Option<Handle>>;
64
+ /** One caller-built evaluation over the authoritative installed AXM skill. */
65
+ readonly axmSkillCompatibility?: Effect.Effect<AxmSkillCompatibility, SettingsReadError | LockfileReadError>;
66
+ /** Deterministic desired-state preflight used by local reconciliation-health rules. */
67
+ readonly health?: {
68
+ readonly desiredState: Effect.Effect<DesiredStateGraph, WorkspaceHealthFailure>;
69
+ readonly canonicalObservations?: Effect.Effect<ReadonlyArray<{
70
+ readonly desired: DesiredExtensionNode;
71
+ readonly observation: CanonicalObservation;
72
+ }>, WorkspaceHealthFailure>;
73
+ };
74
+ readonly displayRoot: string;
75
+ }
76
+ /**
77
+ * One observation of the instruction projection per lint run, shared by every
78
+ * instruction rule so target and `.gitignore` facts come from the same moment.
79
+ * `None` when instruction-file management is not enabled.
80
+ */
81
+ export interface WorkspaceInstructionAccessor {
82
+ readonly snapshot: Effect.Effect<Option.Option<InstructionProjectionSnapshot>>;
83
+ }
84
+ /**
85
+ * Caller-bound shared facts for aggregate managed output units. Fact
86
+ * evaluation suppresses unjudgeable intrinsic conclusions so lint does not
87
+ * cascade from an incomplete desired-state graph or missing canonical input.
88
+ *
89
+ * @experimental This API is unstable and may change without notice.
90
+ */
91
+ export interface WorkspaceProjectionsAccessor {
92
+ /** One shared evaluation of output-derived intrinsic projection facts. */
93
+ readonly facts: Effect.Effect<ReadonlyArray<ProjectionInvariantFact>>;
94
+ }
95
+ /**
96
+ * Narrow accessor over the manifests of every installed non-pack extension.
97
+ *
98
+ * Packs are excluded — they carry neither `standalone` nor `recommendedPacks`.
99
+ *
100
+ * @experimental This API is unstable and may change without notice.
101
+ */
102
+ export interface WorkspaceInstalledExtensionAccessor {
103
+ readonly manifests: Effect.Effect<ReadonlyArray<InstalledExtensionManifest>>;
104
+ }
105
+ /**
106
+ * One installed extension's raw manifest, as read during workspace projection.
107
+ *
108
+ * `manifestJson` is loose parsed JSON, not a decoded manifest — consumers
109
+ * narrow what they need. `undefined` when the manifest file is absent.
110
+ *
111
+ * @experimental This API is unstable and may change without notice.
112
+ */
113
+ export interface InstalledExtensionManifest {
114
+ readonly extensionType: Exclude<ExtensionType, "pack">;
115
+ readonly name: string;
116
+ /** Workspace-root-relative posix path of the manifest file. */
117
+ readonly manifestPath: string;
118
+ readonly manifestJson: unknown;
119
+ }
120
+ /**
121
+ * WorkspaceMutations subject: the rule-addressable identity of the workspace under lint.
122
+ *
123
+ * @experimental This API is unstable and may change without notice.
124
+ */
125
+ export interface WorkspaceSubject {
126
+ readonly root: string;
127
+ readonly scope: "project" | "user";
128
+ }
129
+ //# sourceMappingURL=workspace-context.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Workspace lint rule contexts and accessors.
3
+ *
4
+ * These context types bind the workspace read model, desired-state health,
5
+ * and projection facts to `workspace/*` rules. They complement the
6
+ * contract-level rule contexts owned by `@agentxm/registry-protocol`.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ * @packageDocumentation
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=workspace-context.js.map
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@agentxm/workspace-lint",
3
+ "version": "0.28.4-bootstrap.0",
4
+ "description": "AXM workspace-lint feature: workspace facts, lint rules, findings, normalization, and bounded fix planning for the axm CLI. Unstable and unsupported — use the axm.sh CLI.",
5
+ "type": "module",
6
+ "license": "FSL-1.1-MIT",
7
+ "homepage": "https://axm.sh",
8
+ "bugs": {
9
+ "url": "https://github.com/agentxm/axm/issues"
10
+ },
11
+ "author": "AgentXM <hello@agentxm.ai> (https://agentxm.ai)",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/agentxm/axm.git",
15
+ "directory": "packages/workspace-lint"
16
+ },
17
+ "sideEffects": false,
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/src/index.d.ts",
21
+ "default": "./dist/src/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist/src/",
26
+ "!**/*.map"
27
+ ],
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "engines": {
32
+ "node": ">=22.19.0"
33
+ },
34
+ "nx": {
35
+ "includedScripts": []
36
+ },
37
+ "dependencies": {
38
+ "effect": "4.0.0-rc.112",
39
+ "@agentxm/registry-protocol": "^0.28.4-bootstrap.0",
40
+ "@agentxm/extension-sources": "^0.28.4-bootstrap.0",
41
+ "@agentxm/extension-model": "^0.28.4-bootstrap.0",
42
+ "@agentxm/workspace-state": "^0.28.4-bootstrap.0",
43
+ "@agentxm/workspace-operations": "^0.28.4-bootstrap.0",
44
+ "@agentxm/extension-workspace": "^0.28.4-bootstrap.0"
45
+ },
46
+ "devDependencies": {
47
+ "@effect/platform-node": "4.0.0-rc.112",
48
+ "@effect/vitest": "4.0.0-rc.112",
49
+ "@types/bun": "^1.3.14",
50
+ "@typescript/native": "npm:typescript@^7.0.2",
51
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
52
+ "vitest": "^4.1.10",
53
+ "@agentxm/agent-integration": "^0.28.4-bootstrap.0"
54
+ }
55
+ }