spec-ctx 0.1.1 → 0.2.0
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 +44 -29
- package/dist/adopt/plan.js +26 -14
- package/dist/assets.generated.js +7 -7
- package/dist/baseline/baseline.js +35 -9
- package/dist/baseline/ratchet.js +81 -0
- package/dist/check/check.js +50 -2
- package/dist/check/hints.js +7 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/commands.js +36 -12
- package/dist/cli/gate.js +51 -33
- package/dist/cli/help.js +12 -12
- package/dist/cli/status.js +5 -21
- package/dist/cli/workflow.js +50 -41
- package/dist/config.js +61 -1
- package/dist/graph/query.js +4 -1
- package/dist/handoff/handoff.js +38 -23
- package/dist/handoff/questions.js +99 -0
- package/dist/ids/ids.js +11 -6
- package/dist/index/scan.js +13 -8
- package/dist/init/init.js +283 -35
- package/dist/lint/counts.js +60 -0
- package/dist/loop/fails.js +91 -0
- package/dist/loop/next.js +173 -0
- package/dist/loop/openspec.js +41 -0
- package/dist/stats/stats.js +3 -1
- package/dist/tasks/evidence.js +5 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/schemas/spec-driven-ids/README.md +12 -12
- package/schemas/spec-driven-ids/config-fragment.yaml +3 -2
- package/skills/specctx-bootstrap/SKILL.md +1 -22
- package/skills/specctx-done/SKILL.md +1 -20
- package/skills/specctx-next/SKILL.md +1 -18
- package/skills/specctx-run/SKILL.md +37 -0
- package/schemas/spec-driven-ids/schema.yaml +0 -301
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ You see: a red refusal, the edit, then a green done line.
|
|
|
75
75
|
|
|
76
76
|
### 6. Hand off to the next session
|
|
77
77
|
|
|
78
|
-
`handoff write` puts the state in one file: the change, the done tasks, the current task, and the open questions. The next session starts from it.
|
|
78
|
+
`handoff write` puts the state in one file: the change, the done tasks, the current task, and the open questions. The next session starts from it. The questions live in `.ctx/questions.json`. `handoff ask` adds one and `handoff resolve <n>` answers it.
|
|
79
79
|
|
|
80
80
|

