simt-dev 0.6.3 → 0.7.1
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/CHANGELOG.md +162 -841
- package/README.md +21 -135
- package/agents/simt-agent.md +84 -0
- package/bin/simt.mjs +19 -55
- package/docs/decisions/ADR-009-skill-layout.md +1 -1
- package/docs/decisions/ADR-014-workspace-is-configuration.md +1 -1
- package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md +1 -1
- package/docs/decisions/ADR-099-the-release-verifies-served-bytes-and-the-formula-mirrors-them.md +1 -1
- package/docs/decisions/ADR-106-the-harness-hook-runs-through-a-repo-shim.md +46 -0
- package/docs/decisions/ADR-107-an-install-can-name-one-harness-and-the-skipped-ones-are-named.md +51 -0
- package/docs/decisions/ADR-108-an-adoption-builds-the-index-and-a-command-refreshes-it-on-read.md +59 -0
- package/docs/decisions/ADR-109-simt-dev-is-its-own-consumer-and-the-guards-that-kept-it-from-being-one-are-retired.md +47 -0
- package/docs/decisions/ADR-110-the-lock-file-follows-the-manifest-name-and-the-old-name-is-read-until-the-next-write.md +43 -0
- package/docs/decisions/ADR-111-the-tool-ships-one-agent-rendered-per-harness-and-installs-it-with-init-and-a-command.md +75 -0
- package/docs/decisions/ADR-112-the-registry-is-what-simt-dev-ships-and-a-repo-owns-its-own-skills.md +81 -0
- package/docs/decisions/ADR-113-a-pre-push-that-carries-only-documentation-skips-the-test-gate-and-says-so.md +87 -0
- package/docs/decisions/ADR-114-the-bench-pairs-at-the-task-level-a-missing-cost-is-not-zero-and-an-unasked-cross-group-compare-is-refused.md +74 -0
- package/docs/decisions/ADR-115-the-index-stamps-its-schema-version-migrates-older-databases-in-place-and-serves-newer-ones-from-a-sidecar.md +69 -0
- package/docs/decisions/ADR-116-rust-support-reaches-the-example-path-the-indent-and-the-bench-runner.md +100 -0
- package/docs/decisions/ADR-117-the-darwin-release-builds-on-supported-macos-15-runners-and-a-re-run-is-idempotent.md +81 -0
- package/docs/decisions/ADR-118-the-bench-corpus-is-keyed-on-history-is-append-only-and-is-portable-as-a-sealed-document.md +91 -0
- package/docs/decisions/ADR-119-the-environment-variable-points-into-a-workspace-the-repo-declared.md +90 -0
- package/docs/decisions/ADR-120-a-pre-push-whose-tree-is-already-proven-green-skips-the-test-gate-on-that-evidence.md +96 -0
- package/docs/decisions/ADR-122-the-shims-move-into-the-state-directory-and-the-legacy-directory-removes-itself.md +94 -0
- package/docs/decisions/ADR-123-a-surplus-positional-is-not-refused-because-arity-is-not-in-the-source.md +57 -0
- package/docs/decisions/ADR-124-the-manifest-has-a-schema-in-the-tool-and-its-unknown-keys-stay-the-repositorys.md +64 -0
- package/docs/decisions/ADR-125-a-shared-context-belongs-to-a-read-and-a-writer-reads-directly.md +65 -0
- package/package.json +13 -3
- package/rules/core.md +9 -2
- package/rules/mdc/slice-commit.mdc +57 -4
- package/rules/mdc/test-gate.mdc +2 -0
- package/rules/stacks/android.md +3 -3
- package/rules/stacks/connectiq.md +2 -2
- package/rules/stacks/dart.md +1 -1
- package/rules/stacks/deno.md +1 -1
- package/rules/stacks/dotnet.md +1 -1
- package/rules/stacks/elixir.md +1 -1
- package/rules/stacks/go.md +1 -1
- package/rules/stacks/jvm.md +3 -2
- package/rules/stacks/node.md +2 -2
- package/rules/stacks/python.md +7 -6
- package/rules/stacks/ruby.md +1 -1
- package/rules/stacks/rust.md +1 -1
- package/rules/stacks/swift.md +1 -1
- package/rules/stacks/typescript.md +2 -2
- package/rules/stacks/uv.md +3 -2
- package/skills/simt-commands/SKILL.md +70 -0
- package/src/advice/advice.mjs +7 -3
- package/src/agenthooks/decide.mjs +141 -9
- package/src/agenthooks/opencode.mjs +72 -0
- package/src/agenthooks/prompt.mjs +10 -3
- package/src/agenthooks/render.mjs +186 -19
- package/src/agenthooks/shim.mjs +55 -0
- package/src/agents/render.mjs +277 -0
- package/src/api.mjs +29 -0
- package/src/argv.mjs +37 -3
- package/src/bench/ablate.mjs +31 -14
- package/src/bench/adapters.json +16 -0
- package/src/bench/corpus.mjs +150 -0
- package/src/bench/grade.mjs +23 -49
- package/src/bench/mine.mjs +247 -36
- package/src/bench/probes.mjs +104 -68
- package/src/bench/process.mjs +128 -0
- package/src/bench/report.mjs +260 -84
- package/src/bench/run.mjs +251 -150
- package/src/bench/variants.mjs +112 -40
- package/src/brief/assemble.mjs +44 -3
- package/src/ci/workflow.mjs +46 -14
- package/src/cli.mjs +45 -0
- package/src/commands/agent.mjs +205 -0
- package/src/commands/agenthook.mjs +113 -20
- package/src/commands/bench.mjs +192 -18
- package/src/commands/brief.mjs +5 -4
- package/src/commands/ci.mjs +2 -1
- package/src/commands/compare.mjs +50 -49
- package/src/commands/completions.mjs +2 -6
- package/src/commands/decide.mjs +10 -9
- package/src/commands/distill.mjs +4 -3
- package/src/commands/doctor.mjs +11 -7
- package/src/commands/evidence.mjs +19 -12
- package/src/commands/explain.mjs +0 -1
- package/src/commands/gate.mjs +98 -28
- package/src/commands/graph.mjs +20 -8
- package/src/commands/harness.mjs +3 -2
- package/src/commands/help.mjs +160 -31
- package/src/commands/hooks.mjs +163 -50
- package/src/commands/ignore.mjs +2 -1
- package/src/commands/impact.mjs +8 -9
- package/src/commands/index.mjs +278 -180
- package/src/commands/init.mjs +171 -38
- package/src/commands/lease.mjs +206 -39
- package/src/commands/lessons.mjs +72 -15
- package/src/commands/local.mjs +3 -3
- package/src/commands/map.mjs +47 -27
- package/src/commands/mcp.mjs +0 -1
- package/src/commands/pack.mjs +9 -6
- package/src/commands/plan.mjs +6 -5
- package/src/commands/plugin.mjs +3 -3
- package/src/commands/prbody.mjs +3 -3
- package/src/commands/repoindex.mjs +5 -3
- package/src/commands/review.mjs +2 -2
- package/src/commands/rules.mjs +124 -33
- package/src/commands/shared.mjs +2 -3
- package/src/commands/skills.mjs +82 -45
- package/src/commands/skillscompile.mjs +12 -12
- package/src/commands/skillsinstall.mjs +14 -2
- package/src/commands/slice.mjs +396 -26
- package/src/commands/spec.mjs +151 -0
- package/src/commands/status.mjs +4 -7
- package/src/commands/sync.mjs +50 -33
- package/src/commands/tests.mjs +11 -11
- package/src/commands/tokens.mjs +2 -2
- package/src/commands/undo.mjs +29 -31
- package/src/commands/upgrade.mjs +40 -4
- package/src/commands/why.mjs +19 -12
- package/src/compare/hooksstate.mjs +23 -1
- package/src/compare/learn.mjs +82 -57
- package/src/compare/parallel.mjs +50 -0
- package/src/compare/row.mjs +26 -3
- package/src/completions.mjs +5 -0
- package/src/context.mjs +64 -0
- package/src/decisions/dir.mjs +84 -0
- package/src/decisions/index-doc.mjs +46 -0
- package/src/decisions/index.mjs +109 -11
- package/src/diff.mjs +1 -1
- package/src/distill/distill.mjs +17 -2
- package/src/distill/engine.mjs +5 -1
- package/src/ecosystem/node-runner.mjs +144 -0
- package/src/explain.mjs +18 -6
- package/src/graph/load.mjs +42 -20
- package/src/hooks/capability.mjs +17 -4
- package/src/hooks/dispatcher.mjs +513 -36
- package/src/hooks/gates.mjs +0 -43
- package/src/hooks/resolve.mjs +11 -10
- package/src/hooks/shim.mjs +17 -9
- package/src/identity.mjs +96 -15
- package/src/index/extract.mjs +192 -11
- package/src/index/persist.mjs +307 -47
- package/src/index/store.mjs +263 -60
- package/src/index/tests.mjs +6 -35
- package/src/journal.mjs +372 -84
- package/src/kernel/card.mjs +37 -12
- package/src/kernel/checks.mjs +44 -9
- package/src/kernel/variants.mjs +27 -16
- package/src/lease.mjs +97 -15
- package/src/ledger/index.mjs +120 -25
- package/src/local/client.mjs +4 -6
- package/src/locate.mjs +33 -9
- package/src/manifest/schema.mjs +213 -0
- package/src/mcp/resources.mjs +12 -13
- package/src/mcp/server.mjs +151 -38
- package/src/mcp/tools.mjs +90 -16
- package/src/measure/session.mjs +3 -2
- package/src/memory/decide.mjs +98 -43
- package/src/memory/lessons.mjs +168 -33
- package/src/name.mjs +9 -0
- package/src/node-version.mjs +37 -0
- package/src/output/index.mjs +111 -3
- package/src/packs/manage.mjs +17 -3
- package/src/packs.mjs +20 -2
- package/src/plan/index.mjs +31 -8
- package/src/plan/template.mjs +27 -5
- package/src/plugins.mjs +6 -2
- package/src/prbody.mjs +7 -5
- package/src/projection/baselines.mjs +1 -1
- package/src/projection/disclosure.mjs +2 -2
- package/src/projection/loaders.mjs +41 -1
- package/src/projection/lock.mjs +31 -14
- package/src/projection/manifest.mjs +89 -53
- package/src/projection/markers.mjs +43 -3
- package/src/projection/mdc.mjs +34 -7
- package/src/projection/project.mjs +162 -18
- package/src/projection/rules.mjs +58 -6
- package/src/projection/surfaces.mjs +9 -1
- package/src/repo-root.mjs +88 -0
- package/src/resolve/body.mjs +4 -0
- package/src/resolve/fingerprint.mjs +24 -15
- package/src/resolve/local.mjs +58 -0
- package/src/review/critic.mjs +6 -3
- package/src/secrets.mjs +165 -22
- package/src/state.mjs +76 -0
- package/src/status.mjs +9 -4
- package/src/toolwords.mjs +8 -0
- package/src/tsconfig.mjs +39 -99
- package/src/workspace.mjs +55 -10
- package/templates/plan/tracker.json +1 -1
- package/docs/decisions/README.md +0 -591
package/src/tsconfig.mjs
CHANGED
|
@@ -1,73 +1,36 @@
|
|
|
1
|
-
// `tsconfig.base.json`: the strictness floor
|
|
1
|
+
// `tsconfig.base.json`: the strictness floor a TypeScript repo writes for itself.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// repo owns from the first commit — pointed at the file ADR-025 ruled out by name.
|
|
5
|
-
//
|
|
3
|
+
// ADR-026's mechanism — a config whose grammar has no comments is a whole file the
|
|
4
|
+
// repo owns from the first commit — pointed at the file ADR-025 ruled out by name.
|
|
5
|
+
// `init` used to create it (review 0003 Q28 stopped that): a floor the tool writes
|
|
6
|
+
// into a fresh repo is a file nothing extends, because the repo's own `tsconfig.json`
|
|
7
|
+
// is the repo's to write — and a base that nothing points at is a floor with no
|
|
8
|
+
// effect, which `check` then reported as drift on every fresh adoption.
|
|
6
9
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// spells a flag differently — the child wins and the base is inert for that key. Every
|
|
10
|
-
// key below is therefore one the estate already agrees on, and the one place the estate
|
|
11
|
-
// disagrees is a check instead (see `checkTsconfig`).
|
|
12
|
-
// 2. The bytes cannot explain themselves. JSON has no comments, so this file ships with no
|
|
13
|
-
// header saying who wrote it or what a reader must do next, which is normally the one
|
|
14
|
-
// thing a shipped file owes its reader. `init` prints the `extends` line it requires
|
|
15
|
-
// and `check` reports a repo that has not adopted it, so the obligation is met by the
|
|
16
|
-
// tool's output rather than the file's.
|
|
10
|
+
// What is left here is the reporting side of ADR-026, and it reports exactly the two
|
|
11
|
+
// states a base file can be in that no file can fix:
|
|
17
12
|
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
13
|
+
// 1. `inert` — the base exists and `tsconfig.json` does not extend it, so the floor
|
|
14
|
+
// is in the repository and not in effect. `extends` lets a child override any key,
|
|
15
|
+
// so a base can only repair a repo that is *missing* a flag; it can never repair
|
|
16
|
+
// one that does not point at it.
|
|
17
|
+
// 2. `inconsistent` — `moduleResolution` is spelled the way the parser accepts but
|
|
18
|
+
// the documentation does not. The parser is case-insensitive, which is the whole
|
|
19
|
+
// reason the estate drifted: both spellings work, so nothing ever said so.
|
|
20
|
+
//
|
|
21
|
+
// Both are non-blocking findings (review 0003 Q28): the base file is repo-owned, and
|
|
22
|
+
// a gate that fails a repository over a policy decision is a gate whose remedy is a
|
|
23
|
+
// choice, not a command. An existing base file stays exactly as the repo wrote it:
|
|
24
|
+
// this tool does not own its bytes, so a repo that edited them is not drift.
|
|
21
25
|
|
|
22
26
|
import { readFile } from 'node:fs/promises';
|
|
23
27
|
import { join } from 'node:path';
|
|
24
28
|
|
|
25
29
|
import { fingerprint } from './resolve/fingerprint.mjs';
|
|
26
|
-
import { CLI } from './name.mjs';
|
|
27
30
|
|
|
28
31
|
export const TSCONFIG_BASE_FILE = 'tsconfig.base.json';
|
|
29
32
|
export const TSCONFIG_FILE = 'tsconfig.json';
|
|
30
33
|
|
|
31
|
-
/**
|
|
32
|
-
* The strictness floor.
|
|
33
|
-
*
|
|
34
|
-
* Every key here is already set, and set the same way, by every `tsconfig.json` in the
|
|
35
|
-
* estate that could afford it. One repository is the outlier and it is not one on purpose: it
|
|
36
|
-
* is missing the seven flags below that the three social repos share, which is the gap
|
|
37
|
-
* plan section 3.1 row 5 named. Nothing in this object is a value anybody has argued about;
|
|
38
|
-
* it is the intersection of what four repositories independently wrote.
|
|
39
|
-
*
|
|
40
|
-
* Absent, and each for a reason that would be invisible to the next reader:
|
|
41
|
-
*
|
|
42
|
-
* - `noEmit`. node.md:36 wants it for "any project that bundles elsewhere", and it would
|
|
43
|
-
* be harmless to inherit here because a child can set `false`. It is left out because a
|
|
44
|
-
* base that hands `noEmit` to a repo nobody looked at is a build that stops emitting,
|
|
45
|
-
* and the two repositories that bundle elsewhere did not ask for it. A floor nobody
|
|
46
|
-
* opted into is not a floor.
|
|
47
|
-
* - `moduleResolution`. All four repositories set it; three write `Bundler` and one writes
|
|
48
|
-
* `bundler`. Shipping either would change nothing, because the child overrides the base
|
|
49
|
-
* and the disagreement is precisely that the child sets it. `check` reports the spelling
|
|
50
|
-
* instead.
|
|
51
|
-
* - `target`, `lib`, `module`. Genuinely per-repo: `lib` is DOM+DOM.Iterable in two
|
|
52
|
-
* repositories and DOM+WebWorker in the other two, and a base that chose would silence
|
|
53
|
-
* one of them. These describe the platform, not the estate.
|
|
54
|
-
* - `include`, `exclude`, `paths`, `types`, `jsx`. What a project is made of.
|
|
55
|
-
*/
|
|
56
|
-
export const STRICTNESS_FLOOR = {
|
|
57
|
-
strict: true,
|
|
58
|
-
noUncheckedIndexedAccess: true,
|
|
59
|
-
exactOptionalPropertyTypes: true,
|
|
60
|
-
noImplicitOverride: true,
|
|
61
|
-
useUnknownInCatchVariables: true,
|
|
62
|
-
noFallthroughCasesInSwitch: true,
|
|
63
|
-
noImplicitReturns: true,
|
|
64
|
-
isolatedModules: true,
|
|
65
|
-
verbatimModuleSyntax: true,
|
|
66
|
-
allowJs: false,
|
|
67
|
-
resolveJsonModule: true,
|
|
68
|
-
skipLibCheck: true,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
34
|
/**
|
|
72
35
|
* `moduleResolution` as TypeScript documents it.
|
|
73
36
|
*
|
|
@@ -87,19 +50,6 @@ export const DOCUMENTED_RESOLUTIONS = new Set([
|
|
|
87
50
|
'bundler',
|
|
88
51
|
]);
|
|
89
52
|
|
|
90
|
-
/**
|
|
91
|
-
* The base file, as bytes.
|
|
92
|
-
*
|
|
93
|
-
* Key order is the order of `STRICTNESS_FLOOR` and not alphabetical, because the object
|
|
94
|
-
* literal is the readable form and reordering it here would mean the file and the reason
|
|
95
|
-
* disagree. `JSON.stringify` preserves insertion order, so the two cannot drift.
|
|
96
|
-
*
|
|
97
|
-
* @returns {string} JSON, LF-terminated
|
|
98
|
-
*/
|
|
99
|
-
export function renderTsconfigBase() {
|
|
100
|
-
return `${JSON.stringify({ compilerOptions: STRICTNESS_FLOOR }, null, 2)}\n`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
53
|
/**
|
|
104
54
|
* Parse a file that TypeScript allows to carry comments.
|
|
105
55
|
*
|
|
@@ -170,21 +120,19 @@ export function readJsonc(text) {
|
|
|
170
120
|
}
|
|
171
121
|
|
|
172
122
|
/**
|
|
173
|
-
* What this repo's base file
|
|
123
|
+
* What this repo's base file is, and what `init` will do about it.
|
|
174
124
|
*
|
|
175
|
-
* There is no `refresh
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* mistake as a gate that edits its own expectations.
|
|
125
|
+
* There is no `refresh`, and since review 0003 Q28 there is no `create` either: the
|
|
126
|
+
* file is the repo's to write, and a floor the tool created was one nothing extended.
|
|
127
|
+
* `init`'s whole contribution is now to say so in the plan — a file that appears after
|
|
128
|
+
* the run without being in the plan is a silent write, and "init will not write this"
|
|
129
|
+
* is a plan line, not an absence of one.
|
|
181
130
|
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* claims to be an authority and is read by nothing.
|
|
131
|
+
* `unchanged` means the file exists and is left exactly as it is: this tool does not
|
|
132
|
+
* own its bytes (ADR-026), so a repo that edited them is not drift.
|
|
185
133
|
*
|
|
186
134
|
* @param {string} root repo root
|
|
187
|
-
* @returns {Promise<{file:string, path:string, action:'
|
|
135
|
+
* @returns {Promise<{file:string, path:string, action:'unchanged'|'skip',
|
|
188
136
|
* exists:boolean, bytes:number, current:boolean, typescript:boolean,
|
|
189
137
|
* reason:string|null}>}
|
|
190
138
|
*/
|
|
@@ -212,16 +160,19 @@ export async function planTsconfigBase(root) {
|
|
|
212
160
|
reason: null,
|
|
213
161
|
};
|
|
214
162
|
}
|
|
163
|
+
// Absent is a skip in every case: init no longer creates the file (review 0003 Q28),
|
|
164
|
+
// so the plan states what it will not write and why, rather than leaving a floor
|
|
165
|
+
// behind that nothing extends.
|
|
215
166
|
return {
|
|
216
167
|
file: TSCONFIG_BASE_FILE,
|
|
217
168
|
path,
|
|
218
|
-
action:
|
|
169
|
+
action: 'skip',
|
|
219
170
|
exists,
|
|
220
171
|
bytes: 0,
|
|
221
|
-
current:
|
|
172
|
+
current: true,
|
|
222
173
|
typescript,
|
|
223
174
|
reason: typescript
|
|
224
|
-
?
|
|
175
|
+
? 'init no longer creates the base file; a floor is a policy the repo writes for itself'
|
|
225
176
|
: 'no TypeScript in this repository, so nothing would extend the base',
|
|
226
177
|
};
|
|
227
178
|
}
|
|
@@ -249,23 +200,12 @@ export async function checkTsconfig(root) {
|
|
|
249
200
|
baseExists = false;
|
|
250
201
|
}
|
|
251
202
|
|
|
252
|
-
const { stack } = await fingerprint(root);
|
|
253
|
-
const typescript = stack.includes('typescript');
|
|
254
|
-
if (typescript && !baseExists) {
|
|
255
|
-
findings.push({
|
|
256
|
-
file: TSCONFIG_BASE_FILE,
|
|
257
|
-
status: 'absent',
|
|
258
|
-
detail:
|
|
259
|
-
'this repo is TypeScript and has no base file, so every strictness flag is whatever ' +
|
|
260
|
-
'one person happened to write',
|
|
261
|
-
remedy: `${CLI} init --yes`,
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
203
|
// Nothing below reads the base file's contents, and that is deliberate. This tool does not
|
|
266
204
|
// own those bytes (ADR-026), so a repo that edited them is not drift — and a finding that
|
|
267
205
|
// compared them would report somebody's work as an error. Only the question of whether
|
|
268
|
-
// `tsconfig.json` points at the base is this tool's to ask.
|
|
206
|
+
// `tsconfig.json` points at the base is this tool's to ask. A missing base file raises
|
|
207
|
+
// no finding at all (review 0003 Q28): writing one is the repo's decision, and a gate
|
|
208
|
+
// whose remedy is a policy choice is a gate nothing can satisfy.
|
|
269
209
|
let own = null;
|
|
270
210
|
try {
|
|
271
211
|
own = readJsonc(await readFile(join(root, TSCONFIG_FILE), 'utf8'));
|
package/src/workspace.mjs
CHANGED
|
@@ -38,7 +38,8 @@ export const PACKAGE_ROOT = resolve(HERE, '..');
|
|
|
38
38
|
// other LOCKED on-disk names, and a second copy here is a name that can drift from the
|
|
39
39
|
// list of names that are allowed to move. Imported as well as re-exported, because a
|
|
40
40
|
// bare `export { X } from` does not bind X here and every use below would fail.
|
|
41
|
-
import { CONFIG_DIR } from './name.mjs';
|
|
41
|
+
import { CONFIG_DIR, MANIFEST_FILE } from './name.mjs';
|
|
42
|
+
import { manifestPath, readManifest } from './projection/manifest.mjs';
|
|
42
43
|
export { CONFIG_DIR };
|
|
43
44
|
export const CONFIG_FILE = 'config.json';
|
|
44
45
|
|
|
@@ -133,14 +134,25 @@ async function looksLikeWorkspace(dir) {
|
|
|
133
134
|
/**
|
|
134
135
|
* Resolve the workspace.
|
|
135
136
|
*
|
|
136
|
-
* Precedence, highest first: `--workspace`,
|
|
137
|
-
*
|
|
138
|
-
*
|
|
137
|
+
* Precedence, highest first (ADR-119): `--workspace`, the repository's own manifest
|
|
138
|
+
* declaration, `SIMT_DEV_WORKSPACE` - a value that names a directory is honoured only
|
|
139
|
+
* where the manifest declares a workspace, because the variable is a pointer INTO a
|
|
140
|
+
* workspace the repo opted into, not one imposed on a repo that never declared one;
|
|
141
|
+
* a value that declines is honoured from anywhere, because a decline points at
|
|
142
|
+
* nothing - the manifest's value, the user config file, then the sibling heuristic
|
|
143
|
+
* of this package, which is accepted only if it actually contains a git repository.
|
|
139
144
|
*
|
|
145
|
+
* A relative manifest value resolves against the manifest's directory, not the
|
|
146
|
+
* process's, so `workspace: ".."` means the same thing from any subdirectory.
|
|
147
|
+
*
|
|
148
|
+
* @param {{repoRoot?: string}} [options] `repoRoot` names the repository whose manifest
|
|
149
|
+
* declares the workspace; it defaults to `process.cwd()`, which is the repository
|
|
150
|
+
* for the way every command is run.
|
|
140
151
|
* @returns {Promise<{root: string|null, source: string, detail: string, searched: string[], error: string|null}>}
|
|
141
152
|
*/
|
|
142
153
|
export async function resolveWorkspace(flags = {}, options = {}) {
|
|
143
154
|
const env = options.env ?? process.env;
|
|
155
|
+
const repoRoot = options.repoRoot ?? process.cwd();
|
|
144
156
|
const searched = configPaths(options);
|
|
145
157
|
|
|
146
158
|
// The config file is read FIRST, before precedence is applied, and a parse failure
|
|
@@ -169,16 +181,49 @@ export async function resolveWorkspace(flags = {}, options = {}) {
|
|
|
169
181
|
);
|
|
170
182
|
}
|
|
171
183
|
|
|
184
|
+
// The repository's own declaration (ADR-119). It is read before the environment
|
|
185
|
+
// variable because it is what the variable is allowed to point into: a repo that
|
|
186
|
+
// declared no workspace is a single repo with no workspace, and an environment
|
|
187
|
+
// variable cannot turn it into a member of a workspace nobody reviewed.
|
|
188
|
+
const manifest = await readManifest(repoRoot);
|
|
189
|
+
if (manifest.error) {
|
|
190
|
+
return {
|
|
191
|
+
root: null,
|
|
192
|
+
source: 'repo',
|
|
193
|
+
detail: await manifestPath(repoRoot),
|
|
194
|
+
searched,
|
|
195
|
+
error: manifest.error,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
let repoValue = null;
|
|
199
|
+
if (manifest.exists && typeof manifest.manifest.workspace === 'string' && manifest.manifest.workspace.trim() !== '') {
|
|
200
|
+
const off = disabled(manifest.manifest.workspace, 'repo', `${MANIFEST_FILE} workspace`, searched);
|
|
201
|
+
if (off) return off; // the repo declined; the environment variable cannot enforce one
|
|
202
|
+
repoValue = manifest.manifest.workspace;
|
|
203
|
+
}
|
|
204
|
+
|
|
172
205
|
if (env.SIMT_DEV_WORKSPACE) {
|
|
173
|
-
|
|
174
|
-
|
|
206
|
+
const off = disabled(env.SIMT_DEV_WORKSPACE, 'env', 'SIMT_DEV_WORKSPACE', searched);
|
|
207
|
+
if (off) return off; // a decline points at nothing, so it needs no declaration to work
|
|
208
|
+
if (repoValue !== null) {
|
|
209
|
+
return {
|
|
175
210
|
root: absolute(env.SIMT_DEV_WORKSPACE),
|
|
176
211
|
source: 'env',
|
|
177
212
|
detail: 'SIMT_DEV_WORKSPACE',
|
|
178
213
|
searched,
|
|
179
214
|
error: null,
|
|
180
|
-
}
|
|
181
|
-
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (repoValue !== null) {
|
|
220
|
+
return {
|
|
221
|
+
root: absolute(repoValue, repoRoot),
|
|
222
|
+
source: 'repo',
|
|
223
|
+
detail: `${MANIFEST_FILE} workspace`,
|
|
224
|
+
searched,
|
|
225
|
+
error: null,
|
|
226
|
+
};
|
|
182
227
|
}
|
|
183
228
|
|
|
184
229
|
if (user.exists && typeof user.config.workspace === 'string' && user.config.workspace.length > 0) {
|
|
@@ -245,11 +290,11 @@ export const WORKSPACE_REMEDY = (() => {
|
|
|
245
290
|
return `--workspace <path>, the SIMT_DEV_WORKSPACE environment variable, or ${where}`;
|
|
246
291
|
})();
|
|
247
292
|
|
|
248
|
-
function absolute(p) {
|
|
293
|
+
function absolute(p, base = process.cwd()) {
|
|
249
294
|
const expanded = p.startsWith('~/')
|
|
250
295
|
? join(homedir(), p.slice(2))
|
|
251
296
|
: p;
|
|
252
|
-
return isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
297
|
+
return isAbsolute(expanded) ? expanded : resolve(base, expanded);
|
|
253
298
|
}
|
|
254
299
|
|
|
255
300
|
/**
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"states": [
|
|
19
19
|
{ "id": "queued", "write": "Queued", "read": ["queued"] },
|
|
20
|
-
{ "id": "in-progress", "write": "In progress", "read": ["in progress", "in-progress"] },
|
|
20
|
+
{ "id": "in-progress", "write": "In progress", "read": ["in progress", "in-progress"], "role": "started" },
|
|
21
21
|
{ "id": "complete", "write": "Complete", "read": ["complete", "completed", "done"], "satisfies": true },
|
|
22
22
|
{ "id": "accepted", "write": "Accepted", "read": ["accepted"] }
|
|
23
23
|
],
|