traits 0.5.1 → 0.5.3

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.
Files changed (112) hide show
  1. package/Source.ts +1 -1
  2. package/Trait.ts +109 -4
  3. package/TraitConflict.ts +43 -4
  4. package/TraitDerivation.ts +104 -32
  5. package/TraitEvaluation.ts +104 -49
  6. package/TraitStore.ts +123 -44
  7. package/TraitsConfig.ts +16 -9
  8. package/TraitsContext.ts +3 -1
  9. package/_util/Frontmatter.ts +78 -0
  10. package/_util/Git.ts +6 -5
  11. package/bin.ts +3 -1
  12. package/dist/Source.js +1 -1
  13. package/dist/Source.js.map +1 -1
  14. package/dist/Trait.d.ts +39 -5
  15. package/dist/Trait.d.ts.map +1 -1
  16. package/dist/Trait.js +66 -3
  17. package/dist/Trait.js.map +1 -1
  18. package/dist/TraitConflict.d.ts +47 -15
  19. package/dist/TraitConflict.d.ts.map +1 -1
  20. package/dist/TraitConflict.js +38 -4
  21. package/dist/TraitConflict.js.map +1 -1
  22. package/dist/TraitDerivation.d.ts +85 -28
  23. package/dist/TraitDerivation.d.ts.map +1 -1
  24. package/dist/TraitDerivation.js +85 -23
  25. package/dist/TraitDerivation.js.map +1 -1
  26. package/dist/TraitEvaluation.d.ts +38 -8
  27. package/dist/TraitEvaluation.d.ts.map +1 -1
  28. package/dist/TraitEvaluation.js +84 -47
  29. package/dist/TraitEvaluation.js.map +1 -1
  30. package/dist/TraitStore.d.ts +54 -10
  31. package/dist/TraitStore.d.ts.map +1 -1
  32. package/dist/TraitStore.js +61 -37
  33. package/dist/TraitStore.js.map +1 -1
  34. package/dist/TraitsConfig.d.ts +4 -3
  35. package/dist/TraitsConfig.d.ts.map +1 -1
  36. package/dist/TraitsConfig.js +12 -5
  37. package/dist/TraitsConfig.js.map +1 -1
  38. package/dist/TraitsContext.d.ts +2 -1
  39. package/dist/TraitsContext.d.ts.map +1 -1
  40. package/dist/TraitsContext.js.map +1 -1
  41. package/dist/_util/Frontmatter.d.ts +37 -0
  42. package/dist/_util/Frontmatter.d.ts.map +1 -0
  43. package/dist/_util/Frontmatter.js +52 -0
  44. package/dist/_util/Frontmatter.js.map +1 -0
  45. package/dist/_util/Git.d.ts +2 -3
  46. package/dist/_util/Git.d.ts.map +1 -1
  47. package/dist/_util/Git.js +4 -1
  48. package/dist/_util/Git.js.map +1 -1
  49. package/dist/bin.js +3 -1
  50. package/dist/bin.js.map +1 -1
  51. package/dist/getIncludedFiles.d.ts +13 -0
  52. package/dist/getIncludedFiles.d.ts.map +1 -0
  53. package/dist/getIncludedFiles.js +26 -0
  54. package/dist/getIncludedFiles.js.map +1 -0
  55. package/dist/main.js +16 -12
  56. package/dist/main.js.map +1 -1
  57. package/dist/package.json +7 -2
  58. package/dist/test/Frontmatter.test.d.ts +2 -0
  59. package/dist/test/Frontmatter.test.d.ts.map +1 -0
  60. package/dist/test/Frontmatter.test.js +29 -0
  61. package/dist/test/Frontmatter.test.js.map +1 -0
  62. package/dist/test/Trait.test.d.ts +2 -0
  63. package/dist/test/Trait.test.d.ts.map +1 -0
  64. package/dist/test/Trait.test.js +144 -0
  65. package/dist/test/Trait.test.js.map +1 -0
  66. package/dist/test/TraitDerivation.test.d.ts +2 -0
  67. package/dist/test/TraitDerivation.test.d.ts.map +1 -0
  68. package/dist/test/TraitDerivation.test.js +173 -0
  69. package/dist/test/TraitDerivation.test.js.map +1 -0
  70. package/dist/test/TraitEvaluation.test.d.ts +2 -0
  71. package/dist/test/TraitEvaluation.test.d.ts.map +1 -0
  72. package/dist/test/TraitEvaluation.test.js +116 -0
  73. package/dist/test/TraitEvaluation.test.js.map +1 -0
  74. package/dist/test/TraitIgnores.test.js +29 -5
  75. package/dist/test/TraitIgnores.test.js.map +1 -1
  76. package/dist/test/TraitStore.test.d.ts +2 -0
  77. package/dist/test/TraitStore.test.d.ts.map +1 -0
  78. package/dist/test/TraitStore.test.js +120 -0
  79. package/dist/test/TraitStore.test.js.map +1 -0
  80. package/dist/test/TraitsInitialization.test.d.ts +2 -0
  81. package/dist/test/TraitsInitialization.test.d.ts.map +1 -0
  82. package/dist/test/TraitsInitialization.test.js +130 -0
  83. package/dist/test/TraitsInitialization.test.js.map +1 -0
  84. package/dist/toolkit/CheckTraits.d.ts +97 -13
  85. package/dist/toolkit/CheckTraits.d.ts.map +1 -1
  86. package/dist/toolkit/CheckTraits.js +28 -40
  87. package/dist/toolkit/CheckTraits.js.map +1 -1
  88. package/dist/toolkit/ConfigurationError.d.ts +3 -0
  89. package/dist/toolkit/ConfigurationError.d.ts.map +1 -0
  90. package/dist/toolkit/ConfigurationError.js +15 -0
  91. package/dist/toolkit/ConfigurationError.js.map +1 -0
  92. package/dist/toolkit/DeriveTrait.d.ts +137 -45
  93. package/dist/toolkit/DeriveTrait.d.ts.map +1 -1
  94. package/dist/toolkit/DeriveTrait.js +26 -14
  95. package/dist/toolkit/DeriveTrait.js.map +1 -1
  96. package/dist/traitIgnores.d.ts +18 -0
  97. package/dist/traitIgnores.d.ts.map +1 -1
  98. package/dist/traitIgnores.js +10 -7
  99. package/dist/traitIgnores.js.map +1 -1
  100. package/getIncludedFiles.ts +42 -0
  101. package/main.ts +17 -12
  102. package/package.json +7 -2
  103. package/skills/traits/SKILL.md +107 -51
  104. package/toolkit/CheckTraits.ts +34 -55
  105. package/toolkit/ConfigurationError.ts +27 -0
  106. package/toolkit/DeriveTrait.ts +27 -14
  107. package/traitIgnores.ts +14 -6
  108. package/_util/words.ts +0 -555
  109. package/dist/_util/words.d.ts +0 -2
  110. package/dist/_util/words.d.ts.map +0 -1
  111. package/dist/_util/words.js +0 -556
  112. package/dist/_util/words.js.map +0 -1
