pi-continuous-learning 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/dist/agents-md.d.ts +23 -2
- package/dist/agents-md.d.ts.map +1 -1
- package/dist/agents-md.js +58 -3
- package/dist/agents-md.js.map +1 -1
- package/dist/cli/analyze-single-shot.d.ts +8 -2
- package/dist/cli/analyze-single-shot.d.ts.map +1 -1
- package/dist/cli/analyze-single-shot.js +25 -3
- package/dist/cli/analyze-single-shot.js.map +1 -1
- package/dist/cli/analyze.js +20 -8
- package/dist/cli/analyze.js.map +1 -1
- package/dist/command-scaffold.d.ts +25 -0
- package/dist/command-scaffold.d.ts.map +1 -0
- package/dist/command-scaffold.js +77 -0
- package/dist/command-scaffold.js.map +1 -0
- package/dist/confidence.d.ts.map +1 -1
- package/dist/confidence.js +2 -1
- package/dist/confidence.js.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -1
- package/dist/graduation.d.ts +63 -0
- package/dist/graduation.d.ts.map +1 -0
- package/dist/graduation.js +155 -0
- package/dist/graduation.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/instinct-cleanup.d.ts +57 -0
- package/dist/instinct-cleanup.d.ts.map +1 -0
- package/dist/instinct-cleanup.js +150 -0
- package/dist/instinct-cleanup.js.map +1 -0
- package/dist/instinct-graduate.d.ts +43 -0
- package/dist/instinct-graduate.d.ts.map +1 -0
- package/dist/instinct-graduate.js +253 -0
- package/dist/instinct-graduate.js.map +1 -0
- package/dist/instinct-parser.d.ts.map +1 -1
- package/dist/instinct-parser.js +12 -0
- package/dist/instinct-parser.js.map +1 -1
- package/dist/instinct-tools.d.ts.map +1 -1
- package/dist/instinct-tools.js +19 -0
- package/dist/instinct-tools.js.map +1 -1
- package/dist/instinct-validator.d.ts +61 -0
- package/dist/instinct-validator.d.ts.map +1 -0
- package/dist/instinct-validator.js +235 -0
- package/dist/instinct-validator.js.map +1 -0
- package/dist/prompts/analyzer-system-single-shot.d.ts.map +1 -1
- package/dist/prompts/analyzer-system-single-shot.js +41 -1
- package/dist/prompts/analyzer-system-single-shot.js.map +1 -1
- package/dist/prompts/analyzer-user-single-shot.d.ts.map +1 -1
- package/dist/prompts/analyzer-user-single-shot.js +1 -1
- package/dist/prompts/analyzer-user-single-shot.js.map +1 -1
- package/dist/skill-scaffold.d.ts +23 -0
- package/dist/skill-scaffold.d.ts.map +1 -0
- package/dist/skill-scaffold.js +62 -0
- package/dist/skill-scaffold.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agents-md.ts +73 -3
- package/src/cli/analyze-single-shot.ts +33 -3
- package/src/cli/analyze.ts +19 -8
- package/src/command-scaffold.ts +105 -0
- package/src/confidence.ts +2 -1
- package/src/config.ts +40 -0
- package/src/graduation.ts +243 -0
- package/src/index.ts +14 -0
- package/src/instinct-cleanup.ts +204 -0
- package/src/instinct-graduate.ts +377 -0
- package/src/instinct-parser.ts +12 -0
- package/src/instinct-tools.ts +26 -0
- package/src/instinct-validator.ts +287 -0
- package/src/prompts/analyzer-system-single-shot.ts +41 -1
- package/src/prompts/analyzer-user-single-shot.ts +6 -0
- package/src/skill-scaffold.ts +90 -0
- package/src/types.ts +10 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instinct validation and semantic deduplication.
|
|
3
|
+
* Rejects instincts with empty, undefined, nonsense, or low-quality fields,
|
|
4
|
+
* and detects near-duplicate instincts via Jaccard similarity.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Instinct } from "./types.js";
|
|
8
|
+
|
|
9
|
+
const MIN_FIELD_LENGTH = 10;
|
|
10
|
+
const INVALID_LITERALS = new Set(["undefined", "null", "none", ""]);
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Known domains (with "other" as escape hatch)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export const KNOWN_DOMAINS = new Set([
|
|
17
|
+
"git",
|
|
18
|
+
"testing",
|
|
19
|
+
"debugging",
|
|
20
|
+
"workflow",
|
|
21
|
+
"typescript",
|
|
22
|
+
"javascript",
|
|
23
|
+
"python",
|
|
24
|
+
"go",
|
|
25
|
+
"css",
|
|
26
|
+
"design",
|
|
27
|
+
"security",
|
|
28
|
+
"performance",
|
|
29
|
+
"documentation",
|
|
30
|
+
"react",
|
|
31
|
+
"node",
|
|
32
|
+
"database",
|
|
33
|
+
"api",
|
|
34
|
+
"devops",
|
|
35
|
+
"architecture",
|
|
36
|
+
"other",
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Verb heuristic for action field
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
/** Common imperative verbs expected at the start of an instinct action. */
|
|
44
|
+
export const KNOWN_VERBS = new Set([
|
|
45
|
+
"add",
|
|
46
|
+
"always",
|
|
47
|
+
"analyze",
|
|
48
|
+
"apply",
|
|
49
|
+
"ask",
|
|
50
|
+
"avoid",
|
|
51
|
+
"build",
|
|
52
|
+
"call",
|
|
53
|
+
"catch",
|
|
54
|
+
"check",
|
|
55
|
+
"clean",
|
|
56
|
+
"confirm",
|
|
57
|
+
"consider",
|
|
58
|
+
"create",
|
|
59
|
+
"define",
|
|
60
|
+
"delete",
|
|
61
|
+
"document",
|
|
62
|
+
"emit",
|
|
63
|
+
"ensure",
|
|
64
|
+
"exclude",
|
|
65
|
+
"export",
|
|
66
|
+
"extract",
|
|
67
|
+
"fetch",
|
|
68
|
+
"find",
|
|
69
|
+
"fix",
|
|
70
|
+
"follow",
|
|
71
|
+
"format",
|
|
72
|
+
"generate",
|
|
73
|
+
"get",
|
|
74
|
+
"handle",
|
|
75
|
+
"import",
|
|
76
|
+
"include",
|
|
77
|
+
"inspect",
|
|
78
|
+
"load",
|
|
79
|
+
"log",
|
|
80
|
+
"look",
|
|
81
|
+
"merge",
|
|
82
|
+
"monitor",
|
|
83
|
+
"move",
|
|
84
|
+
"never",
|
|
85
|
+
"parse",
|
|
86
|
+
"pass",
|
|
87
|
+
"prefer",
|
|
88
|
+
"print",
|
|
89
|
+
"read",
|
|
90
|
+
"record",
|
|
91
|
+
"refactor",
|
|
92
|
+
"reject",
|
|
93
|
+
"rename",
|
|
94
|
+
"require",
|
|
95
|
+
"resolve",
|
|
96
|
+
"return",
|
|
97
|
+
"run",
|
|
98
|
+
"save",
|
|
99
|
+
"scan",
|
|
100
|
+
"search",
|
|
101
|
+
"send",
|
|
102
|
+
"set",
|
|
103
|
+
"show",
|
|
104
|
+
"skip",
|
|
105
|
+
"start",
|
|
106
|
+
"stop",
|
|
107
|
+
"test",
|
|
108
|
+
"track",
|
|
109
|
+
"update",
|
|
110
|
+
"use",
|
|
111
|
+
"validate",
|
|
112
|
+
"verify",
|
|
113
|
+
"watch",
|
|
114
|
+
"wrap",
|
|
115
|
+
"write",
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Validation types
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
export interface ValidationResult {
|
|
123
|
+
valid: boolean;
|
|
124
|
+
reason?: string;
|
|
125
|
+
/** Non-fatal warnings that indicate lower-quality instincts. */
|
|
126
|
+
warnings?: string[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Internal helpers
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
function isInvalidField(value: unknown, fieldName: string): string | null {
|
|
134
|
+
if (value === undefined || value === null) {
|
|
135
|
+
return `${fieldName} is ${String(value)}`;
|
|
136
|
+
}
|
|
137
|
+
if (typeof value !== "string") {
|
|
138
|
+
return `${fieldName} is not a string (got ${typeof value})`;
|
|
139
|
+
}
|
|
140
|
+
const trimmed = value.trim();
|
|
141
|
+
if (INVALID_LITERALS.has(trimmed.toLowerCase())) {
|
|
142
|
+
return `${fieldName} is the literal string "${trimmed}"`;
|
|
143
|
+
}
|
|
144
|
+
if (trimmed.length < MIN_FIELD_LENGTH) {
|
|
145
|
+
return `${fieldName} is too short (${trimmed.length} chars, minimum ${MIN_FIELD_LENGTH})`;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// validateInstinct
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Validates that an instinct's fields meet quality requirements.
|
|
156
|
+
*
|
|
157
|
+
* Rules:
|
|
158
|
+
* - action and trigger must be non-empty, non-null, non-"undefined", and >= 10 chars
|
|
159
|
+
* - action first word should be a known imperative verb (warning if not)
|
|
160
|
+
* - domain, if provided, must be in KNOWN_DOMAINS (with "other" as escape hatch)
|
|
161
|
+
*
|
|
162
|
+
* Returns { valid: true } or { valid: false, reason: "..." }.
|
|
163
|
+
* Non-fatal issues are reported in the optional `warnings` array.
|
|
164
|
+
*/
|
|
165
|
+
export function validateInstinct(fields: {
|
|
166
|
+
action: unknown;
|
|
167
|
+
trigger: unknown;
|
|
168
|
+
domain?: unknown;
|
|
169
|
+
}): ValidationResult {
|
|
170
|
+
const actionError = isInvalidField(fields.action, "action");
|
|
171
|
+
if (actionError) {
|
|
172
|
+
return { valid: false, reason: actionError };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const triggerError = isInvalidField(fields.trigger, "trigger");
|
|
176
|
+
if (triggerError) {
|
|
177
|
+
return { valid: false, reason: triggerError };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const warnings: string[] = [];
|
|
181
|
+
|
|
182
|
+
// Domain validation
|
|
183
|
+
if (fields.domain !== undefined) {
|
|
184
|
+
if (typeof fields.domain !== "string" || !KNOWN_DOMAINS.has(fields.domain.toLowerCase().trim())) {
|
|
185
|
+
return {
|
|
186
|
+
valid: false,
|
|
187
|
+
reason: `domain "${String(fields.domain)}" is not in the known set. Use one of: ${[...KNOWN_DOMAINS].join(", ")}`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Verb heuristic (warning only - does not reject)
|
|
193
|
+
const action = (fields.action as string).trim();
|
|
194
|
+
const firstWord = action.split(/\s+/)[0]?.toLowerCase() ?? "";
|
|
195
|
+
if (firstWord && !KNOWN_VERBS.has(firstWord)) {
|
|
196
|
+
warnings.push(
|
|
197
|
+
`action should start with an imperative verb (got "${firstWord}"). Consider rewriting as a clear instruction.`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return warnings.length > 0 ? { valid: true, warnings } : { valid: true };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// Jaccard similarity deduplication
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
const STOP_WORDS = new Set([
|
|
209
|
+
"a", "an", "the", "is", "are", "was", "were", "be", "been", "being",
|
|
210
|
+
"have", "has", "had", "do", "does", "did", "will", "would", "should",
|
|
211
|
+
"could", "may", "might", "shall", "can", "of", "in", "on", "at", "to",
|
|
212
|
+
"for", "with", "by", "from", "up", "about", "into", "it", "its", "this",
|
|
213
|
+
"that", "these", "those", "and", "or", "but", "if", "as", "when", "where",
|
|
214
|
+
"how", "what", "which", "who", "not", "no", "so",
|
|
215
|
+
]);
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Tokenizes text into a set of meaningful lowercase words.
|
|
219
|
+
* Splits on non-alphanumeric characters and filters stop words and short tokens.
|
|
220
|
+
*/
|
|
221
|
+
export function tokenize(text: string): Set<string> {
|
|
222
|
+
return new Set(
|
|
223
|
+
text
|
|
224
|
+
.toLowerCase()
|
|
225
|
+
.split(/[^a-z0-9]+/)
|
|
226
|
+
.filter((t) => t.length > 2 && !STOP_WORDS.has(t))
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Computes Jaccard similarity between two token sets.
|
|
232
|
+
* Returns 1.0 for two empty sets, 0.0 if one is empty.
|
|
233
|
+
*/
|
|
234
|
+
export function jaccardSimilarity(a: Set<string>, b: Set<string>): number {
|
|
235
|
+
if (a.size === 0 && b.size === 0) return 1.0;
|
|
236
|
+
if (a.size === 0 || b.size === 0) return 0.0;
|
|
237
|
+
|
|
238
|
+
let intersection = 0;
|
|
239
|
+
for (const token of a) {
|
|
240
|
+
if (b.has(token)) intersection++;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const union = a.size + b.size - intersection;
|
|
244
|
+
return intersection / union;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface SimilarityMatch {
|
|
248
|
+
instinct: Instinct;
|
|
249
|
+
similarity: number;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Checks whether a candidate instinct is semantically similar to any existing instinct.
|
|
254
|
+
*
|
|
255
|
+
* Tokenizes trigger+action for both candidate and each existing instinct,
|
|
256
|
+
* computes Jaccard similarity, and returns the closest match above the threshold.
|
|
257
|
+
*
|
|
258
|
+
* @param candidate - The new instinct being considered (trigger + action)
|
|
259
|
+
* @param existing - All current instincts to check against
|
|
260
|
+
* @param skipId - ID to skip (the candidate's own ID on updates)
|
|
261
|
+
* @param threshold - Similarity threshold; default 0.6
|
|
262
|
+
*/
|
|
263
|
+
export function findSimilarInstinct(
|
|
264
|
+
candidate: { trigger: string; action: string },
|
|
265
|
+
existing: Instinct[],
|
|
266
|
+
skipId?: string,
|
|
267
|
+
threshold = 0.6
|
|
268
|
+
): SimilarityMatch | null {
|
|
269
|
+
const candidateTokens = tokenize(`${candidate.trigger} ${candidate.action}`);
|
|
270
|
+
|
|
271
|
+
let bestMatch: SimilarityMatch | null = null;
|
|
272
|
+
|
|
273
|
+
for (const instinct of existing) {
|
|
274
|
+
if (instinct.id === skipId) continue;
|
|
275
|
+
|
|
276
|
+
const existingTokens = tokenize(`${instinct.trigger} ${instinct.action}`);
|
|
277
|
+
const similarity = jaccardSimilarity(candidateTokens, existingTokens);
|
|
278
|
+
|
|
279
|
+
if (similarity >= threshold) {
|
|
280
|
+
if (bestMatch === null || similarity > bestMatch.similarity) {
|
|
281
|
+
bestMatch = { instinct, similarity };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return bestMatch;
|
|
287
|
+
}
|
|
@@ -119,5 +119,45 @@ When in doubt, prefer project scope.
|
|
|
119
119
|
4. New instincts from observation data alone are capped at 0.85 confidence.
|
|
120
120
|
5. Check existing instincts (provided in the user message) for duplicates before creating. Update instead.
|
|
121
121
|
6. Write actions as clear instructions starting with a verb.
|
|
122
|
-
7. Be skeptical of outliers - patterns seen only in unusual circumstances should not become instincts
|
|
122
|
+
7. Be skeptical of outliers - patterns seen only in unusual circumstances should not become instincts.
|
|
123
|
+
|
|
124
|
+
## Quality Tiers
|
|
125
|
+
|
|
126
|
+
Not all patterns are worth recording as instincts. Use this classification before creating:
|
|
127
|
+
|
|
128
|
+
### Tier 1 - Project Conventions (RECORD as instinct)
|
|
129
|
+
Patterns specific to this project's tech stack, codebase conventions, or team practices.
|
|
130
|
+
Examples:
|
|
131
|
+
- "Use the Result<T, E> type for error handling in this project"
|
|
132
|
+
- "Place tests next to source files as *.test.ts"
|
|
133
|
+
- "Prefer functional style - avoid class-based patterns in this codebase"
|
|
134
|
+
|
|
135
|
+
### Tier 2 - Workflow Patterns (RECORD as global instinct)
|
|
136
|
+
Recurring multi-step workflows that apply across many projects.
|
|
137
|
+
Examples:
|
|
138
|
+
- "Run linter and type-checker after every code change"
|
|
139
|
+
- "Write tests before implementation in TDD projects"
|
|
140
|
+
|
|
141
|
+
### Tier 3 - Generic Agent Behavior (DO NOT RECORD - already known)
|
|
142
|
+
Fundamental behaviors all coding agents should follow. These are not project-specific patterns.
|
|
143
|
+
|
|
144
|
+
**DO NOT create instincts for:**
|
|
145
|
+
- Reading files before editing them ("read before edit")
|
|
146
|
+
- Grepping for context before modifying code
|
|
147
|
+
- Clarifying ambiguous requirements before starting
|
|
148
|
+
- Using conventional commit message formats
|
|
149
|
+
- Checking for errors after tool calls
|
|
150
|
+
- Any behavior that is basic good practice for any coding agent
|
|
151
|
+
|
|
152
|
+
These patterns belong in AGENTS.md from the start, not in learned instincts.
|
|
153
|
+
|
|
154
|
+
## AGENTS.md Deduplication
|
|
155
|
+
|
|
156
|
+
The user message includes the current AGENTS.md content for this project and globally.
|
|
157
|
+
Before creating any instinct, check: **is this pattern already covered by AGENTS.md?**
|
|
158
|
+
|
|
159
|
+
If yes: do NOT create an instinct. The pattern is already enforced.
|
|
160
|
+
If a pattern appears in AGENTS.md in the same form, skip it entirely.
|
|
161
|
+
|
|
162
|
+
Only create instincts for patterns that are genuinely absent from AGENTS.md.`;
|
|
123
163
|
}
|
|
@@ -80,6 +80,12 @@ export function buildSingleShotUserPrompt(
|
|
|
80
80
|
"3. Return a JSON change-set: create new instincts, update existing ones, or delete obsolete ones.",
|
|
81
81
|
"4. Apply feedback analysis using the active_instincts field in each observation.",
|
|
82
82
|
"5. Passive confidence decay has already been applied before this analysis.",
|
|
83
|
+
"6. Before creating any instinct, check the Existing Guidelines section above.",
|
|
84
|
+
" If the pattern is already covered by AGENTS.md, do NOT create an instinct for it.",
|
|
85
|
+
"7. Apply the Quality Tier rules from the system prompt:",
|
|
86
|
+
" - Generic agent behaviors (read-before-edit, clarify-before-implement) -> skip entirely",
|
|
87
|
+
" - Project-specific patterns -> project-scoped instinct",
|
|
88
|
+
" - Universal workflow patterns -> global-scoped instinct",
|
|
83
89
|
"",
|
|
84
90
|
"Return ONLY the JSON object. No prose, no markdown fences."
|
|
85
91
|
);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill scaffolding from instinct clusters.
|
|
3
|
+
*
|
|
4
|
+
* When 3+ related instincts in the same domain form a cohesive topic,
|
|
5
|
+
* generates a SKILL.md file that can be installed as a Pi skill.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Instinct } from "./types.js";
|
|
9
|
+
import type { DomainCluster } from "./graduation.js";
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Types
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
export interface SkillScaffold {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
domain: string;
|
|
19
|
+
content: string;
|
|
20
|
+
sourceInstinctIds: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Helpers
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
function toSkillName(domain: string): string {
|
|
28
|
+
return domain.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatInstinctAsSection(instinct: Instinct, index: number): string {
|
|
32
|
+
return [
|
|
33
|
+
`### ${index + 1}. ${instinct.title}`,
|
|
34
|
+
"",
|
|
35
|
+
`**When:** ${instinct.trigger}`,
|
|
36
|
+
"",
|
|
37
|
+
instinct.action,
|
|
38
|
+
"",
|
|
39
|
+
].join("\n");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Public API
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generates a SKILL.md scaffold from a domain cluster of instincts.
|
|
48
|
+
*/
|
|
49
|
+
export function generateSkillScaffold(cluster: DomainCluster): SkillScaffold {
|
|
50
|
+
const name = toSkillName(cluster.domain);
|
|
51
|
+
const sortedInstincts = [...cluster.instincts].sort(
|
|
52
|
+
(a, b) => b.confidence - a.confidence
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const description = `Learned ${cluster.domain} patterns from coding sessions. ` +
|
|
56
|
+
`Covers ${sortedInstincts.length} practices distilled from instinct observations.`;
|
|
57
|
+
|
|
58
|
+
const sections = sortedInstincts.map((inst, i) => formatInstinctAsSection(inst, i));
|
|
59
|
+
|
|
60
|
+
const content = [
|
|
61
|
+
`# ${cluster.domain} Skill`,
|
|
62
|
+
"",
|
|
63
|
+
`> Auto-generated from ${sortedInstincts.length} graduated instincts in the "${cluster.domain}" domain.`,
|
|
64
|
+
"",
|
|
65
|
+
`## Description`,
|
|
66
|
+
"",
|
|
67
|
+
description,
|
|
68
|
+
"",
|
|
69
|
+
`## Practices`,
|
|
70
|
+
"",
|
|
71
|
+
...sections,
|
|
72
|
+
].join("\n");
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
name,
|
|
76
|
+
description,
|
|
77
|
+
domain: cluster.domain,
|
|
78
|
+
content,
|
|
79
|
+
sourceInstinctIds: sortedInstincts.map((i) => i.id),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generates skill scaffolds for all qualifying clusters.
|
|
85
|
+
*/
|
|
86
|
+
export function generateAllSkillScaffolds(
|
|
87
|
+
clusters: DomainCluster[]
|
|
88
|
+
): SkillScaffold[] {
|
|
89
|
+
return clusters.map(generateSkillScaffold);
|
|
90
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -67,8 +67,12 @@ export interface Instinct {
|
|
|
67
67
|
inactive_count: number;
|
|
68
68
|
evidence?: string[];
|
|
69
69
|
flagged_for_removal?: boolean;
|
|
70
|
+
graduated_to?: GraduationTarget;
|
|
71
|
+
graduated_at?: string; // ISO 8601
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
export type GraduationTarget = "agents-md" | "skill" | "command";
|
|
75
|
+
|
|
72
76
|
// ---------------------------------------------------------------------------
|
|
73
77
|
// ProjectEntry
|
|
74
78
|
// ---------------------------------------------------------------------------
|
|
@@ -103,4 +107,10 @@ export interface Config {
|
|
|
103
107
|
active_hours_end: number; // 0-23
|
|
104
108
|
max_idle_seconds: number;
|
|
105
109
|
log_path?: string; // Override analyzer log location (default: ~/.pi/continuous-learning/analyzer.log)
|
|
110
|
+
// Volume control
|
|
111
|
+
max_total_instincts_per_project: number; // hard cap per project (enforced by auto-deletion)
|
|
112
|
+
max_total_instincts_global: number; // hard cap for global instincts (enforced by auto-deletion)
|
|
113
|
+
max_new_instincts_per_run: number; // creation rate limit per analyzer run
|
|
114
|
+
flagged_cleanup_days: number; // auto-delete flagged instincts after N days
|
|
115
|
+
instinct_ttl_days: number; // auto-delete zero-confirmation instincts after N days
|
|
106
116
|
}
|