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 +65 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +60 -0
- package/dist/bin.js.map +1 -0
- package/dist/main.js +1 -2
- package/dist/main.js.map +1 -1
- package/dist/package.json +2 -3
- package/dist/test/TraitIgnores.test.d.ts +2 -0
- package/dist/test/TraitIgnores.test.d.ts.map +1 -0
- package/dist/test/TraitIgnores.test.js +20 -0
- package/dist/test/TraitIgnores.test.js.map +1 -0
- package/dist/toolkit/CheckTraits.d.ts.map +1 -1
- package/dist/toolkit/CheckTraits.js +6 -2
- package/dist/toolkit/CheckTraits.js.map +1 -1
- package/dist/traitIgnores.d.ts +3 -0
- package/dist/traitIgnores.d.ts.map +1 -0
- package/dist/traitIgnores.js +17 -0
- package/dist/traitIgnores.js.map +1 -0
- package/main.ts +0 -2
- package/package.json +2 -3
- package/skills/traits/SKILL.md +42 -10
- package/toolkit/CheckTraits.ts +6 -2
- package/traitIgnores.ts +14 -0
- package/commands/init.ts +0 -73
- package/dist/commands/init.d.ts +0 -4
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -67
- package/dist/commands/init.js.map +0 -1
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 @@
|
|
|
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
|
package/dist/bin.js.map
ADDED
|
@@ -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.
|
|
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":";AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,OAAO,KAAK,oBAAoB,MAAM,iCAAiC,CAAA;AACvE,OAAO,KAAK,eAAe,MAAM,4BAA4B,CAAA;AAC7D,OAAO,
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,OAAO,KAAK,oBAAoB,MAAM,iCAAiC,CAAA;AACvE,OAAO,KAAK,eAAe,MAAM,4BAA4B,CAAA;AAC7D,OAAO,WAAW,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAC9C,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AACjD,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE;IACrB,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAC7C,IAAI,CAAC,UAAU,CACb,CAAC,CAAC,aAAa,CAAC,KAAK,CACnB,CAAC,CAAC,UAAU,CACV,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,oCAAoC,CACvG,CACF,CACF,EACD,IAAI,CAAC,eAAe,CAClB,kIAAkI,CACnI,EACD,IAAI,CAAC,QAAQ,CACd;CACF,CAAC,CAAC,IAAI,CACL,OAAO,CAAC,eAAe,CAAC,sCAAsC,CAAC,EAC/D,OAAO,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CAC7B,SAAS,CAAC,UAAU,CAAC;IACnB,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,WAAW,CAAC,OAAO;IAC5B,SAAS,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmDhC,CAAC;CACH,CAAC,CAAC,IAAI,CACL,KAAK,CAAC,OAAO,CACX,OAAO,CAAC,IAAI,CACV,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC,EAC3D,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EACjC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAC9C,CACF,EACD,KAAK,CAAC,MAAM,EACZ,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,EAChC,MAAM,CAAC,OAAO,CACZ,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CACpE,KAAK,CAAC,YAAY,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAC9C,KAAK,CAAC,YAAY,CAAC,eAAe,CAAC,KAAK,CAAC,CAC1C,CACF,CACF,CACF,EACD,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,EAC7C,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EAClC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAChC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,EAC/C,WAAW,CAAC,OAAO,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC,CACrD,CAAA"}
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "traits",
|
|
3
|
-
"version": "0.5.
|
|
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/
|
|
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 @@
|
|
|
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;
|
|
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
|
-
|
|
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(
|
|
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":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC1G,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAElE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAE9D,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;IACnD,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC
|
|
1
|
+
{"version":3,"file":"CheckTraits.js","sourceRoot":"","sources":["../../toolkit/CheckTraits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC1G,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAElE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAE9D,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;IACnD,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4B/B,CAAC;IACF,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC;YAClC,WAAW,EACT,6PAA6P;SAChQ,CAAC;QACF,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC;YACnD,WAAW,EAAE,mGAAmG;SACjH,CAAC;KACH,CAAC;IACF,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IACpD,YAAY,EAAE;QACZ,aAAa,CAAC,aAAa;QAC3B,UAAU;QACV,UAAU,CAAC,UAAU;QACrB,IAAI,CAAC,IAAI;QACT,mBAAmB,CAAC,mBAAmB;QACvC,aAAa;KACd;CACF,CAAC;KACC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC;KACpC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC;KAC7B,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAA;AAEpC,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;AAEhD,MAAM,CAAC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM;QAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,UAAU,CAAA;QAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,aAAa,CAAA;QACpC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAA;QACvC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;QAC1B,MAAM,aAAa,GAAG,OAAO,CAAC,iBAAiB;YAC7C,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAClB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,mBAAmB,CAAA;gBAC9D,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAClC,YAAY,CAAC,IAAI,CAAA,kEAAkE,CAAC,IAAI,CACtF,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CACxC,CACF,CAAA;gBACD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;gBAC/E,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAA;gBACxC,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACnB,OAAO,KAAK,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC;wBACtC,IAAI,EAAE,SAAS;wBACf,MAAM,EAAE,cAAc;wBACtB,MAAM,EAAE,cAAc;wBACtB,WAAW,EAAE,4BAA4B,QAAQ,EAAE;qBACpD,CAAC,CAAA;gBACJ,CAAC;gBACD,OAAO,IAAI,GAAG,CACZ,IAAI,CACF,MAAM,EACN,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAClB,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAC/B,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAC5D,CACF,CAAA;YACH,CAAC,CAAC,CACH;YACH,CAAC,CAAC,SAAS,CAAA;QACb,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS;YACjD,CAAC,CAAC,KAAK,CAAC,EAAE;YACV,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAA;QAC1F,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CACnC,MAAM,CAAC,OAAO,EACd,CAAC,OAAO,EAAE,EAAE;YACV,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,GAAG,OAAO,EAAE,CAAA;YACnF,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE;gBACvB,IAAI,EAAE,OAAO,CAAC,UAAU;gBACxB,OAAO,EAAE,OAAO,CAAC,QAAQ;aAC1B,CAAC,CAAA;QACJ,CAAC,EACD,EAAE,WAAW,EAAE,WAAW,EAAE,CAC7B,CAAA;QACD,MAAM,QAAQ,GAAG,IAAI,CACnB,OAAO,EACP,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,EAC3D,KAAK,CAAC,MAAM,CACb,CAAA;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAChC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAClG,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,EACxE,EAAE,WAAW,EAAE,WAAW,EAAE,CAC7B,CAAA;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CACnC,KAAK,EACL,MAAM,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI;YAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;YAC3C,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;YAClC,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;YAC5E,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC,CAAA;YAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CACjD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC;gBAC7B,QAAQ,EAAE,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC;aACrF,CAAC,CACH,CAAA;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CACnC,OAAO,EACP,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,EAC1G,EAAE,WAAW,EAAE,WAAW,EAAE,CAC7B,CAAA;YACD,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAA;QACnE,CAAC,CAAC,EACF,EAAE,WAAW,EAAE,WAAW,EAAE,CAC7B,CAAA;QACD,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/B,CAAC,CAAC;CACH,CAAC,CAAA"}
|
|
@@ -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.
|
|
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/
|
|
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",
|
package/skills/traits/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
package/toolkit/CheckTraits.ts
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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))
|
package/traitIgnores.ts
ADDED
|
@@ -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
|
-
)
|
package/dist/commands/init.d.ts
DELETED
|
@@ -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"}
|
package/dist/commands/init.js
DELETED
|
@@ -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"}
|