refactor: restructured chunk tree initialization and extraction logic for clarity
- Refactored chunk tree initialization to use explicit prologue/epilogue byte boundaries instead of None values. - Extracted chunk resolution logic to use split_selector_crc_and_region() helper for improved code reuse. - Reorganized package.json exports across multiple modules (autoresearch, cli, dap, edit, modes) for better API surface clarity. - Updated test expectations for Go receiver method rendering and chunk selector formats to match refactored output. - Consolidated notebook conversion logic and removed unused _createErrorToolResult helper function. - Improved leading trivia preservation in chunk edits by detecting and maintaining line prefix whitespace.
This commit is contained in:
@@ -406,8 +406,8 @@ pub fn build_notebook_tree_from_virtual(
|
||||
start_byte: 0,
|
||||
end_byte: virtual_source.len() as u32,
|
||||
checksum_start_byte: 0,
|
||||
prologue_end_byte: None,
|
||||
epilogue_start_byte: None,
|
||||
prologue_end_byte: Some(0),
|
||||
epilogue_start_byte: Some(virtual_source.len() as u32),
|
||||
checksum: root_checksum.clone(),
|
||||
error: false,
|
||||
indent: 0,
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::chunk::{
|
||||
},
|
||||
resolve::{
|
||||
chunk_region_range, chunk_supports_region, resolve_chunk_selector, resolve_chunk_with_crc,
|
||||
sanitize_chunk_selector, sanitize_crc,
|
||||
sanitize_chunk_selector, sanitize_crc, split_selector_crc_and_region,
|
||||
},
|
||||
state::{ChunkState, ChunkStateInner},
|
||||
types::{
|
||||
@@ -284,10 +284,18 @@ fn resolve_edit_target(
|
||||
None
|
||||
}
|
||||
});
|
||||
let batch_auto_accepted = ensure_batch_operation_target_current(scheduled, crc, touched_paths);
|
||||
let resolve_crc = if batch_auto_accepted { None } else { crc };
|
||||
let resolved = resolve_chunk_with_crc(state, selector, resolve_crc, warnings)?;
|
||||
let region = operation.region.unwrap_or(resolved.region);
|
||||
let (cleaned_selector, cleaned_crc, parsed_region) =
|
||||
split_selector_crc_and_region(selector, crc, operation.region)?;
|
||||
let batch_auto_accepted =
|
||||
ensure_batch_operation_target_current(scheduled, cleaned_crc.as_deref(), touched_paths);
|
||||
let resolve_crc = if batch_auto_accepted {
|
||||
None
|
||||
} else {
|
||||
cleaned_crc.as_deref()
|
||||
};
|
||||
let resolved =
|
||||
resolve_chunk_with_crc(state, cleaned_selector.as_deref(), resolve_crc, warnings)?;
|
||||
let region = operation.region.unwrap_or(parsed_region);
|
||||
if !batch_auto_accepted {
|
||||
validate_batch_crc(resolved.chunk, resolved.crc.as_deref(), requires_checksum)?;
|
||||
}
|
||||
@@ -696,16 +704,32 @@ fn preserve_attached_leading_trivia(
|
||||
return replacement.to_owned();
|
||||
}
|
||||
|
||||
let leading_trivia = &state.source[trivia_start..trivia_end];
|
||||
let line_start = state.source[..trivia_start]
|
||||
.rfind('\n')
|
||||
.map_or(0, |pos| pos + 1);
|
||||
let line_prefix = &state.source[line_start..trivia_start];
|
||||
let mut leading_trivia =
|
||||
if !line_prefix.is_empty() && line_prefix.chars().all(|ch| matches!(ch, ' ' | '\t')) {
|
||||
format!("{line_prefix}{}", &state.source[trivia_start..trivia_end])
|
||||
} else {
|
||||
state.source[trivia_start..trivia_end].to_owned()
|
||||
};
|
||||
if let Some(last_newline) = leading_trivia.rfind('\n')
|
||||
&& leading_trivia[last_newline + 1..]
|
||||
.chars()
|
||||
.all(|ch| matches!(ch, ' ' | '\t' | '\r'))
|
||||
{
|
||||
leading_trivia.truncate(last_newline + 1);
|
||||
}
|
||||
if leading_trivia.trim().is_empty()
|
||||
|| replacement.starts_with(leading_trivia)
|
||||
|| replacement_supplies_leading_trivia(leading_trivia, replacement)
|
||||
|| replacement.starts_with(&leading_trivia)
|
||||
|| replacement_supplies_leading_trivia(&leading_trivia, replacement)
|
||||
{
|
||||
return replacement.to_owned();
|
||||
}
|
||||
|
||||
let mut combined = String::with_capacity(leading_trivia.len() + replacement.len());
|
||||
combined.push_str(leading_trivia);
|
||||
combined.push_str(&leading_trivia);
|
||||
combined.push_str(replacement);
|
||||
combined
|
||||
}
|
||||
|
||||
@@ -136,8 +136,8 @@ pub(crate) fn build_chunk_tree(source: &str, language: &str) -> Result<ChunkTree
|
||||
start_byte: 0,
|
||||
end_byte: source.len() as u32,
|
||||
checksum_start_byte: 0,
|
||||
prologue_end_byte: None,
|
||||
epilogue_start_byte: None,
|
||||
prologue_end_byte: Some(0),
|
||||
epilogue_start_byte: Some(source.len() as u32),
|
||||
checksum: root_checksum.clone(),
|
||||
error: false,
|
||||
indent: 0,
|
||||
@@ -213,8 +213,8 @@ fn build_blank_line_tree(
|
||||
start_byte: 0,
|
||||
end_byte: source.len() as u32,
|
||||
checksum_start_byte: 0,
|
||||
prologue_end_byte: None,
|
||||
epilogue_start_byte: None,
|
||||
prologue_end_byte: Some(0),
|
||||
epilogue_start_byte: Some(source.len() as u32),
|
||||
checksum: checksum.clone(),
|
||||
error: false,
|
||||
indent: 0,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Simplified chunk edit operations: removed `append_child`, `prepend_child`, `append_sibling`, `prepend_sibling`, and `replace_body` ops in favor of unified `replace`, `before`, `after`, `prepend`, and `append` with region targeting (`@container`, `@prologue`, `@body`, `@epilogue`)
|
||||
@@ -10,6 +9,16 @@
|
||||
|
||||
### Added
|
||||
|
||||
- Exported autoresearch module and submodules via `./autoresearch` and `./autoresearch/*` package paths
|
||||
- Exported autoresearch tools via `./autoresearch/tools/*` package path
|
||||
- Exported CLI commands via `./cli/commands/*` package path
|
||||
- Exported DAP module and submodules via `./dap` and `./dap/*` package paths
|
||||
- Exported edit module and submodules via `./edit`, `./edit/*`, and `./edit/modes/*` package paths
|
||||
- Exported bundled ci-green custom command via `./extensibility/custom-commands/bundled/ci-green` package path
|
||||
- Exported extensibility plugins marketplace via `./extensibility/plugins/marketplace` and `./extensibility/plugins/marketplace/*` package paths
|
||||
- Exported ACP mode via `./modes/acp` and `./modes/acp/*` package paths
|
||||
- Exported web utilities via `./web/*` package path
|
||||
- Exported line-hash utilities from edit module via `./edit/line-hash`
|
||||
- Host-owned custom tools support: RPC clients can now register custom tools via `setCustomTools()` and the RPC server will invoke them over the transport with `host_tool_call` requests
|
||||
- RPC host tool framework: `RpcHostToolBridge` for managing host tool execution, `RpcHostToolDefinition` for tool metadata, and bidirectional `host_tool_call`, `host_tool_cancel`, `host_tool_update`, and `host_tool_result` frames
|
||||
- RPC client tool API: `defineRpcClientTool()` helper, `RpcClientCustomTool` interface, and `RpcClientToolContext` for implementing host-side tool execution with update streaming and abort support
|
||||
@@ -46,6 +55,9 @@
|
||||
|
||||
### Changed
|
||||
|
||||
- Reorganized package.json exports: moved `./edit` exports before `./plan-mode` for better logical grouping
|
||||
- Notebook conversion logic now checks for raw read mode or non-chunk mode before converting via markit, allowing chunk-mode reads of `.ipynb` files to use chunk parsing instead of conversion
|
||||
- Go receiver methods now render as top-level siblings instead of nested under their receiver type in chunk read output
|
||||
- Moved prompt formatting and rendering utilities from `coding-agent` to `pi-utils` package; `renderPromptTemplate()` and `formatPromptContent()` now accessed via `prompt.render()` and `prompt.format()` from `@oh-my-pi/pi-utils`
|
||||
- Moved `parseFrontmatter()` utility from `coding-agent` to `pi-utils` package; now imported from `@oh-my-pi/pi-utils` instead of local utils
|
||||
- Consolidated prompt template handling: `TemplateContext` type now available as `prompt.TemplateContext` from `@oh-my-pi/pi-utils`
|
||||
@@ -97,6 +109,10 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Removed unused `_createErrorToolResult` helper function from RPC host-tools module
|
||||
- Fixed Go receiver method indentation in append operations to preserve relative indentation from the anchor chunk
|
||||
- Fixed Go type chunk line counts to report only the type body lines instead of including grouped receiver methods
|
||||
- Fixed enum variant insertion to avoid adding extra blank lines between variants
|
||||
- Chunk read output now correctly preserves embedded CRC in selectors (e.g., `class_Foo.fn_bar#ZZPM`) instead of stripping them during path parsing
|
||||
- Chunk edit error messages now consistently report checksum mismatches with format `Checksum mismatch` instead of variable phrasing
|
||||
- Chunk-mode read output now correctly displays scoped response trees showing only touched chunks and adjacent siblings, preventing unrelated distant chunks from appearing in responses
|
||||
|
||||
@@ -91,6 +91,18 @@
|
||||
"types": "./src/async/*.ts",
|
||||
"import": "./src/async/*.ts"
|
||||
},
|
||||
"./autoresearch": {
|
||||
"types": "./src/autoresearch/index.ts",
|
||||
"import": "./src/autoresearch/index.ts"
|
||||
},
|
||||
"./autoresearch/*": {
|
||||
"types": "./src/autoresearch/*.ts",
|
||||
"import": "./src/autoresearch/*.ts"
|
||||
},
|
||||
"./autoresearch/tools/*": {
|
||||
"types": "./src/autoresearch/tools/*.ts",
|
||||
"import": "./src/autoresearch/tools/*.ts"
|
||||
},
|
||||
"./capability": {
|
||||
"types": "./src/capability/index.ts",
|
||||
"import": "./src/capability/index.ts"
|
||||
@@ -103,6 +115,10 @@
|
||||
"types": "./src/cli/*.ts",
|
||||
"import": "./src/cli/*.ts"
|
||||
},
|
||||
"./cli/commands/*": {
|
||||
"types": "./src/cli/commands/*.ts",
|
||||
"import": "./src/cli/commands/*.ts"
|
||||
},
|
||||
"./commands/*": {
|
||||
"types": "./src/commands/*.ts",
|
||||
"import": "./src/commands/*.ts"
|
||||
@@ -167,6 +183,14 @@
|
||||
"types": "./src/config/*.ts",
|
||||
"import": "./src/config/*.ts"
|
||||
},
|
||||
"./dap": {
|
||||
"types": "./src/dap/index.ts",
|
||||
"import": "./src/dap/index.ts"
|
||||
},
|
||||
"./dap/*": {
|
||||
"types": "./src/dap/*.ts",
|
||||
"import": "./src/dap/*.ts"
|
||||
},
|
||||
"./debug": {
|
||||
"types": "./src/debug/index.ts",
|
||||
"import": "./src/debug/index.ts"
|
||||
@@ -183,6 +207,18 @@
|
||||
"types": "./src/discovery/*.ts",
|
||||
"import": "./src/discovery/*.ts"
|
||||
},
|
||||
"./edit": {
|
||||
"types": "./src/edit/index.ts",
|
||||
"import": "./src/edit/index.ts"
|
||||
},
|
||||
"./edit/*": {
|
||||
"types": "./src/edit/*.ts",
|
||||
"import": "./src/edit/*.ts"
|
||||
},
|
||||
"./edit/modes/*": {
|
||||
"types": "./src/edit/modes/*.ts",
|
||||
"import": "./src/edit/modes/*.ts"
|
||||
},
|
||||
"./exa": {
|
||||
"types": "./src/exa/index.ts",
|
||||
"import": "./src/exa/index.ts"
|
||||
@@ -219,6 +255,10 @@
|
||||
"types": "./src/extensibility/custom-commands/*.ts",
|
||||
"import": "./src/extensibility/custom-commands/*.ts"
|
||||
},
|
||||
"./extensibility/custom-commands/bundled/ci-green": {
|
||||
"types": "./src/extensibility/custom-commands/bundled/ci-green/index.ts",
|
||||
"import": "./src/extensibility/custom-commands/bundled/ci-green/index.ts"
|
||||
},
|
||||
"./extensibility/custom-commands/bundled/review": {
|
||||
"types": "./src/extensibility/custom-commands/bundled/review/index.ts",
|
||||
"import": "./src/extensibility/custom-commands/bundled/review/index.ts"
|
||||
@@ -255,6 +295,14 @@
|
||||
"types": "./src/extensibility/plugins/*.ts",
|
||||
"import": "./src/extensibility/plugins/*.ts"
|
||||
},
|
||||
"./extensibility/plugins/marketplace": {
|
||||
"types": "./src/extensibility/plugins/marketplace/index.ts",
|
||||
"import": "./src/extensibility/plugins/marketplace/index.ts"
|
||||
},
|
||||
"./extensibility/plugins/marketplace/*": {
|
||||
"types": "./src/extensibility/plugins/marketplace/*.ts",
|
||||
"import": "./src/extensibility/plugins/marketplace/*.ts"
|
||||
},
|
||||
"./internal-urls": {
|
||||
"types": "./src/internal-urls/index.ts",
|
||||
"import": "./src/internal-urls/index.ts"
|
||||
@@ -315,6 +363,14 @@
|
||||
"types": "./src/modes/*.ts",
|
||||
"import": "./src/modes/*.ts"
|
||||
},
|
||||
"./modes/acp": {
|
||||
"types": "./src/modes/acp/index.ts",
|
||||
"import": "./src/modes/acp/index.ts"
|
||||
},
|
||||
"./modes/acp/*": {
|
||||
"types": "./src/modes/acp/*.ts",
|
||||
"import": "./src/modes/acp/*.ts"
|
||||
},
|
||||
"./modes/components": {
|
||||
"types": "./src/modes/components/index.ts",
|
||||
"import": "./src/modes/components/index.ts"
|
||||
@@ -359,18 +415,6 @@
|
||||
"types": "./src/modes/utils/*.ts",
|
||||
"import": "./src/modes/utils/*.ts"
|
||||
},
|
||||
"./edit": {
|
||||
"types": "./src/edit/index.ts",
|
||||
"import": "./src/edit/index.ts"
|
||||
},
|
||||
"./edit/*": {
|
||||
"types": "./src/edit/*.ts",
|
||||
"import": "./src/edit/*.ts"
|
||||
},
|
||||
"./edit/modes/*": {
|
||||
"types": "./src/edit/modes/*.ts",
|
||||
"import": "./src/edit/modes/*.ts"
|
||||
},
|
||||
"./plan-mode/*": {
|
||||
"types": "./src/plan-mode/*.ts",
|
||||
"import": "./src/plan-mode/*.ts"
|
||||
@@ -440,6 +484,10 @@
|
||||
"types": "./src/utils/*.ts",
|
||||
"import": "./src/utils/*.ts"
|
||||
},
|
||||
"./web/*": {
|
||||
"types": "./src/web/*.ts",
|
||||
"import": "./src/web/*.ts"
|
||||
},
|
||||
"./web/scrapers": {
|
||||
"types": "./src/web/scrapers/index.ts",
|
||||
"import": "./src/web/scrapers/index.ts"
|
||||
|
||||
@@ -27,6 +27,7 @@ import { type EditToolDetails, getLspBatchRequest, type LspBatchRequest } from "
|
||||
|
||||
export { DEFAULT_EDIT_MODE, type EditMode, normalizeEditMode } from "../utils/edit-mode";
|
||||
export * from "./diff";
|
||||
export * from "./line-hash";
|
||||
export * from "./modes/chunk";
|
||||
export * from "./modes/hashline";
|
||||
export * from "./modes/patch";
|
||||
|
||||
@@ -19,13 +19,6 @@ type PendingHostToolCall = {
|
||||
onUpdate?: AgentToolUpdateCallback<unknown>;
|
||||
};
|
||||
|
||||
function _createErrorToolResult(message: string): AgentToolResult<unknown> {
|
||||
return {
|
||||
content: [{ type: "text", text: message }],
|
||||
details: {},
|
||||
};
|
||||
}
|
||||
|
||||
function isAgentToolResult(value: unknown): value is AgentToolResult<unknown> {
|
||||
if (!value || typeof value !== "object") return false;
|
||||
const content = (value as { content?: unknown }).content;
|
||||
|
||||
@@ -818,6 +818,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
||||
const language = getLanguageFromPath(absolutePath);
|
||||
const skipChunksForExplore = !hasEditTool && !this.session.settings.get("read.explorechunks");
|
||||
const skipChunksForProse = isProseLanguage(language) && !this.session.settings.get("read.prosechunks");
|
||||
const shouldConvertWithMarkit =
|
||||
CONVERTIBLE_EXTENSIONS.has(ext) || (ext === ".ipynb" && (parsed.kind === "raw" || !chunkMode));
|
||||
|
||||
if (chunkMode && parsed.kind !== "raw" && !skipChunksForExplore && !skipChunksForProse) {
|
||||
const absoluteLineRange =
|
||||
@@ -928,7 +930,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} else if (CONVERTIBLE_EXTENSIONS.has(ext)) {
|
||||
} else if (shouldConvertWithMarkit) {
|
||||
// Convert document or notebook via markit.
|
||||
const result = await convertFileWithMarkit(absolutePath, signal);
|
||||
if (result.ok) {
|
||||
|
||||
@@ -397,7 +397,7 @@ type Server struct {
|
||||
expect(result.diffSourceAfter).not.toMatch(/Addr string\n[ \t]+func \(s \*Server\) Ping/);
|
||||
});
|
||||
|
||||
test("append on Go type_Server container keeps receiver method body indentation relative to column 0", () => {
|
||||
test("append on Go type_Server container keeps receiver method body indentation relative to the anchor chunk", () => {
|
||||
const source = `package main
|
||||
|
||||
type Server struct {
|
||||
@@ -419,9 +419,9 @@ type Server struct {
|
||||
|
||||
expect(result.parseValid).toBe(true);
|
||||
expect(result.diffSourceAfter).toContain(
|
||||
"\nfunc (s *Server) LogCount() int {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\treturn 0\n}\n",
|
||||
"\n\tfunc (s *Server) LogCount() int {\n\t\ts.mu.Lock()\n\t\tdefer s.mu.Unlock()\n\t\treturn 0\n\t}\n",
|
||||
);
|
||||
expect(result.diffSourceAfter).not.toContain("\n\tfunc (s *Server) LogCount() int {");
|
||||
expect(result.diffSourceAfter).not.toContain("\nfunc (s *Server) LogCount() int {");
|
||||
});
|
||||
test("keeps append separated from the closing delimiter when adding the last child", () => {
|
||||
const result = edit([
|
||||
@@ -728,7 +728,7 @@ describe("formatChunkedRead", () => {
|
||||
expect(result.text).toMatch(new RegExp(`with-tail\\.ts·${totalLines}L`));
|
||||
});
|
||||
|
||||
test("leaf read shows absolute file lines and raw source indentation", async () => {
|
||||
test("leaf read shows absolute file lines and canonical tab indentation", async () => {
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
|
||||
const filePath = path.join(tmpDir, "worker.ts");
|
||||
await Bun.write(filePath, testSource);
|
||||
@@ -740,12 +740,10 @@ describe("formatChunkedRead", () => {
|
||||
language: "typescript",
|
||||
});
|
||||
|
||||
expect(result.text).toContain("worker.ts:class_Worker.fn_run·");
|
||||
expect(result.text).toContain("worker.ts:class_Worker.fn_run@container·");
|
||||
expect(result.text).toContain("[class_Worker.fn_run#");
|
||||
expect(result.text).toContain("run(): void {");
|
||||
expect(result.text).toContain("6| ");
|
||||
expect(result.text).toContain("run(): void {");
|
||||
expect(result.text).toContain("7| ");
|
||||
expect(result.text).toContain("6| \trun(): void {");
|
||||
expect(result.text).toContain("7| \t\tconsole.log(this.name);");
|
||||
expect(result.text).toContain("console.log(this.name);");
|
||||
});
|
||||
|
||||
@@ -763,8 +761,9 @@ describe("formatChunkedRead", () => {
|
||||
});
|
||||
|
||||
expect(result.text).not.toContain("to expand ⋮");
|
||||
expect(result.text).toContain("3| step(0);");
|
||||
expect(result.text).toContain("27| step(24);");
|
||||
expect(result.text).toContain("service.ts:class_Service.fn_handle@container·");
|
||||
expect(result.text).toContain("3| \t\tstep(0);");
|
||||
expect(result.text).toContain("27| \t\tstep(24);");
|
||||
expect(result.text).toContain("done();");
|
||||
});
|
||||
});
|
||||
@@ -873,10 +872,10 @@ describe("addressable member rendering", () => {
|
||||
|
||||
expect(result.text).toContain("[type_Handler#");
|
||||
expect(result.text).toContain("3| type Handler interface {");
|
||||
expect(result.text).toContain("4| Handle(method, path string) Result");
|
||||
expect(result.text).toContain("4| \tHandle(method, path string) Result");
|
||||
});
|
||||
|
||||
test("renders Go receiver methods beneath their receiver type", async () => {
|
||||
test("renders Go receiver methods as top-level siblings", async () => {
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
|
||||
const filePath = path.join(tmpDir, "server.go");
|
||||
await Bun.write(
|
||||
@@ -893,11 +892,13 @@ describe("addressable member rendering", () => {
|
||||
|
||||
expect(result.text).toContain("[type_Server#");
|
||||
expect(result.text).toContain("[type_Server.field_Addr#");
|
||||
expect(result.text).toContain("[type_Server.fn_Start#");
|
||||
expect(result.text).toContain("[type_Server.fn_Stop#");
|
||||
expect(result.text).toContain("[fn_Start#");
|
||||
expect(result.text).toContain("[fn_Stop#");
|
||||
expect(result.text).not.toContain("[type_Server.fn_Start#");
|
||||
expect(result.text).not.toContain("[type_Server.fn_Stop#");
|
||||
});
|
||||
|
||||
test("line range filter shows receiver methods under a type even when the range skips the type header", async () => {
|
||||
test("line range filter shows top-level receiver methods even when the range skips the type header", async () => {
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
|
||||
const filePath = path.join(tmpDir, "server.go");
|
||||
await Bun.write(
|
||||
@@ -913,8 +914,9 @@ describe("addressable member rendering", () => {
|
||||
});
|
||||
|
||||
expect(result.text).toContain("L7-L8");
|
||||
expect(result.text).toContain("[type_Server.fn_Start#");
|
||||
expect(result.text).toContain("[type_Server.fn_Stop#");
|
||||
expect(result.text).toContain("[fn_Start#");
|
||||
expect(result.text).toContain("[fn_Stop#");
|
||||
expect(result.text).not.toContain("[type_Server.fn_Start#");
|
||||
});
|
||||
|
||||
test("renders trivial TypeScript enum variants as addressable children", async () => {
|
||||
@@ -950,8 +952,8 @@ describe("addressable member rendering", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("grouped Go receiver chunk headers", () => {
|
||||
test("reports grouped receiver chunk line counts from rendered lines", async () => {
|
||||
describe("Go type chunk headers", () => {
|
||||
test("reports Go type chunk line counts from the type body only", async () => {
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
|
||||
const filePath = path.join(tmpDir, "server.go");
|
||||
await Bun.write(
|
||||
@@ -966,9 +968,9 @@ describe("grouped Go receiver chunk headers", () => {
|
||||
language: "go",
|
||||
});
|
||||
|
||||
expect(result.text).toContain("server.go:type_Server·6L");
|
||||
expect(result.text).toContain("[type_Server.fn_Start#");
|
||||
expect(result.text).toContain("[type_Server.fn_Stop#");
|
||||
expect(result.text).toContain("server.go:type_Server@container·3L");
|
||||
expect(result.text).not.toContain("[fn_Start#");
|
||||
expect(result.text).not.toContain("[fn_Stop#");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -992,7 +994,7 @@ describe("addressable member editing", () => {
|
||||
expect(result.diffSourceAfter).not.toContain('Busy = "busy"');
|
||||
});
|
||||
|
||||
test("after inserts beside an individually addressable enum variant", () => {
|
||||
test("after inserts beside an individually addressable enum variant without extra blank lines", () => {
|
||||
const result = edit(
|
||||
[
|
||||
{
|
||||
@@ -1004,7 +1006,7 @@ describe("addressable member editing", () => {
|
||||
enumSource,
|
||||
);
|
||||
|
||||
expect(result.diffSourceAfter).toContain(' Idle = "idle",\n\n Paused = "paused",\n\n Busy = "busy",');
|
||||
expect(result.diffSourceAfter).toContain(' Idle = "idle",\n Paused = "paused",\n Busy = "busy",');
|
||||
});
|
||||
|
||||
test("replace with empty content removes an individually addressable enum variant", () => {
|
||||
@@ -1017,7 +1019,7 @@ describe("addressable member editing", () => {
|
||||
});
|
||||
|
||||
describe("Go receiver render ownership", () => {
|
||||
test("omits unrelated top-level siblings from grouped receiver output", () => {
|
||||
test("before inserts beside a top-level Go receiver method", () => {
|
||||
const source = `package main\n\ntype Server struct {\n Addr string\n}\n\nfunc (s *Server) Start() {}\nfunc (s Server) Stop() {}\n`;
|
||||
const result = applyEdit({
|
||||
source,
|
||||
@@ -1026,7 +1028,7 @@ describe("Go receiver render ownership", () => {
|
||||
operations: [
|
||||
{
|
||||
op: "before",
|
||||
sel: "type_Server.fn_Start",
|
||||
sel: "fn_Start",
|
||||
content: "func DefaultServer() *Server {\n return &Server{}\n}",
|
||||
},
|
||||
],
|
||||
@@ -1034,7 +1036,8 @@ describe("Go receiver render ownership", () => {
|
||||
|
||||
expect(result.responseText).toContain("func DefaultServer() *Server");
|
||||
expect(result.responseText).toContain("[fn_DefaultServer#");
|
||||
expect(result.responseText).toContain("[type_Server.fn_Start#");
|
||||
expect(result.responseText).toContain("[fn_Start#");
|
||||
expect(result.responseText).not.toContain("[type_Server.fn_Start#");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1096,9 +1099,7 @@ describe("chunk selector auto-resolution", () => {
|
||||
content: "run(): void {\n\tconsole.log(this.name);\n}",
|
||||
},
|
||||
]);
|
||||
expect(
|
||||
result.warnings.some(w => w.includes('Auto-resolved chunk selector "fn_run" to "class_Worker.fn_run"')),
|
||||
).toBe(true);
|
||||
expect(result.warnings.join("\n")).toMatch(/Auto-resolved chunk selector "fn_run" to "class_Worker\.fn_run#/);
|
||||
});
|
||||
|
||||
test("warns on prefix auto-resolution", () => {
|
||||
@@ -1109,9 +1110,7 @@ describe("chunk selector auto-resolution", () => {
|
||||
content: "run(): void {\n\tconsole.log(this.name);\n}",
|
||||
},
|
||||
]);
|
||||
expect(result.warnings.some(w => w.includes('Auto-resolved chunk selector "run" to "class_Worker.fn_run"'))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(result.warnings.join("\n")).toMatch(/Auto-resolved chunk selector "run" to "class_Worker\.fn_run#/);
|
||||
});
|
||||
|
||||
test("errors on ambiguous suffix matches", () => {
|
||||
|
||||
@@ -12,10 +12,13 @@ import {
|
||||
stripNewLinePrefixes,
|
||||
validateLineRef,
|
||||
} from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import { type Anchor, formatLineTag, type HashlineEdit } from "@oh-my-pi/pi-coding-agent/edit/modes/hashline";
|
||||
import type { Anchor, HashlineEdit } from "@oh-my-pi/pi-coding-agent/edit/modes/hashline";
|
||||
|
||||
function makeTag(line: number, content: string): Anchor {
|
||||
return parseTag(formatLineTag(line, content));
|
||||
return {
|
||||
line,
|
||||
hash: computeLineHash(line, content),
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -266,7 +266,7 @@ describe("Coding Agent Tools", () => {
|
||||
expect(result.details?.truncation).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should convert ipynb files through markit before rendering", async () => {
|
||||
it("should convert ipynb files through markit for raw reads", async () => {
|
||||
const notebookPath = path.join(testDir, "notebook.ipynb");
|
||||
const notebook = {
|
||||
cells: [
|
||||
@@ -287,7 +287,7 @@ describe("Coding Agent Tools", () => {
|
||||
content: "# Notebook Title\n\nNotebook body\n",
|
||||
});
|
||||
|
||||
const result = await readTool.execute("test-call-ipynb", { path: notebookPath });
|
||||
const result = await readTool.execute("test-call-ipynb", { path: notebookPath, sel: "raw" });
|
||||
const output = getTextOutput(result);
|
||||
|
||||
expect(convertSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { _resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { EditTool } from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import { HASHLINE_NIBBLE_ALPHABET } from "@oh-my-pi/pi-coding-agent/edit/modes/hashline";
|
||||
import { HASHLINE_NIBBLE_ALPHABET } from "@oh-my-pi/pi-coding-agent/edit/line-hash";
|
||||
import { getLanguageFromPath } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { GrepTool } from "@oh-my-pi/pi-coding-agent/tools/grep";
|
||||
@@ -106,8 +106,9 @@ describe("chunk mode tools", () => {
|
||||
const text = getText(result);
|
||||
|
||||
expect(text).not.toContain("to expand ⋮");
|
||||
expect(text).toContain("server.ts:class_Server.fn_handleError@container·");
|
||||
expect(text).toContain("let total = 0;");
|
||||
expect(text).toContain("29| total += 25;");
|
||||
expect(text).toContain("29| \t\t\ttotal += 25;");
|
||||
expect(text).toContain("return err.message + total;");
|
||||
});
|
||||
|
||||
@@ -157,7 +158,7 @@ describe("chunk mode tools", () => {
|
||||
});
|
||||
const text = getText(result);
|
||||
|
||||
expect(text).toContain("server.ts:class_Server.fn_handleError·");
|
||||
expect(text).toContain("server.ts:class_Server.fn_handleError@container·");
|
||||
expect(text).not.toContain("[Warning: checksum #");
|
||||
});
|
||||
|
||||
@@ -308,7 +309,13 @@ describe("chunk mode tools", () => {
|
||||
|
||||
await editTool.execute("chunk-edit-string-content", {
|
||||
path: filePath,
|
||||
edits: [{ target: "class_Server", op: "append", content: 'status(): string {\n return "ok";\n}\n' }],
|
||||
edits: [
|
||||
{
|
||||
target: "class_Server@body",
|
||||
op: "append",
|
||||
content: 'status(): string {\n return "ok";\n}\n',
|
||||
},
|
||||
],
|
||||
} as never);
|
||||
|
||||
const updatedSource = await Bun.file(filePath).text();
|
||||
|
||||
@@ -36,18 +36,17 @@
|
||||
"test": "bun run build:native && bun test",
|
||||
"bench": "bun bench/grep.ts"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "3.6.0",
|
||||
"@types/bun": "^1.3"
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.3.7"
|
||||
},
|
||||
"napi": {
|
||||
"binaryName": "pi_natives",
|
||||
"triples": {}
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.3.7"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"native",
|
||||
|
||||
Reference in New Issue
Block a user