@osovv/grace-cli 3.2.0 → 3.3.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/README.md CHANGED
@@ -10,13 +10,13 @@ This repository packages GRACE as reusable skills for coding agents. The current
10
10
  - knowledge-graph synchronization
11
11
  - controller-managed sequential or multi-agent implementation
12
12
 
13
- Current packaged version: `3.2.0`
13
+ Current packaged version: `3.3.0`
14
14
 
15
15
  ## What Changed In This Version
16
16
 
17
- - Documented the published Bun-powered `grace` CLI more explicitly across skills and repo context.
18
- - Added `grace lint` guidance as a fast integrity preflight alongside reviewer, refresh, and status workflows.
19
- - Updated the public install example to use `bun add -g @osovv/grace-cli`.
17
+ - Removed profile selection from `grace lint`; the CLI now validates only against the current GRACE artifact set.
18
+ - Limited `.grace-lint.json` to the current config schema, such as `ignoredDirs`.
19
+ - Kept the role-aware/adaptive lint model while making older GRACE repos fail loudly until they are updated.
20
20
 
21
21
  ## Repository Layout
22
22
 
@@ -215,16 +215,11 @@ Optional repository config file:
215
215
 
216
216
  ```json
217
217
  {
218
- "profile": "auto",
219
218
  "ignoredDirs": ["tmp"]
220
219
  }
221
220
  ```
222
221
 
223
- Profiles:
224
-
225
- - `auto` => require `docs/verification-plan.xml` only when the repo already looks verification-aware
226
- - `current` => require current GRACE artifacts
227
- - `legacy` => allow older GRACE repos without a verification plan
222
+ `grace lint` is current-only. Older GRACE repositories should fail until they are updated to the current artifact set, especially `docs/verification-plan.xml`.
228
223
 
229
224
  The validator checks marketplace/plugin metadata sync, version consistency, required fields, `.claude-plugin` structure, and hardcoded absolute paths. In branch or PR context it scopes validation to changed plugins via `git diff origin/main...HEAD`; otherwise it validates all plugins.
230
225
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osovv/grace-cli",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "GRACE CLI for linting semantic markup, contracts, and GRACE XML artifacts with a Bun-powered grace binary.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/osovv/grace-marketplace#readme",
package/src/grace-lint.ts CHANGED
@@ -6,7 +6,6 @@ import { formatTextReport, isValidTextFormat, lintGraceProject } from "./lint/co
6
6
  import type { LintOptions, LintResult } from "./lint/types";
7
7
 
