@neturely/okffs 0.8.0 → 0.10.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +27 -2
  3. package/dist/autopilot.d.ts +16 -0
  4. package/dist/autopilot.d.ts.map +1 -0
  5. package/dist/autopilot.js +42 -0
  6. package/dist/autopilot.js.map +1 -0
  7. package/dist/cli/banner.d.ts +17 -0
  8. package/dist/cli/banner.d.ts.map +1 -0
  9. package/dist/cli/banner.js +65 -0
  10. package/dist/cli/banner.js.map +1 -0
  11. package/dist/cli/env.d.ts +30 -0
  12. package/dist/cli/env.d.ts.map +1 -0
  13. package/dist/cli/env.js +171 -0
  14. package/dist/cli/env.js.map +1 -0
  15. package/dist/cli/index.d.ts +12 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +77 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/cli/manifest.d.ts +48 -0
  20. package/dist/cli/manifest.d.ts.map +1 -0
  21. package/dist/cli/manifest.js +312 -0
  22. package/dist/cli/manifest.js.map +1 -0
  23. package/dist/cli/probe.d.ts +32 -0
  24. package/dist/cli/probe.d.ts.map +1 -0
  25. package/dist/cli/probe.js +101 -0
  26. package/dist/cli/probe.js.map +1 -0
  27. package/dist/cli/sanity.d.ts +16 -0
  28. package/dist/cli/sanity.d.ts.map +1 -0
  29. package/dist/cli/sanity.js +99 -0
  30. package/dist/cli/sanity.js.map +1 -0
  31. package/dist/cli/setup.d.ts +2 -0
  32. package/dist/cli/setup.d.ts.map +1 -0
  33. package/dist/cli/setup.js +254 -0
  34. package/dist/cli/setup.js.map +1 -0
  35. package/dist/config.d.ts +1 -0
  36. package/dist/config.d.ts.map +1 -1
  37. package/dist/config.js +13 -0
  38. package/dist/config.js.map +1 -1
  39. package/dist/github.d.ts +1 -0
  40. package/dist/github.d.ts.map +1 -1
  41. package/dist/github.js +53 -6
  42. package/dist/github.js.map +1 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +19 -107
  45. package/dist/index.js.map +1 -1
  46. package/dist/prompts/setup.d.ts +18 -0
  47. package/dist/prompts/setup.d.ts.map +1 -0
  48. package/dist/prompts/setup.js +77 -0
  49. package/dist/prompts/setup.js.map +1 -0
  50. package/dist/server.d.ts +2 -0
  51. package/dist/server.d.ts.map +1 -0
  52. package/dist/server.js +183 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/tools/commit_and_update.d.ts +3 -0
  55. package/dist/tools/commit_and_update.d.ts.map +1 -1
  56. package/dist/tools/commit_and_update.js +6 -0
  57. package/dist/tools/commit_and_update.js.map +1 -1
  58. package/dist/tools/configure.d.ts +27 -0
  59. package/dist/tools/configure.d.ts.map +1 -0
  60. package/dist/tools/configure.js +91 -0
  61. package/dist/tools/configure.js.map +1 -0
  62. package/dist/tools/create_issue.d.ts.map +1 -1
  63. package/dist/tools/create_issue.js +24 -4
  64. package/dist/tools/create_issue.js.map +1 -1
  65. package/dist/tools/create_pull_request.d.ts +3 -0
  66. package/dist/tools/create_pull_request.d.ts.map +1 -1
  67. package/dist/tools/create_pull_request.js +9 -0
  68. package/dist/tools/create_pull_request.js.map +1 -1
  69. package/dist/tools/plan.d.ts.map +1 -1
  70. package/dist/tools/plan.js +11 -2
  71. package/dist/tools/plan.js.map +1 -1
  72. package/dist/tools/update_project_status.d.ts +2 -2
  73. package/package.json +6 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ See [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.10.0] - 2026-07-14
