traits 0.5.0 → 0.5.1

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/bin.ts ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ // traits-ignore-file dawn_sparrow_wolf-37a52965-6425-476e-bc3a-d20ce70bc1a6: Effect utilities are unavailable until this launcher installs dependencies.
3
+ import { spawn } from "node:child_process"
4
+ import { once } from "node:events"
5
+ import { existsSync, readFileSync } from "node:fs"
6
+ import { join } from "node:path"
7
+
8
+ const candidates = [
9
+ ["pnpm", ["pnpm-lock.yaml"]],
10
+ ["npm", ["package-lock.json", "npm-shrinkwrap.json"]],
11
+ ["yarn", ["yarn.lock"]],
12
+ ["bun", ["bun.lock", "bun.lockb"]]
13
+ ] as const
14
+
15
+ const [command, args] = installCommand(process.cwd())
16
+ process.stderr.write(`[traits] Installing dependencies in ${process.cwd()} using ${command}\n`)
17
+ await install(command, args)
18
+ await import("./main.ts")
19
+
20
+ function installCommand(root: string): [string, string[]] {
21
+ const manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf8"))
22
+ const declared =
23
+ typeof manifest === "object" && manifest !== null && "packageManager" in manifest
24
+ ? manifest.packageManager
25
+ : undefined
26
+ const matches = candidates.filter((candidate) => candidate[1].some((file) => existsSync(join(root, file))))
27
+ const manager =
28
+ typeof declared === "string" ? declared.split("@")[0] : matches.length === 1 ? matches[0]?.[0] : undefined
29
+ if (manager === undefined || !candidates.some((candidate) => candidate[0] === manager)) {
30
+ throw new Error("Set package.json#packageManager to pnpm, npm, yarn, or bun to select the installer")
31
+ }
32
+ const locked =
33
+ candidates.find((candidate) => candidate[0] === manager)?.[1].some((file) => existsSync(join(root, file))) ?? false
34
+ if (manager === "npm") return [manager, [locked ? "ci" : "install"]]
35
+ if (manager === "yarn") {
36
+ const modern =
37
+ typeof declared === "string"
38
+ ? !declared.startsWith("yarn@1.")
39
+ : /^__metadata:/m.test(readFileSync(join(root, "yarn.lock"), "utf8"))
40
+ return [manager, ["install", ...(locked ? [modern ? "--immutable" : "--frozen-lockfile"] : [])]]
41
+ }
42
+ return [manager, ["install", ...(locked ? ["--frozen-lockfile"] : [])]]
43
+ }
44
+
45
+ function install(command: string, args: string[]) {
46
+ const child = spawn(command, args, {
47
+ stdio: ["ignore", process.stderr, process.stderr]
48
+ })
49
+ const interrupt = () => {
50
+ child.kill("SIGINT")
51
+ }
52
+ const terminate = () => {
53
+ child.kill("SIGTERM")
54
+ }
55
+ process.on("SIGINT", interrupt)
56
+ process.on("SIGTERM", terminate)
57
+ return once(child, "close")
58
+ .then((result) => {
59
+ if (result[0] !== 0) throw new Error(`${command} exited with ${result[1] ?? result[0]}`)
60
+ })
61
+ .finally(() => {
62
+ process.off("SIGINT", interrupt)
63
+ process.off("SIGTERM", terminate)
64
+ })
65
+ }
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ // traits-ignore-file dawn_sparrow_wolf-37a52965-6425-476e-bc3a-d20ce70bc1a6: Effect utilities are unavailable until this launcher installs dependencies.
3
+ import { spawn } from "node:child_process";
4
+ import { once } from "node:events";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ const candidates = [
8
+ ["pnpm", ["pnpm-lock.yaml"]],
9
+ ["npm", ["package-lock.json", "npm-shrinkwrap.json"]],
10
+ ["yarn", ["yarn.lock"]],
11
+ ["bun", ["bun.lock", "bun.lockb"]]
12
+ ];
13
+ const [command, args] = installCommand(process.cwd());
14
+ process.stderr.write(`[traits] Installing dependencies in ${process.cwd()} using ${command}\n`);
15
+ await install(command, args);
16
+ await import("./main.js");
17
+ function installCommand(root) {
18
+ const manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
19
+ const declared = typeof manifest === "object" && manifest !== null && "packageManager" in manifest
20
+ ? manifest.packageManager
21
+ : undefined;
22
+ const matches = candidates.filter((candidate) => candidate[1].some((file) => existsSync(join(root, file))));
23
+ const manager = typeof declared === "string" ? declared.split("@")[0] : matches.length === 1 ? matches[0]?.[0] : undefined;
24
+ if (manager === undefined || !candidates.some((candidate) => candidate[0] === manager)) {
25
+ throw new Error("Set package.json#packageManager to pnpm, npm, yarn, or bun to select the installer");
26
+ }
27
+ const locked = candidates.find((candidate) => candidate[0] === manager)?.[1].some((file) => existsSync(join(root, file))) ?? false;
28
+ if (manager === "npm")
29
+ return [manager, [locked ? "ci" : "install"]];
30
+ if (manager === "yarn") {
31
+ const modern = typeof declared === "string"
32
+ ? !declared.startsWith("yarn@1.")
33
+ : /^__metadata:/m.test(readFileSync(join(root, "yarn.lock"), "utf8"));
34
+ return [manager, ["install", ...(locked ? [modern ? "--immutable" : "--frozen-lockfile"] : [])]];
35
+ }
36
+ return [manager, ["install", ...(locked ? ["--frozen-lockfile"] : [])]];
37
+ }
38
+ function install(command, args) {
39
+ const child = spawn(command, args, {
40
+ stdio: ["ignore", process.stderr, process.stderr]
41
+ });
42
+ const interrupt = () => {
43
+ child.kill("SIGINT");
44
+ };
45
+ const terminate = () => {
46
+ child.kill("SIGTERM");
47
+ };
48
+ process.on("SIGINT", interrupt);
49
+ process.on("SIGTERM", terminate);
50
+ return once(child, "close")
51
+ .then((result) => {
52
+ if (result[0] !== 0)
53
+ throw new Error(`${command} exited with ${result[1] ?? result[0]}`);
54
+ })
55
+ .finally(() => {
56
+ process.off("SIGINT", interrupt);
57
+ process.off("SIGTERM", terminate);
58
+ });
59
+ }
60
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../bin.ts"],"names":[],"mappings":"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"}
package/dist/main.js CHANGED
@@ -6,7 +6,6 @@ import { Command, Flag } from "effect/cli";
6
6
  import { FetchHttpClient } from "effect/http";
7
7
  import * as TraitsConfigProvider from "./_util/TraitsConfigProvider.js";
8
8
  import * as TraitsTelemetry from "./_util/TraitsTelemetry.js";
9
- import { init } from "./commands/init.js";
10
9
  import PackageJson from "./package.json" with { type: "json" };
11
10
  import { toolkit } from "./toolkit/toolkit.js";
12
11
  import * as TraitsConfig from "./TraitsConfig.js";
@@ -69,5 +68,5 @@ Command.make("traits", {
69
68
  | An empty result only means no failures for the selected files and traits; empty selections or an empty store perform no assessments.
70
69
  | On mutation errors, inspect the store before retrying: writes and removals are not transactional, and retries may duplicate saved traits.
71
70
  `)
72
- }).pipe(Layer.provide(toolkit.pipe(Layer.provide(TraitStore.layerFileSystem(".agents/traits")), Layer.provide(TraitsConfig.layer), Layer.provideMerge(McpServer.McpServer.layer))), Layer.launch, Effect.withSpan("traits.server"), Effect.provide(TraitsTelemetry.layer(Option.getOrUndefined(params.otlpEndpoint)).pipe(Layer.provideMerge(TraitsConfigProvider.layer), Layer.provideMerge(FetchHttpClient.layer))))), Command.withSubcommands([init]), Command.run({ version: PackageJson.version }), Effect.provide(NodeServices.layer), Effect.tapCause(Effect.logError), Effect.provideService(Logger.LogToStderr, true), NodeRuntime.runMain({ disableErrorReporting: true }));
71
+ }).pipe(Layer.provide(toolkit.pipe(Layer.provide(TraitStore.layerFileSystem(".agents/traits")), Layer.provide(TraitsConfig.layer), Layer.provideMerge(McpServer.McpServer.layer))), Layer.launch, Effect.withSpan("traits.server"), Effect.provide(TraitsTelemetry.layer(Option.getOrUndefined(params.otlpEndpoint)).pipe(Layer.provideMerge(TraitsConfigProvider.layer), Layer.provideMerge(FetchHttpClient.layer))))), Command.run({ version: PackageJson.version }), Effect.provide(NodeServices.layer), Effect.tapCause(Effect.logError), Effect.provideService(Logger.LogToStderr, true), NodeRuntime.runMain({ disableErrorReporting: true }));
73
72
  //# sourceMappingURL=main.js.map
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":"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"}
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traits",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,13 +8,12 @@
8
8
  "directory": "traits"
9
9
  },
10
10
  "bin": {
11
- "traits": "./dist/main.js"
11
+ "traits": "./dist/bin.js"
12
12
  },
13
13
  "files": [
14
14
  "*.ts",
15
15
  "!vite.config.ts",
16
16
  "_util/**/*.ts",
17
- "commands/**/*.ts",
18
17
  "skills/**/*.md",
19
18
  "toolkit/**/*.ts",
20
19
  "dist/package.json",
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=TraitIgnores.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraitIgnores.test.d.ts","sourceRoot":"","sources":["../../test/TraitIgnores.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import { expect, it } from "vite-plus/test";
2
+ import { traitIgnores } from "../traitIgnores.js";
3
+ it("reads repeated directives from the leading header", () => {
4
+ const ids = traitIgnores("#!/usr/bin/env node\n\n// header\n// traits-ignore-file first: reason\n// traits-ignore-file second: another reason\nimport 'module'");
5
+ expect([...ids]).toEqual(["first", "second"]);
6
+ });
7
+ it("requires a reason and exactly one ID", () => {
8
+ expect([
9
+ ...traitIgnores("// traits-ignore-file first\n// traits-ignore-file second: \n// traits-ignore-file first second: reason")
10
+ ]).toEqual([]);
11
+ });
12
+ it("does not interpret strings or comments after code", () => {
13
+ expect([
14
+ ...traitIgnores("const text = `\n// traits-ignore-file first: reason\n`\n// traits-ignore-file second: reason")
15
+ ]).toEqual([]);
16
+ });
17
+ it("handles CRLF and deduplicates IDs", () => {
18
+ expect([...traitIgnores("// traits-ignore-file first: reason\r\n// traits-ignore-file first: repeated\r\n")]).toEqual(["first"]);
19
+ });
20
+ //# sourceMappingURL=TraitIgnores.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraitIgnores.test.js","sourceRoot":"","sources":["../../test/TraitIgnores.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;IAC3D,MAAM,GAAG,GAAG,YAAY,CACtB,sIAAsI,CACvI,CAAA;IACD,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;AAC/C,CAAC,CAAC,CAAA;AAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAC9C,MAAM,CAAC;QACL,GAAG,YAAY,CACb,2GAA2G,CAC5G;KACF,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;AAChB,CAAC,CAAC,CAAA;AAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;IAC3D,MAAM,CAAC;QACL,GAAG,YAAY,CAAC,8FAA8F,CAAC;KAChH,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;AAChB,CAAC,CAAC,CAAA;AAEF,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;IAC3C,MAAM,CAAC,CAAC,GAAG,YAAY,CAAC,kFAAkF,CAAC,CAAC,CAAC,CAAC,OAAO,CACnH,CAAC,OAAO,CAAC,CACV,CAAA;AACH,CAAC,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"CheckTraits.d.ts","sourceRoot":"","sources":["../../toolkit/CheckTraits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,UAAU,EAAE,IAAI,EAAuB,MAAM,IAAI,CAAC,EAAkB,MAAM,QAAQ,CAAA;AAC1G,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAgB,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAKlE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAE9D,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0IAmDY,CAAA;AAEpC,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA4B,CAAA;AAEhD,eAAO,MAAM,KAAK,0EAoFhB,CAAA"}
1
+ {"version":3,"file":"CheckTraits.d.ts","sourceRoot":"","sources":["../../toolkit/CheckTraits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,UAAU,EAAE,IAAI,EAAuB,MAAM,IAAI,CAAC,EAAkB,MAAM,QAAQ,CAAA;AAC1G,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAgB,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAMlE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAE9D,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0IAoDY,CAAA;AAEpC,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA4B,CAAA;AAEhD,eAAO,MAAM,KAAK,0EAsFhB,CAAA"}
@@ -4,6 +4,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process";
4
4
  import { Source, SourceLocation } from "../Source.js";
5
5
  import { TraitId } from "../Trait.js";
6
6
  import { evaluate, requiresReview, TraitEvaluation } from "../TraitEvaluation.js";
7
+ import { traitIgnores } from "../traitIgnores.js";
7
8
  import { TraitsContext } from "../TraitsContext.js";
8
9
  import { TraitStore, TraitStoreError } from "../TraitStore.js";
9
10
  export const CheckTraits = Tool.make("check_traits", {
@@ -19,7 +20,8 @@ export const CheckTraits = Tool.make("check_traits", {
19
20
  | Git-ignored files are excluded by default, with ignore rules refreshed on every call. Untracked files do not need to be added to Git to be evaluated. Tracked files remain eligible even if ignore rules match them. Set excludeGitignored: false in traits.ts to disable this filter; explicit excludes still apply. Git failures fail the check.
20
21
  | All entries follow native glob syntax, including escaping filenames with glob characters. No custom directory expansion or literal-path classification is applied.
21
22
  | Reads current full file contents, including unstaged edits, and checks each 200-line chunk against the selected traits in parallel.
22
- | Omit traitIds to check all stored traits; supply trait ids for a focused check of one or more traits. Unknown IDs fail the call.
23
+ | Omit traitIds to check all stored traits; supply trait ids for a focused check of one or more traits. Unknown IDs fail the call.
24
+ | A leading // traits-ignore-file <trait-id>: <reason> comment skips only that trait for the entire file, including focused checks. Directives require a reason and must precede code.
23
25
  | Returns findings with the trait, source text, absolute path, 1-based chunk startLine, and assessment classification and probabilities.
24
26
  | Classification distinguishes violation from inconclusive; probabilities are not calibrated guarantees. No exact offending lines are provided.
25
27
  | Diagnose uncertainty using the full distribution, not the selected label alone; inspect source and trait before changing code.
@@ -96,12 +98,14 @@ export const layer = toolkit.toLayer({
96
98
  const paths = yield* Effect.filter(includedFiles === undefined ? resolved : Array.filter(resolved, (file) => includedFiles.has(file)), (file) => fs.stat(file).pipe(Effect.map((info) => info.type === "File")), { concurrency: "unbounded" });
97
99
  const results = yield* Effect.forEach(paths, Effect.fn("CheckTraits.file")(function* (file) {
98
100
  const text = yield* fs.readFileString(file);
101
+ const ignored = traitIgnores(text);
102
+ const activeTraits = Array.filter(traits, (trait) => !ignored.has(trait.id));
99
103
  const chunks = Array.chunksOf(String.split(text, "\n"), 200);
100
104
  const sources = Array.map(chunks, (lines, index) => Source.make({
101
105
  text: Array.join(lines, "\n"),
102
106
  location: SourceLocation.cases.File.make({ path: file, startLine: index * 200 + 1 })
103
107
  }));
104
- const results = yield* Effect.forEach(sources, (source) => Effect.forEach(traits, (trait) => evaluate(trait, source), { concurrency: "unbounded" }), { concurrency: "unbounded" });
108
+ const results = yield* Effect.forEach(sources, (source) => Effect.forEach(activeTraits, (trait) => evaluate(trait, source), { concurrency: "unbounded" }), { concurrency: "unbounded" });
105
109
  return pipe(results, Array.flatten, Array.filter(requiresReview));
106
110
  }), { concurrency: "unbounded" });
107
111
  return Array.flatten(results);
@@ -1 +1 @@
1
- {"version":3,"file":"CheckTraits.js","sourceRoot":"","sources":["../../toolkit/CheckTraits.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CheckTraits.js","sourceRoot":"","sources":["../../toolkit/CheckTraits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** File-scoped exceptions must appear in the leading line-comment header and include a reason. */
2
+ export declare const traitIgnores: (text: string) => ReadonlySet<string>;
3
+ //# sourceMappingURL=traitIgnores.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traitIgnores.d.ts","sourceRoot":"","sources":["../traitIgnores.ts"],"names":[],"mappings":"AAEA,kGAAkG;AAClG,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,WAAW,CAAC,MAAM,CAU7D,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { pipe, String } from "effect";
2
+ /** File-scoped exceptions must appear in the leading line-comment header and include a reason. */
3
+ export const traitIgnores = (text) => {
4
+ const ids = new Set();
5
+ for (const line of String.split(text, "\n")) {
6
+ const trimmed = String.trim(line);
7
+ if (trimmed === "" || pipe(trimmed, String.startsWith("#!")))
8
+ continue;
9
+ if (!pipe(trimmed, String.startsWith("//")))
10
+ break;
11
+ const match = /^\/\/\s*traits-ignore-file\s+(\S+)\s*:\s*(\S.*)$/.exec(trimmed);
12
+ if (match?.[1] !== undefined)
13
+ ids.add(match[1]);
14
+ }
15
+ return ids;
16
+ };
17
+ //# sourceMappingURL=traitIgnores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traitIgnores.js","sourceRoot":"","sources":["../traitIgnores.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAErC,kGAAkG;AAClG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAuB,EAAE;IAChE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAA;IAC7B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjC,IAAI,OAAO,KAAK,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAAE,SAAQ;QACtE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAAE,MAAK;QAClD,MAAM,KAAK,GAAG,kDAAkD,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9E,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS;YAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
package/main.ts CHANGED
@@ -7,7 +7,6 @@ import { FetchHttpClient } from "effect/http"
7
7
 
8
8
  import * as TraitsConfigProvider from "./_util/TraitsConfigProvider.ts"
9
9
  import * as TraitsTelemetry from "./_util/TraitsTelemetry.ts"
10
- import { init } from "./commands/init.ts"
11
10
  import PackageJson from "./package.json" with { type: "json" }
12
11
  import { toolkit } from "./toolkit/toolkit.ts"
13
12
  import * as TraitsConfig from "./TraitsConfig.ts"
@@ -104,7 +103,6 @@ Command.make("traits", {
104
103
  )
105
104
  )
106
105
  ),
107
- Command.withSubcommands([init]),
108
106
  Command.run({ version: PackageJson.version }),
109
107
  Effect.provide(NodeServices.layer),
110
108
  Effect.tapCause(Effect.logError),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traits",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,13 +8,12 @@
8
8
  "directory": "traits"
9
9
  },
10
10
  "bin": {
11
- "traits": "./dist/main.js"
11
+ "traits": "./dist/bin.js"
12
12
  },
13
13
  "files": [
14
14
  "*.ts",
15
15
  "!vite.config.ts",
16
16
  "_util/**/*.ts",
17
- "commands/**/*.ts",
18
17
  "skills/**/*.md",
19
18
  "toolkit/**/*.ts",
20
19
  "dist/package.json",
@@ -180,6 +180,25 @@ store before retrying; some changes may already be present.
180
180
  rejected false positives as caveats. Never claim an automated clean pass when
181
181
  raw findings remain, or turn an empty result into unconditional compliance.
182
182
 
183
+ ### Targeted trait exceptions
184
+
185
+ For a justified file-scoped exception, add a line comment in the leading header,
186
+ before any code:
187
+
188
+ ```ts
189
+ // traits-ignore-file <full-trait-id>: Explain why this trait cannot apply here.
190
+ ```
191
+
192
+ Each directive requires a nonempty reason and exactly one full trait ID. Repeat
193
+ the comment for additional exceptions. Only the referenced traits are skipped;
194
+ other traits remain checked. Exceptions apply to all file chunks and focused
195
+ checks. Comments after code are ignored.
196
+
197
+ Read the referenced trait and verify the reason before adding or retaining an
198
+ exception. Do not add directives merely to silence findings: fix applicable
199
+ violations, and ask the user before introducing an exception that overrides
200
+ their preference. Respect existing justified exceptions during manual review.
201
+
183
202
  ### Tool behavior and remediation
184
203
 
185
204
  - After generating or editing TypeScript files, select those files and call
@@ -259,22 +278,35 @@ skipped remediation or lost signal.
259
278
 
260
279
  ## Operation
261
280
 
281
+ The CLI launcher installs project dependencies in its current directory before
282
+ importing the MCP server. Run it from the checkout/workspace root. It selects
283
+ the package manager from `package.json#packageManager` or an unambiguous
284
+ lockfile; the selected executable must be on PATH. Installation preserves
285
+ lockfiles but can execute lifecycle scripts, so only launch in trusted
286
+ checkouts. Allow enough MCP startup time for a cold install and avoid
287
+ simultaneous installations in the same worktree. During development, use
288
+ `node traits/bin.ts` with a Node version supporting native TypeScript execution;
289
+ pass server flags after the filename.
290
+
291
+ Configuration and providers currently initialize at server startup. Startup
292
+ failures go to stderr and require fixing setup and reconnecting; whether the
293
+ agent can see these diagnostics depends on its MCP client. Expected tool
294
+ failures use MCP error results; unexpected defects receive a generic internal
295
+ error rather than implementation details.
296
+
262
297
  Git-ignored files are excluded by default, with ignore rules refreshed on every
263
298
  check. Untracked files do not need to be added to Git to be evaluated. Tracked
264
299
  files stay eligible even if ignore rules match them. Set
265
300
  `excludeGitignored: false` in `traits.ts` to disable this filter; explicit
266
301
  excludes still apply. Git failures fail checks.
267
302
 
268
- Run `npx traits init` from inside the Git checkout to create `traits.ts` at the
269
- current checkout root. Existing files are preserved; init does not install
270
- skills. Install this bundled skill with pnpm 12.11+ (`pnpm add -D traits`, then
271
- approve skills with `pnpm approve`) or, after installing Traits locally, run
272
- `npx skills add ./node_modules/traits --skill traits` to choose your agents.
273
- pnpm may need explicit `skills.dirs`; dlx and global installs do not link
274
- skills. The skills CLI installs a snapshot; rerun after updating Traits.
275
- Preserve any customizations in older copies and avoid loading duplicate Traits
276
- skills. Launch the MCP server with `npx traits` from the project root. Configure
277
- the server in your coding agent and discover its `derive_trait` and
303
+ For setup, follow the canonical guide at
304
+ https://github.com/crosshatch/traits/blob/main/SETUP.md. Respect the consumer's
305
+ package manager and workspace dependency conventions, ask about providers and
306
+ models before writing configuration, and preserve existing settings. The guide
307
+ covers dependencies, credentials, skill installation, MCP connection, and
308
+ troubleshooting. The agent creates traits.ts using the selected providers; there
309
+ is no init command. Discover the connected server's `derive_trait` and
278
310
  `check_traits` tools through the client's tool catalog.
279
311
 
280
312
  The CLI's configuration layer loads and validates the default-exported object
@@ -5,6 +5,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"
5
5
  import { Source, SourceLocation } from "../Source.ts"
6
6
  import { TraitId } from "../Trait.ts"
7
7
  import { evaluate, requiresReview, TraitEvaluation } from "../TraitEvaluation.ts"
8
+ import { traitIgnores } from "../traitIgnores.ts"
8
9
  import { TraitsContext } from "../TraitsContext.ts"
9
10
  import { TraitStore, TraitStoreError } from "../TraitStore.ts"
10
11
 
@@ -21,7 +22,8 @@ export const CheckTraits = Tool.make("check_traits", {
21
22
  | Git-ignored files are excluded by default, with ignore rules refreshed on every call. Untracked files do not need to be added to Git to be evaluated. Tracked files remain eligible even if ignore rules match them. Set excludeGitignored: false in traits.ts to disable this filter; explicit excludes still apply. Git failures fail the check.
22
23
  | All entries follow native glob syntax, including escaping filenames with glob characters. No custom directory expansion or literal-path classification is applied.
23
24
  | Reads current full file contents, including unstaged edits, and checks each 200-line chunk against the selected traits in parallel.
24
- | Omit traitIds to check all stored traits; supply trait ids for a focused check of one or more traits. Unknown IDs fail the call.
25
+ | Omit traitIds to check all stored traits; supply trait ids for a focused check of one or more traits. Unknown IDs fail the call.
26
+ | A leading // traits-ignore-file <trait-id>: <reason> comment skips only that trait for the entire file, including focused checks. Directives require a reason and must precede code.
25
27
  | Returns findings with the trait, source text, absolute path, 1-based chunk startLine, and assessment classification and probabilities.
26
28
  | Classification distinguishes violation from inconclusive; probabilities are not calibrated guarantees. No exact offending lines are provided.
27
29
  | Diagnose uncertainty using the full distribution, not the selected label alone; inspect source and trait before changing code.
@@ -129,6 +131,8 @@ export const layer = toolkit.toLayer({
129
131
  paths,
130
132
  Effect.fn("CheckTraits.file")(function* (file) {
131
133
  const text = yield* fs.readFileString(file)
134
+ const ignored = traitIgnores(text)
135
+ const activeTraits = Array.filter(traits, (trait) => !ignored.has(trait.id))
132
136
  const chunks = Array.chunksOf(String.split(text, "\n"), 200)
133
137
  const sources = Array.map(chunks, (lines, index) =>
134
138
  Source.make({
@@ -138,7 +142,7 @@ export const layer = toolkit.toLayer({
138
142
  )
139
143
  const results = yield* Effect.forEach(
140
144
  sources,
141
- (source) => Effect.forEach(traits, (trait) => evaluate(trait, source), { concurrency: "unbounded" }),
145
+ (source) => Effect.forEach(activeTraits, (trait) => evaluate(trait, source), { concurrency: "unbounded" }),
142
146
  { concurrency: "unbounded" }
143
147
  )
144
148
  return pipe(results, Array.flatten, Array.filter(requiresReview))
@@ -0,0 +1,14 @@
1
+ import { pipe, String } from "effect"
2
+
3
+ /** File-scoped exceptions must appear in the leading line-comment header and include a reason. */
4
+ export const traitIgnores = (text: string): ReadonlySet<string> => {
5
+ const ids = new Set<string>()
6
+ for (const line of String.split(text, "\n")) {
7
+ const trimmed = String.trim(line)
8
+ if (trimmed === "" || pipe(trimmed, String.startsWith("#!"))) continue
9
+ if (!pipe(trimmed, String.startsWith("//"))) break
10
+ const match = /^\/\/\s*traits-ignore-file\s+(\S+)\s*:\s*(\S.*)$/.exec(trimmed)
11
+ if (match?.[1] !== undefined) ids.add(match[1])
12
+ }
13
+ return ids
14
+ }
package/commands/init.ts DELETED
@@ -1,73 +0,0 @@
1
- import { Console, Effect, FileSystem, Path, String } from "effect"
2
- import { Command } from "effect/cli"
3
-
4
- import { getWorktreeRoot } from "../_util/Git.ts"
5
-
6
- const configuration = String.stripMargin(`
7
- |import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai"
8
- |import { TypeSafeClient, TypeSafeDecisionModel } from "@effect/ai-typesafe"
9
- |import { Config, Layer } from "effect"
10
- |import type { TraitsConfig } from "traits"
11
- |
12
- |export default {
13
- | excludes: ["**/dist/**", "node_modules/**"],
14
- | layerDecisionModel: TypeSafeDecisionModel.layer({ model: "jev-latest" }).pipe(
15
- | Layer.provide(TypeSafeClient.layerConfig())
16
- | ),
17
- | layerLanguageModel: OpenAiLanguageModel.layer({
18
- | model: "gpt-6.1-sol",
19
- | config: { reasoning: { effort: "low" } }
20
- | }).pipe(Layer.provide(OpenAiClient.layerConfig({ apiKey: Config.Redacted("OPENAI_API_KEY") })))
21
- |} satisfies TraitsConfig
22
- `)
23
-
24
- const nextSteps = String.stripMargin(`
25
- |
26
- |Next steps:
27
- |
28
- |1. Edit traits.ts to choose your decision and language models. Install the
29
- | provider packages imported by that file in your project.
30
- | Git-ignored files are excluded by default; set excludeGitignored: false
31
- | in traits.ts to disable the fresh Git file-set filter on each check.
32
- |
33
- |2. Configure your chosen providers' API keys in the MCP process environment
34
- | or an untracked .env at the checkout root. The default providers use
35
- | TYPESAFE_API_KEY and OPENAI_API_KEY. Never commit your secrets.
36
- |
37
- |3. Add Traits as a local MCP server in your coding agent of choice:
38
- | command: npx -y traits
39
- | working directory: the current checkout root
40
- | Connect the server and confirm derive_trait and check_traits are available.
41
- |
42
- |4. Install the bundled Traits skill from the checkout root:
43
- | pnpm 12.11+: pnpm add -D traits, then pnpm approve to approve its skills.
44
- | pnpm links into existing agent skill directories or the running agent's
45
- | directory. Set skills.dirs in pnpm-workspace.yaml if needed.
46
- | Other package managers or older pnpm: install traits locally, then run
47
- | npx skills add ./node_modules/traits --skill traits to choose your agents.
48
- | Without a local Traits dependency, run:
49
- | npx skills add https://github.com/crosshatch/traits/tree/main/traits/skills/traits
50
- | This uses the repository version; local installation uses your package's skill.
51
- | The skills CLI installs a snapshot; rerun after updating Traits.
52
- | pnpm dlx and global installs do not link skills.
53
- | Existing skill copies are left untouched. Back up any customizations before
54
- | replacing an old copy, and avoid loading both it and a package-managed skill.
55
- `)
56
-
57
- export const init = Command.make("init").pipe(
58
- Command.withHandler(
59
- Effect.fn("traits.init")(function* () {
60
- const fs = yield* FileSystem.FileSystem
61
- const path = yield* Path.Path
62
- const root = yield* getWorktreeRoot
63
- const destination = path.join(root, "traits.ts")
64
- if (yield* fs.exists(destination)) {
65
- yield* Console.log(`Leaving existing "${destination}" unchanged`)
66
- } else {
67
- yield* fs.writeFileString(destination, configuration, { flag: "wx" })
68
- yield* Console.log(`Created "${destination}"`)
69
- }
70
- yield* Console.log(nextSteps)
71
- })
72
- )
73
- )
@@ -1,4 +0,0 @@
1
- import { FileSystem, Path } from "effect";
2
- import { Command } from "effect/cli";
3
- export declare const init: Command.Command<"init", {}, {}, import("../_util/Git.ts").GitRootError | import("effect/PlatformError").PlatformError, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path>;
4
- //# sourceMappingURL=init.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,UAAU,EAAE,IAAI,EAAU,MAAM,QAAQ,CAAA;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAuDpC,eAAO,MAAM,IAAI,8NAgBhB,CAAA"}
@@ -1,67 +0,0 @@
1
- import { Console, Effect, FileSystem, Path, String } from "effect";
2
- import { Command } from "effect/cli";
3
- import { getWorktreeRoot } from "../_util/Git.js";
4
- const configuration = String.stripMargin(`
5
- |import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai"
6
- |import { TypeSafeClient, TypeSafeDecisionModel } from "@effect/ai-typesafe"
7
- |import { Config, Layer } from "effect"
8
- |import type { TraitsConfig } from "traits"
9
- |
10
- |export default {
11
- | excludes: ["**/dist/**", "node_modules/**"],
12
- | layerDecisionModel: TypeSafeDecisionModel.layer({ model: "jev-latest" }).pipe(
13
- | Layer.provide(TypeSafeClient.layerConfig())
14
- | ),
15
- | layerLanguageModel: OpenAiLanguageModel.layer({
16
- | model: "gpt-6.1-sol",
17
- | config: { reasoning: { effort: "low" } }
18
- | }).pipe(Layer.provide(OpenAiClient.layerConfig({ apiKey: Config.Redacted("OPENAI_API_KEY") })))
19
- |} satisfies TraitsConfig
20
- `);
21
- const nextSteps = String.stripMargin(`
22
- |
23
- |Next steps:
24
- |
25
- |1. Edit traits.ts to choose your decision and language models. Install the
26
- | provider packages imported by that file in your project.
27
- | Git-ignored files are excluded by default; set excludeGitignored: false
28
- | in traits.ts to disable the fresh Git file-set filter on each check.
29
- |
30
- |2. Configure your chosen providers' API keys in the MCP process environment
31
- | or an untracked .env at the checkout root. The default providers use
32
- | TYPESAFE_API_KEY and OPENAI_API_KEY. Never commit your secrets.
33
- |
34
- |3. Add Traits as a local MCP server in your coding agent of choice:
35
- | command: npx -y traits
36
- | working directory: the current checkout root
37
- | Connect the server and confirm derive_trait and check_traits are available.
38
- |
39
- |4. Install the bundled Traits skill from the checkout root:
40
- | pnpm 12.11+: pnpm add -D traits, then pnpm approve to approve its skills.
41
- | pnpm links into existing agent skill directories or the running agent's
42
- | directory. Set skills.dirs in pnpm-workspace.yaml if needed.
43
- | Other package managers or older pnpm: install traits locally, then run
44
- | npx skills add ./node_modules/traits --skill traits to choose your agents.
45
- | Without a local Traits dependency, run:
46
- | npx skills add https://github.com/crosshatch/traits/tree/main/traits/skills/traits
47
- | This uses the repository version; local installation uses your package's skill.
48
- | The skills CLI installs a snapshot; rerun after updating Traits.
49
- | pnpm dlx and global installs do not link skills.
50
- | Existing skill copies are left untouched. Back up any customizations before
51
- | replacing an old copy, and avoid loading both it and a package-managed skill.
52
- `);
53
- export const init = Command.make("init").pipe(Command.withHandler(Effect.fn("traits.init")(function* () {
54
- const fs = yield* FileSystem.FileSystem;
55
- const path = yield* Path.Path;
56
- const root = yield* getWorktreeRoot;
57
- const destination = path.join(root, "traits.ts");
58
- if (yield* fs.exists(destination)) {
59
- yield* Console.log(`Leaving existing "${destination}" unchanged`);
60
- }
61
- else {
62
- yield* fs.writeFileString(destination, configuration, { flag: "wx" });
63
- yield* Console.log(`Created "${destination}"`);
64
- }
65
- yield* Console.log(nextSteps);
66
- })));
67
- //# sourceMappingURL=init.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;CAgBxC,CAAC,CAAA;AAEF,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BpC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAC3C,OAAO,CAAC,WAAW,CACjB,MAAM,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC;IAChC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAA;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;IAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,eAAe,CAAA;IACnC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;IAChD,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,WAAW,aAAa,CAAC,CAAA;IACnE,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,WAAW,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACrE,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,WAAW,GAAG,CAAC,CAAA;IAChD,CAAC;IACD,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;AAC/B,CAAC,CAAC,CACH,CACF,CAAA"}