@neturely/okffs 0.7.0 → 0.9.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/CHANGELOG.md +19 -1
- package/README.md +15 -2
- package/dist/cli/banner.d.ts +17 -0
- package/dist/cli/banner.d.ts.map +1 -0
- package/dist/cli/banner.js +65 -0
- package/dist/cli/banner.js.map +1 -0
- package/dist/cli/env.d.ts +30 -0
- package/dist/cli/env.d.ts.map +1 -0
- package/dist/cli/env.js +171 -0
- package/dist/cli/env.js.map +1 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +77 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/manifest.d.ts +48 -0
- package/dist/cli/manifest.d.ts.map +1 -0
- package/dist/cli/manifest.js +303 -0
- package/dist/cli/manifest.js.map +1 -0
- package/dist/cli/probe.d.ts +32 -0
- package/dist/cli/probe.d.ts.map +1 -0
- package/dist/cli/probe.js +101 -0
- package/dist/cli/probe.js.map +1 -0
- package/dist/cli/sanity.d.ts +16 -0
- package/dist/cli/sanity.d.ts.map +1 -0
- package/dist/cli/sanity.js +99 -0
- package/dist/cli/sanity.js.map +1 -0
- package/dist/cli/setup.d.ts +2 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +254 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +4 -2
- package/dist/github.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -105
- package/dist/index.js.map +1 -1
- package/dist/prompts/setup.d.ts +18 -0
- package/dist/prompts/setup.d.ts.map +1 -0
- package/dist/prompts/setup.js +77 -0
- package/dist/prompts/setup.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +166 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/commit_and_update.d.ts +19 -0
- package/dist/tools/commit_and_update.d.ts.map +1 -1
- package/dist/tools/commit_and_update.js +59 -6
- package/dist/tools/commit_and_update.js.map +1 -1
- package/dist/tools/configure.d.ts +27 -0
- package/dist/tools/configure.d.ts.map +1 -0
- package/dist/tools/configure.js +91 -0
- package/dist/tools/configure.js.map +1 -0
- package/dist/tools/fix_into_base.d.ts +23 -0
- package/dist/tools/fix_into_base.d.ts.map +1 -0
- package/dist/tools/fix_into_base.js +110 -0
- package/dist/tools/fix_into_base.js.map +1 -0
- package/dist/tools/merge_pull_request.d.ts +6 -3
- package/dist/tools/merge_pull_request.d.ts.map +1 -1
- package/dist/tools/merge_pull_request.js +52 -18
- package/dist/tools/merge_pull_request.js.map +1 -1
- package/dist/tools/reply_to_review_comment.d.ts +4 -1
- package/dist/tools/reply_to_review_comment.d.ts.map +1 -1
- package/dist/tools/reply_to_review_comment.js +39 -10
- package/dist/tools/reply_to_review_comment.js.map +1 -1
- package/dist/tools/update_project_status.d.ts +2 -2
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ See [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.9.0] - 2026-07-11
|
|
9
|
+
### Changed
|
|
10
|
+
- commit_and_update: empty commit subject from whitespace-only or leading-blank-line hint ([#236](https://github.com/neturely/okffs/issues/236))
|
|
11
|
+
### Added
|
|
12
|
+
- `/okffs:setup` prompt + `configure` tool — configure okffs conversationally inside Claude Code (the in-chat counterpart to the `okffs setup` CLI). Claude interviews you from the same manifest the CLI uses and persists via `configure`, which writes only okffs's marked `.env` block and preserves your own variables/comments. On startup the server also stamps the configuring version and, when okffs has been upgraded and new options exist, nudges you (once) to run `/okffs:setup` ([#242](https://github.com/neturely/okffs/issues/242))
|
|
13
|
+
- `okffs setup` — an interactive CLI wizard to create/update `.env` for a repo, with Quick/Full first-run modes and a sync mode that only prompts for options new since your last run (e.g. after an okffs upgrade). Runs a non-fatal GitHub sanity check and prints a config banner. Reach it via `npx @neturely/okffs setup` ([#240](https://github.com/neturely/okffs/issues/240))
|
|
14
|
+
|
|
15
|
+
## [0.8.0] - 2026-07-08
|
|
16
|
+
### Added
|
|
17
|
+
- Add an issue-less "fix PR into base branch" tool (open + merge into develop, the mirror of promote_branch) ([#226](https://github.com/neturely/okffs/issues/226))
|
|
18
|
+
- merge_pull_request: accept pr_number for issue-less fix PRs into the base branch (merge-only mode) ([#224](https://github.com/neturely/okffs/issues/224))
|
|
19
|
+
### Changed
|
|
20
|
+
- Harden reply_to_review_comment against reply-without-resolve footgun ([#230](https://github.com/neturely/okffs/issues/230))
|
|
21
|
+
- commit_and_update: split hint into subject + body instead of a blind 72-char slice ([#228](https://github.com/neturely/okffs/issues/228))
|
|
22
|
+
- Document fix_into_base + merge_pull_request pr_number mode; remove leaked INSTRUCTIONS.md and gitignore it ([#232](https://github.com/neturely/okffs/issues/232))
|
|
23
|
+
|
|
8
24
|
## [0.7.0] - 2026-07-06
|
|
9
25
|
### Added
|
|
10
26
|
- Address #217 review: allow clearing milestone in update_issue + accurate auth wording ([#218](https://github.com/neturely/okffs/issues/218))
|
|
@@ -135,7 +151,9 @@ See [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
135
151
|
- `create_pull_request` commits the updated CHANGELOG onto the branch and pushes the branch before opening the PR, with non-blocking error handling ([#38](https://github.com/2b9sa2owa/okffs/issues/38)).
|
|
136
152
|
- All git operations now run via `execFileSync` with argument arrays (no shell), removing command-injection risk from branch names and commit hints; tools also checkout the target branch before committing/pushing and restore the original branch afterward.
|
|
137
153
|
|
|
138
|
-
[Unreleased]: https://github.com/neturely/okffs/compare/v0.
|
|
154
|
+
[Unreleased]: https://github.com/neturely/okffs/compare/v0.9.0...HEAD
|
|
155
|
+
[0.9.0]: https://github.com/neturely/okffs/compare/v0.8.0...v0.9.0
|
|
156
|
+
[0.8.0]: https://github.com/neturely/okffs/compare/v0.7.0...v0.8.0
|
|
139
157
|
[0.7.0]: https://github.com/neturely/okffs/compare/v0.6.0...v0.7.0
|
|
140
158
|
[0.6.0]: https://github.com/neturely/okffs/compare/v0.5.2...v0.6.0
|
|
141
159
|
[0.5.2]: https://github.com/neturely/okffs/compare/v0.5.1...v0.5.2
|
package/README.md
CHANGED
|
@@ -15,7 +15,17 @@ okffs is a [Model Context Protocol](https://modelcontextprotocol.io) server that
|
|
|
15
15
|
|
|
16
16
|
## Quick start
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
**1. Run the setup wizard** from your project root:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx @neturely/okffs setup
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
It walks you through auth, repo, and any optional features, writes a `.env`, then runs a quick GitHub sanity check. Re-run it any time (e.g. after upgrading okffs) — it only asks about options that are new since your last run. If you're happy relying on the [GitHub CLI](https://cli.github.com/) (`gh auth login`) and working inside the repo you want to manage, you can skip this step entirely — okffs works with **no config at all**.
|
|
25
|
+
|
|
26
|
+
> **Already in Claude Code?** Once okffs is connected you can configure it conversationally instead — run the **`/okffs:setup`** slash command and Claude will interview you and write the `.env` for you (no terminal needed). okffs also nudges you to run it after an upgrade introduces new options.
|
|
27
|
+
|
|
28
|
+
**2. Add a `.mcp.json`** to your project root:
|
|
19
29
|
|
|
20
30
|
```json
|
|
21
31
|
{
|
|
@@ -28,7 +38,7 @@ Add a `.mcp.json` to your project root:
|
|
|
28
38
|
}
|
|
29
39
|
```
|
|
30
40
|
|
|
31
|
-
That's it — `npx` fetches okffs on first use, and Claude Code picks up the tools automatically.
|
|
41
|
+
That's it — `npx` fetches okffs on first use, and Claude Code picks up the tools automatically. To use a token or a different repo instead of the `gh`/auto-detect defaults, run `okffs setup` (above) or edit `.env` by hand — see [Configuration](#configuration).
|
|
32
42
|
|
|
33
43
|
Now just ask Claude:
|
|
34
44
|
|
|
@@ -64,6 +74,7 @@ Claude infers labels (`bug`, `enhancement`, …) from the title and description
|
|
|
64
74
|
| `update_project_status` | Moves an issue between board columns (`Backlog`, `Ready`, `In Progress`, `Review`). Needs `OKFFS_PROJECT_ENABLED`. |
|
|
65
75
|
| `set_issue_fields` | Sets board Priority/Effort **and/or the native Issue Type** on an **existing** issue. Priority/Effort handle project-native and org-level Issue Fields (needs `OKFFS_PROJECT_ENABLED`); `type` is org-native and works independently. `create_issue` only sets these at creation; use this afterwards. Status stays with `update_project_status`. |
|
|
66
76
|
| `update_issue` | Edits an **existing** issue's core fields — `title`, `assignees`, `labels`, `milestone`, `body` — via one PATCH with the configured token. `labels`/`assignees` replace the whole set (`[]` clears). For Priority/Effort/Type use `set_issue_fields`; for Status use `update_project_status`. |
|
|
77
|
+
| `configure` | Writes okffs config to `.env` — the backend for the `/okffs:setup` prompt. Reuses the `okffs setup` wizard's manifest/serializer: updates only okffs's marked block, preserving your own variables and comments. Usually driven by `/okffs:setup`, not called directly. |
|
|
67
78
|
| `delete_issue` | Closes an issue **and** deletes its branch. Destructive — needs `confirmed: true`. |
|
|
68
79
|
| `delete_branch` | Deletes a branch **and** closes its issue. Destructive — needs `confirmed: true`. |
|
|
69
80
|
|
|
@@ -107,6 +118,8 @@ Once enabled, `list_issues` shows each issue's column, priority, and effort (ord
|
|
|
107
118
|
|
|
108
119
|
## Configuration
|
|
109
120
|
|
|
121
|
+
The quickest way to configure okffs is the wizard — `npx @neturely/okffs setup` — which writes and maintains the `.env` described below for you. The rest of this section documents what it (or you, by hand) can set.
|
|
122
|
+
|
|
110
123
|
okffs resolves a **token** and a **target repository** with fallbacks, so most users need little config.
|
|
111
124
|
|
|
112
125
|
**Token** (first match wins):
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function packageVersion(): string;
|
|
2
|
+
export interface BannerInfo {
|
|
3
|
+
version: string;
|
|
4
|
+
owner?: string;
|
|
5
|
+
repo?: string;
|
|
6
|
+
tokenSource: "env" | "gh" | "none";
|
|
7
|
+
baseBranch: string;
|
|
8
|
+
protectedBranch: string;
|
|
9
|
+
features: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function buildBannerInfo(values: Record<string, string>, resolved: {
|
|
12
|
+
owner?: string;
|
|
13
|
+
repo?: string;
|
|
14
|
+
tokenSource: "env" | "gh" | "none";
|
|
15
|
+
}): BannerInfo;
|
|
16
|
+
export declare function renderBanner(info: BannerInfo): string;
|
|
17
|
+
//# sourceMappingURL=banner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../../src/cli/banner.ts"],"names":[],"mappings":"AAMA,wBAAgB,cAAc,IAAI,MAAM,CAQvC;AAWD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,QAAQ,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,KAAK,GAAG,IAAI,GAAG,MAAM,CAAA;CAAE,GAC9E,UAAU,CAuBZ;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAWrD"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// The startup info banner: package version, resolved owner/repo, branch tiers,
|
|
2
|
+
// and which optional features are active. Factored out (plain string, no prompt
|
|
3
|
+
// library) so a future `okffs status` command can reuse it standalone.
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
export function packageVersion() {
|
|
6
|
+
try {
|
|
7
|
+
// dist/cli/banner.js → ../../package.json is the package root in both the
|
|
8
|
+
// published layout and local dist build.
|
|
9
|
+
return JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return "unknown";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function isTrue(v) {
|
|
16
|
+
return v === "true";
|
|
17
|
+
}
|
|
18
|
+
// Booleans that default ON (OKFFS_*_METADATA/INFER) are "off" only at "false".
|
|
19
|
+
function isOff(v) {
|
|
20
|
+
return v === "false";
|
|
21
|
+
}
|
|
22
|
+
export function buildBannerInfo(values, resolved) {
|
|
23
|
+
const features = [];
|
|
24
|
+
if (isTrue(values.OKFFS_PROJECT_ENABLED))
|
|
25
|
+
features.push("Projects v2 board");
|
|
26
|
+
if (isTrue(values.OKFFS_AUTO_PR))
|
|
27
|
+
features.push("auto draft-PR on new issue");
|
|
28
|
+
if (isTrue(values.OKFFS_AUTO_MERGE_BASE))
|
|
29
|
+
features.push("auto-merge into base");
|
|
30
|
+
if (isTrue(values.OKFFS_UPDATE_DOCS))
|
|
31
|
+
features.push("auto doc updates");
|
|
32
|
+
if (isTrue(values.OKFFS_RESOLVE_THREADS))
|
|
33
|
+
features.push("auto-resolve review threads");
|
|
34
|
+
if (isTrue(values.OKFFS_UPDATE_GUIDANCE))
|
|
35
|
+
features.push("guidance sync");
|
|
36
|
+
if (isTrue(values.OKFFS_CLASSIC_PAT))
|
|
37
|
+
features.push("classic PAT (org Issue Fields)");
|
|
38
|
+
if (isTrue(values.OKFFS_PROMOTION_AUTO_REVIEW))
|
|
39
|
+
features.push("auto-request promotion reviewers");
|
|
40
|
+
if (isOff(values.OKFFS_INFER_PRIORITY) || isOff(values.OKFFS_INFER_EFFORT) || isOff(values.OKFFS_INFER_TYPE)) {
|
|
41
|
+
features.push("some inference disabled");
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
version: packageVersion(),
|
|
45
|
+
owner: resolved.owner,
|
|
46
|
+
repo: resolved.repo,
|
|
47
|
+
tokenSource: resolved.tokenSource,
|
|
48
|
+
baseBranch: values.OKFFS_BASE_BRANCH || "(repo default)",
|
|
49
|
+
protectedBranch: values.OKFFS_PROTECTED_BRANCH || "(none)",
|
|
50
|
+
features,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function renderBanner(info) {
|
|
54
|
+
const tokenLabel = info.tokenSource === "env" ? "GITHUB_TOKEN" : info.tokenSource === "gh" ? "gh CLI" : "none";
|
|
55
|
+
const lines = [
|
|
56
|
+
`okffs v${info.version}`,
|
|
57
|
+
`repo: ${info.owner && info.repo ? `${info.owner}/${info.repo}` : "(unresolved)"}`,
|
|
58
|
+
`token: ${tokenLabel}`,
|
|
59
|
+
`base: ${info.baseBranch}`,
|
|
60
|
+
`protected: ${info.protectedBranch}`,
|
|
61
|
+
`features: ${info.features.length ? info.features.join(", ") : "defaults only"}`,
|
|
62
|
+
];
|
|
63
|
+
return lines.join("\n");
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/cli/banner.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,uEAAuE;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,0EAA0E;QAC1E,yCAAyC;QACzC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAClG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,CAAqB;IACnC,OAAO,CAAC,KAAK,MAAM,CAAC;AACtB,CAAC;AAED,+EAA+E;AAC/E,SAAS,KAAK,CAAC,CAAqB;IAClC,OAAO,CAAC,KAAK,OAAO,CAAC;AACvB,CAAC;AAYD,MAAM,UAAU,eAAe,CAC7B,MAA8B,EAC9B,QAA+E;IAE/E,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC7E,IAAI,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC9E,IAAI,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAChF,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACxE,IAAI,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACvF,IAAI,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IACtF,IAAI,MAAM,CAAC,MAAM,CAAC,2BAA2B,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAClG,IAAI,KAAK,CAAC,MAAM,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC7G,QAAQ,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO;QACL,OAAO,EAAE,cAAc,EAAE;QACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,UAAU,EAAE,MAAM,CAAC,iBAAiB,IAAI,gBAAgB;QACxD,eAAe,EAAE,MAAM,CAAC,sBAAsB,IAAI,QAAQ;QAC1D,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;IAC/G,MAAM,KAAK,GAAG;QACZ,UAAU,IAAI,CAAC,OAAO,EAAE;QACxB,cAAc,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE;QACvF,cAAc,UAAU,EAAE;QAC1B,cAAc,IAAI,CAAC,UAAU,EAAE;QAC/B,cAAc,IAAI,CAAC,eAAe,EAAE;QACpC,cAAc,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,EAAE;KAClF,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface ParsedEnv {
|
|
2
|
+
/** Whether the file existed at all. */
|
|
3
|
+
exists: boolean;
|
|
4
|
+
/** User content before okffs's managed block — preserved verbatim (okffs var lines migrated out). */
|
|
5
|
+
preamble: string;
|
|
6
|
+
/** User content after okffs's managed block — preserved verbatim (okffs var lines migrated out). */
|
|
7
|
+
postamble: string;
|
|
8
|
+
/** okffs var values found anywhere (managed block or migrated from user content), for seeding. */
|
|
9
|
+
values: Record<string, string>;
|
|
10
|
+
/** Every okffs key that appears, set OR as a `# KEY=` placeholder, anywhere in the file. */
|
|
11
|
+
known: Set<string>;
|
|
12
|
+
/** The okffs version stamped into the managed block last time it was written, if any. */
|
|
13
|
+
configuredVersion: string | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseEnv(path: string): ParsedEnv;
|
|
16
|
+
export type EntryState = "set" | "declined";
|
|
17
|
+
export interface Entry {
|
|
18
|
+
state: EntryState;
|
|
19
|
+
value: string;
|
|
20
|
+
}
|
|
21
|
+
/** Keys absent from the map are "never asked" and omitted from the output. */
|
|
22
|
+
export type Collected = Record<string, Entry>;
|
|
23
|
+
/**
|
|
24
|
+
* Assemble the full .env: the preserved user preamble, then okffs's managed
|
|
25
|
+
* block (regenerated from the manifest + collected values), then the preserved
|
|
26
|
+
* user postamble. Only the managed block is okffs's to rewrite.
|
|
27
|
+
*/
|
|
28
|
+
export declare function serializeEnv(collected: Collected, preamble: string, postamble: string, version?: string): string;
|
|
29
|
+
export declare function writeEnv(path: string, contents: string): void;
|
|
30
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/cli/env.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,SAAS;IACxB,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAC;IAChB,qGAAqG;IACrG,QAAQ,EAAE,MAAM,CAAC;IACjB,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;IAClB,kGAAkG;IAClG,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,4FAA4F;IAC5F,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,yFAAyF;IACzF,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAMD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAgDhD;AAkDD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,UAAU,CAAC;AAC5C,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAQ9C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BhH;AAYD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE7D"}
|
package/dist/cli/env.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Reading and (re)writing .env for the setup wizard.
|
|
2
|
+
//
|
|
3
|
+
// okffs owns ONLY a marker-delimited block. Everything OUTSIDE the block — the
|
|
4
|
+
// user's own variables, their comments, their layout — is preserved verbatim
|
|
5
|
+
// across every run. Inside the block okffs regenerates its own variables in a
|
|
6
|
+
// fixed section order.
|
|
7
|
+
//
|
|
8
|
+
// <user content, preserved byte-for-byte>
|
|
9
|
+
// # okffs:env:start ── managed; regenerated each run ──
|
|
10
|
+
// OKFFS_… = …
|
|
11
|
+
// # okffs:env:end
|
|
12
|
+
// <more user content, preserved byte-for-byte>
|
|
13
|
+
//
|
|
14
|
+
// Recognized okffs variables found OUTSIDE the block (e.g. in a hand-written
|
|
15
|
+
// .env being adopted for the first time, or one copied from .env.example) are
|
|
16
|
+
// MIGRATED into the block: their value is read and re-emitted inside the block,
|
|
17
|
+
// and the stray line is removed so there is never a duplicate assignment. Their
|
|
18
|
+
// value is never lost — only relocated to the block okffs manages. Any comment
|
|
19
|
+
// that happened to sit directly above such a line stays where it was.
|
|
20
|
+
//
|
|
21
|
+
// The parser also powers sync mode: a variable counts as "known" whether it is
|
|
22
|
+
// set (`KEY=value`) or written as a declined placeholder (`# KEY=`), which is
|
|
23
|
+
// how sync distinguishes "asked and declined" from "new, never asked".
|
|
24
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
25
|
+
import { SECTIONS, allKeys } from "./manifest.js";
|
|
26
|
+
const KEY_RE = /^([A-Z][A-Z0-9_]*)=(.*)$/;
|
|
27
|
+
const COMMENTED_KEY_RE = /^#\s*([A-Z][A-Z0-9_]*)\s*=/;
|
|
28
|
+
const START_MARKER = "okffs:env:start";
|
|
29
|
+
const END_MARKER = "okffs:env:end";
|
|
30
|
+
// A version stamp okffs writes into the managed block so a later run can tell
|
|
31
|
+
// which okffs version last configured this .env (drives the upgrade nudge).
|
|
32
|
+
const VERSION_STAMP_RE = /okffs:configured-version=(\S+)/;
|
|
33
|
+
export function parseEnv(path) {
|
|
34
|
+
let raw;
|
|
35
|
+
try {
|
|
36
|
+
raw = readFileSync(path, "utf8");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return { exists: false, preamble: "", postamble: "", values: {}, known: new Set(), configuredVersion: null };
|
|
40
|
+
}
|
|
41
|
+
const manifestKeys = new Set(allKeys());
|
|
42
|
+
const lines = raw.split("\n");
|
|
43
|
+
// Locate okffs's managed block by its markers.
|
|
44
|
+
const startIdx = lines.findIndex((l) => l.includes(START_MARKER));
|
|
45
|
+
const endIdx = lines.findIndex((l) => l.includes(END_MARKER));
|
|
46
|
+
let preLines;
|
|
47
|
+
let managedLines;
|
|
48
|
+
let postLines;
|
|
49
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
50
|
+
preLines = lines.slice(0, startIdx);
|
|
51
|
+
managedLines = lines.slice(startIdx + 1, endIdx);
|
|
52
|
+
postLines = lines.slice(endIdx + 1);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// No markers: the whole file is user content. okffs vars in it get migrated.
|
|
56
|
+
preLines = lines;
|
|
57
|
+
managedLines = [];
|
|
58
|
+
postLines = [];
|
|
59
|
+
}
|
|
60
|
+
const values = {};
|
|
61
|
+
const known = new Set();
|
|
62
|
+
// The managed block is discarded and regenerated; just harvest its values and
|
|
63
|
+
// the version stamp.
|
|
64
|
+
let configuredVersion = null;
|
|
65
|
+
for (const line of managedLines) {
|
|
66
|
+
const vm = line.match(VERSION_STAMP_RE);
|
|
67
|
+
if (vm)
|
|
68
|
+
configuredVersion = vm[1];
|
|
69
|
+
harvest(line, manifestKeys, values, known);
|
|
70
|
+
}
|
|
71
|
+
// From user content, migrate okffs var lines out (harvest + drop) and keep
|
|
72
|
+
// everything else — comments, blanks, custom vars, commented custom vars —
|
|
73
|
+
// byte-for-byte.
|
|
74
|
+
const preamble = migrate(preLines, manifestKeys, values, known);
|
|
75
|
+
const postamble = migrate(postLines, manifestKeys, values, known);
|
|
76
|
+
return { exists: true, preamble, postamble, values, known, configuredVersion };
|
|
77
|
+
}
|
|
78
|
+
// Record an okffs var line's key (+ value if set) without keeping the line.
|
|
79
|
+
function harvest(line, manifestKeys, values, known) {
|
|
80
|
+
const cm = line.match(COMMENTED_KEY_RE);
|
|
81
|
+
if (cm && manifestKeys.has(cm[1])) {
|
|
82
|
+
known.add(cm[1]);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
const m = line.match(KEY_RE);
|
|
86
|
+
if (m && manifestKeys.has(m[1])) {
|
|
87
|
+
known.add(m[1]);
|
|
88
|
+
const v = unquote(m[2].trim());
|
|
89
|
+
if (v !== "" && values[m[1]] === undefined)
|
|
90
|
+
values[m[1]] = v; // first occurrence wins (dotenv semantics)
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
// Harvest okffs var lines out of `lines` and return the remaining lines
|
|
96
|
+
// (verbatim) joined back together.
|
|
97
|
+
function migrate(lines, manifestKeys, values, known) {
|
|
98
|
+
const kept = [];
|
|
99
|
+
for (const line of lines) {
|
|
100
|
+
if (harvest(line, manifestKeys, values, known))
|
|
101
|
+
continue; // okffs var — migrated into the block
|
|
102
|
+
kept.push(line); // user content — preserved
|
|
103
|
+
}
|
|
104
|
+
return kept.join("\n");
|
|
105
|
+
}
|
|
106
|
+
function unquote(v) {
|
|
107
|
+
if (v.length >= 2 && ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'")))) {
|
|
108
|
+
return v.slice(1, -1);
|
|
109
|
+
}
|
|
110
|
+
return v;
|
|
111
|
+
}
|
|
112
|
+
// Quote a value only when it needs it (spaces, #, or leading/trailing space),
|
|
113
|
+
// so simple values stay bare and readable.
|
|
114
|
+
function quoteIfNeeded(v) {
|
|
115
|
+
if (v === "")
|
|
116
|
+
return "";
|
|
117
|
+
if (/[\s#'"]/.test(v) || v !== v.trim())
|
|
118
|
+
return JSON.stringify(v);
|
|
119
|
+
return v;
|
|
120
|
+
}
|
|
121
|
+
// The block boundary is a SINGLE decorative line that CONTAINS the marker, so a
|
|
122
|
+
// re-parse slices exactly at it — no stray rule line can leak into the preserved
|
|
123
|
+
// user content and make the file grow on each run (regen must be idempotent).
|
|
124
|
+
const START_LINE = `# ${"═".repeat(22)} ${START_MARKER} ${"═".repeat(22)}`;
|
|
125
|
+
const END_LINE = `# ${"═".repeat(23)} ${END_MARKER} ${"═".repeat(24)}`;
|
|
126
|
+
/**
|
|
127
|
+
* Assemble the full .env: the preserved user preamble, then okffs's managed
|
|
128
|
+
* block (regenerated from the manifest + collected values), then the preserved
|
|
129
|
+
* user postamble. Only the managed block is okffs's to rewrite.
|
|
130
|
+
*/
|
|
131
|
+
export function serializeEnv(collected, preamble, postamble, version) {
|
|
132
|
+
const block = [
|
|
133
|
+
START_LINE,
|
|
134
|
+
"# Managed by `okffs setup` — regenerated on every run; set these via",
|
|
135
|
+
"# `okffs setup`, not by hand. Your own variables and comments OUTSIDE this",
|
|
136
|
+
"# block are left untouched.",
|
|
137
|
+
...(version ? [`# okffs:configured-version=${version}`] : []),
|
|
138
|
+
"",
|
|
139
|
+
];
|
|
140
|
+
for (const section of SECTIONS) {
|
|
141
|
+
const keys = [...(section.gateKey ? [section.gateKey] : []), ...section.vars.map((v) => v.key)];
|
|
142
|
+
if (!keys.some((k) => collected[k]))
|
|
143
|
+
continue; // whole section unasked — skip it
|
|
144
|
+
block.push(`# ${section.title}`);
|
|
145
|
+
if (section.blurb)
|
|
146
|
+
block.push(`# ${section.blurb}`);
|
|
147
|
+
for (const k of keys)
|
|
148
|
+
emitVar(block, k, collected);
|
|
149
|
+
block.push("");
|
|
150
|
+
}
|
|
151
|
+
block.push(END_LINE);
|
|
152
|
+
const pre = preamble.trim() ? preamble.replace(/\s+$/, "") + "\n\n" : "";
|
|
153
|
+
const post = postamble.trim() ? "\n" + postamble.replace(/^\s+/, "") : "";
|
|
154
|
+
const out = pre + block.join("\n") + post;
|
|
155
|
+
return out.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
156
|
+
}
|
|
157
|
+
function emitVar(out, key, collected) {
|
|
158
|
+
const e = collected[key];
|
|
159
|
+
if (!e)
|
|
160
|
+
return; // never asked — omit so a later sync run offers it
|
|
161
|
+
if (e.state === "declined" || e.value === "") {
|
|
162
|
+
out.push(`# ${key}=`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
out.push(`${key}=${quoteIfNeeded(e.value)}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export function writeEnv(path, contents) {
|
|
169
|
+
writeFileSync(path, contents, "utf8");
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/cli/env.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
/** Subcommands that route to the CLI rather than the MCP server. */
|
|
4
|
+
export declare const SUBCOMMANDS: Set<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Whether argv[2..] should be handled as a CLI invocation rather than starting
|
|
7
|
+
* the stdio MCP server. MCP hosts launch the server with no extra args, so a
|
|
8
|
+
* bare invocation always falls through to the server.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isCliInvocation(args: string[]): boolean;
|
|
11
|
+
export declare function runCli(args: string[]): Promise<number>;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AASA,OAAO,eAAe,CAAC;AAMvB,oEAAoE;AACpE,eAAO,MAAM,WAAW,aAA8B,CAAC;AAIvD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAIvD;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B5D"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// okffs command-line entrypoint. Dispatches subcommands (currently `setup`) and
|
|
3
|
+
// is deliberately structured so `login` (Tier 3 OAuth Device Flow) and a future
|
|
4
|
+
// `status` slot in without reworking the dispatcher.
|
|
5
|
+
//
|
|
6
|
+
// Reachable two ways:
|
|
7
|
+
// npx @neturely/okffs setup → the package's single `okffs` bin is dist/index.js
|
|
8
|
+
// (the MCP server), which forwards CLI argv here.
|
|
9
|
+
// node dist/cli/index.js setup → this file run directly (local dev).
|
|
10
|
+
import "dotenv/config";
|
|
11
|
+
import { pathToFileURL } from "node:url";
|
|
12
|
+
import { runSetup } from "./setup.js";
|
|
13
|
+
import { packageVersion } from "./banner.js";
|
|
14
|
+
/** Subcommands that route to the CLI rather than the MCP server. */
|
|
15
|
+
export const SUBCOMMANDS = new Set(["setup", "login"]);
|
|
16
|
+
const HELP_FLAGS = new Set(["help", "--help", "-h"]);
|
|
17
|
+
const VERSION_FLAGS = new Set(["--version", "-v"]);
|
|
18
|
+
/**
|
|
19
|
+
* Whether argv[2..] should be handled as a CLI invocation rather than starting
|
|
20
|
+
* the stdio MCP server. MCP hosts launch the server with no extra args, so a
|
|
21
|
+
* bare invocation always falls through to the server.
|
|
22
|
+
*/
|
|
23
|
+
export function isCliInvocation(args) {
|
|
24
|
+
const first = args[0];
|
|
25
|
+
if (!first)
|
|
26
|
+
return false;
|
|
27
|
+
return SUBCOMMANDS.has(first) || HELP_FLAGS.has(first) || VERSION_FLAGS.has(first);
|
|
28
|
+
}
|
|
29
|
+
export async function runCli(args) {
|
|
30
|
+
const [sub, ...rest] = args;
|
|
31
|
+
if (VERSION_FLAGS.has(sub)) {
|
|
32
|
+
console.log(packageVersion());
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
if (!sub || HELP_FLAGS.has(sub)) {
|
|
36
|
+
printHelp();
|
|
37
|
+
return sub ? 0 : 1;
|
|
38
|
+
}
|
|
39
|
+
switch (sub) {
|
|
40
|
+
case "setup":
|
|
41
|
+
return runSetup(rest);
|
|
42
|
+
case "login":
|
|
43
|
+
console.error("`okffs login` (OAuth Device Flow) isn't implemented yet.\n" +
|
|
44
|
+
"For now, configure a GITHUB_TOKEN (or sign in with `gh auth login`) via `okffs setup`.");
|
|
45
|
+
return 1;
|
|
46
|
+
default:
|
|
47
|
+
console.error(`Unknown command: ${sub}\n`);
|
|
48
|
+
printHelp();
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function printHelp() {
|
|
53
|
+
console.log([
|
|
54
|
+
`okffs v${packageVersion()} — GitHub issue → branch → PR → merge workflow for Claude Code`,
|
|
55
|
+
"",
|
|
56
|
+
"Usage:",
|
|
57
|
+
" npx @neturely/okffs <command>",
|
|
58
|
+
"",
|
|
59
|
+
"Commands:",
|
|
60
|
+
" setup Interactive wizard to create/update .env (run in a repo)",
|
|
61
|
+
" login (coming soon) OAuth Device Flow sign-in",
|
|
62
|
+
"",
|
|
63
|
+
"Flags:",
|
|
64
|
+
" -h, --help Show this help",
|
|
65
|
+
" -v, --version Print the okffs version",
|
|
66
|
+
"",
|
|
67
|
+
"With no command, okffs runs as an MCP server over stdio (how Claude Code launches it).",
|
|
68
|
+
].join("\n"));
|
|
69
|
+
}
|
|
70
|
+
// Run directly: `node dist/cli/index.js <args>`.
|
|
71
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
72
|
+
runCli(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
|
|
73
|
+
console.error(err instanceof Error ? err.message : err);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type VarKind = "text" | "boolean" | "secret" | "select";
|
|
2
|
+
export interface VarSpec {
|
|
3
|
+
key: string;
|
|
4
|
+
kind: VarKind;
|
|
5
|
+
/** One-line help shown at the prompt. */
|
|
6
|
+
description: string;
|
|
7
|
+
/** Default value (as it would appear in .env). Empty string = unset. */
|
|
8
|
+
default: string;
|
|
9
|
+
/** Allowed values for kind: "select". */
|
|
10
|
+
options?: string[];
|
|
11
|
+
/** Example value shown as placeholder for free-text vars. */
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface Section {
|
|
15
|
+
id: string;
|
|
16
|
+
/** Header comment written into the generated .env. */
|
|
17
|
+
title: string;
|
|
18
|
+
/** Optional explanatory line under the header. */
|
|
19
|
+
blurb?: string;
|
|
20
|
+
vars: VarSpec[];
|
|
21
|
+
/**
|
|
22
|
+
* A gated section asks a single yes/no question first; declining it skips the
|
|
23
|
+
* whole group (its vars are written as commented placeholders). Auth & repo is
|
|
24
|
+
* the only ungated section — it is always asked.
|
|
25
|
+
*/
|
|
26
|
+
gated: boolean;
|
|
27
|
+
/** Prompt shown for the gate question. */
|
|
28
|
+
gatePrompt?: string;
|
|
29
|
+
/**
|
|
30
|
+
* For a section whose gate maps onto a real boolean env var (Projects), the
|
|
31
|
+
* gate answer is persisted to this key (true/false) rather than just toggling
|
|
32
|
+
* whether the sub-vars are asked.
|
|
33
|
+
*/
|
|
34
|
+
gateKey?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Only offer this section when the predicate holds against values collected so
|
|
37
|
+
* far (e.g. promotion is only relevant once a protected branch is set).
|
|
38
|
+
*/
|
|
39
|
+
onlyIf?: (values: Record<string, string>) => boolean;
|
|
40
|
+
}
|
|
41
|
+
export declare const SECTIONS: Section[];
|
|
42
|
+
/** Every var key the manifest knows about, in section order. */
|
|
43
|
+
export declare function allKeys(): string[];
|
|
44
|
+
/** Look up a var spec by key across all sections. */
|
|
45
|
+
export declare function findVar(key: string): VarSpec | undefined;
|
|
46
|
+
/** Keys that make up the Quick-setup subset (auth & repo + base branch). */
|
|
47
|
+
export declare const QUICK_KEYS: string[];
|
|
48
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/cli/manifest.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/D,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB;;;;OAIG;IACH,KAAK,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC;CACtD;AAMD,eAAO,MAAM,QAAQ,EAAE,OAAO,EAoR7B,CAAC;AAEF,gEAAgE;AAChE,wBAAgB,OAAO,IAAI,MAAM,EAAE,CAMlC;AAED,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAMxD;AAED,4EAA4E;AAC5E,eAAO,MAAM,UAAU,UAAuE,CAAC"}
|