pi-continuous-learning 0.5.1 → 0.6.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/dist/cli/analyze-single-shot.d.ts +56 -0
- package/dist/cli/analyze-single-shot.d.ts.map +1 -0
- package/dist/cli/analyze-single-shot.js +83 -0
- package/dist/cli/analyze-single-shot.js.map +1 -0
- package/dist/cli/analyze.js +70 -81
- package/dist/cli/analyze.js.map +1 -1
- package/dist/observation-preprocessor.d.ts +26 -0
- package/dist/observation-preprocessor.d.ts.map +1 -0
- package/dist/observation-preprocessor.js +31 -0
- package/dist/observation-preprocessor.js.map +1 -0
- package/dist/prompts/analyzer-system-single-shot.d.ts +6 -0
- package/dist/prompts/analyzer-system-single-shot.d.ts.map +1 -0
- package/dist/prompts/analyzer-system-single-shot.js +124 -0
- package/dist/prompts/analyzer-system-single-shot.js.map +1 -0
- package/dist/prompts/analyzer-user-single-shot.d.ts +22 -0
- package/dist/prompts/analyzer-user-single-shot.d.ts.map +1 -0
- package/dist/prompts/analyzer-user-single-shot.js +53 -0
- package/dist/prompts/analyzer-user-single-shot.js.map +1 -0
- package/dist/prompts/analyzer-user.d.ts +3 -1
- package/dist/prompts/analyzer-user.d.ts.map +1 -1
- package/dist/prompts/analyzer-user.js +20 -7
- package/dist/prompts/analyzer-user.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/analyze-single-shot.ts +145 -0
- package/src/cli/analyze.ts +82 -124
- package/src/observation-preprocessor.ts +48 -0
- package/src/prompts/analyzer-system-single-shot.ts +123 -0
- package/src/prompts/analyzer-user-single-shot.ts +88 -0
- package/src/prompts/analyzer-user.ts +26 -8
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User prompt builder for the single-shot background analyzer.
|
|
3
|
+
* Includes current instincts inline (no tool calls needed) and filtered observations.
|
|
4
|
+
*/
|
|
5
|
+
import type { InstalledSkill, Instinct, ProjectEntry } from "../types.js";
|
|
6
|
+
export interface SingleShotPromptOptions {
|
|
7
|
+
agentsMdProject?: string | null;
|
|
8
|
+
agentsMdGlobal?: string | null;
|
|
9
|
+
installedSkills?: InstalledSkill[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Builds the user prompt for the single-shot analyzer.
|
|
13
|
+
* Embeds all current instincts inline so the model has full context
|
|
14
|
+
* without making any tool calls.
|
|
15
|
+
*
|
|
16
|
+
* @param project - Project metadata
|
|
17
|
+
* @param existingInstincts - All current instincts (project + global)
|
|
18
|
+
* @param observationLines - Preprocessed observation lines (JSONL strings)
|
|
19
|
+
* @param options - Optional AGENTS.md content and installed skills
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildSingleShotUserPrompt(project: ProjectEntry, existingInstincts: Instinct[], observationLines: string[], options?: SingleShotPromptOptions): string;
|
|
22
|
+
//# sourceMappingURL=analyzer-user-single-shot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer-user-single-shot.d.ts","sourceRoot":"","sources":["../../src/prompts/analyzer-user-single-shot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG1E,MAAM,WAAW,uBAAuB;IACtC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,YAAY,EACrB,iBAAiB,EAAE,QAAQ,EAAE,EAC7B,gBAAgB,EAAE,MAAM,EAAE,EAC1B,OAAO,GAAE,uBAA4B,GACpC,MAAM,CA2DR"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { formatInstinctsForPrompt } from "../cli/analyze-single-shot.js";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the user prompt for the single-shot analyzer.
|
|
4
|
+
* Embeds all current instincts inline so the model has full context
|
|
5
|
+
* without making any tool calls.
|
|
6
|
+
*
|
|
7
|
+
* @param project - Project metadata
|
|
8
|
+
* @param existingInstincts - All current instincts (project + global)
|
|
9
|
+
* @param observationLines - Preprocessed observation lines (JSONL strings)
|
|
10
|
+
* @param options - Optional AGENTS.md content and installed skills
|
|
11
|
+
*/
|
|
12
|
+
export function buildSingleShotUserPrompt(project, existingInstincts, observationLines, options = {}) {
|
|
13
|
+
const { agentsMdProject = null, agentsMdGlobal = null, installedSkills = [] } = options;
|
|
14
|
+
const observationBlock = observationLines.length > 0
|
|
15
|
+
? observationLines.join("\n")
|
|
16
|
+
: "(no observations recorded yet)";
|
|
17
|
+
const instinctBlock = formatInstinctsForPrompt(existingInstincts);
|
|
18
|
+
const parts = [
|
|
19
|
+
"## Project Context",
|
|
20
|
+
"",
|
|
21
|
+
`project_id: ${project.id}`,
|
|
22
|
+
`project_name: ${project.name}`,
|
|
23
|
+
"",
|
|
24
|
+
"## Existing Instincts",
|
|
25
|
+
"",
|
|
26
|
+
instinctBlock,
|
|
27
|
+
"",
|
|
28
|
+
"## New Observations (preprocessed)",
|
|
29
|
+
"",
|
|
30
|
+
"```",
|
|
31
|
+
observationBlock,
|
|
32
|
+
"```",
|
|
33
|
+
];
|
|
34
|
+
if (agentsMdProject != null || agentsMdGlobal != null) {
|
|
35
|
+
parts.push("", "## Existing Guidelines", "");
|
|
36
|
+
if (agentsMdProject != null) {
|
|
37
|
+
parts.push("### Project AGENTS.md", "", agentsMdProject, "");
|
|
38
|
+
}
|
|
39
|
+
if (agentsMdGlobal != null) {
|
|
40
|
+
parts.push("### Global AGENTS.md", "", agentsMdGlobal, "");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (installedSkills.length > 0) {
|
|
44
|
+
parts.push("", "## Installed Skills", "");
|
|
45
|
+
for (const skill of installedSkills) {
|
|
46
|
+
parts.push(`- **${skill.name}**: ${skill.description}`);
|
|
47
|
+
}
|
|
48
|
+
parts.push("");
|
|
49
|
+
}
|
|
50
|
+
parts.push("", "## Instructions", "", "1. Review the existing instincts above.", "2. Analyze the new observations for patterns per the system prompt rules.", "3. Return a JSON change-set: create new instincts, update existing ones, or delete obsolete ones.", "4. Apply feedback analysis using the active_instincts field in each observation.", "5. Passive confidence decay has already been applied before this analysis.", "", "Return ONLY the JSON object. No prose, no markdown fences.");
|
|
51
|
+
return parts.join("\n");
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=analyzer-user-single-shot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer-user-single-shot.js","sourceRoot":"","sources":["../../src/prompts/analyzer-user-single-shot.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAQzE;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAAqB,EACrB,iBAA6B,EAC7B,gBAA0B,EAC1B,UAAmC,EAAE;IAErC,MAAM,EAAE,eAAe,GAAG,IAAI,EAAE,cAAc,GAAG,IAAI,EAAE,eAAe,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAExF,MAAM,gBAAgB,GACpB,gBAAgB,CAAC,MAAM,GAAG,CAAC;QACzB,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;QAC7B,CAAC,CAAC,gCAAgC,CAAC;IAEvC,MAAM,aAAa,GAAG,wBAAwB,CAAC,iBAAiB,CAAC,CAAC;IAElE,MAAM,KAAK,GAAa;QACtB,oBAAoB;QACpB,EAAE;QACF,eAAe,OAAO,CAAC,EAAE,EAAE;QAC3B,iBAAiB,OAAO,CAAC,IAAI,EAAE;QAC/B,EAAE;QACF,uBAAuB;QACvB,EAAE;QACF,aAAa;QACb,EAAE;QACF,oCAAoC;QACpC,EAAE;QACF,KAAK;QACL,gBAAgB;QAChB,KAAK;KACN,CAAC;IAEF,IAAI,eAAe,IAAI,IAAI,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,wBAAwB,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI,eAAe,IAAI,IAAI,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAC1C,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,EAAE,EACF,iBAAiB,EACjB,EAAE,EACF,yCAAyC,EACzC,2EAA2E,EAC3E,mGAAmG,EACnG,kFAAkF,EAClF,4EAA4E,EAC5E,EAAE,EACF,4DAA4D,CAC7D,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -15,8 +15,10 @@ export declare function tailObservations(observationsPath: string, maxEntries?:
|
|
|
15
15
|
export interface TailSinceResult {
|
|
16
16
|
lines: string[];
|
|
17
17
|
totalLineCount: number;
|
|
18
|
+
/** Number of raw new lines before preprocessing. */
|
|
19
|
+
rawLineCount: number;
|
|
18
20
|
}
|
|
19
|
-
export declare function tailObservationsSince(observationsPath: string, sinceLineCount: number, maxEntries?: number): TailSinceResult;
|
|
21
|
+
export declare function tailObservationsSince(observationsPath: string, sinceLineCount: number, maxEntries?: number, preprocess?: boolean): TailSinceResult;
|
|
20
22
|
export interface AnalyzerUserPromptOptions {
|
|
21
23
|
agentsMdProject?: string | null;
|
|
22
24
|
agentsMdGlobal?: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer-user.d.ts","sourceRoot":"","sources":["../../src/prompts/analyzer-user.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"analyzer-user.d.ts","sourceRoot":"","sources":["../../src/prompts/analyzer-user.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAM7E;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,gBAAgB,EAAE,MAAM,EACxB,UAAU,SAAmB,GAC5B,MAAM,EAAE,CAUV;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,qBAAqB,CACnC,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,UAAU,SAAmB,EAC7B,UAAU,UAAO,GAChB,eAAe,CAkCjB;AAED,MAAM,WAAW,yBAAyB;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAmER"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* to locate observations and instinct files for the current project.
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { preprocessObservations } from "../observation-preprocessor.js";
|
|
7
8
|
/** Maximum number of observation lines to include in analysis. */
|
|
8
9
|
const MAX_TAIL_ENTRIES = 500;
|
|
9
10
|
/**
|
|
@@ -24,9 +25,9 @@ export function tailObservations(observationsPath, maxEntries = MAX_TAIL_ENTRIES
|
|
|
24
25
|
.filter((l) => l.length > 0);
|
|
25
26
|
return lines.slice(-maxEntries);
|
|
26
27
|
}
|
|
27
|
-
export function tailObservationsSince(observationsPath, sinceLineCount, maxEntries = MAX_TAIL_ENTRIES) {
|
|
28
|
+
export function tailObservationsSince(observationsPath, sinceLineCount, maxEntries = MAX_TAIL_ENTRIES, preprocess = true) {
|
|
28
29
|
if (!existsSync(observationsPath)) {
|
|
29
|
-
return { lines: [], totalLineCount: 0 };
|
|
30
|
+
return { lines: [], totalLineCount: 0, rawLineCount: 0 };
|
|
30
31
|
}
|
|
31
32
|
const content = readFileSync(observationsPath, "utf-8");
|
|
32
33
|
const allLines = content
|
|
@@ -36,11 +37,23 @@ export function tailObservationsSince(observationsPath, sinceLineCount, maxEntri
|
|
|
36
37
|
const totalLineCount = allLines.length;
|
|
37
38
|
// If file was archived/reset (fewer lines than cursor), treat as fresh
|
|
38
39
|
const effectiveSince = totalLineCount < sinceLineCount ? 0 : sinceLineCount;
|
|
39
|
-
const newLines = allLines.slice(effectiveSince);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
totalLineCount,
|
|
43
|
-
}
|
|
40
|
+
const newLines = allLines.slice(effectiveSince).slice(-maxEntries);
|
|
41
|
+
const rawLineCount = newLines.length;
|
|
42
|
+
if (!preprocess) {
|
|
43
|
+
return { lines: newLines, totalLineCount, rawLineCount };
|
|
44
|
+
}
|
|
45
|
+
const parsed = [];
|
|
46
|
+
for (const line of newLines) {
|
|
47
|
+
try {
|
|
48
|
+
parsed.push(JSON.parse(line));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// skip malformed lines
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const filtered = preprocessObservations(parsed);
|
|
55
|
+
const lines = filtered.map((obs) => JSON.stringify(obs));
|
|
56
|
+
return { lines, totalLineCount, rawLineCount };
|
|
44
57
|
}
|
|
45
58
|
/**
|
|
46
59
|
* Builds the user prompt for the background Haiku analyzer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer-user.js","sourceRoot":"","sources":["../../src/prompts/analyzer-user.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"analyzer-user.js","sourceRoot":"","sources":["../../src/prompts/analyzer-user.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-shot (non-agentic) analyzer core.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the multi-turn agentic session with a single complete() call.
|
|
5
|
+
* The model receives all current instincts inline and returns a JSON change-set.
|
|
6
|
+
* Changes are applied client-side, eliminating the ~16x cache-read multiplier.
|
|
7
|
+
*/
|
|
8
|
+
import type { AssistantMessage, Context } from "@mariozechner/pi-ai";
|
|
9
|
+
import { complete } from "@mariozechner/pi-ai";
|
|
10
|
+
import type { Instinct } from "../types.js";
|
|
11
|
+
import { serializeInstinct } from "../instinct-parser.js";
|
|
12
|
+
|
|
13
|
+
export interface InstinctChangePayload {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string;
|
|
16
|
+
trigger: string;
|
|
17
|
+
action: string;
|
|
18
|
+
confidence: number;
|
|
19
|
+
domain: string;
|
|
20
|
+
scope: "project" | "global";
|
|
21
|
+
observation_count?: number;
|
|
22
|
+
confirmed_count?: number;
|
|
23
|
+
contradicted_count?: number;
|
|
24
|
+
inactive_count?: number;
|
|
25
|
+
evidence?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface InstinctChange {
|
|
29
|
+
action: "create" | "update" | "delete";
|
|
30
|
+
instinct?: InstinctChangePayload;
|
|
31
|
+
/** For delete: the instinct ID to remove. */
|
|
32
|
+
id?: string;
|
|
33
|
+
/** For delete: the scope to target. */
|
|
34
|
+
scope?: "project" | "global";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SingleShotResult {
|
|
38
|
+
changes: InstinctChange[];
|
|
39
|
+
message: AssistantMessage;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Parses the model's raw text response into an array of InstinctChange.
|
|
44
|
+
* Strips markdown code fences if present. Throws on invalid JSON or schema.
|
|
45
|
+
*/
|
|
46
|
+
export function parseChanges(raw: string): InstinctChange[] {
|
|
47
|
+
const stripped = raw
|
|
48
|
+
.replace(/^```(?:json)?\s*/i, "")
|
|
49
|
+
.replace(/\s*```\s*$/, "")
|
|
50
|
+
.trim();
|
|
51
|
+
|
|
52
|
+
let parsed: unknown;
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(stripped);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Analyzer returned invalid JSON: ${String(e)}\nRaw: ${raw.slice(0, 200)}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
typeof parsed !== "object" ||
|
|
63
|
+
parsed === null ||
|
|
64
|
+
!Array.isArray((parsed as { changes?: unknown }).changes)
|
|
65
|
+
) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Analyzer response missing 'changes' array. Got: ${JSON.stringify(parsed).slice(0, 200)}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (parsed as { changes: InstinctChange[] }).changes;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Builds a full Instinct from a create/update change.
|
|
76
|
+
* Returns null for delete changes or changes with missing instinct data.
|
|
77
|
+
*/
|
|
78
|
+
export function buildInstinctFromChange(
|
|
79
|
+
change: InstinctChange,
|
|
80
|
+
existing: Instinct | null,
|
|
81
|
+
projectId: string
|
|
82
|
+
): Instinct | null {
|
|
83
|
+
if (change.action === "delete" || !change.instinct) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const now = new Date().toISOString();
|
|
88
|
+
const payload = change.instinct;
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
id: payload.id,
|
|
92
|
+
title: payload.title,
|
|
93
|
+
trigger: payload.trigger,
|
|
94
|
+
action: payload.action,
|
|
95
|
+
confidence: Math.max(0.1, Math.min(0.9, payload.confidence)),
|
|
96
|
+
domain: payload.domain,
|
|
97
|
+
scope: payload.scope,
|
|
98
|
+
source: "personal",
|
|
99
|
+
...(payload.scope === "project" ? { project_id: projectId } : {}),
|
|
100
|
+
created_at: existing?.created_at ?? now,
|
|
101
|
+
updated_at: now,
|
|
102
|
+
observation_count: payload.observation_count ?? 1,
|
|
103
|
+
confirmed_count: payload.confirmed_count ?? 0,
|
|
104
|
+
contradicted_count: payload.contradicted_count ?? 0,
|
|
105
|
+
inactive_count: payload.inactive_count ?? 0,
|
|
106
|
+
...(payload.evidence !== undefined ? { evidence: payload.evidence } : {}),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Formats existing instincts as serialized markdown blocks for inline context.
|
|
112
|
+
*/
|
|
113
|
+
export function formatInstinctsForPrompt(instincts: Instinct[]): string {
|
|
114
|
+
if (instincts.length === 0) {
|
|
115
|
+
return "(no existing instincts)";
|
|
116
|
+
}
|
|
117
|
+
return instincts.map((i) => serializeInstinct(i)).join("\n---\n");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Runs a single complete() call with the provided context.
|
|
122
|
+
* Returns parsed changes and the raw AssistantMessage (for usage stats).
|
|
123
|
+
*/
|
|
124
|
+
export async function runSingleShot(
|
|
125
|
+
context: Context,
|
|
126
|
+
model: Parameters<typeof complete>[0],
|
|
127
|
+
apiKey: string,
|
|
128
|
+
signal?: AbortSignal
|
|
129
|
+
): Promise<SingleShotResult> {
|
|
130
|
+
const opts: Parameters<typeof complete>[2] = { apiKey };
|
|
131
|
+
if (signal !== undefined) opts.signal = signal;
|
|
132
|
+
const message = await complete(model, context, opts);
|
|
133
|
+
|
|
134
|
+
const textContent = message.content
|
|
135
|
+
.filter((c) => c.type === "text")
|
|
136
|
+
.map((c) => (c as { type: "text"; text: string }).text)
|
|
137
|
+
.join("");
|
|
138
|
+
|
|
139
|
+
if (!textContent.trim()) {
|
|
140
|
+
throw new Error("Analyzer returned empty response");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const changes = parseChanges(textContent);
|
|
144
|
+
return { changes, message };
|
|
145
|
+
}
|
package/src/cli/analyze.ts
CHANGED
|
@@ -7,36 +7,35 @@ import {
|
|
|
7
7
|
unlinkSync,
|
|
8
8
|
} from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
-
import {
|
|
11
|
-
createAgentSession,
|
|
12
|
-
SessionManager,
|
|
13
|
-
AuthStorage,
|
|
14
|
-
ModelRegistry,
|
|
15
|
-
DefaultResourceLoader,
|
|
16
|
-
} from "@mariozechner/pi-coding-agent";
|
|
10
|
+
import { AuthStorage } from "@mariozechner/pi-coding-agent";
|
|
17
11
|
import { getModel } from "@mariozechner/pi-ai";
|
|
18
12
|
|
|
19
13
|
import { loadConfig, DEFAULT_CONFIG } from "../config.js";
|
|
20
|
-
import type { ProjectEntry } from "../types.js";
|
|
14
|
+
import type { InstalledSkill, ProjectEntry } from "../types.js";
|
|
21
15
|
import {
|
|
22
16
|
getBaseDir,
|
|
23
17
|
getProjectsRegistryPath,
|
|
24
18
|
getObservationsPath,
|
|
25
19
|
getProjectDir,
|
|
20
|
+
getProjectInstinctsDir,
|
|
21
|
+
getGlobalInstinctsDir,
|
|
26
22
|
} from "../storage.js";
|
|
27
23
|
import { countObservations } from "../observations.js";
|
|
28
24
|
import { runDecayPass } from "../instinct-decay.js";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
25
|
+
import { tailObservationsSince } from "../prompts/analyzer-user.js";
|
|
26
|
+
import { buildSingleShotSystemPrompt } from "../prompts/analyzer-system-single-shot.js";
|
|
27
|
+
import { buildSingleShotUserPrompt } from "../prompts/analyzer-user-single-shot.js";
|
|
28
|
+
import {
|
|
29
|
+
runSingleShot,
|
|
30
|
+
buildInstinctFromChange,
|
|
31
|
+
} from "./analyze-single-shot.js";
|
|
31
32
|
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} from "../instinct-tools.js";
|
|
33
|
+
loadProjectInstincts,
|
|
34
|
+
loadGlobalInstincts,
|
|
35
|
+
saveInstinct,
|
|
36
|
+
} from "../instinct-store.js";
|
|
37
37
|
import { readAgentsMd } from "../agents-md.js";
|
|
38
38
|
import { homedir } from "node:os";
|
|
39
|
-
import type { InstalledSkill } from "../types.js";
|
|
40
39
|
import { AnalyzeLogger, type ProjectRunStats, type RunSummary } from "./analyze-logger.js";
|
|
41
40
|
|
|
42
41
|
// ---------------------------------------------------------------------------
|
|
@@ -59,7 +58,6 @@ function acquireLock(baseDir: string): boolean {
|
|
|
59
58
|
const lock = JSON.parse(content) as { pid: number; started_at: string };
|
|
60
59
|
const age = Date.now() - new Date(lock.started_at).getTime();
|
|
61
60
|
|
|
62
|
-
// Check if the owning process is still alive
|
|
63
61
|
try {
|
|
64
62
|
process.kill(lock.pid, 0); // signal 0 = existence check, no actual signal
|
|
65
63
|
if (age < LOCK_STALE_MS) {
|
|
@@ -104,72 +102,6 @@ function startGlobalTimeout(timeoutMs: number, logger: AnalyzeLogger): void {
|
|
|
104
102
|
}, timeoutMs).unref();
|
|
105
103
|
}
|
|
106
104
|
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
// Instinct operation tracking
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
|
-
|
|
111
|
-
interface InstinctOpCounts {
|
|
112
|
-
created: number;
|
|
113
|
-
updated: number;
|
|
114
|
-
deleted: number;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Wraps instinct tools to count create/update/delete operations.
|
|
119
|
-
* Returns new tool instances that increment the provided counts.
|
|
120
|
-
*/
|
|
121
|
-
function wrapInstinctToolsWithTracking(
|
|
122
|
-
projectId: string,
|
|
123
|
-
projectName: string,
|
|
124
|
-
baseDir: string,
|
|
125
|
-
counts: InstinctOpCounts
|
|
126
|
-
) {
|
|
127
|
-
const writeTool = createInstinctWriteTool(projectId, projectName, baseDir);
|
|
128
|
-
const deleteTool = createInstinctDeleteTool(projectId, baseDir);
|
|
129
|
-
|
|
130
|
-
const trackedWrite = {
|
|
131
|
-
...writeTool,
|
|
132
|
-
async execute(
|
|
133
|
-
toolCallId: string,
|
|
134
|
-
params: Parameters<typeof writeTool.execute>[1],
|
|
135
|
-
signal: AbortSignal | undefined,
|
|
136
|
-
onUpdate: unknown,
|
|
137
|
-
ctx: unknown
|
|
138
|
-
) {
|
|
139
|
-
const result = await writeTool.execute(toolCallId, params, signal, onUpdate, ctx);
|
|
140
|
-
const details = result.details as { action?: string } | undefined;
|
|
141
|
-
if (details?.action === "created") {
|
|
142
|
-
counts.created++;
|
|
143
|
-
} else {
|
|
144
|
-
counts.updated++;
|
|
145
|
-
}
|
|
146
|
-
return result;
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const trackedDelete = {
|
|
151
|
-
...deleteTool,
|
|
152
|
-
async execute(
|
|
153
|
-
toolCallId: string,
|
|
154
|
-
params: Parameters<typeof deleteTool.execute>[1],
|
|
155
|
-
signal: AbortSignal | undefined,
|
|
156
|
-
onUpdate: unknown,
|
|
157
|
-
ctx: unknown
|
|
158
|
-
) {
|
|
159
|
-
const result = await deleteTool.execute(toolCallId, params, signal, onUpdate, ctx);
|
|
160
|
-
counts.deleted++;
|
|
161
|
-
return result;
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
listTool: createInstinctListTool(projectId, baseDir),
|
|
167
|
-
readTool: createInstinctReadTool(projectId, baseDir),
|
|
168
|
-
writeTool: trackedWrite,
|
|
169
|
-
deleteTool: trackedDelete,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
105
|
// ---------------------------------------------------------------------------
|
|
174
106
|
// Per-project analysis
|
|
175
107
|
// ---------------------------------------------------------------------------
|
|
@@ -237,10 +169,13 @@ async function analyzeProject(
|
|
|
237
169
|
|
|
238
170
|
const obsPath = getObservationsPath(project.id, baseDir);
|
|
239
171
|
const sinceLineCount = meta.last_observation_line_count ?? 0;
|
|
240
|
-
const { lines: newObsLines, totalLineCount } = tailObservationsSince(
|
|
172
|
+
const { lines: newObsLines, totalLineCount, rawLineCount } = tailObservationsSince(
|
|
173
|
+
obsPath,
|
|
174
|
+
sinceLineCount
|
|
175
|
+
);
|
|
241
176
|
|
|
242
177
|
if (newObsLines.length === 0) {
|
|
243
|
-
return { ran: false, skippedReason: "no new observation lines" };
|
|
178
|
+
return { ran: false, skippedReason: "no new observation lines after preprocessing" };
|
|
244
179
|
}
|
|
245
180
|
|
|
246
181
|
const obsCount = countObservations(project.id, baseDir);
|
|
@@ -249,11 +184,14 @@ async function analyzeProject(
|
|
|
249
184
|
}
|
|
250
185
|
|
|
251
186
|
const startTime = Date.now();
|
|
252
|
-
logger.projectStart(project.id, project.name,
|
|
187
|
+
logger.projectStart(project.id, project.name, rawLineCount, obsCount);
|
|
253
188
|
|
|
254
189
|
runDecayPass(project.id, baseDir);
|
|
255
190
|
|
|
256
|
-
|
|
191
|
+
// Load current instincts inline - no tool calls needed
|
|
192
|
+
const projectInstincts = loadProjectInstincts(project.id, baseDir);
|
|
193
|
+
const globalInstincts = loadGlobalInstincts(baseDir);
|
|
194
|
+
const allInstincts = [...projectInstincts, ...globalInstincts];
|
|
257
195
|
|
|
258
196
|
const agentsMdProject = readAgentsMd(join(project.root, "AGENTS.md"));
|
|
259
197
|
const agentsMdGlobal = readAgentsMd(join(homedir(), ".pi", "agent", "AGENTS.md"));
|
|
@@ -270,68 +208,89 @@ async function analyzeProject(
|
|
|
270
208
|
// Skills loading is best-effort - continue without them
|
|
271
209
|
}
|
|
272
210
|
|
|
273
|
-
const userPrompt =
|
|
211
|
+
const userPrompt = buildSingleShotUserPrompt(project, allInstincts, newObsLines, {
|
|
274
212
|
agentsMdProject,
|
|
275
213
|
agentsMdGlobal,
|
|
276
214
|
installedSkills,
|
|
277
|
-
observationLines: newObsLines,
|
|
278
215
|
});
|
|
279
216
|
|
|
280
217
|
const authStorage = AuthStorage.create();
|
|
281
|
-
const modelRegistry = new ModelRegistry(authStorage);
|
|
282
218
|
const modelId = (config.model || DEFAULT_CONFIG.model) as Parameters<typeof getModel>[1];
|
|
283
219
|
const model = getModel("anthropic", modelId);
|
|
220
|
+
const apiKey = await authStorage.getApiKey("anthropic");
|
|
221
|
+
|
|
222
|
+
if (!apiKey) {
|
|
223
|
+
throw new Error("No Anthropic API key configured. Set via auth.json or ANTHROPIC_API_KEY.");
|
|
224
|
+
}
|
|
284
225
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
226
|
+
const context = {
|
|
227
|
+
systemPrompt: buildSingleShotSystemPrompt(),
|
|
228
|
+
messages: [
|
|
229
|
+
{ role: "user" as const, content: userPrompt, timestamp: Date.now() },
|
|
230
|
+
],
|
|
231
|
+
};
|
|
288
232
|
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
trackedTools.writeTool,
|
|
293
|
-
trackedTools.deleteTool,
|
|
294
|
-
];
|
|
233
|
+
const timeoutMs = (config.timeout_seconds ?? DEFAULT_CONFIG.timeout_seconds) * 1000;
|
|
234
|
+
const abortController = new AbortController();
|
|
235
|
+
const timeoutHandle = setTimeout(() => abortController.abort(), timeoutMs);
|
|
295
236
|
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
await loader.reload();
|
|
300
|
-
|
|
301
|
-
const { session } = await createAgentSession({
|
|
302
|
-
model,
|
|
303
|
-
authStorage,
|
|
304
|
-
modelRegistry,
|
|
305
|
-
sessionManager: SessionManager.inMemory(),
|
|
306
|
-
customTools,
|
|
307
|
-
resourceLoader: loader,
|
|
308
|
-
});
|
|
237
|
+
const instinctCounts = { created: 0, updated: 0, deleted: 0 };
|
|
238
|
+
const projectInstinctsDir = getProjectInstinctsDir(project.id, "personal", baseDir);
|
|
239
|
+
const globalInstinctsDir = getGlobalInstinctsDir("personal", baseDir);
|
|
309
240
|
|
|
241
|
+
let singleShotMessage;
|
|
310
242
|
try {
|
|
311
|
-
await
|
|
243
|
+
const result = await runSingleShot(context, model, apiKey, abortController.signal);
|
|
244
|
+
singleShotMessage = result.message;
|
|
245
|
+
|
|
246
|
+
for (const change of result.changes) {
|
|
247
|
+
if (change.action === "delete") {
|
|
248
|
+
const id = change.id;
|
|
249
|
+
if (!id) continue;
|
|
250
|
+
const dir = change.scope === "global" ? globalInstinctsDir : projectInstinctsDir;
|
|
251
|
+
const filePath = join(dir, `${id}.md`);
|
|
252
|
+
if (existsSync(filePath)) {
|
|
253
|
+
unlinkSync(filePath);
|
|
254
|
+
instinctCounts.deleted++;
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
// create or update
|
|
258
|
+
const existing = allInstincts.find((i) => i.id === change.instinct?.id) ?? null;
|
|
259
|
+
const instinct = buildInstinctFromChange(change, existing, project.id);
|
|
260
|
+
if (!instinct) continue;
|
|
261
|
+
|
|
262
|
+
const dir = instinct.scope === "global" ? globalInstinctsDir : projectInstinctsDir;
|
|
263
|
+
saveInstinct(instinct, dir);
|
|
264
|
+
|
|
265
|
+
if (change.action === "create") {
|
|
266
|
+
instinctCounts.created++;
|
|
267
|
+
} else {
|
|
268
|
+
instinctCounts.updated++;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
312
272
|
} finally {
|
|
313
|
-
|
|
273
|
+
clearTimeout(timeoutHandle);
|
|
314
274
|
}
|
|
315
275
|
|
|
316
|
-
|
|
317
|
-
const sessionStats = session.getSessionStats();
|
|
276
|
+
const usage = singleShotMessage!.usage;
|
|
318
277
|
const durationMs = Date.now() - startTime;
|
|
319
278
|
|
|
320
279
|
const stats: ProjectRunStats = {
|
|
321
280
|
project_id: project.id,
|
|
322
281
|
project_name: project.name,
|
|
323
282
|
duration_ms: durationMs,
|
|
324
|
-
observations_processed:
|
|
283
|
+
observations_processed: rawLineCount,
|
|
325
284
|
observations_total: obsCount,
|
|
326
285
|
instincts_created: instinctCounts.created,
|
|
327
286
|
instincts_updated: instinctCounts.updated,
|
|
328
287
|
instincts_deleted: instinctCounts.deleted,
|
|
329
|
-
tokens_input:
|
|
330
|
-
tokens_output:
|
|
331
|
-
tokens_cache_read:
|
|
332
|
-
tokens_cache_write:
|
|
333
|
-
tokens_total:
|
|
334
|
-
cost_usd:
|
|
288
|
+
tokens_input: usage.input,
|
|
289
|
+
tokens_output: usage.output,
|
|
290
|
+
tokens_cache_read: usage.cacheRead,
|
|
291
|
+
tokens_cache_write: usage.cacheWrite,
|
|
292
|
+
tokens_total: usage.totalTokens,
|
|
293
|
+
cost_usd: usage.cost.total,
|
|
335
294
|
model: modelId,
|
|
336
295
|
};
|
|
337
296
|
|
|
@@ -420,7 +379,6 @@ async function main(): Promise<void> {
|
|
|
420
379
|
|
|
421
380
|
main().catch((err) => {
|
|
422
381
|
releaseLock(getBaseDir());
|
|
423
|
-
// Last-resort logging - config may not have loaded
|
|
424
382
|
const logger = new AnalyzeLogger();
|
|
425
383
|
logger.error("Fatal error", err);
|
|
426
384
|
process.exit(1);
|