|
|
81
81
|
|
|
@@ -86,7 +86,7 @@ The transcript of all six clips is in `docs/demo/loop.txt`. The clips run on a c
|
|
|
86
86
|
## Quickstart
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
-
npx -y spec-ctx init --all # config,
|
|
89
|
+
npx -y spec-ctx init --all # config, ID templates, rules, and the agent skill
|
|
90
90
|
npx -y spec-ctx task next # the next task and the command that proves it
|
|
91
91
|
npx -y spec-ctx task start <task> # only the spec that this task needs
|
|
92
92
|
npx -y spec-ctx task done <task> # runs that command. A pass ticks the box.
|
|
@@ -120,25 +120,25 @@ test tests/login.test.ts:1 active tests/login.test.ts:1 covers REQ-UA-004
|
|
|
120
120
|
|
|
121
121
|
## The life cycle for agents
|
|
122
122
|
|
|
123
|
-
`init --all` installs
|
|
123
|
+
`init --all` installs one skill, `specctx-run`. The user types one prompt, such as "build this" or "continue". The skill loops until the change reaches the archive or a human must decide.
|
|
124
124
|
|
|
125
|
-
- `specctx
|
|
126
|
-
-
|
|
127
|
-
-
|
|
125
|
+
- It runs `specctx next --json`. The engine reads the files on disk and prints one action: `plan`, `cite`, `start`, `work`, `fix`, `repair`, `gate`, `archive`, `ask`, or `stop`.
|
|
126
|
+
- It acts on the action with the exact commands, runs `specctx handoff write`, and repeats.
|
|
127
|
+
- It stops twice for a human by default: at the plan, and before archive. A Verify command that fails three times writes a question and stops too.
|
|
128
|
+
- `specctx-bootstrap`, `specctx-next`, and `specctx-done` stay for one release as aliases. Each one says: run `specctx-run`.
|
|
128
129
|
|
|
129
|
-
The
|
|
130
|
+
The commands the skill runs for one task:
|
|
130
131
|
|
|
131
132
|
```bash
|
|
132
|
-
specctx init --all # config,
|
|
133
|
-
specctx
|
|
134
|
-
specctx task next # the next open task, its REQ IDs, and its Verify command
|
|
133
|
+
specctx init --all # config, ID templates, rules, and the skill
|
|
134
|
+
specctx next --json # the next action, its task, and its command
|
|
135
135
|
specctx task start <task> # the bundle of that task and its size
|
|
136
136
|
specctx task done <task> # run Verify, write evidence, set the checkbox
|
|
137
|
-
specctx
|
|
137
|
+
specctx gate --strict # check, baseline, linters, openspec validate, length
|
|
138
138
|
specctx handoff write # .ctx/handoff.md for the next session
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
The rule: the agent never opens `openspec/specs/` by hand. It uses `task start` or `bundle`. The
|
|
141
|
+
The rule: the agent never opens `openspec/specs/` by hand. It uses `task start` or `bundle`. It never ticks a checkbox and never runs `/opsx:apply`. The skill is in [`skills/`](skills/). Install it alone with `npx skills add sonqh/spec-ctx` or `specctx init --skills`.
|
|
142
142
|
|
|
143
143
|
## Install
|
|
144
144
|
|
|
@@ -152,27 +152,30 @@ npx -y github:sonqh/spec-ctx#<sha> init --all # from git, for an unreleas
|
|
|
152
152
|
|
|
153
153
|
The git form builds the package on install with Node only. Pin a commit SHA. A branch name such as `main` also works, but it moves.
|
|
154
154
|
|
|
155
|
-
The command writes `spec-ctx.toml`,
|
|
155
|
+
The command writes `spec-ctx.toml`, copies the templates with ID placeholders to `openspec/templates/spec-driven-ids/`, adds the spec-ctx `rules` and `operations` to `openspec/config.yaml`, and installs the `specctx-run` skill and its three aliases into `.claude/skills/` and `.agents/skills/`. The project keeps the stock `schema: spec-driven`. Upstream OpenSpec injects the rules into every artifact instruction. It pins the OpenSpec version it finds in `[gate] openspec`. It adds four lines to `.gitignore`: `.ctx/index.json`, `adopt-report.md`, `.ctx/current.json`, and `.ctx/gate-log.jsonl`. The rest of `.ctx/` is meant for git, `questions.json` included.
|
|
156
156
|
|
|
157
157
|
The examples below use `specctx`. With `npx`, write `npx -y spec-ctx` in its place. Each GitHub release also carries a single binary for Linux and macOS.
|
|
158
158
|
|
|
159
159
|
## Two paths
|
|
160
160
|
|
|
161
|
+
[`docs/getting-started.md`](docs/getting-started.md) walks the fresh path on upstream OpenSpec with real output, for a developer who knows OpenSpec.
|
|
162
|
+
|
|
161
163
|
### Fresh project
|
|
162
164
|
|
|
163
165
|
```bash
|
|
164
|
-
npx -y @fission-ai/openspec@latest init
|
|
166
|
+
npx -y @fission-ai/openspec@latest init --tools <tool>
|
|
165
167
|
npx spec-ctx init --all
|
|
166
168
|
specctx gate --strict
|
|
167
169
|
```
|
|
168
170
|
|
|
171
|
+
`--tools <tool>` names your agent, such as `claude`, and skips the interactive picker. `--no-animation` keeps logs clean. `init --all` merges the ID rules into an existing `rules` key of `openspec/config.yaml` item by item and prints `merged openspec/config.yaml rules (<n> added)`.
|
|
172
|
+
|
|
169
173
|
### Existing OpenSpec project
|
|
170
174
|
|
|
171
175
|
```bash
|
|
172
176
|
npx spec-ctx init --all
|
|
173
177
|
specctx adopt --dry-run # the ID that each header gets. Writes nothing.
|
|
174
178
|
specctx adopt # writes the IDs into main specs and active deltas
|
|
175
|
-
specctx task backfill --manual "adopted"
|
|
176
179
|
specctx baseline write
|
|
177
180
|
specctx gate --strict
|
|
178
181
|
```
|
|
@@ -180,11 +183,12 @@ specctx gate --strict
|
|
|
180
183
|
- `adopt` gives an ID to every requirement and scenario header without one, in `openspec/specs/` and in the deltas of active changes. A delta header gets the main ID by its text or through its RENAMED line. A delta header that matches neither keeps its text and goes into `adopt-report.md`.
|
|
181
184
|
- `adopt` derives a capability code from each folder name, for example `UA` for `user-auth`. It writes the codes into `spec-ctx.toml`. Rename a code there before the IDs spread.
|
|
182
185
|
- `adopt` runs `check` before and after the write. A finding that the write added undoes every file. A second run prints `nothing to adopt`.
|
|
183
|
-
- `
|
|
184
|
-
- `baseline
|
|
186
|
+
- `baseline write` records the debt of the day in `.ctx/baseline.json`. The debt has five kinds. Ticked tasks without evidence. Legacy tasks without a citation or a Verify code span. Delta headers that match no main requirement. Hard lint findings per file and rule on the gate globs. `check` hides that debt and prints `baselined: <n>`. `gate` fails the lint step only when a count rises. New debt still shows.
|
|
187
|
+
- `gate` compares `.ctx/baseline.json` with the copy at the merge base of `HEAD` and `[gate] base`, default `origin/main`. A new entry fails the `baseline` step. Commit a grown baseline with a subject that starts with `baseline:` to pass once. In CI, check out with `fetch-depth: 0`, so the merge base exists. Without git or a merge base the step prints `skipped`.
|
|
188
|
+
- `[gate] markdown` in `spec-ctx.toml` names the markdown globs that `gate` lints. `init` writes `[]`, so the gate lints nothing until you opt in. The comment in the file names the globs this repository uses.
|
|
185
189
|
- Legacy tasks stay in the baseline until an agent adds `[REQ-...]` citations and a `Verify:` code span. `specctx baseline prune` then drops them. No command adds a single entry.
|
|
186
190
|
|
|
187
|
-
Both paths have an end to end test: `tests/paths/fresh.sh` and `tests/paths/existing.sh`.
|
|
191
|
+
Both paths have an end to end test on upstream OpenSpec: `tests/paths/fresh.sh` and `tests/paths/existing.sh`. `tests/paths/rules-only.sh` shows that the merged `rules` reach the stock `spec-driven` schema.
|
|
188
192
|
|
|
189
193
|
## Commands
|
|
190
194
|
|
|
@@ -192,18 +196,18 @@ Both paths have an end to end test: `tests/paths/fresh.sh` and `tests/paths/exis
|
|
|
192
196
|
| --- | --- |
|
|
193
197
|
| `build` | Write `.ctx/index.json`. Every other command rebuilds a stale index first. |
|
|
194
198
|
| `status` | Active changes with task counts, the current task, and open questions. |
|
|
199
|
+
| `next` | The next action of the loop from the files on disk: `plan`, `cite`, `start`, `work`, `fix`, `repair`, `gate`, `archive`, `ask`, or `stop`. `--json` prints one object. Flag: `--change`. |
|
|
195
200
|
| `query <term...>` | Entries that hold every term. Flags: `--kind`, `--change`, `--limit`, `--all`. |
|
|
196
201
|
| `related <ID>` | Linked entries with the reason for each link. Flags: `--depth`, `--all`. |
|
|
197
202
|
| `bundle <ID...>` | Spec text of the IDs, their scenarios, decisions, and neighbors. Flags: `--budget`, `--depth`, `--all`. |
|
|
198
|
-
| `check` | ID and link integrity. `--strict` adds test coverage and evidence. |
|
|
203
|
+
| `check` | ID and link integrity. `--strict` adds test coverage and evidence. `retitle-needs-renamed` names a retitled delta header that needs a RENAMED pair before `openspec archive`. |
|
|
199
204
|
| `id new`, `id retire` | Allocate and retire IDs. |
|
|
200
205
|
| `task next`, `start`, `verify`, `done`, `reopen` | The task state machine. |
|
|
201
|
-
| `handoff write`, `ask`, `resolve` | The session handoff and its open questions. |
|
|
202
|
-
| `init` | Config file,
|
|
206
|
+
| `handoff write`, `ask`, `resolve` | The session handoff and its open questions in `.ctx/questions.json`. |
|
|
207
|
+
| `init` | Config file, ID templates and rules, and skills. Flags: `--schema`, `--skills`, `--all`, `--force`. |
|
|
203
208
|
| `adopt` | IDs for a project without them. `--dry-run` writes nothing. |
|
|
204
209
|
| `baseline write`, `prune` | The debt ratchet in `.ctx/baseline.json`. |
|
|
205
|
-
| `
|
|
206
|
-
| `gate` | `check`, both markdown linters, `openspec validate`, and the 500 character length check on active deltas. |
|
|
210
|
+
| `gate` | `check`, the baseline against the merge base, both markdown linters, `openspec validate`, and the 500 character length check on active deltas. |
|
|
207
211
|
| `lint tells`, `lint ste` | The two markdown linters. Flags: `--baseline`, `--disable`, `--json`, `--selftest`. |
|
|
208
212
|
| `help <command>` | The page of one command. `specctx <command> --help` prints the same page. |
|
|
209
213
|
|
|
@@ -225,6 +229,7 @@ ignore = [] # globs that the scan skips
|
|
|
225
229
|
[ids]
|
|
226
230
|
caps = [] # capability codes. Empty means any code is valid.
|
|
227
231
|
task_pattern = '\d+\.\d+' # the task number in a tasks.md checkbox
|
|
232
|
+
brackets = false # true: adopt and id new write [REQ-UA-001]. The scan reads both forms.
|
|
228
233
|
|
|
229
234
|
[ids.paths]
|
|
230
235
|
UA = "user-auth" # capability path of each code. adopt writes the derived ones.
|
|
@@ -235,13 +240,23 @@ depth = 1
|
|
|
235
240
|
|
|
236
241
|
[verify]
|
|
237
242
|
timeout_s = 600
|
|
243
|
+
|
|
244
|
+
[gate]
|
|
245
|
+
markdown = [] # globs that gate lints. Empty means no lint. This repo opts in.
|
|
246
|
+
openspec = "npx -y @fission-ai/openspec@1.15.0" # the command that runs OpenSpec. init pins the version it found.
|
|
247
|
+
base = "origin/main" # the ref whose merge base holds the baseline that gate compares with
|
|
248
|
+
|
|
249
|
+
[loop]
|
|
250
|
+
max_attempts = 3 # Verify failures on one task before task done writes a question
|
|
251
|
+
archive = "ask" # "ask" stops before archive for a human. "auto" lets the loop archive.
|
|
238
252
|
```
|
|
239
253
|
|
|
240
|
-
|
|
254
|
+
Two environment variables change the engine:
|
|
241
255
|
|
|
242
256
|
| Variable | Effect |
|
|
243
257
|
| --- | --- |
|
|
244
|
-
| `SPECCTX_OPENSPEC` | The command that `gate` runs for `openspec validate
|
|
258
|
+
| `SPECCTX_OPENSPEC` | The command that `gate` runs for `openspec validate` and `next` runs for `openspec status`. It overrides `[gate] openspec` for one run. At `init` time it is written into the file as it is, and no version detection runs. Tests and offline machines set it. |
|
|
259
|
+
| `SPECCTX_BASELINE_SUBJECT` | The commit subject that the `baseline` step of `gate` tests for `baseline:`. It replaces the subject of `HEAD`. Tests set it. |
|
|
245
260
|
|
|
246
261
|
## Markdown linters
|
|
247
262
|
|
|
@@ -269,18 +284,18 @@ The action runs `specctx gate`: `check --strict`, both markdown linters, `opensp
|
|
|
269
284
|
|
|
270
285
|
## Status
|
|
271
286
|
|
|
272
|
-
- Published: `spec-ctx@0.
|
|
273
|
-
- Archived: 001 to
|
|
287
|
+
- Published: `spec-ctx@0.2.0` on npm, with tags `v0.2.0` and `v0` and binaries on the GitHub release.
|
|
288
|
+
- Archived: 001 to 025, and 027. No change is open. See `CHANGELOG.md` for each release.
|
|
274
289
|
- A version bump on `main` publishes the next release. See `docs/CONTRIBUTING.md`.
|
|
275
290
|
- Upstream: the ID scheme is proposed as a comment on [OpenSpec#1967](https://github.com/Fission-AI/OpenSpec/issues/1967#issuecomment-6414314730). The schema bundle is submitted to the community table in [OpenSpec#2157](https://github.com/Fission-AI/OpenSpec/issues/2157). No pull request until a maintainer asks.
|
|
276
291
|
|
|
277
292
|
## Measured
|
|
278
293
|
|
|
279
|
-
`specctx stats --write` measures this repository after each change. The newest file is `docs/metrics/2026-10-
|
|
294
|
+
`specctx stats --write` measures this repository after each change. The newest file is `docs/metrics/2026-10-29.md`. Its total row:
|
|
280
295
|
|
|
281
296
|
| change | context_ratio | evidence_ratio | stale_catches | cycle_time_median_s | debt |
|
|
282
297
|
| --- | --- | --- | --- | --- | --- |
|
|
283
|
-
| total | 0.
|
|
298
|
+
| total | 0.0327 (n=76) | 0.90 (143/159) | 36 (of 81 reopens) | 98 (n=76) | 0 (0 baselined + 0 new) |
|
|
284
299
|
|
|
285
300
|
- `context_ratio`: the size of one task bundle divided by the size of all spec text. Lower is better.
|
|
286
301
|
- `evidence_ratio`: done tasks with a passed Verify command, divided by all done tasks. The rest have a manual note. Higher is better.
|
package/dist/adopt/plan.js
CHANGED
|
@@ -8,6 +8,9 @@ const REQ_HDR = /^### Requirement:[ \t]*(.*)$/s;
|
|
|
8
8
|
const SCN_HDR = /^#### Scenario:[ \t]*(.*)$/s;
|
|
9
9
|
const HAS_REQ = /^REQ-([A-Z]{2,6})-(\d{3})(?:[ \t]|$)/;
|
|
10
10
|
const HAS_SCN = /^SCN-([A-Z]{2,6})-(\d{3})-(\d+)(?:[ \t]|$)/;
|
|
11
|
+
// REQ-ADOPT-010 an ID in square brackets, the form of OpenSpec PR #1979 (027/D3)
|
|
12
|
+
const BR_REQ = /^\[(REQ-[A-Z]{2,6}-\d{3})\](?:[ \t]|$)/;
|
|
13
|
+
const BR_SCN = /^\[(SCN-[A-Z]{2,6}-\d{3}-\d+)\](?:[ \t]|$)/;
|
|
11
14
|
const RENAME = /^- (FROM|TO):[ \t]*`### Requirement:[ \t]*(.*)`[ \t]*$/s;
|
|
12
15
|
function listSpecs(root, base) {
|
|
13
16
|
const out = [];
|
|
@@ -26,12 +29,15 @@ function listSpecs(root, base) {
|
|
|
26
29
|
walk(join(root, base), base);
|
|
27
30
|
return out;
|
|
28
31
|
}
|
|
29
|
-
|
|
32
|
+
/** REQ-ADOPT-010 the ID at the start of a header text, bare or in square brackets, and the name after it. */
|
|
33
|
+
function splitId(text, re, br) {
|
|
30
34
|
const m = re.exec(text);
|
|
31
|
-
if (
|
|
32
|
-
return { id:
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
+
if (m)
|
|
36
|
+
return { id: m[0].trim(), name: text.slice(m[0].length).trim(), bracketed: false };
|
|
37
|
+
const b = br.exec(text);
|
|
38
|
+
if (b)
|
|
39
|
+
return { id: b[1], name: text.slice(b[0].length).trim(), bracketed: true };
|
|
40
|
+
return { id: null, name: text.trim(), bracketed: false };
|
|
35
41
|
}
|
|
36
42
|
function parse(root, rel, cap) {
|
|
37
43
|
const lines = splitLines(readFileSync(join(root, rel)).toString("utf8"));
|
|
@@ -53,21 +59,21 @@ function parse(root, rel, cap) {
|
|
|
53
59
|
}
|
|
54
60
|
let m = REQ_HDR.exec(line);
|
|
55
61
|
if (m) {
|
|
56
|
-
const { id, name } = splitId(m[1], HAS_REQ);
|
|
57
|
-
req = { kind: "req", line: i + 1, raw: line, name, id, section };
|
|
62
|
+
const { id, name, bracketed } = splitId(m[1], HAS_REQ, BR_REQ);
|
|
63
|
+
req = { kind: "req", line: i + 1, raw: line, name, id, bracketed, section };
|
|
58
64
|
blocks.push(req);
|
|
59
65
|
return;
|
|
60
66
|
}
|
|
61
67
|
m = SCN_HDR.exec(line);
|
|
62
68
|
if (m) {
|
|
63
|
-
const { id, name } = splitId(m[1], HAS_SCN);
|
|
64
|
-
blocks.push({ kind: "scn", line: i + 1, raw: line, name, id, req, section });
|
|
69
|
+
const { id, name, bracketed } = splitId(m[1], HAS_SCN, BR_SCN);
|
|
70
|
+
blocks.push({ kind: "scn", line: i + 1, raw: line, name, id, bracketed, req, section });
|
|
65
71
|
return;
|
|
66
72
|
}
|
|
67
73
|
m = RENAME.exec(line);
|
|
68
74
|
if (m && section === "RENAMED") {
|
|
69
|
-
const { id, name } = splitId(m[2], HAS_REQ);
|
|
70
|
-
const item = { line: i + 1, name, id, raw: line };
|
|
75
|
+
const { id, name, bracketed } = splitId(m[2], HAS_REQ, BR_REQ);
|
|
76
|
+
const item = { line: i + 1, name, id, bracketed, raw: line };
|
|
71
77
|
if (m[1] === "FROM")
|
|
72
78
|
from = item;
|
|
73
79
|
else if (from) {
|
|
@@ -189,11 +195,15 @@ export function planAdopt(root, cfg) {
|
|
|
189
195
|
};
|
|
190
196
|
const edits = [];
|
|
191
197
|
const unmatched = [];
|
|
192
|
-
|
|
193
|
-
const
|
|
198
|
+
// REQ-ADOPT-010 the configured form of a new ID: bare, or in square brackets (027/D3)
|
|
199
|
+
const form = (id, br = cfg.ids.brackets) => (br ? `[${id}]` : id);
|
|
200
|
+
const reqHeader = (id, name, br) => `### Requirement: ${form(id, br)} ${name}`;
|
|
201
|
+
const scnHeader = (id, name) => `#### Scenario: ${form(id)} ${name}`;
|
|
194
202
|
// main spec: requirement id by name, and scenario id by requirement and name
|
|
195
203
|
const mainReq = new Map();
|
|
196
204
|
const mainScn = new Map();
|
|
205
|
+
// the form of each main header after adopt: its own form when it had an ID, else the configured form
|
|
206
|
+
const mainBr = new Map();
|
|
197
207
|
for (const f of mainFiles) {
|
|
198
208
|
const code = codeFor.get(f.cap);
|
|
199
209
|
const seen = new Set();
|
|
@@ -210,6 +220,7 @@ export function planAdopt(root, cfg) {
|
|
|
210
220
|
if (!b.id)
|
|
211
221
|
edits.push({ file: f.rel, line: b.line, old: b.raw, new: reqHeader(id, b.name) });
|
|
212
222
|
mainReq.set(`${f.cap}\u0000${b.name}`, id);
|
|
223
|
+
mainBr.set(`${f.cap}\u0000${b.name}`, b.id ? b.bracketed : cfg.ids.brackets);
|
|
213
224
|
b.id = id;
|
|
214
225
|
}
|
|
215
226
|
else if (b.req?.id) {
|
|
@@ -232,8 +243,9 @@ export function planAdopt(root, cfg) {
|
|
|
232
243
|
continue;
|
|
233
244
|
}
|
|
234
245
|
renamedTo.set(r.to.name, id);
|
|
246
|
+
// REQ-ADOPT-010 the FROM line names the main header, so it takes the form of that header (027/D3)
|
|
235
247
|
if (!r.from.id)
|
|
236
|
-
edits.push({ file: f.rel, line: r.from.line, old: r.from.raw, new: `- FROM: \`${reqHeader(id, r.from.name)}\`` });
|
|
248
|
+
edits.push({ file: f.rel, line: r.from.line, old: r.from.raw, new: `- FROM: \`${reqHeader(id, r.from.name, mainBr.get(`${f.cap}\u0000${r.from.name}`))}\`` });
|
|
237
249
|
if (!r.to.id)
|
|
238
250
|
edits.push({ file: f.rel, line: r.to.line, old: r.to.raw, new: `- TO: \`${reqHeader(id, r.to.name)}\`` });
|
|
239
251
|
}
|
package/dist/assets.generated.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// Generated by scripts/gen-assets.mjs from schemas/spec-driven-ids/ and skills/. Do not edit.
|
|
2
|
-
// REQ-SCH-
|
|
2
|
+
// REQ-SCH-003 REQ-SKL-002
|
|
3
3
|
export const ASSETS = {
|
|
4
|
-
"schemas/spec-driven-ids/README.md": "
|
|
5
|
-
"schemas/spec-driven-ids/config-fragment.yaml": "
|
|
6
|
-
"schemas/spec-driven-ids/schema.yaml": "bmFtZTogc3BlYy1kcml2ZW4taWRzCnZlcnNpb246IDEKZGVzY3JpcHRpb246IE9wZW5TcGVjIHNwZWMtZHJpdmVuIHdvcmtmbG93IHdpdGggc3RhYmxlIElEcyBmcm9tIHNwZWMtY3R4IC0gcHJvcG9zYWwg4oaSIHNwZWNzIOKGkiBkZXNpZ24g4oaSIHRhc2tzCmFydGlmYWN0czoKICAtIGlkOiBwcm9wb3NhbAogICAgZ2VuZXJhdGVzOiBwcm9wb3NhbC5tZAogICAgZGVzY3JpcHRpb246IEluaXRpYWwgcHJvcG9zYWwgZG9jdW1lbnQgb3V0bGluaW5nIHRoZSBjaGFuZ2UKICAgIHRlbXBsYXRlOiBwcm9wb3NhbC5tZAogICAgaW5zdHJ1Y3Rpb246IHwKICAgICAgQ3JlYXRlIHRoZSBwcm9wb3NhbCBkb2N1bWVudCB0aGF0IGVzdGFibGlzaGVzIFdIWSB0aGlzIGNoYW5nZSBpcyBuZWVkZWQuCgogICAgICBTZWN0aW9uczoKICAgICAgLSAqKldoeSoqOiAxLTIgc2VudGVuY2VzIG9uIHRoZSBwcm9ibGVtIG9yIG9wcG9ydHVuaXR5LiBXaGF0IHByb2JsZW0gZG9lcyB0aGlzIHNvbHZlPyBXaHkgbm93PwogICAgICAtICoqV2hhdCBDaGFuZ2VzKio6IEJ1bGxldCBsaXN0IG9mIGNoYW5nZXMuIEJlIHNwZWNpZmljIGFib3V0IG5ldyBjYXBhYmlsaXRpZXMsIG1vZGlmaWNhdGlvbnMsIG9yIHJlbW92YWxzLiBNYXJrIGJyZWFraW5nIGNoYW5nZXMgd2l0aCAqKkJSRUFLSU5HKiouCiAgICAgIC0gKipDYXBhYmlsaXRpZXMqKjogSWRlbnRpZnkgd2hpY2ggc3BlY3Mgd2lsbCBiZSBjcmVhdGVkIG9yIG1vZGlmaWVkOgogICAgICAgIC0gKipOZXcgQ2FwYWJpbGl0aWVzKio6IExpc3QgY2FwYWJpbGl0aWVzIGJlaW5nIGludHJvZHVjZWQuIEVhY2ggYmVjb21lcyBhIG5ldyBgc3BlY3MvPGNhcGFiaWxpdHktcGF0aD4vc3BlYy5tZGAuIE5hbWUgZWFjaCBjYXBhYmlsaXR5IGZvciBhIGR1cmFibGUgc3lzdGVtIGJlaGF2aW9yIChmb3IgZXhhbXBsZSwgYHVzZXItYXV0aGApLCBub3QgdGhlIHdvcmsgaW4gdGhpcyBjaGFuZ2UgKGZvciBleGFtcGxlLCBgYWRkLWxvZ2luLWVuZHBvaW50YCkuIENob29zZSBhIGNvaGVzaXZlIGJvdW5kYXJ5IHRoYXQgY2FuIG93biByZWxhdGVkIHJlcXVpcmVtZW50cyBhcyB0aGUgc3lzdGVtIGV2b2x2ZXM7IGF2b2lkIGJyb2FkIGNhdGNoLWFsbCBjYXBhYmlsaXRpZXMuIFVzZSBrZWJhYi1jYXNlIGZvciBwYXRoIHNlZ21lbnRzIHlvdSBpbnRyb2R1Y2UgKGUuZy4sIGB1c2VyLWF1dGhgIG9yIGBpZGVudGl0eS91c2VyLWF1dGhgKSBhbmQgZm9sbG93IHRoZSBwcm9qZWN0J3MgZXhpc3Rpbmcgc3BlYyBvcmdhbml6YXRpb24uCiAgICAgICAgLSAqKk1vZGlmaWVkIENhcGFiaWxpdGllcyoqOiBMaXN0IGV4aXN0aW5nIGNhcGFiaWxpdGllcyB3aG9zZSBSRVFVSVJFTUVOVFMgYXJlIGNoYW5naW5nLiBPbmx5IGluY2x1ZGUgaWYgc3BlYy1sZXZlbCBiZWhhdmlvciBjaGFuZ2VzIChub3QganVzdCBpbXBsZW1lbnRhdGlvbiBkZXRhaWxzKS4gRWFjaCBuZWVkcyBhIGRlbHRhIHNwZWMgZmlsZS4gVXNlIHRoZSBleGFjdCBleGlzdGluZyBwYXRoIHVuZGVyIGBvcGVuc3BlYy9zcGVjcy9gLiBMZWF2ZSBlbXB0eSBpZiBubyByZXF1aXJlbWVudCBjaGFuZ2VzLgogICAgICAtICoqSW1wYWN0Kio6IEFmZmVjdGVkIGNvZGUsIEFQSXMsIGRlcGVuZGVuY2llcywgb3Igc3lzdGVtcy4KCiAgICAgIElNUE9SVEFOVDogVGhlIENhcGFiaWxpdGllcyBzZWN0aW9uIGlzIGNyaXRpY2FsLiBJdCBjcmVhdGVzIHRoZSBjb250cmFjdCBiZXR3ZWVuCiAgICAgIHByb3Bvc2FsIGFuZCBzcGVjcyBwaGFzZXMuIFJlc2VhcmNoIGV4aXN0aW5nIHNwZWNzIGJlZm9yZSBmaWxsaW5nIHRoaXMgaW46CiAgICAgIHJ1biBgb3BlbnNwZWMgbGlzdCAtLXNwZWNzYCBmb3IgdGhlIHByb2plY3QncyBjYXBhYmlsaXR5IGludmVudG9yeSwgdGhlbgogICAgICBgb3BlbnNwZWMgc2hvdyAiPHNwZWMtaWQ+IiAtLXR5cGUgc3BlYyAtLWpzb24gLS1uby1zY2VuYXJpb3NgIGZvciBhbnkgdGhhdAogICAgICBsb29rIHJlbGF0ZWQgLSB0aGF0IHJldHVybnMgYSBjYXBhYmlsaXR5J3MgcHVycG9zZSBhbmQgcmVxdWlyZW1lbnQgdGV4dHMKICAgICAgd2l0aG91dCBwdWxsaW5nIHdob2xlIHNwZWMgZmlsZXMgaW50byBjb250ZXh0LiBBcHBlbmQgYC0tc3RvcmUgIjxpZD4iYCB0bwogICAgICBib3RoIGNvbW1hbmRzIG9ubHkgZm9yIGEgcmVnaXN0ZXJlZCBzdGFuZGFsb25lIHN0b3JlLCBhbmQga2VlcCBgLS10eXBlCiAgICAgIHNwZWNgOiBhIGNoYW5nZSBhbmQgYSBzcGVjIHNoYXJpbmcgYSBuYW1lIGlzIG90aGVyd2lzZSBhbiBhbWJpZ3VvdXMtaXRlbQogICAgICBlcnJvci4gYG9wZW5zcGVjIGxpc3RgIHdpdGhvdXQgYC0tc3BlY3NgIGxpc3RzIGluLWZsaWdodCBjaGFuZ2VzLCBub3QKICAgICAgc3BlY3MgLSBpdCBuZXZlciBzaG93cyB3aGF0IHRoZSBwcm9qZWN0IGFscmVhZHkgY292ZXJzLiBSZXVzZSBhbiBleGlzdGluZwogICAgICBjYXBhYmlsaXR5J3MgZXhhY3QgcGF0aCBpbnN0ZWFkIG9mIGludHJvZHVjaW5nIGEgbmVhci1kdXBsaWNhdGUgbmFtZS4KICAgICAgVGhlIGZpbHRlcmVkIHJlYWQgaXMgb25seSBhbiBvdmVydmlldy4gQmVmb3JlIGRlY2lkaW5nIHdoYXQgaXMgYWxyZWFkeQogICAgICBjb3ZlcmVkIG9yIHdoYXQgc2hvdWxkIGNoYW5nZSwgcmVhZCBlYWNoIHJlbGV2YW50IHNwZWMgaW4gZnVsbCwgaW5jbHVkaW5nCiAgICAgIHNjZW5hcmlvcywgd2l0aCBgb3BlbnNwZWMgc2hvdyAiPHNwZWMtaWQ+IiAtLXR5cGUgc3BlY2AgKHNhbWUgYC0tc3RvcmVgIHJ1bGUpLgogICAgICBFYWNoIGNhcGFiaWxpdHkgbGlzdGVkIGhlcmUgd2lsbCBuZWVkIGEgY29ycmVzcG9uZGluZyBzcGVjIGZpbGUuCgogICAgICBFdmVyeSBjaGFuZ2UgbXVzdCBlaXRoZXIgZGVjbGFyZSBhdCBsZWFzdCBvbmUgY2FwYWJpbGl0eSAobmV3IG9yCiAgICAgIG1vZGlmaWVkKSBvciBleHBsaWNpdGx5IG9wdCBvdXQgb2Ygc3BlY3M6IGBvcGVuc3BlYyB2YWxpZGF0ZWAgcmVqZWN0cyBhCiAgICAgIGNoYW5nZSB3aXRoIHplcm8gZGVsdGFzIHVubGVzcyB0aGUgY2hhbmdlJ3MgYC5vcGVuc3BlYy55YW1sYCBzZXRzCiAgICAgIGBza2lwX3NwZWNzOiB0cnVlYC4gVXNlIGBza2lwX3NwZWNzOiB0cnVlYCBvbmx5IHdoZW4gbm8gc3BlYy1sZXZlbAogICAgICBiZWhhdmlvciBjaGFuZ2VzIChwdXJlIHJlZmFjdG9yLCB0b29saW5nLCBkb2NzKSAtIHNwZWNzIGRlc2NyaWJlCiAgICAgIGJlaGF2aW9yLCBzbyBpZiBiZWhhdmlvciBkb2VzIG5vdCBjaGFuZ2UsIG5vIHNwZWMgc2hvdWxkIGNoYW5nZSBlaXRoZXIuCiAgICAgIERvIG5vdCBpbnZlbnQgYSByZXF1aXJlbWVudCBqdXN0IHRvIHNhdGlzZnkgdmFsaWRhdGlvbi4KCiAgICAgIEtlZXAgaXQgY29uY2lzZSAoMS0yIHBhZ2VzKS4gRm9jdXMgb24gdGhlICJ3aHkiIG5vdCB0aGUgImhvdyIgLQogICAgICBpbXBsZW1lbnRhdGlvbiBkZXRhaWxzIGJlbG9uZyBpbiBkZXNpZ24ubWQuCgogICAgICBUaGlzIGlzIHRoZSBmb3VuZGF0aW9uIC0gc3BlY3MsIGRlc2lnbiwgYW5kIHRhc2tzIGFsbCBidWlsZCBvbiB0aGlzLgogICAgcmVxdWlyZXM6IFtdCgogIC0gaWQ6IHNwZWNzCiAgICBnZW5lcmF0ZXM6ICJzcGVjcy8qKi8qLm1kIgogICAgZGVzY3JpcHRpb246IERldGFpbGVkIHNwZWNpZmljYXRpb25zIGZvciB0aGUgY2hhbmdlCiAgICB0ZW1wbGF0ZTogc3BlYy5tZAogICAgaW5zdHJ1Y3Rpb246IHwKICAgICAgQ3JlYXRlIHNwZWNpZmljYXRpb24gZmlsZXMgdGhhdCBkZWZpbmUgV0hBVCB0aGUgc3lzdGVtIHNob3VsZCBkby4KCiAgICAgIEEgc3BlYyBpcyBhIGJlaGF2aW9yIGNvbnRyYWN0LCBub3QgYW4gaW1wbGVtZW50YXRpb24gcGxhbi4KCiAgICAgIEdvb2Qgc3BlYyBjb250ZW50OgogICAgICAtIE9ic2VydmFibGUgYmVoYXZpb3IgdXNlcnMgb3IgZG93bnN0cmVhbSBzeXN0ZW1zIHJlbHkgb24KICAgICAgLSBJbnB1dHMsIG91dHB1dHMsIGFuZCBlcnJvciBjb25kaXRpb25zCiAgICAgIC0gRXh0ZXJuYWwgY29uc3RyYWludHMgKHNlY3VyaXR5LCBwcml2YWN5LCByZWxpYWJpbGl0eSwgY29tcGF0aWJpbGl0eSkKICAgICAgLSBTY2VuYXJpb3MgdGhhdCBjYW4gYmUgdGVzdGVkIG9yIGV4cGxpY2l0bHkgdmFsaWRhdGVkCgogICAgICBBdm9pZCBpbiBzcGVjczoKICAgICAgLSBJbnRlcm5hbCBjbGFzcy9mdW5jdGlvbiBuYW1lcwogICAgICAtIExpYnJhcnkgb3IgZnJhbWV3b3JrIGNob2ljZXMKICAgICAgLSBTdGVwLWJ5LXN0ZXAgaW1wbGVtZW50YXRpb24gZGV0YWlscwogICAgICAtIERldGFpbGVkIGV4ZWN1dGlvbiBwbGFucyAodGhvc2UgYmVsb25nIGluIGRlc2lnbi5tZCBvciB0YXNrcy5tZCkKCiAgICAgIFF1aWNrIHRlc3Q6IGlmIHRoZSBpbXBsZW1lbnRhdGlvbiBjYW4gY2hhbmdlIHdpdGhvdXQgY2hhbmdpbmcgZXh0ZXJuYWxseQogICAgICB2aXNpYmxlIGJlaGF2aW9yLCBpdCBsaWtlbHkgZG9lcyBub3QgYmVsb25nIGluIHRoZSBzcGVjLgoKICAgICAgQ3JlYXRlIG9uZSBzcGVjIGZpbGUgcGVyIGNhcGFiaWxpdHkgbGlzdGVkIGluIHRoZSBwcm9wb3NhbCdzIENhcGFiaWxpdGllcyBzZWN0aW9uLgogICAgICBgPGNhcGFiaWxpdHktcGF0aD5gIGlzIHRoZSBzcGVjIGRpcmVjdG9yeSByZWxhdGl2ZSB0byBgc3BlY3MvYCAoZm9yIGV4YW1wbGUsCiAgICAgIGB1c2VyLWF1dGhgIG9yIGBpZGVudGl0eS91c2VyLWF1dGhgKS4gUHJlc2VydmUgdGhlIGZ1bGwgcGF0aDoKICAgICAgLSBOZXcgY2FwYWJpbGl0aWVzOiB1c2UgdGhlIGV4YWN0IHBhdGggZnJvbSB0aGUgcHJvcG9zYWwgYXQgYHNwZWNzLzxjYXBhYmlsaXR5LXBhdGg+L3NwZWMubWRgLiBBbnkgcGF0aCBzZWdtZW50IG5ld2x5IGludHJvZHVjZWQgaW4gdGhlIHByb3Bvc2FsIG11c3QgYmUga2ViYWItY2FzZS4gRm9sbG93IHRoZSBwcm9qZWN0J3MgZXhpc3Rpbmcgb3JnYW5pemF0aW9uOyBkbyBub3QgYWRkIGEgbmV3IGRvbWFpbiBsZXZlbCB3aGVuIHRoZSBwcm9qZWN0IHVzZXMgYSBmbGF0IGxheW91dC4KICAgICAgLSBNb2RpZmllZCBjYXBhYmlsaXRpZXM6IHVzZSB0aGUgZXhhY3QgZXhpc3RpbmcgcGF0aCBmcm9tIGBvcGVuc3BlYy9zcGVjcy88Y2FwYWJpbGl0eS1wYXRoPi9gIHdoZW4gY3JlYXRpbmcgdGhlIGRlbHRhIGF0IGBzcGVjcy88Y2FwYWJpbGl0eS1wYXRoPi9zcGVjLm1kYC4gUnVuIGBvcGVuc3BlYyBsaXN0IC0tc3BlY3NgIHRvIGNvbmZpcm0gdGhhdCBwYXRoIGJlZm9yZSB3cml0aW5nIHRoZSBkZWx0YSwgYXBwZW5kaW5nIGAtLXN0b3JlICI8aWQ+ImAgb25seSBmb3IgYSByZWdpc3RlcmVkIHN0YW5kYWxvbmUgc3RvcmUgLSBhIG1pc3R5cGVkIG9yIGludmVudGVkIHBhdGggdGFyZ2V0cyBhIGNhcGFiaWxpdHkgdGhhdCBkb2VzIG5vdCBleGlzdCByYXRoZXIgdGhhbiB0aGUgb25lIHlvdSBtZWFudC4gRG8gbm90IG1vdmUgb3IgcmVuYW1lIHRoZSBjYXBhYmlsaXR5LgoKICAgICAgV3JpdGUgdGhlIHNwZWMgZmlsZXMgb25lIGF0IGEgdGltZSwgc28gdGhlIHVzZXIgc2VlcyBwcm9ncmVzcyBhbmQgYW4KICAgICAgaW50ZXJydXB0ZWQgcnVuIGtlZXBzIHRoZSBmaWxlcyBpdCBhbHJlYWR5IHdyb3RlOgogICAgICAtIEJlZm9yZSB3cml0aW5nIGFueXRoaW5nLCBjb21wYXJlIHRoZSBjYXBhYmlsaXRpZXMgdGhlIHByb3Bvc2FsIGxpc3RzCiAgICAgICAgd2l0aCB0aGUgYHNwZWNzLzxjYXBhYmlsaXR5LXBhdGg+L3NwZWMubWRgIGZpbGVzIHRoYXQgYWxyZWFkeSBleGlzdCwKICAgICAgICBhbmQgc3RhcnQgd2l0aCB0aGUgZmlyc3QgY2FwYWJpbGl0eSB0aGF0IGhhcyBubyBmaWxlLiBEbyBub3QgcmV3cml0ZQogICAgICAgIGFuIGV4aXN0aW5nIGZpbGUgdW5sZXNzIHRoZSB1c2VyIGFza3MgeW91IHRvLgogICAgICAtIEJlZm9yZSBlYWNoIGZpbGUsIHNheSBpbiBvbmUgbGluZSB3aGljaCBjYXBhYmlsaXR5IHlvdSBhcmUgd3JpdGluZy4KICAgICAgLSBXcml0ZSBlYWNoIGZpbGUgYXMgc29vbiBhcyB5b3UgaGF2ZSBkcmFmdGVkIGl0LiBEbyBub3QgcGxhbiBldmVyeSBmaWxlCiAgICAgICAgYmVmb3JlIHdyaXRpbmcgdGhlIGZpcnN0LgogICAgICAtIFN0b3Agd2hlbiBldmVyeSBjYXBhYmlsaXR5IHRoZSBwcm9wb3NhbCBsaXN0cyBoYXMgaXRzIGZpbGUuCgogICAgICBUaGVyZSBtdXN0IGJlIGF0IGxlYXN0IG9uZSBzcGVjIGZpbGUgdW5sZXNzIHRoZSBjaGFuZ2UncyBgLm9wZW5zcGVjLnlhbWxgCiAgICAgIHNldHMgYHNraXBfc3BlY3M6IHRydWVgIChubyBzcGVjLWxldmVsIGJlaGF2aW9yIGNoYW5nZSkgLSBgb3BlbnNwZWMgdmFsaWRhdGVgCiAgICAgIHJlamVjdHMgYSB6ZXJvLWRlbHRhIGNoYW5nZSB3aXRob3V0IHRoYXQgbWFya2VyLiBJZiB0aGUgcHJvcG9zYWwgbGlzdHMgbm8KICAgICAgY2FwYWJpbGl0aWVzIGFuZCBgc2tpcF9zcGVjc2AgaXMgbm90IHNldCwgcmV2aXNpdCB0aGUgcHJvcG9zYWwgZmlyc3QuCgogICAgICBEZWx0YSBvcGVyYXRpb25zICh1c2UgIyMgaGVhZGVycyk6CiAgICAgIC0gKipBRERFRCBSZXF1aXJlbWVudHMqKjogTmV3IGNhcGFiaWxpdGllcwogICAgICAtICoqTU9ESUZJRUQgUmVxdWlyZW1lbnRzKio6IENoYW5nZWQgYmVoYXZpb3IgLSBNVVNUIGluY2x1ZGUgZnVsbCB1cGRhdGVkIGNvbnRlbnQKICAgICAgLSAqKlJFTU9WRUQgUmVxdWlyZW1lbnRzKio6IERlcHJlY2F0ZWQgZmVhdHVyZXMgLSBNVVNUIGluY2x1ZGUgKipSZWFzb24qKiBhbmQgKipNaWdyYXRpb24qKgogICAgICAtICoqUkVOQU1FRCBSZXF1aXJlbWVudHMqKjogTmFtZSBjaGFuZ2VzIG9ubHkgLSB1c2UgRlJPTTovVE86IGZvcm1hdAoKICAgICAgRm9ybWF0IHJlcXVpcmVtZW50czoKICAgICAgLSBFYWNoIHJlcXVpcmVtZW50OiBgIyMjIFJlcXVpcmVtZW50OiA8bmFtZT5gIGZvbGxvd2VkIGJ5IGRlc2NyaXB0aW9uCiAgICAgIC0gVXNlIFNIQUxML01VU1QgZm9yIG5vcm1hdGl2ZSByZXF1aXJlbWVudHMgKGF2b2lkIHNob3VsZC9tYXkpCiAgICAgIC0gRWFjaCBzY2VuYXJpbzogYCMjIyMgU2NlbmFyaW86IDxuYW1lPmAgd2l0aCBXSEVOL1RIRU4gZm9ybWF0CiAgICAgIC0gKipDUklUSUNBTCoqOiBTY2VuYXJpb3MgTVVTVCB1c2UgZXhhY3RseSA0IGhhc2h0YWdzIChgIyMjI2ApLiBVc2luZyAzIGhhc2h0YWdzIG9yIGJ1bGxldHMgd2lsbCBmYWlsIHNpbGVudGx5LgogICAgICAtIEV2ZXJ5IHJlcXVpcmVtZW50IE1VU1QgaGF2ZSBhdCBsZWFzdCBvbmUgc2NlbmFyaW8uCiAgICAgIC0gS2VlcCBlYWNoIHJlcXVpcmVtZW50J3MgZGVzY3JpcHRpb24gKHRoZSB0ZXh0IGJldHdlZW4gYCMjIyBSZXF1aXJlbWVudDpgIGFuZCBpdHMgZmlyc3Qgc2NlbmFyaW8pIHRvIDUwMCBjaGFyYWN0ZXJzIG9yIGZld2VyLiBgb3BlbnNwZWMgdmFsaWRhdGVgIGZsYWdzIGxvbmdlciBkZXNjcmlwdGlvbnMgaW4gQURERUQgcmVxdWlyZW1lbnRzIGFuZCBpbiB0aGUgbWFpbiBzcGVjLiBUaGlzIGlzIGEgd2FybmluZzogbm9ybWFsIHZhbGlkYXRpb24gc3RpbGwgcGFzc2VzLCBidXQgYG9wZW5zcGVjIHZhbGlkYXRlIC0tc3RyaWN0YCBmYWlscyBvbiBpdC4gV2hlbiB3cml0aW5nIGEgbmV3IHJlcXVpcmVtZW50LCBzdGF0ZSBvbmUgYmVoYXZpb3IgcGVyIHJlcXVpcmVtZW50OiBtb3ZlIGV4YW1wbGVzIGFuZCBlZGdlIGNhc2VzIGludG8gc2NlbmFyaW9zLCBhbmQgc3BsaXQgYSByZXF1aXJlbWVudCB0aGF0IGNvdmVycyBzZXZlcmFsIGJlaGF2aW9ycyBpbnRvIHNlcGFyYXRlIGAjIyMgUmVxdWlyZW1lbnQ6YCBibG9ja3MsIGVhY2ggd2l0aCBpdHMgb3duIHNjZW5hcmlvcy4gVW5kZXIgTU9ESUZJRUQsIGtlZXAgdGhlIGV4aXN0aW5nIHJlcXVpcmVtZW50IGJsb2NrIHdob2xlOyBuZXZlciBzcGxpdCwgdHJpbSBvciByZXdyaXRlIGV4aXN0aW5nIHRleHQganVzdCB0byBtZWV0IHRoZSBsZW5ndGguIFNwbGl0IGFuIGV4aXN0aW5nIGxvbmcgcmVxdWlyZW1lbnQgb25seSB3aGVuIHRoZSB1c2VyIGFza3MgZm9yIGl0LCBpbiBhIGNoYW5nZSBtYWRlIGZvciB0aGF0IHB1cnBvc2U6IHVuZGVyIE1PRElGSUVELCBrZWVwIGl0cyBoZWFkZXIgYW5kIGV2ZXJ5IHNjZW5hcmlvIGFuZCBjdXQgaXRzIGRlc2NyaXB0aW9uIGRvd24gdG8gb25lIGJlaGF2aW9yIHdpdGhvdXQgY2hhbmdpbmcgaXRzIG1lYW5pbmcsIHRoZW4gYWRkIGVhY2ggYmVoYXZpb3IgeW91IHJlbW92ZWQgYXMgaXRzIG93biBBRERFRCByZXF1aXJlbWVudCB3aXRoIGl0cyBvd24gc2NlbmFyaW9zLgoKICAgICAgTmV3IGNhcGFiaWxpdGllcyBvbmx5OiB0aGUgZGVsdGEgc3BlYydzIGZpcnN0IHNlY3Rpb24gaXMgYCMjIFB1cnBvc2VgIC0KICAgICAgb25lIG9yIHR3byBzZW50ZW5jZXMgKDUwKyBjaGFyYWN0ZXJzLCBvciBgb3BlbnNwZWMgdmFsaWRhdGUgLS1zdHJpY3RgCiAgICAgIHJlcG9ydHMgaXQgYXMgdG9vIGJyaWVmKSBkZXNjcmliaW5nIHdoYXQgdGhlIGNhcGFiaWxpdHkgaXMgZm9yLiBBcmNoaXZlCiAgICAgIGNvcGllcyBpdCBpbnRvIHRoZSBtYWluIHNwZWMgaXQgY3JlYXRlczsgd2l0aG91dCBpdCB0aGUgbmV3IG1haW4gc3BlYyBpcwogICAgICBsZWZ0IHdpdGggYSBgVEJEIC4uLiBVcGRhdGUgUHVycG9zZSBhZnRlciBhcmNoaXZlYCBwbGFjZWhvbGRlciB0byBmaWxsIGluCiAgICAgIGJ5IGhhbmQuIERvIE5PVCBhZGQgYCMjIFB1cnBvc2VgIHRvIGEgZGVsdGEgZm9yIGFuIGV4aXN0aW5nIGNhcGFiaWxpdHkgLQogICAgICB0aGF0IHNwZWMgYWxyZWFkeSBoYXMgb25lIGFuZCB0aGUgZGVsdGEncyBpcyBpZ25vcmVkLiBUbyBjaGFuZ2UgYW4KICAgICAgZXhpc3RpbmcgY2FwYWJpbGl0eSdzIFB1cnBvc2UgLSBpbmNsdWRpbmcgYSBsZWZ0b3ZlciBgVEJEYCBwbGFjZWhvbGRlciAtCiAgICAgIGVkaXQgYDxwbGFubmluZ0hvbWUucm9vdD4vb3BlbnNwZWMvc3BlY3MvPGNhcGFiaWxpdHktcGF0aD4vc3BlYy5tZGAKICAgICAgZGlyZWN0bHkuIGBwbGFubmluZ0hvbWUucm9vdGAgY29tZXMgZnJvbSB0aGUgYG9wZW5zcGVjIGluc3RydWN0aW9ucyAuLi4KICAgICAgLS1qc29uYCByZXNwb25zZS4gQWx3YXlzIHVzZSBpdCByYXRoZXIgdGhhbiBhIHJlcG8tcmVsYXRpdmUgcGF0aDogaXQKICAgICAgcmVzb2x2ZXMgdG8gdGhlIHN0b3JlIHdoZW5ldmVyIHRoZSBjaGFuZ2UgbGl2ZXMgaW4gb25lIC0gd2hldGhlciB0aGF0CiAgICAgIGNhbWUgZnJvbSBgLS1zdG9yZWAsIGEgcHJvamVjdCBgc3RvcmU6YCBwb2ludGVyLCBvciBhIGdsb2JhbCBkZWZhdWx0CiAgICAgIHN0b3JlIC0gYW5kIHRvIHRoZSBjdXJyZW50IHJlcG9zaXRvcnkgb3RoZXJ3aXNlLiBEbyBub3QgdHJ5IHRvIHdvcmsgb3V0CiAgICAgIHdoaWNoIGNhc2UgYXBwbGllczsgdGhlIGZpZWxkIGFscmVhZHkgaGFzLgoKICAgICAgTU9ESUZJRUQgcmVxdWlyZW1lbnRzIHdvcmtmbG93OgogICAgICAxLiBMb2NhdGUgdGhlIGV4aXN0aW5nIHJlcXVpcmVtZW50IGluIGA8cGxhbm5pbmdIb21lLnJvb3Q+L29wZW5zcGVjL3NwZWNzLzxjYXBhYmlsaXR5LXBhdGg+L3NwZWMubWRgICh0aGUgc2FtZSBzdG9yZS1hd2FyZSByb290IGFzIGFib3ZlKQogICAgICAyLiBDb3B5IHRoZSBFTlRJUkUgcmVxdWlyZW1lbnQgYmxvY2sgKGZyb20gYCMjIyBSZXF1aXJlbWVudDpgIHRocm91Z2ggYWxsIHNjZW5hcmlvcykKICAgICAgMy4gUGFzdGUgdW5kZXIgYCMjIE1PRElGSUVEIFJlcXVpcmVtZW50c2AgYW5kIGVkaXQgdG8gcmVmbGVjdCBuZXcgYmVoYXZpb3IKICAgICAgNC4gRW5zdXJlIGhlYWRlciB0ZXh0IG1hdGNoZXMgZXhhY3RseSAod2hpdGVzcGFjZS1pbnNlbnNpdGl2ZSkKCiAgICAgIENvbW1vbiBwaXRmYWxsOiBVc2luZyBNT0RJRklFRCB3aXRoIHBhcnRpYWwgY29udGVudCBsb3NlcyBkZXRhaWwgYXQgYXJjaGl2ZSB0aW1lLgogICAgICBJZiBhZGRpbmcgbmV3IGNvbmNlcm5zIHdpdGhvdXQgY2hhbmdpbmcgZXhpc3RpbmcgYmVoYXZpb3IsIHVzZSBBRERFRCBpbnN0ZWFkLgoKICAgICAgRXhhbXBsZSAoYSBuZXcgY2FwYWJpbGl0eSwgc28gaXRzIGZpcnN0IHNlY3Rpb24gaXMgYCMjIFB1cnBvc2VgKToKICAgICAgYGBgCiAgICAgICMgU3BlYyBEZWx0YQoKICAgICAgIyMgUHVycG9zZQoKICAgICAgTGV0cyB1c2VycyB0YWtlIHRoZWlyIGRhdGEgb3V0IG9mIHRoZSBwcm9kdWN0IGluIGEgcG9ydGFibGUgZm9ybWF0LgoKICAgICAgIyMgQURERUQgUmVxdWlyZW1lbnRzCgogICAgICAjIyMgUmVxdWlyZW1lbnQ6IFVzZXIgY2FuIGV4cG9ydCBkYXRhCiAgICAgIFRoZSBzeXN0ZW0gU0hBTEwgYWxsb3cgdXNlcnMgdG8gZXhwb3J0IHRoZWlyIGRhdGEgaW4gQ1NWIGZvcm1hdC4KCiAgICAgICMjIyMgU2NlbmFyaW86IFN1Y2Nlc3NmdWwgZXhwb3J0CiAgICAgIC0gKipXSEVOKiogdXNlciBjbGlja3MgIkV4cG9ydCIgYnV0dG9uCiAgICAgIC0gKipUSEVOKiogc3lzdGVtIGRvd25sb2FkcyBhIENTViBmaWxlIHdpdGggYWxsIHVzZXIgZGF0YQoKICAgICAgIyMgUkVNT1ZFRCBSZXF1aXJlbWVudHMKCiAgICAgICMjIyBSZXF1aXJlbWVudDogTGVnYWN5IGV4cG9ydAogICAgICAqKlJlYXNvbioqOiBSZXBsYWNlZCBieSBuZXcgZXhwb3J0IHN5c3RlbQogICAgICAqKk1pZ3JhdGlvbioqOiBVc2UgbmV3IGV4cG9ydCBlbmRwb2ludCBhdCAvYXBpL3YyL2V4cG9ydAogICAgICBgYGAKCgogICAgICBTdGFibGUgSURzIChzcGVjLWN0eCk6CiAgICAgIC0gRXZlcnkgcmVxdWlyZW1lbnQgaGVhZGVyIHN0YXJ0cyB3aXRoIGFuIElEOiBgIyMjIFJlcXVpcmVtZW50OiBSRVEtPENBUD4tTk5OIDxuYW1lPmAuCiAgICAgICAgR2V0IGEgbmV3IElEIGZyb20gYHNwZWNjdHggaWQgbmV3IFJFUSA8Q0FQPmAuIE5ldmVyIGNvdW50IG51bWJlcnMgYnkgaGFuZC4KICAgICAgICBgPENBUD5gIGlzIGEgY2FwYWJpbGl0eSBjb2RlIGZyb20gYFtpZHNdIGNhcHNgIGluIGBzcGVjLWN0eC50b21sYC4KICAgICAgLSBFdmVyeSBzY2VuYXJpbyBoZWFkZXIgc3RhcnRzIHdpdGggYW4gSUQ6IGAjIyMjIFNjZW5hcmlvOiBTQ04tPENBUD4tTk5OLTxuPiA8bmFtZT5gLgogICAgICAgIEdldCBpdCBmcm9tIGBzcGVjY3R4IGlkIG5ldyBTQ04gPENBUD4gLS1mb3IgUkVRLTxDQVA+LU5OTmAuCiAgICAgIC0gVW5kZXIgTU9ESUZJRUQsIFJFTU9WRUQsIG9yIFJFTkFNRUQsIGtlZXAgdGhlIGV4aXN0aW5nIElELiBBbiBJRCBuZXZlciBjaGFuZ2VzLgogICAgICAtIExpbmsgcmVsYXRlZCBJRHMgd2l0aCBvbmUgbGluZSBgUmVmczogSUQsIElEYCBhZnRlciB0aGUgcmVxdWlyZW1lbnQgdGV4dC4KICAgICAgLSBSZXBsYWNlIGV2ZXJ5IHBsYWNlaG9sZGVyIG9mIHRoZSB0ZW1wbGF0ZS4gYHNwZWNjdHggY2hlY2tgIHJlcG9ydHMgYSBoZWFkZXIgd2l0aAogICAgICAgIG5vIElEIGFzIGBtaXNzaW5nLWlkYC4KCiAgICAgIFNwZWNzIHNob3VsZCBiZSB0ZXN0YWJsZSAtIGVhY2ggc2NlbmFyaW8gaXMgYSBwb3RlbnRpYWwgdGVzdCBjYXNlLgogICAgcmVxdWlyZXM6CiAgICAgIC0gcHJvcG9zYWwKCiAgLSBpZDogZGVzaWduCiAgICBnZW5lcmF0ZXM6IGRlc2lnbi5tZAogICAgZGVzY3JpcHRpb246IFRlY2huaWNhbCBkZXNpZ24gZG9jdW1lbnQgd2l0aCBpbXBsZW1lbnRhdGlvbiBkZXRhaWxzCiAgICB0ZW1wbGF0ZTogZGVzaWduLm1kCiAgICBpbnN0cnVjdGlvbjogfAogICAgICBDcmVhdGUgdGhlIGRlc2lnbiBkb2N1bWVudCB0aGF0IGV4cGxhaW5zIEhPVyB0byBpbXBsZW1lbnQgdGhlIGNoYW5nZS4KCiAgICAgIFdoZW4gdG8gaW5jbHVkZSBkZXNpZ24ubWQgKGNyZWF0ZSBvbmx5IGlmIGFueSBhcHBseSk6CiAgICAgIC0gQ3Jvc3MtY3V0dGluZyBjaGFuZ2UgKG11bHRpcGxlIHNlcnZpY2VzL21vZHVsZXMpIG9yIG5ldyBhcmNoaXRlY3R1cmFsIHBhdHRlcm4KICAgICAgLSBOZXcgZXh0ZXJuYWwgZGVwZW5kZW5jeSBvciBzaWduaWZpY2FudCBkYXRhIG1vZGVsIGNoYW5nZXMKICAgICAgLSBTZWN1cml0eSwgcGVyZm9ybWFuY2UsIG9yIG1pZ3JhdGlvbiBjb21wbGV4aXR5CiAgICAgIC0gQW1iaWd1aXR5IHRoYXQgYmVuZWZpdHMgZnJvbSB0ZWNobmljYWwgZGVjaXNpb25zIGJlZm9yZSBjb2RpbmcKCiAgICAgIFNlY3Rpb25zOgogICAgICAtICoqQ29udGV4dCoqOiBPbmx5IHRoZSBjdXJyZW50IHN0YXRlIGFuZCBjb25zdHJhaW50cyBuZWVkZWQgdG8gZXhwbGFpbiB0aGUgYXBwcm9hY2guIFJlZmVyZW5jZSB0aGUgcHJvcG9zYWwgZm9yIG1vdGl2YXRpb24gaW5zdGVhZCBvZiByZXN0YXRpbmcgaXQgKGUuZy4sICJTZWUgcHJvcG9zYWwubWQgLSBXaHkiKS4KICAgICAgLSAqKkdvYWxzIC8gTm9uLUdvYWxzKio6IFdoYXQgdGhpcyBkZXNpZ24gYWNoaWV2ZXMgYW5kIGV4cGxpY2l0bHkgZXhjbHVkZXMuIERvbid0IHJlc3RhdGUgdGhlIHByb3Bvc2FsJ3Mgc2NvcGUgLSBhZGQgb25seSBkZXNpZ24tbGV2ZWwgYm91bmRhcmllcy4KICAgICAgLSAqKkRlY2lzaW9ucyoqOiBLZXkgdGVjaG5pY2FsIGNob2ljZXMgd2l0aCByYXRpb25hbGUgKHdoeSBYIG92ZXIgWT8pLiBJbmNsdWRlIGFsdGVybmF0aXZlcyBjb25zaWRlcmVkIGZvciBlYWNoIGRlY2lzaW9uLgogICAgICAtICoqUmlza3MgLyBUcmFkZS1vZmZzKio6IEtub3duIGxpbWl0YXRpb25zLCB0aGluZ3MgdGhhdCBjb3VsZCBnbyB3cm9uZy4gRm9ybWF0OiBbUmlza10g4oaSIE1pdGlnYXRpb24KICAgICAgLSAqKk1pZ3JhdGlvbiBQbGFuKio6IFN0ZXBzIHRvIGRlcGxveSwgcm9sbGJhY2sgc3RyYXRlZ3kgKGlmIGFwcGxpY2FibGUpCiAgICAgIC0gKipPcGVuIFF1ZXN0aW9ucyoqOiBVbmtub3ducyB0aGF0IGNhbiBzYWZlbHkgYmUgYW5zd2VyZWQgbGF0ZXIgd2l0aG91dAogICAgICAgIGNoYW5naW5nIHRoZSBzcGVjcywgdGhlIGFwcHJvYWNoLCBvciB0aGUgdGFzayBicmVha2Rvd24uIE9taXQgaWYgbm9uZS4KCiAgICAgIE9wZW4gcXVlc3Rpb25zIGFyZSBmb3IgZ2VudWluZWx5IGRlZmVycmFibGUgdW5rbm93bnMsIG5vdCBkZWNpc2lvbnMgeW91CiAgICAgIHNraXBwZWQuIElmIGEgcXVlc3Rpb24gd291bGQgY2hhbmdlIHRoZSBzcGVjcywgdGhlIGNob3NlbiBhcHByb2FjaCwgb3IKICAgICAgdGhlIHRhc2sgYnJlYWtkb3duLCByZXNvbHZlIGl0IG5vdyAtIGFzayB0aGUgdXNlciBpbnN0ZWFkIG9mIGd1ZXNzaW5nLgoKICAgICAgRm9jdXMgb24gYXJjaGl0ZWN0dXJlIGFuZCBhcHByb2FjaCwgbm90IGxpbmUtYnktbGluZSBpbXBsZW1lbnRhdGlvbi4KICAgICAgVGhlIHByb3Bvc2FsIGNvdmVycyB3aHkgYW5kIHdoYXQ7IGRlc2lnbiBjb3ZlcnMgaG93LiBSZWZlcmVuY2UgdGhlCiAgICAgIHByb3Bvc2FsIGZvciBtb3RpdmF0aW9uIGFuZCwgb25jZSB3cml0dGVuLCB0aGUgc3BlY3MgZm9yIHJlcXVpcmVtZW50cyAtCiAgICAgIGlmIGEgc2VjdGlvbiB3b3VsZCBvbmx5IHJlc3RhdGUgdGhlbSwgcG9pbnQgdG8gdGhlbSBpbnN0ZWFkLgoKCiAgICAgIFN0YWJsZSBJRHMgKHNwZWMtY3R4KToKICAgICAgLSBFYWNoIGJ1bGxldCB1bmRlciBgIyMgRGVjaXNpb25zYCBpcyBvbmUgZGVjaXNpb24uIEl0cyBJRCBpcyBgPGNoYW5nZSBudW1iZXI+L0Q8bj5gCiAgICAgICAgYnkgcG9zaXRpb24sIHNvIGFkZCBuZXcgZGVjaXNpb25zIGF0IHRoZSBlbmQgYW5kIG5ldmVyIHJlb3JkZXIgdGhlbS4KICAgICAgLSBFbmQgYSBkZWNpc2lvbiB3aXRoIGBSZWZzOiBgIGFuZCB0aGUgUkVRIG9yIEFEUiBJRHMgaXQgc2VydmVzLiBUaGVuIGBzcGVjY3R4IGJ1bmRsZWAKICAgICAgICBwcmludHMgdGhlIGRlY2lzaW9uIHdpdGggdGhvc2UgcmVxdWlyZW1lbnRzLgoKICAgICAgR29vZCBkZXNpZ24gZG9jcyBleHBsYWluIHRoZSAid2h5IiBiZWhpbmQgdGVjaG5pY2FsIGRlY2lzaW9ucy4KICAgIHJlcXVpcmVzOgogICAgICAtIHByb3Bvc2FsCgogIC0gaWQ6IHRhc2tzCiAgICBnZW5lcmF0ZXM6IHRhc2tzLm1kCiAgICBkZXNjcmlwdGlvbjogSW1wbGVtZW50YXRpb24gY2hlY2tsaXN0IHdpdGggdHJhY2thYmxlIHRhc2tzCiAgICB0ZW1wbGF0ZTogdGFza3MubWQKICAgIGluc3RydWN0aW9uOiB8CiAgICAgIENyZWF0ZSB0aGUgdGFzayBsaXN0IHRoYXQgYnJlYWtzIGRvd24gdGhlIGltcGxlbWVudGF0aW9uIHdvcmsuCgogICAgICBCZWZvcmUgd3JpdGluZyB0YXNrcywgY2hlY2sgZGVzaWduLm1kIGZvciBPcGVuIFF1ZXN0aW9ucy4gSWYgYW55IG9mIHRoZW0KICAgICAgd291bGQgY2hhbmdlIHdoYXQgZ2V0cyBidWlsdCwgcmVzb2x2ZSB0aGVtIHdpdGggdGhlIHVzZXIgZmlyc3QgLSBkbyBub3QKICAgICAgYmFrZSBhbiB1bnN0YXRlZCBhc3N1bXB0aW9uIGludG8gdGhlIHRhc2sgbGlzdC4KCiAgICAgICoqSU1QT1JUQU5UOiBGb2xsb3cgdGhlIHRlbXBsYXRlIGJlbG93IGZvciB0cmFja2VkIHRhc2tzLioqIFRoZSBhcHBseSBwaGFzZSBwYXJzZXMKICAgICAgY2hlY2tib3ggZm9ybWF0IHRvIHRyYWNrIHByb2dyZXNzLiBBIGJveCBob2xkaW5nIG9ubHkgYHhgIGNvdW50cyBhcyBkb25lLAogICAgICB1cHBlciBvciBsb3dlciBjYXNlIGFuZCB3aXRoIGFueSBzcGFjaW5nLCBzbyBgLSBbIHhdYCBpcyBkb25lIHRvby4gRXZlcnkKICAgICAgb3RoZXIgbWFya2VyLCBpbmNsdWRpbmcgYC0gW35dYCwgYC0gWy1dYCBhbmQgYW4gZW1wdHkgYC0gW11gLCByZWFkcyBhcwogICAgICB1bmZpbmlzaGVkLiBBIGxpbmUgd2l0aCBubyBjaGVja2JveCBpcyBub3QgdHJhY2tlZCBhdCBhbGwuCgogICAgICBHdWlkZWxpbmVzOgogICAgICAtIEdyb3VwIHJlbGF0ZWQgdHJhY2tlZCB0YXNrcyB1bmRlciAjIyBudW1iZXJlZCBoZWFkaW5ncwogICAgICAtIEVhY2ggdHJhY2tlZCB0YXNrIE1VU1QgYmUgYSBjaGVja2JveDogYC0gWyBdIFguWSBUYXNrIGRlc2NyaXB0aW9uYAogICAgICAtIFRhc2tzIHNob3VsZCBiZSBzbWFsbCBlbm91Z2ggdG8gY29tcGxldGUgaW4gb25lIHNlc3Npb24KICAgICAgLSBPcmRlciB0YXNrcyBieSBkZXBlbmRlbmN5ICh3aGF0IG11c3QgYmUgZG9uZSBmaXJzdD8pCiAgICAgIC0gVHJhY2sgaW1wbGVtZW50YXRpb24gYW5kIHZlcmlmaWNhdGlvbiB3b3JrIHRoYXQgY2FuIGJlIGNvbXBsZXRlZCBiZWZvcmUKICAgICAgICBhcmNoaXZlLiBJZiB0aGUgcmVxdWVzdGVkIHdvcmtmbG93IGluY2x1ZGVzIGFyY2hpdmUgb3Igd29yayB0aGF0IHJlcXVpcmVzCiAgICAgICAgdGhpcyBjaGFuZ2UgdG8gYmUgYXJjaGl2ZWQsIHByZXNlcnZlIHRob3NlIHN0ZXBzIGFzIHBsYWluIGJ1bGxldHMgaW4gYW4KICAgICAgICBvcHRpb25hbCBgIyMgV29ya2Zsb3cgZm9sbG93LXVwYCBzZWN0aW9uIGF0IHRoZSBlbmQgb2YgdGFza3MubWQuIFRoZXNlCiAgICAgICAgYnVsbGV0cyBhcmUgcmVmZXJlbmNlIGluZm9ybWF0aW9uIG91dHNpZGUgdHJhY2tlZCB0YXNrIHByb2dyZXNzLgogICAgICAtIEVhY2ggdGFzayBNVVNUIHN0YXRlIGhvdyB0byB2ZXJpZnkgY29tcGxldGlvbiAoYSB0ZXN0LCBjb21tYW5kLAogICAgICAgIG9ic2VydmFibGUgYmVoYXZpb3IsIG9yIGRlbGl2ZXJlZCBhcnRpZmFjdCkuIFB1dCB0aGUgdmVyaWZpY2F0aW9uIGluCiAgICAgICAgdGhhdCB0YXNrJ3MgY2hlY2tib3ggZGVzY3JpcHRpb24uIFVzZSBhIHNlcGFyYXRlIHZlcmlmaWNhdGlvbiB0YXNrIG9ubHkKICAgICAgICB3aGVuIGl0IGNoZWNrcyBicm9hZGVyIGludGVncmF0aW9uIG9yIHN5c3RlbSBiZWhhdmlvciB0aGF0IHNwYW5zCiAgICAgICAgbXVsdGlwbGUgaW1wbGVtZW50YXRpb24gdGFza3MuCiAgICAgIC0gRWFjaCB0YXNrIGdyb3VwIE1VU1QgbGFuZCB0aGUgdGVzdHMgYW5kIGRvY3VtZW50YXRpb24gaXRzIG93biB3b3JrCiAgICAgICAgY2FsbHMgZm9yLiBEbyBOT1QgY29sbGVjdCB0ZXN0aW5nIG9yIGRvY3VtZW50YXRpb24gaW50byBhIGZpbmFsIGdyb3VwIC0KICAgICAgICB3aGVuIGEgbGF0ZSBncm91cCBmaXJzdCBleGVyY2lzZXMgd29yayBmcm9tIGFuIGVhcmx5IG9uZSwgdGhlIGZhaWx1cmVzCiAgICAgICAgY2FzY2FkZSBiYWNrIHRocm91Z2ggZXZlcnkgZ3JvdXAgaW4gYmV0d2VlbiBhbmQgZm9yY2UgcmV3b3JrLiBBIGdyb3VwCiAgICAgICAgd2hvc2Ugd29yayBjYWxscyBmb3IgbmVpdGhlciwgc3VjaCBhcyBzY2FmZm9sZGluZyBvciBkZXBlbmRlbmN5IHNldHVwLAogICAgICAgIGNhcnJpZXMgbmVpdGhlci4gQSBmaW5hbCBncm91cCBpcyBmb3IgaW50ZWdyYXRpb24gY2hlY2tzIG9ubHksIG5vdCBmb3IKICAgICAgICB0aGUgdGVzdHMgYW5kIGRvY3MgYW4gZWFybGllciBncm91cCBvd2VkLgoKICAgICAgRXhhbXBsZToKICAgICAgYGBgCiAgICAgICMgVGFza3MKCiAgICAgICMjIDEuIFNldHVwCgogICAgICAtIFsgXSAxLjEgQ3JlYXRlIG5ldyBtb2R1bGUgc3RydWN0dXJlIGFuZCB2ZXJpZnkgZXhwZWN0ZWQgZmlsZXMgYXJlIHByZXNlbnQKICAgICAgLSBbIF0gMS4yIEFkZCBkZXBlbmRlbmNpZXMgdG8gcGFja2FnZS5qc29uIGFuZCB2ZXJpZnkgcGFja2FnZSBpbnN0YWxsYXRpb24gc3VjY2VlZHMKCiAgICAgICMjIDIuIENvcmUgSW1wbGVtZW50YXRpb24KCiAgICAgIC0gWyBdIDIuMSBJbXBsZW1lbnQgZGF0YSBleHBvcnQgZnVuY3Rpb24gYW5kIHZlcmlmeSB0aGUgZXhwb3J0IHRlc3QgcGFzc2VzCiAgICAgIC0gWyBdIDIuMiBBZGQgQ1NWIGZvcm1hdHRpbmcgdXRpbGl0aWVzIGFuZCB2ZXJpZnkgdW5pdCB0ZXN0cyBjb3ZlciBxdW90aW5nIGFuZCBkZWxpbWl0ZXJzCiAgICAgIC0gWyBdIDIuMyBEb2N1bWVudCB0aGUgZXhwb3J0IEFQSSBpbiBkb2NzL2V4cG9ydC5tZCBhbmQgdmVyaWZ5IHRoZSBkb2N1bWVudGVkIGNvbW1hbmQgcnVucyBhcyB3cml0dGVuCiAgICAgIGBgYAoKICAgICAgV2hlbiBhcHBsaWNhYmxlLCBhcHBlbmQgd29ya2Zsb3cgZm9sbG93LXVwIGFzIHBsYWluIGJ1bGxldHMsIGZvciBleGFtcGxlOgogICAgICBgYGAKICAgICAgIyMgV29ya2Zsb3cgZm9sbG93LXVwCgogICAgICAtIEFyY2hpdmUgdGhlIGNoYW5nZSBhZnRlciB0aGUgcHJvamVjdCdzIHJldmlldyByZXF1aXJlbWVudHMgYXJlIHNhdGlzZmllZC4KICAgICAgLSBWZXJpZnkgdGhlIGFyY2hpdmVkIHJlc3VsdC4KICAgICAgYGBgCgoKICAgICAgU3RhYmxlIElEcyBhbmQgVmVyaWZ5IChzcGVjLWN0eCk6CiAgICAgIC0gRXZlcnkgdGFzayBjaXRlcyBhdCBsZWFzdCBvbmUgcmVxdWlyZW1lbnQgaW4gc3F1YXJlIGJyYWNrZXRzOiBgW1JFUS08Q0FQPi1OTk5dYC4KICAgICAgLSBFdmVyeSB0YXNrIGVuZHMgd2l0aCBgVmVyaWZ5OmAgYW5kIHRoZSBjb21tYW5kIGluIGEgY29kZSBzcGFuLCBmb3IgZXhhbXBsZQogICAgICAgIGBgVmVyaWZ5OiBgbnBtIHRlc3QgLS0gdGVzdHMvZXhwb3J0LnRlc3QudHNgIGBgLiBUd28gc3BhbnMgcnVuIGpvaW5lZCB3aXRoIGAmJmAuCiAgICAgIC0gVXNlIHByb3NlIGFmdGVyIGBWZXJpZnk6YCBvbmx5IHdoZW4gbm8gY29tbWFuZCBjYW4gY2hlY2sgdGhlIHdvcmsuIFRoZW4KICAgICAgICBgc3BlY2N0eCB0YXNrIGRvbmUgPGlkPiAtLW1hbnVhbCAiPG5vdGU+ImAgcmVjb3JkcyBpdC4KICAgICAgLSBDbG9zZSBhIHRhc2sgb25seSB3aXRoIGBzcGVjY3R4IHRhc2sgZG9uZSA8aWQ+YC4gSXQgcnVucyB0aGUgY29tbWFuZCwgd3JpdGVzCiAgICAgICAgZXZpZGVuY2UgdW5kZXIgYC5jdHgvZXZpZGVuY2UvYCwgYW5kIHNldHMgdGhlIGNoZWNrYm94LgoKICAgICAgUmVmZXJlbmNlIHNwZWNzIGZvciB3aGF0IG5lZWRzIHRvIGJlIGJ1aWx0LCBkZXNpZ24gZm9yIGhvdyB0byBidWlsZCBpdC4KICAgIHJlcXVpcmVzOgogICAgICAtIHNwZWNzCiAgICAgIC0gZGVzaWduCgphcHBseToKICByZXF1aXJlczogWyB0YXNrcyBdCiAgdHJhY2tzOiB0YXNrcy5tZAogIGluc3RydWN0aW9uOiB8CiAgICBSZWFkIGNvbnRleHQgZmlsZXMsIHdvcmsgdGhyb3VnaCBwZW5kaW5nIHRhc2tzLCBtYXJrIGNvbXBsZXRlIGFzIHlvdSBnby4KICAgIFBhdXNlIGlmIHlvdSBoaXQgYmxvY2tlcnMgb3IgbmVlZCBjbGFyaWZpY2F0aW9uLgo=",
|
|
4
|
+
"schemas/spec-driven-ids/README.md": "IyBzcGVjLWRyaXZlbi1pZHMKClRlbXBsYXRlcyBhbmQgY29uZmlnIHJ1bGVzIHRoYXQgYWRkIHN0YWJsZSBJRHMgZnJvbSBzcGVjLWN0eCB0byB0aGUgT3BlblNwZWMgYHNwZWMtZHJpdmVuYCB3b3JrZmxvdy4gVGhlIHByb2plY3Qga2VlcHMgYHNjaGVtYTogc3BlYy1kcml2ZW5gIGluIGBvcGVuc3BlYy9jb25maWcueWFtbGAuIFVwc3RyZWFtIE9wZW5TcGVjIGluamVjdHMgdGhlIGBydWxlc2AgYW5kIGBvcGVyYXRpb25zYCBiZWxvdyBpbnRvIGV2ZXJ5IGFydGlmYWN0IGluc3RydWN0aW9uLCBzbyB0aGUgYWdlbnQgbGVhcm5zIHRoZSBJRCBjb21tYW5kcyBmcm9tIHRoZXJlLgoKLSBgdGVtcGxhdGVzL3NwZWMubWRgIHN0YXJ0cyBlYWNoIHJlcXVpcmVtZW50IHdpdGggYFJFUS08Q0FQPi1OTk5gIGFuZCBlYWNoIHNjZW5hcmlvIHdpdGggYFNDTi08Q0FQPi1OTk4tPG4+YC4KLSBgdGVtcGxhdGVzL3Rhc2tzLm1kYCBjaXRlcyBSRVEgSURzIGFuZCBwdXRzIHRoZSBgVmVyaWZ5OmAgY29tbWFuZCBpbiBhIGNvZGUgc3Bhbiwgc28gYHNwZWNjdHggdGFzayBkb25lYCBjYW4gcnVuIGl0LgotIGB0ZW1wbGF0ZXMvZGVzaWduLm1kYCBlbmRzIGVhY2ggZGVjaXNpb24gd2l0aCBhIGBSZWZzOmAgbGluZS4KLSBgY29uZmlnLWZyYWdtZW50LnlhbWxgIGhvbGRzIHRoZSBgcnVsZXNgIGFuZCBgb3BlcmF0aW9uc2AgZm9yIGBvcGVuc3BlYy9jb25maWcueWFtbGAuIFRoZSBydWxlcyB0ZWxsIHRoZSBhZ2VudCB0byBnZXQgSURzIGZyb20gYHNwZWNjdHggaWQgbmV3YCBhbmQgbmFtZSB0aGUgdGVtcGxhdGVzIHBhdGguIFRoZSBgYXBwbHlgIGd1aWRhbmNlIHNheXMgdGhhdCBhIHRhc2sgY2xvc2VzIG9ubHkgd2l0aCBgc3BlY2N0eCB0YXNrIGRvbmUgPGlkPmAgYW5kIHRoYXQgdGhlIGFnZW50IHJ1bnMgdGhlIGBzcGVjY3R4LXJ1bmAgc2tpbGwsIG5ldmVyIGAvb3BzeDphcHBseWAuCgojIyBJbnN0YWxsIGJ5IGNvcHkKCi0gQ2xvbmUgdGhlIHJlcG9zaXRvcnk6IGBnaXQgY2xvbmUgaHR0cHM6Ly9naXRodWIuY29tL3NvbnFoL3NwZWMtY3R4YC4KLSBDb3B5IGBzY2hlbWFzL3NwZWMtZHJpdmVuLWlkcy90ZW1wbGF0ZXNgIHRvIGBvcGVuc3BlYy90ZW1wbGF0ZXMvc3BlYy1kcml2ZW4taWRzYCBpbiB5b3VyIHByb2plY3QuCi0gS2VlcCBgc2NoZW1hOiBzcGVjLWRyaXZlbmAgaW4gYG9wZW5zcGVjL2NvbmZpZy55YW1sYC4KLSBNZXJnZSBgY29uZmlnLWZyYWdtZW50LnlhbWxgIGludG8gYG9wZW5zcGVjL2NvbmZpZy55YW1sYCBieSBoYW5kLiBBZGQgaXRzIGBydWxlc2AgYW5kIGBvcGVyYXRpb25zYCBrZXlzLiBLZWVwIGV2ZXJ5IGl0ZW0gcXVvdGVkLgotIFJ1biBgb3BlbnNwZWMgdmFsaWRhdGUgLS1hbGxgLgoKIyMgSW5zdGFsbCB3aXRoIGluaXQgLS1zY2hlbWEKCmBgYGJhc2gKbnB4IC15IHNwZWMtY3R4IGluaXQgLS1zY2hlbWEKYGBgCgotIFRoZSBjb21tYW5kIGNvcGllcyB0aGUgZm91ciB0ZW1wbGF0ZXMgdG8gYG9wZW5zcGVjL3RlbXBsYXRlcy9zcGVjLWRyaXZlbi1pZHMvYC4KLSBJdCBhZGRzIHRoZSBgcnVsZXNgIGFuZCBgb3BlcmF0aW9uc2Aga2V5cyB0aGF0IHRoZSBjb25maWcgZG9lcyBub3QgaGF2ZSB5ZXQuIEEga2V5IHRoYXQgZXhpc3RzIGdldHMgdGhlIGl0ZW1zIGl0IGxhY2tzLiBPdGhlciBrZXlzIHN0YXkgYXMgdGhleSBhcmUuCi0gSXQgcXVvdGVzIGEgbGlzdCBpdGVtIHRoYXQgWUFNTCB3b3VsZCByZWFkIGFzIGEgbWFwcGluZy4KLSBJdCBkb2VzIG5vdCB0b3VjaCB0aGUgYHNjaGVtYWAga2V5LiBBIHByb2plY3QgdGhhdCBzdGlsbCBzYXlzIGBzY2hlbWE6IHNwZWMtZHJpdmVuLWlkc2AgZ2V0cyBhIG5vdGUuIFNldCBgc2NoZW1hOiBzcGVjLWRyaXZlbmAgYnkgaGFuZCBhbmQgZGVsZXRlIGBvcGVuc3BlYy9zY2hlbWFzL3NwZWMtZHJpdmVuLWlkcy9gLgoKIyMgVmVyc2lvbgoKVGhlIHRlbXBsYXRlcyBmb2xsb3cgdGhlIGBzcGVjLWRyaXZlbmAgdGVtcGxhdGVzIG9mIE9wZW5TcGVjIDEuMTQuMyB3aXRoIHRoZSBJRCBwbGFjZWhvbGRlcnMgYWRkZWQuIGB0ZXN0cy9zY2hlbWEvdmFsaWRhdGUuc2hgIGNoZWNrcyB0aGVtIG9uIHVwc3RyZWFtIE9wZW5TcGVjIGluIENJLgo=",
|
|
5
|
+
"schemas/spec-driven-ids/config-fragment.yaml": "IyBzcGVjLWN0eCBmcmFnbWVudCBmb3Igb3BlbnNwZWMvY29uZmlnLnlhbWwuIHNwZWNjdHggaW5pdCAtLXNjaGVtYSBhZGRzIGVhY2ggdG9wLWxldmVsIGtleQojIHRoYXQgdGhlIHByb2plY3QgY29uZmlnIGRvZXMgbm90IGhhdmUgeWV0LiBBIGtleSB0aGF0IGV4aXN0cyBnZXRzIHRoZSBpdGVtcyBpdCBsYWNrcy4KcnVsZXM6CiAgc3BlY3M6CiAgICAtICdFdmVyeSByZXF1aXJlbWVudCBoZWFkZXIgc3RhcnRzIHdpdGggaXRzIElELCBlLmcuICIjIyMgUmVxdWlyZW1lbnQ6IFJFUS1BQkMtMDAxIE5hbWUiLiBHZXQgdGhlIElEIGZyb20gYHNwZWNjdHggaWQgbmV3IFJFUSBBQkNgLicKICAgIC0gJ0V2ZXJ5IHNjZW5hcmlvIGhlYWRlciBzdGFydHMgd2l0aCBpdHMgSUQsIGUuZy4gIiMjIyMgU2NlbmFyaW86IFNDTi1BQkMtMDAxLTEgTmFtZSIuJwogICAgLSAnQSByZXF1aXJlbWVudCBtYXkgZW5kIHdpdGggYSBsaW5lICJSZWZzOiBSRVEtQUJDLTAwMiwgQURSLTAwMSIgdG8gbGluayByZWxhdGVkIElEcy4nCiAgICAtICdSZXF1aXJlbWVudCB0ZXh0IHN0YXlzIHVuZGVyIDUwMCBjaGFyYWN0ZXJzLiBNb3ZlIGRldGFpbCBpbnRvIHNjZW5hcmlvcy4nCiAgICAtICdUaGUgdGVtcGxhdGVzIHdpdGggdGhlIElEIHBsYWNlaG9sZGVycyBhcmUgaW4gb3BlbnNwZWMvdGVtcGxhdGVzL3NwZWMtZHJpdmVuLWlkcy8uIFN0YXJ0IGEgc3BlYyBmcm9tIHNwZWMubWQgYW5kIGEgdGFzayBsaXN0IGZyb20gdGFza3MubWQgdGhlcmUuJwogIGRlc2lnbjoKICAgIC0gJ0VhY2ggZGVjaXNpb24gaXMgb25lIGJ1bGxldCB1bmRlciAiIyMgRGVjaXNpb25zIi4gQWRkIG5ldyBkZWNpc2lvbnMgYXQgdGhlIGVuZC4nCiAgdGFza3M6CiAgICAtICdFdmVyeSB0YXNrIGNpdGVzIGF0IGxlYXN0IG9uZSBSRVEgSUQgaW4gc3F1YXJlIGJyYWNrZXRzLCBlLmcuICJbUkVRLUFCQy0wMDFdIi4nCiAgICAtICdFdmVyeSB0YXNrIHN0YXRlcyBpdHMgdmVyaWZpY2F0aW9uIGFzIGEgY29tbWFuZCBpbiBhIGNvZGUgc3BhbiBhZnRlciAiVmVyaWZ5OiIuJwoKb3BlcmF0aW9uczoKICBhcHBseToKICAgIGd1aWRhbmNlOgogICAgICAtICdTdGFydCBlYWNoIHRhc2sgd2l0aCBgc3BlY2N0eCB0YXNrIHN0YXJ0IDxpZD5gIGFuZCByZWFkIHRoZSBidW5kbGUgaXQgcHJpbnRzIGJlZm9yZSB5b3UgcmVhZCBzcGVjcy4nCiAgICAgIC0gJ1RhZyBjb2RlIHdpdGggdGhlIFJFUSBJRCBpdCBpbXBsZW1lbnRzIChhIGNvbW1lbnQgYC8vIFJFUS1BQkMtMDAxYCkuIFRhZyB0ZXN0cyB3aXRoIGAvLyBDb3ZlcnM6IFJFUS1BQkMtMDAxYC4nCiAgICAgIC0gJ0Nsb3NlIGVhY2ggdGFzayBvbmx5IHdpdGggYHNwZWNjdHggdGFzayBkb25lIDxpZD5gLiBOZXZlciBlZGl0IGEgY2hlY2tib3guIE5ldmVyIHJ1biBgL29wc3g6YXBwbHlgOyBydW4gdGhlIGBzcGVjY3R4LXJ1bmAgc2tpbGwuJwogICAgICAtICdSdW4gYHNwZWNjdHggY2hlY2sgLS1zdHJpY3RgIGFmdGVyIGVhY2ggdGFzayBncm91cC4nCiAgYXJjaGl2ZToKICAgIGd1aWRhbmNlOgogICAgICAtICdSdW4gYHNwZWNjdHggY2hlY2sgLS1zdHJpY3RgIGFuZCBgb3BlbnNwZWMgdmFsaWRhdGUgLS1hbGwgLS1zdHJpY3RgLiBCb3RoIG11c3QgcGFzcyBiZWZvcmUgYXJjaGl2ZS4nCiAgICAgIC0gJ1J1biBgb3BlbnNwZWMgdmFsaWRhdGUgLS1hbGwgLS1zdHJpY3RgIGFnYWluIGFmdGVyIGFyY2hpdmUgYW5kIGJlZm9yZSBwdXNoLicK",
|
|
7
6
|
"schemas/spec-driven-ids/templates/design.md": "IyBEZXNpZ24KCiMjIENvbnRleHQKCjwhLS0gVGhlIGN1cnJlbnQgc3RhdGUgYW5kIHRoZSBjb25zdHJhaW50cyB0aGF0IHNoYXBlIHRoZSBhcHByb2FjaC4gVGhlIHByb3Bvc2FsIGhvbGRzIHRoZSBtb3RpdmF0aW9uLiAtLT4KCiMjIEdvYWxzIC8gTm9uLUdvYWxzCgoqKkdvYWxzOioqCjwhLS0gV2hhdCB0aGlzIGRlc2lnbiBnaXZlcyAtLT4KCioqTm9uLUdvYWxzOioqCjwhLS0gV2hhdCBzdGF5cyBvdXQgb2Ygc2NvcGUgLS0+CgojIyBEZWNpc2lvbnMKCjwhLS0gT25lIGJ1bGxldCBwZXIgZGVjaXNpb24uIFRoZSBJRCBvZiBhIGRlY2lzaW9uIGlzIDxjaGFuZ2UgbnVtYmVyPi9EPG4+IGJ5IHBvc2l0aW9uLiBBZGQgbmV3IG9uZXMgYXQgdGhlIGVuZC4gLS0+Ci0gPCEtLSBUaGUgZGVjaXNpb24sIHRoZSBhbHRlcm5hdGl2ZSwgYW5kIHdoeS4gLS0+IFJlZnM6IDwhLS0gUkVRLTxDQVA+LU5OTiwgQURSLU5OTiAtLT4KCiMjIFJpc2tzIC8gVHJhZGUtb2ZmcwoKPCEtLSBLbm93biByaXNrcyBhbmQgdGhlIG1pdGlnYXRpb24gZm9yIGVhY2ggLS0+Cg==",
|
|
8
7
|
"schemas/spec-driven-ids/templates/proposal.md": "IyBQcm9wb3NhbAoKIyMgV2h5Cgo8IS0tIEV4cGxhaW4gdGhlIG1vdGl2YXRpb24gZm9yIHRoaXMgY2hhbmdlLiBXaGF0IHByb2JsZW0gZG9lcyB0aGlzIHNvbHZlPyBXaHkgbm93PyAtLT4KCiMjIFdoYXQgQ2hhbmdlcwoKPCEtLSBEZXNjcmliZSB3aGF0IHdpbGwgY2hhbmdlLiBCZSBzcGVjaWZpYyBhYm91dCBuZXcgY2FwYWJpbGl0aWVzLCBtb2RpZmljYXRpb25zLCBvciByZW1vdmFscy4gLS0+CgojIyBDYXBhYmlsaXRpZXMKCiMjIyBOZXcgQ2FwYWJpbGl0aWVzCjwhLS0gQ2FwYWJpbGl0aWVzIGJlaW5nIGludHJvZHVjZWQuIE5hbWUgZWFjaCBjYXBhYmlsaXR5IGZvciBhIGNvaGVzaXZlIHN5c3RlbQogICAgIGJlaGF2aW9yIHRoYXQgY2FuIG93biByZWxhdGVkIHJlcXVpcmVtZW50cyBhcyB0aGUgc3lzdGVtIGV2b2x2ZXMuIERvIG5vdCBuYW1lCiAgICAgaW1wbGVtZW50YXRpb24gdGFza3Mgb3IgcHJvcG9zYWwgc2VjdGlvbnMuIEF2b2lkIGJyb2FkIGNhdGNoLWFsbCBuYW1lcy4gVXNlCiAgICAga2ViYWItY2FzZSBmb3IgcGF0aCBzZWdtZW50cyB5b3UgaW50cm9kdWNlIChlLmcuLCB1c2VyLWF1dGggb3IgaWRlbnRpdHkvdXNlci1hdXRoKQogICAgIHRoYXQgZm9sbG93IHRoZSBwcm9qZWN0J3MgZXhpc3Rpbmcgc3BlYyBvcmdhbml6YXRpb24uIEVhY2ggY3JlYXRlcwogICAgIHNwZWNzLzxjYXBhYmlsaXR5LXBhdGg+L3NwZWMubWQuIC0tPgotIGA8Y2FwYWJpbGl0eS1wYXRoPmA6IDxicmllZiBkZXNjcmlwdGlvbiBvZiB3aGF0IHRoaXMgY2FwYWJpbGl0eSBjb3ZlcnM+CgojIyMgTW9kaWZpZWQgQ2FwYWJpbGl0aWVzCjwhLS0gRXhpc3RpbmcgY2FwYWJpbGl0aWVzIHdob3NlIFJFUVVJUkVNRU5UUyBhcmUgY2hhbmdpbmcgKG5vdCBqdXN0IGltcGxlbWVudGF0aW9uKS4KICAgICBPbmx5IGxpc3QgaGVyZSBpZiBzcGVjLWxldmVsIGJlaGF2aW9yIGNoYW5nZXMuIEVhY2ggbmVlZHMgYSBkZWx0YSBzcGVjIGZpbGUuCiAgICAgVXNlIHRoZSBleGFjdCBleGlzdGluZyBwYXRoIHVuZGVyIG9wZW5zcGVjL3NwZWNzLy4gTGVhdmUgZW1wdHkgaWYgbm8gcmVxdWlyZW1lbnQKICAgICBjaGFuZ2VzLiBBIGNoYW5nZSB3aXRoIG5vIGNhcGFiaWxpdGllcyBhdCBhbGwgKHB1cmUgcmVmYWN0b3IsIHRvb2xpbmcsIGRvY3MpCiAgICAgbXVzdCBzZXQgYHNraXBfc3BlY3M6IHRydWVgIGluIGl0cyAub3BlbnNwZWMueWFtbCAtIG9wZW5zcGVjIHZhbGlkYXRlIHJlamVjdHMKICAgICBhIHplcm8tZGVsdGEgY2hhbmdlIHdpdGhvdXQgdGhhdCBtYXJrZXIuIERvIG5vdCBpbnZlbnQgYSByZXF1aXJlbWVudCBqdXN0IHRvCiAgICAgc2F0aXNmeSB2YWxpZGF0aW9uLiAtLT4KLSBgPGV4aXN0aW5nLWNhcGFiaWxpdHktcGF0aD5gOiA8d2hhdCByZXF1aXJlbWVudCBpcyBjaGFuZ2luZz4KCiMjIEltcGFjdAoKPCEtLSBBZmZlY3RlZCBjb2RlLCBBUElzLCBkZXBlbmRlbmNpZXMsIHN5c3RlbXMgLS0+Cg==",
|
|
9
8
|
"schemas/spec-driven-ids/templates/spec.md": "IyBTcGVjIERlbHRhCgojIyBQdXJwb3NlCjwhLS0gTmV3IGNhcGFiaWxpdHkgb25seS4gV3JpdGUgb25lIG9yIHR3byBzZW50ZW5jZXMgb2YgNTAgY2hhcmFjdGVycyBvciBtb3JlLiBEZWxldGUgdGhpcyBzZWN0aW9uIGZvciBhbiBleGlzdGluZyBjYXBhYmlsaXR5LiAtLT4KCiMjIEFEREVEIFJlcXVpcmVtZW50cwoKIyMjIFJlcXVpcmVtZW50OiBSRVEtPENBUD4tTk5OIDwhLS0gcmVxdWlyZW1lbnQgbmFtZSAtLT4KPCEtLSBSZXF1aXJlbWVudCB0ZXh0LiBSZXBsYWNlIFJFUS08Q0FQPi1OTk4gd2l0aCB0aGUgb3V0cHV0IG9mIGBzcGVjY3R4IGlkIG5ldyBSRVEgPENBUD5gLiAtLT4KClJlZnM6IDwhLS0gcmVsYXRlZCBJRHMgc3VjaCBhcyBBRFItMDAxLiBEZWxldGUgdGhpcyBsaW5lIHdoZW4gdGhlcmUgYXJlIG5vbmUuIC0tPgoKIyMjIyBTY2VuYXJpbzogU0NOLTxDQVA+LU5OTi0xIDwhLS0gc2NlbmFyaW8gbmFtZS4gR2V0IHRoZSBJRCBmcm9tIGBzcGVjY3R4IGlkIG5ldyBTQ04gPENBUD4gLS1mb3IgUkVRLTxDQVA+LU5OTmAuIC0tPgotICoqV0hFTioqIDwhLS0gY29uZGl0aW9uIC0tPgotICoqVEhFTioqIDwhLS0gZXhwZWN0ZWQgb3V0Y29tZSAtLT4K",
|
|
10
9
|
"schemas/spec-driven-ids/templates/tasks.md": "IyBUYXNrcwoKIyMgMS4gPCEtLSBUYXNrIGdyb3VwIG5hbWUgLS0+CgotIFsgXSAxLjEgPCEtLSBUYXNrIGRlc2NyaXB0aW9uIC0tPiBbUkVRLTxDQVA+LU5OTl0gVmVyaWZ5OiBgPCEtLSBjb21tYW5kIC0tPmAKLSBbIF0gMS4yIDwhLS0gVGFzayBkZXNjcmlwdGlvbiAtLT4gW1JFUS08Q0FQPi1OTk5dIFZlcmlmeTogYDwhLS0gY29tbWFuZCAtLT5gCgojIyAyLiA8IS0tIFRhc2sgZ3JvdXAgbmFtZSAtLT4KCi0gWyBdIDIuMSA8IS0tIFRhc2sgZGVzY3JpcHRpb24gLS0+IFtSRVEtPENBUD4tTk5OXSBWZXJpZnk6IGA8IS0tIGNvbW1hbmQgLS0+YAo=",
|
|
11
|
-
"skills/specctx-bootstrap/SKILL.md": "
|
|
12
|
-
"skills/specctx-done/SKILL.md": "
|
|
13
|
-
"skills/specctx-next/SKILL.md": "
|
|
10
|
+
"skills/specctx-bootstrap/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtYm9vdHN0cmFwCmRlc2NyaXB0aW9uOiBTdGFydCBhIHNlc3Npb24gaW4gYW4gT3BlblNwZWMgcHJvamVjdCB0aGF0IHVzZXMgc3BlYy1jdHguIFVzZSBhdCB0aGUgc3RhcnQgb2YgZXZlcnkgc2Vzc2lvbiwgYW5kIG9uICJzdGFydCIsICJib290c3RyYXAiLCBvciAid2hhdCBzaG91bGQgSSB3b3JrIG9uIi4gTG9hZHMgb25seSB0aGUgc3BlYyB0ZXh0IHRoYXQgdGhlIG5leHQgdGFzayBuZWVkcy4KLS0tCgpUaGlzIHNraWxsIGlzIG5vdyBgc3BlY2N0eC1ydW5gLiBSdW4gaXQuCg==",
|
|
11
|
+
"skills/specctx-done/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtZG9uZQpkZXNjcmlwdGlvbjogQ2xvc2UgYSB0YXNrIGluIGFuIE9wZW5TcGVjIHByb2plY3QgdGhhdCB1c2VzIHNwZWMtY3R4LiBSdW5zIHRoZSBWZXJpZnkgY29tbWFuZCwgd3JpdGVzIGV2aWRlbmNlLCBzZXRzIHRoZSBjaGVja2JveCwgd3JpdGVzIHRoZSBoYW5kb2ZmLCBhbmQgc2hvd3MgdGhlIG1ldHJpY3MuIFVzZSB3aGVuIHRoZSB3b3JrIG9mIGEgdGFzayBpcyBjb21wbGV0ZSwgb3Igb24gImRvbmUiLCAiY2xvc2UgdGhlIHRhc2siLCBhbmQgImVuZCB0aGUgc2Vzc2lvbiIuCi0tLQoKVGhpcyBza2lsbCBpcyBub3cgYHNwZWNjdHgtcnVuYC4gUnVuIGl0Lgo=",
|
|
12
|
+
"skills/specctx-next/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtbmV4dApkZXNjcmlwdGlvbjogUGljayBhbmQgc3RhcnQgdGhlIG5leHQgdGFzayBpbiB0aGUgbWlkZGxlIG9mIGEgc2Vzc2lvbiBpbiBhbiBPcGVuU3BlYyBwcm9qZWN0IHRoYXQgdXNlcyBzcGVjLWN0eC4gVXNlIGFmdGVyIHlvdSBjbG9zZSBhIHRhc2ssIG9yIG9uICJuZXh0IHRhc2siIGFuZCAid2hhdCBpcyBuZXh0Ii4KLS0tCgpUaGlzIHNraWxsIGlzIG5vdyBgc3BlY2N0eC1ydW5gLiBSdW4gaXQuCg==",
|
|
13
|
+
"skills/specctx-run/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtcnVuCmRlc2NyaXB0aW9uOiBSdW4gdGhlIHNwZWMtY3R4IGxvb3AgaW4gYW4gT3BlblNwZWMgcHJvamVjdC4gVXNlIG9uICJydW4iLCAiY29udGludWUiLCAibmV4dCB0YXNrIiwgImJ1aWxkIHRoaXMiLCAiY2Fycnkgb24iLCBhbmQgb24gYSBmaXJzdCBtZXNzYWdlIHRoYXQgZGVzY3JpYmVzIGEgY2hhbmdlIHRvIGJ1aWxkLiBUaGUgZW5naW5lIHBpY2tzIHRoZSBuZXh0IGFjdGlvbi4gVGhlIHNraWxsIGFjdHMgb24gaXQgdW50aWwgdGhlIGNoYW5nZSByZWFjaGVzIHRoZSBhcmNoaXZlIG9yIGEgaHVtYW4gbXVzdCBkZWNpZGUuCi0tLQoKIyBzcGVjY3R4LXJ1bgoKUnVuIGBzcGVjY3R4YCBhcyBgbnB4IHNwZWMtY3R4YCB3aGVuIGl0IGlzIG5vdCBpbnN0YWxsZWQuIGBvcGVuc3BlY2AgaXMgdGhlIGNvbW1hbmQgaW4gYFtnYXRlXSBvcGVuc3BlY2Agb2YgYHNwZWMtY3R4LnRvbWxgLgoKIyMgVGhlIGxvb3AKCjEuIFJ1biBgc3BlY2N0eCBuZXh0IC0tanNvbmAuIFJlYWQgYGFjdGlvbmAsIGBjaGFuZ2VgIChgPGM+YCksIGB0YXNrYCAoYDx0PmApLCBgY29tbWFuZGAsIGByZWFzb25gLCBhbmQgYGh1bWFuYC4KMi4gQWN0IG9uIGBhY3Rpb25gIGFzIHRoZSB0YWJsZSBiZWxvdyBzYXlzLgozLiBSdW4gYHNwZWNjdHggaGFuZG9mZiB3cml0ZWAuCjQuIFJlcGVhdCBmcm9tIHN0ZXAgMSB1bnRpbCBgYWN0aW9uYCBpcyBgc3RvcGAgb3IgYGh1bWFuYCBpcyB0cnVlLgoKIyMgQWN0aW9ucwoKLSBgcGxhbmA6IGZvbGxvdyB0aGUgdXBzdHJlYW0gYG9wZW5zcGVjLXByb3Bvc2VgIHNraWxsIGZvciB0aGUgY2hhbmdlIGluIGBjb21tYW5kYC4gVGFrZSBldmVyeSBJRCBmcm9tIGBzcGVjY3R4IGlkIG5ldyBSRVEgPENBUD5gIGFuZCBgc3BlY2N0eCBpZCBuZXcgU0NOIDxDQVA+IC0tZm9yIDxSRVE+YC4gR2l2ZSBldmVyeSB0YXNrIGEgYFtSRVEtLi4uXWAgY2l0YXRpb24gYW5kIGEgYFZlcmlmeTpgIGNvZGUgc3Bhbi4gVGhlbiBydW4gYHNwZWNjdHggY2hlY2sgLS1zdHJpY3RgIGFuZCBgb3BlbnNwZWMgdmFsaWRhdGUgPGM+IC0tc3RyaWN0YC4gRml4IGV2ZXJ5IGZpbmRpbmcuIFN0b3AgYW5kIHNob3cgdGhlIHBsYW4gdG8gdGhlIHVzZXIuIFRoaXMgaXMgdGhlIHBsYW4gc3RvcC4KLSBgY2l0ZWA6IHJ1biBgc3BlY2N0eCBxdWVyeSAtLWNoYW5nZSA8Yz4gLS1raW5kIHJlcWAuIEFkZCB0aGUgYFtSRVEtLi4uXWAgY2l0YXRpb24gYW5kIGEgYFZlcmlmeTpgIGNvZGUgc3BhbiB0byB0aGUgdGFzayBsaW5lIG5hbWVkIGluIGB0YXNrYC4gUnVuIGBzcGVjY3R4IGNoZWNrYC4gQ29udGludWUuCi0gYHN0YXJ0YDogcnVuIGBzcGVjY3R4IHRhc2sgc3RhcnQgPHQ+YC4gUmVhZCB0aGUgYnVuZGxlIGl0IHByaW50cy4gQ29udGludWUuCi0gYHdvcmtgOiBpbXBsZW1lbnQgdGhlIHRhc2suIFRhZyBjb2RlIHdpdGggYC8vIFJFUS0uLi5gLiBUYWcgdGVzdHMgd2l0aCBgLy8gQ292ZXJzOiBSRVEtLi4uYC4gVGhlbiBydW4gYHNwZWNjdHggdGFzayBkb25lIDx0PmAuCi0gYGZpeGA6IHJlYWQgYHJlYXNvbmAuIEl0IGhvbGRzIHRoZSB0YWlsIG9mIHRoZSBmYWlsZWQgVmVyaWZ5IG91dHB1dC4gRml4IHRoZSBjYXVzZS4gUnVuIGBzcGVjY3R4IHRhc2sgZG9uZSA8dD5gIGFnYWluLgotIGByZXBhaXJgOiBydW4gYHNwZWNjdHggdGFzayBkb25lIDx0PmAuIEl0IHJ1bnMgVmVyaWZ5IGZvciBhIHRpY2tlZCB0YXNrIHRoYXQgaGFzIG5vIGV2aWRlbmNlLgotIGBnYXRlYDogcnVuIGBzcGVjY3R4IGdhdGUgLS1zdHJpY3RgLiBGaXggZXZlcnkgZmluZGluZy4gUnVuIGl0IGFnYWluIHVudGlsIGl0IGV4aXRzIDAuCi0gYGFyY2hpdmVgOiB3aGVuIGBodW1hbmAgaXMgdHJ1ZSwgc3RvcCBhbmQgYXNrIHRoZSB1c2VyIGluIG9uZSBsaW5lIHdoZXRoZXIgdG8gYXJjaGl2ZSBgPGM+YC4gV2hlbiBgaHVtYW5gIGlzIGZhbHNlLCBmb2xsb3cgdGhlIHVwc3RyZWFtIGBvcGVuc3BlYy1hcmNoaXZlLWNoYW5nZWAgc2tpbGwuIFRoZW4gcnVuIGBvcGVuc3BlYyB2YWxpZGF0ZSAtLWFsbCAtLXN0cmljdGAsIGBzcGVjY3R4IGNoZWNrIC0tc3RyaWN0YCwgYW5kIGBzcGVjY3R4IGhhbmRvZmYgd3JpdGVgLgotIGBhc2tgOiBwcmludCBgcmVhc29uYC4gSXQgaXMgdGhlIG9wZW4gcXVlc3Rpb24uIFN0b3AuIFRoZSB1c2VyIGFuc3dlcnMgd2l0aCBgc3BlY2N0eCBoYW5kb2ZmIHJlc29sdmUgPG4+ICI8YW5zd2VyPiJgLgotIGBzdG9wYDogcHJpbnQgYHJlYXNvbmAuIFN0b3AuCgojIyBSdWxlcwoKLSBOZXZlciB0aWNrIGEgY2hlY2tib3ggYnkgaGFuZC4gYHNwZWNjdHggdGFzayBkb25lYCBpcyB0aGUgb25seSB3YXkgYSB0YXNrIGNsb3Nlcy4KLSBOZXZlciB1c2UgYC0tbWFudWFsYCBvbiBhIHRhc2sgd2l0aCBhIFZlcmlmeSBjb21tYW5kLgotIE5ldmVyIGNhbGwgYC9vcHN4OmFwcGx5YC4gVGhpcyBza2lsbCByZXBsYWNlcyBpdC4KLSBOZXZlciByZWFkIGBvcGVuc3BlYy9zcGVjcy9gIG9yIGBvcGVuc3BlYy9jaGFuZ2VzLyovc3BlY3MvYCBkaXJlY3RseS4gVXNlIHRoZSBidW5kbGUgZnJvbSBgdGFzayBzdGFydGAgb3IgYHNwZWNjdHggYnVuZGxlIDxpZD5gLgotIFN0b3Agb24gYW55IGBhY3Rpb25gIG5vdCBpbiB0aGUgbGlzdCBhYm92ZS4gUHJpbnQgdGhlIG9iamVjdCBhbmQgYXNrIHRoZSB1c2VyLgotIEtlZXAgbm8gc3RhdGUgaW4geW91ciBjb250ZXh0LiBBIG5ldyBzZXNzaW9uIHN0YXJ0cyB0aGUgc2FtZSBsb29wIGFnYWluIHdpdGggc3RlcCAxLgo=",
|
|
14
14
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// REQ-ADOPT-005 REQ-CHK-002 the baseline ratchet in .ctx/baseline.json: sorted "rule id" pairs.
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { lintLine, parseLintLine } from "../lint/counts.js";
|
|
4
5
|
import { cmpStr } from "../util/pystr.js";
|
|
5
6
|
export const BASELINE_PATH = ".ctx/baseline.json";
|
|
6
|
-
export const BASELINE_RULES = ["uncovered", "scenario-untested", "done-without-evidence", "manual-evidence", "task-without-req", "task-without-verify"];
|
|
7
|
-
|
|
7
|
+
export const BASELINE_RULES = ["uncovered", "scenario-untested", "done-without-evidence", "manual-evidence", "task-without-req", "task-without-verify", "missing-id"];
|
|
8
|
+
/** REQ-ADOPT-005 a missing-id finding has no ID. Its key carries the file and the header title. */
|
|
9
|
+
const key = (f) => (f.rule === "missing-id" ? `${f.rule} ${f.file} ${f.id}` : `${f.rule} ${f.id}`);
|
|
8
10
|
/** A warning line reads "manual-evidence <task>". */
|
|
9
11
|
const warningKey = (w) => w;
|
|
10
12
|
export function readBaseline(root) {
|
|
@@ -24,24 +26,48 @@ function save(root, entries) {
|
|
|
24
26
|
}
|
|
25
27
|
/** The current debt that the baseline may hold: strict findings and warnings of the listed rules. */
|
|
26
28
|
export function debt(findings, warnings) {
|
|
27
|
-
const out = findings.filter((f) => BASELINE_RULES.includes(f.rule)).map((f) => key(f
|
|
29
|
+
const out = findings.filter((f) => BASELINE_RULES.includes(f.rule)).map((f) => key(f));
|
|
28
30
|
for (const w of warnings)
|
|
29
31
|
if (BASELINE_RULES.includes(w.split(" ")[0]))
|
|
30
32
|
out.push(warningKey(w));
|
|
31
33
|
return out;
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
/** REQ-ADOPT-007 one `lint <file> <rule> <count>` line per key with hard lint findings. */
|
|
36
|
+
export function lintDebt(counts) {
|
|
37
|
+
return [...counts].filter(([, n]) => n > 0).map(([k, n]) => lintLine(k, n));
|
|
38
|
+
}
|
|
39
|
+
export function writeBaseline(root, findings, warnings, lint = new Map()) {
|
|
40
|
+
return save(root, [...debt(findings, warnings), ...lintDebt(lint)]);
|
|
35
41
|
}
|
|
36
42
|
/** Drop entries that no longer fire. Returns the number dropped. */
|
|
37
|
-
export function pruneBaseline(root, findings, warnings) {
|
|
43
|
+
export function pruneBaseline(root, findings, warnings, lint = new Map()) {
|
|
38
44
|
const cur = readBaseline(root);
|
|
39
45
|
if (!cur)
|
|
40
46
|
return 0;
|
|
41
47
|
const live = new Set(debt(findings, warnings));
|
|
42
|
-
const kept = [
|
|
48
|
+
const kept = [];
|
|
49
|
+
let pruned = 0;
|
|
50
|
+
for (const e of cur) {
|
|
51
|
+
const l = parseLintLine(e);
|
|
52
|
+
if (l) {
|
|
53
|
+
// REQ-ADOPT-007 a lint line drops at zero and lowers when the count fell
|
|
54
|
+
const now = lint.get(l.key) ?? 0;
|
|
55
|
+
if (now === 0)
|
|
56
|
+
pruned++;
|
|
57
|
+
else if (now < l.count) {
|
|
58
|
+
pruned++;
|
|
59
|
+
kept.push(lintLine(l.key, now));
|
|
60
|
+
}
|
|
61
|
+
else
|
|
62
|
+
kept.push(e);
|
|
63
|
+
}
|
|
64
|
+
else if (live.has(e))
|
|
65
|
+
kept.push(e);
|
|
66
|
+
else
|
|
67
|
+
pruned++;
|
|
68
|
+
}
|
|
43
69
|
save(root, kept);
|
|
44
|
-
return
|
|
70
|
+
return pruned;
|
|
45
71
|
}
|
|
46
72
|
/** REQ-CHK-002 a finding or warning in the baseline leaves the output and counts as baselined. */
|
|
47
73
|
export function applyBaseline(root, findings, warnings) {
|
|
@@ -50,7 +76,7 @@ export function applyBaseline(root, findings, warnings) {
|
|
|
50
76
|
return { findings, warnings, baselined: 0 };
|
|
51
77
|
let n = 0;
|
|
52
78
|
const f2 = findings.filter((f) => {
|
|
53
|
-
const hit = base.has(key(f
|
|
79
|
+
const hit = base.has(key(f));
|
|
54
80
|
if (hit)
|
|
55
81
|
n++;
|
|
56
82
|
return !hit;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// REQ-ADOPT-008 the baseline against the merge base: new entries fail the gate (020/D2).
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { parseLintLine } from "../lint/counts.js";
|
|
4
|
+
import { cmpStr } from "../util/pystr.js";
|
|
5
|
+
import { BASELINE_PATH } from "./baseline.js";
|
|
6
|
+
/**
|
|
7
|
+
* Lines of `current` that are new debt against `base`.
|
|
8
|
+
* A line that the base lacks is new. A `lint <file> <rule> <count>` line with a count above
|
|
9
|
+
* the base line of the same key is new, and reads `lint <file> <rule> <count> (base <n>)`.
|
|
10
|
+
*/
|
|
11
|
+
export function newEntries(current, base) {
|
|
12
|
+
const baseSet = new Set(base);
|
|
13
|
+
const baseLint = new Map();
|
|
14
|
+
for (const b of base) {
|
|
15
|
+
const l = parseLintLine(b);
|
|
16
|
+
if (l)
|
|
17
|
+
baseLint.set(l.key, l.count);
|
|
18
|
+
}
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const c of current) {
|
|
21
|
+
if (baseSet.has(c))
|
|
22
|
+
continue;
|
|
23
|
+
const l = parseLintLine(c);
|
|
24
|
+
if (l) {
|
|
25
|
+
const n = baseLint.get(l.key);
|
|
26
|
+
if (n === undefined)
|
|
27
|
+
out.push(c);
|
|
28
|
+
else if (l.count > n)
|
|
29
|
+
out.push(`${c} (base ${n})`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
out.push(c);
|
|
33
|
+
}
|
|
34
|
+
return [...new Set(out)].sort(cmpStr);
|
|
35
|
+
}
|
|
36
|
+
function git(root, args) {
|
|
37
|
+
const r = spawnSync("git", args, { cwd: root, encoding: "utf8" });
|
|
38
|
+
return { ok: r.status === 0, out: (r.stdout ?? "").trim() };
|
|
39
|
+
}
|
|
40
|
+
/** `.ctx/baseline.json` at `git merge-base HEAD <base>`, or the reason to skip the step. */
|
|
41
|
+
export function baseAtMergeBase(root, base) {
|
|
42
|
+
const inside = git(root, ["rev-parse", "--is-inside-work-tree"]);
|
|
43
|
+
if (!inside.ok || inside.out !== "true")
|
|
44
|
+
return { skip: "no git repository" };
|
|
45
|
+
const mb = git(root, ["merge-base", "HEAD", base]);
|
|
46
|
+
if (!mb.ok || !mb.out)
|
|
47
|
+
return { skip: `no merge base with ${base}` };
|
|
48
|
+
const sha = mb.out;
|
|
49
|
+
const show = spawnSync("git", ["show", `${sha}:${BASELINE_PATH}`], { cwd: root, encoding: "utf8" });
|
|
50
|
+
if (show.status !== 0)
|
|
51
|
+
return { skip: `no ${BASELINE_PATH} at ${sha.slice(0, 7)}` };
|
|
52
|
+
let v;
|
|
53
|
+
try {
|
|
54
|
+
v = JSON.parse(show.stdout);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return { skip: `no ${BASELINE_PATH} at ${sha.slice(0, 7)}` };
|
|
58
|
+
}
|
|
59
|
+
if (!Array.isArray(v) || !v.every((x) => typeof x === "string"))
|
|
60
|
+
return { skip: `no ${BASELINE_PATH} at ${sha.slice(0, 7)}` };
|
|
61
|
+
return { lines: v, sha };
|
|
62
|
+
}
|
|
63
|
+
/** True when the subject starts with `baseline:`, the same pattern as REQ-GOV-007 for `release:`. */
|
|
64
|
+
export const isBaselineSubject = (subject) => subject.trimStart().startsWith("baseline:");
|
|
65
|
+
/**
|
|
66
|
+
* The subject of HEAD. On a merge commit, such as a pull request checkout in CI, the subject of the
|
|
67
|
+
* second parent counts too, because that is the commit the author wrote (020/D4).
|
|
68
|
+
*/
|
|
69
|
+
export function headSubjects(root) {
|
|
70
|
+
const head = git(root, ["log", "-1", "--format=%s", "HEAD"]);
|
|
71
|
+
if (!head.ok)
|
|
72
|
+
return [];
|
|
73
|
+
const out = [head.out];
|
|
74
|
+
const parents = git(root, ["rev-list", "--parents", "-n", "1", "HEAD"]);
|
|
75
|
+
if (parents.ok && parents.out.split(/\s+/).length > 2) {
|
|
76
|
+
const second = git(root, ["log", "-1", "--format=%s", "HEAD^2"]);
|
|
77
|
+
if (second.ok)
|
|
78
|
+
out.push(second.out);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|