8
8
  export type {
9
- EffectiveProfile,
10
9
  GraceLintConfig,
11
10
  LanguageAdapter,
12
11
  LanguageAnalysis,
@@ -16,7 +15,6 @@ export type {
16
15
  LintSeverity,
17
16
  MapMode,
18
17
  ModuleRole,
19
- RepoProfile,
20
18
  } from "./lint/types";
21
19
 
22
20
  export { formatTextReport, lintGraceProject } from "./lint/core";
@@ -48,11 +46,6 @@ export const lintCommand = defineCommand({
48
46
  description: "Output format: text or json",
49
47
  default: "text",
50
48
  },
51
- profile: {
52
- type: "string",
53
- description: "Lint profile: auto, current, or legacy",
54
- default: "auto",
55
- },
56
49
  allowMissingDocs: {
57
50
  type: "boolean",
58
51
  description: "Allow repositories that do not yet have full GRACE docs",
@@ -67,7 +60,6 @@ export const lintCommand = defineCommand({
67
60
 
68
61
  const result = lintGraceProject(String(context.args.path ?? "."), {
69
62
  allowMissingDocs: Boolean(context.args.allowMissingDocs),
70
- profile: String(context.args.profile ?? "auto") as LintOptions["profile"],
71
63
  });
72
64
 
73
65
  writeResult(format, result);
package/src/grace.ts CHANGED
@@ -7,7 +7,7 @@ import { lintCommand } from "./grace-lint";
7
7
  const main = defineCommand({
8
8
  meta: {
9
9
  name: "grace",
10
- version: "3.2.0",
10
+ version: "3.3.0",
11
11
  description: "GRACE CLI for linting semantic markup and GRACE project artifacts.",
12
12
  },
13
13
  subCommands: {
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  import type { GraceLintConfig, LintIssue } from "./types";
5
5
 
6
6
  const CONFIG_FILE_NAME = ".grace-lint.json";
7
- const VALID_PROFILES = new Set(["auto", "current", "legacy"]);
7
+ const SUPPORTED_KEYS = new Set(["ignoredDirs"]);
8
8
 
9
9
  export function loadGraceLintConfig(projectRoot: string) {
10
10
  const configPath = path.join(projectRoot, CONFIG_FILE_NAME);
@@ -16,12 +16,26 @@ export function loadGraceLintConfig(projectRoot: string) {
16
16
  const parsed = JSON.parse(readFileSync(configPath, "utf8")) as GraceLintConfig;
17
17
  const issues: LintIssue[] = [];
18
18
 
19
- if (parsed.profile && !VALID_PROFILES.has(parsed.profile)) {
19
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
20
20
  issues.push({
21
21
  severity: "error",
22
- code: "config.invalid-profile",
22
+ code: "config.invalid-shape",
23
23
  file: CONFIG_FILE_NAME,
24
- message: `Unsupported profile \`${parsed.profile}\` in ${CONFIG_FILE_NAME}. Use \`auto\`, \`current\`, or \`legacy\`.`,
24
+ message: `${CONFIG_FILE_NAME} must contain a JSON object.`,
25
+ });
26
+ return { config: parsed, issues };
27
+ }
28
+
29
+ for (const key of Object.keys(parsed)) {
30
+ if (SUPPORTED_KEYS.has(key)) {
31
+ continue;
32
+ }
33
+
34
+ issues.push({
35
+ severity: "error",
36
+ code: "config.unknown-key",
37
+ file: CONFIG_FILE_NAME,
38
+ message: `Unsupported key \`${key}\` in ${CONFIG_FILE_NAME}. Supported keys: ignoredDirs.`,
25
39
  });
26
40
  }
27
41
 
package/src/lint/core.ts CHANGED
@@ -4,7 +4,6 @@ import path from "node:path";
4
4
  import { loadGraceLintConfig } from "./config";
5
5
  import { getLanguageAdapter } from "./adapters/base";
6
6
  import type {
7
- EffectiveProfile,
8
7
  GraceLintConfig,
9
8
  LanguageAnalysis,
10
9
  LintIssue,
@@ -15,13 +14,9 @@ import type {
15
14
  ModuleContractInfo,
16
15
  ModuleMapItem,
17
16
  ModuleRole,
18
- RepoProfile,
19
17
  } from "./types";
20
18
 
21
- const REQUIRED_DOCS_BY_PROFILE: Record<EffectiveProfile, string[]> = {
22
- legacy: ["docs/knowledge-graph.xml", "docs/development-plan.xml"],
23
- current: ["docs/knowledge-graph.xml", "docs/development-plan.xml", "docs/verification-plan.xml"],
24
- };
19
+ const REQUIRED_DOCS = ["docs/knowledge-graph.xml", "docs/development-plan.xml", "docs/verification-plan.xml"] as const;
25
20
 
26
21
  const OPTIONAL_PACKET_DOC = "docs/operational-packets.xml";
27
22
  const LINT_CONFIG_FILE = ".grace-lint.json";
@@ -557,46 +552,6 @@ function lintRequiredPacketSections(result: LintResult, relativePath: string, te
557
552
  }
558
553
  }
559
554
 
560
- function resolveProfile(
561
- requestedProfile: RepoProfile | undefined,
562
- configProfile: RepoProfile | undefined,
563
- docs: Record<string, string | null>,
564
- ) {
565
- const desiredProfile = requestedProfile ?? configProfile ?? "auto";
566
- if (desiredProfile !== "auto") {
567
- return desiredProfile;
568
- }
569
-
570
- const verificationPlan = docs["docs/verification-plan.xml"];
571
- if (verificationPlan) {
572
- return "current" as const;
573
- }
574
-
575
- const joinedDocs = `${docs["docs/knowledge-graph.xml"] ?? ""}\n${docs["docs/development-plan.xml"] ?? ""}`;
576
- return /<verification-ref>|<V-M-/.test(joinedDocs) ? "current" : "legacy";
577
- }
578
-
579
- function validateProfileSelection(profile: string | undefined) {
580
- if (!profile) {
581
- return null;
582
- }
583
-
584
- if (profile === "auto" || profile === "current" || profile === "legacy") {
585
- return null;
586
- }
587
-
588
- return {
589
- severity: "error",
590
- code: "config.invalid-profile-selection",
591
- file: "CLI",
592
- message: `Unsupported profile \`${profile}\`. Use \`auto\`, \`current\`, or \`legacy\`.`,
593
- } satisfies LintIssue;
594
- }
595
-
596
- function isInvalidProfileIssue(issue: LintIssue) {
597
- return issue.code === "config.invalid-profile" || issue.code === "config.invalid-profile-selection";
598
- }
599
-
600
555
  function lintExportMapParity(
601
556
  result: LintResult,
602
557
  relativePath: string,
@@ -771,11 +726,6 @@ function lintGovernedFile(result: LintResult, root: string, filePath: string, te
771
726
  export function lintGraceProject(projectRoot: string, options: LintOptions = {}): LintResult {
772
727
  const root = path.resolve(projectRoot);
773
728
  const { config, issues: configIssues } = loadGraceLintConfig(root);
774
- const requestedProfileIssue = validateProfileSelection(options.profile as string | undefined);
775
- const effectiveRequestedProfile = requestedProfileIssue ? undefined : options.profile;
776
- const effectiveConfigProfile = configIssues.some((issue) => issue.code === "config.invalid-profile")
777
- ? undefined
778
- : config?.profile;
779
729
 
780
730
  const docs = {
781
731
  "docs/knowledge-graph.xml": readTextIfExists(path.join(root, "docs/knowledge-graph.xml")),
@@ -783,29 +733,26 @@ export function lintGraceProject(projectRoot: string, options: LintOptions = {})
783
733
  "docs/verification-plan.xml": readTextIfExists(path.join(root, "docs/verification-plan.xml")),
784
734
  } satisfies Record<string, string | null>;
785
735
 
786
- const profile = resolveProfile(effectiveRequestedProfile, effectiveConfigProfile, docs);
787
736
  const result: LintResult = {
788
737
  root,
789
- profile,
790
738
  filesChecked: 0,
791
739
  governedFiles: 0,
792
740
  xmlFilesChecked: 0,
793
- issues: [...configIssues, ...(requestedProfileIssue ? [requestedProfileIssue] : [])],
741
+ issues: [...configIssues],
794
742
  };
795
743
 
796
- if (configIssues.some((issue) => issue.severity === "error" && issue.file === LINT_CONFIG_FILE && !isInvalidProfileIssue(issue))) {
744
+ if (configIssues.some((issue) => issue.severity === "error" && issue.file === LINT_CONFIG_FILE)) {
797
745
  return result;
798
746
  }
799
747
 
800
- const requiredDocs = REQUIRED_DOCS_BY_PROFILE[profile];
801
748
  if (!options.allowMissingDocs) {
802
- for (const relativePath of requiredDocs) {
749
+ for (const relativePath of REQUIRED_DOCS) {
803
750
  if (!docs[relativePath]) {
804
751
  addIssue(result, {
805
752
  severity: "error",
806
753
  code: "docs.missing-required-artifact",
807
754
  file: relativePath,
808
- message: `Missing required GRACE artifact \`${relativePath}\` for the ${profile} profile.`,
755
+ message: `Missing required current GRACE artifact \`${relativePath}\`.`,
809
756
  });
810
757
  }
811
758
  }
@@ -928,7 +875,6 @@ export function formatTextReport(result: LintResult) {
928
875
  "GRACE Lint Report",
929
876
  "=================",
930
877
  `Root: ${result.root}`,
931
- `Profile: ${result.profile}`,
932
878
  `Code files checked: ${result.filesChecked}`,
933
879
  `Governed files checked: ${result.governedFiles}`,
934
880
  `XML files checked: ${result.xmlFilesChecked}`,
package/src/lint/types.ts CHANGED
@@ -1,8 +1,5 @@
1
1
  export type LintSeverity = "error" | "warning";
2
2
 
3
- export type RepoProfile = "auto" | "current" | "legacy";
4
- export type EffectiveProfile = Exclude<RepoProfile, "auto">;
5
-
6
3
  export type ModuleRole = "RUNTIME" | "TEST" | "BARREL" | "CONFIG" | "TYPES" | "SCRIPT";
7
4
  export type MapMode = "EXPORTS" | "LOCALS" | "SUMMARY" | "NONE";
8
5
 
@@ -16,7 +13,6 @@ export type LintIssue = {
16
13
 
17
14
  export type LintResult = {
18
15
  root: string;
19
- profile: EffectiveProfile;
20
16
  filesChecked: number;
21
17
  governedFiles: number;
22
18
  xmlFilesChecked: number;
@@ -25,11 +21,9 @@ export type LintResult = {
25
21
 
26
22
  export type LintOptions = {
27
23
  allowMissingDocs?: boolean;
28
- profile?: RepoProfile;
29
24
  };
30
25
 
31
26
  export type GraceLintConfig = {
32
- profile?: RepoProfile;
33
27
  ignoredDirs?: string[];
34
28
  };
35
29