@coldtea/pr-lens-cli 0.1.0 → 0.1.2
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/LICENSE +1 -1
- package/README.md +26 -16
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +6 -2
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/render.d.ts +1 -1
- package/dist/commands/render.d.ts.map +1 -1
- package/dist/commands/render.js +3 -1
- package/dist/commands/render.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace.d.ts +36 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +158 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +4 -4
- package/src/commands/analyze.ts +6 -2
- package/src/commands/render.ts +4 -1
- package/src/version.ts +1 -1
- package/src/workspace.ts +190 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
PR Lens on the command line. It reads a diff with your own model key, checks the result against the contract, and hands you the pull request comment.
|
|
4
4
|
|
|
5
|
-
MIT ©
|
|
5
|
+
MIT © Coldtea AI.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npx @coldtea/pr-lens-cli analyze --base origin/main
|
|
@@ -10,10 +10,10 @@ npx @coldtea/pr-lens-cli analyze --base origin/main
|
|
|
10
10
|
|
|
11
11
|
## Bring your own key
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Only `analyze` talks to a model; `render`, `comment`, `validate` and `export` never do. Its key is read from the environment, never from a flag: a flag lands in shell history and in the log of whatever CI runs it. The diff goes to the provider you name and nowhere else: there is no PR Lens service in this path.
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
export GEMINI_API_KEY=…
|
|
16
|
+
export GEMINI_API_KEY=… # Gemini is the default, not a requirement
|
|
17
17
|
pr-lens analyze --base origin/main --head HEAD
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -35,6 +35,14 @@ pr-lens analyze --base origin/main \
|
|
|
35
35
|
--api-key-env OLLAMA_KEY
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
## Where the files go
|
|
39
|
+
|
|
40
|
+
`analyze` and `render` write to `.pr-lens/` in the current directory, and the first write adds `.pr-lens/` to the repository's `.gitignore` alongside a README saying what the directory holds.
|
|
41
|
+
|
|
42
|
+
None of it is meant to be committed. The SVGs, the document and the manifest are rebuilt from the diff whenever anyone wants them, and a diagram of a pull request that merged months ago is worse than no diagram. What is meant to last is the comment on the pull request, and the map `export` writes.
|
|
43
|
+
|
|
44
|
+
`--out` overrides the location on every command that writes. Point it somewhere else and PR Lens leaves your `.gitignore` alone: that directory is yours to decide about.
|
|
45
|
+
|
|
38
46
|
## The commands
|
|
39
47
|
|
|
40
48
|
### `analyze`
|
|
@@ -42,22 +50,24 @@ pr-lens analyze --base origin/main \
|
|
|
42
50
|
Diff in, graph document out.
|
|
43
51
|
|
|
44
52
|
```bash
|
|
45
|
-
pr-lens analyze --base origin/main --pr 42
|
|
53
|
+
pr-lens analyze --base origin/main --pr 42
|
|
46
54
|
```
|
|
47
55
|
|
|
56
|
+
The document lands in `.pr-lens/graph.json` unless `--out` says otherwise.
|
|
57
|
+
|
|
48
58
|
The base is the merge base of the two refs, not the tip of the base branch: a diff against the tip would blame this pull request for every change made on the base branch since it forked.
|
|
49
59
|
|
|
50
60
|
The commit shas, the repository, the pull request number and the line counts are filled in from the repository itself, and anything the model writes there is discarded. A fact the model is free to restate is a fact that eventually disagrees with itself.
|
|
51
61
|
|
|
52
|
-
The answer is parsed against the contract. If it fails, the validation errors
|
|
62
|
+
The answer is parsed against the contract. If it fails, the validation errors, paths and all, go back to the model once, and only once: a model that cannot fix a named path in one round does not fix it in three. `--dry-run` reports what would be sent and sends nothing.
|
|
53
63
|
|
|
54
64
|
### `render`
|
|
55
65
|
|
|
56
66
|
```bash
|
|
57
|
-
pr-lens render pr-lens/graph.json
|
|
67
|
+
pr-lens render .pr-lens/graph.json
|
|
58
68
|
```
|
|
59
69
|
|
|
60
|
-
Draws the document as self-contained light and dark SVGs
|
|
70
|
+
Draws the document as self-contained light and dark SVGs (one pair per drill-down section per lens, or one pair per lens when the document has no sections), and writes two files beside them: `manifest.json`, which says what was drawn and under which file name, and `drawn.graph.json`, the document those pictures actually show.
|
|
61
71
|
|
|
62
72
|
Both matter to what comes next. The manifest is where `comment` gets its file names, so neither command re-derives the other's. And `drawn.graph.json` exists because this is where corrections are applied: excluding a node can empty out a whole drill-down section, and the renderer then draws no picture for it. A comment composed from the document that went *in* would announce a section that came out of nothing.
|
|
63
73
|
|
|
@@ -66,16 +76,16 @@ The SVGs carry no script and no external reference, and the same document render
|
|
|
66
76
|
### `comment`
|
|
67
77
|
|
|
68
78
|
```bash
|
|
69
|
-
pr-lens comment --graph pr-lens/drawn.graph.json --manifest pr-lens/manifest.json \
|
|
79
|
+
pr-lens comment --graph .pr-lens/drawn.graph.json --manifest .pr-lens/manifest.json \
|
|
70
80
|
--asset-base-url https://raw.githubusercontent.com/owner/repo/pr-lens/42
|
|
71
81
|
```
|
|
72
82
|
|
|
73
|
-
Composes the markdown
|
|
83
|
+
Composes the markdown (the `<picture>` pairs that read in both GitHub themes, the headline chips, the nested `<details>` tree) and prints it. Each diagram links to itself: a comment column is about 830 pixels wide and a system with several lanes is several times that, so it arrives scaled to fit and one click gives a reader the size the labels were drawn at. The two files have to belong to each other: the manifest records the hash of the document it came from, and a mismatched pair is refused rather than composed into a comment describing diagrams nobody drew. It posts nothing; posting is the caller's business, and `--print-marker` gives that caller the hidden marker that identifies an existing comment to update.
|
|
74
84
|
|
|
75
85
|
### `validate`
|
|
76
86
|
|
|
77
87
|
```bash
|
|
78
|
-
pr-lens validate pr-lens/graph.json .github/pr-lens.yml
|
|
88
|
+
pr-lens validate .pr-lens/graph.json .github/pr-lens.yml
|
|
79
89
|
```
|
|
80
90
|
|
|
81
91
|
Parses graph documents, patch documents, render manifests and configs, JSON or YAML, and reports every problem in each rather than only the first. A file with no `kind` field is read as a config, because a config is the only one of the four a person writes by hand.
|
|
@@ -83,16 +93,16 @@ Parses graph documents, patch documents, render manifests and configs, JSON or Y
|
|
|
83
93
|
### `export`
|
|
84
94
|
|
|
85
95
|
```bash
|
|
86
|
-
pr-lens export pr-lens/graph.json -o .github/pr-lens.map.json
|
|
96
|
+
pr-lens export .pr-lens/graph.json -o .github/pr-lens.map.json
|
|
87
97
|
```
|
|
88
98
|
|
|
89
|
-
Turns a pull-request document into the map of the system once that pull request has merged: elements the change deletes are dropped
|
|
99
|
+
Turns a pull-request document into the map of the system once that pull request has merged: elements the change deletes are dropped, along with the edges and flow steps that hung from them; the rest stops being annotated, and the result is stamped with the single commit it reflects.
|
|
90
100
|
|
|
91
101
|
The map is a snapshot, not a source of truth. Nothing reads it back into the pipeline: a committed map that overrode inference would be hand-maintained rot with merge conflicts attached. Commit it so a repository has something to read, to diff, and to hand an agent.
|
|
92
102
|
|
|
93
103
|
## Corrections
|
|
94
104
|
|
|
95
|
-
A repository's `.github/pr-lens.yml` is picked up automatically by `render` and applied at draw time
|
|
105
|
+
A repository's `.github/pr-lens.yml` is picked up automatically by `render` and applied at draw time: renames, exclusions, lane pins, groupings. It is an overlay: inference never writes back into it, so a correction keeps holding as the code moves and the model renames things between runs, and the document on disk stays the record of what was inferred.
|
|
96
106
|
|
|
97
107
|
```yaml
|
|
98
108
|
schemaVersion: 0.1.0
|
|
@@ -104,9 +114,9 @@ map:
|
|
|
104
114
|
- "**/*.test.ts"
|
|
105
115
|
```
|
|
106
116
|
|
|
107
|
-
A lane pin may name a lane the document never declared; the band is created and takes the id for its label. And `render` reports any correction that changed nothing about what it drew
|
|
117
|
+
A lane pin may name a lane the document never declared; the band is created and takes the id for its label. And `render` reports any correction that changed nothing about what it drew. A config that has drifted, usually because the file a selector named has moved, otherwise fails silently and forever.
|
|
108
118
|
|
|
109
|
-
`--config` points elsewhere and `--no-config` ignores it, on both `render` and `analyze
|
|
119
|
+
`--config` points elsewhere and `--no-config` ignores it, on both `render` and `analyze`. `analyze` reads only `lenses` from it, since which lenses to fill is a question for extraction and the rest is a question for drawing.
|
|
110
120
|
|
|
111
121
|
## Failures
|
|
112
122
|
|
|
@@ -114,4 +124,4 @@ Every failure carries a code, so a script can branch on it: `USAGE`, `UNREADABLE
|
|
|
114
124
|
|
|
115
125
|
---
|
|
116
126
|
|
|
117
|
-
Part of [PR Lens](https://prlens.dev)
|
|
127
|
+
Part of [PR Lens](https://prlens.dev). Review what actually matters.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAQ/C,eAAO,MAAM,KAAK,QAsB+D,CAAC;AAyBlF,eAAO,MAAM,cAAc,SACnB,SAAS,MAAM,EAAE,YACb,QAAQ,OACb,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KACtC,OAAO,CAAC,IAAI,CA2Id,CAAC"}
|
package/dist/commands/analyze.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LENSES } from "@coldtea/pr-lens-schema";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname } from "node:path";
|
|
3
4
|
import { parseOptions, readBoolean, readInt, readList, readString } from "../args.js";
|
|
4
5
|
import { discoverConfig, loadConfig } from "../config-file.js";
|
|
5
6
|
import { PrLensCliError, usageError } from "../errors.js";
|
|
@@ -9,7 +10,8 @@ import { readTextFile, writeJsonFile } from "../io.js";
|
|
|
9
10
|
import { buildExtractionPrompt, SYSTEM_PROMPT } from "../prompt.js";
|
|
10
11
|
import { completeJson, isProviderId, PROVIDER_IDS, resolveProvider } from "../providers/index.js";
|
|
11
12
|
import { CLI_VERSION, GENERATOR_NAME } from "../version.js";
|
|
12
|
-
|
|
13
|
+
import { prepareWorkspace, WORKSPACE_DIR } from "../workspace.js";
|
|
14
|
+
const DEFAULT_OUT = `${WORKSPACE_DIR}/graph.json`;
|
|
13
15
|
const DEFAULT_MAX_OUTPUT_TOKENS = 32_768;
|
|
14
16
|
const DEFAULT_MAX_DIFF_BYTES = 400_000;
|
|
15
17
|
export const USAGE = `pr-lens analyze --base <ref> [options]
|
|
@@ -152,7 +154,9 @@ export const analyzeCommand = async (args, terminal, env) => {
|
|
|
152
154
|
terminal.err(`the document did not validate — asking for a correction`);
|
|
153
155
|
},
|
|
154
156
|
}, (request) => completeJson(provider, request));
|
|
155
|
-
const
|
|
157
|
+
const outPath = readString(values.out, "out") ?? DEFAULT_OUT;
|
|
158
|
+
await prepareWorkspace(dirname(outPath), terminal);
|
|
159
|
+
const written = await writeJsonFile(outPath, document);
|
|
156
160
|
terminal.out(`✓ ${written} — ${document.nodes.length} nodes, ${document.edges.length} edges, ${document.flows.length} flows${attempts > 1 ? ` (${attempts} attempts)` : ""}`);
|
|
157
161
|
};
|
|
158
162
|
//# sourceMappingURL=analyze.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAiC,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,UAAU,EAAqB,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC7G,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAElG,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Terminal } from "../terminal.js";
|
|
2
|
-
export declare const USAGE = "pr-lens render <graph.json> [options]\n\nDraws the document as self-contained SVGs \u2014 one per drill-down section per\nlens, in light and dark \u2014 and writes the manifest describing them. A document\nwith no sections gets one diagram per lens it declares.\n\n -o, --out <dir> where the SVGs and the manifest go (default pr-lens/)\n --theme <theme> light | dark | both (default both)\n --config <file> corrections to draw with (default the repository's, if any)\n --no-config ignore the repository's corrections";
|
|
2
|
+
export declare const USAGE = "pr-lens render <graph.json> [options]\n\nDraws the document as self-contained SVGs \u2014 one per drill-down section per\nlens, in light and dark \u2014 and writes the manifest describing them. A document\nwith no sections gets one diagram per lens it declares.\n\n -o, --out <dir> where the SVGs and the manifest go (default .pr-lens/)\n --theme <theme> light | dark | both (default both)\n --config <file> corrections to draw with (default the repository's, if any)\n --no-config ignore the repository's corrections";
|
|
3
3
|
export declare const renderCommand: (args: readonly string[], terminal: Terminal) => Promise<void>;
|
|
4
4
|
//# sourceMappingURL=render.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAiB/C,eAAO,MAAM,KAAK,siBASyC,CAAC;AA6B5D,eAAO,MAAM,aAAa,SAAgB,SAAS,MAAM,EAAE,YAAY,QAAQ,KAAG,OAAO,CAAC,IAAI,CA8D7F,CAAC"}
|
package/dist/commands/render.js
CHANGED
|
@@ -8,7 +8,8 @@ import { readGraphDoc } from "../document.js";
|
|
|
8
8
|
import { PrLensCliError, usageError } from "../errors.js";
|
|
9
9
|
import { repositoryRoot } from "../git.js";
|
|
10
10
|
import { writeJsonFile, writeTextFile } from "../io.js";
|
|
11
|
-
|
|
11
|
+
import { prepareWorkspace, WORKSPACE_DIR } from "../workspace.js";
|
|
12
|
+
const DEFAULT_OUT = WORKSPACE_DIR;
|
|
12
13
|
const MANIFEST = "manifest.json";
|
|
13
14
|
/**
|
|
14
15
|
* The document as it was drawn, written beside the manifest.
|
|
@@ -94,6 +95,7 @@ export const renderCommand = async (args, terminal) => {
|
|
|
94
95
|
throw new PrLensCliError("RENDER_FAILED", `${error.message} [${error.code}]`);
|
|
95
96
|
}
|
|
96
97
|
})();
|
|
98
|
+
await prepareWorkspace(out, terminal);
|
|
97
99
|
for (const drawing of rendered.assets) {
|
|
98
100
|
const { id, path } = drawing.asset;
|
|
99
101
|
if (path === undefined)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAc,MAAM,2BAA2B,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAA8B,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Terminal } from "./terminal.js";
|
|
2
|
+
/**
|
|
3
|
+
* Where the CLI leaves what it makes, unless someone says otherwise.
|
|
4
|
+
*
|
|
5
|
+
* A dot-directory, and ignored by git from the first write, because none of it
|
|
6
|
+
* is source. The SVGs, the document they were drawn from and the manifest are
|
|
7
|
+
* a preview of the comment, rebuilt from the diff whenever anyone wants it
|
|
8
|
+
* again. A working tree that fills with them leaves the user deciding what to
|
|
9
|
+
* commit, and the answer is always none of it.
|
|
10
|
+
*/
|
|
11
|
+
export declare const WORKSPACE_DIR = ".pr-lens";
|
|
12
|
+
/**
|
|
13
|
+
* Adds the workspace to the repository's .gitignore, and says so. Answers with
|
|
14
|
+
* the file it wrote, or nothing when there was nothing to do. Call it once the
|
|
15
|
+
* workspace directory exists, so git can see that it is one.
|
|
16
|
+
*
|
|
17
|
+
* Nothing is written when git already ignores the previews, or when the
|
|
18
|
+
* repository un-ignores this workspace on purpose: a user who chose to track
|
|
19
|
+
* these files has chosen.
|
|
20
|
+
*
|
|
21
|
+
* The entry is normally unanchored, so it holds wherever in the tree the CLI is
|
|
22
|
+
* run. It is narrowed to this one workspace when the repository un-ignores a
|
|
23
|
+
* different one, since an unanchored line lands last and would overrule that
|
|
24
|
+
* choice on its way past.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ignoreWorkspace: (workspace: string) => Promise<string | undefined>;
|
|
27
|
+
/**
|
|
28
|
+
* Prepares the workspace on the way to writing in it: the README that says
|
|
29
|
+
* what the directory is, and the .gitignore entry that keeps anyone from
|
|
30
|
+
* having to ask.
|
|
31
|
+
*
|
|
32
|
+
* Both are skipped when --out named somewhere else. That directory is the
|
|
33
|
+
* user's, and so is the question of what to do with it.
|
|
34
|
+
*/
|
|
35
|
+
export declare const prepareWorkspace: (out: string, terminal: Terminal) => Promise<void>;
|
|
36
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,aAAa,CAAC;AA+GxC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,cAAqB,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAenF,CAAC;AAgBF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,QAAe,MAAM,YAAY,QAAQ,KAAG,OAAO,CAAC,IAAI,CAUpF,CAAC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { assertNever } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { git } from "./git.js";
|
|
4
|
+
import { readTextFile, writeTextFile } from "./io.js";
|
|
5
|
+
/**
|
|
6
|
+
* Where the CLI leaves what it makes, unless someone says otherwise.
|
|
7
|
+
*
|
|
8
|
+
* A dot-directory, and ignored by git from the first write, because none of it
|
|
9
|
+
* is source. The SVGs, the document they were drawn from and the manifest are
|
|
10
|
+
* a preview of the comment, rebuilt from the diff whenever anyone wants it
|
|
11
|
+
* again. A working tree that fills with them leaves the user deciding what to
|
|
12
|
+
* commit, and the answer is always none of it.
|
|
13
|
+
*/
|
|
14
|
+
export const WORKSPACE_DIR = ".pr-lens";
|
|
15
|
+
const README_NAME = "README.md";
|
|
16
|
+
/**
|
|
17
|
+
* A path written into a .gitignore is read as a pattern, not as a name. A
|
|
18
|
+
* directory called `sub[1]` spells a character class that matches neither
|
|
19
|
+
* bracket; one beginning with `#` turns the line into a comment and one
|
|
20
|
+
* beginning with `!` turns it into another negation. Each leaves the previews
|
|
21
|
+
* visible under an entry that looks like it was written correctly.
|
|
22
|
+
*
|
|
23
|
+
* Trailing spaces want no escape here. git strips them from the end of a
|
|
24
|
+
* pattern, and every pattern this writes ends in a slash.
|
|
25
|
+
*/
|
|
26
|
+
const asPattern = (path) => path.replace(/[\\*?[\]]/g, (character) => `\\${character}`).replace(/^([#!])/, "\\$1");
|
|
27
|
+
/** The name a pattern stands for, with the escapes git reads taken back off. */
|
|
28
|
+
const asLiteral = (pattern) => pattern.replace(/\\(.)/g, "$1");
|
|
29
|
+
const ignoreEntry = (path) => `# PR Lens writes its previews here. They are rebuilt on demand.\n${asPattern(path)}/\n`;
|
|
30
|
+
/**
|
|
31
|
+
* Asked of git rather than computed from the two paths, because on macOS the
|
|
32
|
+
* root comes back through /private and the working directory usually does not,
|
|
33
|
+
* and subtracting one from the other then yields a climb out of the repository.
|
|
34
|
+
*
|
|
35
|
+
* Only the record terminator comes off each line. A directory may legitimately
|
|
36
|
+
* begin or end with a space, and trimming one writes a rule for a directory
|
|
37
|
+
* nobody has.
|
|
38
|
+
*/
|
|
39
|
+
const locate = async (from) => {
|
|
40
|
+
const [root = "", prefix = ""] = (await git(from, ["rev-parse", "--show-toplevel", "--show-prefix"]))
|
|
41
|
+
.split("\n")
|
|
42
|
+
.map((line) => line.replace(/\r$/, ""));
|
|
43
|
+
return { root, prefix };
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Whether git already ignores this workspace. Asked of git rather than worked
|
|
47
|
+
* out from the file, because a pattern that looks right often is not: at the
|
|
48
|
+
* root of a repository `/.pr-lens/` covers the root and nothing below it, and
|
|
49
|
+
* two leading spaces are part of the pattern and so cover nothing at all.
|
|
50
|
+
* Reading the entry back and deciding it means what we would have meant is how
|
|
51
|
+
* a run leaves the previews visible while reporting them handled.
|
|
52
|
+
*
|
|
53
|
+
* The question is about the directory, and nothing standing in for it. A rule
|
|
54
|
+
* covering one file inside — `README.md` is a common one to carry — answers
|
|
55
|
+
* for that file and for nothing beside it, and taking it as settled leaves
|
|
56
|
+
* every SVG and both documents in the working tree.
|
|
57
|
+
*
|
|
58
|
+
* The directory has to exist to be recognised as one, since a pattern ending
|
|
59
|
+
* in a slash matches directories only. It does: the caller writes into it
|
|
60
|
+
* first, for this reason.
|
|
61
|
+
*/
|
|
62
|
+
const alreadyIgnored = (root, workspace) => git(root, ["check-ignore", "-q", "--", workspace]).then(() => true, () => false);
|
|
63
|
+
/**
|
|
64
|
+
* The negations a .gitignore makes about a PR Lens workspace.
|
|
65
|
+
*
|
|
66
|
+
* git will not answer this one. When a negation wins, `check-ignore` reports
|
|
67
|
+
* the path as matching nothing at all — the same answer it gives for a path
|
|
68
|
+
* nobody has mentioned — so the file has to be read, and read the way git reads
|
|
69
|
+
* it. `!/.pr-lens/` at the root is a choice about the root's own previews and
|
|
70
|
+
* says nothing about a workspace inside a subdirectory.
|
|
71
|
+
*
|
|
72
|
+
* Only literal names are recognised. A negation written with wildcards is not,
|
|
73
|
+
* and the entry is added despite it — a line in a .gitignore is visible, the
|
|
74
|
+
* CLI names the file it wrote, and the user can take it back.
|
|
75
|
+
*/
|
|
76
|
+
const negations = (gitignore) => gitignore.split("\n").flatMap((line) => {
|
|
77
|
+
const pattern = line.trimEnd();
|
|
78
|
+
if (!pattern.startsWith("!"))
|
|
79
|
+
return [];
|
|
80
|
+
const body = asLiteral(pattern.slice(1)).replace(/\/+$/, "");
|
|
81
|
+
if (!body.includes("/"))
|
|
82
|
+
return body === WORKSPACE_DIR ? [{ kind: "anywhere" }] : [];
|
|
83
|
+
const workspace = body.replace(/^\//, "");
|
|
84
|
+
return workspace.endsWith(WORKSPACE_DIR) ? [{ kind: "one", workspace }] : [];
|
|
85
|
+
});
|
|
86
|
+
const spares = (negation, workspace) => {
|
|
87
|
+
switch (negation.kind) {
|
|
88
|
+
case "anywhere":
|
|
89
|
+
return true;
|
|
90
|
+
case "one":
|
|
91
|
+
return negation.workspace === workspace;
|
|
92
|
+
default:
|
|
93
|
+
return assertNever(negation, "Unhandled negation");
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Adds the workspace to the repository's .gitignore, and says so. Answers with
|
|
98
|
+
* the file it wrote, or nothing when there was nothing to do. Call it once the
|
|
99
|
+
* workspace directory exists, so git can see that it is one.
|
|
100
|
+
*
|
|
101
|
+
* Nothing is written when git already ignores the previews, or when the
|
|
102
|
+
* repository un-ignores this workspace on purpose: a user who chose to track
|
|
103
|
+
* these files has chosen.
|
|
104
|
+
*
|
|
105
|
+
* The entry is normally unanchored, so it holds wherever in the tree the CLI is
|
|
106
|
+
* run. It is narrowed to this one workspace when the repository un-ignores a
|
|
107
|
+
* different one, since an unanchored line lands last and would overrule that
|
|
108
|
+
* choice on its way past.
|
|
109
|
+
*/
|
|
110
|
+
export const ignoreWorkspace = async (workspace) => {
|
|
111
|
+
const location = await locate(dirname(workspace)).catch(() => undefined);
|
|
112
|
+
if (location === undefined)
|
|
113
|
+
return undefined;
|
|
114
|
+
if (await alreadyIgnored(location.root, workspace))
|
|
115
|
+
return undefined;
|
|
116
|
+
const path = join(location.root, ".gitignore");
|
|
117
|
+
const existing = (await readTextFile(path).catch(() => undefined)) ?? "";
|
|
118
|
+
const mine = `${location.prefix}${WORKSPACE_DIR}`;
|
|
119
|
+
const spared = negations(existing);
|
|
120
|
+
if (spared.some((negation) => spares(negation, mine)))
|
|
121
|
+
return undefined;
|
|
122
|
+
const before = existing === "" || existing.endsWith("\n") ? existing : `${existing}\n`;
|
|
123
|
+
const entry = ignoreEntry(spared.length > 0 ? mine : WORKSPACE_DIR);
|
|
124
|
+
return writeTextFile(path, `${before}${before === "" ? "" : "\n"}${entry}`);
|
|
125
|
+
};
|
|
126
|
+
const README = `# .pr-lens
|
|
127
|
+
|
|
128
|
+
PR Lens writes its previews here: the diagrams as light and dark SVGs, the
|
|
129
|
+
document they were drawn from, and the manifest describing them.
|
|
130
|
+
|
|
131
|
+
None of it belongs in a commit. Every file is rebuilt from the diff by
|
|
132
|
+
\`pr-lens analyze\` and \`pr-lens render\`, so a stale copy in the history is
|
|
133
|
+
worth less than nothing — it is a diagram of a pull request somebody already
|
|
134
|
+
merged. What readers are meant to see is the comment on the pull request, or
|
|
135
|
+
the share page it links to.
|
|
136
|
+
|
|
137
|
+
Delete the directory whenever you like. Nothing reads it back.
|
|
138
|
+
`;
|
|
139
|
+
/**
|
|
140
|
+
* Prepares the workspace on the way to writing in it: the README that says
|
|
141
|
+
* what the directory is, and the .gitignore entry that keeps anyone from
|
|
142
|
+
* having to ask.
|
|
143
|
+
*
|
|
144
|
+
* Both are skipped when --out named somewhere else. That directory is the
|
|
145
|
+
* user's, and so is the question of what to do with it.
|
|
146
|
+
*/
|
|
147
|
+
export const prepareWorkspace = async (out, terminal) => {
|
|
148
|
+
const directory = resolve(out);
|
|
149
|
+
if (directory !== resolve(WORKSPACE_DIR))
|
|
150
|
+
return;
|
|
151
|
+
// The README first, which is what puts the directory on disk: git is asked
|
|
152
|
+
// whether a directory is ignored, and one that is not there cannot be.
|
|
153
|
+
await writeTextFile(join(directory, README_NAME), README);
|
|
154
|
+
const ignored = await ignoreWorkspace(directory);
|
|
155
|
+
if (ignored !== undefined)
|
|
156
|
+
terminal.err(`✓ ${ignored} — ${WORKSPACE_DIR}/ is ignored`);
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coldtea/pr-lens-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The PR Lens command line: turn a pull request diff into a schema-valid graph with your own model key, render it, and compose the comment.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"author": "
|
|
6
|
+
"author": "Coldtea AI",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/coldteadotai/pr-lens.git",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"yaml": "^2.8.1",
|
|
42
42
|
"zod": "^4.4.3",
|
|
43
|
-
"@coldtea/pr-lens-renderer": "^0.1.
|
|
44
|
-
"@coldtea/pr-lens-schema": "^0.1.
|
|
43
|
+
"@coldtea/pr-lens-renderer": "^0.1.2",
|
|
44
|
+
"@coldtea/pr-lens-schema": "^0.1.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/node": "^20.19.0",
|
package/src/commands/analyze.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LENSES, type GraphDocInput, type Lens } from "@coldtea/pr-lens-schema";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname } from "node:path";
|
|
3
4
|
import { parseOptions, readBoolean, readInt, readList, readString } from "../args.js";
|
|
4
5
|
import { discoverConfig, loadConfig, type LoadedConfig } from "../config-file.js";
|
|
5
6
|
import { PrLensCliError, usageError } from "../errors.js";
|
|
@@ -10,8 +11,9 @@ import { buildExtractionPrompt, SYSTEM_PROMPT } from "../prompt.js";
|
|
|
10
11
|
import { completeJson, isProviderId, PROVIDER_IDS, resolveProvider } from "../providers/index.js";
|
|
11
12
|
import type { Terminal } from "../terminal.js";
|
|
12
13
|
import { CLI_VERSION, GENERATOR_NAME } from "../version.js";
|
|
14
|
+
import { prepareWorkspace, WORKSPACE_DIR } from "../workspace.js";
|
|
13
15
|
|
|
14
|
-
const DEFAULT_OUT =
|
|
16
|
+
const DEFAULT_OUT = `${WORKSPACE_DIR}/graph.json`;
|
|
15
17
|
const DEFAULT_MAX_OUTPUT_TOKENS = 32_768;
|
|
16
18
|
const DEFAULT_MAX_DIFF_BYTES = 400_000;
|
|
17
19
|
|
|
@@ -199,7 +201,9 @@ export const analyzeCommand = async (
|
|
|
199
201
|
(request) => completeJson(provider, request),
|
|
200
202
|
);
|
|
201
203
|
|
|
202
|
-
const
|
|
204
|
+
const outPath = readString(values.out, "out") ?? DEFAULT_OUT;
|
|
205
|
+
await prepareWorkspace(dirname(outPath), terminal);
|
|
206
|
+
const written = await writeJsonFile(outPath, document);
|
|
203
207
|
terminal.out(
|
|
204
208
|
`✓ ${written} — ${document.nodes.length} nodes, ${document.edges.length} edges, ${document.flows.length} flows${attempts > 1 ? ` (${attempts} attempts)` : ""}`,
|
|
205
209
|
);
|
package/src/commands/render.ts
CHANGED
|
@@ -9,8 +9,9 @@ import { PrLensCliError, usageError } from "../errors.js";
|
|
|
9
9
|
import { repositoryRoot } from "../git.js";
|
|
10
10
|
import { writeJsonFile, writeTextFile } from "../io.js";
|
|
11
11
|
import type { Terminal } from "../terminal.js";
|
|
12
|
+
import { prepareWorkspace, WORKSPACE_DIR } from "../workspace.js";
|
|
12
13
|
|
|
13
|
-
const DEFAULT_OUT =
|
|
14
|
+
const DEFAULT_OUT = WORKSPACE_DIR;
|
|
14
15
|
const MANIFEST = "manifest.json";
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -109,6 +110,8 @@ export const renderCommand = async (args: readonly string[], terminal: Terminal)
|
|
|
109
110
|
}
|
|
110
111
|
})();
|
|
111
112
|
|
|
113
|
+
await prepareWorkspace(out, terminal);
|
|
114
|
+
|
|
112
115
|
for (const drawing of rendered.assets) {
|
|
113
116
|
const { id, path } = drawing.asset;
|
|
114
117
|
if (path === undefined)
|
package/src/version.ts
CHANGED
package/src/workspace.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { assertNever } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { git } from "./git.js";
|
|
4
|
+
import { readTextFile, writeTextFile } from "./io.js";
|
|
5
|
+
import type { Terminal } from "./terminal.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Where the CLI leaves what it makes, unless someone says otherwise.
|
|
9
|
+
*
|
|
10
|
+
* A dot-directory, and ignored by git from the first write, because none of it
|
|
11
|
+
* is source. The SVGs, the document they were drawn from and the manifest are
|
|
12
|
+
* a preview of the comment, rebuilt from the diff whenever anyone wants it
|
|
13
|
+
* again. A working tree that fills with them leaves the user deciding what to
|
|
14
|
+
* commit, and the answer is always none of it.
|
|
15
|
+
*/
|
|
16
|
+
export const WORKSPACE_DIR = ".pr-lens";
|
|
17
|
+
|
|
18
|
+
const README_NAME = "README.md";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A path written into a .gitignore is read as a pattern, not as a name. A
|
|
22
|
+
* directory called `sub[1]` spells a character class that matches neither
|
|
23
|
+
* bracket; one beginning with `#` turns the line into a comment and one
|
|
24
|
+
* beginning with `!` turns it into another negation. Each leaves the previews
|
|
25
|
+
* visible under an entry that looks like it was written correctly.
|
|
26
|
+
*
|
|
27
|
+
* Trailing spaces want no escape here. git strips them from the end of a
|
|
28
|
+
* pattern, and every pattern this writes ends in a slash.
|
|
29
|
+
*/
|
|
30
|
+
const asPattern = (path: string): string =>
|
|
31
|
+
path.replace(/[\\*?[\]]/g, (character) => `\\${character}`).replace(/^([#!])/, "\\$1");
|
|
32
|
+
|
|
33
|
+
/** The name a pattern stands for, with the escapes git reads taken back off. */
|
|
34
|
+
const asLiteral = (pattern: string): string => pattern.replace(/\\(.)/g, "$1");
|
|
35
|
+
|
|
36
|
+
const ignoreEntry = (path: string): string =>
|
|
37
|
+
`# PR Lens writes its previews here. They are rebuilt on demand.\n${asPattern(path)}/\n`;
|
|
38
|
+
|
|
39
|
+
/** Where a directory sits in its repository: the root, and its path below it. */
|
|
40
|
+
type Location = { root: string; prefix: string };
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Asked of git rather than computed from the two paths, because on macOS the
|
|
44
|
+
* root comes back through /private and the working directory usually does not,
|
|
45
|
+
* and subtracting one from the other then yields a climb out of the repository.
|
|
46
|
+
*
|
|
47
|
+
* Only the record terminator comes off each line. A directory may legitimately
|
|
48
|
+
* begin or end with a space, and trimming one writes a rule for a directory
|
|
49
|
+
* nobody has.
|
|
50
|
+
*/
|
|
51
|
+
const locate = async (from: string): Promise<Location> => {
|
|
52
|
+
const [root = "", prefix = ""] = (
|
|
53
|
+
await git(from, ["rev-parse", "--show-toplevel", "--show-prefix"])
|
|
54
|
+
)
|
|
55
|
+
.split("\n")
|
|
56
|
+
.map((line) => line.replace(/\r$/, ""));
|
|
57
|
+
|
|
58
|
+
return { root, prefix };
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether git already ignores this workspace. Asked of git rather than worked
|
|
63
|
+
* out from the file, because a pattern that looks right often is not: at the
|
|
64
|
+
* root of a repository `/.pr-lens/` covers the root and nothing below it, and
|
|
65
|
+
* two leading spaces are part of the pattern and so cover nothing at all.
|
|
66
|
+
* Reading the entry back and deciding it means what we would have meant is how
|
|
67
|
+
* a run leaves the previews visible while reporting them handled.
|
|
68
|
+
*
|
|
69
|
+
* The question is about the directory, and nothing standing in for it. A rule
|
|
70
|
+
* covering one file inside — `README.md` is a common one to carry — answers
|
|
71
|
+
* for that file and for nothing beside it, and taking it as settled leaves
|
|
72
|
+
* every SVG and both documents in the working tree.
|
|
73
|
+
*
|
|
74
|
+
* The directory has to exist to be recognised as one, since a pattern ending
|
|
75
|
+
* in a slash matches directories only. It does: the caller writes into it
|
|
76
|
+
* first, for this reason.
|
|
77
|
+
*/
|
|
78
|
+
const alreadyIgnored = (root: string, workspace: string): Promise<boolean> =>
|
|
79
|
+
git(root, ["check-ignore", "-q", "--", workspace]).then(
|
|
80
|
+
() => true,
|
|
81
|
+
() => false,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A line that un-ignores a workspace, and how far its choice reaches: a bare
|
|
86
|
+
* name holds at any depth, while a pattern carrying a slash anywhere but its
|
|
87
|
+
* end is anchored to the root and speaks for one directory only.
|
|
88
|
+
*/
|
|
89
|
+
type Negation = { kind: "anywhere" } | { kind: "one"; workspace: string };
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The negations a .gitignore makes about a PR Lens workspace.
|
|
93
|
+
*
|
|
94
|
+
* git will not answer this one. When a negation wins, `check-ignore` reports
|
|
95
|
+
* the path as matching nothing at all — the same answer it gives for a path
|
|
96
|
+
* nobody has mentioned — so the file has to be read, and read the way git reads
|
|
97
|
+
* it. `!/.pr-lens/` at the root is a choice about the root's own previews and
|
|
98
|
+
* says nothing about a workspace inside a subdirectory.
|
|
99
|
+
*
|
|
100
|
+
* Only literal names are recognised. A negation written with wildcards is not,
|
|
101
|
+
* and the entry is added despite it — a line in a .gitignore is visible, the
|
|
102
|
+
* CLI names the file it wrote, and the user can take it back.
|
|
103
|
+
*/
|
|
104
|
+
const negations = (gitignore: string): Negation[] =>
|
|
105
|
+
gitignore.split("\n").flatMap((line): Negation[] => {
|
|
106
|
+
const pattern = line.trimEnd();
|
|
107
|
+
if (!pattern.startsWith("!")) return [];
|
|
108
|
+
|
|
109
|
+
const body = asLiteral(pattern.slice(1)).replace(/\/+$/, "");
|
|
110
|
+
if (!body.includes("/")) return body === WORKSPACE_DIR ? [{ kind: "anywhere" }] : [];
|
|
111
|
+
|
|
112
|
+
const workspace = body.replace(/^\//, "");
|
|
113
|
+
return workspace.endsWith(WORKSPACE_DIR) ? [{ kind: "one", workspace }] : [];
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const spares = (negation: Negation, workspace: string): boolean => {
|
|
117
|
+
switch (negation.kind) {
|
|
118
|
+
case "anywhere":
|
|
119
|
+
return true;
|
|
120
|
+
case "one":
|
|
121
|
+
return negation.workspace === workspace;
|
|
122
|
+
default:
|
|
123
|
+
return assertNever(negation, "Unhandled negation");
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Adds the workspace to the repository's .gitignore, and says so. Answers with
|
|
129
|
+
* the file it wrote, or nothing when there was nothing to do. Call it once the
|
|
130
|
+
* workspace directory exists, so git can see that it is one.
|
|
131
|
+
*
|
|
132
|
+
* Nothing is written when git already ignores the previews, or when the
|
|
133
|
+
* repository un-ignores this workspace on purpose: a user who chose to track
|
|
134
|
+
* these files has chosen.
|
|
135
|
+
*
|
|
136
|
+
* The entry is normally unanchored, so it holds wherever in the tree the CLI is
|
|
137
|
+
* run. It is narrowed to this one workspace when the repository un-ignores a
|
|
138
|
+
* different one, since an unanchored line lands last and would overrule that
|
|
139
|
+
* choice on its way past.
|
|
140
|
+
*/
|
|
141
|
+
export const ignoreWorkspace = async (workspace: string): Promise<string | undefined> => {
|
|
142
|
+
const location = await locate(dirname(workspace)).catch(() => undefined);
|
|
143
|
+
if (location === undefined) return undefined;
|
|
144
|
+
if (await alreadyIgnored(location.root, workspace)) return undefined;
|
|
145
|
+
|
|
146
|
+
const path = join(location.root, ".gitignore");
|
|
147
|
+
const existing = (await readTextFile(path).catch(() => undefined)) ?? "";
|
|
148
|
+
|
|
149
|
+
const mine = `${location.prefix}${WORKSPACE_DIR}`;
|
|
150
|
+
const spared = negations(existing);
|
|
151
|
+
if (spared.some((negation) => spares(negation, mine))) return undefined;
|
|
152
|
+
|
|
153
|
+
const before = existing === "" || existing.endsWith("\n") ? existing : `${existing}\n`;
|
|
154
|
+
const entry = ignoreEntry(spared.length > 0 ? mine : WORKSPACE_DIR);
|
|
155
|
+
return writeTextFile(path, `${before}${before === "" ? "" : "\n"}${entry}`);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const README = `# .pr-lens
|
|
159
|
+
|
|
160
|
+
PR Lens writes its previews here: the diagrams as light and dark SVGs, the
|
|
161
|
+
document they were drawn from, and the manifest describing them.
|
|
162
|
+
|
|
163
|
+
None of it belongs in a commit. Every file is rebuilt from the diff by
|
|
164
|
+
\`pr-lens analyze\` and \`pr-lens render\`, so a stale copy in the history is
|
|
165
|
+
worth less than nothing — it is a diagram of a pull request somebody already
|
|
166
|
+
merged. What readers are meant to see is the comment on the pull request, or
|
|
167
|
+
the share page it links to.
|
|
168
|
+
|
|
169
|
+
Delete the directory whenever you like. Nothing reads it back.
|
|
170
|
+
`;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Prepares the workspace on the way to writing in it: the README that says
|
|
174
|
+
* what the directory is, and the .gitignore entry that keeps anyone from
|
|
175
|
+
* having to ask.
|
|
176
|
+
*
|
|
177
|
+
* Both are skipped when --out named somewhere else. That directory is the
|
|
178
|
+
* user's, and so is the question of what to do with it.
|
|
179
|
+
*/
|
|
180
|
+
export const prepareWorkspace = async (out: string, terminal: Terminal): Promise<void> => {
|
|
181
|
+
const directory = resolve(out);
|
|
182
|
+
if (directory !== resolve(WORKSPACE_DIR)) return;
|
|
183
|
+
|
|
184
|
+
// The README first, which is what puts the directory on disk: git is asked
|
|
185
|
+
// whether a directory is ignored, and one that is not there cannot be.
|
|
186
|
+
await writeTextFile(join(directory, README_NAME), README);
|
|
187
|
+
|
|
188
|
+
const ignored = await ignoreWorkspace(directory);
|
|
189
|
+
if (ignored !== undefined) terminal.err(`✓ ${ignored} — ${WORKSPACE_DIR}/ is ignored`);
|
|
190
|
+
};
|