pi-readseek 0.9.12 → 0.9.14
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 +14 -51
- package/dist/index.ts +253 -287
- package/package.json +7 -7
- package/prompts/edit.md +4 -4
- package/LICENSE +0 -176
package/README.md
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# pi-readseek
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`overrideTools` maps `read`, `edit`, `write`, or `grep` to a ReadSeek-backed
|
|
6
|
-
implementation. The `read` override runs ReadSeek's `digest` command; it does not
|
|
7
|
-
add a separate `readSeek_read` tool.
|
|
3
|
+
Pi extension for anchored file operations, structural search, symbol navigation,
|
|
4
|
+
and document inspection.
|
|
8
5
|
|
|
9
6
|
## Install
|
|
10
7
|
|
|
@@ -12,33 +9,18 @@ add a separate `readSeek_read` tool.
|
|
|
12
9
|
pi install npm:pi-readseek
|
|
13
10
|
```
|
|
14
11
|
|
|
15
|
-
The
|
|
16
|
-
|
|
17
|
-
unresolved binary indicates a broken installation rather than falling back to an
|
|
18
|
-
unrelated `readseek` executable on `PATH`.
|
|
12
|
+
The extension uses the native binary supplied by `@jarkkojs/readseek`. Set
|
|
13
|
+
`READSEEK_BINARY` to an absolute executable path to override it.
|
|
19
14
|
|
|
20
15
|
## Tools
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
| `readSeek_write` | Create or replace a complete text file and return anchors |
|
|
26
|
-
| `readSeek_grep` | Search text or regex and return edit-ready anchors |
|
|
27
|
-
| `readSeek_search` | Search AST patterns |
|
|
28
|
-
| `readSeek_def` | Find symbol definitions |
|
|
29
|
-
| `readSeek_refs` | Find identifier usages, optionally scoped to a cursor binding |
|
|
30
|
-
| `readSeek_rename` | Rename a cursor symbol; set `apply: false` to preview the plan |
|
|
31
|
-
| `readSeek_digest` | Native `digest` envelope: map, diagnostics, identity, content, metadata |
|
|
32
|
-
| `readSeek_view` | View or narrow a structural document index (PDF first); optional asset vision |
|
|
33
|
-
|
|
34
|
-
The extension also tells Pi to get fresh anchors before editing and to run
|
|
35
|
-
`readSeek_digest` with diagnostics after source edits.
|
|
17
|
+
`readSeek_edit`, `readSeek_write`, `readSeek_grep`, `readSeek_search`,
|
|
18
|
+
`readSeek_def`, `readSeek_refs`, `readSeek_rename`, `readSeek_digest`, and
|
|
19
|
+
`readSeek_view`.
|
|
36
20
|
|
|
37
21
|
## Settings
|
|
38
22
|
|
|
39
|
-
Add
|
|
40
|
-
`.pi/settings.json` (project). Project settings override matching global values.
|
|
41
|
-
Defaults:
|
|
23
|
+
Add `readseek` to `~/.pi/agent/settings.json` or `.pi/settings.json`:
|
|
42
24
|
|
|
43
25
|
```json
|
|
44
26
|
{
|
|
@@ -47,10 +29,7 @@ Defaults:
|
|
|
47
29
|
"imageMode": "auto",
|
|
48
30
|
"syntaxValidation": "warn",
|
|
49
31
|
"timeoutMs": 120000,
|
|
50
|
-
"grep": {
|
|
51
|
-
"maxLines": 2000,
|
|
52
|
-
"maxBytes": 51200
|
|
53
|
-
},
|
|
32
|
+
"grep": { "maxLines": 2000, "maxBytes": 51200 },
|
|
54
33
|
"display": {
|
|
55
34
|
"grep": "compact",
|
|
56
35
|
"edit": "expanded",
|
|
@@ -60,23 +39,8 @@ Defaults:
|
|
|
60
39
|
}
|
|
61
40
|
```
|
|
62
41
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
`"auto"` passes the prepared image to image-capable Pi models and otherwise
|
|
66
|
-
runs local analysis, `"on"` always runs local analysis, and `"off"` skips the
|
|
67
|
-
image. Local analysis defaults to `all` and also supports `ocr`, `caption`, and
|
|
68
|
-
`objects` through `visionMode`. The namespaced `readSeek_digest` tool applies
|
|
69
|
-
`visionMode` directly, including `none`. Documents use `readSeek_view` with
|
|
70
|
-
optional `visionMode` and `visionLevel`. Local analysis uses
|
|
71
|
-
[Qwen3-VL-2B-Instruct](../../docs/models/qwen3-vl-2b-instruct.md).
|
|
72
|
-
- `syntaxValidation`: `"warn"` (write and warn), `"block"` (abort), or `"off"`.
|
|
73
|
-
Validation triggers only when an edit increases parser error or missing-node
|
|
74
|
-
counts.
|
|
75
|
-
- `timeoutMs`: native command timeout in milliseconds.
|
|
76
|
-
- `grep.maxLines` and `grep.maxBytes`: output limits; larger values are clamped
|
|
77
|
-
to the defaults.
|
|
78
|
-
- `display`: initial `"compact"` or `"expanded"` state for `grep`,
|
|
79
|
-
`edit`, and `write` results.
|
|
42
|
+
`overrideTools` accepts `read`, `edit`, `write`, and `grep`. `imageMode` is
|
|
43
|
+
`auto`, `on`, or `off`; `syntaxValidation` is `warn`, `block`, or `off`.
|
|
80
44
|
|
|
81
45
|
## Development
|
|
82
46
|
|
|
@@ -86,8 +50,7 @@ npm test
|
|
|
86
50
|
npm run typecheck
|
|
87
51
|
```
|
|
88
52
|
|
|
89
|
-
## License
|
|
90
53
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Apache
|
|
54
|
+
## Licensing
|
|
55
|
+
|
|
56
|
+
[Apache 2.0](../../LICENSE-APACHE-2.0).
|
package/dist/index.ts
CHANGED
|
@@ -28,6 +28,13 @@ var DOCUMENT_NODE_KINDS = [
|
|
|
28
28
|
"section",
|
|
29
29
|
"structural_section"
|
|
30
30
|
];
|
|
31
|
+
var EDIT_VARIANTS = [
|
|
32
|
+
"set_line",
|
|
33
|
+
"replace_lines",
|
|
34
|
+
"insert_after",
|
|
35
|
+
"replace",
|
|
36
|
+
"replace_symbol"
|
|
37
|
+
];
|
|
31
38
|
var TOOL_NAMES = [
|
|
32
39
|
"digest",
|
|
33
40
|
"edit",
|
|
@@ -485,6 +492,25 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
485
492
|
return [title, ...bullets].join(`
|
|
486
493
|
`);
|
|
487
494
|
}
|
|
495
|
+
// ../readseek-api/src/hashline.ts
|
|
496
|
+
var HASH_LEN = 6;
|
|
497
|
+
var LINE_ANCHOR_RE = /^(\d+):([0-9a-fA-F]{6})$/;
|
|
498
|
+
function tryParseLineAnchor(anchor) {
|
|
499
|
+
const match = LINE_ANCHOR_RE.exec(anchor.trim());
|
|
500
|
+
if (!match) {
|
|
501
|
+
return { ok: false, error: `invalid LINE:HASH anchor: ${anchor}` };
|
|
502
|
+
}
|
|
503
|
+
const digits = match[1];
|
|
504
|
+
const line = Number.parseInt(digits, 10);
|
|
505
|
+
if (!Number.isSafeInteger(line)) {
|
|
506
|
+
return { ok: false, error: `Line number must be a safe integer, got ${digits} in "${anchor}".` };
|
|
507
|
+
}
|
|
508
|
+
if (line < 1) {
|
|
509
|
+
return { ok: false, error: `Line number must be >= 1, got ${line} in "${anchor}".` };
|
|
510
|
+
}
|
|
511
|
+
return { ok: true, anchor: { line, hash: match[2].toLowerCase() } };
|
|
512
|
+
}
|
|
513
|
+
|
|
488
514
|
// ../readseek-api/src/validation.ts
|
|
489
515
|
function validateGitSelection(input) {
|
|
490
516
|
if (input.ignored && !input.others) {
|
|
@@ -516,6 +542,73 @@ function validateVisionLevelVsMode(input) {
|
|
|
516
542
|
}
|
|
517
543
|
return { ok: true };
|
|
518
544
|
}
|
|
545
|
+
var VARIANT_FIELDS = {
|
|
546
|
+
set_line: ["anchor", "new_text"],
|
|
547
|
+
replace_lines: ["start_anchor", "end_anchor", "new_text"],
|
|
548
|
+
insert_after: ["anchor", "new_text"],
|
|
549
|
+
replace: ["old_text", "new_text"],
|
|
550
|
+
replace_symbol: ["symbol", "new_body"]
|
|
551
|
+
};
|
|
552
|
+
function isEditVariant(value) {
|
|
553
|
+
return EDIT_VARIANTS.includes(value);
|
|
554
|
+
}
|
|
555
|
+
function parseEditItem(item, index) {
|
|
556
|
+
const keys = Object.keys(item);
|
|
557
|
+
const variant = keys[0];
|
|
558
|
+
if (keys.length !== 1 || variant === undefined || !isEditVariant(variant)) {
|
|
559
|
+
return {
|
|
560
|
+
ok: false,
|
|
561
|
+
error: `edits[${index}] must contain exactly one of: ${EDIT_VARIANTS.join(", ")}. Got: [${keys.join(", ")}]`
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
const payload = item[variant];
|
|
565
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
566
|
+
return { ok: false, error: `edits[${index}].${variant} must be an object` };
|
|
567
|
+
}
|
|
568
|
+
const expected = VARIANT_FIELDS[variant];
|
|
569
|
+
const allowed = variant === "replace" ? [...expected, "all"] : [...expected];
|
|
570
|
+
const actual = Object.keys(payload);
|
|
571
|
+
if (actual.some((field) => !allowed.includes(field)) || expected.some((field) => !actual.includes(field))) {
|
|
572
|
+
return { ok: false, error: `edits[${index}].${variant} contains invalid fields` };
|
|
573
|
+
}
|
|
574
|
+
const values = payload;
|
|
575
|
+
for (const field of expected) {
|
|
576
|
+
if (typeof values[field] !== "string") {
|
|
577
|
+
return { ok: false, error: `edits[${index}].${variant}.${field} must be a string` };
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (variant === "replace" && values.all !== undefined && typeof values.all !== "boolean") {
|
|
581
|
+
return { ok: false, error: `edits[${index}].replace.all must be a boolean` };
|
|
582
|
+
}
|
|
583
|
+
for (const field of expected) {
|
|
584
|
+
if (!field.endsWith("anchor"))
|
|
585
|
+
continue;
|
|
586
|
+
const parsed = tryParseLineAnchor(values[field]);
|
|
587
|
+
if (!parsed.ok) {
|
|
588
|
+
return { ok: false, error: `edits[${index}].${variant}.${field}: ${parsed.error}` };
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return { ok: true, edit: item };
|
|
592
|
+
}
|
|
593
|
+
function validateExactlyOneEditVariant(edits) {
|
|
594
|
+
if (!Array.isArray(edits) || edits.length === 0) {
|
|
595
|
+
return { ok: false, error: "edits must contain at least one edit" };
|
|
596
|
+
}
|
|
597
|
+
const normalized = [];
|
|
598
|
+
for (let index = 0;index < edits.length; index++) {
|
|
599
|
+
const item = edits[index];
|
|
600
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
601
|
+
return { ok: false, error: `edits[${index}] must be an edit object` };
|
|
602
|
+
}
|
|
603
|
+
const parsed = parseEditItem(item, index);
|
|
604
|
+
if (!parsed.ok)
|
|
605
|
+
return parsed;
|
|
606
|
+
normalized.push(parsed.edit);
|
|
607
|
+
}
|
|
608
|
+
return { ok: true, edits: normalized };
|
|
609
|
+
}
|
|
610
|
+
var MIN_SAFE_INTEGER = BigInt(Number.MIN_SAFE_INTEGER);
|
|
611
|
+
var MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
|
|
519
612
|
// ../readseek-api/src/argv.ts
|
|
520
613
|
function buildLineLocation(line, column) {
|
|
521
614
|
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
@@ -525,14 +618,12 @@ function appendGitSelectionArgs(args, input) {
|
|
|
525
618
|
if (!validated.ok) {
|
|
526
619
|
throw new Error(validated.error);
|
|
527
620
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
args.push("--git", selection.join(","));
|
|
535
|
-
}
|
|
621
|
+
if (input.cached)
|
|
622
|
+
args.push("--cached");
|
|
623
|
+
if (input.others)
|
|
624
|
+
args.push("--others");
|
|
625
|
+
if (input.ignored)
|
|
626
|
+
args.push("--ignored");
|
|
536
627
|
return args;
|
|
537
628
|
}
|
|
538
629
|
function pushOptionalString(args, flag, value) {
|
|
@@ -609,15 +700,17 @@ function buildViewArgs(input) {
|
|
|
609
700
|
return args;
|
|
610
701
|
}
|
|
611
702
|
function buildSearchArgs(input) {
|
|
612
|
-
const args = ["search", input.
|
|
703
|
+
const args = ["search", input.pattern];
|
|
704
|
+
pushOptionalString(args, "--path", input.path);
|
|
613
705
|
pushOptionalString(args, "--language", input.language);
|
|
706
|
+
pushOptionalNumber(args, "--limit", input.limit);
|
|
614
707
|
return appendGitSelectionArgs(args, input);
|
|
615
708
|
}
|
|
616
709
|
function buildDefArgs(input) {
|
|
617
|
-
const args = ["def", input.
|
|
710
|
+
const args = ["def", input.name];
|
|
711
|
+
pushOptionalString(args, "--path", input.path);
|
|
618
712
|
if (input.format !== undefined)
|
|
619
713
|
args.push("--format", input.format);
|
|
620
|
-
args.push(input.name);
|
|
621
714
|
pushOptionalString(args, "--language", input.language);
|
|
622
715
|
return appendGitSelectionArgs(args, input);
|
|
623
716
|
}
|
|
@@ -625,11 +718,14 @@ function buildRefsArgs(input) {
|
|
|
625
718
|
const validated = validateRefsNameVsScope(input);
|
|
626
719
|
if (!validated.ok)
|
|
627
720
|
throw new Error(validated.error);
|
|
628
|
-
const args = ["refs"
|
|
629
|
-
if (input.
|
|
630
|
-
args.push("--at", buildLineLocation(input.line, input.column));
|
|
631
|
-
} else if (input.name !== undefined) {
|
|
721
|
+
const args = ["refs"];
|
|
722
|
+
if (input.name !== undefined)
|
|
632
723
|
args.push(input.name);
|
|
724
|
+
pushOptionalString(args, "--path", input.path);
|
|
725
|
+
if (input.scope) {
|
|
726
|
+
args.push("--scope");
|
|
727
|
+
pushOptionalNumber(args, "--line", input.line);
|
|
728
|
+
pushOptionalNumber(args, "--column", input.column);
|
|
633
729
|
}
|
|
634
730
|
if (input.format !== undefined)
|
|
635
731
|
args.push("--format", input.format);
|
|
@@ -645,13 +741,13 @@ function buildEditArgs(input) {
|
|
|
645
741
|
});
|
|
646
742
|
pushOptionalString(args, "--language", input.language);
|
|
647
743
|
if (input.requestFromStdin)
|
|
648
|
-
args.push("--
|
|
744
|
+
args.push("--edits", "-");
|
|
649
745
|
return args;
|
|
650
746
|
}
|
|
651
747
|
function buildRenameArgs(input) {
|
|
652
748
|
const args = ["rename", input.path, "--at", buildLineLocation(input.line, input.column), "--to", input.to];
|
|
653
749
|
if (input.workspace !== undefined)
|
|
654
|
-
args.push(
|
|
750
|
+
args.push(`--workspace=${input.workspace}`);
|
|
655
751
|
appendApplyAndPlanHash(args, {
|
|
656
752
|
apply: input.apply,
|
|
657
753
|
planHash: input.planHash,
|
|
@@ -661,9 +757,10 @@ function buildRenameArgs(input) {
|
|
|
661
757
|
return appendGitSelectionArgs(args, input);
|
|
662
758
|
}
|
|
663
759
|
// ../readseek-api/src/platform.ts
|
|
664
|
-
import { accessSync, constants as fsConstants } from "node:fs";
|
|
760
|
+
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
665
761
|
import { createRequire } from "node:module";
|
|
666
762
|
import path from "node:path";
|
|
763
|
+
var READSEEK_BINARY_ENV = "READSEEK_BINARY";
|
|
667
764
|
var READSEEK_PLATFORM_PACKAGES = {
|
|
668
765
|
"darwin-arm64": "@jarkkojs/readseek-darwin-arm64",
|
|
669
766
|
"linux-arm64": "@jarkkojs/readseek-linux-arm64",
|
|
@@ -677,6 +774,24 @@ function readSeekBinaryName(platform = process.platform) {
|
|
|
677
774
|
function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
|
|
678
775
|
return `${platform}-${arch}`;
|
|
679
776
|
}
|
|
777
|
+
function resolveReadSeekBinaryOverride(environment = process.env) {
|
|
778
|
+
const binaryPath = environment[READSEEK_BINARY_ENV];
|
|
779
|
+
if (binaryPath === undefined)
|
|
780
|
+
return null;
|
|
781
|
+
if (!path.isAbsolute(binaryPath)) {
|
|
782
|
+
throw new Error(`${READSEEK_BINARY_ENV} must be an absolute path to the readseek executable`);
|
|
783
|
+
}
|
|
784
|
+
try {
|
|
785
|
+
if (!statSync(binaryPath).isFile())
|
|
786
|
+
throw new Error("not a regular file");
|
|
787
|
+
accessSync(binaryPath, fsConstants.X_OK);
|
|
788
|
+
} catch (error) {
|
|
789
|
+
throw new Error(`${READSEEK_BINARY_ENV} does not point to an executable file: ${binaryPath}`, {
|
|
790
|
+
cause: error
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
return binaryPath;
|
|
794
|
+
}
|
|
680
795
|
function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, options = {}) {
|
|
681
796
|
const resolve = typeof resolver === "function" ? resolver : resolver.resolve.bind(resolver);
|
|
682
797
|
const platform = options.platform ?? process.platform;
|
|
@@ -1033,6 +1148,7 @@ function parseCheckOutput(value) {
|
|
|
1033
1148
|
if (!Array.isArray(output.diagnostics))
|
|
1034
1149
|
throw new Error("invalid readseek diagnostics");
|
|
1035
1150
|
return {
|
|
1151
|
+
file_hash: optionalString(output.file_hash, "file_hash"),
|
|
1036
1152
|
errorCount: requireNumber(output.error_count, "error_count"),
|
|
1037
1153
|
missingCount: requireNumber(output.missing_count, "missing_count"),
|
|
1038
1154
|
diagnostics: output.diagnostics.map((diagnostic) => {
|
|
@@ -1437,6 +1553,7 @@ function createReadSeekClient(runner) {
|
|
|
1437
1553
|
path: target,
|
|
1438
1554
|
pattern,
|
|
1439
1555
|
language: options.language,
|
|
1556
|
+
limit: options.limit,
|
|
1440
1557
|
cached: options.cached,
|
|
1441
1558
|
others: options.others,
|
|
1442
1559
|
ignored: options.ignored
|
|
@@ -1502,7 +1619,8 @@ function createReadSeekClient(runner) {
|
|
|
1502
1619
|
path: filePath,
|
|
1503
1620
|
apply: options.apply,
|
|
1504
1621
|
planHash: options.planHash,
|
|
1505
|
-
language: options.language
|
|
1622
|
+
language: options.language,
|
|
1623
|
+
requestFromStdin: true
|
|
1506
1624
|
});
|
|
1507
1625
|
return parseEditOutput(await runner.json(args, {
|
|
1508
1626
|
signal: options.signal,
|
|
@@ -1574,10 +1692,7 @@ import * as Diff from "diff";
|
|
|
1574
1692
|
|
|
1575
1693
|
// src/hashline.ts
|
|
1576
1694
|
import xxhashWasm from "xxhash-wasm";
|
|
1577
|
-
var
|
|
1578
|
-
var RADIX = 16;
|
|
1579
|
-
var HASH_MOD = RADIX ** HASH_LEN;
|
|
1580
|
-
var DICT = Array.from({ length: HASH_MOD }, (_, i) => i.toString(RADIX).padStart(HASH_LEN, "0"));
|
|
1695
|
+
var ANCHOR_MASK = 16777215;
|
|
1581
1696
|
var HASHLINE_STATE_KEY = Symbol.for("pi-readseek.hashlineState.v1");
|
|
1582
1697
|
function getHashlineState() {
|
|
1583
1698
|
const globalObject = globalThis;
|
|
@@ -1595,6 +1710,9 @@ async function ensureHashInit() {
|
|
|
1595
1710
|
if (!state.initPromise) {
|
|
1596
1711
|
state.initPromise = xxhashWasm().then((hasher) => {
|
|
1597
1712
|
state.h32Fn = hasher.h32;
|
|
1713
|
+
}).catch((error) => {
|
|
1714
|
+
state.initPromise = null;
|
|
1715
|
+
throw error;
|
|
1598
1716
|
});
|
|
1599
1717
|
}
|
|
1600
1718
|
await state.initPromise;
|
|
@@ -1605,11 +1723,11 @@ function xxh32(input) {
|
|
|
1605
1723
|
throw new Error("Hash not initialized — call ensureHashInit() first");
|
|
1606
1724
|
return state.h32Fn(input, 0) >>> 0;
|
|
1607
1725
|
}
|
|
1726
|
+
function canonicalizeLine(line) {
|
|
1727
|
+
return line.match(/[^\t\r\n ]+/g)?.join(" ") ?? "";
|
|
1728
|
+
}
|
|
1608
1729
|
function computeLineHash(line) {
|
|
1609
|
-
|
|
1610
|
-
line = line.slice(0, -1);
|
|
1611
|
-
line = line.replace(/\s+/g, "");
|
|
1612
|
-
return DICT[xxh32(line) % HASH_MOD];
|
|
1730
|
+
return (xxh32(canonicalizeLine(line)) & ANCHOR_MASK).toString(16).padStart(HASH_LEN, "0");
|
|
1613
1731
|
}
|
|
1614
1732
|
var DISPLAY_CONTROL_CHAR_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
|
|
1615
1733
|
function escapeControlCharsForDisplay(text) {
|
|
@@ -1762,27 +1880,10 @@ function generateCompactOrFullDiff(oldContent, newContent, contextLines = 4) {
|
|
|
1762
1880
|
}
|
|
1763
1881
|
|
|
1764
1882
|
// src/path-utils.ts
|
|
1765
|
-
import {
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
return str.replace(UNICODE_SPACES, " ");
|
|
1770
|
-
}
|
|
1771
|
-
function normalizeAtPrefix(filePath) {
|
|
1772
|
-
return filePath.startsWith("@") ? filePath.slice(1) : filePath;
|
|
1773
|
-
}
|
|
1774
|
-
function expandPath(filePath) {
|
|
1775
|
-
const normalized = normalizeUnicodeSpaces(normalizeAtPrefix(filePath));
|
|
1776
|
-
if (normalized === "~")
|
|
1777
|
-
return homedir();
|
|
1778
|
-
if (normalized.startsWith("~/"))
|
|
1779
|
-
return homedir() + normalized.slice(1);
|
|
1780
|
-
return normalized;
|
|
1781
|
-
}
|
|
1782
|
-
function resolveToCwd(filePath, cwd) {
|
|
1783
|
-
const expanded = expandPath(filePath);
|
|
1784
|
-
return isAbsolute(expanded) ? expanded : resolvePath(cwd, expanded);
|
|
1785
|
-
}
|
|
1883
|
+
import {
|
|
1884
|
+
expandUserPath as expandUserPath2,
|
|
1885
|
+
resolveToCwd as resolveToCwd2
|
|
1886
|
+
} from "@jarkkojs/readseek-api";
|
|
1786
1887
|
|
|
1787
1888
|
// src/runtime.ts
|
|
1788
1889
|
function throwIfAborted(signal) {
|
|
@@ -2112,41 +2213,14 @@ function classifyEdit(oldContent, newContent) {
|
|
|
2112
2213
|
}
|
|
2113
2214
|
|
|
2114
2215
|
// src/readseek-client.ts
|
|
2115
|
-
import { existsSync
|
|
2216
|
+
import { existsSync as existsSync2, statSync as statSync3 } from "node:fs";
|
|
2116
2217
|
import { createRequire as createRequire2 } from "node:module";
|
|
2117
|
-
import { homedir as
|
|
2218
|
+
import { homedir as homedir2 } from "node:os";
|
|
2118
2219
|
import path2 from "node:path";
|
|
2119
2220
|
|
|
2120
|
-
// src/readseek/enums.ts
|
|
2121
|
-
var SymbolKind;
|
|
2122
|
-
((SymbolKind2) => {
|
|
2123
|
-
SymbolKind2["Class"] = "class";
|
|
2124
|
-
SymbolKind2["Function"] = "function";
|
|
2125
|
-
SymbolKind2["Method"] = "method";
|
|
2126
|
-
SymbolKind2["Variable"] = "variable";
|
|
2127
|
-
SymbolKind2["Constant"] = "constant";
|
|
2128
|
-
SymbolKind2["Interface"] = "interface";
|
|
2129
|
-
SymbolKind2["Type"] = "type";
|
|
2130
|
-
SymbolKind2["Enum"] = "enum";
|
|
2131
|
-
SymbolKind2["Struct"] = "struct";
|
|
2132
|
-
SymbolKind2["Import"] = "import";
|
|
2133
|
-
SymbolKind2["Module"] = "module";
|
|
2134
|
-
SymbolKind2["Namespace"] = "namespace";
|
|
2135
|
-
SymbolKind2["Property"] = "property";
|
|
2136
|
-
SymbolKind2["Heading"] = "heading";
|
|
2137
|
-
SymbolKind2["Table"] = "table";
|
|
2138
|
-
SymbolKind2["View"] = "view";
|
|
2139
|
-
SymbolKind2["Procedure"] = "procedure";
|
|
2140
|
-
SymbolKind2["Trigger"] = "trigger";
|
|
2141
|
-
SymbolKind2["Index"] = "index";
|
|
2142
|
-
SymbolKind2["Schema"] = "schema";
|
|
2143
|
-
SymbolKind2["Signal"] = "signal";
|
|
2144
|
-
SymbolKind2["Unknown"] = "unknown";
|
|
2145
|
-
})(SymbolKind ||= {});
|
|
2146
|
-
|
|
2147
2221
|
// src/readseek-settings.ts
|
|
2148
|
-
import { readFileSync, statSync } from "node:fs";
|
|
2149
|
-
import { homedir
|
|
2222
|
+
import { readFileSync, statSync as statSync2 } from "node:fs";
|
|
2223
|
+
import { homedir } from "node:os";
|
|
2150
2224
|
import { join } from "node:path";
|
|
2151
2225
|
var READSEEK_TOOL_REPLACEMENTS = {
|
|
2152
2226
|
read: "readSeek_digest",
|
|
@@ -2163,7 +2237,7 @@ var DEFAULT_DISPLAY_MODES = {
|
|
|
2163
2237
|
write: "expanded"
|
|
2164
2238
|
};
|
|
2165
2239
|
function globalSettingsPath() {
|
|
2166
|
-
return join(
|
|
2240
|
+
return join(homedir(), ".pi/agent/settings.json");
|
|
2167
2241
|
}
|
|
2168
2242
|
function projectSettingsPath() {
|
|
2169
2243
|
return join(process.cwd(), ".pi/settings.json");
|
|
@@ -2303,7 +2377,7 @@ function validateSettings(raw, source) {
|
|
|
2303
2377
|
var settingsFileCache = new Map;
|
|
2304
2378
|
function statFileOrNull(path2) {
|
|
2305
2379
|
try {
|
|
2306
|
-
return
|
|
2380
|
+
return statSync2(path2);
|
|
2307
2381
|
} catch {
|
|
2308
2382
|
return null;
|
|
2309
2383
|
}
|
|
@@ -2362,7 +2436,7 @@ function resolveReadSeekToolDisplayMode(tool) {
|
|
|
2362
2436
|
// src/readseek-client.ts
|
|
2363
2437
|
var resolveFromHost = createRequire2(import.meta.url).resolve;
|
|
2364
2438
|
function readSeekBinaryPath() {
|
|
2365
|
-
return resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
2439
|
+
return resolveReadSeekBinaryOverride() ?? resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
2366
2440
|
}
|
|
2367
2441
|
function readSeekBinaryAvailability() {
|
|
2368
2442
|
try {
|
|
@@ -2372,46 +2446,18 @@ function readSeekBinaryAvailability() {
|
|
|
2372
2446
|
return { available: false, reason: classifyReadSeekFailure(err).message };
|
|
2373
2447
|
}
|
|
2374
2448
|
}
|
|
2375
|
-
var READSEEK_REPO_PACKAGE_NAMES = new Set(["@jarkkojs/readseek", "readseek"]);
|
|
2376
2449
|
var defaultReadSeekDirInit;
|
|
2377
|
-
function
|
|
2450
|
+
function defaultReadSeekDir() {
|
|
2451
|
+
const home = homedir2();
|
|
2452
|
+
return home ? path2.join(home, ".pi", "readseek") : null;
|
|
2453
|
+
}
|
|
2454
|
+
function directoryExists(dir) {
|
|
2378
2455
|
try {
|
|
2379
|
-
return
|
|
2456
|
+
return statSync3(dir).isDirectory();
|
|
2380
2457
|
} catch {
|
|
2381
2458
|
return false;
|
|
2382
2459
|
}
|
|
2383
2460
|
}
|
|
2384
|
-
var ownRepositoryByDir = new Map;
|
|
2385
|
-
function isOwnReadSeekRepository(cwd = process.cwd()) {
|
|
2386
|
-
const start = path2.resolve(cwd);
|
|
2387
|
-
const cached = ownRepositoryByDir.get(start);
|
|
2388
|
-
if (cached !== undefined)
|
|
2389
|
-
return cached;
|
|
2390
|
-
let dir = start;
|
|
2391
|
-
let result = false;
|
|
2392
|
-
while (true) {
|
|
2393
|
-
const packageJsonPath = path2.join(dir, "package.json");
|
|
2394
|
-
if (existsSync(packageJsonPath)) {
|
|
2395
|
-
try {
|
|
2396
|
-
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
2397
|
-
if (typeof packageJson.name === "string" && READSEEK_REPO_PACKAGE_NAMES.has(packageJson.name)) {
|
|
2398
|
-
result = true;
|
|
2399
|
-
break;
|
|
2400
|
-
}
|
|
2401
|
-
} catch {}
|
|
2402
|
-
}
|
|
2403
|
-
const parent = path2.dirname(dir);
|
|
2404
|
-
if (parent === dir)
|
|
2405
|
-
break;
|
|
2406
|
-
dir = parent;
|
|
2407
|
-
}
|
|
2408
|
-
ownRepositoryByDir.set(start, result);
|
|
2409
|
-
return result;
|
|
2410
|
-
}
|
|
2411
|
-
function defaultReadSeekDir() {
|
|
2412
|
-
const home = homedir3();
|
|
2413
|
-
return home ? path2.join(home, ".pi", "readseek") : null;
|
|
2414
|
-
}
|
|
2415
2461
|
var DEFAULT_READSEEK_VISION_TIMEOUT_MS = 30 * 60000;
|
|
2416
2462
|
function readSeekTimeoutMs() {
|
|
2417
2463
|
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
@@ -2435,8 +2481,19 @@ async function ensureDefaultReadSeekDir() {
|
|
|
2435
2481
|
});
|
|
2436
2482
|
return defaultReadSeekDirInit;
|
|
2437
2483
|
}
|
|
2484
|
+
function hasProjectReadSeekDir(cwd = process.cwd()) {
|
|
2485
|
+
let dir = path2.resolve(cwd);
|
|
2486
|
+
while (true) {
|
|
2487
|
+
if (existsSync2(path2.join(dir, ".readseek")))
|
|
2488
|
+
return true;
|
|
2489
|
+
const parent = path2.dirname(dir);
|
|
2490
|
+
if (parent === dir)
|
|
2491
|
+
return false;
|
|
2492
|
+
dir = parent;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2438
2495
|
async function readSeekInvocationArgs(args) {
|
|
2439
|
-
if (
|
|
2496
|
+
if (hasProjectReadSeekDir())
|
|
2440
2497
|
return [...args];
|
|
2441
2498
|
const readSeekDir = await ensureDefaultReadSeekDir();
|
|
2442
2499
|
return readSeekDir ? ["--readseek-dir", readSeekDir, ...args] : [...args];
|
|
@@ -2491,7 +2548,7 @@ async function runReadSeekVisionRaw(args, options = {}) {
|
|
|
2491
2548
|
}
|
|
2492
2549
|
}
|
|
2493
2550
|
async function runReadSeekVision(args, options = {}) {
|
|
2494
|
-
return
|
|
2551
|
+
return parseReadSeekJson(await runReadSeekVisionRaw(args, options));
|
|
2495
2552
|
}
|
|
2496
2553
|
var runner = {
|
|
2497
2554
|
text: (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) : runReadSeekRaw(args, options),
|
|
@@ -2512,67 +2569,12 @@ var readSeekIdentify = client.identify;
|
|
|
2512
2569
|
var readSeekDetect = client.detect;
|
|
2513
2570
|
var readSeekEdit = client.edit;
|
|
2514
2571
|
var readSeekRename = client.rename;
|
|
2515
|
-
function
|
|
2516
|
-
return language === "java" ? "Java" : language;
|
|
2517
|
-
}
|
|
2518
|
-
function normalizeKind(kind) {
|
|
2519
|
-
if (kind === "constructor")
|
|
2520
|
-
return "method" /* Method */;
|
|
2521
|
-
if (Object.values(SymbolKind).includes(kind))
|
|
2522
|
-
return kind;
|
|
2523
|
-
return "unknown" /* Unknown */;
|
|
2524
|
-
}
|
|
2525
|
-
function parentQualifiedNameFor(qualifiedName) {
|
|
2526
|
-
const lastDot = qualifiedName.lastIndexOf(".");
|
|
2527
|
-
return lastDot === -1 ? "" : qualifiedName.slice(0, lastDot);
|
|
2528
|
-
}
|
|
2529
|
-
function symbolsFromReadSeek(symbols) {
|
|
2530
|
-
const symbolsByQualifiedName = new Map;
|
|
2531
|
-
const entries = [];
|
|
2532
|
-
for (const symbol of symbols) {
|
|
2533
|
-
const parentQualifiedName = parentQualifiedNameFor(symbol.qualified_name);
|
|
2534
|
-
const fileSymbol = {
|
|
2535
|
-
name: symbol.name,
|
|
2536
|
-
kind: normalizeKind(symbol.kind),
|
|
2537
|
-
startLine: symbol.start_line,
|
|
2538
|
-
endLine: symbol.end_line
|
|
2539
|
-
};
|
|
2540
|
-
const bucket = symbolsByQualifiedName.get(symbol.qualified_name);
|
|
2541
|
-
if (bucket)
|
|
2542
|
-
bucket.push(fileSymbol);
|
|
2543
|
-
else
|
|
2544
|
-
symbolsByQualifiedName.set(symbol.qualified_name, [fileSymbol]);
|
|
2545
|
-
entries.push({ parentQualifiedName, symbol: fileSymbol });
|
|
2546
|
-
}
|
|
2547
|
-
const roots = [];
|
|
2548
|
-
for (const entry of entries) {
|
|
2549
|
-
const parent = entry.parentQualifiedName ? symbolsByQualifiedName.get(entry.parentQualifiedName)?.[0] : undefined;
|
|
2550
|
-
if (!parent) {
|
|
2551
|
-
roots.push(entry.symbol);
|
|
2552
|
-
continue;
|
|
2553
|
-
}
|
|
2554
|
-
parent.children ??= [];
|
|
2555
|
-
parent.children.push(entry.symbol);
|
|
2556
|
-
}
|
|
2557
|
-
return roots;
|
|
2558
|
-
}
|
|
2559
|
-
function fileMapFromReadSeekOutput(output, filePath, totalBytes) {
|
|
2560
|
-
if (output.language === "unknown" && output.symbols.length === 0)
|
|
2561
|
-
return null;
|
|
2562
|
-
return {
|
|
2563
|
-
path: filePath,
|
|
2564
|
-
totalLines: output.line_count,
|
|
2565
|
-
totalBytes,
|
|
2566
|
-
language: normalizeLanguage(output.language),
|
|
2567
|
-
detailLevel: "full" /* Full */,
|
|
2568
|
-
symbols: symbolsFromReadSeek(output.symbols)
|
|
2569
|
-
};
|
|
2570
|
-
}
|
|
2571
|
-
async function readSeekMap(filePath, totalBytes, options = {}) {
|
|
2572
|
+
async function readSeekMap(filePath, options = {}) {
|
|
2572
2573
|
const envelope = await client.digest(filePath, { select: "map", signal: options.signal });
|
|
2573
|
-
|
|
2574
|
+
const map = envelope.map;
|
|
2575
|
+
if (!map || map.language === "unknown" && map.symbols.length === 0)
|
|
2574
2576
|
return null;
|
|
2575
|
-
return
|
|
2577
|
+
return map;
|
|
2576
2578
|
}
|
|
2577
2579
|
|
|
2578
2580
|
// src/edit-syntax-validate.ts
|
|
@@ -2677,19 +2679,19 @@ function parseCompactDiffEntries(diff, oldContent, newContent) {
|
|
|
2677
2679
|
`);
|
|
2678
2680
|
const newLines = newContent.split(`
|
|
2679
2681
|
`);
|
|
2680
|
-
const compactDelete = diff.match(/^(\d+):[0-9a-f]{
|
|
2682
|
+
const compactDelete = diff.match(/^(\d+):[0-9a-f]{6}\|(.*) → \[deleted\]$/);
|
|
2681
2683
|
if (compactDelete) {
|
|
2682
2684
|
const oldLine2 = Number(compactDelete[1]);
|
|
2683
2685
|
const text = compactDelete[2] ?? "";
|
|
2684
2686
|
if (oldLines[oldLine2 - 1] === text)
|
|
2685
2687
|
return [{ kind: "remove", oldLine: oldLine2, text }];
|
|
2686
2688
|
}
|
|
2687
|
-
const compactPrefix = diff.match(/^(\d+):[0-9a-f]{
|
|
2689
|
+
const compactPrefix = diff.match(/^(\d+):[0-9a-f]{6}\|/);
|
|
2688
2690
|
if (!compactPrefix)
|
|
2689
2691
|
return;
|
|
2690
2692
|
const oldLine = Number(compactPrefix[1]);
|
|
2691
2693
|
const oldTextStart = compactPrefix[0].length;
|
|
2692
|
-
const separatorPattern = / → (\d+):[0-9a-f]{
|
|
2694
|
+
const separatorPattern = / → (\d+):[0-9a-f]{6}\|/g;
|
|
2693
2695
|
let separator;
|
|
2694
2696
|
while ((separator = separatorPattern.exec(diff)) !== null) {
|
|
2695
2697
|
const newLine = Number(separator[1]);
|
|
@@ -2873,7 +2875,7 @@ function linkToolPath(styledText, rawPath, cwd) {
|
|
|
2873
2875
|
try {
|
|
2874
2876
|
if (!getCapabilities().hyperlinks)
|
|
2875
2877
|
return styledText;
|
|
2876
|
-
const absolutePath =
|
|
2878
|
+
const absolutePath = resolveToCwd2(rawPath, cwd);
|
|
2877
2879
|
return hyperlink(styledText, pathToFileURL(absolutePath).href);
|
|
2878
2880
|
} catch {
|
|
2879
2881
|
return styledText;
|
|
@@ -3149,11 +3151,6 @@ class DiffPreviewComponent {
|
|
|
3149
3151
|
});
|
|
3150
3152
|
for (const line of body.lines)
|
|
3151
3153
|
lines.push(line);
|
|
3152
|
-
for (const suffix of this.options.suffixLines ?? []) {
|
|
3153
|
-
for (const line of suffix.split(`
|
|
3154
|
-
`))
|
|
3155
|
-
lines.push(clampLineToWidth(line, normalized));
|
|
3156
|
-
}
|
|
3157
3154
|
const clamped = lines.map((line) => visibleWidth3(line) <= normalized ? line : clampLineToWidth(line, normalized));
|
|
3158
3155
|
this.cachedLines = clamped;
|
|
3159
3156
|
this.cachedWidth = normalized;
|
|
@@ -3251,7 +3248,7 @@ async function executeEdit(opts) {
|
|
|
3251
3248
|
const parsed = params;
|
|
3252
3249
|
const rawPath = parsed.path;
|
|
3253
3250
|
const path3 = rawPath.replace(/^@/, "");
|
|
3254
|
-
const absolutePath =
|
|
3251
|
+
const absolutePath = resolveToCwd2(path3, cwd);
|
|
3255
3252
|
throwIfAborted(signal);
|
|
3256
3253
|
try {
|
|
3257
3254
|
return await withFileMutationQueue(absolutePath, async () => {
|
|
@@ -3260,18 +3257,11 @@ async function executeEdit(opts) {
|
|
|
3260
3257
|
const hint = `Call readSeek_digest(${JSON.stringify(rawPath)}) first, or use readSeek_grep, readSeek_search, or readSeek_write to produce fresh anchors for this file.`;
|
|
3261
3258
|
return buildEditError(absolutePath, "file-not-read", `You must get fresh anchors for ${absolutePath} before editing it. ${hint}`, hint);
|
|
3262
3259
|
}
|
|
3263
|
-
const
|
|
3264
|
-
if (!
|
|
3265
|
-
return buildEditError(absolutePath, "invalid-edit-variant",
|
|
3266
|
-
}
|
|
3267
|
-
const variants = new Set(["set_line", "replace_lines", "insert_after", "replace", "replace_symbol"]);
|
|
3268
|
-
for (let index = 0;index < edits.length; index++) {
|
|
3269
|
-
const edit = edits[index];
|
|
3270
|
-
const keys = edit && typeof edit === "object" && !Array.isArray(edit) ? Object.keys(edit) : [];
|
|
3271
|
-
if (keys.length !== 1 || !variants.has(keys[0])) {
|
|
3272
|
-
return buildEditError(absolutePath, "invalid-edit-variant", `edits[${index}] must contain exactly one of: 'set_line', 'replace_lines', 'insert_after', 'replace', 'replace_symbol'. Got: [${keys.join(", ")}].`);
|
|
3273
|
-
}
|
|
3260
|
+
const parsedEdits = validateExactlyOneEditVariant(parsed.edits ?? []);
|
|
3261
|
+
if (!parsedEdits.ok) {
|
|
3262
|
+
return buildEditError(absolutePath, "invalid-edit-variant", parsedEdits.error);
|
|
3274
3263
|
}
|
|
3264
|
+
const edits = parsedEdits.edits;
|
|
3275
3265
|
let plan;
|
|
3276
3266
|
try {
|
|
3277
3267
|
plan = await readSeekEdit(absolutePath, edits, { signal, language: parsed.language });
|
|
@@ -3619,39 +3609,34 @@ ${text}`;
|
|
|
3619
3609
|
}
|
|
3620
3610
|
|
|
3621
3611
|
// src/file-map.ts
|
|
3622
|
-
import { stat } from "node:fs/promises";
|
|
3623
3612
|
async function getOrGenerateMap(absPath) {
|
|
3624
3613
|
try {
|
|
3625
|
-
|
|
3626
|
-
return await readSeekMap(absPath, size);
|
|
3614
|
+
return await readSeekMap(absPath);
|
|
3627
3615
|
} catch {
|
|
3628
3616
|
return null;
|
|
3629
3617
|
}
|
|
3630
3618
|
}
|
|
3631
3619
|
|
|
3632
|
-
// src/readseek/symbol-tree.ts
|
|
3633
|
-
function traverseSymbolTree(symbols, visit) {
|
|
3634
|
-
const result = [];
|
|
3635
|
-
const walk = (nodes, parentName) => {
|
|
3636
|
-
for (const symbol of nodes) {
|
|
3637
|
-
result.push(visit(symbol, parentName));
|
|
3638
|
-
if (symbol.children?.length)
|
|
3639
|
-
walk(symbol.children, symbol.name);
|
|
3640
|
-
}
|
|
3641
|
-
};
|
|
3642
|
-
walk(symbols);
|
|
3643
|
-
return result;
|
|
3644
|
-
}
|
|
3645
|
-
|
|
3646
3620
|
// src/grep-symbol-scope.ts
|
|
3647
3621
|
function findEnclosingSymbol(map, lineNumber) {
|
|
3648
|
-
const
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3622
|
+
const byQualifiedName = new Map;
|
|
3623
|
+
for (const symbol of map.symbols) {
|
|
3624
|
+
const qname = symbol.qualified_name ?? symbol.name;
|
|
3625
|
+
if (!byQualifiedName.has(qname))
|
|
3626
|
+
byQualifiedName.set(qname, symbol);
|
|
3627
|
+
}
|
|
3628
|
+
const candidates = map.symbols.filter((symbol) => lineNumber >= symbol.start_line && lineNumber <= symbol.end_line).map((symbol) => {
|
|
3629
|
+
const qname = symbol.qualified_name ?? symbol.name;
|
|
3630
|
+
const dot = qname.lastIndexOf(".");
|
|
3631
|
+
const parent = dot === -1 ? undefined : byQualifiedName.get(qname.slice(0, dot));
|
|
3632
|
+
return {
|
|
3633
|
+
name: symbol.name,
|
|
3634
|
+
kind: symbol.kind === "constructor" ? "method" : symbol.kind,
|
|
3635
|
+
startLine: symbol.start_line,
|
|
3636
|
+
endLine: symbol.end_line,
|
|
3637
|
+
parentName: parent?.name
|
|
3638
|
+
};
|
|
3639
|
+
}).sort((a, b) => {
|
|
3655
3640
|
const rangeA = a.endLine - a.startLine;
|
|
3656
3641
|
const rangeB = b.endLine - b.startLine;
|
|
3657
3642
|
if (rangeA !== rangeB)
|
|
@@ -3660,7 +3645,7 @@ function findEnclosingSymbol(map, lineNumber) {
|
|
|
3660
3645
|
return a.startLine - b.startLine;
|
|
3661
3646
|
return a.name.localeCompare(b.name);
|
|
3662
3647
|
});
|
|
3663
|
-
return candidates
|
|
3648
|
+
return candidates[0] ?? null;
|
|
3664
3649
|
}
|
|
3665
3650
|
function firstLineNumber(group) {
|
|
3666
3651
|
const first = group.entries.find((e) => e.kind !== "separator");
|
|
@@ -3851,38 +3836,9 @@ function formatGrepResultText(input) {
|
|
|
3851
3836
|
}
|
|
3852
3837
|
|
|
3853
3838
|
// src/coerce-obvious-int.ts
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
function formatReceived(value) {
|
|
3858
|
-
if (typeof value === "number" || typeof value === "bigint")
|
|
3859
|
-
return String(value);
|
|
3860
|
-
return JSON.stringify(value) ?? String(value);
|
|
3861
|
-
}
|
|
3862
|
-
function invalidInteger(value, name) {
|
|
3863
|
-
return {
|
|
3864
|
-
ok: false,
|
|
3865
|
-
message: `Invalid ${name}: expected a safe base-10 integer, received ${formatReceived(value)}.`
|
|
3866
|
-
};
|
|
3867
|
-
}
|
|
3868
|
-
function coerceObviousBase10Int(value, name) {
|
|
3869
|
-
if (value === undefined || typeof value === "string" && value.trim() === "") {
|
|
3870
|
-
return { ok: true, value: undefined };
|
|
3871
|
-
}
|
|
3872
|
-
if (typeof value === "number") {
|
|
3873
|
-
if (Number.isSafeInteger(value)) {
|
|
3874
|
-
return { ok: true, value };
|
|
3875
|
-
}
|
|
3876
|
-
return invalidInteger(value, name);
|
|
3877
|
-
}
|
|
3878
|
-
if (typeof value === "string" && BASE10_INT_RE.test(value)) {
|
|
3879
|
-
const parsed = BigInt(value);
|
|
3880
|
-
if (parsed >= MIN_SAFE_INTEGER && parsed <= MAX_SAFE_INTEGER) {
|
|
3881
|
-
return { ok: true, value: Number(parsed) };
|
|
3882
|
-
}
|
|
3883
|
-
}
|
|
3884
|
-
return invalidInteger(value, name);
|
|
3885
|
-
}
|
|
3839
|
+
import {
|
|
3840
|
+
coerceObviousBase10Int as coerceObviousBase10Int2
|
|
3841
|
+
} from "@jarkkojs/readseek-api";
|
|
3886
3842
|
|
|
3887
3843
|
// src/tui-source-render.ts
|
|
3888
3844
|
import { getLanguageFromPath, highlightCode } from "@earendil-works/pi-coding-agent";
|
|
@@ -4019,15 +3975,15 @@ async function executeGrep(opts) {
|
|
|
4019
3975
|
const { toolCallId, params, signal, onUpdate, cwd, onFileAnchored } = opts;
|
|
4020
3976
|
await ensureHashInit();
|
|
4021
3977
|
const rawParams = params;
|
|
4022
|
-
const context =
|
|
3978
|
+
const context = coerceObviousBase10Int2(rawParams.context, "context");
|
|
4023
3979
|
if (!context.ok) {
|
|
4024
3980
|
return buildToolErrorResult("grep", "invalid-params-combo", context.message);
|
|
4025
3981
|
}
|
|
4026
|
-
const limit =
|
|
3982
|
+
const limit = coerceObviousBase10Int2(rawParams.limit, "limit");
|
|
4027
3983
|
if (!limit.ok) {
|
|
4028
3984
|
return buildToolErrorResult("grep", "invalid-limit", limit.message);
|
|
4029
3985
|
}
|
|
4030
|
-
const scopeContext =
|
|
3986
|
+
const scopeContext = coerceObviousBase10Int2(rawParams.scopeContext, "scopeContext");
|
|
4031
3987
|
if (!scopeContext.ok) {
|
|
4032
3988
|
return buildToolErrorResult("grep", "invalid-params-combo", scopeContext.message);
|
|
4033
3989
|
}
|
|
@@ -4055,7 +4011,7 @@ async function executeGrep(opts) {
|
|
|
4055
4011
|
if (!textBlock?.text)
|
|
4056
4012
|
return result;
|
|
4057
4013
|
const { path: rawSearchPath } = p;
|
|
4058
|
-
const searchPath =
|
|
4014
|
+
const searchPath = resolveToCwd2(rawSearchPath || ".", cwd);
|
|
4059
4015
|
let searchPathIsDirectory = false;
|
|
4060
4016
|
try {
|
|
4061
4017
|
searchPathIsDirectory = (await fsStat(searchPath)).isDirectory();
|
|
@@ -4481,7 +4437,7 @@ async function executeSearch(opts) {
|
|
|
4481
4437
|
const ignoredError = validateIgnoredRequiresOthers("search", p);
|
|
4482
4438
|
if (ignoredError)
|
|
4483
4439
|
return ignoredError;
|
|
4484
|
-
const searchPath =
|
|
4440
|
+
const searchPath = resolveToCwd2(p.path ?? ".", cwd);
|
|
4485
4441
|
const statResult = await statSearchPathOrError("search", p.path, searchPath);
|
|
4486
4442
|
if (!statResult.ok)
|
|
4487
4443
|
return statResult.error;
|
|
@@ -4656,7 +4612,7 @@ async function executeRefs(opts) {
|
|
|
4656
4612
|
if (!scopeError.ok) {
|
|
4657
4613
|
return buildToolErrorResult("refs", "invalid-parameter", `refs parameter: ${scopeError.error}`);
|
|
4658
4614
|
}
|
|
4659
|
-
const searchPath =
|
|
4615
|
+
const searchPath = resolveToCwd2(p.path ?? ".", cwd);
|
|
4660
4616
|
const statResult = await statSearchPathOrError("refs", p.path, searchPath);
|
|
4661
4617
|
if (!statResult.ok)
|
|
4662
4618
|
return statResult.error;
|
|
@@ -4762,7 +4718,7 @@ async function executeRename(opts) {
|
|
|
4762
4718
|
if (p.column !== undefined && (!Number.isSafeInteger(p.column) || p.column < 1)) {
|
|
4763
4719
|
return buildToolErrorResult("rename", "invalid-parameter", "rename parameter 'column' must be a positive integer");
|
|
4764
4720
|
}
|
|
4765
|
-
const filePath =
|
|
4721
|
+
const filePath = resolveToCwd2(p.path, cwd);
|
|
4766
4722
|
try {
|
|
4767
4723
|
const output = await readSeekRename(filePath, {
|
|
4768
4724
|
to: p.to,
|
|
@@ -4885,8 +4841,8 @@ import { Text as Text6 } from "@earendil-works/pi-tui";
|
|
|
4885
4841
|
import { Type as Type8 } from "@sinclair/typebox";
|
|
4886
4842
|
|
|
4887
4843
|
// src/pending-diff-preview.ts
|
|
4888
|
-
import { existsSync as
|
|
4889
|
-
import { dirname, isAbsolute
|
|
4844
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync, statSync as statSync4 } from "node:fs";
|
|
4845
|
+
import { dirname, isAbsolute, relative as relative2, resolve, sep } from "node:path";
|
|
4890
4846
|
var PENDING_DIFF_MAX_BYTES = 1024 * 1024;
|
|
4891
4847
|
function skip(reason) {
|
|
4892
4848
|
return { type: "skip", reason };
|
|
@@ -4900,9 +4856,9 @@ function resolveWorkspacePreviewPath(rawPath, cwd, allowMissing) {
|
|
|
4900
4856
|
return { type: "skip", reason: "missing path" };
|
|
4901
4857
|
const workspace = realpathSync(cwd);
|
|
4902
4858
|
const normalizedPath = rawPath.replace(/^@/, "");
|
|
4903
|
-
const explicitAbsolute =
|
|
4859
|
+
const explicitAbsolute = isAbsolute(normalizedPath);
|
|
4904
4860
|
const requested = resolve(workspace, normalizedPath);
|
|
4905
|
-
if (
|
|
4861
|
+
if (existsSync3(requested)) {
|
|
4906
4862
|
const realTarget = realpathSync(requested);
|
|
4907
4863
|
if (!explicitAbsolute && !isInsidePath(workspace, realTarget))
|
|
4908
4864
|
return { type: "skip", reason: "path outside workspace" };
|
|
@@ -4913,17 +4869,17 @@ function resolveWorkspacePreviewPath(rawPath, cwd, allowMissing) {
|
|
|
4913
4869
|
if (!allowMissing)
|
|
4914
4870
|
return { type: "skip", reason: "file not found" };
|
|
4915
4871
|
const parent = dirname(requested);
|
|
4916
|
-
if (!
|
|
4872
|
+
if (!existsSync3(parent))
|
|
4917
4873
|
return { type: "skip", reason: "parent directory not found" };
|
|
4918
4874
|
return { type: "ok", path: requested, existed: false };
|
|
4919
4875
|
}
|
|
4920
4876
|
function readUtf8File(filePath) {
|
|
4921
|
-
const
|
|
4922
|
-
if (!
|
|
4877
|
+
const stat = statSync4(filePath);
|
|
4878
|
+
if (!stat.isFile())
|
|
4923
4879
|
return { type: "skip", reason: "not a file" };
|
|
4924
|
-
if (
|
|
4880
|
+
if (stat.size > PENDING_DIFF_MAX_BYTES)
|
|
4925
4881
|
return { type: "skip", reason: "file too large" };
|
|
4926
|
-
const content =
|
|
4882
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
4927
4883
|
if (content.includes("\x00"))
|
|
4928
4884
|
return { type: "skip", reason: "binary file" };
|
|
4929
4885
|
return { type: "ok", content };
|
|
@@ -5184,7 +5140,7 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5184
5140
|
}),
|
|
5185
5141
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
5186
5142
|
const cwd = ctx?.cwd ?? process.cwd();
|
|
5187
|
-
const absolutePath =
|
|
5143
|
+
const absolutePath = resolveToCwd2(params.path, cwd);
|
|
5188
5144
|
try {
|
|
5189
5145
|
return await withFileMutationQueue2(absolutePath, async () => {
|
|
5190
5146
|
let result;
|
|
@@ -5294,7 +5250,7 @@ async function executeDef(opts) {
|
|
|
5294
5250
|
if (!p.name || !p.name.trim()) {
|
|
5295
5251
|
return buildToolErrorResult("def", "invalid-parameter", "readSeek_def requires 'name'");
|
|
5296
5252
|
}
|
|
5297
|
-
const searchPath =
|
|
5253
|
+
const searchPath = resolveToCwd2(p.path ?? ".", cwd);
|
|
5298
5254
|
const statResult = await statSearchPathOrError("def", p.path, searchPath);
|
|
5299
5255
|
if (!statResult.ok)
|
|
5300
5256
|
return statResult.error;
|
|
@@ -5506,12 +5462,12 @@ async function executeDigest(opts) {
|
|
|
5506
5462
|
}
|
|
5507
5463
|
const facets = selectedFacets(select);
|
|
5508
5464
|
const appliesImagePolicy = opts.imageMode !== undefined && facets.includes("content");
|
|
5509
|
-
const end =
|
|
5465
|
+
const end = coerceObviousBase10Int2(p.end, "end");
|
|
5510
5466
|
if (!end.ok || end.value !== undefined && end.value < 1) {
|
|
5511
5467
|
const message = end.ok ? `Invalid end: expected a positive integer, received ${end.value}.` : end.message;
|
|
5512
5468
|
return buildToolErrorResult("digest", "invalid-parameter", message, { path: p.path });
|
|
5513
5469
|
}
|
|
5514
|
-
const limit =
|
|
5470
|
+
const limit = coerceObviousBase10Int2(p.limit, "limit");
|
|
5515
5471
|
if (!limit.ok || limit.value !== undefined && limit.value < 1) {
|
|
5516
5472
|
const message = limit.ok ? `Invalid limit: expected a positive integer, received ${limit.value}.` : limit.message;
|
|
5517
5473
|
return buildToolErrorResult("digest", "invalid-parameter", message, { path: p.path });
|
|
@@ -5527,7 +5483,7 @@ async function executeDigest(opts) {
|
|
|
5527
5483
|
path: p.path
|
|
5528
5484
|
});
|
|
5529
5485
|
}
|
|
5530
|
-
const filePath =
|
|
5486
|
+
const filePath = resolveToCwd2(p.path, cwd);
|
|
5531
5487
|
const commonOptions = { select, signal };
|
|
5532
5488
|
const textOptions = {
|
|
5533
5489
|
...commonOptions,
|
|
@@ -5665,12 +5621,12 @@ var VIEW_PROMPT_METADATA = defineToolPromptMetadata({
|
|
|
5665
5621
|
async function executeView(opts) {
|
|
5666
5622
|
const { params, signal, cwd } = opts;
|
|
5667
5623
|
const input = params;
|
|
5668
|
-
const page =
|
|
5624
|
+
const page = coerceObviousBase10Int2(input.page, "page");
|
|
5669
5625
|
if (!page.ok || page.value !== undefined && page.value < 1) {
|
|
5670
5626
|
const message = page.ok ? `Invalid page: expected a positive integer, received ${page.value}.` : page.message;
|
|
5671
5627
|
return buildToolErrorResult("view", "invalid-page", message, { path: input.path });
|
|
5672
5628
|
}
|
|
5673
|
-
const depth =
|
|
5629
|
+
const depth = coerceObviousBase10Int2(input.depth, "depth");
|
|
5674
5630
|
if (!depth.ok || depth.value !== undefined && depth.value < 0) {
|
|
5675
5631
|
const message = depth.ok ? `Invalid depth: expected a non-negative integer, received ${depth.value}.` : depth.message;
|
|
5676
5632
|
return buildToolErrorResult("view", "invalid-depth", message, { path: input.path });
|
|
@@ -5691,7 +5647,7 @@ async function executeView(opts) {
|
|
|
5691
5647
|
path: input.path
|
|
5692
5648
|
});
|
|
5693
5649
|
}
|
|
5694
|
-
const filePath =
|
|
5650
|
+
const filePath = resolveToCwd2(input.path, cwd);
|
|
5695
5651
|
try {
|
|
5696
5652
|
const output = await readSeekView(filePath, {
|
|
5697
5653
|
node,
|
|
@@ -5823,7 +5779,6 @@ function piReadSeekExtension(pi) {
|
|
|
5823
5779
|
const forgetAnchors = (absolutePath) => sessionAnchors.forget(absolutePath);
|
|
5824
5780
|
const { settings } = resolveReadSeekJsonSettings();
|
|
5825
5781
|
const replacedBuiltIns = new Set(settings.overrideTools ?? []);
|
|
5826
|
-
const binaryAvailable = readSeekBinaryAvailability().available;
|
|
5827
5782
|
const swap = (builtIn, readSeekName) => replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
|
|
5828
5783
|
const readName = swap("read", "readSeek_digest");
|
|
5829
5784
|
const editName = swap("edit", "readSeek_edit");
|
|
@@ -5853,8 +5808,24 @@ function piReadSeekExtension(pi) {
|
|
|
5853
5808
|
});
|
|
5854
5809
|
registerViewTool(pi);
|
|
5855
5810
|
registerWriteTool(pi, { onFileAnchored: markAnchored, name: writeName });
|
|
5811
|
+
const activeReadSeekNames = READSEEK_TOOL_ENTRIES.map((entry) => entry.builtIn && replacedBuiltIns.has(entry.builtIn) ? entry.builtIn : entry.readSeekName);
|
|
5812
|
+
const inactiveReadSeekNames = new Set(READSEEK_TOOL_ENTRIES.filter((entry) => entry.builtIn !== null && replacedBuiltIns.has(entry.builtIn)).map((entry) => entry.readSeekName));
|
|
5813
|
+
const activateReadSeekTools = () => {
|
|
5814
|
+
if (!readSeekBinaryAvailability().available)
|
|
5815
|
+
return false;
|
|
5816
|
+
const current = pi.getActiveTools();
|
|
5817
|
+
const currentSet = new Set(current);
|
|
5818
|
+
const missing = activeReadSeekNames.some((name) => !currentSet.has(name));
|
|
5819
|
+
const extraInactive = current.some((name) => inactiveReadSeekNames.has(name));
|
|
5820
|
+
if (!missing && !extraInactive)
|
|
5821
|
+
return true;
|
|
5822
|
+
pi.setActiveTools([
|
|
5823
|
+
...new Set([...current, ...activeReadSeekNames].filter((name) => !inactiveReadSeekNames.has(name)))
|
|
5824
|
+
]);
|
|
5825
|
+
return true;
|
|
5826
|
+
};
|
|
5856
5827
|
pi.on("before_agent_start", (event) => {
|
|
5857
|
-
if (!
|
|
5828
|
+
if (!activateReadSeekTools())
|
|
5858
5829
|
return;
|
|
5859
5830
|
return { systemPrompt: `${event.systemPrompt}
|
|
5860
5831
|
|
|
@@ -5874,12 +5845,7 @@ ${toolRoutingPolicy(policyAliases)}` };
|
|
|
5874
5845
|
else
|
|
5875
5846
|
console.warn(problem);
|
|
5876
5847
|
}
|
|
5877
|
-
|
|
5878
|
-
return;
|
|
5879
|
-
const activeReadSeekNames = READSEEK_TOOL_ENTRIES.map((entry) => entry.builtIn && replacedBuiltIns.has(entry.builtIn) ? entry.builtIn : entry.readSeekName);
|
|
5880
|
-
const inactiveReadSeekNames = new Set(READSEEK_TOOL_ENTRIES.filter((entry) => entry.builtIn !== null && replacedBuiltIns.has(entry.builtIn)).map((entry) => entry.readSeekName));
|
|
5881
|
-
const activeTools = [...pi.getActiveTools(), ...activeReadSeekNames].filter((name) => !inactiveReadSeekNames.has(name));
|
|
5882
|
-
pi.setActiveTools([...new Set(activeTools)]);
|
|
5848
|
+
activateReadSeekTools();
|
|
5883
5849
|
});
|
|
5884
5850
|
}
|
|
5885
5851
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-readseek",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.14",
|
|
4
4
|
"description": "Pi extension for LINE:HASH-anchored file operations and structural code navigation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"dist/",
|
|
29
29
|
"prompts/",
|
|
30
|
-
"LICENSE",
|
|
31
30
|
"README.md"
|
|
32
31
|
],
|
|
33
32
|
"scripts": {
|
|
@@ -40,7 +39,8 @@
|
|
|
40
39
|
"node": ">=20.0.0"
|
|
41
40
|
},
|
|
42
41
|
"dependencies": {
|
|
43
|
-
"@jarkkojs/readseek": "^0.9.
|
|
42
|
+
"@jarkkojs/readseek": "^0.9.14",
|
|
43
|
+
"@jarkkojs/readseek-api": "^0.9.14",
|
|
44
44
|
"diff": "^9.0.0",
|
|
45
45
|
"xxhash-wasm": "^1.1.0"
|
|
46
46
|
},
|
|
@@ -50,13 +50,13 @@
|
|
|
50
50
|
"@sinclair/typebox": "*"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@earendil-works/pi-ai": "^0.
|
|
54
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
55
|
-
"@earendil-works/pi-tui": "^0.
|
|
53
|
+
"@earendil-works/pi-ai": "^0.84.2",
|
|
54
|
+
"@earendil-works/pi-coding-agent": "^0.84.2",
|
|
55
|
+
"@earendil-works/pi-tui": "^0.84.2",
|
|
56
56
|
"@sinclair/typebox": "^0.34.48",
|
|
57
57
|
"@types/node": "^26.1.1",
|
|
58
58
|
"jiti": "^2.7.0",
|
|
59
|
-
"typescript": "^
|
|
59
|
+
"typescript": "^5.9.3",
|
|
60
60
|
"vitest": "^4.1.0"
|
|
61
61
|
},
|
|
62
62
|
"directories": {
|
package/prompts/edit.md
CHANGED
|
@@ -24,15 +24,15 @@ plain file content, not a diff or hashline output.
|
|
|
24
24
|
{
|
|
25
25
|
"path": "src/foo.ts",
|
|
26
26
|
"edits": [
|
|
27
|
-
{ "set_line": { "anchor": "42:
|
|
27
|
+
{ "set_line": { "anchor": "42:ab1cde", "new_text": "const x = 2;" } },
|
|
28
28
|
{
|
|
29
29
|
"replace_lines": {
|
|
30
|
-
"start_anchor": "50:
|
|
31
|
-
"end_anchor": "55:
|
|
30
|
+
"start_anchor": "50:c3d4e5",
|
|
31
|
+
"end_anchor": "55:e4f5a6",
|
|
32
32
|
"new_text": "const y = 3;\nreturn y;"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
|
-
{ "insert_after": { "anchor": "60:
|
|
35
|
+
{ "insert_after": { "anchor": "60:f5a6b7", "new_text": "// TODO\n" } }
|
|
36
36
|
]
|
|
37
37
|
}
|
|
38
38
|
```
|
package/LICENSE
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
Apache License
|
|
2
|
-
Version 2.0, January 2004
|
|
3
|
-
http://www.apache.org/licenses/
|
|
4
|
-
|
|
5
|
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
-
|
|
7
|
-
1. Definitions.
|
|
8
|
-
|
|
9
|
-
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
-
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
-
|
|
12
|
-
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
-
the copyright owner that is granting the License.
|
|
14
|
-
|
|
15
|
-
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
-
other entities that control, are controlled by, or are under common
|
|
17
|
-
control with that entity. For the purposes of this definition,
|
|
18
|
-
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
-
direction or management of such entity, whether by contract or
|
|
20
|
-
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
-
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
-
|
|
23
|
-
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
-
exercising permissions granted by this License.
|
|
25
|
-
|
|
26
|
-
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
-
including but not limited to software source code, documentation
|
|
28
|
-
source, and configuration files.
|
|
29
|
-
|
|
30
|
-
"Object" form shall mean any form resulting from mechanical
|
|
31
|
-
transformation or translation of a Source form, including but
|
|
32
|
-
not limited to compiled object code, generated documentation,
|
|
33
|
-
and conversions to other media types.
|
|
34
|
-
|
|
35
|
-
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
-
Object form, made available under the License, as indicated by a
|
|
37
|
-
copyright notice that is included in or attached to the work
|
|
38
|
-
(an example is provided in the Appendix below).
|
|
39
|
-
|
|
40
|
-
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
-
form, that is based on (or derived from) the Work and for which the
|
|
42
|
-
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
-
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
-
of this License, Derivative Works shall not include works that remain
|
|
45
|
-
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
-
the Work and Derivative Works thereof.
|
|
47
|
-
|
|
48
|
-
"Contribution" shall mean any work of authorship, including
|
|
49
|
-
the original version of the Work and any modifications or additions
|
|
50
|
-
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
-
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
-
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
-
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
-
means any form of electronic, verbal, or written communication sent
|
|
55
|
-
to the Licensor or its representatives, including but not limited to
|
|
56
|
-
communication on electronic mailing lists, source code control systems,
|
|
57
|
-
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
-
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
-
excluding communication that is conspicuously marked or otherwise
|
|
60
|
-
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
-
|
|
62
|
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
-
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
-
subsequently incorporated within the Work.
|
|
65
|
-
|
|
66
|
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
-
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
-
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
-
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
-
Work and such Derivative Works in Source or Object form.
|
|
72
|
-
|
|
73
|
-
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
-
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
-
(except as stated in this section) patent license to make, have made,
|
|
77
|
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
-
where such license applies only to those patent claims licensable
|
|
79
|
-
by such Contributor that are necessarily infringed by their
|
|
80
|
-
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
-
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
-
institute patent litigation against any entity (including a
|
|
83
|
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
-
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
-
or contributory patent infringement, then any patent licenses
|
|
86
|
-
granted to You under this License for that Work shall terminate
|
|
87
|
-
as of the date such litigation is filed.
|
|
88
|
-
|
|
89
|
-
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
-
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
-
modifications, and in Source or Object form, provided that You
|
|
92
|
-
meet the following conditions:
|
|
93
|
-
|
|
94
|
-
(a) You must give any other recipients of the Work or
|
|
95
|
-
Derivative Works a copy of this License; and
|
|
96
|
-
|
|
97
|
-
(b) You must cause any modified files to carry prominent notices
|
|
98
|
-
stating that You changed the files; and
|
|
99
|
-
|
|
100
|
-
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
-
that You distribute, all copyright, patent, trademark, and
|
|
102
|
-
attribution notices from the Source form of the Work,
|
|
103
|
-
excluding those notices that do not pertain to any part of
|
|
104
|
-
the Derivative Works; and
|
|
105
|
-
|
|
106
|
-
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
-
distribution, then any Derivative Works that You distribute must
|
|
108
|
-
include a readable copy of the attribution notices contained
|
|
109
|
-
within such NOTICE file, excluding those notices that do not
|
|
110
|
-
pertain to any part of the Derivative Works, in at least one
|
|
111
|
-
of the following places: within a NOTICE text file distributed
|
|
112
|
-
as part of the Derivative Works; within the Source form or
|
|
113
|
-
documentation, if provided along with the Derivative Works; or,
|
|
114
|
-
within a display generated by the Derivative Works, if and
|
|
115
|
-
wherever such third-party notices normally appear. The contents
|
|
116
|
-
of the NOTICE file are for informational purposes only and
|
|
117
|
-
do not modify the License. You may add Your own attribution
|
|
118
|
-
notices within Derivative Works that You distribute, alongside
|
|
119
|
-
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
-
that such additional attribution notices cannot be construed
|
|
121
|
-
as modifying the License.
|
|
122
|
-
|
|
123
|
-
You may add Your own copyright statement to Your modifications and
|
|
124
|
-
may provide additional or different license terms and conditions
|
|
125
|
-
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
-
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
-
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
-
the conditions stated in this License.
|
|
129
|
-
|
|
130
|
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
-
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
-
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
-
this License, without any additional terms or conditions.
|
|
134
|
-
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
-
the terms of any separate license agreement you may have executed
|
|
136
|
-
with Licensor regarding such Contributions.
|
|
137
|
-
|
|
138
|
-
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
-
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
-
except as required for reasonable and customary use in describing the
|
|
141
|
-
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
-
|
|
143
|
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
-
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
-
implied, including, without limitation, any warranties or conditions
|
|
148
|
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
-
appropriateness of using or redistributing the Work and assume any
|
|
151
|
-
risks associated with Your exercise of permissions under this License.
|
|
152
|
-
|
|
153
|
-
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
-
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
-
unless required by applicable law (such as deliberate and grossly
|
|
156
|
-
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
-
liable to You for damages, including any direct, indirect, special,
|
|
158
|
-
incidental, or consequential damages of any character arising as a
|
|
159
|
-
result of this License or out of the use or inability to use the
|
|
160
|
-
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
-
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
-
other commercial damages or losses), even if such Contributor
|
|
163
|
-
has been advised of the possibility of such damages.
|
|
164
|
-
|
|
165
|
-
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
-
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
-
or other liability obligations and/or rights consistent with this
|
|
169
|
-
License. However, in accepting such obligations, You may act only
|
|
170
|
-
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
-
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
-
defend, and hold each Contributor harmless for any liability
|
|
173
|
-
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
-
of your accepting any such warranty or additional liability.
|
|
175
|
-
|
|
176
|
-
END OF TERMS AND CONDITIONS
|