traits 0.5.0 → 0.5.2
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/Source.ts +1 -1
- package/Trait.ts +109 -4
- package/TraitConflict.ts +43 -4
- package/TraitDerivation.ts +104 -32
- package/TraitEvaluation.ts +104 -49
- package/TraitStore.ts +128 -44
- package/TraitsConfig.ts +16 -9
- package/TraitsContext.ts +3 -1
- package/_util/Frontmatter.ts +78 -0
- package/_util/Git.ts +6 -5
- package/bin.ts +67 -0
- package/dist/Source.js +1 -1
- package/dist/Source.js.map +1 -1
- package/dist/Trait.d.ts +39 -5
- package/dist/Trait.d.ts.map +1 -1
- package/dist/Trait.js +66 -3
- package/dist/Trait.js.map +1 -1
- package/dist/TraitConflict.d.ts +47 -15
- package/dist/TraitConflict.d.ts.map +1 -1
- package/dist/TraitConflict.js +38 -4
- package/dist/TraitConflict.js.map +1 -1
- package/dist/TraitDerivation.d.ts +85 -28
- package/dist/TraitDerivation.d.ts.map +1 -1
- package/dist/TraitDerivation.js +85 -23
- package/dist/TraitDerivation.js.map +1 -1
- package/dist/TraitEvaluation.d.ts +38 -8
- package/dist/TraitEvaluation.d.ts.map +1 -1
- package/dist/TraitEvaluation.js +84 -47
- package/dist/TraitEvaluation.js.map +1 -1
- package/dist/TraitStore.d.ts +60 -10
- package/dist/TraitStore.d.ts.map +1 -1
- package/dist/TraitStore.js +57 -37
- package/dist/TraitStore.js.map +1 -1
- package/dist/TraitsConfig.d.ts +4 -3
- package/dist/TraitsConfig.d.ts.map +1 -1
- package/dist/TraitsConfig.js +12 -5
- package/dist/TraitsConfig.js.map +1 -1
- package/dist/TraitsContext.d.ts +2 -1
- package/dist/TraitsContext.d.ts.map +1 -1
- package/dist/TraitsContext.js.map +1 -1
- package/dist/_util/Frontmatter.d.ts +37 -0
- package/dist/_util/Frontmatter.d.ts.map +1 -0
- package/dist/_util/Frontmatter.js +52 -0
- package/dist/_util/Frontmatter.js.map +1 -0
- package/dist/_util/Git.d.ts +2 -3
- package/dist/_util/Git.d.ts.map +1 -1
- package/dist/_util/Git.js +4 -1
- package/dist/_util/Git.js.map +1 -1
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +62 -0
- package/dist/bin.js.map +1 -0
- package/dist/getIncludedFiles.d.ts +13 -0
- package/dist/getIncludedFiles.d.ts.map +1 -0
- package/dist/getIncludedFiles.js +26 -0
- package/dist/getIncludedFiles.js.map +1 -0
- package/dist/main.js +15 -13
- package/dist/main.js.map +1 -1
- package/dist/package.json +8 -4
- package/dist/test/Frontmatter.test.d.ts +2 -0
- package/dist/test/Frontmatter.test.d.ts.map +1 -0
- package/dist/test/Frontmatter.test.js +29 -0
- package/dist/test/Frontmatter.test.js.map +1 -0
- package/dist/test/Trait.test.d.ts +2 -0
- package/dist/test/Trait.test.d.ts.map +1 -0
- package/dist/test/Trait.test.js +144 -0
- package/dist/test/Trait.test.js.map +1 -0
- package/dist/test/TraitDerivation.test.d.ts +2 -0
- package/dist/test/TraitDerivation.test.d.ts.map +1 -0
- package/dist/test/TraitDerivation.test.js +173 -0
- package/dist/test/TraitDerivation.test.js.map +1 -0
- package/dist/test/TraitEvaluation.test.d.ts +2 -0
- package/dist/test/TraitEvaluation.test.d.ts.map +1 -0
- package/dist/test/TraitEvaluation.test.js +116 -0
- package/dist/test/TraitEvaluation.test.js.map +1 -0
- 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 +44 -0
- package/dist/test/TraitIgnores.test.js.map +1 -0
- package/dist/test/TraitStore.test.d.ts +2 -0
- package/dist/test/TraitStore.test.d.ts.map +1 -0
- package/dist/test/TraitStore.test.js +94 -0
- package/dist/test/TraitStore.test.js.map +1 -0
- package/dist/toolkit/CheckTraits.d.ts +121 -13
- package/dist/toolkit/CheckTraits.d.ts.map +1 -1
- package/dist/toolkit/CheckTraits.js +28 -36
- package/dist/toolkit/CheckTraits.js.map +1 -1
- package/dist/toolkit/DeriveTrait.d.ts +161 -45
- package/dist/toolkit/DeriveTrait.d.ts.map +1 -1
- package/dist/toolkit/DeriveTrait.js +26 -14
- package/dist/toolkit/DeriveTrait.js.map +1 -1
- package/dist/traitIgnores.d.ts +21 -0
- package/dist/traitIgnores.d.ts.map +1 -0
- package/dist/traitIgnores.js +20 -0
- package/dist/traitIgnores.js.map +1 -0
- package/getIncludedFiles.ts +42 -0
- package/main.ts +14 -13
- package/package.json +8 -4
- package/skills/traits/SKILL.md +129 -51
- package/toolkit/CheckTraits.ts +34 -51
- package/toolkit/DeriveTrait.ts +27 -14
- package/traitIgnores.ts +22 -0
- package/_util/words.ts +0 -555
- package/commands/init.ts +0 -73
- package/dist/_util/words.d.ts +0 -2
- package/dist/_util/words.d.ts.map +0 -1
- package/dist/_util/words.js +0 -556
- package/dist/_util/words.js.map +0 -1
- 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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Schema as S, String } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { DecisionModel, LanguageModel, McpSchema, Tool, Toolkit } from "effect/ai";
|
|
3
|
+
import * as Trait from "../Trait.js";
|
|
4
|
+
import * as Derivation from "../TraitDerivation.js";
|
|
4
5
|
import { TraitsContext } from "../TraitsContext.js";
|
|
5
6
|
import { TraitStore, TraitStoreError } from "../TraitStore.js";
|
|
6
7
|
export const DeriveTrait = Tool.make("derive_trait", {
|
|
@@ -15,40 +16,51 @@ export const DeriveTrait = Tool.make("derive_trait", {
|
|
|
15
16
|
| If Revise supplies no concrete feedback, ask for revision details with the interactive question UI and free text. Do not guess or set approved: true.
|
|
16
17
|
| Keep the proposal pending through related work and return to it afterward unless cancelled; obtain fresh approval of the complete revised body.
|
|
17
18
|
| First decide whether the feedback generalizes beyond the current task and inspect existing trait files for coverage.
|
|
18
|
-
| Supply a concise, standalone rule as proposedTrait, preserving its scope and exceptions; omit headings
|
|
19
|
+
| Supply a concise, standalone rule as proposedTrait, preserving its scope and exceptions; omit headings.
|
|
20
|
+
| Include a separate diff showing BAD (-) versus GOOD (+) code whenever a useful example applies. Omit it only when a code example is not meaningful. Approval covers the exact diff as well as the body.
|
|
21
|
+
| Supply a unique descriptive kebab-case name without a redundant trait- prefix for a newly created trait; it is the canonical identity, frontmatter name, and Markdown filename stem. Duplicate names are rejected.
|
|
22
|
+
| Avoid repeated requirements and duplicate traits. Keep bodies standalone; do not strip essential scope or exceptions just to shorten them.
|
|
23
|
+
| When authoring trait frontmatter, let mime restrict file types and when define positive semantic scope. Include skip only for an independent exception within that scope, never its negation or a repetition of another field.
|
|
24
|
+
| Write when values without a leading When, for example when: editing React state.
|
|
19
25
|
| Compare against all stored traits. Only a distinct proposal is saved as a new trait.
|
|
20
26
|
| Inconclusive returns relationship findings and blocks all writes; clarify ambiguity or revise the proposal before retrying.
|
|
21
|
-
| Confirmed conflicts enter reconciliation within this tool. Approval shows affected trait
|
|
22
|
-
| Nothing is replaced before approval. After approval,
|
|
27
|
+
| Confirmed conflicts and independently verified coverage gaps enter reconciliation within this tool. Approval shows affected trait names, bodies, applicability metadata, removals, the proposal, and replacement policy.
|
|
28
|
+
| Nothing is replaced before approval. After approval, validate unique replacement names against unaffected traits, update reused selected names, save new names, then delete only selected originals whose names were not reused.
|
|
23
29
|
| The proposal takes precedence only in direct conflicts; preserve unrelated requirements, scopes, and exceptions. Resolved returns saved replacements.
|
|
24
30
|
| Do not submit the same proposal again after Resolved: it is already incorporated. Inspect replacements and check relevant TypeScript files.
|
|
25
31
|
| Reconciliation is not transactional; on an error, inspect the store before retrying because some writes or removals may have completed.
|
|
26
|
-
| Covered
|
|
32
|
+
| Covered requires a second independent decision-model assessment of the saved body and when/mime/skip metadata against every proposed requirement. Similar intent or partial overlap is insufficient. Coverage gaps require approval-gated revision; inconclusive verification blocks writes.
|
|
33
|
+
| Covered returns only independently verified existing traits; reuse them without writing a duplicate.
|
|
27
34
|
| A single clarification candidate is selected automatically for approval; provide a complete replacement body preserving existing requirements and exceptions.
|
|
28
35
|
| Multiple clarification candidates return Clarification with those traits and the proposal without writing. Refine the proposal or clarify intent.
|
|
29
36
|
| The shared approval flow shows the existing and proposed bodies; ask in the client UI only if ApprovalRequired is returned.
|
|
30
|
-
| The tool rechecks all traits before updating, preserving the selected
|
|
37
|
+
| The tool rechecks all traits before updating, preserving the selected name on clarification. Updated returns the saved trait.
|
|
31
38
|
| Never use approved: true for an unapproved replacement, and never resolve a mere clarification by deleting traits.
|
|
32
|
-
| Created returns the saved trait including its
|
|
33
|
-
| Then call check_traits on relevant TypeScript files, using returned trait
|
|
39
|
+
| Created returns the saved trait including its unique name. Every response is one tagged TraitDerivation outcome, never a bare array or void.
|
|
40
|
+
| Then call check_traits on relevant TypeScript files, using returned trait names for focused reviews when appropriate.
|
|
34
41
|
`),
|
|
35
42
|
parameters: S.Struct({
|
|
43
|
+
diff: Trait.TraitDiff.pipe(S.optional),
|
|
44
|
+
name: Trait.TraitName.annotate({
|
|
45
|
+
description: "Unique descriptive kebab-case identity and Markdown filename stem without a redundant trait- prefix for a newly created trait. Existing names are preserved on clarification."
|
|
46
|
+
}),
|
|
36
47
|
proposedTrait: S.String.annotate({
|
|
37
|
-
description: "
|
|
48
|
+
description: "A concise, standalone reusable rule with its essential scope and exceptions, not raw conversation text or repeated restatements of the same requirement."
|
|
38
49
|
}),
|
|
39
50
|
approved: S.Boolean.pipe(S.optional).annotate({
|
|
40
51
|
description: "Fallback approval for the exact proposal and automatically selected action shown to the user, including any existing trait being updated. Set true only after approval; native clients still receive a form. Does not bypass relationship checks."
|
|
41
52
|
})
|
|
42
53
|
}),
|
|
43
|
-
success: TraitDerivation,
|
|
44
|
-
failure: S.Union([
|
|
45
|
-
|
|
54
|
+
success: Derivation.TraitDerivation,
|
|
55
|
+
failure: S.Union([S.TaggedStruct("AiError", { message: S.String }), TraitStoreError, McpSchema.ElicitationDeclined]),
|
|
56
|
+
failureMode: "return",
|
|
57
|
+
dependencies: [DecisionModel.DecisionModel, LanguageModel.LanguageModel, TraitsContext, TraitStore]
|
|
46
58
|
})
|
|
47
59
|
.annotate(Tool.Title, "Derive Trait")
|
|
48
60
|
.annotate(Tool.Readonly, false)
|
|
49
61
|
.annotate(Tool.Destructive, true);
|
|
50
62
|
export const toolkit = Toolkit.make(DeriveTrait);
|
|
51
63
|
export const layer = toolkit.toLayer({
|
|
52
|
-
derive_trait: derive
|
|
64
|
+
derive_trait: Derivation.derive
|
|
53
65
|
});
|
|
54
66
|
//# sourceMappingURL=DeriveTrait.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeriveTrait.js","sourceRoot":"","sources":["../../toolkit/DeriveTrait.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"DeriveTrait.js","sourceRoot":"","sources":["../../toolkit/DeriveTrait.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAElF,OAAO,KAAK,KAAK,MAAM,aAAa,CAAA;AACpC,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAA;AACnD,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiC/B,CAAC;IACF,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC;YAC7B,WAAW,EACT,+KAA+K;SAClL,CAAC;QACF,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;YAC/B,WAAW,EACT,0JAA0J;SAC7J,CAAC;QACF,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC;YAC5C,WAAW,EACT,mPAAmP;SACtP,CAAC;KACH,CAAC;IACF,OAAO,EAAE,UAAU,CAAC,eAAe;IACnC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,eAAe,EAAE,SAAS,CAAC,mBAAmB,CAAC,CAAC;IACpH,WAAW,EAAE,QAAQ;IACrB,YAAY,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,aAAa,CAAC,aAAa,EAAE,aAAa,EAAE,UAAU,CAAC;CACpG,CAAC;KACC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC;KACpC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC;KAC9B,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;AAEnC,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,UAAU,CAAC,MAAM;CAChC,CAAC,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
/** References match unique trait names exactly. */
|
|
4
|
+
export declare const excludeIgnored: (text: string) => (self: Iterable<{
|
|
5
|
+
readonly [x: string]: import("effect/Schema").Json;
|
|
6
|
+
readonly body: string;
|
|
7
|
+
readonly diff?: string | undefined;
|
|
8
|
+
readonly mime?: string | readonly string[] | undefined;
|
|
9
|
+
readonly name: string & import("effect/Brand").Brand<"traits/TraitName">;
|
|
10
|
+
readonly skip?: string | undefined;
|
|
11
|
+
readonly when?: string | undefined;
|
|
12
|
+
}>) => {
|
|
13
|
+
readonly [x: string]: import("effect/Schema").Json;
|
|
14
|
+
readonly body: string;
|
|
15
|
+
readonly diff?: string | undefined;
|
|
16
|
+
readonly mime?: string | readonly string[] | undefined;
|
|
17
|
+
readonly name: string & import("effect/Brand").Brand<"traits/TraitName">;
|
|
18
|
+
readonly skip?: string | undefined;
|
|
19
|
+
readonly when?: string | undefined;
|
|
20
|
+
}[];
|
|
21
|
+
//# sourceMappingURL=traitIgnores.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traitIgnores.d.ts","sourceRoot":"","sources":["../traitIgnores.ts"],"names":[],"mappings":"AAIA,kGAAkG;AAClG,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,WAAW,CAAC,MAAM,CAW7D,CAAA;AAED,mDAAmD;AACnD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;GAE1B,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Array, flow, 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 references = new Set();
|
|
5
|
+
for (const line of String.split(text, "\n")) {
|
|
6
|
+
const trimmed = String.trim(line);
|
|
7
|
+
if (trimmed === "" || trimmed.startsWith("#!"))
|
|
8
|
+
continue;
|
|
9
|
+
if (!trimmed.startsWith("//"))
|
|
10
|
+
break;
|
|
11
|
+
const match = /^\/\/\s*traits-ignore-file\s+(\S+)\s*:\s*(\S.*)$/.exec(trimmed);
|
|
12
|
+
const reference = match?.[1];
|
|
13
|
+
if (reference !== undefined)
|
|
14
|
+
references.add(reference);
|
|
15
|
+
}
|
|
16
|
+
return references;
|
|
17
|
+
};
|
|
18
|
+
/** References match unique trait names exactly. */
|
|
19
|
+
export const excludeIgnored = flow(traitIgnores, (references) => Array.filter((trait) => !references.has(trait.name)));
|
|
20
|
+
//# sourceMappingURL=traitIgnores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traitIgnores.js","sourceRoot":"","sources":["../traitIgnores.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAI5C,kGAAkG;AAClG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAuB,EAAE;IAChE,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAA;IACpC,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,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAQ;QACxD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAK;QACpC,MAAM,KAAK,GAAG,kDAAkD,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9E,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;QAC5B,IAAI,SAAS,KAAK,SAAS;YAAE,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC,CAAA;AAED,mDAAmD;AACnD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,EAAE,CAC9D,KAAK,CAAC,MAAM,CAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAC5D,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Array, Effect, Equal, Path, pipe, Schema as S, Stream, String } from "effect"
|
|
2
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/process"
|
|
3
|
+
|
|
4
|
+
import { TraitsContext } from "./TraitsContext.ts"
|
|
5
|
+
|
|
6
|
+
export class GitFileListingError extends S.TaggedError<GitFileListingError>()("GitFileListingError", {
|
|
7
|
+
directory: S.String,
|
|
8
|
+
exitCode: S.Int.check(S.isGreaterThan(0))
|
|
9
|
+
}) {
|
|
10
|
+
override get message() {
|
|
11
|
+
return `git ls-files exited with code ${this.exitCode}`
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const getIncludedFiles = Effect.gen(function* () {
|
|
16
|
+
const context = yield* TraitsContext
|
|
17
|
+
if (!context.config.excludeGitignored) return undefined
|
|
18
|
+
|
|
19
|
+
const path = yield* Path.Path
|
|
20
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
21
|
+
const process = yield* spawner.spawn(
|
|
22
|
+
ChildProcess.make`git ls-files --cached --others --exclude-standard --full-name -z`.pipe(
|
|
23
|
+
ChildProcess.setCwd(context.configRoot)
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
const output = yield* process.stdout.pipe(Stream.decodeText(), Stream.mkString)
|
|
27
|
+
yield* process.exitCode.pipe(
|
|
28
|
+
Effect.filterOrFail(Equal.equals(0), (exitCode) =>
|
|
29
|
+
GitFileListingError.make({
|
|
30
|
+
directory: context.configRoot,
|
|
31
|
+
exitCode
|
|
32
|
+
})
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
return pipe(
|
|
36
|
+
output,
|
|
37
|
+
String.split("\0"),
|
|
38
|
+
Array.filter(String.isNonEmpty),
|
|
39
|
+
Array.map((file) => path.resolve(context.configRoot, file)),
|
|
40
|
+
(files) => new Set(files)
|
|
41
|
+
)
|
|
42
|
+
}).pipe(Effect.scoped, Effect.withSpan("getIncludedFiles"))
|
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"
|
|
@@ -35,8 +34,8 @@ Command.make("traits", {
|
|
|
35
34
|
version: PackageJson.version,
|
|
36
35
|
protocols: [McpProtocol.v2025_11_25],
|
|
37
36
|
instructions: String.stripMargin(`
|
|
38
|
-
| Traits are durable, reusable coding preferences
|
|
39
|
-
| The
|
|
37
|
+
| Traits are durable, reusable coding preferences. The filesystem adapter stores each as .agents/traits/<name>.md, with a unique descriptive kebab-case name (no redundant trait- prefix) in frontmatter and no separate id.
|
|
38
|
+
| The unique name is the canonical identity across storage backends. Use exact names for focused traitNames selections and ignore directives, not filenames. Update references after renaming. Duplicate names are rejected.
|
|
40
39
|
| Before completing a response to a user correction, coding preference, or approved alternative idiom, check whether it expresses a reusable rule.
|
|
41
40
|
| Treat multi-turn exchanges as a whole: asking about a better idiom and then approving it can establish a preference without saying "always" or "trait".
|
|
42
41
|
| Err on the side of proposing a concise trait unless you have read an existing trait that clearly covers the exact rule and its scope.
|
|
@@ -52,24 +51,27 @@ Command.make("traits", {
|
|
|
52
51
|
| Approval to edit code alone is not approval to persist a trait. Make the requested edit, but also propose the reusable rule before your final response.
|
|
53
52
|
| Also act on explicit requests to remember a convention. Skip one-off requirements, transient facts, and preferences already covered.
|
|
54
53
|
| Read existing trait files before drafting. Keep each trait concise and independently applicable, preserving scope and exceptions.
|
|
54
|
+
| Keep frontmatter complementary: mime restricts file types, when defines positive semantic scope, and skip is only an independent exception within that scope. Omit redundant fields; never repeat when's negation as skip. Keep bodies standalone without repeated requirements or duplicate traits.
|
|
55
|
+
| Write when values as conditions without a redundant leading When, for example when: editing React state.
|
|
55
56
|
| Call derive_trait with the drafted body as proposedTrait. It saves only distinct rules; Covered means reuse an existing trait.
|
|
56
57
|
| A single clarification candidate is selected automatically and shown for approval; supply a complete replacement preserving the existing rule.
|
|
57
|
-
| Multiple candidates return Clarification entries without writing: refine the proposal or clarify intent. Do not supply a target
|
|
58
|
-
| After fallback approval, retry with the same proposedTrait and approved: true; Updated preserves the selected existing
|
|
59
|
-
| Every derive_trait response is one tagged TraitDerivation outcome. Created returns the saved trait
|
|
58
|
+
| Multiple candidates return Clarification entries without writing: refine the proposal or clarify intent. Do not supply a clarification target.
|
|
59
|
+
| After fallback approval, retry with the same name, proposedTrait and approved: true; Updated preserves the selected existing name.
|
|
60
|
+
| Every derive_trait response is one tagged TraitDerivation outcome. Created returns the saved trait with its unique name; Updated and Resolved return saved replacements.
|
|
60
61
|
| Covered and Clarification return existing traits without writing. Inconclusive returns relationship findings and blocks all writes.
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
62
|
+
| Inconclusive outcome findings use reason to distinguish conflict, coverage_gap, and inconclusive; clarify ambiguity before retrying.
|
|
63
|
+
| Covered requires independent second-pass verification of the saved body and applicability metadata against the complete proposal. Similar intent is insufficient; coverage gaps enter approval-gated reconciliation and inconclusive verification blocks writes.
|
|
64
|
+
| Confirmed conflicts and verified coverage gaps enter reconciliation inside derive_trait, selecting only the affected originals for an explicit replacement approval.
|
|
65
|
+
| Approval must cover affected names, bodies, removals, the proposal, and generated reconciliation. Nothing is replaced silently.
|
|
66
|
+
| After approval, reconciliation gives the proposal precedence in direct conflicts, updates selected names reused by replacements, saves new names, then removes only originals whose names were not reused. Unaffected names and duplicate generated names cannot be overwritten.
|
|
67
|
+
| Resolved returns saved replacement bodies and unique names. Review them and check relevant code; do not derive the same proposal again.
|
|
66
68
|
| After generating or changing TypeScript files, call check_traits on those files.
|
|
67
69
|
| check_traits accepts include as file paths or glob strings, for example include: ["traits/**/*.ts"]. Overlapping matches are deduplicated.
|
|
68
70
|
| Restrict assessments to project-owned source and relevant authored configuration/docs. Prefer an explicit project-file manifest for whole-project audits.
|
|
69
71
|
| Never submit dependency/reference code, generated build output or lockfiles, Git metadata, secrets, .env files, credentials, or binary assets for assessment.
|
|
70
72
|
| Unmatched patterns select nothing. Built-in exclusions cover dependencies, Git metadata, and nested worktrees; configure project-specific filenames or native glob patterns through TraitsConfig.excludes.
|
|
71
73
|
| After saving or resolving a trait, check the relevant TypeScript files explicitly; creation does not run that review for you.
|
|
72
|
-
| Omit
|
|
74
|
+
| Omit traitNames for all traits; use specific names for focused follow-ups. Before committing or opening a PR, review the changed TypeScript files.
|
|
73
75
|
| Checks read current full files in 200-line chunks, not Git diffs. A finding's startLine locates a chunk, not an exact violation.
|
|
74
76
|
| Each finding includes assessment.classification and probabilities: inconclusive is unresolved context, not a confirmed violation. Probabilities are not calibrated guarantees.
|
|
75
77
|
| Take responsibility for trait compliance whenever a violation is discovered, through tool findings or your own inspection.
|
|
@@ -104,7 +106,6 @@ Command.make("traits", {
|
|
|
104
106
|
)
|
|
105
107
|
)
|
|
106
108
|
),
|
|
107
|
-
Command.withSubcommands([init]),
|
|
108
109
|
Command.run({ version: PackageJson.version }),
|
|
109
110
|
Effect.provide(NodeServices.layer),
|
|
110
111
|
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.2",
|
|
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",
|
|
@@ -22,7 +21,8 @@
|
|
|
22
21
|
"dist/**/*.js.map",
|
|
23
22
|
"dist/**/*.d.ts",
|
|
24
23
|
"dist/**/*.d.ts.map",
|
|
25
|
-
"!dist/vite.config.*"
|
|
24
|
+
"!dist/vite.config.*",
|
|
25
|
+
"!dist/integration/**"
|
|
26
26
|
],
|
|
27
27
|
"type": "module",
|
|
28
28
|
"exports": {
|
|
@@ -35,8 +35,12 @@
|
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"yaml": "^2.9.1"
|
|
40
|
+
},
|
|
38
41
|
"devDependencies": {
|
|
39
42
|
"@effect/platform-node": "4.0.0",
|
|
43
|
+
"@effect/vitest": "4.0.0",
|
|
40
44
|
"effect": "4.0.0"
|
|
41
45
|
},
|
|
42
46
|
"peerDependencies": {
|
package/skills/traits/SKILL.md
CHANGED
|
@@ -10,10 +10,39 @@ description: >-
|
|
|
10
10
|
# Traits
|
|
11
11
|
|
|
12
12
|
Traits are concise, durable coding rules. The filesystem implementation stores
|
|
13
|
-
each body in `.agents/traits/<
|
|
14
|
-
|
|
15
|
-
`
|
|
16
|
-
|
|
13
|
+
each body in `.agents/traits/<name>.md`, relative to its working directory.
|
|
14
|
+
Frontmatter contains a unique descriptive kebab-case `name` without a redundant
|
|
15
|
+
`trait-` prefix and no separate `id`. The name is the canonical identity across
|
|
16
|
+
backends: use exact names for focused `check_traits({ traitNames: [...] })`
|
|
17
|
+
selections and ignore directives, not filenames. Duplicate names are rejected.
|
|
18
|
+
Renaming changes identity and requires updating references. Construct complete
|
|
19
|
+
records with the schema's `Trait.Trait.make`. `TraitStore` persists complete
|
|
20
|
+
traits and uses names for get, update, and remove. Updates retain the name; a
|
|
21
|
+
rename requires adding the new name and removing the old one after saving. The
|
|
22
|
+
`TraitStore` service class declares its shape inline; use
|
|
23
|
+
`TraitStore["Service"]` for its implementation type. Its filesystem layer is
|
|
24
|
+
colocated in the same module. Only the filesystem adapter builds `<name>.md`
|
|
25
|
+
paths.
|
|
26
|
+
|
|
27
|
+
Include a concise BAD-to-GOOD code example whenever meaningful. Pass its raw
|
|
28
|
+
unified diff as `diff` to `derive_trait` (removed `-` lines are BAD, added `+`
|
|
29
|
+
lines are GOOD), separately from `proposedTrait`. Omit it only when a code
|
|
30
|
+
example does not apply. Approval and retries cover the exact diff too. On disk,
|
|
31
|
+
`diff` is not frontmatter: put it in a Markdown `diff` fence directly after the
|
|
32
|
+
frontmatter, followed by a blank line, `---`, and the standalone body. Keep
|
|
33
|
+
examples out of the body and preserve them when updating a trait unless the
|
|
34
|
+
updated rule needs a changed example.
|
|
35
|
+
|
|
36
|
+
Keep frontmatter fields complementary. `mime` restricts file types; `when`
|
|
37
|
+
defines positive semantic scope. Write `when` values without a leading "When",
|
|
38
|
+
such as `when: editing React state`. Add `skip` only for an independent
|
|
39
|
+
exception inside that scope, not for its negation or a restriction already
|
|
40
|
+
expressed by another field. For example,
|
|
41
|
+
`when: importing from local project modules` already excludes external packages;
|
|
42
|
+
do not repeat that as a skip. Leave unnecessary optional fields absent. Keep the
|
|
43
|
+
body concise and standalone, preserving essential scope and exceptions without
|
|
44
|
+
repeated restatements. Reuse existing traits that already cover the same rule
|
|
45
|
+
rather than creating duplicates.
|
|
17
46
|
|
|
18
47
|
## Capture a reusable insight
|
|
19
48
|
|
|
@@ -32,15 +61,23 @@ rule; if an existing rule needs clearer wording, propose a clarification. Skip
|
|
|
32
61
|
only clearly one-off requests, transient facts, or rules you can identify as
|
|
33
62
|
already covered.
|
|
34
63
|
|
|
35
|
-
|
|
36
|
-
not
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
64
|
+
When coverage is uncertain, call `derive_trait` with the specific preference; do
|
|
65
|
+
not skip capture because a broad trait or a combination of general traits seems
|
|
66
|
+
related. Let the tool assess coverage or clarification. For example, “prefer
|
|
67
|
+
Effect utilities” and “pass callbacks directly” are not sufficient grounds to
|
|
68
|
+
skip proposing “prefer native inline checks and use Effect predicate helpers
|
|
69
|
+
directly in callbacks or existing pipelines.”
|
|
70
|
+
|
|
71
|
+
Call `derive_trait` with the drafted rule and a descriptive kebab-case `name`
|
|
72
|
+
first, leaving `approved` unset. Do not ask for approval separately before
|
|
73
|
+
invoking: capable clients receive a native MCP form. Only if `ApprovalRequired`
|
|
74
|
+
is returned, show the complete message in the interactive question tool with
|
|
75
|
+
**Save trait**, **Revise**, and **Skip** and its automatic free-text option. Do
|
|
76
|
+
not add a duplicate "Other" option. Use plain text only if that UI is
|
|
77
|
+
unavailable. Approval to edit code alone does not approve persisting a trait:
|
|
78
|
+
make the requested edit and propose the reusable rule before the final response.
|
|
79
|
+
Do not stop after implementing an idiom and running code checks; preference
|
|
80
|
+
capture is a separate responsibility.
|
|
44
81
|
|
|
45
82
|
### Shared approval protocol
|
|
46
83
|
|
|
@@ -52,7 +89,7 @@ without form support use the same fallback.
|
|
|
52
89
|
|
|
53
90
|
On `ApprovalRequired`, show its complete message in the interactive question UI
|
|
54
91
|
with Save/Revise/Skip and free text. Save permits retrying the identical body
|
|
55
|
-
and target
|
|
92
|
+
and target names with `approved: true`. Revision or free-text input requires
|
|
56
93
|
drafting the changed proposal and asking approval again, not silently setting
|
|
57
94
|
approved. Skip means stop. Native `RevisionRequired` and `Declined` have the
|
|
58
95
|
same meanings. Transport errors are failures, never declines or approvals.
|
|
@@ -88,45 +125,50 @@ authenticated consent token.
|
|
|
88
125
|
3. Draft the complete rule as `proposedTrait`. Preserve essential requirements,
|
|
89
126
|
scopes, and exceptions. Keep independently applicable rules separate; omit
|
|
90
127
|
headings, repetition, and unnecessary explanation or examples.
|
|
91
|
-
4. Call `derive_trait({ proposedTrait })` first, without a separate
|
|
92
|
-
prompt. This checks existing traits and writes only when the
|
|
93
|
-
distinct from all of them. It does not independently decide
|
|
94
|
-
conversation contains a generalizable insight.
|
|
95
|
-
5. `Created` means the trait was saved and includes its body and
|
|
96
|
-
|
|
128
|
+
4. Call `derive_trait({ name, proposedTrait })` first, without a separate
|
|
129
|
+
approval prompt. This checks existing traits and writes only when the
|
|
130
|
+
proposal is distinct from all of them. It does not independently decide
|
|
131
|
+
whether the conversation contains a generalizable insight.
|
|
132
|
+
5. `Created` means the trait was saved and includes its body and unique name. Do
|
|
133
|
+
not submit it again. Use that name for focused checks when needed.
|
|
97
134
|
6. `Inconclusive` means nothing was saved and contains relationship findings;
|
|
98
135
|
each finding's `reason` distinguishes conflict from inconclusive. Any
|
|
99
136
|
inconclusive comparison blocks all writes. Inspect the existing bodies and
|
|
100
|
-
|
|
101
|
-
enter approval-gated reconciliation within this same tool.
|
|
102
|
-
|
|
103
|
-
`Covered.traits`
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
137
|
+
names, clarify ambiguity, or revise the proposal. Confirmed conflicts
|
|
138
|
+
otherwise enter approval-gated reconciliation within this same tool.
|
|
139
|
+
|
|
140
|
+
`Covered.traits` requires an independent second decision-model check of the
|
|
141
|
+
saved body and when, mime, and skip metadata against the entire proposal.
|
|
142
|
+
Partial overlap or similar intent is insufficient. Verified coverage gaps enter
|
|
143
|
+
approval-gated reconciliation, including needed applicability updates;
|
|
144
|
+
inconclusive verification blocks writes. `Covered.traits` means existing traits
|
|
145
|
+
already express the proposal; reuse them without creating a duplicate. When
|
|
146
|
+
there is exactly one clarification candidate and no covered or blocking result,
|
|
147
|
+
the tool selects it automatically and requests approval of its current body and
|
|
148
|
+
complete replacement. The proposal must preserve the existing rule's intent,
|
|
149
|
+
requirements, scope, and exceptions; the tool does not generate a merged
|
|
150
|
+
replacement from an incremental hint.
|
|
109
151
|
|
|
110
152
|
Multiple candidates return `Clarification` with `traits` and `proposedTrait`
|
|
111
153
|
without writing. Refine the proposal or clarify intent rather than choosing an
|
|
112
|
-
|
|
154
|
+
name arbitrarily. Do not supply a clarification target parameter.
|
|
113
155
|
|
|
114
156
|
Use the interactive question UI only on `ApprovalRequired`, then retry the same
|
|
115
157
|
`proposedTrait` with `approved: true` after Save. The tool rechecks every
|
|
116
|
-
existing trait and preserves the selected
|
|
117
|
-
action changes between approval and retry, obtain fresh approval rather
|
|
118
|
-
reusing the flag. `Updated` returns the saved trait. Never claim approval
|
|
119
|
-
given by the user or delete a trait merely needing clearer wording.
|
|
158
|
+
existing trait and preserves the selected name on clarification. If the store or
|
|
159
|
+
selected action changes between approval and retry, obtain fresh approval rather
|
|
160
|
+
than reusing the flag. `Updated` returns the saved trait. Never claim approval
|
|
161
|
+
not given by the user or delete a trait merely needing clearer wording.
|
|
120
162
|
|
|
121
163
|
## Reconcile conflicting traits
|
|
122
164
|
|
|
123
165
|
`derive_trait` checks the proposal against every stored trait and automatically
|
|
124
166
|
selects confirmed conflicts for reconciliation. There is no separate resolution
|
|
125
|
-
tool and no
|
|
167
|
+
tool and no trait-selection parameter. Inconclusive comparisons block
|
|
126
168
|
reconciliation rather than authorizing deletion.
|
|
127
169
|
|
|
128
170
|
Before writing, the shared approval flow shows the selected originals with their
|
|
129
|
-
|
|
171
|
+
names and bodies, the proposal, the removal plan, and the replacement policy.
|
|
130
172
|
Approval authorizes model-generated reconciliation, not silent deletion. On
|
|
131
173
|
`ApprovalRequired`, ask through the interactive UI and retry the same proposal
|
|
132
174
|
with `approved: true` only after Save. If relationships or originals change,
|
|
@@ -135,8 +177,10 @@ obtain fresh approval.
|
|
|
135
177
|
After approval, the tool evaluates each body and the proposal for decomposition,
|
|
136
178
|
then generates standalone replacements. The proposal takes precedence in direct
|
|
137
179
|
conflicts; unrelated requirements, scopes, and exceptions should be preserved.
|
|
138
|
-
It
|
|
139
|
-
|
|
180
|
+
It validates replacement names against each other and unaffected traits before
|
|
181
|
+
writing. It updates selected names reused by replacements, adds new names, then
|
|
182
|
+
deletes only selected originals whose names were not reused. It returns
|
|
183
|
+
`Resolved` with the saved traits and their unique names.
|
|
140
184
|
|
|
141
185
|
Inspect those replacements. Do not call `derive_trait` again for the same
|
|
142
186
|
proposal: resolution has already saved its content. The initial comparison
|
|
@@ -168,7 +212,7 @@ store before retrying; some changes may already be present.
|
|
|
168
212
|
schema modifiers, and misplaced imports when their traits apply. Searches
|
|
169
213
|
support review; a lack of matches is not proof that every trait is satisfied.
|
|
170
214
|
5. Call `check_traits` on all changed TypeScript files after the latest edits.
|
|
171
|
-
Before a commit or PR, omit `
|
|
215
|
+
Before a commit or PR, omit `traitNames` so every stored trait is checked;
|
|
172
216
|
focused checks supplement rather than replace that final review.
|
|
173
217
|
6. Inspect findings, fix confirmed violations, and repeat direct inspection,
|
|
174
218
|
tool checks, and relevant project verification after fixes. If any reviewed
|
|
@@ -180,15 +224,36 @@ store before retrying; some changes may already be present.
|
|
|
180
224
|
rejected false positives as caveats. Never claim an automated clean pass when
|
|
181
225
|
raw findings remain, or turn an empty result into unconditional compliance.
|
|
182
226
|
|
|
227
|
+
### Targeted trait exceptions
|
|
228
|
+
|
|
229
|
+
For a justified file-scoped exception, add a line comment in the leading header,
|
|
230
|
+
before any code:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
// traits-ignore-file <trait-name>: Explain why this trait cannot apply here.
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Each directive requires a nonempty reason and exactly one unique trait name.
|
|
237
|
+
Names are case-sensitive; update directives after renaming a trait. Filenames
|
|
238
|
+
and partial names are not selectors. Repeat the comment for additional
|
|
239
|
+
exceptions. Only the referenced traits are skipped; other traits remain checked.
|
|
240
|
+
Exceptions apply to all file chunks and focused checks. Comments after code are
|
|
241
|
+
ignored.
|
|
242
|
+
|
|
243
|
+
Read the referenced trait and verify the reason before adding or retaining an
|
|
244
|
+
exception. Do not add directives merely to silence findings: fix applicable
|
|
245
|
+
violations, and ask the user before introducing an exception that overrides
|
|
246
|
+
their preference. Respect existing justified exceptions during manual review.
|
|
247
|
+
|
|
183
248
|
### Tool behavior and remediation
|
|
184
249
|
|
|
185
250
|
- After generating or editing TypeScript files, select those files and call
|
|
186
251
|
`check_traits({ include: files })`.
|
|
187
252
|
- After creating or resolving traits, explicitly review relevant TypeScript
|
|
188
253
|
files. Saving traits does not automatically check code.
|
|
189
|
-
- Omit `
|
|
254
|
+
- Omit `traitNames` for the full trait set. Supply names for focused follow-ups,
|
|
190
255
|
especially the replacements returned by resolution. An empty array selects no
|
|
191
|
-
traits; unknown
|
|
256
|
+
traits; unknown names fail the call.
|
|
192
257
|
- `include` accepts file paths or glob patterns, absolute or relative to the MCP
|
|
193
258
|
working directory. Overlapping matches are deduplicated; unmatched patterns
|
|
194
259
|
select no files. Use `traits/**/*.ts` rather than a bare directory. Glob
|
|
@@ -259,22 +324,35 @@ skipped remediation or lost signal.
|
|
|
259
324
|
|
|
260
325
|
## Operation
|
|
261
326
|
|
|
327
|
+
The CLI launcher installs project dependencies in its current directory before
|
|
328
|
+
importing the MCP server. Run it from the checkout/workspace root. It selects
|
|
329
|
+
the package manager from `package.json#packageManager` or an unambiguous
|
|
330
|
+
lockfile; the selected executable must be on PATH. Installation preserves
|
|
331
|
+
lockfiles but can execute lifecycle scripts, so only launch in trusted
|
|
332
|
+
checkouts. Allow enough MCP startup time for a cold install and avoid
|
|
333
|
+
simultaneous installations in the same worktree. During development, use
|
|
334
|
+
`node traits/bin.ts` with a Node version supporting native TypeScript execution;
|
|
335
|
+
pass server flags after the filename.
|
|
336
|
+
|
|
337
|
+
Configuration and providers currently initialize at server startup. Startup
|
|
338
|
+
failures go to stderr and require fixing setup and reconnecting; whether the
|
|
339
|
+
agent can see these diagnostics depends on its MCP client. Expected tool
|
|
340
|
+
failures use MCP error results; unexpected defects receive a generic internal
|
|
341
|
+
error rather than implementation details.
|
|
342
|
+
|
|
262
343
|
Git-ignored files are excluded by default, with ignore rules refreshed on every
|
|
263
344
|
check. Untracked files do not need to be added to Git to be evaluated. Tracked
|
|
264
345
|
files stay eligible even if ignore rules match them. Set
|
|
265
346
|
`excludeGitignored: false` in `traits.ts` to disable this filter; explicit
|
|
266
347
|
excludes still apply. Git failures fail checks.
|
|
267
348
|
|
|
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
|
|
349
|
+
For setup, follow the canonical guide at
|
|
350
|
+
https://github.com/crosshatch/traits/blob/main/SETUP.md. Respect the consumer's
|
|
351
|
+
package manager and workspace dependency conventions, ask about providers and
|
|
352
|
+
models before writing configuration, and preserve existing settings. The guide
|
|
353
|
+
covers dependencies, credentials, skill installation, MCP connection, and
|
|
354
|
+
troubleshooting. The agent creates traits.ts using the selected providers; there
|
|
355
|
+
is no init command. Discover the connected server's `derive_trait` and
|
|
278
356
|
`check_traits` tools through the client's tool catalog.
|
|
279
357
|
|
|
280
358
|
The CLI's configuration layer loads and validates the default-exported object
|