@pi-ohm/references 0.6.4-dev.27705031416.1.1d8069f
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 +76 -0
- package/SPEC.md +186 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.js +261 -0
- package/dist/config.d.ts +71 -0
- package/dist/config.js +119 -0
- package/dist/extension.d.ts +32 -0
- package/dist/extension.js +414 -0
- package/dist/references.d.ts +54 -0
- package/dist/references.js +180 -0
- package/dist/repository.d.ts +40 -0
- package/dist/repository.js +165 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# @pi-ohm/references
|
|
2
|
+
|
|
3
|
+
Project references for Pi. Adds named local directories and Git repositories to
|
|
4
|
+
agent system prompt guidance, with `@alias` autocomplete in the TUI.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pi install npm:@pi-ohm/references
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Config lives under `references` in `.pi/ohm.json` or global `ohm.json`.
|
|
11
|
+
|
|
12
|
+
```jsonc
|
|
13
|
+
{
|
|
14
|
+
"references": {
|
|
15
|
+
"docs": {
|
|
16
|
+
"path": "../product-docs",
|
|
17
|
+
"description": "Use for product behavior and documentation conventions",
|
|
18
|
+
},
|
|
19
|
+
"sdk": {
|
|
20
|
+
"repository": "anomalyco/opencode-sdk-js",
|
|
21
|
+
"branch": "main",
|
|
22
|
+
"description": "Use for JavaScript SDK implementation details",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
No model-facing tool is registered. Agents use existing Pi tools with absolute
|
|
29
|
+
paths from system guidance and per-prompt reference invocation blocks.
|
|
30
|
+
|
|
31
|
+
## Prompt references
|
|
32
|
+
|
|
33
|
+
Autocomplete inserts the reference alias, not the absolute path. If you select
|
|
34
|
+
`@sdk`, the editor keeps `@sdk`.
|
|
35
|
+
|
|
36
|
+
When a prompt contains `@alias` or `@alias/path`, the package adds a collapsed
|
|
37
|
+
custom message for the agent with the resolved root path and grouped mentioned
|
|
38
|
+
paths:
|
|
39
|
+
|
|
40
|
+
```xml
|
|
41
|
+
<reference name="sdk" token="@sdk" path="/home/user/.local/share/pi-ohm/agent/references/repos/github.com/anomalyco/opencode-sdk-js">
|
|
42
|
+
The user inserted this project reference with @ autocomplete. Use the resolved path when reading or searching this referenced project.
|
|
43
|
+
|
|
44
|
+
Use for JavaScript SDK implementation details
|
|
45
|
+
|
|
46
|
+
<reference_files>
|
|
47
|
+
<file token="@sdk/packages/client" relative_path="packages/client" path="/home/user/.local/share/pi-ohm/agent/references/repos/github.com/anomalyco/opencode-sdk-js/packages/client" />
|
|
48
|
+
<file token="@sdk/README.md" relative_path="README.md" path="/home/user/.local/share/pi-ohm/agent/references/repos/github.com/anomalyco/opencode-sdk-js/README.md" />
|
|
49
|
+
</reference_files>
|
|
50
|
+
</reference>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The TUI shows this as a compact `[ref] @alias` block. Use `ctrl+o` to expand it.
|
|
54
|
+
Nested autocomplete rows show relative descriptions like `packages/client`, not
|
|
55
|
+
the full cache path. The XML still includes the exact absolute paths.
|
|
56
|
+
|
|
57
|
+
Read-only path tools (`read`, `ls`, `grep`, `find`) also accept `@alias` and
|
|
58
|
+
`@alias/path`; the extension rewrites those tool path arguments to absolute
|
|
59
|
+
paths before execution.
|
|
60
|
+
|
|
61
|
+
## Quirks
|
|
62
|
+
|
|
63
|
+
Pi autocomplete providers are wrapper-ordered. A provider registered later can
|
|
64
|
+
handle `@...` first and stop delegation, so file-search extensions can hide
|
|
65
|
+
reference aliases.
|
|
66
|
+
|
|
67
|
+
`@ff-labs/pi-fff` does this intentionally for FFF-backed `@` file search: it
|
|
68
|
+
queries FFF first and only delegates when FFF returns no results. Since
|
|
69
|
+
`@opencode` can fuzzy-match normal project files, references may never see the
|
|
70
|
+
request if pi-fff is the outer wrapper.
|
|
71
|
+
|
|
72
|
+
This package remedies that by bridging `ctx.ui.addAutocompleteProvider` during
|
|
73
|
+
session startup. Providers registered after references are wrapped with
|
|
74
|
+
references outside them, and providers registered before references are still
|
|
75
|
+
wrapped by the normal registration path. Reference matches are merged first, so
|
|
76
|
+
`@alias` entries stay visible without disabling pi-fff.
|
package/SPEC.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# @pi-ohm/references spec
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Add OpenCode-style project references to Pi as a small Pi package. A reference is
|
|
6
|
+
a named local directory or Git repository that Pi can advertise to the agent as
|
|
7
|
+
readable supporting context.
|
|
8
|
+
|
|
9
|
+
References are not slash commands. Slash commands are user-only in Pi, so the
|
|
10
|
+
agent must receive references through normal system prompt guidance and existing
|
|
11
|
+
tools.
|
|
12
|
+
|
|
13
|
+
## OpenCode behavior to replicate
|
|
14
|
+
|
|
15
|
+
OpenCode exposes no dedicated model-facing reference tool. References are wired
|
|
16
|
+
through:
|
|
17
|
+
|
|
18
|
+
1. config parsing,
|
|
19
|
+
2. local/Git materialization,
|
|
20
|
+
3. system prompt guidance for references with descriptions,
|
|
21
|
+
4. UI autocomplete and file attachments,
|
|
22
|
+
5. external-directory permission allowlisting.
|
|
23
|
+
|
|
24
|
+
This package should follow the same shape for Pi:
|
|
25
|
+
|
|
26
|
+
- Do not register a model-facing `references` tool.
|
|
27
|
+
- Do not make `/commands` part of the agent contract.
|
|
28
|
+
- Inject resolved references into `before_agent_start` system prompt text.
|
|
29
|
+
- Use Pi's existing read/bash/edit tools by giving the model absolute paths.
|
|
30
|
+
|
|
31
|
+
## Config
|
|
32
|
+
|
|
33
|
+
Config namespace: `references` in `.pi/ohm.json` and global `ohm.json` through
|
|
34
|
+
`@pi-ohm/core/config`.
|
|
35
|
+
|
|
36
|
+
Shape:
|
|
37
|
+
|
|
38
|
+
```jsonc
|
|
39
|
+
{
|
|
40
|
+
"references": {
|
|
41
|
+
"docs": {
|
|
42
|
+
"path": "../product-docs",
|
|
43
|
+
"description": "Use for product behavior and docs conventions",
|
|
44
|
+
},
|
|
45
|
+
"sdk": {
|
|
46
|
+
"repository": "anomalyco/opencode-sdk-js",
|
|
47
|
+
"branch": "main",
|
|
48
|
+
"description": "Use for JavaScript SDK implementation details",
|
|
49
|
+
},
|
|
50
|
+
"local-shorthand": "./docs",
|
|
51
|
+
"git-shorthand": "owner/repo",
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Entries:
|
|
57
|
+
|
|
58
|
+
- `string | { path, description?, hidden? } | { repository, branch?, description?, hidden? }`
|
|
59
|
+
- string values starting with `.`, `/`, or `~` are local paths
|
|
60
|
+
- all other string values are Git repository references
|
|
61
|
+
- aliases cannot be empty or contain `/`, whitespace, backticks, or commas
|
|
62
|
+
- invalid aliases are ignored
|
|
63
|
+
|
|
64
|
+
Path resolution:
|
|
65
|
+
|
|
66
|
+
- `~/x` resolves against user home
|
|
67
|
+
- absolute paths stay absolute
|
|
68
|
+
- relative paths resolve from the current Pi cwd because `@pi-ohm/core/config`
|
|
69
|
+
does not expose per-file config origins
|
|
70
|
+
|
|
71
|
+
## Git repository references
|
|
72
|
+
|
|
73
|
+
Accept:
|
|
74
|
+
|
|
75
|
+
- `owner/repo`
|
|
76
|
+
- `github:owner/repo`
|
|
77
|
+
- `github.com/owner/repo`
|
|
78
|
+
- `gitlab.com/group/repo`
|
|
79
|
+
- `git@github.com:owner/repo.git`
|
|
80
|
+
- `https://github.com/owner/repo.git`
|
|
81
|
+
- `ssh://git@github.com/owner/repo`
|
|
82
|
+
|
|
83
|
+
Normalization:
|
|
84
|
+
|
|
85
|
+
- trim input
|
|
86
|
+
- remove leading `git+`
|
|
87
|
+
- strip fragments after `#`
|
|
88
|
+
- strip trailing slashes
|
|
89
|
+
- strip `.git` from path segments
|
|
90
|
+
|
|
91
|
+
Safety:
|
|
92
|
+
|
|
93
|
+
- host cannot be empty, start with `-`, or contain whitespace, `/`, or `\`
|
|
94
|
+
- path segment cannot be `.`, `..`, contain `:`, whitespace, `/`, or `\`
|
|
95
|
+
- branch can contain alphanumeric, `/`, `_`, `.`, and `-`
|
|
96
|
+
- branch cannot start with `-` or contain `..`
|
|
97
|
+
- `file:` repositories are not supported through `repository`; use `path`
|
|
98
|
+
|
|
99
|
+
Cache path:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
${XDG_DATA_HOME:-~/.local/share}/pi-ohm/agent/references/repos/<host>/<path>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Materialization:
|
|
106
|
+
|
|
107
|
+
- Git refs appear in guidance immediately at their deterministic cache path
|
|
108
|
+
- clone/fetch happens asynchronously on `session_start`
|
|
109
|
+
- existing cache with mismatched origin is deleted and recloned
|
|
110
|
+
- clone uses `git clone --depth 100 [--branch branch] -- remote target`
|
|
111
|
+
- refresh uses `git fetch --all --prune` and `git reset --hard`
|
|
112
|
+
|
|
113
|
+
## Runtime model
|
|
114
|
+
|
|
115
|
+
Runtime reference info:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
type ReferenceInfo = {
|
|
119
|
+
name: string;
|
|
120
|
+
path: string;
|
|
121
|
+
description?: string;
|
|
122
|
+
hidden?: boolean;
|
|
123
|
+
source:
|
|
124
|
+
| { type: "local"; path: string; description?: string; hidden?: boolean }
|
|
125
|
+
| { type: "git"; repository: string; branch?: string; description?: string; hidden?: boolean };
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`hidden` only affects UI autocomplete. A hidden reference with a description is
|
|
130
|
+
still included in agent guidance.
|
|
131
|
+
|
|
132
|
+
Only references with descriptions are advertised in system prompt text.
|
|
133
|
+
|
|
134
|
+
## System prompt guidance
|
|
135
|
+
|
|
136
|
+
Append to `before_agent_start` only when at least one described reference exists:
|
|
137
|
+
|
|
138
|
+
```xml
|
|
139
|
+
Project references provide additional directories that can be accessed when relevant.
|
|
140
|
+
<available_references>
|
|
141
|
+
<reference>
|
|
142
|
+
<name>docs</name>
|
|
143
|
+
<path>/absolute/path/to/docs</path>
|
|
144
|
+
<description>Use for product behavior and docs conventions</description>
|
|
145
|
+
</reference>
|
|
146
|
+
</available_references>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Sort by reference name for deterministic prompt text.
|
|
150
|
+
|
|
151
|
+
## User UI
|
|
152
|
+
|
|
153
|
+
Initial package can include:
|
|
154
|
+
|
|
155
|
+
- `/ohm-references` user command to inspect resolved references and cache status
|
|
156
|
+
- optional autocomplete provider for `@alias` and `@alias/path` that keeps the
|
|
157
|
+
alias in the editor, shows nested picker descriptions as relative paths, and
|
|
158
|
+
emits a collapsed custom reference message on submit
|
|
159
|
+
- grouped XML invocation blocks: one `<reference>` per alias with mentioned
|
|
160
|
+
`@alias/path` entries under `<reference_files>`
|
|
161
|
+
- read-only tool path rewriting for `read`, `ls`, `grep`, and `find` so models
|
|
162
|
+
may use `@alias` path arguments without a dedicated reference tool
|
|
163
|
+
|
|
164
|
+
The custom message renderer should mirror Pi's skill invocation shape: compact
|
|
165
|
+
by default, expandable with the normal tool-output expansion key.
|
|
166
|
+
|
|
167
|
+
## Agent tools
|
|
168
|
+
|
|
169
|
+
This package exposes no new model-facing tool. The model uses existing Pi tools:
|
|
170
|
+
|
|
171
|
+
- `read` to inspect files/directories by absolute path
|
|
172
|
+
- `read`, `ls`, `grep`, and `find` may receive `@alias` paths, rewritten before
|
|
173
|
+
execution
|
|
174
|
+
- `bash` for allowed shell inspection
|
|
175
|
+
- `edit`/`write` only if normal Pi permissions allow mutation
|
|
176
|
+
|
|
177
|
+
References do not grant mutation permission.
|
|
178
|
+
|
|
179
|
+
## Out of scope for first implementation
|
|
180
|
+
|
|
181
|
+
- DB-backed clone metadata
|
|
182
|
+
- permission-policy mutation
|
|
183
|
+
- background file watching
|
|
184
|
+
- remote reference registries
|
|
185
|
+
- branch-per-alias multi-checkout support for the same repo
|
|
186
|
+
- rich TUI panels
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ReferencesError, RemoteRepositoryReference } from "./repository.js";
|
|
2
|
+
import { Result } from "better-result";
|
|
3
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
|
|
5
|
+
//#region src/cache.d.ts
|
|
6
|
+
interface RepositoryCacheResult {
|
|
7
|
+
readonly repository: string;
|
|
8
|
+
readonly host: string;
|
|
9
|
+
readonly remote: string;
|
|
10
|
+
readonly localPath: string;
|
|
11
|
+
readonly status: "cached" | "cloned" | "refreshed";
|
|
12
|
+
readonly head?: string;
|
|
13
|
+
readonly branch?: string;
|
|
14
|
+
}
|
|
15
|
+
interface EnsureRepositoryInput {
|
|
16
|
+
readonly pi: Pick<ExtensionAPI, "exec">;
|
|
17
|
+
readonly reference: RemoteRepositoryReference;
|
|
18
|
+
readonly branch?: string;
|
|
19
|
+
readonly refresh?: boolean;
|
|
20
|
+
readonly signal?: AbortSignal;
|
|
21
|
+
readonly root?: string;
|
|
22
|
+
}
|
|
23
|
+
declare function defaultReferencesCacheRoot(): string;
|
|
24
|
+
declare function ensureRepository(input: EnsureRepositoryInput): Promise<Result<RepositoryCacheResult, ReferencesError>>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { EnsureRepositoryInput, RepositoryCacheResult, defaultReferencesCacheRoot, ensureRepository };
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { ReferencesError, parseRepositoryReference, repositoryCachePath, sameRepositoryReference, validateBranch } from "./repository.js";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Result } from "better-result";
|
|
5
|
+
import { resolveOhmAgentDataHome } from "@pi-ohm/core/paths";
|
|
6
|
+
|
|
7
|
+
//#region src/cache.ts
|
|
8
|
+
const GIT_TIMEOUT_MS = 12e4;
|
|
9
|
+
function defaultReferencesCacheRoot() {
|
|
10
|
+
return path.join(resolveOhmAgentDataHome(), "references", "repos");
|
|
11
|
+
}
|
|
12
|
+
function causeMessage(cause) {
|
|
13
|
+
if (cause instanceof Error && cause.message.trim().length > 0) return cause.message;
|
|
14
|
+
if (typeof cause === "string" && cause.trim().length > 0) return cause;
|
|
15
|
+
return String(cause);
|
|
16
|
+
}
|
|
17
|
+
function cacheError(input) {
|
|
18
|
+
return new ReferencesError(input);
|
|
19
|
+
}
|
|
20
|
+
async function exists(target) {
|
|
21
|
+
return fs.access(target).then(() => true, () => false);
|
|
22
|
+
}
|
|
23
|
+
async function cacheOperation(input) {
|
|
24
|
+
return Result.tryPromise({
|
|
25
|
+
try: input.run,
|
|
26
|
+
catch: (cause) => cacheError({
|
|
27
|
+
code: "cache_operation_failed",
|
|
28
|
+
path: input.target,
|
|
29
|
+
message: `${input.stage} failed for ${input.target}: ${causeMessage(cause)}`,
|
|
30
|
+
cause
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async function git(input) {
|
|
35
|
+
const result = await Result.tryPromise({
|
|
36
|
+
try: () => input.pi.exec("git", [...input.args], {
|
|
37
|
+
cwd: input.cwd,
|
|
38
|
+
timeout: GIT_TIMEOUT_MS,
|
|
39
|
+
...input.signal ? { signal: input.signal } : {}
|
|
40
|
+
}),
|
|
41
|
+
catch: (cause) => cacheError({
|
|
42
|
+
code: "git_command_failed",
|
|
43
|
+
repository: input.repository,
|
|
44
|
+
message: `git ${input.args.join(" ")} failed: ${causeMessage(cause)}`,
|
|
45
|
+
cause
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
if (Result.isError(result)) return result;
|
|
49
|
+
if (result.value.code === 0) return result;
|
|
50
|
+
return Result.err(cacheError({
|
|
51
|
+
code: "git_command_failed",
|
|
52
|
+
repository: input.repository,
|
|
53
|
+
message: result.value.stderr.trim() || result.value.stdout.trim() || `git ${input.args.join(" ")} failed with exit code ${result.value.code}`
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
async function gitOptional(input) {
|
|
57
|
+
const result = await Result.tryPromise({
|
|
58
|
+
try: () => input.pi.exec("git", [...input.args], {
|
|
59
|
+
cwd: input.cwd,
|
|
60
|
+
timeout: GIT_TIMEOUT_MS,
|
|
61
|
+
...input.signal ? { signal: input.signal } : {}
|
|
62
|
+
}),
|
|
63
|
+
catch: (cause) => cause
|
|
64
|
+
});
|
|
65
|
+
if (Result.isError(result)) return undefined;
|
|
66
|
+
if (result.value.code !== 0) return undefined;
|
|
67
|
+
const trimmed = result.value.stdout.trim();
|
|
68
|
+
if (trimmed.length === 0) return undefined;
|
|
69
|
+
return trimmed;
|
|
70
|
+
}
|
|
71
|
+
function statusForRepository(input) {
|
|
72
|
+
if (!input.reuse) return "cloned";
|
|
73
|
+
if (input.branchMatches === false || input.refresh) return "refreshed";
|
|
74
|
+
return "cached";
|
|
75
|
+
}
|
|
76
|
+
async function resetTarget(input) {
|
|
77
|
+
if (input.branch) return `origin/${input.branch}`;
|
|
78
|
+
const remoteHead = await gitOptional({
|
|
79
|
+
pi: input.pi,
|
|
80
|
+
cwd: input.cwd,
|
|
81
|
+
args: ["symbolic-ref", "refs/remotes/origin/HEAD"],
|
|
82
|
+
signal: input.signal
|
|
83
|
+
});
|
|
84
|
+
if (remoteHead) return remoteHead.replace(/^refs\/remotes\//, "");
|
|
85
|
+
const branch = await gitOptional({
|
|
86
|
+
pi: input.pi,
|
|
87
|
+
cwd: input.cwd,
|
|
88
|
+
args: [
|
|
89
|
+
"symbolic-ref",
|
|
90
|
+
"--quiet",
|
|
91
|
+
"--short",
|
|
92
|
+
"HEAD"
|
|
93
|
+
],
|
|
94
|
+
signal: input.signal
|
|
95
|
+
});
|
|
96
|
+
if (branch) return `origin/${branch}`;
|
|
97
|
+
return "HEAD";
|
|
98
|
+
}
|
|
99
|
+
async function ensureRepository(input) {
|
|
100
|
+
if (input.branch) {
|
|
101
|
+
const branch = validateBranch(input.branch);
|
|
102
|
+
if (Result.isError(branch)) return Result.err(branch.error);
|
|
103
|
+
}
|
|
104
|
+
const root = input.root ?? defaultReferencesCacheRoot();
|
|
105
|
+
const repository = input.reference.label;
|
|
106
|
+
const localPath = repositoryCachePath(root, input.reference);
|
|
107
|
+
const cloneTargetResult = parseRepositoryReference(input.reference.remote);
|
|
108
|
+
const cloneTarget = Result.isOk(cloneTargetResult) ? cloneTargetResult.value : input.reference;
|
|
109
|
+
const ensured = await cacheOperation({
|
|
110
|
+
stage: "ensure repository cache directory",
|
|
111
|
+
target: localPath,
|
|
112
|
+
run: () => fs.mkdir(path.dirname(localPath), { recursive: true })
|
|
113
|
+
});
|
|
114
|
+
if (Result.isError(ensured)) return Result.err(ensured.error);
|
|
115
|
+
const pathExists = await exists(localPath);
|
|
116
|
+
const hasGitDir = await exists(path.join(localPath, ".git"));
|
|
117
|
+
const origin = hasGitDir ? await gitOptional({
|
|
118
|
+
pi: input.pi,
|
|
119
|
+
cwd: localPath,
|
|
120
|
+
args: [
|
|
121
|
+
"config",
|
|
122
|
+
"--get",
|
|
123
|
+
"remote.origin.url"
|
|
124
|
+
],
|
|
125
|
+
signal: input.signal
|
|
126
|
+
}) : undefined;
|
|
127
|
+
const originReference = origin ? parseRepositoryReference(origin) : undefined;
|
|
128
|
+
const reuse = hasGitDir && originReference !== undefined && Result.isOk(originReference) && sameRepositoryReference(originReference.value, cloneTarget);
|
|
129
|
+
if (pathExists && !reuse) {
|
|
130
|
+
const removed = await cacheOperation({
|
|
131
|
+
stage: "remove stale repository cache",
|
|
132
|
+
target: localPath,
|
|
133
|
+
run: () => fs.rm(localPath, {
|
|
134
|
+
recursive: true,
|
|
135
|
+
force: true
|
|
136
|
+
})
|
|
137
|
+
});
|
|
138
|
+
if (Result.isError(removed)) return Result.err(removed.error);
|
|
139
|
+
}
|
|
140
|
+
const currentBranch = reuse ? await gitOptional({
|
|
141
|
+
pi: input.pi,
|
|
142
|
+
cwd: localPath,
|
|
143
|
+
args: [
|
|
144
|
+
"symbolic-ref",
|
|
145
|
+
"--quiet",
|
|
146
|
+
"--short",
|
|
147
|
+
"HEAD"
|
|
148
|
+
],
|
|
149
|
+
signal: input.signal
|
|
150
|
+
}) : undefined;
|
|
151
|
+
const status = statusForRepository({
|
|
152
|
+
reuse,
|
|
153
|
+
refresh: input.refresh,
|
|
154
|
+
...input.branch ? { branchMatches: currentBranch === input.branch } : {}
|
|
155
|
+
});
|
|
156
|
+
if (status === "cloned") {
|
|
157
|
+
const cloned = await git({
|
|
158
|
+
pi: input.pi,
|
|
159
|
+
cwd: path.dirname(localPath),
|
|
160
|
+
args: [
|
|
161
|
+
"clone",
|
|
162
|
+
"--depth",
|
|
163
|
+
"100",
|
|
164
|
+
...input.branch ? ["--branch", input.branch] : [],
|
|
165
|
+
"--",
|
|
166
|
+
input.reference.remote,
|
|
167
|
+
localPath
|
|
168
|
+
],
|
|
169
|
+
repository,
|
|
170
|
+
signal: input.signal
|
|
171
|
+
});
|
|
172
|
+
if (Result.isError(cloned)) return Result.err(cloned.error);
|
|
173
|
+
}
|
|
174
|
+
if (status === "refreshed") {
|
|
175
|
+
const fetched = await git({
|
|
176
|
+
pi: input.pi,
|
|
177
|
+
cwd: localPath,
|
|
178
|
+
args: [
|
|
179
|
+
"fetch",
|
|
180
|
+
"--all",
|
|
181
|
+
"--prune"
|
|
182
|
+
],
|
|
183
|
+
repository,
|
|
184
|
+
signal: input.signal
|
|
185
|
+
});
|
|
186
|
+
if (Result.isError(fetched)) return Result.err(fetched.error);
|
|
187
|
+
if (input.branch) {
|
|
188
|
+
const branchFetched = await git({
|
|
189
|
+
pi: input.pi,
|
|
190
|
+
cwd: localPath,
|
|
191
|
+
args: [
|
|
192
|
+
"fetch",
|
|
193
|
+
"origin",
|
|
194
|
+
`+refs/heads/${input.branch}:refs/remotes/origin/${input.branch}`
|
|
195
|
+
],
|
|
196
|
+
repository,
|
|
197
|
+
signal: input.signal
|
|
198
|
+
});
|
|
199
|
+
if (Result.isError(branchFetched)) return Result.err(branchFetched.error);
|
|
200
|
+
const checkedOut = await git({
|
|
201
|
+
pi: input.pi,
|
|
202
|
+
cwd: localPath,
|
|
203
|
+
args: [
|
|
204
|
+
"checkout",
|
|
205
|
+
"-B",
|
|
206
|
+
input.branch,
|
|
207
|
+
`origin/${input.branch}`
|
|
208
|
+
],
|
|
209
|
+
repository,
|
|
210
|
+
signal: input.signal
|
|
211
|
+
});
|
|
212
|
+
if (Result.isError(checkedOut)) return Result.err(checkedOut.error);
|
|
213
|
+
}
|
|
214
|
+
const reset = await git({
|
|
215
|
+
pi: input.pi,
|
|
216
|
+
cwd: localPath,
|
|
217
|
+
args: [
|
|
218
|
+
"reset",
|
|
219
|
+
"--hard",
|
|
220
|
+
await resetTarget({
|
|
221
|
+
pi: input.pi,
|
|
222
|
+
cwd: localPath,
|
|
223
|
+
branch: input.branch,
|
|
224
|
+
...input.signal ? { signal: input.signal } : {}
|
|
225
|
+
})
|
|
226
|
+
],
|
|
227
|
+
repository,
|
|
228
|
+
signal: input.signal
|
|
229
|
+
});
|
|
230
|
+
if (Result.isError(reset)) return Result.err(reset.error);
|
|
231
|
+
}
|
|
232
|
+
const head = await gitOptional({
|
|
233
|
+
pi: input.pi,
|
|
234
|
+
cwd: localPath,
|
|
235
|
+
args: ["rev-parse", "HEAD"],
|
|
236
|
+
signal: input.signal
|
|
237
|
+
});
|
|
238
|
+
const branch = await gitOptional({
|
|
239
|
+
pi: input.pi,
|
|
240
|
+
cwd: localPath,
|
|
241
|
+
args: [
|
|
242
|
+
"symbolic-ref",
|
|
243
|
+
"--quiet",
|
|
244
|
+
"--short",
|
|
245
|
+
"HEAD"
|
|
246
|
+
],
|
|
247
|
+
signal: input.signal
|
|
248
|
+
});
|
|
249
|
+
return Result.ok({
|
|
250
|
+
repository,
|
|
251
|
+
host: input.reference.host,
|
|
252
|
+
remote: input.reference.remote,
|
|
253
|
+
localPath,
|
|
254
|
+
status,
|
|
255
|
+
...head ? { head } : {},
|
|
256
|
+
...branch ? { branch } : {}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
//#endregion
|
|
261
|
+
export { defaultReferencesCacheRoot, ensureRepository };
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { StaticDecode, Type } from "typebox";
|
|
2
|
+
|
|
3
|
+
//#region src/config.d.ts
|
|
4
|
+
declare const LocalReferenceConfigSchema: Type.TObject<{
|
|
5
|
+
path: Type.TString;
|
|
6
|
+
description: Type.TOptional<Type.TString>;
|
|
7
|
+
hidden: Type.TOptional<Type.TBoolean>;
|
|
8
|
+
}>;
|
|
9
|
+
declare const GitReferenceConfigSchema: Type.TObject<{
|
|
10
|
+
repository: Type.TString;
|
|
11
|
+
branch: Type.TOptional<Type.TString>;
|
|
12
|
+
description: Type.TOptional<Type.TString>;
|
|
13
|
+
hidden: Type.TOptional<Type.TBoolean>;
|
|
14
|
+
}>;
|
|
15
|
+
declare const ReferenceEntryConfigSchema: Type.TUnion<[Type.TString, Type.TObject<{
|
|
16
|
+
path: Type.TString;
|
|
17
|
+
description: Type.TOptional<Type.TString>;
|
|
18
|
+
hidden: Type.TOptional<Type.TBoolean>;
|
|
19
|
+
}>, Type.TObject<{
|
|
20
|
+
repository: Type.TString;
|
|
21
|
+
branch: Type.TOptional<Type.TString>;
|
|
22
|
+
description: Type.TOptional<Type.TString>;
|
|
23
|
+
hidden: Type.TOptional<Type.TBoolean>;
|
|
24
|
+
}>]>;
|
|
25
|
+
declare const ReferencesConfigSchema: Type.TRecord<"^.*$", Type.TUnion<[Type.TString, Type.TObject<{
|
|
26
|
+
path: Type.TString;
|
|
27
|
+
description: Type.TOptional<Type.TString>;
|
|
28
|
+
hidden: Type.TOptional<Type.TBoolean>;
|
|
29
|
+
}>, Type.TObject<{
|
|
30
|
+
repository: Type.TString;
|
|
31
|
+
branch: Type.TOptional<Type.TString>;
|
|
32
|
+
description: Type.TOptional<Type.TString>;
|
|
33
|
+
hidden: Type.TOptional<Type.TBoolean>;
|
|
34
|
+
}>]>>;
|
|
35
|
+
type ReferencesConfigPatch = StaticDecode<typeof ReferencesConfigSchema>;
|
|
36
|
+
interface LocalReferenceConfig {
|
|
37
|
+
readonly path: string;
|
|
38
|
+
readonly description?: string;
|
|
39
|
+
readonly hidden?: boolean;
|
|
40
|
+
}
|
|
41
|
+
interface GitReferenceConfig {
|
|
42
|
+
readonly repository: string;
|
|
43
|
+
readonly branch?: string;
|
|
44
|
+
readonly description?: string;
|
|
45
|
+
readonly hidden?: boolean;
|
|
46
|
+
}
|
|
47
|
+
type ReferenceEntryConfig = string | LocalReferenceConfig | GitReferenceConfig;
|
|
48
|
+
type ReferencesRuntimeConfig = Readonly<Record<string, ReferenceEntryConfig>>;
|
|
49
|
+
interface JsonMap {
|
|
50
|
+
readonly [key: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
declare const referencesConfigModule: import("@pi-ohm/core/config").ExtensionConfigModule<Readonly<Record<string, ReferenceEntryConfig>>, Type.TRecord<"^.*$", Type.TUnion<[Type.TString, Type.TObject<{
|
|
53
|
+
path: Type.TString;
|
|
54
|
+
description: Type.TOptional<Type.TString>;
|
|
55
|
+
hidden: Type.TOptional<Type.TBoolean>;
|
|
56
|
+
}>, Type.TObject<{
|
|
57
|
+
repository: Type.TString;
|
|
58
|
+
branch: Type.TOptional<Type.TString>;
|
|
59
|
+
description: Type.TOptional<Type.TString>;
|
|
60
|
+
hidden: Type.TOptional<Type.TBoolean>;
|
|
61
|
+
}>]>>>;
|
|
62
|
+
declare function isJsonMap(value: unknown): value is JsonMap;
|
|
63
|
+
declare function validReferenceAlias(name: string): boolean;
|
|
64
|
+
declare function mergeReferencesConfig(current: ReferencesRuntimeConfig | undefined, patch: ReferencesConfigPatch): ReferencesRuntimeConfig;
|
|
65
|
+
declare function isReferencesRuntimeConfig(value: unknown): value is ReferencesRuntimeConfig;
|
|
66
|
+
declare function loadReferencesConfig(cwd: string): Promise<import("better-result").Err<never, import("@pi-ohm/core/config").ExtensionConfigRuntimeError> | import("better-result").Ok<{
|
|
67
|
+
loaded: import("@pi-ohm/core/config").LoadedExtensionConfig;
|
|
68
|
+
config: Readonly<Record<string, ReferenceEntryConfig>>;
|
|
69
|
+
}, never>>;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { GitReferenceConfig, GitReferenceConfigSchema, LocalReferenceConfig, LocalReferenceConfigSchema, ReferenceEntryConfig, ReferenceEntryConfigSchema, ReferencesConfigSchema, ReferencesRuntimeConfig, isJsonMap, isReferencesRuntimeConfig, loadReferencesConfig, mergeReferencesConfig, referencesConfigModule, validReferenceAlias };
|