@brainervirus/workit-core 0.5.6 → 0.6.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 +4 -17
- package/package.json +1 -1
- package/scripts/lib/config-dir.sh +26 -0
- package/scripts/sync-runtime.sh +4 -1
- package/src/core/config.ts +50 -3
- package/src/core/docs-repo.ts +2 -2
- package/src/core/vcs-config.ts +3 -4
- package/src/core/youtrack.ts +3 -3
- package/src/tools/youtrack.ts +7 -3
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Multi-platform Superpowers workflow plugin for **Cursor** and **OpenCode**: veri
|
|
|
9
9
|
| **Shared core** | `packages/workit-core/` (src, skills, commands, scripts, templates) |  |
|
|
10
10
|
| **CLI** | `packages/workit-cli/` (Ink wizard, bin `workit`) |  |
|
|
11
11
|
|
|
12
|
-
Config directory (both platforms): `~/.config/workflow-toolkit/`
|
|
12
|
+
Config directory (both platforms): `~/.config/workit/` — legacy `~/.config/workflow-toolkit/` is auto-migrated on first run and kept as a fallback.
|
|
13
13
|
|
|
14
14
|
[](https://www.npmjs.com/package/@brainervirus/workit-opencode)
|
|
15
15
|
[](https://github.com/BrainerVirus/workit/actions)
|
|
@@ -119,7 +119,6 @@ GitHub Actions:
|
|
|
119
119
|
|
|
120
120
|
- **CI** — on push/PR to `main`: matrix of 3 OS (ubuntu, macos, windows) running `actions/checkout@v7` + `oven-sh/setup-bun@v2`, then `bun install --frozen-lockfile` + `bun run check`
|
|
121
121
|
- **Release** — on push to `main`: [semantic-release](https://github.com/semantic-release/semantic-release) computes the next version from Conventional Commits, publishes the four workspaces to npm in dependency order — `workit-core`, `workit-opencode`, `workit-cursor`, `workit-cli` (with provenance), and creates the git tag + GitHub Release
|
|
122
|
-
- **Code review** — on every PR: the [OpenCode GitHub integration](https://opencode.ai/docs/github/) reviews the diff (see [Code review](#code-review))
|
|
123
122
|
|
|
124
123
|
No manual tags — semantic-release owns the version/tag flow:
|
|
125
124
|
|
|
@@ -127,22 +126,10 @@ No manual tags — semantic-release owns the version/tag flow:
|
|
|
127
126
|
git push origin main
|
|
128
127
|
```
|
|
129
128
|
|
|
130
|
-
##
|
|
129
|
+
## Quality gates
|
|
131
130
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
Checks fall into two categories:
|
|
135
|
-
|
|
136
|
-
- **BLOCKING — the `pull_request` review** (`review` job): runs on every PR (opened/synchronized/reopened/ready_for_review) and is registered as a required check in branch protection — the PR cannot merge while it's red.
|
|
137
|
-
- **ADVISORY — on-demand and triage flows** (never block merge): mention `/oc` or `/opencode` in a comment — issue, PR thread, or a specific line in the Files tab — to have OpenCode fix, explain, or update the PR (`oc` job); new issues are triaged with guidance and doc links (`triage` job).
|
|
138
|
-
|
|
139
|
-
To activate the review on a fork or fresh clone:
|
|
140
|
-
|
|
141
|
-
1. Install the [OpenCode GitHub app](https://github.com/apps/opencode-agent) on the repository.
|
|
142
|
-
2. Add the `OPENCODE_API_KEY` secret under Settings → Secrets and variables → Actions.
|
|
143
|
-
3. PRs trigger the review on opened/synchronized/reopened events automatically.
|
|
144
|
-
|
|
145
|
-
[Cursor Bugbot](https://cursor.com/dashboard/bugbot) is an optional alternative to the OpenCode review.
|
|
131
|
+
- **CI checks** — the per-package check jobs (workit-core/opencode/cursor/cli/shared) are required in branch protection; the PR cannot merge while any is red.
|
|
132
|
+
- **Subagent review** — `wk-implement` runs a two-stage review (spec compliance + code quality) per task with fresh `general` agents.
|
|
146
133
|
|
|
147
134
|
## Architecture
|
|
148
135
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Resolve the config dir (workit default) with one-time legacy migration.
|
|
3
|
+
# Mirrors src/core/config.ts resolveConfigDir + ensureConfigDir.
|
|
4
|
+
# Env overrides bypass migration (explicit user intent wins); migration is
|
|
5
|
+
# idempotent: only runs when workit/ is absent; never overwrites, never
|
|
6
|
+
# deletes the legacy workflow-toolkit/ dir.
|
|
7
|
+
resolve_config_dir() {
|
|
8
|
+
if [ -n "${WORKFLOW_TOOLKIT_CONFIG:-}" ]; then
|
|
9
|
+
printf '%s\n' "$WORKFLOW_TOOLKIT_CONFIG"
|
|
10
|
+
return
|
|
11
|
+
fi
|
|
12
|
+
if [ -n "${WORKFLOW_TOOLKIT_CONFIG_DIR:-}" ]; then
|
|
13
|
+
printf '%s\n' "$WORKFLOW_TOOLKIT_CONFIG_DIR"
|
|
14
|
+
return
|
|
15
|
+
fi
|
|
16
|
+
local base="${XDG_CONFIG_HOME:-$HOME/.config}"
|
|
17
|
+
local NEW="$base/workit"
|
|
18
|
+
local LEGACY="$base/workflow-toolkit"
|
|
19
|
+
if [ ! -d "$NEW" ] && [ -d "$LEGACY" ]; then
|
|
20
|
+
mkdir -p "$NEW"
|
|
21
|
+
if ! cp -r "$LEGACY/." "$NEW/"; then
|
|
22
|
+
printf 'workit: warning: partial config migration from %s (kept in place; retry on next run)\n' "$LEGACY" >&2
|
|
23
|
+
fi
|
|
24
|
+
fi
|
|
25
|
+
printf '%s\n' "$NEW"
|
|
26
|
+
}
|
package/scripts/sync-runtime.sh
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
# Prefer local monorepo (dev) when present; otherwise git-pull the share clone.
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
|
+
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
7
|
+
. "$SCRIPT_DIR/lib/config-dir.sh"
|
|
8
|
+
|
|
6
9
|
SHARE="${HOME}/.local/share/workflow-toolkit"
|
|
7
10
|
PLUGIN_DIR="${HOME}/.cursor/plugins/local/workflow-toolkit"
|
|
8
11
|
OPENCODE_PLUGINS="${HOME}/.config/opencode/plugins"
|
|
@@ -54,7 +57,7 @@ if [ -d "$SHARE/packages/workit-core/vendor/superpowers/skills" ]; then
|
|
|
54
57
|
rsync -a --delete "$SHARE/packages/workit-core/vendor/superpowers/skills" "$PLUGIN_DIR/vendor/superpowers/"
|
|
55
58
|
fi
|
|
56
59
|
# Canonical user rules -> Cursor .mdc (compiled by the shared core)
|
|
57
|
-
CONFIG_RULES_DIR="$
|
|
60
|
+
CONFIG_RULES_DIR="$(resolve_config_dir)/rules"
|
|
58
61
|
if [ -d "$CONFIG_RULES_DIR" ]; then
|
|
59
62
|
"$HOME/.bun/bin/bun" -e "
|
|
60
63
|
import('${SHARE}/packages/workit-core/src/core/rules.ts').then(async ({ writeCompiledCursorRules }) => {
|
package/src/core/config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
@@ -18,10 +18,57 @@ export const PRESETS: Record<BranchPreset, { allowed: string[]; protected: strin
|
|
|
18
18
|
custom: { allowed: [], protected: [] },
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
export const
|
|
21
|
+
export const resolveConfigDir = (): string =>
|
|
22
22
|
process.env.WORKFLOW_TOOLKIT_CONFIG
|
|
23
23
|
?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR
|
|
24
|
-
?? path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "
|
|
24
|
+
?? path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "workit");
|
|
25
|
+
|
|
26
|
+
// One-time lazy migration from the legacy ~/.config/workflow-toolkit dir.
|
|
27
|
+
// migratedDir remembers the resolved dir already checked: configDir() is on
|
|
28
|
+
// hot paths, so subsequent calls are one string compare. Re-checking per
|
|
29
|
+
// unique dir also keeps tests with swapped env working.
|
|
30
|
+
// ponytail: cache keyed by dir value — an env override explicitly set to the
|
|
31
|
+
// default path caches before migration could trigger; only matters if that
|
|
32
|
+
// env is cleared mid-process (next unique dir value re-checks).
|
|
33
|
+
let migratedDir: string | null = null;
|
|
34
|
+
// A mid-loop copy failure leaves the new dir half-populated; keep retrying
|
|
35
|
+
// until a full pass succeeds instead of silently skipping the failed entry.
|
|
36
|
+
let migrationFailed = false;
|
|
37
|
+
|
|
38
|
+
export const ensureConfigDir = (dir: string = resolveConfigDir()): string => {
|
|
39
|
+
if (migratedDir === dir) return dir;
|
|
40
|
+
if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
|
|
41
|
+
migratedDir = dir;
|
|
42
|
+
return dir;
|
|
43
|
+
}
|
|
44
|
+
const legacy = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "workflow-toolkit");
|
|
45
|
+
if (!existsSync(legacy)) {
|
|
46
|
+
migratedDir = dir;
|
|
47
|
+
return dir;
|
|
48
|
+
}
|
|
49
|
+
if (!migrationFailed && existsSync(dir)) {
|
|
50
|
+
migratedDir = dir;
|
|
51
|
+
return dir;
|
|
52
|
+
}
|
|
53
|
+
migrationFailed = false;
|
|
54
|
+
mkdirSync(dir, { recursive: true });
|
|
55
|
+
for (const entry of readdirSync(legacy, { withFileTypes: true })) {
|
|
56
|
+
const src = path.join(legacy, entry.name);
|
|
57
|
+
const dest = path.join(dir, entry.name);
|
|
58
|
+
if (existsSync(dest)) continue;
|
|
59
|
+
try {
|
|
60
|
+
if (entry.isDirectory()) cpSync(src, dest, { recursive: true });
|
|
61
|
+
else if (entry.isFile()) copyFileSync(src, dest);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
migrationFailed = true;
|
|
64
|
+
console.warn(`[workit] config migration: failed to copy ${src}: ${(err as Error).message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!migrationFailed) migratedDir = dir;
|
|
68
|
+
return dir;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const configDir = (): string => ensureConfigDir();
|
|
25
72
|
|
|
26
73
|
export const LOCALE_RE = /^[a-z]{2,3}(-[A-Z]{2})?$/;
|
|
27
74
|
|
package/src/core/docs-repo.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
|
-
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import { configDir } from "./config";
|
|
5
5
|
|
|
6
6
|
const configPath = () =>
|
|
7
7
|
process.env.WORKFLOW_DOCS_REPO_CONFIG
|
|
8
|
-
?? path.join(
|
|
8
|
+
?? path.join(configDir(), "docs-repo.json");
|
|
9
9
|
|
|
10
10
|
export const readDocsRepoConfig = (): { path: string } | null => {
|
|
11
11
|
try {
|
package/src/core/vcs-config.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { spawnSync } from "node:child_process";
|
|
5
4
|
import { configDir } from "./config";
|
|
@@ -69,7 +68,7 @@ export function vcsConfig(mode: "load" | "summary" | "resolve", cwd?: string): R
|
|
|
69
68
|
if (!cfgOk) return { ok: false, error: "missing or invalid vcs.json" };
|
|
70
69
|
|
|
71
70
|
const prov = (cfg[provider] ?? {}) as Record<string, any>;
|
|
72
|
-
const tokenFile = String(prov.tokenFile ?? path.join(
|
|
71
|
+
const tokenFile = String(prov.tokenFile ?? path.join(configDir(), `${provider}.token`));
|
|
73
72
|
const tokenPath = path.resolve(tokenFile);
|
|
74
73
|
let tokenOk = false;
|
|
75
74
|
if (fs.existsSync(tokenPath)) {
|
|
@@ -170,13 +169,13 @@ export function vcsTokenCreateUrls(): Record<string, any> {
|
|
|
170
169
|
const provider = String(cfg.provider ?? "gitlab").toLowerCase();
|
|
171
170
|
const active = {
|
|
172
171
|
gitlab: {
|
|
173
|
-
tokenFile: gitlab.tokenFile ?? path.join(
|
|
172
|
+
tokenFile: gitlab.tokenFile ?? path.join(configDir(), "gitlab.token"),
|
|
174
173
|
createUrl: gitlabUrl,
|
|
175
174
|
scopes: gitlabScopes,
|
|
176
175
|
name,
|
|
177
176
|
},
|
|
178
177
|
github: {
|
|
179
|
-
tokenFile: (cfg.github as Record<string, any>)?.tokenFile ?? path.join(
|
|
178
|
+
tokenFile: (cfg.github as Record<string, any>)?.tokenFile ?? path.join(configDir(), "github.token"),
|
|
180
179
|
createUrl: githubFineUrl,
|
|
181
180
|
createUrlClassic: githubClassicUrl,
|
|
182
181
|
permissions: githubPerms,
|
package/src/core/youtrack.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { spawnSync } from "node:child_process";
|
|
5
4
|
import { readTemplate } from "./templates";
|
|
6
5
|
import { resolveWorkspaceRoot } from "./scripts";
|
|
6
|
+
import { configDir } from "./config";
|
|
7
7
|
|
|
8
8
|
const ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
9
9
|
const TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
10
10
|
|
|
11
11
|
// Port of scripts/youtrack/config.sh chain: WORKFLOW_YOUTRACK_CONFIG ->
|
|
12
|
-
// XDG_CONFIG_HOME / HOME .config +
|
|
12
|
+
// configDir() (XDG_CONFIG_HOME / HOME .config + workit).
|
|
13
13
|
export const youTrackConfigPath = (): string =>
|
|
14
14
|
process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
15
|
-
path.join(
|
|
15
|
+
path.join(configDir(), "youtrack.json");
|
|
16
16
|
|
|
17
17
|
const youTrackTokenModeOk = (p: string): boolean => {
|
|
18
18
|
if (process.platform === "win32") return true;
|
package/src/tools/youtrack.ts
CHANGED
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { tool, type ToolContext } from "@opencode-ai/plugin";
|
|
5
5
|
import { fail, ok, resolveInside, type Result } from "../core";
|
|
6
6
|
import { configGuardError, describeConfigGaps } from "../core/config-guard";
|
|
7
|
+
import { configDir } from "../core/config";
|
|
7
8
|
import {
|
|
8
9
|
buildDraft as legacyBuildDraft,
|
|
9
10
|
context as legacyContext,
|
|
@@ -20,11 +21,14 @@ const message = (error: unknown) => error instanceof Error ? error.message : Str
|
|
|
20
21
|
|
|
21
22
|
// Both override names point at the config dir itself, same precedence as
|
|
22
23
|
// src/core/config.ts and scripts/init/status.sh: WORKFLOW_TOOLKIT_CONFIG → WORKFLOW_TOOLKIT_CONFIG_DIR → XDG.
|
|
24
|
+
// Default env (no args) routes through configDir() so the legacy migration runs.
|
|
23
25
|
export const configPath = (env: NodeJS.ProcessEnv = process.env, home = os.homedir()) =>
|
|
24
26
|
path.join(
|
|
25
|
-
env.
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
env === process.env
|
|
28
|
+
? configDir()
|
|
29
|
+
: env.WORKFLOW_TOOLKIT_CONFIG
|
|
30
|
+
?? env.WORKFLOW_TOOLKIT_CONFIG_DIR
|
|
31
|
+
?? path.join(env.XDG_CONFIG_HOME || path.join(home, ".config"), "workit"),
|
|
28
32
|
"youtrack.json",
|
|
29
33
|
);
|
|
30
34
|
|