@ui8kit/uxdx 0.1.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/LICENSE +21 -0
- package/README.md +38 -0
- package/dist/uxdx.mjs +296 -0
- package/package.json +53 -0
- package/packages/sdlc/harness/README.md +58 -0
- package/packages/sdlc/harness/core/AGENTS.md +12 -0
- package/packages/sdlc/harness/core/README.md +15 -0
- package/packages/sdlc/harness/core/REVIEW.md +18 -0
- package/packages/sdlc/harness/core/changes/README.md +4 -0
- package/packages/sdlc/harness/core/cursor/rules/sdlc-core.mdc +15 -0
- package/packages/sdlc/harness/core/install.sh +3 -0
- package/packages/sdlc/harness/core/intent.md +25 -0
- package/packages/sdlc/harness/core/manifest.yaml +15 -0
- package/packages/sdlc/harness/core/plan.md +25 -0
- package/packages/sdlc/harness/core/sdlc.sh +230 -0
- package/packages/sdlc/harness/core/skills/sdlc-invariants/SKILL.md +20 -0
- package/packages/sdlc/harness/core/skills/sdlc-plan/SKILL.md +15 -0
- package/packages/sdlc/harness/core/spec.md +24 -0
- package/packages/sdlc/harness/core/start.sh +3 -0
- package/packages/sdlc/harness/full/AGENTS.md +15 -0
- package/packages/sdlc/harness/full/README.md +23 -0
- package/packages/sdlc/harness/full/REVIEW.md +19 -0
- package/packages/sdlc/harness/full/bands.yaml +13 -0
- package/packages/sdlc/harness/full/changes/README.md +4 -0
- package/packages/sdlc/harness/full/cursor/rules/sdlc-full.mdc +17 -0
- package/packages/sdlc/harness/full/evals/README.md +16 -0
- package/packages/sdlc/harness/full/hooks/README.md +17 -0
- package/packages/sdlc/harness/full/install.sh +3 -0
- package/packages/sdlc/harness/full/intent.md +26 -0
- package/packages/sdlc/harness/full/manifest.yaml +17 -0
- package/packages/sdlc/harness/full/plan.md +30 -0
- package/packages/sdlc/harness/full/sdlc.sh +230 -0
- package/packages/sdlc/harness/full/skills/sdlc-feedback/SKILL.md +18 -0
- package/packages/sdlc/harness/full/skills/sdlc-loop/SKILL.md +21 -0
- package/packages/sdlc/harness/full/skills/sdlc-review/SKILL.md +15 -0
- package/packages/sdlc/harness/full/spec.md +28 -0
- package/packages/sdlc/harness/full/start.sh +3 -0
- package/packages/sdlc/harness/light/AGENTS.md +13 -0
- package/packages/sdlc/harness/light/README.md +15 -0
- package/packages/sdlc/harness/light/changes/README.md +6 -0
- package/packages/sdlc/harness/light/cursor/rules/sdlc-light.mdc +16 -0
- package/packages/sdlc/harness/light/install.sh +3 -0
- package/packages/sdlc/harness/light/intent.md +24 -0
- package/packages/sdlc/harness/light/manifest.yaml +15 -0
- package/packages/sdlc/harness/light/plan.md +28 -0
- package/packages/sdlc/harness/light/sdlc.sh +230 -0
- package/packages/sdlc/harness/light/skills/sdlc-light/SKILL.md +30 -0
- package/packages/sdlc/harness/light/spec.md +23 -0
- package/packages/sdlc/harness/light/start.sh +3 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 ui8kit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# UXDX
|
|
2
|
+
|
|
3
|
+
CLI to accumulate operator and developer experience packs. One binary, two
|
|
4
|
+
commands. Package name: [`@ui8kit/uxdx`](https://www.npmjs.com/package/@ui8kit/uxdx).
|
|
5
|
+
|
|
6
|
+
Homepage: [github.com/ui8kit/uxdx](https://github.com/ui8kit/uxdx)
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
npx @ui8kit/uxdx --help
|
|
10
|
+
npx @ui8kit/uxdx project | p | -p | --project [init] [--dir <path>] [--rules] [--force]
|
|
11
|
+
npx @ui8kit/uxdx sdlc | s | -s | --sdlc [init] [--dir <path>] --level light|core|full [--force] [--no-install]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Same via Bun: `bunx @ui8kit/uxdx --help`.
|
|
15
|
+
|
|
16
|
+
The binary is still `uxdx`. After a local or global install you can call it
|
|
17
|
+
without the scope:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm i -g @ui8kit/uxdx
|
|
21
|
+
uxdx --help
|
|
22
|
+
|
|
23
|
+
npm i -D @ui8kit/uxdx
|
|
24
|
+
npx uxdx --help
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `project` writes `README.md`, `.gitignore`, `.cursorignore`, and `.manual/`.
|
|
28
|
+
- `sdlc` copies a vendored harness pack into `.sdlc/`. `--level` is required.
|
|
29
|
+
- After copy, `sdlc` runs `.sdlc/sdlc.sh install` unless `--no-install`.
|
|
30
|
+
|
|
31
|
+
## Local
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bun install
|
|
35
|
+
bun test
|
|
36
|
+
bun run uxdx -- --help
|
|
37
|
+
bun run build
|
|
38
|
+
```
|
package/dist/uxdx.mjs
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// packages/cli/src/parse.ts
|
|
5
|
+
var LEVELS = new Set(["light", "core", "full"]);
|
|
6
|
+
function takeFlag(rest, name) {
|
|
7
|
+
const i = rest.indexOf(name);
|
|
8
|
+
if (i === -1)
|
|
9
|
+
return false;
|
|
10
|
+
rest.splice(i, 1);
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
function takeOpt(rest, name) {
|
|
14
|
+
const i = rest.indexOf(name);
|
|
15
|
+
if (i === -1)
|
|
16
|
+
return null;
|
|
17
|
+
const v = rest[i + 1];
|
|
18
|
+
if (!v || v.startsWith("-")) {
|
|
19
|
+
throw new Error(`${name} requires a value`);
|
|
20
|
+
}
|
|
21
|
+
rest.splice(i, 2);
|
|
22
|
+
return v;
|
|
23
|
+
}
|
|
24
|
+
function stripInit(rest) {
|
|
25
|
+
if (rest[0] === "init")
|
|
26
|
+
rest.shift();
|
|
27
|
+
}
|
|
28
|
+
function parseArgv(argv) {
|
|
29
|
+
const args = argv.slice(2);
|
|
30
|
+
if (args.length === 0)
|
|
31
|
+
return { command: "help" };
|
|
32
|
+
const head = args[0];
|
|
33
|
+
const rest = args.slice(1);
|
|
34
|
+
if (head === "--help" || head === "-h") {
|
|
35
|
+
if (rest[0] === "-p" || rest[0] === "--project" || rest[0] === "p" || rest[0] === "project") {
|
|
36
|
+
return parseProject(rest.slice(1), true);
|
|
37
|
+
}
|
|
38
|
+
if (rest[0] === "-s" || rest[0] === "--sdlc" || rest[0] === "s" || rest[0] === "sdlc") {
|
|
39
|
+
return parseSdlc(rest.slice(1), true);
|
|
40
|
+
}
|
|
41
|
+
return { command: "help" };
|
|
42
|
+
}
|
|
43
|
+
if (head === "-p" || head === "--project" || head === "p" || head === "project") {
|
|
44
|
+
return parseProject(rest, false);
|
|
45
|
+
}
|
|
46
|
+
if (head === "-s" || head === "--sdlc" || head === "s" || head === "sdlc") {
|
|
47
|
+
return parseSdlc(rest, false);
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`unknown command ${head} (try --help)`);
|
|
50
|
+
}
|
|
51
|
+
function parseProject(restIn, forcedHelp) {
|
|
52
|
+
const rest = [...restIn];
|
|
53
|
+
stripInit(rest);
|
|
54
|
+
const help = forcedHelp || takeFlag(rest, "--help") || takeFlag(rest, "-h");
|
|
55
|
+
const force = takeFlag(rest, "--force");
|
|
56
|
+
const rules = takeFlag(rest, "--rules");
|
|
57
|
+
const dir = takeOpt(rest, "--dir") ?? process.cwd();
|
|
58
|
+
if (rest.length)
|
|
59
|
+
throw new Error(`unexpected arguments: ${rest.join(" ")}`);
|
|
60
|
+
return { command: "project", help, dir, force, rules };
|
|
61
|
+
}
|
|
62
|
+
function parseSdlc(restIn, forcedHelp) {
|
|
63
|
+
const rest = [...restIn];
|
|
64
|
+
stripInit(rest);
|
|
65
|
+
const help = forcedHelp || takeFlag(rest, "--help") || takeFlag(rest, "-h");
|
|
66
|
+
const force = takeFlag(rest, "--force");
|
|
67
|
+
const noInstall = takeFlag(rest, "--no-install");
|
|
68
|
+
const dir = takeOpt(rest, "--dir") ?? process.cwd();
|
|
69
|
+
const levelRaw = takeOpt(rest, "--level");
|
|
70
|
+
let level = null;
|
|
71
|
+
if (levelRaw) {
|
|
72
|
+
if (!LEVELS.has(levelRaw))
|
|
73
|
+
throw new Error(`--level must be light, core, or full`);
|
|
74
|
+
level = levelRaw;
|
|
75
|
+
}
|
|
76
|
+
if (rest.length)
|
|
77
|
+
throw new Error(`unexpected arguments: ${rest.join(" ")}`);
|
|
78
|
+
return {
|
|
79
|
+
command: "sdlc",
|
|
80
|
+
help,
|
|
81
|
+
dir,
|
|
82
|
+
force,
|
|
83
|
+
level,
|
|
84
|
+
install: !noInstall
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
var ROOT_HELP = `uxdx — operator and developer experience packs
|
|
88
|
+
|
|
89
|
+
Usage:
|
|
90
|
+
uxdx [--help]
|
|
91
|
+
uxdx project | p | -p | --project [init] [--dir <path>] [--rules] [--force]
|
|
92
|
+
uxdx sdlc | s | -s | --sdlc [init] [--dir <path>] --level light|core|full [--force] [--no-install]
|
|
93
|
+
|
|
94
|
+
-p / --project Scaffold README, ignores, and .manual/
|
|
95
|
+
-s / --sdlc Install an SDLC harness pack into .sdlc/
|
|
96
|
+
|
|
97
|
+
Run uxdx project --help or uxdx sdlc --help for command flags.
|
|
98
|
+
`;
|
|
99
|
+
var PROJECT_HELP = `uxdx project — default operator layout
|
|
100
|
+
|
|
101
|
+
Aliases: project, p, -p, --project
|
|
102
|
+
|
|
103
|
+
[init] Default action (may be omitted)
|
|
104
|
+
--dir <path> Target directory (default: cwd)
|
|
105
|
+
--rules Create a stub .cursor/rules file
|
|
106
|
+
--force Overwrite files this command owns
|
|
107
|
+
--help This text
|
|
108
|
+
`;
|
|
109
|
+
var SDLC_HELP = `uxdx sdlc — install an SDLC harness into .sdlc/
|
|
110
|
+
|
|
111
|
+
Aliases: sdlc, s, -s, --sdlc
|
|
112
|
+
|
|
113
|
+
[init] Default action (may be omitted)
|
|
114
|
+
--level <name> Required: light | core | full
|
|
115
|
+
--dir <path> Target directory (default: cwd)
|
|
116
|
+
--no-install Copy the pack only; do not run .sdlc/sdlc.sh install
|
|
117
|
+
--force Replace an existing .sdlc of a different level
|
|
118
|
+
--help This text
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
// packages/project/src/init.ts
|
|
122
|
+
import { mkdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
123
|
+
import { basename, join } from "node:path";
|
|
124
|
+
function ensureLine(file, line, _force) {
|
|
125
|
+
if (!existsSync(file)) {
|
|
126
|
+
writeFileSync(file, `${line}
|
|
127
|
+
`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const cur = readFileSync(file, "utf8");
|
|
131
|
+
if (cur.split(/\r?\n/).some((l) => l.trim() === line.trim()))
|
|
132
|
+
return;
|
|
133
|
+
const nl = cur.endsWith(`
|
|
134
|
+
`) || cur.length === 0 ? "" : `
|
|
135
|
+
`;
|
|
136
|
+
writeFileSync(file, `${cur}${nl}${line}
|
|
137
|
+
`);
|
|
138
|
+
}
|
|
139
|
+
function writeIfMissing(file, body, force) {
|
|
140
|
+
if (existsSync(file) && !force)
|
|
141
|
+
return;
|
|
142
|
+
writeFileSync(file, body);
|
|
143
|
+
}
|
|
144
|
+
function initProject(opts) {
|
|
145
|
+
const root = opts.dir;
|
|
146
|
+
mkdirSync(root, { recursive: true });
|
|
147
|
+
const done = [];
|
|
148
|
+
const name = basename(root);
|
|
149
|
+
const readme = join(root, "README.md");
|
|
150
|
+
writeIfMissing(readme, `# ${name}
|
|
151
|
+
`, opts.force);
|
|
152
|
+
done.push("README.md");
|
|
153
|
+
ensureLine(join(root, ".gitignore"), ".manual/", opts.force);
|
|
154
|
+
done.push(".gitignore");
|
|
155
|
+
ensureLine(join(root, ".cursorignore"), ".manual/", opts.force);
|
|
156
|
+
done.push(".cursorignore");
|
|
157
|
+
const projectDir = join(root, ".project");
|
|
158
|
+
mkdirSync(projectDir, { recursive: true });
|
|
159
|
+
writeIfMissing(join(projectDir, "README.md"), `# .project
|
|
160
|
+
|
|
161
|
+
Operator notes for this repository. See \`intent.md\` when present.
|
|
162
|
+
`, opts.force);
|
|
163
|
+
done.push(".project/");
|
|
164
|
+
mkdirSync(join(root, ".manual"), { recursive: true });
|
|
165
|
+
done.push(".manual/");
|
|
166
|
+
if (opts.rules) {
|
|
167
|
+
const rulesDir = join(root, ".cursor", "rules");
|
|
168
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
169
|
+
writeIfMissing(join(rulesDir, "uxdx.mdc"), `---
|
|
170
|
+
description: UXDX operator layout
|
|
171
|
+
alwaysApply: true
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
# UXDX
|
|
175
|
+
|
|
176
|
+
Read the repository operator notes before changing product scope.
|
|
177
|
+
`, opts.force);
|
|
178
|
+
done.push(".cursor/rules/");
|
|
179
|
+
}
|
|
180
|
+
return done;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// packages/sdlc/src/init.ts
|
|
184
|
+
import { cpSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync } from "node:fs";
|
|
185
|
+
import { dirname, join as join2 } from "node:path";
|
|
186
|
+
import { fileURLToPath } from "node:url";
|
|
187
|
+
import { spawnSync } from "node:child_process";
|
|
188
|
+
function harnessRoot() {
|
|
189
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
190
|
+
const candidates = [
|
|
191
|
+
join2(here, "..", "harness"),
|
|
192
|
+
join2(here, "..", "..", "harness"),
|
|
193
|
+
join2(here, "..", "packages", "sdlc", "harness"),
|
|
194
|
+
join2(process.cwd(), "packages", "sdlc", "harness")
|
|
195
|
+
];
|
|
196
|
+
for (const c of candidates) {
|
|
197
|
+
if (existsSync2(join2(c, "light", "manifest.yaml")))
|
|
198
|
+
return c;
|
|
199
|
+
}
|
|
200
|
+
throw new Error("uxdx: SDLC harness packs not found (packages/sdlc/harness)");
|
|
201
|
+
}
|
|
202
|
+
function readLevel(manifestPath) {
|
|
203
|
+
if (!existsSync2(manifestPath))
|
|
204
|
+
return null;
|
|
205
|
+
const m = readFileSync2(manifestPath, "utf8").match(/^level:\s*(\S+)/m);
|
|
206
|
+
return m ? m[1] : null;
|
|
207
|
+
}
|
|
208
|
+
function initSdlc(opts) {
|
|
209
|
+
const dest = join2(opts.dir, ".sdlc");
|
|
210
|
+
const existing = readLevel(join2(dest, "manifest.yaml"));
|
|
211
|
+
if (existing && existing !== opts.level && !opts.force) {
|
|
212
|
+
throw new Error(`uxdx: .sdlc already has level ${existing}; use --force to replace with ${opts.level}`);
|
|
213
|
+
}
|
|
214
|
+
const src = join2(harnessRoot(), opts.level);
|
|
215
|
+
if (!existsSync2(join2(src, "manifest.yaml"))) {
|
|
216
|
+
throw new Error(`uxdx: missing harness pack ${opts.level}`);
|
|
217
|
+
}
|
|
218
|
+
mkdirSync2(opts.dir, { recursive: true });
|
|
219
|
+
if (existsSync2(dest) && opts.force) {
|
|
220
|
+
rmSync(dest, { recursive: true, force: true });
|
|
221
|
+
}
|
|
222
|
+
mkdirSync2(dest, { recursive: true });
|
|
223
|
+
cpSync(src, dest, { recursive: true });
|
|
224
|
+
let installed = false;
|
|
225
|
+
if (opts.install) {
|
|
226
|
+
const script = join2(dest, "sdlc.sh");
|
|
227
|
+
const r = spawnSync("bash", [script, "install"], {
|
|
228
|
+
cwd: opts.dir,
|
|
229
|
+
encoding: "utf8"
|
|
230
|
+
});
|
|
231
|
+
if (r.error || r.status !== 0) {
|
|
232
|
+
const err = r.stderr || r.error?.message || `exit ${r.status}`;
|
|
233
|
+
throw new Error(`uxdx: .sdlc/sdlc.sh install failed (${err.trim()})`);
|
|
234
|
+
}
|
|
235
|
+
installed = true;
|
|
236
|
+
}
|
|
237
|
+
return { sdlcDir: dest, installed };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// packages/cli/src/main.ts
|
|
241
|
+
function run(argv) {
|
|
242
|
+
let parsed;
|
|
243
|
+
try {
|
|
244
|
+
parsed = parseArgv(argv);
|
|
245
|
+
} catch (e) {
|
|
246
|
+
console.error(`uxdx: ${e.message}`);
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
if (parsed.command === "help") {
|
|
250
|
+
console.log(ROOT_HELP);
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
if (parsed.command === "project") {
|
|
254
|
+
if (parsed.help) {
|
|
255
|
+
console.log(PROJECT_HELP);
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
const done = initProject({
|
|
259
|
+
dir: parsed.dir,
|
|
260
|
+
force: parsed.force,
|
|
261
|
+
rules: parsed.rules
|
|
262
|
+
});
|
|
263
|
+
console.log(`uxdx: project init in ${parsed.dir}`);
|
|
264
|
+
for (const p of done)
|
|
265
|
+
console.log(` ${p}`);
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
if (parsed.help) {
|
|
269
|
+
console.log(SDLC_HELP);
|
|
270
|
+
return 0;
|
|
271
|
+
}
|
|
272
|
+
if (!parsed.level) {
|
|
273
|
+
console.error("uxdx: --level is required (light | core | full)");
|
|
274
|
+
console.log(SDLC_HELP);
|
|
275
|
+
return 1;
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
const { sdlcDir, installed } = initSdlc({
|
|
279
|
+
dir: parsed.dir,
|
|
280
|
+
level: parsed.level,
|
|
281
|
+
force: parsed.force,
|
|
282
|
+
install: parsed.install
|
|
283
|
+
});
|
|
284
|
+
console.log(`uxdx: sdlc ${parsed.level} → ${sdlcDir}${installed ? " (install ran)" : ""}`);
|
|
285
|
+
return 0;
|
|
286
|
+
} catch (e) {
|
|
287
|
+
console.error(e.message);
|
|
288
|
+
return 1;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (__require.main == __require.module) {
|
|
292
|
+
process.exit(run(process.argv));
|
|
293
|
+
}
|
|
294
|
+
export {
|
|
295
|
+
run
|
|
296
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ui8kit/uxdx",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI to accumulate operator and developer experience packs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"uxdx": "./dist/uxdx.mjs"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/ui8kit/uxdx.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/ui8kit/uxdx/issues"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"packages/sdlc/harness",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"cli",
|
|
27
|
+
"uxdx",
|
|
28
|
+
"sdlc",
|
|
29
|
+
"cursor",
|
|
30
|
+
"scaffold",
|
|
31
|
+
"ui8kit"
|
|
32
|
+
],
|
|
33
|
+
"author": {
|
|
34
|
+
"name": "ui8kit",
|
|
35
|
+
"url": "https://github.com/ui8kit"
|
|
36
|
+
},
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"homepage": "https://github.com/ui8kit/uxdx",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "bun build packages/cli/src/main.ts --outfile dist/uxdx.mjs --target node",
|
|
41
|
+
"uxdx": "bun packages/cli/src/main.ts",
|
|
42
|
+
"test": "bun test",
|
|
43
|
+
"prepack": "bun run build",
|
|
44
|
+
"prepublishOnly": "bun test && bun run build",
|
|
45
|
+
"pack:check": "npm pack --dry-run"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=20.0.0"
|
|
49
|
+
},
|
|
50
|
+
"workspaces": [
|
|
51
|
+
"packages/*"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# SDLC harness packs (solo)
|
|
2
|
+
|
|
3
|
+
Copy-paste packs for the AI-native loop, sized for one operator. Source method:
|
|
4
|
+
[`.manual/sdlc-en`](../.manual/sdlc-en/README.md). Which product gets which pack:
|
|
5
|
+
[`.project/sdlc-scope.md`](../.project/sdlc-scope.md).
|
|
6
|
+
|
|
7
|
+
Later this tree can move to its own repo or a CLI (`sdlc init --level full`).
|
|
8
|
+
Until then the consumer copies **one** pack into **`.sdlc/`**.
|
|
9
|
+
|
|
10
|
+
## How it works
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
hinddy/brainstorm/.harness-sdlc/<level>/ (this gym)
|
|
14
|
+
│
|
|
15
|
+
│ copy (do not merge levels)
|
|
16
|
+
▼
|
|
17
|
+
your-repo/.sdlc/ artifacts + overlay text
|
|
18
|
+
│
|
|
19
|
+
├── intent.md spec.md plan.md templates at pack root
|
|
20
|
+
├── changes/<slug>/ one folder per real change
|
|
21
|
+
├── AGENTS.md overlay — merge into root AGENTS.md
|
|
22
|
+
├── skills/ copy → .cursor/skills/
|
|
23
|
+
└── cursor/rules/ copy → .cursor/rules/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
1. Pick **full**, **core**, or **light** from `sdlc-scope.md`. Never stack two packs.
|
|
27
|
+
2. Copy the pack directory contents into `<repo>/.sdlc/` (keep `manifest.yaml`).
|
|
28
|
+
3. **Activate Cursor** (rules/skills are not loaded from `.sdlc` by default):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
mkdir -p .cursor/rules .cursor/skills
|
|
32
|
+
cp .sdlc/cursor/rules/*.mdc .cursor/rules/
|
|
33
|
+
cp -R .sdlc/skills/* .cursor/skills/
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
4. **AGENTS.md**: if the repo already has a product `AGENTS.md` (SiteStarter, YPanel, …), **append** a short pointer. Do not replace stack-contract text. If there is no root file, copy `.sdlc/AGENTS.md` to the repo root.
|
|
37
|
+
5. For each change: copy `intent.md` / `spec.md` / `plan.md` into `.sdlc/changes/<slug>/` and fill them. Leave the pack-root files as templates.
|
|
38
|
+
6. Existing product rules (`.cursor/rules/stack-contract.mdc`, …) stay. The harness adds SDLC rules; it does not own Codex IA.
|
|
39
|
+
|
|
40
|
+
Human still merges. The agent writes artifacts and code; you accept `plan.md` before a large diff.
|
|
41
|
+
|
|
42
|
+
## Levels
|
|
43
|
+
|
|
44
|
+
| Pack | Use when | Artifacts | Extra |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| `light/` | Product on an existing contract; small blast radius | `plan.md` required; `intent.md` optional; skip `spec.md` unless IA changes | No evals, no maintain bands |
|
|
47
|
+
| `core/` | Kernel, API, ARIA, shared library | `intent.md` + `plan.md`; compressed `spec.md` on boundary changes | Invariant skills; short `REVIEW.md` |
|
|
48
|
+
| `full/` | Portable Codex path, YPanel, anything that ships + is pinned | Full chain including `spec.md` | `REVIEW.md`, `evals/` stub, `bands.yaml` stub |
|
|
49
|
+
|
|
50
|
+
## Future CLI (not built)
|
|
51
|
+
|
|
52
|
+
Suggested commands, matching `manifest.yaml` in each pack:
|
|
53
|
+
|
|
54
|
+
- `sdlc init --level full` → copy pack to `.sdlc`, overlay rules/skills, patch root `AGENTS.md` between markers
|
|
55
|
+
- `sdlc change start <slug>` → copy templates into `.sdlc/changes/<slug>/`
|
|
56
|
+
- `sdlc change accept` → remind to commit artifacts with the diff
|
|
57
|
+
|
|
58
|
+
Do not invent a second content REST or product kinds from this harness.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!-- sdlc-harness:begin -->
|
|
2
|
+
## SDLC harness (core)
|
|
3
|
+
|
|
4
|
+
This repo uses the **core** pack (`.harness-sdlc/core` → `.sdlc/`).
|
|
5
|
+
|
|
6
|
+
- Boundary, API, and policy changes: `intent.md` + short `spec.md` + `plan.md`.
|
|
7
|
+
- Mechanical fixes: `plan.md` only, still name the invariant you must not break.
|
|
8
|
+
- Skills: `sdlc-invariants`, `sdlc-plan`. Rule: `sdlc-core.mdc`.
|
|
9
|
+
- PR review against `.sdlc/REVIEW.md` (bugs, contract, compatibility).
|
|
10
|
+
- Do not add product behaviour to this module to ship a site. Consumers pin.
|
|
11
|
+
- Human merge. Skip eval-CI and maintain-bands unless this repo already runs them.
|
|
12
|
+
<!-- sdlc-harness:end -->
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Core harness
|
|
2
|
+
|
|
3
|
+
For kernels, shared libraries, and high blast-radius contracts.
|
|
4
|
+
Examples: FormSet, Framework, Panel, ui8kit/aria, Context.
|
|
5
|
+
|
|
6
|
+
Copy this directory to `<repo>/.sdlc/`. Overlay Cursor files — `../README.md`.
|
|
7
|
+
|
|
8
|
+
## Per change
|
|
9
|
+
|
|
10
|
+
1. Write `intent.md` when the change is a boundary, policy, or public API.
|
|
11
|
+
2. Keep `spec.md` short: constraints + concerns, not a product PRD.
|
|
12
|
+
3. `plan.md` always names files, order, tests, and what must not break.
|
|
13
|
+
4. Encode repeated invariants as skills; do not repeat them in every plan.
|
|
14
|
+
5. Human merge. No on-call agent. No eval suite unless this repo already has CI
|
|
15
|
+
for agent config (then add cases there, not a second framework).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Review instructions (core)
|
|
2
|
+
|
|
3
|
+
## Passes
|
|
4
|
+
|
|
5
|
+
Tag each finding with its pass:
|
|
6
|
+
|
|
7
|
+
- Bugs: logic errors, broken edge cases, subtle regressions
|
|
8
|
+
- Contract: public API, ARIA, kernel boundary, generated-file policy
|
|
9
|
+
- Compatibility: importers / pins still valid
|
|
10
|
+
|
|
11
|
+
## Important vs nit
|
|
12
|
+
|
|
13
|
+
Important: would break callers, leak data, or violate the module contract.
|
|
14
|
+
Style and naming are nits. Cap nits at five; count the rest.
|
|
15
|
+
|
|
16
|
+
## Do not report
|
|
17
|
+
|
|
18
|
+
Generated files the repo forbids editing, and anything CI already enforces.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Core SDLC — invariants, compressed spec, plan before large diffs
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Core SDLC overlay
|
|
7
|
+
|
|
8
|
+
This repository opted into `.sdlc` harness **core**.
|
|
9
|
+
|
|
10
|
+
- Artifacts: `.sdlc/changes/<slug>/`.
|
|
11
|
+
- Boundary changes need `intent.md`, a short `spec.md`, and `plan.md`.
|
|
12
|
+
- Review incoming diffs against `.sdlc/REVIEW.md`.
|
|
13
|
+
- Skills `sdlc-invariants` and `sdlc-plan` apply.
|
|
14
|
+
- Product `AGENTS.md` and stack-contract win on conflict.
|
|
15
|
+
- No autonomous production actions. No second content API.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Intent (core)
|
|
2
|
+
|
|
3
|
+
Author: <!-- name -->
|
|
4
|
+
Status: draft
|
|
5
|
+
Slug: <!-- kebab-case -->
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
What invariant, API, or shared behaviour is wrong or missing.
|
|
10
|
+
|
|
11
|
+
## Proposed outcome
|
|
12
|
+
|
|
13
|
+
The new contract in one paragraph. Name the callers that must keep working.
|
|
14
|
+
|
|
15
|
+
## Affected users and systems
|
|
16
|
+
|
|
17
|
+
Who imports this module. Do not expand into product IA of a consumer site.
|
|
18
|
+
|
|
19
|
+
## Constraints
|
|
20
|
+
|
|
21
|
+
- Callers pin this module; do not require them to fork.
|
|
22
|
+
- No product schemas, carts, or site-specific kinds in a shared kernel.
|
|
23
|
+
- Existing tests that encode the compiler/API contract stay green.
|
|
24
|
+
|
|
25
|
+
## Open questions
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
level: core
|
|
2
|
+
version: 1
|
|
3
|
+
copy_to: .sdlc
|
|
4
|
+
cursor:
|
|
5
|
+
rules: cursor/rules
|
|
6
|
+
skills: skills
|
|
7
|
+
agents_md:
|
|
8
|
+
overlay: AGENTS.md
|
|
9
|
+
merge_markers:
|
|
10
|
+
begin: "<!-- sdlc-harness:begin -->"
|
|
11
|
+
end: "<!-- sdlc-harness:end -->"
|
|
12
|
+
artifacts:
|
|
13
|
+
required: [plan.md]
|
|
14
|
+
on_boundary_change: [intent.md, spec.md]
|
|
15
|
+
review: REVIEW.md
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Plan (core)
|
|
2
|
+
|
|
3
|
+
From: `.sdlc/changes/<slug>/spec.md` (or intent if spec was skipped)
|
|
4
|
+
Status: draft
|
|
5
|
+
|
|
6
|
+
## Files that change
|
|
7
|
+
|
|
8
|
+
-
|
|
9
|
+
|
|
10
|
+
## Order of work
|
|
11
|
+
|
|
12
|
+
1.
|
|
13
|
+
2.
|
|
14
|
+
|
|
15
|
+
## Proof
|
|
16
|
+
|
|
17
|
+
Commands that must pass (repo SoT: `AGENTS.md` / Makefile / `bun run`).
|
|
18
|
+
|
|
19
|
+
## Risks
|
|
20
|
+
|
|
21
|
+
Blast radius for importers. Generated paths, frozen packages, public API.
|
|
22
|
+
|
|
23
|
+
## Rollback
|
|
24
|
+
|
|
25
|
+
How to revert the pin in callers if this ships broken.
|