@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.
- package/LICENSE +110 -0
- package/dist/src/catalog/index.d.ts +31 -0
- package/dist/src/catalog/index.js +60 -0
- package/dist/src/catalog/workspace/agents-detected-declared.d.ts +23 -0
- package/dist/src/catalog/workspace/agents-detected-declared.js +61 -0
- package/dist/src/catalog/workspace/agents-recognized.d.ts +23 -0
- package/dist/src/catalog/workspace/agents-recognized.js +64 -0
- package/dist/src/catalog/workspace/axm-skill-compatible.d.ts +4 -0
- package/dist/src/catalog/workspace/axm-skill-compatible.js +51 -0
- package/dist/src/catalog/workspace/configured-but-not-installed.d.ts +4 -0
- package/dist/src/catalog/workspace/configured-but-not-installed.js +88 -0
- package/dist/src/catalog/workspace/desired-state-reconcilable.d.ts +4 -0
- package/dist/src/catalog/workspace/desired-state-reconcilable.js +96 -0
- package/dist/src/catalog/workspace/display-paths.d.ts +7 -0
- package/dist/src/catalog/workspace/display-paths.js +4 -0
- package/dist/src/catalog/workspace/helpers/empty.d.ts +18 -0
- package/dist/src/catalog/workspace/helpers/empty.js +16 -0
- package/dist/src/catalog/workspace/helpers/finding.d.ts +10 -0
- package/dist/src/catalog/workspace/helpers/finding.js +11 -0
- package/dist/src/catalog/workspace/helpers/source-categorize.d.ts +16 -0
- package/dist/src/catalog/workspace/helpers/source-categorize.js +51 -0
- package/dist/src/catalog/workspace/hook-ownership-ambiguous.d.ts +4 -0
- package/dist/src/catalog/workspace/hook-ownership-ambiguous.js +25 -0
- package/dist/src/catalog/workspace/initialized.d.ts +22 -0
- package/dist/src/catalog/workspace/initialized.js +58 -0
- package/dist/src/catalog/workspace/instructions-agent-supported.d.ts +4 -0
- package/dist/src/catalog/workspace/instructions-agent-supported.js +38 -0
- package/dist/src/catalog/workspace/instructions-gitignore-current.d.ts +4 -0
- package/dist/src/catalog/workspace/instructions-gitignore-current.js +49 -0
- package/dist/src/catalog/workspace/instructions-source-present.d.ts +4 -0
- package/dist/src/catalog/workspace/instructions-source-present.js +41 -0
- package/dist/src/catalog/workspace/instructions-target-current.d.ts +4 -0
- package/dist/src/catalog/workspace/instructions-target-current.js +51 -0
- package/dist/src/catalog/workspace/instructions-target-stale.d.ts +9 -0
- package/dist/src/catalog/workspace/instructions-target-stale.js +41 -0
- package/dist/src/catalog/workspace/instructions-target-unowned.d.ts +10 -0
- package/dist/src/catalog/workspace/instructions-target-unowned.js +59 -0
- package/dist/src/catalog/workspace/knowledge-state-valid.d.ts +5 -0
- package/dist/src/catalog/workspace/knowledge-state-valid.js +65 -0
- package/dist/src/catalog/workspace/lockfile-valid.d.ts +5 -0
- package/dist/src/catalog/workspace/lockfile-valid.js +63 -0
- package/dist/src/catalog/workspace/managed-file-unowned.d.ts +4 -0
- package/dist/src/catalog/workspace/managed-file-unowned.js +25 -0
- package/dist/src/catalog/workspace/mcps-agent-drift.d.ts +4 -0
- package/dist/src/catalog/workspace/mcps-agent-drift.js +139 -0
- package/dist/src/catalog/workspace/mcps-agent-orphaned.d.ts +4 -0
- package/dist/src/catalog/workspace/mcps-agent-orphaned.js +64 -0
- package/dist/src/catalog/workspace/mcps-no-secret-literal.d.ts +4 -0
- package/dist/src/catalog/workspace/mcps-no-secret-literal.js +67 -0
- package/dist/src/catalog/workspace/mcps-shared-target-compatible.d.ts +4 -0
- package/dist/src/catalog/workspace/mcps-shared-target-compatible.js +58 -0
- package/dist/src/catalog/workspace/mcps-transport-exclusivity.d.ts +4 -0
- package/dist/src/catalog/workspace/mcps-transport-exclusivity.js +67 -0
- package/dist/src/catalog/workspace/packs-declarations-valid.d.ts +19 -0
- package/dist/src/catalog/workspace/packs-declarations-valid.js +110 -0
- package/dist/src/catalog/workspace/packs-dependencies-resolved.d.ts +5 -0
- package/dist/src/catalog/workspace/packs-dependencies-resolved.js +49 -0
- package/dist/src/catalog/workspace/projections-current.d.ts +15 -0
- package/dist/src/catalog/workspace/projections-current.js +35 -0
- package/dist/src/catalog/workspace/settings-keys-recognized.d.ts +17 -0
- package/dist/src/catalog/workspace/settings-keys-recognized.js +90 -0
- package/dist/src/catalog/workspace/settings-schema-valid.d.ts +27 -0
- package/dist/src/catalog/workspace/settings-schema-valid.js +68 -0
- package/dist/src/catalog/workspace/skills-artifacts-correct.d.ts +28 -0
- package/dist/src/catalog/workspace/skills-artifacts-correct.js +143 -0
- package/dist/src/catalog/workspace/skills-declarations-valid.d.ts +27 -0
- package/dist/src/catalog/workspace/skills-declarations-valid.js +102 -0
- package/dist/src/catalog/workspace/skills-integrity-valid.d.ts +28 -0
- package/dist/src/catalog/workspace/skills-integrity-valid.js +95 -0
- package/dist/src/catalog/workspace/skills-lockfile-aligned.d.ts +5 -0
- package/dist/src/catalog/workspace/skills-lockfile-aligned.js +64 -0
- package/dist/src/catalog/workspace/source-endpoints-aligned.d.ts +5 -0
- package/dist/src/catalog/workspace/source-endpoints-aligned.js +75 -0
- package/dist/src/catalog/workspace-fixtures/fixture-state.d.ts +8 -0
- package/dist/src/catalog/workspace-fixtures/fixture-state.js +120 -0
- package/dist/src/catalog/workspace-fixtures/interpret-ops.d.ts +11 -0
- package/dist/src/catalog/workspace-fixtures/interpret-ops.js +9 -0
- package/dist/src/catalog/workspace-read-model/lint-workspace.d.ts +191 -0
- package/dist/src/catalog/workspace-read-model/lint-workspace.js +651 -0
- package/dist/src/catalog/workspace-read-model/manifest-json.d.ts +14 -0
- package/dist/src/catalog/workspace-read-model/manifest-json.js +27 -0
- package/dist/src/catalog/workspace.d.ts +58 -0
- package/dist/src/catalog/workspace.js +164 -0
- package/dist/src/catalog-contexts.d.ts +106 -0
- package/dist/src/catalog-contexts.js +95 -0
- package/dist/src/cli.d.ts +239 -0
- package/dist/src/cli.js +1077 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +21 -0
- package/dist/src/json-schema.d.ts +121 -0
- package/dist/src/json-schema.js +99 -0
- package/dist/src/run/errors.d.ts +23 -0
- package/dist/src/run/errors.js +21 -0
- package/dist/src/run/settings.d.ts +63 -0
- package/dist/src/run/settings.js +139 -0
- package/dist/src/run/staged-workspace.d.ts +31 -0
- package/dist/src/run/staged-workspace.js +273 -0
- package/dist/src/workspace-context.d.ts +129 -0
- package/dist/src/workspace-context.js +12 -0
- package/package.json +55 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed empty constants used by workspace rules to satisfy return types
|
|
3
|
+
* without resorting to `[]` + type assertion. Using a shared `const` avoids
|
|
4
|
+
* repeated `as ReadonlyArray<T>` assertions (banned by the repo style guide)
|
|
5
|
+
* while keeping the inferred type stable.
|
|
6
|
+
*
|
|
7
|
+
* @experimental This API is unstable and may change without notice.
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
import type { AdvisoryFinding, LintFinding } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
11
|
+
import type { Operation } from "@agentxm/workspace-operations";
|
|
12
|
+
/** Shared empty AdvisoryFinding array. */
|
|
13
|
+
export declare const EMPTY_ADVISORY_FINDINGS: ReadonlyArray<AdvisoryFinding>;
|
|
14
|
+
/** Shared empty LintFinding array. */
|
|
15
|
+
export declare const EMPTY_LINT_FINDINGS: ReadonlyArray<LintFinding>;
|
|
16
|
+
/** Shared empty Operation array. */
|
|
17
|
+
export declare const EMPTY_OPERATIONS: ReadonlyArray<Operation<string, unknown>>;
|
|
18
|
+
//# sourceMappingURL=empty.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed empty constants used by workspace rules to satisfy return types
|
|
3
|
+
* without resorting to `[]` + type assertion. Using a shared `const` avoids
|
|
4
|
+
* repeated `as ReadonlyArray<T>` assertions (banned by the repo style guide)
|
|
5
|
+
* while keeping the inferred type stable.
|
|
6
|
+
*
|
|
7
|
+
* @experimental This API is unstable and may change without notice.
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
/** Shared empty AdvisoryFinding array. */
|
|
11
|
+
export const EMPTY_ADVISORY_FINDINGS = [];
|
|
12
|
+
/** Shared empty LintFinding array. */
|
|
13
|
+
export const EMPTY_LINT_FINDINGS = [];
|
|
14
|
+
/** Shared empty Operation array. */
|
|
15
|
+
export const EMPTY_OPERATIONS = [];
|
|
16
|
+
//# sourceMappingURL=empty.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared finding helpers for workspace lint rules.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
import type { FindingBase } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
8
|
+
/** Structural identity used by rule `fix` lookups. */
|
|
9
|
+
export declare const isSameFinding: (left: FindingBase, right: FindingBase) => boolean;
|
|
10
|
+
//# sourceMappingURL=finding.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared finding helpers for workspace lint rules.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
/** Structural identity used by rule `fix` lookups. */
|
|
8
|
+
export const isSameFinding = (left, right) => left.ruleId === right.ruleId &&
|
|
9
|
+
left.message === right.message &&
|
|
10
|
+
left.location?.file === right.location?.file;
|
|
11
|
+
//# sourceMappingURL=finding.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared source categorization for workspace declaration rules.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
/** Shared normalized view of a declared source string. */
|
|
8
|
+
export interface Categorized {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly source: string;
|
|
11
|
+
readonly kind: "registry" | "workspace" | "bare" | "non-registry" | "registry-no-owner";
|
|
12
|
+
readonly registryFqn?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Classify a declaration source into registry / bare / non-registry buckets. */
|
|
15
|
+
export declare const categorizeEntry: (name: string, source: string) => Categorized;
|
|
16
|
+
//# sourceMappingURL=source-categorize.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared source categorization for workspace declaration rules.
|
|
3
|
+
*
|
|
4
|
+
* @experimental This API is unstable and may change without notice.
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
import { parseRegistrySourceRef } from "@agentxm/extension-model/unstable/extensions/registry-source";
|
|
8
|
+
import { parseFqn } from "@agentxm/extension-model/unstable/extensions/fqn";
|
|
9
|
+
import { isWorkspaceSourceLocator } from "@agentxm/extension-model/unstable/sources/workspace";
|
|
10
|
+
import * as Result from "effect/Result";
|
|
11
|
+
const BARE_NAME_RE = /^[a-z0-9][a-z0-9-]*(?:@[^\s/:]+)?$/i;
|
|
12
|
+
const NON_REGISTRY_MARKERS = [
|
|
13
|
+
/^\.\//,
|
|
14
|
+
/^\.\.\//,
|
|
15
|
+
/^\//,
|
|
16
|
+
/^file:\/\//,
|
|
17
|
+
/:\/\//,
|
|
18
|
+
/^[a-z][a-z0-9+.-]*:/i,
|
|
19
|
+
];
|
|
20
|
+
const isClearlyNonRegistrySource = (source) => NON_REGISTRY_MARKERS.some((pattern) => pattern.test(source));
|
|
21
|
+
/** Classify a declaration source into registry / bare / non-registry buckets. */
|
|
22
|
+
export const categorizeEntry = (name, source) => {
|
|
23
|
+
if (isWorkspaceSourceLocator(source)) {
|
|
24
|
+
const parsed = parseFqn(source.slice("workspace:".length));
|
|
25
|
+
return Result.isSuccess(parsed)
|
|
26
|
+
? {
|
|
27
|
+
name,
|
|
28
|
+
source,
|
|
29
|
+
kind: "workspace",
|
|
30
|
+
registryFqn: `${parsed.success.owner}/${parsed.success.type}/${parsed.success.name}`,
|
|
31
|
+
}
|
|
32
|
+
: { name, source, kind: "workspace" };
|
|
33
|
+
}
|
|
34
|
+
const parsed = parseRegistrySourceRef(source);
|
|
35
|
+
if (parsed !== undefined) {
|
|
36
|
+
return {
|
|
37
|
+
name,
|
|
38
|
+
source,
|
|
39
|
+
kind: "registry",
|
|
40
|
+
registryFqn: `${parsed.owner}/${parsed.type}/${parsed.name}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (isClearlyNonRegistrySource(source)) {
|
|
44
|
+
return { name, source, kind: "non-registry" };
|
|
45
|
+
}
|
|
46
|
+
if (BARE_NAME_RE.test(source)) {
|
|
47
|
+
return { name, source, kind: "bare" };
|
|
48
|
+
}
|
|
49
|
+
return { name, source, kind: "registry-no-owner" };
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=source-categorize.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
2
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
3
|
+
export declare const hookOwnershipAmbiguousRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
4
|
+
//# sourceMappingURL=hook-ownership-ambiguous.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { EMPTY_LINT_FINDINGS } from "./helpers/empty.js";
|
|
3
|
+
const RULE_ID = "workspace/hook-ownership-ambiguous";
|
|
4
|
+
const relativeToRoot = (root, file) => {
|
|
5
|
+
const prefix = `${root}/`;
|
|
6
|
+
return file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
|
7
|
+
};
|
|
8
|
+
export const hookOwnershipAmbiguousRule = {
|
|
9
|
+
id: RULE_ID,
|
|
10
|
+
description: "Hook entries that resemble AXM output carry explicit ownership metadata.",
|
|
11
|
+
kind: "advisory",
|
|
12
|
+
severity: "warning",
|
|
13
|
+
check: (context) => context.ownership === undefined
|
|
14
|
+
? Effect.succeed(EMPTY_LINT_FINDINGS)
|
|
15
|
+
: context.ownership.pipe(Effect.map((issues) => issues
|
|
16
|
+
.filter((issue) => issue.kind === "hook-ownership-ambiguous")
|
|
17
|
+
.map((issue) => ({
|
|
18
|
+
kind: "advisory",
|
|
19
|
+
ruleId: RULE_ID,
|
|
20
|
+
severity: "warning",
|
|
21
|
+
message: issue.detail,
|
|
22
|
+
location: { file: relativeToRoot(context.subject.root, issue.path) },
|
|
23
|
+
})))),
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=hook-ownership-ambiguous.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `workspace/initialized` — the scope's settings authority exists.
|
|
3
|
+
*
|
|
4
|
+
* Per the lint design "Foundation" row:
|
|
5
|
+
*
|
|
6
|
+
* WorkspaceMutations bootstrap invariant. Absorbs doctor `workspace-ready.*` checks.
|
|
7
|
+
*
|
|
8
|
+
* Applies to every workspace read model (both scopes). Owns the presence arm;
|
|
9
|
+
* `workspace/settings-schema-valid` owns the contents arm and early-returns
|
|
10
|
+
* when `axm.json` is missing, so a fresh workspace surfaces exactly one
|
|
11
|
+
* finding from this rule.
|
|
12
|
+
*
|
|
13
|
+
* Advisory-only — scaffolding a workspace is a multi-step `axm setup` command;
|
|
14
|
+
* keeping it a suggestion scopes autofix to Operation-expressible mutations.
|
|
15
|
+
*
|
|
16
|
+
* @experimental This API is unstable and may change without notice.
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
20
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
21
|
+
export declare const initializedRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
22
|
+
//# sourceMappingURL=initialized.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `workspace/initialized` — the scope's settings authority exists.
|
|
3
|
+
*
|
|
4
|
+
* Per the lint design "Foundation" row:
|
|
5
|
+
*
|
|
6
|
+
* WorkspaceMutations bootstrap invariant. Absorbs doctor `workspace-ready.*` checks.
|
|
7
|
+
*
|
|
8
|
+
* Applies to every workspace read model (both scopes). Owns the presence arm;
|
|
9
|
+
* `workspace/settings-schema-valid` owns the contents arm and early-returns
|
|
10
|
+
* when `axm.json` is missing, so a fresh workspace surfaces exactly one
|
|
11
|
+
* finding from this rule.
|
|
12
|
+
*
|
|
13
|
+
* Advisory-only — scaffolding a workspace is a multi-step `axm setup` command;
|
|
14
|
+
* keeping it a suggestion scopes autofix to Operation-expressible mutations.
|
|
15
|
+
*
|
|
16
|
+
* @experimental This API is unstable and may change without notice.
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
import * as Effect from "effect/Effect";
|
|
20
|
+
import * as Option from "effect/Option";
|
|
21
|
+
import * as Result from "effect/Result";
|
|
22
|
+
import { settingsDisplayPath } from "./display-paths.js";
|
|
23
|
+
const RULE_ID = "workspace/initialized";
|
|
24
|
+
const AXM_REL = ".axm";
|
|
25
|
+
export const initializedRule = {
|
|
26
|
+
id: RULE_ID,
|
|
27
|
+
description: "The workspace includes its scope-specific settings authority.",
|
|
28
|
+
kind: "advisory",
|
|
29
|
+
severity: "error",
|
|
30
|
+
check: (context) => Effect.gen(function* () {
|
|
31
|
+
const scoped = context.workspace;
|
|
32
|
+
const settingsPath = settingsDisplayPath(context.subject.scope);
|
|
33
|
+
const settingsRaw = yield* Effect.result(scoped.state.raw("settings"));
|
|
34
|
+
const axmDirExists = yield* context.axmDirExists;
|
|
35
|
+
const findings = [];
|
|
36
|
+
if (Result.isFailure(settingsRaw) || Option.isNone(settingsRaw.success)) {
|
|
37
|
+
if (context.subject.scope === "user" && !axmDirExists) {
|
|
38
|
+
findings.push({
|
|
39
|
+
kind: "advisory",
|
|
40
|
+
ruleId: RULE_ID,
|
|
41
|
+
severity: "error",
|
|
42
|
+
message: "The workspace does not contain an `.axm/` state directory.",
|
|
43
|
+
location: { file: AXM_REL },
|
|
44
|
+
});
|
|
45
|
+
return findings;
|
|
46
|
+
}
|
|
47
|
+
findings.push({
|
|
48
|
+
kind: "advisory",
|
|
49
|
+
ruleId: RULE_ID,
|
|
50
|
+
severity: "error",
|
|
51
|
+
message: "The workspace settings file is missing.",
|
|
52
|
+
location: { file: settingsPath },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return findings;
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=initialized.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
2
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
3
|
+
export declare const instructionsAgentSupportedRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
4
|
+
//# sourceMappingURL=instructions-agent-supported.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { EMPTY_ADVISORY_FINDINGS } from "./helpers/empty.js";
|
|
4
|
+
const RULE_ID = "workspace/instructions-agent-supported";
|
|
5
|
+
const relativeToRoot = (root, file) => {
|
|
6
|
+
if (file === root)
|
|
7
|
+
return "";
|
|
8
|
+
const prefix = `${root}/`;
|
|
9
|
+
return file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
|
10
|
+
};
|
|
11
|
+
export const instructionsAgentSupportedRule = {
|
|
12
|
+
id: RULE_ID,
|
|
13
|
+
description: "Configured agents support automatic instruction propagation.",
|
|
14
|
+
kind: "advisory",
|
|
15
|
+
severity: "warning",
|
|
16
|
+
check: (context) => Effect.gen(function* () {
|
|
17
|
+
if (context.instructions === undefined)
|
|
18
|
+
return EMPTY_ADVISORY_FINDINGS;
|
|
19
|
+
const snapshot = yield* context.instructions.snapshot;
|
|
20
|
+
if (Option.isNone(snapshot))
|
|
21
|
+
return EMPTY_ADVISORY_FINDINGS;
|
|
22
|
+
const findings = [];
|
|
23
|
+
for (const item of snapshot.value.status.items) {
|
|
24
|
+
if (item.health !== "unsupported" && item.mechanism !== "adapter")
|
|
25
|
+
continue;
|
|
26
|
+
findings.push({
|
|
27
|
+
kind: "advisory",
|
|
28
|
+
ruleId: RULE_ID,
|
|
29
|
+
severity: "warning",
|
|
30
|
+
message: `${item.agentName} does not support automatic instruction-file propagation. ` +
|
|
31
|
+
"Manage that agent's instruction file manually.",
|
|
32
|
+
location: { file: relativeToRoot(context.subject.root, item.targetFile) },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return findings;
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=instructions-agent-supported.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
2
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
3
|
+
export declare const instructionsGitignoreCurrentRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
4
|
+
//# sourceMappingURL=instructions-gitignore-current.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { EMPTY_LINT_FINDINGS } from "./helpers/empty.js";
|
|
4
|
+
const RULE_ID = "workspace/instructions-gitignore-current";
|
|
5
|
+
const relativeToRoot = (root, file) => {
|
|
6
|
+
if (file === root)
|
|
7
|
+
return "";
|
|
8
|
+
const prefix = `${root}/`;
|
|
9
|
+
return file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
|
10
|
+
};
|
|
11
|
+
export const instructionsGitignoreCurrentRule = {
|
|
12
|
+
id: RULE_ID,
|
|
13
|
+
description: "Instruction-file gitignore entries match the configured policy.",
|
|
14
|
+
kind: "advisory",
|
|
15
|
+
severity: "info",
|
|
16
|
+
check: (context) => Effect.gen(function* () {
|
|
17
|
+
if (context.instructions === undefined)
|
|
18
|
+
return EMPTY_LINT_FINDINGS;
|
|
19
|
+
const snapshot = yield* context.instructions.snapshot;
|
|
20
|
+
if (Option.isNone(snapshot))
|
|
21
|
+
return EMPTY_LINT_FINDINGS;
|
|
22
|
+
const status = snapshot.value.gitignore;
|
|
23
|
+
if (status.trackedAliases.length > 0) {
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
kind: "advisory",
|
|
27
|
+
ruleId: RULE_ID,
|
|
28
|
+
severity: "info",
|
|
29
|
+
message: `Managed ignore entries cover paths already present in the Git index (${status.trackedAliases.join(", ")}); set gitignoreAliases: false to reconcile tracked instruction aliases.`,
|
|
30
|
+
location: { file: relativeToRoot(context.subject.root, status.file) },
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
if (status.current)
|
|
35
|
+
return EMPTY_LINT_FINDINGS;
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
kind: "advisory",
|
|
39
|
+
ruleId: RULE_ID,
|
|
40
|
+
severity: "info",
|
|
41
|
+
message: status.desired
|
|
42
|
+
? "Instruction-file ignore entries are missing or stale."
|
|
43
|
+
: "Instruction-file ignore entries are disabled but a managed block remains.",
|
|
44
|
+
location: { file: relativeToRoot(context.subject.root, status.file) },
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=instructions-gitignore-current.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
2
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
3
|
+
export declare const instructionsSourcePresentRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
4
|
+
//# sourceMappingURL=instructions-source-present.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { EMPTY_ADVISORY_FINDINGS } from "./helpers/empty.js";
|
|
4
|
+
const RULE_ID = "workspace/instructions-source-present";
|
|
5
|
+
const relativeToRoot = (root, file) => {
|
|
6
|
+
if (file === root)
|
|
7
|
+
return "";
|
|
8
|
+
const prefix = `${root}/`;
|
|
9
|
+
return file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
|
10
|
+
};
|
|
11
|
+
export const instructionsSourcePresentRule = {
|
|
12
|
+
id: RULE_ID,
|
|
13
|
+
description: "The configured instruction source file exists.",
|
|
14
|
+
kind: "advisory",
|
|
15
|
+
severity: "error",
|
|
16
|
+
check: (context) => Effect.gen(function* () {
|
|
17
|
+
if (context.instructions === undefined)
|
|
18
|
+
return EMPTY_ADVISORY_FINDINGS;
|
|
19
|
+
const snapshot = yield* context.instructions.snapshot;
|
|
20
|
+
if (Option.isNone(snapshot))
|
|
21
|
+
return EMPTY_ADVISORY_FINDINGS;
|
|
22
|
+
const sourceFiles = new Set([
|
|
23
|
+
...snapshot.value.status.missingSources,
|
|
24
|
+
...snapshot.value.status.items
|
|
25
|
+
.filter((item) => item.health === "missing-source")
|
|
26
|
+
.map((item) => item.sourceFile),
|
|
27
|
+
]);
|
|
28
|
+
const findings = [];
|
|
29
|
+
for (const sourceFile of sourceFiles) {
|
|
30
|
+
findings.push({
|
|
31
|
+
kind: "advisory",
|
|
32
|
+
ruleId: RULE_ID,
|
|
33
|
+
severity: "error",
|
|
34
|
+
message: "The configured instruction source file is missing.",
|
|
35
|
+
location: { file: relativeToRoot(context.subject.root, sourceFile) },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return findings;
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=instructions-source-present.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
2
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
3
|
+
export declare const instructionsTargetCurrentRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
4
|
+
//# sourceMappingURL=instructions-target-current.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { EMPTY_LINT_FINDINGS } from "./helpers/empty.js";
|
|
4
|
+
const RULE_ID = "workspace/instructions-target-current";
|
|
5
|
+
const relativeToRoot = (root, file) => {
|
|
6
|
+
if (file === root)
|
|
7
|
+
return "";
|
|
8
|
+
const prefix = `${root}/`;
|
|
9
|
+
return file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
|
10
|
+
};
|
|
11
|
+
const targetHealth = new Set(["missing-target", "drift"]);
|
|
12
|
+
/**
|
|
13
|
+
* A projected target whose desired state is fully determined by its source:
|
|
14
|
+
* absent, or an AXM-owned copy that drifted. Anything unowned at the same
|
|
15
|
+
* path — authored content, a foreign or dangling symlink — is a collision,
|
|
16
|
+
* reported by `workspace/instructions-target-unowned`, never as regenerable
|
|
17
|
+
* drift. A missing source is reported by `workspace/instructions-source-present`.
|
|
18
|
+
*/
|
|
19
|
+
const isTargetFinding = (item) => (item.mechanism === "symlink" || item.mechanism === "copy") &&
|
|
20
|
+
targetHealth.has(item.health) &&
|
|
21
|
+
item.ownership !== "unowned";
|
|
22
|
+
const messageFor = (item) => item.health === "missing-target"
|
|
23
|
+
? `The ${item.agentName} instruction file is missing.`
|
|
24
|
+
: `The AXM-managed ${item.agentName} instruction copy differs from the source file.`;
|
|
25
|
+
export const instructionsTargetCurrentRule = {
|
|
26
|
+
id: RULE_ID,
|
|
27
|
+
description: "Configured agent instruction target files are current.",
|
|
28
|
+
kind: "advisory",
|
|
29
|
+
severity: "warning",
|
|
30
|
+
check: (context) => Effect.gen(function* () {
|
|
31
|
+
if (context.instructions === undefined)
|
|
32
|
+
return EMPTY_LINT_FINDINGS;
|
|
33
|
+
const snapshot = yield* context.instructions.snapshot;
|
|
34
|
+
if (Option.isNone(snapshot))
|
|
35
|
+
return EMPTY_LINT_FINDINGS;
|
|
36
|
+
const findings = [];
|
|
37
|
+
for (const item of snapshot.value.status.items) {
|
|
38
|
+
if (!isTargetFinding(item))
|
|
39
|
+
continue;
|
|
40
|
+
findings.push({
|
|
41
|
+
kind: "advisory",
|
|
42
|
+
ruleId: RULE_ID,
|
|
43
|
+
severity: "warning",
|
|
44
|
+
message: messageFor(item),
|
|
45
|
+
location: { file: relativeToRoot(context.subject.root, item.targetFile) },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return findings;
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=instructions-target-current.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
2
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
3
|
+
/**
|
|
4
|
+
* An AXM-owned alias the current plan no longer desires — left behind by a
|
|
5
|
+
* removed source root, a removed agent, or a changed source filename. Its
|
|
6
|
+
* ownership is proven by inspection, so removal is fully determined.
|
|
7
|
+
*/
|
|
8
|
+
export declare const instructionsTargetStaleRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
9
|
+
//# sourceMappingURL=instructions-target-stale.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { EMPTY_LINT_FINDINGS } from "./helpers/empty.js";
|
|
4
|
+
const RULE_ID = "workspace/instructions-target-stale";
|
|
5
|
+
const relativeToRoot = (root, file) => {
|
|
6
|
+
if (file === root)
|
|
7
|
+
return "";
|
|
8
|
+
const prefix = `${root}/`;
|
|
9
|
+
return file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* An AXM-owned alias the current plan no longer desires — left behind by a
|
|
13
|
+
* removed source root, a removed agent, or a changed source filename. Its
|
|
14
|
+
* ownership is proven by inspection, so removal is fully determined.
|
|
15
|
+
*/
|
|
16
|
+
export const instructionsTargetStaleRule = {
|
|
17
|
+
id: RULE_ID,
|
|
18
|
+
description: "No AXM-owned instruction alias outlives the plan that created it.",
|
|
19
|
+
kind: "advisory",
|
|
20
|
+
severity: "warning",
|
|
21
|
+
check: (context) => Effect.gen(function* () {
|
|
22
|
+
if (context.instructions === undefined)
|
|
23
|
+
return EMPTY_LINT_FINDINGS;
|
|
24
|
+
const snapshot = yield* context.instructions.snapshot;
|
|
25
|
+
if (Option.isNone(snapshot))
|
|
26
|
+
return EMPTY_LINT_FINDINGS;
|
|
27
|
+
const findings = [];
|
|
28
|
+
for (const item of snapshot.value.status.staleTargets) {
|
|
29
|
+
findings.push({
|
|
30
|
+
kind: "advisory",
|
|
31
|
+
ruleId: RULE_ID,
|
|
32
|
+
severity: "warning",
|
|
33
|
+
message: `The AXM-owned ${item.agentName} instruction ${item.observedForm === "copy" ? "copy" : "symlink"} ` +
|
|
34
|
+
"is no longer desired by the current instruction configuration.",
|
|
35
|
+
location: { file: relativeToRoot(context.subject.root, item.targetFile) },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return findings;
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=instructions-target-stale.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
2
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
3
|
+
/**
|
|
4
|
+
* An unowned file at a planned instruction target is a collision, not drift:
|
|
5
|
+
* AXM cannot prove it produced the content, so no reconciliation may replace
|
|
6
|
+
* it and `axm sync` refuses until a person decides. The finding names the
|
|
7
|
+
* path and the decision; it carries no repair.
|
|
8
|
+
*/
|
|
9
|
+
export declare const instructionsTargetUnownedRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
10
|
+
//# sourceMappingURL=instructions-target-unowned.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { EMPTY_LINT_FINDINGS } from "./helpers/empty.js";
|
|
4
|
+
const RULE_ID = "workspace/instructions-target-unowned";
|
|
5
|
+
const occupantFor = (item) => {
|
|
6
|
+
switch (item.observedForm) {
|
|
7
|
+
case "directory":
|
|
8
|
+
return "directory";
|
|
9
|
+
case "symlink":
|
|
10
|
+
return "symlink";
|
|
11
|
+
case "broken-link":
|
|
12
|
+
return "dangling symlink";
|
|
13
|
+
default:
|
|
14
|
+
return "file";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const relativeToRoot = (root, file) => {
|
|
18
|
+
if (file === root)
|
|
19
|
+
return "";
|
|
20
|
+
const prefix = `${root}/`;
|
|
21
|
+
return file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* An unowned file at a planned instruction target is a collision, not drift:
|
|
25
|
+
* AXM cannot prove it produced the content, so no reconciliation may replace
|
|
26
|
+
* it and `axm sync` refuses until a person decides. The finding names the
|
|
27
|
+
* path and the decision; it carries no repair.
|
|
28
|
+
*/
|
|
29
|
+
export const instructionsTargetUnownedRule = {
|
|
30
|
+
id: RULE_ID,
|
|
31
|
+
description: "Planned instruction targets are free of files AXM does not own.",
|
|
32
|
+
kind: "advisory",
|
|
33
|
+
severity: "warning",
|
|
34
|
+
check: (context) => Effect.gen(function* () {
|
|
35
|
+
if (context.instructions === undefined)
|
|
36
|
+
return EMPTY_LINT_FINDINGS;
|
|
37
|
+
const snapshot = yield* context.instructions.snapshot;
|
|
38
|
+
if (Option.isNone(snapshot))
|
|
39
|
+
return EMPTY_LINT_FINDINGS;
|
|
40
|
+
const findings = [];
|
|
41
|
+
for (const item of snapshot.value.status.items) {
|
|
42
|
+
if (item.mechanism !== "symlink" && item.mechanism !== "copy")
|
|
43
|
+
continue;
|
|
44
|
+
if (item.ownership !== "unowned")
|
|
45
|
+
continue;
|
|
46
|
+
findings.push({
|
|
47
|
+
kind: "advisory",
|
|
48
|
+
ruleId: RULE_ID,
|
|
49
|
+
severity: "warning",
|
|
50
|
+
message: `An unowned ${occupantFor(item)} occupies the ${item.agentName} instruction target; ` +
|
|
51
|
+
"AXM will not modify it. Remove or rename it, or make it the canonical source with " +
|
|
52
|
+
"`axm instructions enable --file`.",
|
|
53
|
+
location: { file: relativeToRoot(context.subject.root, item.targetFile) },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return findings;
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=instructions-target-unowned.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Reports invalid observed state for desired Knowledge bundles. */
|
|
2
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
3
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
4
|
+
export declare const knowledgeStateValidRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
5
|
+
//# sourceMappingURL=knowledge-state-valid.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Reports invalid observed state for desired Knowledge bundles. */
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Option from "effect/Option";
|
|
4
|
+
import * as Result from "effect/Result";
|
|
5
|
+
import { canonicalDisplayRoot, settingsDisplayPath } from "./display-paths.js";
|
|
6
|
+
const RULE_ID = "workspace/knowledge-state-valid";
|
|
7
|
+
export const knowledgeStateValidRule = {
|
|
8
|
+
id: RULE_ID,
|
|
9
|
+
description: "Desired Knowledge content has usable canonical state.",
|
|
10
|
+
kind: "advisory",
|
|
11
|
+
severity: "error",
|
|
12
|
+
check: (context) => Effect.gen(function* () {
|
|
13
|
+
const [unmanaged, resolved] = yield* Effect.all([
|
|
14
|
+
Effect.result(context.workspace.knowledge.unmanaged),
|
|
15
|
+
Effect.result(context.workspace.knowledge.resolved),
|
|
16
|
+
]);
|
|
17
|
+
const resolvedNames = Result.isFailure(resolved)
|
|
18
|
+
? new Set()
|
|
19
|
+
: new Set(Option.getOrElse(resolved.success, () => []).map(({ name }) => name));
|
|
20
|
+
const unmanagedFindings = Result.isFailure(unmanaged)
|
|
21
|
+
? []
|
|
22
|
+
: unmanaged.success.flatMap(({ key, actual }) => resolvedNames.has(key.name) ||
|
|
23
|
+
!actual.contentRoot
|
|
24
|
+
.replaceAll("\\", "/")
|
|
25
|
+
.includes(context.subject.scope === "project"
|
|
26
|
+
? "/agent_extensions/"
|
|
27
|
+
: "/.axm/workspace/agent_extensions/")
|
|
28
|
+
? []
|
|
29
|
+
: [
|
|
30
|
+
{
|
|
31
|
+
kind: "advisory",
|
|
32
|
+
ruleId: RULE_ID,
|
|
33
|
+
severity: "error",
|
|
34
|
+
message: `Knowledge bundle '${key.name}' has canonical content without an accepted AXM ownership fact.`,
|
|
35
|
+
location: { file: canonicalDisplayRoot(context.subject.scope) },
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
38
|
+
if (context.health?.canonicalObservations === undefined)
|
|
39
|
+
return unmanagedFindings;
|
|
40
|
+
const observations = yield* Effect.result(context.health.canonicalObservations);
|
|
41
|
+
if (Result.isFailure(observations))
|
|
42
|
+
return unmanagedFindings;
|
|
43
|
+
const observedFindings = observations.success.flatMap(({ desired, observation }) => {
|
|
44
|
+
if (desired.type !== "knowledge" ||
|
|
45
|
+
observation.status === "usable" ||
|
|
46
|
+
observation.status === "not-applicable" ||
|
|
47
|
+
observation.status === "locally-modified") {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
kind: "advisory",
|
|
53
|
+
ruleId: RULE_ID,
|
|
54
|
+
severity: "error",
|
|
55
|
+
message: `Knowledge bundle '${desired.name}' has canonical state ${observation.status}.`,
|
|
56
|
+
location: {
|
|
57
|
+
file: observation.path ?? settingsDisplayPath(context.subject.scope),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
});
|
|
62
|
+
return [...unmanagedFindings, ...observedFindings];
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=knowledge-state-valid.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Reports whether accepted external-resolution state is readable and present when required. */
|
|
2
|
+
import type { WorkspaceRuleContext } from "../../workspace-context.js";
|
|
3
|
+
import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
|
|
4
|
+
export declare const lockfileValidRule: AdvisoryRule<WorkspaceRuleContext>;
|
|
5
|
+
//# sourceMappingURL=lockfile-valid.d.ts.map
|