@@ -1,107 +1,199 @@
1
1
  import { Schema as S } from "effect";
2
- import { AiError, DecisionModel, LanguageModel, McpSchema, Tool, Toolkit } from "effect/ai";
2
+ import { DecisionModel, LanguageModel, McpSchema, Tool, Toolkit } from "effect/ai";
3
3
  import { TraitsContext } from "../TraitsContext.ts";
4
4
  import { TraitStore } from "../TraitStore.ts";
5
5
  export declare const DeriveTrait: Tool.Tool<"derive_trait", {
6
6
  readonly parameters: S.Struct<{
7
+ readonly diff: S.optional<S.String>;
8
+ readonly name: S.brand<S.String, "traits/TraitName">;
7
9
  readonly proposedTrait: S.String;
8
10
  readonly approved: S.optional<S.Boolean>;
9
11
  }>;
10
12
  readonly success: S.toTaggedUnion<"_tag", readonly [S.TaggedStruct<"Created", {
11
- readonly trait: S.Struct<{
12
- readonly id: S.brand<S.String, "traits/Trait">;
13
+ readonly trait: S.StructWithRest<S.Struct<{
13
14
  readonly body: S.String;
14
- }>;
15
+ readonly diff: S.optional<S.String>;
16
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
17
+ readonly name: S.brand<S.String, "traits/TraitName">;
18
+ readonly skip: S.optional<S.String>;
19
+ readonly when: S.optional<S.String>;
20
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>;
15
21
  }>, S.TaggedStruct<"Covered", {
16
- readonly traits: S.$Array<S.Struct<{
17
- readonly id: S.brand<S.String, "traits/Trait">;
22
+ readonly traits: S.$Array<S.StructWithRest<S.Struct<{
18
23
  readonly body: S.String;
19
- }>>;
24
+ readonly diff: S.optional<S.String>;
25
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
26
+ readonly name: S.brand<S.String, "traits/TraitName">;
27
+ readonly skip: S.optional<S.String>;
28
+ readonly when: S.optional<S.String>;
29
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>>;
20
30
  }>, S.TaggedStruct<"Clarification", {
21
- readonly traits: S.$Array<S.Struct<{
22
- readonly id: S.brand<S.String, "traits/Trait">;
31
+ readonly traits: S.$Array<S.StructWithRest<S.Struct<{
23
32
  readonly body: S.String;
24
- }>>;
33
+ readonly diff: S.optional<S.String>;
34
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
35
+ readonly name: S.brand<S.String, "traits/TraitName">;
36
+ readonly skip: S.optional<S.String>;
37
+ readonly when: S.optional<S.String>;
38
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>>;
25
39
  readonly proposedTrait: S.String;
26
40
  }>, S.TaggedStruct<"Inconclusive", {
27
41
  readonly findings: S.$Array<S.TaggedStruct<"Fail", {
28
- readonly existing: S.Struct<{
29
- readonly id: S.brand<S.String, "traits/Trait">;
42
+ readonly existing: S.StructWithRest<S.Struct<{
30
43
  readonly body: S.String;
31
- }>;
44
+ readonly diff: S.optional<S.String>;
45
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
46
+ readonly name: S.brand<S.String, "traits/TraitName">;
47
+ readonly skip: S.optional<S.String>;
48
+ readonly when: S.optional<S.String>;
49
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>;
32
50
  readonly proposed: S.String;
33
- readonly reason: S.Literals<readonly ["conflict", "inconclusive"]>;
51
+ readonly reason: S.Literals<readonly ["conflict", "coverage_gap", "inconclusive"]>;
34
52
  }>>;
35
53
  }>, S.TaggedStruct<"Updated", {
36
- readonly trait: S.Struct<{
37
- readonly id: S.brand<S.String, "traits/Trait">;
54
+ readonly trait: S.StructWithRest<S.Struct<{
38
55
  readonly body: S.String;
39
- }>;
56
+ readonly diff: S.optional<S.String>;
57
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
58
+ readonly name: S.brand<S.String, "traits/TraitName">;
59
+ readonly skip: S.optional<S.String>;
60
+ readonly when: S.optional<S.String>;
61
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>;
40
62
  }>, S.TaggedStruct<"Resolved", {
41
- readonly traits: S.$Array<S.Struct<{
42
- readonly id: S.brand<S.String, "traits/Trait">;
63
+ readonly traits: S.$Array<S.StructWithRest<S.Struct<{
43
64
  readonly body: S.String;
44
- }>>;
65
+ readonly diff: S.optional<S.String>;
66
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
67
+ readonly name: S.brand<S.String, "traits/TraitName">;
68
+ readonly skip: S.optional<S.String>;
69
+ readonly when: S.optional<S.String>;
70
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>>;
45
71
  }>, S.TaggedStruct<"ApprovalRequired", {
46
72
  readonly message: S.String;
47
73
  }>, S.TaggedStruct<"RevisionRequired", {
48
74
  readonly feedback: S.String;
49
75
  }>, S.TaggedStruct<"Declined", {}>]>;
50
- readonly failure: S.Union<readonly [typeof AiError.AiError, S.TaggedStruct<"PlatformError", {
76
+ readonly failure: S.Union<readonly [S.TaggedStruct<"AiError", {
77
+ readonly message: S.String;
78
+ }>, S.Union<readonly [S.Struct<{
79
+ readonly _tag: S.tag<"TraitNotFoundError">;
80
+ readonly name: S.brand<S.String, "traits/TraitName">;
81
+ readonly message: S.String;
82
+ }>, S.Struct<{
83
+ readonly _tag: S.tag<"TraitAlreadyExistsError">;
84
+ readonly name: S.brand<S.String, "traits/TraitName">;
85
+ readonly message: S.String;
86
+ }>, S.Struct<{
87
+ readonly _tag: S.tag<"TraitFilenameMismatchError">;
88
+ readonly path: S.String;
89
+ readonly expectedFilename: S.String;
90
+ readonly message: S.String;
91
+ }>, S.Struct<{
92
+ readonly _tag: S.tag<"TraitParseError">;
93
+ readonly path: S.String;
94
+ readonly cause: S.Defect;
51
95
  readonly message: S.String;
52
- }>, typeof McpSchema.ElicitationDeclined]>;
53
- readonly failureMode: "error";
96
+ }>, S.TaggedStruct<"PlatformError", {
97
+ readonly message: S.String;
98
+ }>]>, typeof McpSchema.ElicitationDeclined]>;
99
+ readonly failureMode: "return";
54
100
  }, DecisionModel.DecisionModel | LanguageModel.LanguageModel | TraitStore | TraitsContext>;
55
101
  export declare const toolkit: Toolkit.Toolkit<{
56
102
  readonly derive_trait: Tool.Tool<"derive_trait", {
57
103
  readonly parameters: S.Struct<{
104
+ readonly diff: S.optional<S.String>;
105
+ readonly name: S.brand<S.String, "traits/TraitName">;
58
106
  readonly proposedTrait: S.String;
59
107
  readonly approved: S.optional<S.Boolean>;
60
108
  }>;
61
109
  readonly success: S.toTaggedUnion<"_tag", readonly [S.TaggedStruct<"Created", {
62
- readonly trait: S.Struct<{
63
- readonly id: S.brand<S.String, "traits/Trait">;
110
+ readonly trait: S.StructWithRest<S.Struct<{
64
111
  readonly body: S.String;
65
- }>;
112
+ readonly diff: S.optional<S.String>;
113
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
114
+ readonly name: S.brand<S.String, "traits/TraitName">;
115
+ readonly skip: S.optional<S.String>;
116
+ readonly when: S.optional<S.String>;
117
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>;
66
118
  }>, S.TaggedStruct<"Covered", {
67
- readonly traits: S.$Array<S.Struct<{
68
- readonly id: S.brand<S.String, "traits/Trait">;
119
+ readonly traits: S.$Array<S.StructWithRest<S.Struct<{
69
120
  readonly body: S.String;
70
- }>>;
121
+ readonly diff: S.optional<S.String>;
122
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
123
+ readonly name: S.brand<S.String, "traits/TraitName">;
124
+ readonly skip: S.optional<S.String>;
125
+ readonly when: S.optional<S.String>;
126
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>>;
71
127
  }>, S.TaggedStruct<"Clarification", {
72
- readonly traits: S.$Array<S.Struct<{
73
- readonly id: S.brand<S.String, "traits/Trait">;
128
+ readonly traits: S.$Array<S.StructWithRest<S.Struct<{
74
129
  readonly body: S.String;
75
- }>>;
130
+ readonly diff: S.optional<S.String>;
131
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
132
+ readonly name: S.brand<S.String, "traits/TraitName">;
133
+ readonly skip: S.optional<S.String>;
134
+ readonly when: S.optional<S.String>;
135
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>>;
76
136
  readonly proposedTrait: S.String;
77
137
  }>, S.TaggedStruct<"Inconclusive", {
78
138
  readonly findings: S.$Array<S.TaggedStruct<"Fail", {
79
- readonly existing: S.Struct<{
80
- readonly id: S.brand<S.String, "traits/Trait">;
139
+ readonly existing: S.StructWithRest<S.Struct<{
81
140
  readonly body: S.String;
82
- }>;
141
+ readonly diff: S.optional<S.String>;
142
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
143
+ readonly name: S.brand<S.String, "traits/TraitName">;
144
+ readonly skip: S.optional<S.String>;
145
+ readonly when: S.optional<S.String>;
146
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>;
83
147
  readonly proposed: S.String;
84
- readonly reason: S.Literals<readonly ["conflict", "inconclusive"]>;
148
+ readonly reason: S.Literals<readonly ["conflict", "coverage_gap", "inconclusive"]>;
85
149
  }>>;
86
150
  }>, S.TaggedStruct<"Updated", {
87
- readonly trait: S.Struct<{
88
- readonly id: S.brand<S.String, "traits/Trait">;
151
+ readonly trait: S.StructWithRest<S.Struct<{
89
152
  readonly body: S.String;
90
- }>;
153
+ readonly diff: S.optional<S.String>;
154
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
155
+ readonly name: S.brand<S.String, "traits/TraitName">;
156
+ readonly skip: S.optional<S.String>;
157
+ readonly when: S.optional<S.String>;
158
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>;
91
159
  }>, S.TaggedStruct<"Resolved", {
92
- readonly traits: S.$Array<S.Struct<{
93
- readonly id: S.brand<S.String, "traits/Trait">;
160
+ readonly traits: S.$Array<S.StructWithRest<S.Struct<{
94
161
  readonly body: S.String;
95
- }>>;
162
+ readonly diff: S.optional<S.String>;
163
+ readonly mime: S.optional<S.Union<readonly [S.String, S.$Array<S.String>]>>;
164
+ readonly name: S.brand<S.String, "traits/TraitName">;
165
+ readonly skip: S.optional<S.String>;
166
+ readonly when: S.optional<S.String>;
167
+ }>, readonly [S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>]>>;
96
168
  }>, S.TaggedStruct<"ApprovalRequired", {
97
169
  readonly message: S.String;
98
170
  }>, S.TaggedStruct<"RevisionRequired", {
99
171
  readonly feedback: S.String;
100
172
  }>, S.TaggedStruct<"Declined", {}>]>;
101
- readonly failure: S.Union<readonly [typeof AiError.AiError, S.TaggedStruct<"PlatformError", {
173
+ readonly failure: S.Union<readonly [S.TaggedStruct<"AiError", {
174
+ readonly message: S.String;
175
+ }>, S.Union<readonly [S.Struct<{
176
+ readonly _tag: S.tag<"TraitNotFoundError">;
177
+ readonly name: S.brand<S.String, "traits/TraitName">;
178
+ readonly message: S.String;
179
+ }>, S.Struct<{
180
+ readonly _tag: S.tag<"TraitAlreadyExistsError">;
181
+ readonly name: S.brand<S.String, "traits/TraitName">;
182
+ readonly message: S.String;
183
+ }>, S.Struct<{
184
+ readonly _tag: S.tag<"TraitFilenameMismatchError">;
185
+ readonly path: S.String;
186
+ readonly expectedFilename: S.String;
187
+ readonly message: S.String;
188
+ }>, S.Struct<{
189
+ readonly _tag: S.tag<"TraitParseError">;
190
+ readonly path: S.String;
191
+ readonly cause: S.Defect;
192
+ readonly message: S.String;
193
+ }>, S.TaggedStruct<"PlatformError", {
102
194
  readonly message: S.String;
103
- }>, typeof McpSchema.ElicitationDeclined]>;
104
- readonly failureMode: "error";
195
+ }>]>, typeof McpSchema.ElicitationDeclined]>;
196
+ readonly failureMode: "return";
105
197
  }, DecisionModel.DecisionModel | LanguageModel.LanguageModel | TraitStore | TraitsContext>;
106
198
  }>;
107
199
  export declare const layer: import("effect/Layer").Layer<Tool.Handler<"derive_trait">, never, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"DeriveTrait.d.ts","sourceRoot":"","sources":["../../toolkit/DeriveTrait.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC,EAAU,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAG3F,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAE9D,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0FA6CW,CAAA;AAEnC,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA4B,CAAA;AAEhD,eAAO,MAAM,KAAK,0EAEhB,CAAA"}
1
+ {"version":3,"file":"DeriveTrait.d.ts","sourceRoot":"","sources":["../../toolkit/DeriveTrait.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC,EAAU,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAIlF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAE9D,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0FAyDW,CAAA;AAEnC,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA4B,CAAA;AAEhD,eAAO,MAAM,KAAK,0EAEhB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { Schema as S, String } from "effect";
2
- import { AiError, DecisionModel, LanguageModel, McpSchema, Tool, Toolkit } from "effect/ai";
3
- import { derive, TraitDerivation } from "../TraitDerivation.js";
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 and unnecessary examples.
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 IDs/bodies, removals, the proposal, and replacement policy.
22
- | Nothing is replaced before approval. After approval, generate compatible standalone replacements, save them, then delete only the conflicting originals.
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 returns the existing traits representing the proposal; reuse them without writing a duplicate.
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 ID. Updated returns the saved trait.
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 generated ID. Every response is one tagged TraitDerivation outcome, never a bare array or void.
33
- | Then call check_traits on relevant TypeScript files, using returned trait IDs for focused reviews when appropriate.
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: "The complete reusable rule to save, including scope and exceptions, rather than raw conversation text."
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([AiError.AiError, TraitStoreError, McpSchema.ElicitationDeclined]),
45
- dependencies: [DecisionModel.DecisionModel, LanguageModel.LanguageModel, TraitStore, TraitsContext]
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,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE3F,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/D,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;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2B/B,CAAC;IACF,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;YAC/B,WAAW,EACT,wGAAwG;SAC3G,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,eAAe;IACxB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,SAAS,CAAC,mBAAmB,CAAC,CAAC;IACnF,YAAY,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,aAAa,CAAC,aAAa,EAAE,UAAU,EAAE,aAAa,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,MAAM;CACrB,CAAC,CAAA"}
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"}
@@ -1,3 +1,21 @@
1
1
  /** File-scoped exceptions must appear in the leading line-comment header and include a reason. */
2
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
+ }[];
3
21
  //# sourceMappingURL=traitIgnores.d.ts.map
@@ -1 +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"}
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"}
@@ -1,17 +1,20 @@
1
- import { pipe, String } from "effect";
1
+ import { Array, flow, String } from "effect";
2
2
  /** File-scoped exceptions must appear in the leading line-comment header and include a reason. */
3
3
  export const traitIgnores = (text) => {
4
- const ids = new Set();
4
+ const references = new Set();
5
5
  for (const line of String.split(text, "\n")) {
6
6
  const trimmed = String.trim(line);
7
- if (trimmed === "" || pipe(trimmed, String.startsWith("#!")))
7
+ if (trimmed === "" || trimmed.startsWith("#!"))
8
8
  continue;
9
- if (!pipe(trimmed, String.startsWith("//")))
9
+ if (!trimmed.startsWith("//"))
10
10
  break;
11
11
  const match = /^\/\/\s*traits-ignore-file\s+(\S+)\s*:\s*(\S.*)$/.exec(trimmed);
12
- if (match?.[1] !== undefined)
13
- ids.add(match[1]);
12
+ const reference = match?.[1];
13
+ if (reference !== undefined)
14
+ references.add(reference);
14
15
  }
15
- return ids;
16
+ return references;
16
17
  };
18
+ /** References match unique trait names exactly. */
19
+ export const excludeIgnored = flow(traitIgnores, (references) => Array.filter((trait) => !references.has(trait.name)));
17
20
  //# sourceMappingURL=traitIgnores.js.map
@@ -1 +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"}
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
@@ -8,6 +8,7 @@ import { FetchHttpClient } from "effect/http"
8
8
  import * as TraitsConfigProvider from "./_util/TraitsConfigProvider.ts"
9
9
  import * as TraitsTelemetry from "./_util/TraitsTelemetry.ts"
10
10
  import PackageJson from "./package.json" with { type: "json" }
11
+ import * as ConfigurationError from "./toolkit/ConfigurationError.ts"
11
12
  import { toolkit } from "./toolkit/toolkit.ts"
12
13
  import * as TraitsConfig from "./TraitsConfig.ts"
13
14
  import * as TraitStore from "./TraitStore.ts"
@@ -34,8 +35,8 @@ Command.make("traits", {
34
35
  version: PackageJson.version,
35
36
  protocols: [McpProtocol.v2025_11_25],
36
37
  instructions: String.stripMargin(`
37
- | Traits are durable, reusable coding preferences stored as .agents/traits/<three_words-uuid>.md in the working directory.
38
- | The full filename stem is the trait ID; use it in tool calls, for example rose_karate_frog-<uuid>.
38
+ | 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.
39
+ | 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.
39
40
  | Before completing a response to a user correction, coding preference, or approved alternative idiom, check whether it expresses a reusable rule.
40
41
  | 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".
41
42
  | 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.
@@ -51,24 +52,27 @@ Command.make("traits", {
51
52
  | 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.
52
53
  | Also act on explicit requests to remember a convention. Skip one-off requirements, transient facts, and preferences already covered.
53
54
  | Read existing trait files before drafting. Keep each trait concise and independently applicable, preserving scope and exceptions.
55
+ | 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.
56
+ | Write when values as conditions without a redundant leading When, for example when: editing React state.
54
57
  | Call derive_trait with the drafted body as proposedTrait. It saves only distinct rules; Covered means reuse an existing trait.
55
58
  | A single clarification candidate is selected automatically and shown for approval; supply a complete replacement preserving the existing rule.
56
- | Multiple candidates return Clarification entries without writing: refine the proposal or clarify intent. Do not supply a target ID.
57
- | After fallback approval, retry with the same proposedTrait and approved: true; Updated preserves the selected existing ID.
58
- | Every derive_trait response is one tagged TraitDerivation outcome. Created returns the saved trait and ID; Updated and Resolved return saved replacements.
59
+ | Multiple candidates return Clarification entries without writing: refine the proposal or clarify intent. Do not supply a clarification target.
60
+ | After fallback approval, retry with the same name, proposedTrait and approved: true; Updated preserves the selected existing name.
61
+ | Every derive_trait response is one tagged TraitDerivation outcome. Created returns the saved trait with its unique name; Updated and Resolved return saved replacements.
59
62
  | Covered and Clarification return existing traits without writing. Inconclusive returns relationship findings and blocks all writes.
60
- | Each inconclusive outcome's findings distinguish conflict from inconclusive via reason; clarify ambiguity before retrying.
61
- | Confirmed conflicts enter reconciliation inside derive_trait, selecting only the conflicting originals for an explicit replacement approval.
62
- | Approval must cover affected IDs/bodies, removals, the proposal, and generated reconciliation. Nothing is replaced silently.
63
- | After approval, reconciliation gives the proposal precedence in direct conflicts, saves generated replacements, then removes the conflicting originals.
64
- | Resolved returns saved replacement bodies and IDs. Review them and check relevant code; do not derive the same proposal again.
63
+ | Inconclusive outcome findings use reason to distinguish conflict, coverage_gap, and inconclusive; clarify ambiguity before retrying.
64
+ | 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.
65
+ | Confirmed conflicts and verified coverage gaps enter reconciliation inside derive_trait, selecting only the affected originals for an explicit replacement approval.
66
+ | Approval must cover affected names, bodies, removals, the proposal, and generated reconciliation. Nothing is replaced silently.
67
+ | 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.
68
+ | Resolved returns saved replacement bodies and unique names. Review them and check relevant code; do not derive the same proposal again.
65
69
  | After generating or changing TypeScript files, call check_traits on those files.
66
70
  | check_traits accepts include as file paths or glob strings, for example include: ["traits/**/*.ts"]. Overlapping matches are deduplicated.
67
71
  | Restrict assessments to project-owned source and relevant authored configuration/docs. Prefer an explicit project-file manifest for whole-project audits.
68
72
  | Never submit dependency/reference code, generated build output or lockfiles, Git metadata, secrets, .env files, credentials, or binary assets for assessment.
69
73
  | 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.
70
74
  | After saving or resolving a trait, check the relevant TypeScript files explicitly; creation does not run that review for you.
71
- | Omit traitIds for all traits; use specific IDs for focused follow-ups. Before committing or opening a PR, review the changed TypeScript files.
75
+ | Omit traitNames for all traits; use specific names for focused follow-ups. Before committing or opening a PR, review the changed TypeScript files.
72
76
  | Checks read current full files in 200-line chunks, not Git diffs. A finding's startLine locates a chunk, not an exact violation.
73
77
  | Each finding includes assessment.classification and probabilities: inconclusive is unresolved context, not a confirmed violation. Probabilities are not calibrated guarantees.
74
78
  | Take responsibility for trait compliance whenever a violation is discovered, through tool findings or your own inspection.
@@ -90,6 +94,8 @@ Command.make("traits", {
90
94
  toolkit.pipe(
91
95
  Layer.provide(TraitStore.layerFileSystem(".agents/traits")),
92
96
  Layer.provide(TraitsConfig.layer),
97
+ Layer.provide(TraitsConfigProvider.layer),
98
+ Layer.catchCause(ConfigurationError.layer),
93
99
  Layer.provideMerge(McpServer.McpServer.layer)
94
100
  )
95
101
  ),
@@ -97,7 +103,6 @@ Command.make("traits", {
97
103
  Effect.withSpan("traits.server"),
98
104
  Effect.provide(
99
105
  TraitsTelemetry.layer(Option.getOrUndefined(params.otlpEndpoint)).pipe(
100
- Layer.provideMerge(TraitsConfigProvider.layer),
101
106
  Layer.provideMerge(FetchHttpClient.layer)
102
107
  )
103
108
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traits",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,7 +21,8 @@
21
21
  "dist/**/*.js.map",
22
22
  "dist/**/*.d.ts",
23
23
  "dist/**/*.d.ts.map",
24
- "!dist/vite.config.*"
24
+ "!dist/vite.config.*",
25
+ "!dist/integration/**"
25
26
  ],
26
27
  "type": "module",
27
28
  "exports": {
@@ -34,8 +35,12 @@
34
35
  "publishConfig": {
35
36
  "access": "public"
36
37
  },
38
+ "dependencies": {
39
+ "yaml": "^2.9.1"
40
+ },
37
41
  "devDependencies": {
38
42
  "@effect/platform-node": "4.0.0",
43
+ "@effect/vitest": "4.0.0",
39
44
  "effect": "4.0.0"
40
45
  },
41
46
  "peerDependencies": {