pi-model-profiles 0.2.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/CHANGELOG.md +34 -0
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/config/config.example.json +10 -0
- package/index.ts +3 -0
- package/package.json +66 -0
- package/src/agent-writer.ts +331 -0
- package/src/atomic-write.ts +28 -0
- package/src/config.ts +250 -0
- package/src/constants.ts +99 -0
- package/src/debug-logger.ts +351 -0
- package/src/errors.ts +16 -0
- package/src/frontmatter-parser.ts +249 -0
- package/src/import-service.ts +60 -0
- package/src/index.ts +158 -0
- package/src/modal-theme.ts +334 -0
- package/src/pi-api-utils.ts +56 -0
- package/src/profile-fields.ts +83 -0
- package/src/profile-modal.ts +1175 -0
- package/src/profile-removal-service.ts +106 -0
- package/src/profile-sort-service.ts +105 -0
- package/src/profile-store.ts +418 -0
- package/src/profile-update-service.ts +134 -0
- package/src/types-shims.d.ts +121 -0
- package/src/types.ts +104 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { multiProfilesDebugLogger } from "./debug-logger.js";
|
|
2
|
+
import { ModelProfilesError } from "./errors.js";
|
|
3
|
+
import { captureAgentSnapshots, type AgentSelectionOptions } from "./agent-writer.js";
|
|
4
|
+
import type { ProfilesFile, ProfileUpdateResult, SavedProfile } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Error code for profile not found during update.
|
|
8
|
+
*/
|
|
9
|
+
export const PROFILE_NOT_FOUND_CODE = "PROFILE_NOT_FOUND";
|
|
10
|
+
|
|
11
|
+
export interface ProfileUpdateDataResult {
|
|
12
|
+
data: ProfilesFile;
|
|
13
|
+
result: ProfileUpdateResult;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ProfileUpdatePlan {
|
|
17
|
+
updatedData: ProfilesFile;
|
|
18
|
+
updatedProfile: SavedProfile;
|
|
19
|
+
previousProfile: SavedProfile;
|
|
20
|
+
result: ProfileUpdateResult;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createProfileNotFoundError(profileId: string): ModelProfilesError {
|
|
24
|
+
return new ModelProfilesError(
|
|
25
|
+
`Profile '${profileId}' not found. It may have been removed already.`,
|
|
26
|
+
PROFILE_NOT_FOUND_CODE,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildUpdatePlan(
|
|
31
|
+
data: ProfilesFile,
|
|
32
|
+
profileId: string,
|
|
33
|
+
agentOptions: AgentSelectionOptions,
|
|
34
|
+
): ProfileUpdatePlan {
|
|
35
|
+
const profileIndex = data.profiles.findIndex((profile) => profile.id === profileId);
|
|
36
|
+
const profile = data.profiles[profileIndex];
|
|
37
|
+
|
|
38
|
+
if (profileIndex === -1 || !profile) {
|
|
39
|
+
multiProfilesDebugLogger.log("profile-update", {
|
|
40
|
+
event: "update_failed",
|
|
41
|
+
profileId,
|
|
42
|
+
reason: "profile_not_found",
|
|
43
|
+
profileCount: data.profiles.length,
|
|
44
|
+
});
|
|
45
|
+
throw createProfileNotFoundError(profileId);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const snapshot = captureAgentSnapshots(agentOptions);
|
|
49
|
+
const updatedProfile: SavedProfile = {
|
|
50
|
+
id: profile.id,
|
|
51
|
+
name: profile.name,
|
|
52
|
+
agents: snapshot.agents,
|
|
53
|
+
createdAt: profile.createdAt,
|
|
54
|
+
updatedAt: new Date().toISOString(),
|
|
55
|
+
};
|
|
56
|
+
const updatedProfiles = [
|
|
57
|
+
...data.profiles.slice(0, profileIndex),
|
|
58
|
+
updatedProfile,
|
|
59
|
+
...data.profiles.slice(profileIndex + 1),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
updatedData: {
|
|
64
|
+
version: data.version,
|
|
65
|
+
importedAt: data.importedAt,
|
|
66
|
+
profiles: updatedProfiles,
|
|
67
|
+
},
|
|
68
|
+
updatedProfile,
|
|
69
|
+
previousProfile: profile,
|
|
70
|
+
result: {
|
|
71
|
+
updatedProfileId: profile.id,
|
|
72
|
+
updatedProfileName: profile.name,
|
|
73
|
+
updatedAgents: snapshot.agents.length,
|
|
74
|
+
warnings: snapshot.warnings,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function logProfileUpdated(plan: ProfileUpdatePlan): void {
|
|
80
|
+
multiProfilesDebugLogger.log("profile-update", {
|
|
81
|
+
event: "profile_updated",
|
|
82
|
+
profileId: plan.updatedProfile.id,
|
|
83
|
+
profileName: plan.updatedProfile.name,
|
|
84
|
+
beforeAgentCount: plan.previousProfile.agents.length,
|
|
85
|
+
afterAgentCount: plan.updatedProfile.agents.length,
|
|
86
|
+
warnings: plan.result.warnings,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Update a profile with the current agent state.
|
|
92
|
+
*
|
|
93
|
+
* Captures the current agent snapshots and replaces the profile's agents array.
|
|
94
|
+
* Preserves profile.id, profile.name, and profile.createdAt.
|
|
95
|
+
* Updates profile.updatedAt timestamp to current time.
|
|
96
|
+
* Logs update event with before/after agent counts via debug logger.
|
|
97
|
+
*
|
|
98
|
+
* @param data - The profiles file data
|
|
99
|
+
* @param profileId - The ID of the profile to update
|
|
100
|
+
* @param agentOptions - Options for capturing agent snapshots
|
|
101
|
+
* @returns ProfileUpdateResult with updated profile info and agent counts
|
|
102
|
+
* @throws ModelProfilesError with code PROFILE_NOT_FOUND if profile doesn't exist
|
|
103
|
+
*/
|
|
104
|
+
export function updateProfile(
|
|
105
|
+
data: ProfilesFile,
|
|
106
|
+
profileId: string,
|
|
107
|
+
agentOptions: AgentSelectionOptions,
|
|
108
|
+
): ProfileUpdateResult {
|
|
109
|
+
const plan = buildUpdatePlan(data, profileId, agentOptions);
|
|
110
|
+
logProfileUpdated(plan);
|
|
111
|
+
return plan.result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Update a profile and return the updated profiles file with update metadata.
|
|
116
|
+
*
|
|
117
|
+
* @param data - The profiles file data
|
|
118
|
+
* @param profileId - The ID of the profile to update
|
|
119
|
+
* @param agentOptions - Options for capturing agent snapshots
|
|
120
|
+
* @returns New ProfilesFile with the profile updated and update metadata
|
|
121
|
+
* @throws ModelProfilesError with code PROFILE_NOT_FOUND if profile doesn't exist
|
|
122
|
+
*/
|
|
123
|
+
export function updateProfileAndReturn(
|
|
124
|
+
data: ProfilesFile,
|
|
125
|
+
profileId: string,
|
|
126
|
+
agentOptions: AgentSelectionOptions,
|
|
127
|
+
): ProfileUpdateDataResult {
|
|
128
|
+
const plan = buildUpdatePlan(data, profileId, agentOptions);
|
|
129
|
+
logProfileUpdated(plan);
|
|
130
|
+
return {
|
|
131
|
+
data: plan.updatedData,
|
|
132
|
+
result: plan.result,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
declare namespace NodeJS {
|
|
2
|
+
interface Process {
|
|
3
|
+
pid: number;
|
|
4
|
+
stdout: { columns?: number; rows?: number };
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare const process: NodeJS.Process;
|
|
9
|
+
|
|
10
|
+
declare module "node:assert/strict" {
|
|
11
|
+
const assert: any;
|
|
12
|
+
export default assert;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare module "node:crypto" {
|
|
16
|
+
export function randomUUID(): string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare module "node:fs" {
|
|
20
|
+
export function existsSync(path: string): boolean;
|
|
21
|
+
export function mkdirSync(path: string, options?: unknown): void;
|
|
22
|
+
export function mkdtempSync(path: string): string;
|
|
23
|
+
export function readFileSync(path: string, encoding: string): string;
|
|
24
|
+
export function readdirSync(path: string, options?: unknown): unknown[];
|
|
25
|
+
export function renameSync(oldPath: string, newPath: string): void;
|
|
26
|
+
export function rmSync(path: string, options?: unknown): void;
|
|
27
|
+
export function unlinkSync(path: string): void;
|
|
28
|
+
export function writeFileSync(path: string, content: string, encoding: string): void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare module "node:os" {
|
|
32
|
+
export function homedir(): string;
|
|
33
|
+
export function tmpdir(): string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare module "node:path" {
|
|
37
|
+
export function basename(path: string): string;
|
|
38
|
+
export function dirname(path: string): string;
|
|
39
|
+
export function join(...parts: string[]): string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
declare module "node:test" {
|
|
43
|
+
const test: any;
|
|
44
|
+
export default test;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare module "@mariozechner/pi-coding-agent" {
|
|
48
|
+
export interface Theme {
|
|
49
|
+
[key: string]: any;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ExtensionContext {
|
|
53
|
+
hasUI: boolean;
|
|
54
|
+
cwd: string;
|
|
55
|
+
sessionManager: {
|
|
56
|
+
getEntries(): unknown[];
|
|
57
|
+
};
|
|
58
|
+
ui: {
|
|
59
|
+
notify(message: string, level?: "info" | "warning" | "error"): void;
|
|
60
|
+
custom<T>(...args: any[]): Promise<T>;
|
|
61
|
+
};
|
|
62
|
+
getSystemPrompt(): string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ExtensionCommandContext extends ExtensionContext {
|
|
66
|
+
reload(): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ExtensionAPI {
|
|
70
|
+
registerCommand(name: string, options: { description: string; handler: (...args: any[]) => Promise<void> | void }): void;
|
|
71
|
+
sendUserMessage(content: string, options?: { deliverAs?: "steer" | "followUp" }): void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getAgentDir(): string;
|
|
75
|
+
export function getSettingsListTheme(theme?: Theme): any;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare module "@mariozechner/pi-tui" {
|
|
79
|
+
export type SettingItem = any;
|
|
80
|
+
|
|
81
|
+
export class Box {
|
|
82
|
+
[key: string]: any;
|
|
83
|
+
constructor(...args: any[]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export class Container {
|
|
87
|
+
[key: string]: any;
|
|
88
|
+
constructor(...args: any[]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export class Input {
|
|
92
|
+
focused: boolean;
|
|
93
|
+
onSubmit?: (value: string) => void;
|
|
94
|
+
onEscape?: () => void;
|
|
95
|
+
constructor(...args: any[]);
|
|
96
|
+
setValue(value: string): void;
|
|
97
|
+
render(width: number): string[];
|
|
98
|
+
handleInput(data: string): void;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export class SettingsList {
|
|
102
|
+
[key: string]: any;
|
|
103
|
+
constructor(...args: any[]);
|
|
104
|
+
handleInput(data: string): void;
|
|
105
|
+
updateValue(id: string, value: string): void;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class Spacer {
|
|
109
|
+
[key: string]: any;
|
|
110
|
+
constructor(...args: any[]);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export class Text {
|
|
114
|
+
[key: string]: any;
|
|
115
|
+
constructor(...args: any[]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function matchesKey(data: string, key: string): boolean;
|
|
119
|
+
export function truncateToWidth(text: string, width: number, ellipsis?: string, trimWhitespace?: boolean): string;
|
|
120
|
+
export function visibleWidth(text: string): number;
|
|
121
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export const PROFILE_FIELD_KEYS = ["model", "temperature", "reasoningEffort"] as const;
|
|
2
|
+
|
|
3
|
+
export type ProfileFieldKey = (typeof PROFILE_FIELD_KEYS)[number];
|
|
4
|
+
|
|
5
|
+
/** Sort order for profile listing */
|
|
6
|
+
export type ProfileSortOrder = "name-asc" | "name-desc" | "date-asc" | "date-desc";
|
|
7
|
+
|
|
8
|
+
export interface ProfileFields {
|
|
9
|
+
model?: string;
|
|
10
|
+
temperature?: number;
|
|
11
|
+
reasoningEffort?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SavedProfileAgent {
|
|
15
|
+
fileName: string;
|
|
16
|
+
agentName: string;
|
|
17
|
+
fields: ProfileFields;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SavedProfile {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
agents: SavedProfileAgent[];
|
|
24
|
+
createdAt: string;
|
|
25
|
+
updatedAt: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ProfilesFile {
|
|
29
|
+
version: 2;
|
|
30
|
+
importedAt?: string;
|
|
31
|
+
profiles: SavedProfile[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ProfileStoreLoadResult {
|
|
35
|
+
data: ProfilesFile;
|
|
36
|
+
warning?: string;
|
|
37
|
+
needsSave: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AgentFileRecord {
|
|
41
|
+
path: string;
|
|
42
|
+
fileName: string;
|
|
43
|
+
agentName: string;
|
|
44
|
+
fields: ProfileFields;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AgentScanResult {
|
|
48
|
+
agents: AgentFileRecord[];
|
|
49
|
+
warnings: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface AgentSnapshotResult {
|
|
53
|
+
agents: SavedProfileAgent[];
|
|
54
|
+
warnings: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AppliedAgentUpdate {
|
|
58
|
+
updatedPath: string;
|
|
59
|
+
fileName: string;
|
|
60
|
+
agentName: string;
|
|
61
|
+
appliedKeys: ProfileFieldKey[];
|
|
62
|
+
removedKeys: ProfileFieldKey[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ApplyProfileResult {
|
|
66
|
+
appliedAgents: AppliedAgentUpdate[];
|
|
67
|
+
missingAgents: string[];
|
|
68
|
+
warnings: string[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ImportProfilesResult {
|
|
72
|
+
data: ProfilesFile;
|
|
73
|
+
imported: boolean;
|
|
74
|
+
importedCount: number;
|
|
75
|
+
warnings: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface AppliedProfileOutcome extends ApplyProfileResult {
|
|
79
|
+
profileName: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Result of profile removal operation */
|
|
83
|
+
export interface ProfileRemovalResult {
|
|
84
|
+
removedProfileId: string;
|
|
85
|
+
removedProfileName: string;
|
|
86
|
+
remainingCount: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Result of profile update operation */
|
|
90
|
+
export interface ProfileUpdateResult {
|
|
91
|
+
updatedProfileId: string;
|
|
92
|
+
updatedProfileName: string;
|
|
93
|
+
updatedAgents: number;
|
|
94
|
+
warnings: string[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Result of profile sort operation */
|
|
98
|
+
export interface ProfileSortResult {
|
|
99
|
+
sortedProfiles: SavedProfile[];
|
|
100
|
+
sortOrder: ProfileSortOrder;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Re-export config types for type access
|
|
104
|
+
export type { MultiProfilesConfig } from "./config.js";
|