@aliou/pi-dev-kit 0.6.4 → 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 +2 -2
- package/package.json +15 -10
- package/src/commands/index.ts +5 -1
- package/src/commands/update.ts +139 -91
- package/src/skills/pi-extension/SKILL.md +108 -131
- package/src/skills/pi-extension/references/additional-apis.md +256 -173
- package/src/skills/pi-extension/references/commands.md +113 -33
- package/src/skills/pi-extension/references/components.md +267 -102
- package/src/skills/pi-extension/references/hooks.md +229 -156
- package/src/skills/pi-extension/references/messages.md +97 -92
- package/src/skills/pi-extension/references/modes.md +80 -90
- package/src/skills/pi-extension/references/providers.md +255 -96
- package/src/skills/pi-extension/references/publish.md +76 -62
- package/src/skills/pi-extension/references/state.md +80 -33
- package/src/skills/pi-extension/references/structure.md +156 -245
- package/src/skills/pi-extension/references/testing.md +1 -1
- package/src/skills/pi-extension/references/tools.md +212 -816
- package/src/tools/changelog-tool.ts +237 -230
- package/src/tools/docs-tool.ts +127 -130
- package/src/tools/index.ts +5 -1
- package/src/tools/package-manager-tool.ts +152 -147
- package/src/tools/utils.ts +33 -23
- package/src/tools/version-tool.ts +51 -51
- package/src/index.ts +0 -8
package/src/tools/docs-tool.ts
CHANGED
|
@@ -4,17 +4,19 @@ import { ToolBody, ToolCallHeader, ToolFooter } from "@aliou/pi-utils-ui";
|
|
|
4
4
|
import type {
|
|
5
5
|
AgentToolResult,
|
|
6
6
|
ExtensionAPI,
|
|
7
|
-
ExtensionContext,
|
|
8
7
|
Theme,
|
|
9
|
-
|
|
10
|
-
} from "@
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { Type } from "@sinclair/typebox";
|
|
8
|
+
} from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import { defineTool, keyHint } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
11
|
+
import { type Static, Type } from "typebox";
|
|
14
12
|
import { findPiInstallation } from "./utils";
|
|
15
13
|
|
|
16
14
|
const DocsParamsSchema = Type.Object({});
|
|
17
|
-
type DocsParams =
|
|
15
|
+
type DocsParams = Static<typeof DocsParamsSchema>;
|
|
16
|
+
|
|
17
|
+
function renderDocsCall(_args: DocsParams, theme: Theme) {
|
|
18
|
+
return new ToolCallHeader({ toolName: "Pi Docs" }, theme);
|
|
19
|
+
}
|
|
18
20
|
|
|
19
21
|
interface DocsDetails {
|
|
20
22
|
/** Relative paths from the pi install root, markdown only. */
|
|
@@ -38,144 +40,139 @@ function listFilesRecursive(dir: string, prefix = ""): string[] {
|
|
|
38
40
|
return results;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
43
|
+
const docsTool = defineTool({
|
|
44
|
+
name: "pi_docs",
|
|
45
|
+
label: "Pi Documentation",
|
|
46
|
+
description:
|
|
47
|
+
"List Pi markdown documentation files (README, docs/, examples/)",
|
|
48
|
+
|
|
49
|
+
promptSnippet: "List Pi documentation files",
|
|
50
|
+
promptGuidelines: [
|
|
51
|
+
"Use pi_docs to discover available Pi documentation",
|
|
52
|
+
"pi_docs returns markdown files from README.md, docs/, and examples/",
|
|
53
|
+
],
|
|
54
|
+
|
|
55
|
+
parameters: DocsParamsSchema,
|
|
56
|
+
|
|
57
|
+
async execute(
|
|
58
|
+
_toolCallId,
|
|
59
|
+
_params,
|
|
60
|
+
_signal,
|
|
61
|
+
_onUpdate,
|
|
62
|
+
_ctx,
|
|
63
|
+
): Promise<AgentToolResult<DocsDetails>> {
|
|
64
|
+
const piPath = findPiInstallation();
|
|
65
|
+
if (!piPath) {
|
|
66
|
+
throw new Error("Could not locate running Pi installation directory");
|
|
67
|
+
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
const readmePath = path.join(piPath, "README.md");
|
|
70
|
+
const docsDir = path.join(piPath, "docs");
|
|
71
|
+
const examplesDir = path.join(piPath, "examples");
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
const docFiles: string[] = [];
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
if (fs.existsSync(readmePath)) {
|
|
76
|
+
docFiles.push("README.md");
|
|
77
|
+
}
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
79
|
+
if (fs.existsSync(docsDir)) {
|
|
80
|
+
for (const file of listFilesRecursive(docsDir)) {
|
|
81
|
+
if (file.endsWith(".md")) {
|
|
82
|
+
docFiles.push(`docs/${file}`);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
+
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
87
|
+
if (fs.existsSync(examplesDir)) {
|
|
88
|
+
for (const file of listFilesRecursive(examplesDir)) {
|
|
89
|
+
if (file.endsWith(".md")) {
|
|
90
|
+
docFiles.push(`examples/${file}`);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
}
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
if (docFiles.length === 0) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`No markdown documentation found in Pi installation at ${piPath}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
// Content sent to LLM: full relative paths so it can read them.
|
|
102
|
+
const lines = docFiles.map((rel) => `${path.join(piPath, rel)} (${rel})`);
|
|
103
|
+
const message = `${docFiles.length} markdown files:\n${lines.join("\n")}`;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: "text", text: message }],
|
|
107
|
+
details: {
|
|
108
|
+
docFiles,
|
|
109
|
+
installPath: piPath,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
renderCall(args, theme) {
|
|
115
|
+
return renderDocsCall(args, theme);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
renderResult(result, options, theme) {
|
|
119
|
+
const { details } = result;
|
|
120
|
+
const { isPartial } = options;
|
|
121
|
+
|
|
122
|
+
// Handle isPartial first (this tool doesn't stream, but keep the pattern)
|
|
123
|
+
if (isPartial) {
|
|
124
|
+
return new Text(theme.fg("dim", "Loading..."), 0, 0);
|
|
125
|
+
}
|
|
103
126
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
renderCall(_args: DocsParams, theme: Theme) {
|
|
114
|
-
return new ToolCallHeader({ toolName: "Pi Docs" }, theme);
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
renderResult(
|
|
118
|
-
result: AgentToolResult<DocsDetails>,
|
|
119
|
-
options: ToolRenderResultOptions,
|
|
120
|
-
theme: Theme,
|
|
121
|
-
) {
|
|
122
|
-
const { details } = result;
|
|
123
|
-
const { isPartial } = options;
|
|
124
|
-
|
|
125
|
-
// Handle isPartial first (this tool doesn't stream, but keep the pattern)
|
|
126
|
-
if (isPartial) {
|
|
127
|
-
return new Text(theme.fg("dim", "Loading..."), 0, 0);
|
|
128
|
-
}
|
|
127
|
+
// Check for missing expected fields in details to detect errors
|
|
128
|
+
if (!details || !details.docFiles) {
|
|
129
|
+
const text = result.content[0];
|
|
130
|
+
return new Text(
|
|
131
|
+
text?.type === "text" && text.text ? text.text : "No result",
|
|
132
|
+
0,
|
|
133
|
+
0,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
const { docFiles } = details;
|
|
138
|
+
const fields: Array<
|
|
139
|
+
{ label: string; value: string; showCollapsed?: boolean } | Text
|
|
140
|
+
> = [];
|
|
141
|
+
|
|
142
|
+
if (options.expanded) {
|
|
143
|
+
// Expanded view: show full file list
|
|
144
|
+
const lines: string[] = [];
|
|
145
|
+
lines.push(theme.fg("accent", `${docFiles.length} markdown files:`), "");
|
|
146
|
+
for (const rel of docFiles) {
|
|
147
|
+
lines.push(theme.fg("dim", ` ${rel}`));
|
|
138
148
|
}
|
|
149
|
+
fields.push(new Text(lines.join("\n"), 0, 0));
|
|
150
|
+
} else {
|
|
151
|
+
// Collapsed view: show file count + expand hint
|
|
152
|
+
fields.push({
|
|
153
|
+
label: "Files",
|
|
154
|
+
value:
|
|
155
|
+
theme.fg("accent", `${docFiles.length} markdown files`) +
|
|
156
|
+
` (${keyHint("app.tools.expand", "to expand")})`,
|
|
157
|
+
showCollapsed: true,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
139
160
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
"",
|
|
151
|
-
);
|
|
152
|
-
for (const rel of docFiles) {
|
|
153
|
-
lines.push(theme.fg("dim", ` ${rel}`));
|
|
154
|
-
}
|
|
155
|
-
fields.push(new Text(lines.join("\n"), 0, 0));
|
|
156
|
-
} else {
|
|
157
|
-
// Collapsed view: show file count + expand hint
|
|
158
|
-
fields.push({
|
|
159
|
-
label: "Files",
|
|
160
|
-
value:
|
|
161
|
-
theme.fg("accent", `${docFiles.length} markdown files`) +
|
|
162
|
-
` (${keyHint("app.tools.expand", "to expand")})`,
|
|
163
|
-
showCollapsed: true,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
161
|
+
// Only show footer if there are items worth showing
|
|
162
|
+
const footer = new ToolFooter(theme, {
|
|
163
|
+
items: [
|
|
164
|
+
{
|
|
165
|
+
label: "docs",
|
|
166
|
+
value: String(docFiles.length),
|
|
167
|
+
tone: "accent",
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
});
|
|
166
171
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
{
|
|
171
|
-
label: "docs",
|
|
172
|
-
value: String(docFiles.length),
|
|
173
|
-
tone: "accent",
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
});
|
|
172
|
+
return new ToolBody({ fields, footer }, options, theme);
|
|
173
|
+
},
|
|
174
|
+
});
|
|
177
175
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
});
|
|
176
|
+
export function setupDocsTool(pi: ExtensionAPI) {
|
|
177
|
+
pi.registerTool(docsTool);
|
|
181
178
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { setupChangelogTool } from "./changelog-tool";
|
|
3
3
|
import { setupDocsTool } from "./docs-tool";
|
|
4
4
|
import { setupPackageManagerTool } from "./package-manager-tool";
|
|
@@ -10,3 +10,7 @@ export function setupTools(pi: ExtensionAPI) {
|
|
|
10
10
|
setupDocsTool(pi);
|
|
11
11
|
setupChangelogTool(pi);
|
|
12
12
|
}
|
|
13
|
+
|
|
14
|
+
export default function toolsExtension(pi: ExtensionAPI) {
|
|
15
|
+
setupTools(pi);
|
|
16
|
+
}
|
|
@@ -4,15 +4,18 @@ import { ToolCallHeader } from "@aliou/pi-utils-ui";
|
|
|
4
4
|
import type {
|
|
5
5
|
AgentToolResult,
|
|
6
6
|
ExtensionAPI,
|
|
7
|
-
ExtensionContext,
|
|
8
7
|
Theme,
|
|
9
|
-
|
|
10
|
-
} from "@
|
|
11
|
-
import { Text } from "@
|
|
12
|
-
import { Type } from "
|
|
8
|
+
} from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
11
|
+
import { type Static, Type } from "typebox";
|
|
13
12
|
|
|
14
13
|
const Params = Type.Object({});
|
|
15
|
-
type PackageManagerParams =
|
|
14
|
+
type PackageManagerParams = Static<typeof Params>;
|
|
15
|
+
|
|
16
|
+
function renderPackageManagerCall(_args: PackageManagerParams, theme: Theme) {
|
|
17
|
+
return new ToolCallHeader({ toolName: "Detect Package Manager" }, theme);
|
|
18
|
+
}
|
|
16
19
|
|
|
17
20
|
interface PackageManagerDetails {
|
|
18
21
|
packageManager?: string;
|
|
@@ -38,157 +41,159 @@ const COMMANDS: Record<string, { install: string; run: string }> = {
|
|
|
38
41
|
bun: { install: "bun install", run: "bun run" },
|
|
39
42
|
};
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
declaredVersion = match[2] || undefined;
|
|
89
|
-
}
|
|
44
|
+
const packageManagerTool = defineTool({
|
|
45
|
+
name: "detect_package_manager",
|
|
46
|
+
label: "Package Manager",
|
|
47
|
+
description:
|
|
48
|
+
"Detect the package manager used in the current project by checking lockfiles and package.json",
|
|
49
|
+
promptSnippet: "Detect the package manager for this project",
|
|
50
|
+
promptGuidelines: [
|
|
51
|
+
"Use detect_package_manager when you need to know which package manager (npm, yarn, pnpm, bun) the project uses",
|
|
52
|
+
"detect_package_manager is helpful before running install commands or scripts",
|
|
53
|
+
],
|
|
54
|
+
|
|
55
|
+
parameters: Params,
|
|
56
|
+
|
|
57
|
+
async execute(
|
|
58
|
+
_toolCallId,
|
|
59
|
+
_params,
|
|
60
|
+
_signal,
|
|
61
|
+
_onUpdate,
|
|
62
|
+
ctx,
|
|
63
|
+
): Promise<AgentToolResult<PackageManagerDetails>> {
|
|
64
|
+
const cwd = ctx.cwd;
|
|
65
|
+
|
|
66
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
67
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
68
|
+
throw new Error(`No package.json found in ${cwd}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Walk up from cwd to repo root, collecting packageManager and lockfile.
|
|
72
|
+
// Stop at .git boundary to avoid escaping the repository.
|
|
73
|
+
let declaredPm: string | undefined;
|
|
74
|
+
let declaredVersion: string | undefined;
|
|
75
|
+
let lockfile: string | undefined;
|
|
76
|
+
let lockfilePm: string | undefined;
|
|
77
|
+
|
|
78
|
+
let searchDir = cwd;
|
|
79
|
+
while (true) {
|
|
80
|
+
// Check packageManager field in package.json
|
|
81
|
+
if (!declaredPm) {
|
|
82
|
+
const pkgPath = path.join(searchDir, "package.json");
|
|
83
|
+
try {
|
|
84
|
+
if (fs.existsSync(pkgPath)) {
|
|
85
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
86
|
+
if (typeof pkg.packageManager === "string") {
|
|
87
|
+
const match = pkg.packageManager.match(/^([^@]+)@?(.*)?$/);
|
|
88
|
+
if (match) {
|
|
89
|
+
declaredPm = match[1];
|
|
90
|
+
declaredVersion = match[2] || undefined;
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
|
-
} catch {
|
|
93
|
-
// Ignore parse errors
|
|
94
93
|
}
|
|
94
|
+
} catch {
|
|
95
|
+
// Ignore parse errors
|
|
95
96
|
}
|
|
97
|
+
}
|
|
96
98
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
99
|
+
// Check lockfiles
|
|
100
|
+
if (!lockfilePm) {
|
|
101
|
+
for (const [filename, pm] of Object.entries(LOCKFILES)) {
|
|
102
|
+
if (fs.existsSync(path.join(searchDir, filename))) {
|
|
103
|
+
lockfilePm = pm;
|
|
104
|
+
lockfile = filename;
|
|
105
|
+
break;
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
|
-
|
|
108
|
-
// Stop if we found both, hit .git, or hit filesystem root
|
|
109
|
-
if (
|
|
110
|
-
(declaredPm && lockfilePm) ||
|
|
111
|
-
fs.existsSync(path.join(searchDir, ".git"))
|
|
112
|
-
) {
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
const parent = path.dirname(searchDir);
|
|
116
|
-
if (parent === searchDir) break;
|
|
117
|
-
searchDir = parent;
|
|
118
108
|
}
|
|
119
109
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (declaredVersion) {
|
|
127
|
-
parts.push(`Declared version: ${declaredVersion}`);
|
|
128
|
-
}
|
|
129
|
-
if (lockfile) {
|
|
130
|
-
parts.push(`Lockfile: ${lockfile}`);
|
|
131
|
-
}
|
|
132
|
-
if (!lockfile && !declaredPm) {
|
|
133
|
-
parts.push(
|
|
134
|
-
"No lockfile or packageManager field found, defaulting to npm",
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
parts.push(`Install: ${commands.install}`);
|
|
138
|
-
parts.push(`Run: ${commands.run}`);
|
|
139
|
-
|
|
140
|
-
const message = parts.join("\n");
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
content: [{ type: "text", text: message }],
|
|
144
|
-
details: {
|
|
145
|
-
packageManager: pm,
|
|
146
|
-
version: declaredVersion,
|
|
147
|
-
lockfile,
|
|
148
|
-
installCommand: commands.install,
|
|
149
|
-
runCommand: commands.run,
|
|
150
|
-
cwd,
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
},
|
|
154
|
-
|
|
155
|
-
renderCall(_args: PackageManagerParams, theme: Theme) {
|
|
156
|
-
return new ToolCallHeader({ toolName: "Detect Package Manager" }, theme);
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
renderResult(
|
|
160
|
-
result: AgentToolResult<PackageManagerDetails>,
|
|
161
|
-
_options: ToolRenderResultOptions,
|
|
162
|
-
theme: Theme,
|
|
163
|
-
): Text {
|
|
164
|
-
const { details } = result;
|
|
165
|
-
|
|
166
|
-
// Check for missing expected fields (framework passes {} on error)
|
|
167
|
-
if (!details?.packageManager) {
|
|
168
|
-
// Extract error message from result.content
|
|
169
|
-
const text = result.content[0];
|
|
170
|
-
const errorMessage =
|
|
171
|
-
text?.type === "text" && text.text
|
|
172
|
-
? text.text
|
|
173
|
-
: "Failed to detect package manager";
|
|
174
|
-
return new Text(theme.fg("error", errorMessage), 0, 0);
|
|
110
|
+
// Stop if we found both, hit .git, or hit filesystem root
|
|
111
|
+
if (
|
|
112
|
+
(declaredPm && lockfilePm) ||
|
|
113
|
+
fs.existsSync(path.join(searchDir, ".git"))
|
|
114
|
+
) {
|
|
115
|
+
break;
|
|
175
116
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
117
|
+
const parent = path.dirname(searchDir);
|
|
118
|
+
if (parent === searchDir) break;
|
|
119
|
+
searchDir = parent;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const pm = declaredPm || lockfilePm || "npm";
|
|
123
|
+
const fallback = { install: `${pm} install`, run: pm };
|
|
124
|
+
const commands = COMMANDS[pm] ?? fallback;
|
|
125
|
+
|
|
126
|
+
const parts: string[] = [];
|
|
127
|
+
parts.push(`Package manager: ${pm}`);
|
|
128
|
+
if (declaredVersion) {
|
|
129
|
+
parts.push(`Declared version: ${declaredVersion}`);
|
|
130
|
+
}
|
|
131
|
+
if (lockfile) {
|
|
132
|
+
parts.push(`Lockfile: ${lockfile}`);
|
|
133
|
+
}
|
|
134
|
+
if (!lockfile && !declaredPm) {
|
|
135
|
+
parts.push(
|
|
136
|
+
"No lockfile or packageManager field found, defaulting to npm",
|
|
183
137
|
);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
138
|
+
}
|
|
139
|
+
parts.push(`Install: ${commands.install}`);
|
|
140
|
+
parts.push(`Run: ${commands.run}`);
|
|
141
|
+
|
|
142
|
+
const message = parts.join("\n");
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
content: [{ type: "text", text: message }],
|
|
146
|
+
details: {
|
|
147
|
+
packageManager: pm,
|
|
148
|
+
version: declaredVersion,
|
|
149
|
+
lockfile,
|
|
150
|
+
installCommand: commands.install,
|
|
151
|
+
runCommand: commands.run,
|
|
152
|
+
cwd,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
renderCall(args, theme) {
|
|
158
|
+
return renderPackageManagerCall(args, theme);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
renderResult(result, options, theme) {
|
|
162
|
+
if (options.isPartial) {
|
|
163
|
+
return new Text(theme.fg("dim", "Package Manager: detecting..."), 0, 0);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const { details } = result;
|
|
167
|
+
|
|
168
|
+
// Check for missing expected fields (framework passes {} on error)
|
|
169
|
+
if (!details?.packageManager) {
|
|
170
|
+
// Extract error message from result.content
|
|
171
|
+
const text = result.content[0];
|
|
172
|
+
const errorMessage =
|
|
173
|
+
text?.type === "text" && text.text
|
|
174
|
+
? text.text
|
|
175
|
+
: "Failed to detect package manager";
|
|
176
|
+
return new Text(theme.fg("error", errorMessage), 0, 0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const lines: string[] = [];
|
|
180
|
+
lines.push(
|
|
181
|
+
theme.fg(
|
|
182
|
+
"success",
|
|
183
|
+
`Package manager: ${theme.bold(details.packageManager)}`,
|
|
184
|
+
),
|
|
185
|
+
);
|
|
186
|
+
if (details.version) {
|
|
187
|
+
lines.push(theme.fg("dim", `Version: ${details.version}`));
|
|
188
|
+
}
|
|
189
|
+
if (details.lockfile) {
|
|
190
|
+
lines.push(theme.fg("dim", `Lockfile: ${details.lockfile}`));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return new Text(lines.join("\n"), 0, 0);
|
|
194
|
+
},
|
|
195
|
+
});
|
|
190
196
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
197
|
+
export function setupPackageManagerTool(pi: ExtensionAPI) {
|
|
198
|
+
pi.registerTool(packageManagerTool);
|
|
194
199
|
}
|