9
+ ### Added
10
+ - Autopilot / "minimum interference" mode — autonomous issue → develop-PR with a decisions report ([#238](https://github.com/neturely/okffs/issues/238))
11
+ ### Fixed
12
+ - Silent auto-PR failure in create_issue - swallowed to stderr, retry on 422 ([#247](https://github.com/neturely/okffs/issues/247))
13
+
14
+ ## [0.9.0] - 2026-07-11
15
+ ### Changed
16
+ - commit_and_update: empty commit subject from whitespace-only or leading-blank-line hint ([#236](https://github.com/neturely/okffs/issues/236))
17
+ ### Added
18
+ - `/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))
19
+ - `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))
20
+
8
21
  ## [0.8.0] - 2026-07-08
9
22
  ### Added
10
23
  - 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))
@@ -144,7 +157,9 @@ See [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
144
157
  - `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)).
145
158
  - 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.
146
159
 
147
- [Unreleased]: https://github.com/neturely/okffs/compare/v0.8.0...HEAD
160
+ [Unreleased]: https://github.com/neturely/okffs/compare/v0.10.0...HEAD
161
+ [0.10.0]: https://github.com/neturely/okffs/compare/v0.9.0...v0.10.0
162
+ [0.9.0]: https://github.com/neturely/okffs/compare/v0.8.0...v0.9.0
148
163
  [0.8.0]: https://github.com/neturely/okffs/compare/v0.7.0...v0.8.0
149
164
  [0.7.0]: https://github.com/neturely/okffs/compare/v0.6.0...v0.7.0
150
165
  [0.6.0]: https://github.com/neturely/okffs/compare/v0.5.2...v0.6.0
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
- Add a `.mcp.json` to your project root:
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. If you're signed in with the [GitHub CLI](https://cli.github.com/) (`gh auth login`) and working inside the repo you want to manage, **no other setup is needed**. To use a token or a different repo instead, see [Configuration](#configuration).
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
 
@@ -75,6 +86,12 @@ okffs has a built-in loop for review feedback — just ask (*"address the review
75
86
 
76
87
  By default review threads are left open for you to resolve; set `OKFFS_RESOLVE_THREADS=true` to have okffs resolve them once addressed.
77
88
 
89
+ ## Autopilot (minimum interference)
90
+
91
+ For a hands-off session, ask Claude to *"fully handle this"* (or *"minimum interference"*) — or set `OKFFS_AUTOPILOT=true` to make it the default. In autopilot, Claude stops asking you to choose between options for **reversible** decisions, takes the recommended option at each fork, and drives an issue all the way to a PR into the base branch (and a base merge when `OKFFS_AUTO_MERGE_BASE=true`) — then posts an **"Autopilot decisions"** report (one line per choice, with a one-line why) to the PR and the issue, so you can redirect anything in a single message.
92
+
93
+ It removes confirmation friction; it does **not** grant new powers. The hard stops always interrupt, even in autopilot: anything into `OKFFS_PROTECTED_BRANCH` (merge/tag/publish), destructive tools (`delete_issue` / `delete_branch`), anything billable (e.g. a Copilot review on a new promotion PR), and genuinely irreversible actions. And for a real *missing-information* call — where only you hold the context — Claude still asks one quick question rather than guess. Off by default.
94
+
78
95
  ## Keeping CLAUDE.md in sync
79
96
 
80
97
  The **`/okffs:update_guidance`** slash command reviews the changes on the current branch and maintains one okffs-owned section of your `CLAUDE.md` — `## Project Guidance (okffs usage)`, delimited by HTML markers — without ever touching your hand-written content. It's guidance curation (tools, env vars, conventions), not a changelog. Set `OKFFS_UPDATE_GUIDANCE=true` to have `create_pull_request` nudge Claude to run it at PR time; the command is always available.
@@ -107,6 +124,8 @@ Once enabled, `list_issues` shows each issue's column, priority, and effort (ord
107
124
 
108
125
  ## Configuration
109
126
 
127
+ 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.
128
+
110
129
  okffs resolves a **token** and a **target repository** with fallbacks, so most users need little config.
111
130
 
112
131
  **Token** (first match wins):
@@ -185,6 +204,12 @@ All optional; unset unless noted. Grouped by concern — the same groups appear
185
204
  | `OKFFS_PROMOTION_REVIEWERS` | — | Comma-separated reviewers to request on the gate PR (e.g. `copilot-pull-request-reviewer[bot]`). Only acted on when `OKFFS_PROMOTION_AUTO_REVIEW=true`. |
186
205
  | `OKFFS_PROMOTION_AUTO_REVIEW` | `false` | Opt in to auto-request those reviewers, **on gate-PR creation only** (never on re-runs). **⚠️ Cost:** Copilot code review is billable, so this charges per newly-created promotion PR. |
187
206
 
207
+ **Autopilot (minimum interference)**
208
+
209
+ | Variable | Default | Purpose |
210
+ |----------|---------|---------|
211
+ | `OKFFS_AUTOPILOT` | `false` | Default the session to [autopilot](#autopilot-minimum-interference): take the recommended option at each reversible fork, drive to a base-branch PR, and report the decisions. Hard stops (protected branch, destructive, billable, irreversible) still interrupt. Per-request activation (*"fully handle this"*) works regardless. |
212
+
188
213
  ## Conventions
189
214
 
190
215
  - **Branches:** `{issue-number}-{kebab-title-slug}` (title truncated to ~5 words), e.g. `42-add-hero-section-to-homepage`.
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Whether minimum-interference mode is on by env default (OKFFS_AUTOPILOT=true).
3
+ * Per-request activation ("fully handle this") is behavioural and not visible
4
+ * here — this only reflects the persistent default.
5
+ */
6
+ export declare function autopilotEnabled(): boolean;
7
+ /**
8
+ * Render the "Autopilot decisions" report block — one line per fork, each already
9
+ * phrased by the agent as *what was chosen* + *one-line why*. Returns null when
10
+ * there is nothing to report so callers omit the section cleanly rather than
11
+ * emitting an empty heading. Blank/whitespace-only entries are dropped. #238.
12
+ */
13
+ export declare function renderAutopilotDecisions(decisions?: string[] | null): string | null;
14
+ /** Shared schema description for the `autopilot_decisions` tool parameter. */
15
+ export declare const AUTOPILOT_DECISIONS_DESCRIPTION: string;
16
+ //# sourceMappingURL=autopilot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autopilot.d.ts","sourceRoot":"","sources":["../src/autopilot.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAQnF;AAED,8EAA8E;AAC9E,eAAO,MAAM,+BAA+B,QAKmC,CAAC"}
@@ -0,0 +1,42 @@
1
+ // Autopilot ("minimum interference") — #238.
2
+ //
3
+ // The mode itself is behavioural: okffs can't suppress the host LLM's questions,
4
+ // so the load-bearing piece is the always-on guidance in SERVER_INSTRUCTIONS
5
+ // (src/server.ts), which tells the agent to take the recommended option at each
6
+ // reversible fork, honour the hard stops, and report the decisions. This module
7
+ // is the small amount of *plumbing* okffs owns: knowing whether the env default
8
+ // is on, and rendering the decisions report into a PR body / issue comment in a
9
+ // consistent shape (the host LLM supplies the content — the decisions — the same
10
+ // way it supplies triage/fixes for address_pr_review).
11
+ import { config } from "./config.js";
12
+ /**
13
+ * Whether minimum-interference mode is on by env default (OKFFS_AUTOPILOT=true).
14
+ * Per-request activation ("fully handle this") is behavioural and not visible
15
+ * here — this only reflects the persistent default.
16
+ */
17
+ export function autopilotEnabled() {
18
+ return config.autopilot;
19
+ }
20
+ /**
21
+ * Render the "Autopilot decisions" report block — one line per fork, each already
22
+ * phrased by the agent as *what was chosen* + *one-line why*. Returns null when
23
+ * there is nothing to report so callers omit the section cleanly rather than
24
+ * emitting an empty heading. Blank/whitespace-only entries are dropped. #238.
25
+ */
26
+ export function renderAutopilotDecisions(decisions) {
27
+ // Strip a leading bullet marker (-, *, •) the caller may have already added, so
28
+ // "- chose X" and "chose X" both render as a single bullet rather than "- - …".
29
+ const items = (decisions ?? [])
30
+ .map((d) => d.trim().replace(/^[-*•]\s+/, "").trim())
31
+ .filter(Boolean);
32
+ if (items.length === 0)
33
+ return null;
34
+ return ["## 🤖 Autopilot decisions", ...items.map((d) => `- ${d}`)].join("\n");
35
+ }
36
+ /** Shared schema description for the `autopilot_decisions` tool parameter. */
37
+ export const AUTOPILOT_DECISIONS_DESCRIPTION = "Autopilot decisions report — when driving this issue in minimum-interference " +
38
+ "(autopilot) mode, pass one entry per reversible fork you decided without asking, " +
39
+ "each phrased as 'what you chose — one-line why'. okffs renders them as an " +
40
+ "'Autopilot decisions' block in the PR body and the issue comment so the user has " +
41
+ "an audit trail to redirect in a single message. Omit when not in autopilot.";
42
+ //# sourceMappingURL=autopilot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autopilot.js","sourceRoot":"","sources":["../src/autopilot.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,iFAAiF;AACjF,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAChF,iFAAiF;AACjF,uDAAuD;AAEvD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,MAAM,CAAC,SAAS,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAA2B;IAClE,gFAAgF;IAChF,gFAAgF;IAChF,MAAM,KAAK,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;SAC5B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;SACpD,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,CAAC,2BAA2B,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjF,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,+BAA+B,GAC1C,+EAA+E;IAC/E,mFAAmF;IACnF,4EAA4E;IAC5E,mFAAmF;IACnF,6EAA6E,CAAC"}
@@ -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":"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"}
@@ -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"}
@@ -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