mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(agent): handle Windows resource paths
This commit is contained in:
parent
7830975233
commit
6a140371d6
4 changed files with 30 additions and 31 deletions
|
|
@ -13,6 +13,10 @@
|
|||
- Added bounded `Session.findEntriesOnBranch()` and `findEntryOnBranch()` queries with explicit traversal, filtering, ordering, and limit options.
|
||||
- Added a compile-complete `AgentHarness` v2 scaffold; unfinished operation paths reject with `HarnessNotImplemented` while durable execution is implemented.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Windows path handling for `NodeExecutionEnv` file basenames, recursive skill loading, and prompt template names.
|
||||
|
||||
## [0.83.0] - 2026-07-29
|
||||
|
||||
## [0.82.1] - 2026-07-25
|
||||
|
|
|
|||
4
packages/agent/src/harness/env/nodejs.ts
vendored
4
packages/agent/src/harness/env/nodejs.ts
vendored
|
|
@ -14,7 +14,7 @@ import {
|
|||
writeFile,
|
||||
} from "node:fs/promises";
|
||||
import { homedir, tmpdir } from "node:os";
|
||||
import { isAbsolute, join, resolve } from "node:path";
|
||||
import { basename, isAbsolute, join, resolve } from "node:path";
|
||||
import { createInterface } from "node:readline";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import {
|
||||
|
|
@ -81,7 +81,7 @@ function fileInfoFromStats(
|
|||
const kind = fileKindFromStats(stats);
|
||||
if (!kind) return err(new FileError("invalid", "Unsupported file type", path));
|
||||
return ok({
|
||||
name: path.replace(/\/+$/, "").split("/").pop() ?? path,
|
||||
name: basename(path),
|
||||
path,
|
||||
kind,
|
||||
size: stats.size,
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export async function loadPromptTemplates(
|
|||
promptTemplates.push(...result.promptTemplates);
|
||||
diagnostics.push(...result.diagnostics);
|
||||
} else if (kind === "file" && info.name.endsWith(".md")) {
|
||||
const result = await loadTemplateFromFile(env, info.path);
|
||||
const result = await loadTemplateFromFile(env, info.path, info.name);
|
||||
if (result.promptTemplate) promptTemplates.push(result.promptTemplate);
|
||||
diagnostics.push(...result.diagnostics);
|
||||
}
|
||||
|
|
@ -113,7 +113,7 @@ async function loadTemplatesFromDir(
|
|||
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
||||
const kind = await resolveKind(env, entry, diagnostics);
|
||||
if (kind !== "file" || !entry.name.endsWith(".md")) continue;
|
||||
const result = await loadTemplateFromFile(env, entry.path);
|
||||
const result = await loadTemplateFromFile(env, entry.path, entry.name);
|
||||
if (result.promptTemplate) promptTemplates.push(result.promptTemplate);
|
||||
diagnostics.push(...result.diagnostics);
|
||||
}
|
||||
|
|
@ -123,6 +123,7 @@ async function loadTemplatesFromDir(
|
|||
async function loadTemplateFromFile(
|
||||
env: ExecutionEnv,
|
||||
filePath: string,
|
||||
fileName: string,
|
||||
): Promise<{ promptTemplate: PromptTemplate | null; diagnostics: PromptTemplateDiagnostic[] }> {
|
||||
const diagnostics: PromptTemplateDiagnostic[] = [];
|
||||
const rawContent = await env.readTextFile(filePath);
|
||||
|
|
@ -156,7 +157,7 @@ async function loadTemplateFromFile(
|
|||
}
|
||||
return {
|
||||
promptTemplate: {
|
||||
name: basenameEnvPath(filePath).replace(/\.md$/i, ""),
|
||||
name: fileName.replace(/\.md$/i, ""),
|
||||
description,
|
||||
content: body,
|
||||
},
|
||||
|
|
@ -213,12 +214,6 @@ function parseFrontmatter<T extends Record<string, unknown>>(
|
|||
}
|
||||
}
|
||||
|
||||
function basenameEnvPath(path: string): string {
|
||||
const normalized = path.replace(/\/+$/, "");
|
||||
const slashIndex = normalized.lastIndexOf("/");
|
||||
return slashIndex === -1 ? normalized : normalized.slice(slashIndex + 1);
|
||||
}
|
||||
|
||||
/** Parse an argument string using simple shell-style single and double quotes. */
|
||||
export function parseCommandArgs(argsString: string): string[] {
|
||||
const args: string[] = [];
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ async function loadSkillsFromDirInternal(
|
|||
const relPath = relativeEnvPath(rootDir, fullPath);
|
||||
if (ignoreMatcher.ignores(relPath)) continue;
|
||||
|
||||
const result = await loadSkillFromFile(env, fullPath);
|
||||
const result = await loadSkillFromFile(env, fullPath, dirInfo.name);
|
||||
if (result.skill) skills.push(result.skill);
|
||||
diagnostics.push(...result.diagnostics);
|
||||
return { skills, diagnostics };
|
||||
|
|
@ -166,7 +166,7 @@ async function loadSkillsFromDirInternal(
|
|||
}
|
||||
|
||||
if (kind !== "file" || !includeRootFiles || !entry.name.endsWith(".md")) continue;
|
||||
const result = await loadSkillFromFile(env, fullPath);
|
||||
const result = await loadSkillFromFile(env, fullPath, dirInfo.name);
|
||||
if (result.skill) skills.push(result.skill);
|
||||
diagnostics.push(...result.diagnostics);
|
||||
}
|
||||
|
|
@ -185,7 +185,17 @@ async function addIgnoreRules(
|
|||
const prefix = relativeDir ? `${relativeDir}/` : "";
|
||||
|
||||
for (const filename of IGNORE_FILE_NAMES) {
|
||||
const ignorePath = joinEnvPath(dir, filename);
|
||||
const ignorePathResult = await env.joinPath([dir, filename]);
|
||||
if (!ignorePathResult.ok) {
|
||||
diagnostics.push({
|
||||
type: "warning",
|
||||
code: "file_info_failed",
|
||||
message: ignorePathResult.error.message,
|
||||
path: dir,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const ignorePath = ignorePathResult.value;
|
||||
const info = await env.fileInfo(ignorePath);
|
||||
if (!info.ok) {
|
||||
if (info.error.code !== "not_found") {
|
||||
|
|
@ -233,6 +243,7 @@ function prefixIgnorePattern(line: string, prefix: string): string | null {
|
|||
async function loadSkillFromFile(
|
||||
env: ExecutionEnv,
|
||||
filePath: string,
|
||||
parentDirName: string,
|
||||
): Promise<{ skill: Skill | null; diagnostics: SkillDiagnostic[] }> {
|
||||
const diagnostics: SkillDiagnostic[] = [];
|
||||
const rawContent = await env.readTextFile(filePath);
|
||||
|
|
@ -248,8 +259,6 @@ async function loadSkillFromFile(
|
|||
}
|
||||
|
||||
const { frontmatter, body } = parsed.value;
|
||||
const skillDir = dirnameEnvPath(filePath);
|
||||
const parentDirName = basenameEnvPath(skillDir);
|
||||
const description = typeof frontmatter.description === "string" ? frontmatter.description : undefined;
|
||||
|
||||
for (const error of validateDescription(description)) {
|
||||
|
|
@ -349,25 +358,16 @@ async function resolveKind(
|
|||
return target.value.kind === "file" || target.value.kind === "directory" ? target.value.kind : undefined;
|
||||
}
|
||||
|
||||
function joinEnvPath(base: string, child: string): string {
|
||||
return `${base.replace(/\/+$/, "")}/${child.replace(/^\/+/, "")}`;
|
||||
}
|
||||
|
||||
function dirnameEnvPath(path: string): string {
|
||||
const normalized = path.replace(/\/+$/, "");
|
||||
const slashIndex = normalized.lastIndexOf("/");
|
||||
return slashIndex <= 0 ? "/" : normalized.slice(0, slashIndex);
|
||||
}
|
||||
|
||||
function basenameEnvPath(path: string): string {
|
||||
const normalized = path.replace(/\/+$/, "");
|
||||
const slashIndex = normalized.lastIndexOf("/");
|
||||
return slashIndex === -1 ? normalized : normalized.slice(slashIndex + 1);
|
||||
const normalized = path.replace(/[\\/]+$/, "");
|
||||
const separatorIndex = Math.max(normalized.lastIndexOf("/"), normalized.lastIndexOf("\\"));
|
||||
if (separatorIndex === 2 && normalized[1] === ":") return normalized.slice(0, 3);
|
||||
return separatorIndex <= 0 ? "/" : normalized.slice(0, separatorIndex);
|
||||
}
|
||||
|
||||
function relativeEnvPath(root: string, path: string): string {
|
||||
const normalizedRoot = root.replace(/\/+$/, "");
|
||||
const normalizedPath = path.replace(/\/+$/, "");
|
||||
const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "");
|
||||
const normalizedPath = path.replace(/\\/g, "/").replace(/\/+$/, "");
|
||||
if (normalizedPath === normalizedRoot) return "";
|
||||
return normalizedPath.startsWith(`${normalizedRoot}/`)
|
||||
? normalizedPath.slice(normalizedRoot.length + 1)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue