@delegance/claude-autopilot 5.2.2 → 6.2.2
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 +1027 -1
- package/README.md +104 -17
- package/dist/src/adapters/council/claude.js +2 -1
- package/dist/src/adapters/council/openai.js +14 -7
- package/dist/src/adapters/deploy/_http.d.ts +43 -0
- package/dist/src/adapters/deploy/_http.js +99 -0
- package/dist/src/adapters/deploy/fly.d.ts +206 -0
- package/dist/src/adapters/deploy/fly.js +696 -0
- package/dist/src/adapters/deploy/generic.d.ts +39 -0
- package/dist/src/adapters/deploy/generic.js +98 -0
- package/dist/src/adapters/deploy/index.d.ts +15 -0
- package/dist/src/adapters/deploy/index.js +78 -0
- package/dist/src/adapters/deploy/render.d.ts +181 -0
- package/dist/src/adapters/deploy/render.js +550 -0
- package/dist/src/adapters/deploy/types.d.ts +221 -0
- package/dist/src/adapters/deploy/types.js +15 -0
- package/dist/src/adapters/deploy/vercel.d.ts +143 -0
- package/dist/src/adapters/deploy/vercel.js +426 -0
- package/dist/src/adapters/pricing.d.ts +36 -0
- package/dist/src/adapters/pricing.js +40 -0
- package/dist/src/adapters/review-engine/claude.js +2 -1
- package/dist/src/adapters/review-engine/codex.js +12 -8
- package/dist/src/adapters/review-engine/gemini.js +2 -1
- package/dist/src/adapters/review-engine/openai-compatible.js +2 -1
- package/dist/src/adapters/sdk-loader.d.ts +15 -0
- package/dist/src/adapters/sdk-loader.js +77 -0
- package/dist/src/cli/autopilot.d.ts +71 -0
- package/dist/src/cli/autopilot.js +735 -0
- package/dist/src/cli/brainstorm.d.ts +23 -0
- package/dist/src/cli/brainstorm.js +131 -0
- package/dist/src/cli/costs.d.ts +15 -1
- package/dist/src/cli/costs.js +99 -10
- package/dist/src/cli/deploy.d.ts +71 -0
- package/dist/src/cli/deploy.js +539 -0
- package/dist/src/cli/fix.d.ts +18 -0
- package/dist/src/cli/fix.js +105 -11
- package/dist/src/cli/help-text.d.ts +52 -0
- package/dist/src/cli/help-text.js +400 -0
- package/dist/src/cli/implement.d.ts +91 -0
- package/dist/src/cli/implement.js +196 -0
- package/dist/src/cli/index.js +784 -222
- package/dist/src/cli/json-envelope.d.ts +187 -0
- package/dist/src/cli/json-envelope.js +270 -0
- package/dist/src/cli/json-mode.d.ts +33 -0
- package/dist/src/cli/json-mode.js +201 -0
- package/dist/src/cli/migrate.d.ts +111 -0
- package/dist/src/cli/migrate.js +305 -0
- package/dist/src/cli/plan.d.ts +81 -0
- package/dist/src/cli/plan.js +149 -0
- package/dist/src/cli/pr.d.ts +106 -0
- package/dist/src/cli/pr.js +191 -19
- package/dist/src/cli/preflight.js +102 -1
- package/dist/src/cli/review.d.ts +27 -0
- package/dist/src/cli/review.js +126 -0
- package/dist/src/cli/runs-watch-renderer.d.ts +45 -0
- package/dist/src/cli/runs-watch-renderer.js +275 -0
- package/dist/src/cli/runs-watch.d.ts +41 -0
- package/dist/src/cli/runs-watch.js +395 -0
- package/dist/src/cli/runs.d.ts +122 -0
- package/dist/src/cli/runs.js +902 -0
- package/dist/src/cli/scan.d.ts +93 -0
- package/dist/src/cli/scan.js +166 -40
- package/dist/src/cli/spec.d.ts +66 -0
- package/dist/src/cli/spec.js +132 -0
- package/dist/src/cli/validate.d.ts +29 -0
- package/dist/src/cli/validate.js +131 -0
- package/dist/src/core/config/schema.d.ts +43 -0
- package/dist/src/core/config/schema.js +25 -0
- package/dist/src/core/config/types.d.ts +17 -0
- package/dist/src/core/council/runner.d.ts +10 -1
- package/dist/src/core/council/runner.js +25 -3
- package/dist/src/core/council/types.d.ts +7 -0
- package/dist/src/core/errors.d.ts +1 -1
- package/dist/src/core/errors.js +12 -0
- package/dist/src/core/logging/redaction.d.ts +13 -0
- package/dist/src/core/logging/redaction.js +20 -0
- package/dist/src/core/migrate/detector-rules.js +6 -0
- package/dist/src/core/migrate/schema-validator.js +22 -1
- package/dist/src/core/phases/static-rules.d.ts +5 -1
- package/dist/src/core/phases/static-rules.js +2 -5
- package/dist/src/core/run-state/budget.d.ts +88 -0
- package/dist/src/core/run-state/budget.js +141 -0
- package/dist/src/core/run-state/cli-internal.d.ts +21 -0
- package/dist/src/core/run-state/cli-internal.js +174 -0
- package/dist/src/core/run-state/events.d.ts +59 -0
- package/dist/src/core/run-state/events.js +504 -0
- package/dist/src/core/run-state/lock.d.ts +61 -0
- package/dist/src/core/run-state/lock.js +206 -0
- package/dist/src/core/run-state/phase-context.d.ts +60 -0
- package/dist/src/core/run-state/phase-context.js +108 -0
- package/dist/src/core/run-state/phase-registry.d.ts +137 -0
- package/dist/src/core/run-state/phase-registry.js +162 -0
- package/dist/src/core/run-state/phase-runner.d.ts +80 -0
- package/dist/src/core/run-state/phase-runner.js +447 -0
- package/dist/src/core/run-state/provider-readback.d.ts +130 -0
- package/dist/src/core/run-state/provider-readback.js +426 -0
- package/dist/src/core/run-state/replay-decision.d.ts +69 -0
- package/dist/src/core/run-state/replay-decision.js +144 -0
- package/dist/src/core/run-state/resolve-engine.d.ts +100 -0
- package/dist/src/core/run-state/resolve-engine.js +190 -0
- package/dist/src/core/run-state/resume-preflight.d.ts +66 -0
- package/dist/src/core/run-state/resume-preflight.js +116 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.d.ts +73 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.js +186 -0
- package/dist/src/core/run-state/runs.d.ts +57 -0
- package/dist/src/core/run-state/runs.js +288 -0
- package/dist/src/core/run-state/snapshot.d.ts +14 -0
- package/dist/src/core/run-state/snapshot.js +114 -0
- package/dist/src/core/run-state/state.d.ts +40 -0
- package/dist/src/core/run-state/state.js +164 -0
- package/dist/src/core/run-state/types.d.ts +278 -0
- package/dist/src/core/run-state/types.js +13 -0
- package/dist/src/core/run-state/ulid.d.ts +11 -0
- package/dist/src/core/run-state/ulid.js +95 -0
- package/dist/src/core/schema-alignment/extractor/index.d.ts +1 -1
- package/dist/src/core/schema-alignment/extractor/index.js +2 -2
- package/dist/src/core/schema-alignment/extractor/prisma.d.ts +13 -1
- package/dist/src/core/schema-alignment/extractor/prisma.js +65 -10
- package/dist/src/core/schema-alignment/git-history.d.ts +19 -0
- package/dist/src/core/schema-alignment/git-history.js +53 -0
- package/dist/src/core/static-rules/rules/brand-tokens.js +2 -2
- package/dist/src/core/static-rules/rules/schema-alignment.js +14 -4
- package/package.json +9 -5
- package/scripts/autoregress.ts +3 -2
- package/skills/claude-autopilot.md +1 -1
- package/skills/make-interfaces-feel-better/SKILL.md +104 -0
- package/skills/migrate/SKILL.md +193 -47
- package/skills/simplify-ui/SKILL.md +103 -0
- package/skills/ui/SKILL.md +117 -0
- package/skills/ui-ux-pro-max/SKILL.md +90 -0
|
@@ -1,18 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
for (const modelMatch of content.matchAll(
|
|
1
|
+
const MODEL_RE = /^model\s+(\w+)\s*\{([^}]+)\}/gm;
|
|
2
|
+
const FIELD_RE = /^\s+(\w+)\s+\S/gm;
|
|
3
|
+
function parseModels(content) {
|
|
4
|
+
const models = new Map();
|
|
5
|
+
for (const modelMatch of content.matchAll(MODEL_RE)) {
|
|
6
6
|
const table = modelMatch[1];
|
|
7
|
-
|
|
7
|
+
const fields = new Set();
|
|
8
8
|
const body = modelMatch[2];
|
|
9
|
-
|
|
10
|
-
const fieldRe = /^\s+(\w+)\s+\S/gm;
|
|
11
|
-
for (const fieldMatch of body.matchAll(fieldRe)) {
|
|
9
|
+
for (const fieldMatch of body.matchAll(FIELD_RE)) {
|
|
12
10
|
const column = fieldMatch[1];
|
|
13
11
|
if (column.startsWith('@') || column === 'id')
|
|
14
12
|
continue;
|
|
15
|
-
|
|
13
|
+
fields.add(column);
|
|
14
|
+
}
|
|
15
|
+
models.set(table, fields);
|
|
16
|
+
}
|
|
17
|
+
return models;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Extract schema entities from a Prisma schema file.
|
|
21
|
+
*
|
|
22
|
+
* When `previousContent` is provided, only the diff (added/removed fields,
|
|
23
|
+
* new/dropped tables) is emitted — this avoids over-reporting when a user
|
|
24
|
+
* touches schema.prisma for any reason (adding one field, editing a comment)
|
|
25
|
+
* and every long-existing field gets re-checked against type/API/UI layers.
|
|
26
|
+
*
|
|
27
|
+
* When `previousContent` is null/undefined, every model and field is emitted
|
|
28
|
+
* — the original "all entities are new" behavior used as a fallback when git
|
|
29
|
+
* history isn't available.
|
|
30
|
+
*/
|
|
31
|
+
export function extractFromPrisma(content, previousContent) {
|
|
32
|
+
const current = parseModels(content);
|
|
33
|
+
if (previousContent === undefined || previousContent === null) {
|
|
34
|
+
const entities = [];
|
|
35
|
+
for (const [table, fields] of current) {
|
|
36
|
+
entities.push({ table, operation: 'create_table' });
|
|
37
|
+
for (const column of fields)
|
|
38
|
+
entities.push({ table, column, operation: 'add_column' });
|
|
39
|
+
}
|
|
40
|
+
return entities;
|
|
41
|
+
}
|
|
42
|
+
const previous = parseModels(previousContent);
|
|
43
|
+
const entities = [];
|
|
44
|
+
for (const [table, currentFields] of current) {
|
|
45
|
+
const previousFields = previous.get(table);
|
|
46
|
+
if (!previousFields) {
|
|
47
|
+
// New table — emit create_table + add_column for every field
|
|
48
|
+
entities.push({ table, operation: 'create_table' });
|
|
49
|
+
for (const column of currentFields)
|
|
50
|
+
entities.push({ table, column, operation: 'add_column' });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
for (const column of currentFields) {
|
|
54
|
+
if (!previousFields.has(column))
|
|
55
|
+
entities.push({ table, column, operation: 'add_column' });
|
|
56
|
+
}
|
|
57
|
+
for (const column of previousFields) {
|
|
58
|
+
if (!currentFields.has(column))
|
|
59
|
+
entities.push({ table, column, operation: 'drop_column' });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Second pass — entirely dropped models. The first loop only iterates
|
|
63
|
+
// `current`, so a model present in `previous` but removed from `current`
|
|
64
|
+
// would never emit any entity, leaving stale references in type/API/UI
|
|
65
|
+
// layers undetected. Caught by Cursor Bugbot on PR #44 (MEDIUM).
|
|
66
|
+
for (const [table, previousFields] of previous) {
|
|
67
|
+
if (current.has(table))
|
|
68
|
+
continue;
|
|
69
|
+
for (const column of previousFields) {
|
|
70
|
+
entities.push({ table, column, operation: 'drop_column' });
|
|
16
71
|
}
|
|
17
72
|
}
|
|
18
73
|
return entities;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the previous version of a file from git, comparing against `base`.
|
|
3
|
+
* Returns null if the file has no git history at that ref (untracked,
|
|
4
|
+
* brand-new, not in a repo, or the ref doesn't resolve) — callers should
|
|
5
|
+
* fall back to whole-file extraction in that case.
|
|
6
|
+
*
|
|
7
|
+
* When `base` is omitted, the default is CI-aware (see `resolveDefaultBase`):
|
|
8
|
+
* - GitHub Actions PR build → `origin/<GITHUB_BASE_REF>`
|
|
9
|
+
* - GitLab MR build → `origin/<CI_MERGE_REQUEST_TARGET_BRANCH_NAME>`
|
|
10
|
+
* - everything else → `HEAD~1`
|
|
11
|
+
*
|
|
12
|
+
* Reading from `HEAD` directly is wrong in CI (any post-commit context):
|
|
13
|
+
* `HEAD` IS the current commit, so the diff against the working-tree file
|
|
14
|
+
* is always empty and no schema entities are emitted. Caught by Cursor
|
|
15
|
+
* Bugbot on PR #44 (HIGH); the multi-commit-PR variant of the same bug
|
|
16
|
+
* caught as a MEDIUM follow-up on the rebased commit.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getPreviousFileContent(filePath: string, cwd?: string, base?: string): string | null;
|
|
19
|
+
//# sourceMappingURL=git-history.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the default base ref for git diffs in a CI-aware way.
|
|
5
|
+
*
|
|
6
|
+
* Priority: `GITHUB_BASE_REF` (GitHub Actions PR builds), then
|
|
7
|
+
* `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` (GitLab MR builds), then `HEAD~1`
|
|
8
|
+
* for local / single-commit contexts. Branch-name env vars are prefixed
|
|
9
|
+
* with `origin/` so `git show <ref>:<file>` resolves to the
|
|
10
|
+
* remote-tracking-branch tip (the actual merge base).
|
|
11
|
+
*
|
|
12
|
+
* Caught + extended by Cursor Bugbot follow-up on PR #44 (MEDIUM): a static
|
|
13
|
+
* default of `HEAD~1` is wrong for multi-commit PRs — it points at the
|
|
14
|
+
* previous commit on the branch, not the merge base.
|
|
15
|
+
*/
|
|
16
|
+
function resolveDefaultBase() {
|
|
17
|
+
const ghBase = process.env.GITHUB_BASE_REF;
|
|
18
|
+
if (ghBase && ghBase.length > 0)
|
|
19
|
+
return `origin/${ghBase}`;
|
|
20
|
+
const glBase = process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;
|
|
21
|
+
if (glBase && glBase.length > 0)
|
|
22
|
+
return `origin/${glBase}`;
|
|
23
|
+
return 'HEAD~1';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Read the previous version of a file from git, comparing against `base`.
|
|
27
|
+
* Returns null if the file has no git history at that ref (untracked,
|
|
28
|
+
* brand-new, not in a repo, or the ref doesn't resolve) — callers should
|
|
29
|
+
* fall back to whole-file extraction in that case.
|
|
30
|
+
*
|
|
31
|
+
* When `base` is omitted, the default is CI-aware (see `resolveDefaultBase`):
|
|
32
|
+
* - GitHub Actions PR build → `origin/<GITHUB_BASE_REF>`
|
|
33
|
+
* - GitLab MR build → `origin/<CI_MERGE_REQUEST_TARGET_BRANCH_NAME>`
|
|
34
|
+
* - everything else → `HEAD~1`
|
|
35
|
+
*
|
|
36
|
+
* Reading from `HEAD` directly is wrong in CI (any post-commit context):
|
|
37
|
+
* `HEAD` IS the current commit, so the diff against the working-tree file
|
|
38
|
+
* is always empty and no schema entities are emitted. Caught by Cursor
|
|
39
|
+
* Bugbot on PR #44 (HIGH); the multi-commit-PR variant of the same bug
|
|
40
|
+
* caught as a MEDIUM follow-up on the rebased commit.
|
|
41
|
+
*/
|
|
42
|
+
export function getPreviousFileContent(filePath, cwd = process.cwd(), base = resolveDefaultBase()) {
|
|
43
|
+
const relPath = path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath;
|
|
44
|
+
const result = spawnSync('git', ['show', `${base}:${relPath}`], {
|
|
45
|
+
cwd,
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
timeout: 5000,
|
|
48
|
+
});
|
|
49
|
+
if (result.status !== 0)
|
|
50
|
+
return null;
|
|
51
|
+
return result.stdout;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=git-history.js.map
|
|
@@ -36,8 +36,8 @@ function buildPalette(brandCfg, cwd) {
|
|
|
36
36
|
export const brandTokensRule = {
|
|
37
37
|
name: 'brand-tokens',
|
|
38
38
|
severity: 'warning',
|
|
39
|
-
async check(touchedFiles,
|
|
40
|
-
const brandCfg = config
|
|
39
|
+
async check(touchedFiles, ctx = {}) {
|
|
40
|
+
const brandCfg = ctx.config?.brand;
|
|
41
41
|
if (!brandCfg)
|
|
42
42
|
return [];
|
|
43
43
|
const cwd = process.cwd();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { detect } from "../../schema-alignment/detector.js";
|
|
2
2
|
import { extract } from "../../schema-alignment/extractor/index.js";
|
|
3
|
+
import { getPreviousFileContent } from "../../schema-alignment/git-history.js";
|
|
3
4
|
import { scanLayers } from "../../schema-alignment/scanner.js";
|
|
4
5
|
import { runLlmCheck } from "../../schema-alignment/llm-check.js";
|
|
5
6
|
function isDestructive(entity) {
|
|
@@ -50,15 +51,24 @@ function structuralFinding(result, layer, defaultSev, sourceFile) {
|
|
|
50
51
|
export const schemaAlignmentRule = {
|
|
51
52
|
name: 'schema-alignment',
|
|
52
53
|
severity: 'warning',
|
|
53
|
-
async check(touchedFiles,
|
|
54
|
-
const saConfig = config['schema-alignment'];
|
|
54
|
+
async check(touchedFiles, ctx = {}) {
|
|
55
|
+
const saConfig = ctx.config?.['schema-alignment'];
|
|
55
56
|
if (saConfig?.enabled === false)
|
|
56
57
|
return [];
|
|
57
58
|
const cwd = process.cwd();
|
|
58
59
|
const migrationFiles = detect(touchedFiles, saConfig);
|
|
59
60
|
if (migrationFiles.length === 0)
|
|
60
61
|
return [];
|
|
61
|
-
|
|
62
|
+
// For Prisma schema files, fetch the previous version from git so we only
|
|
63
|
+
// emit entities for what actually changed in this diff. SQL migrations
|
|
64
|
+
// are inherently a diff already; the SQL extractor ignores
|
|
65
|
+
// `previousContent`, so skipping the `git show` spawn there avoids pure
|
|
66
|
+
// waste (Bugbot LOW on PR #44).
|
|
67
|
+
const allEntities = migrationFiles.flatMap(f => {
|
|
68
|
+
const isPrisma = f.endsWith('.prisma');
|
|
69
|
+
const previousContent = isPrisma ? getPreviousFileContent(f, cwd) : null;
|
|
70
|
+
return extract(f, previousContent).map(entity => ({ entity, sourceFile: f }));
|
|
71
|
+
});
|
|
62
72
|
if (allEntities.length === 0)
|
|
63
73
|
return [];
|
|
64
74
|
const scanResults = scanLayers(allEntities.map(e => e.entity), cwd, saConfig);
|
|
@@ -75,7 +85,7 @@ export const schemaAlignmentRule = {
|
|
|
75
85
|
return [];
|
|
76
86
|
const defaultSev = saConfig?.severity ?? 'warning';
|
|
77
87
|
const llmEnabled = saConfig?.llmCheck !== false;
|
|
78
|
-
const engine =
|
|
88
|
+
const engine = ctx.engine;
|
|
79
89
|
// Structural mode — always compute these so we can fall back if LLM path yields nothing
|
|
80
90
|
const structural = [];
|
|
81
91
|
for (const { result: r, sourceFile } of gapResults) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delegance/claude-autopilot",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Autonomous development pipeline for Claude Code: brainstorm → spec → plan → implement → migrate → validate → PR → review → merge. Multi-model, local-first, every phase a skill you can intervene in.",
|
|
6
6
|
"keywords": [
|
|
@@ -52,24 +52,28 @@
|
|
|
52
52
|
],
|
|
53
53
|
"scripts": {
|
|
54
54
|
"test": "node scripts/test-runner.mjs",
|
|
55
|
+
"test:adapters:live": "node --test --import=tsx tests/adapters/live/vercel.cert.ts tests/adapters/live/fly.cert.ts tests/adapters/live/render.cert.ts",
|
|
55
56
|
"typecheck": "tsc --noEmit",
|
|
56
57
|
"build": "tsc -p tsconfig.build.json && node scripts/post-build-rewrite-imports.mjs",
|
|
57
58
|
"prepublishOnly": "npm run build && npm test",
|
|
58
59
|
"autoregress": "tsx scripts/autoregress.ts"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
61
|
-
"@anthropic-ai/sdk": "^0.91.1",
|
|
62
|
-
"@google/generative-ai": "^0.24.1",
|
|
63
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
64
62
|
"ajv": "^8",
|
|
63
|
+
"ajv-formats": "^3.0.1",
|
|
65
64
|
"dotenv": ">=16",
|
|
66
65
|
"js-yaml": "^4",
|
|
67
66
|
"minimatch": ">=9",
|
|
68
|
-
"openai": ">=4",
|
|
69
67
|
"proper-lockfile": "^4.1.2",
|
|
70
68
|
"shell-quote": "^1.8.3",
|
|
71
69
|
"tsx": ">=4"
|
|
72
70
|
},
|
|
71
|
+
"optionalDependencies": {
|
|
72
|
+
"@anthropic-ai/sdk": "^0.91.1",
|
|
73
|
+
"@google/generative-ai": "^0.24.1",
|
|
74
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
75
|
+
"openai": ">=4"
|
|
76
|
+
},
|
|
73
77
|
"devDependencies": {
|
|
74
78
|
"@types/js-yaml": "^4",
|
|
75
79
|
"@types/node": "^25",
|
package/scripts/autoregress.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as os from 'node:os';
|
|
|
6
6
|
import { spawnSync } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { selectSnapshots } from './snapshots/impact-selector.ts';
|
|
9
|
-
import
|
|
9
|
+
import { loadOpenAI } from '../src/adapters/sdk-loader.ts';
|
|
10
10
|
import { buildImportMap } from './snapshots/import-scanner.ts';
|
|
11
11
|
|
|
12
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -230,6 +230,7 @@ async function cmdGenerate(args: string[]): Promise<number> {
|
|
|
230
230
|
|
|
231
231
|
console.log(`[autoregress generate] generating snapshots for ${srcFiles.length} file(s)`);
|
|
232
232
|
|
|
233
|
+
const OpenAI = await loadOpenAI();
|
|
233
234
|
const client = new OpenAI({ apiKey });
|
|
234
235
|
let sourceCommit = 'unknown';
|
|
235
236
|
try {
|
|
@@ -258,7 +259,7 @@ async function cmdGenerate(args: string[]): Promise<number> {
|
|
|
258
259
|
let snapContent: string;
|
|
259
260
|
try {
|
|
260
261
|
const response = await client.responses.create({
|
|
261
|
-
model: process.env.CODEX_MODEL ?? 'gpt-5.
|
|
262
|
+
model: process.env.CODEX_MODEL ?? 'gpt-5.5',
|
|
262
263
|
instructions: 'You write TypeScript snapshot tests. Output ONLY the file contents, no markdown fences.',
|
|
263
264
|
input: prompt,
|
|
264
265
|
max_output_tokens: 2000,
|
|
@@ -47,7 +47,7 @@ Each phase writes its output to disk. Claude can stop, the user can edit the art
|
|
|
47
47
|
- PR review finds criticals → fix on branch, push, re-review (max 2 rounds).
|
|
48
48
|
- Bugbot finds real bugs → fix, push, re-triage (max 3 rounds).
|
|
49
49
|
- Unrecoverable failure → stop, report what completed, show what remains.
|
|
50
|
-
4. **Codex review is part of the loop, not optional.** The pipeline explicitly dispatches to `gpt-5.
|
|
50
|
+
4. **Codex review is part of the loop, not optional.** The pipeline explicitly dispatches to `gpt-5.5` for spec review, plan review, and PR review. This is the multi-model moat — don't skip it.
|
|
51
51
|
5. **Skills are swappable.** `review-2pass` and `council` are alternative review phases — a user can configure which runs. The pipeline doesn't hardcode Claude or Codex.
|
|
52
52
|
|
|
53
53
|
## Phase outputs
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: make-interfaces-feel-better
|
|
3
|
+
description: Craft-and-feel polish for an interface that already works correctly and is already simple. Use when the user says "feels off", "feels clunky", "not quite right", "doesn't feel polished", "lacks soul", "feels cheap", "add some life to it", "make it feel expensive", "feels like AI slop", or wants motion/typography/microcopy/color work that isn't about fixing bugs or alignment. This is the vibes layer — assumes correctness and subtraction are already handled. If the screen is broken or cluttered, route to /ui-ux-pro-max or /simplify-ui first. Complements frontend-design:frontend-design (creative vision) but is scoped to tuning what exists.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Make interfaces feel better — the craft layer
|
|
7
|
+
|
|
8
|
+
This skill is for the pass where the interface already *works* and is already *simple*, but still feels mediocre. You are tuning emotion, not structure. If the user hasn't had the basics fixed yet, recommend `/ui-ux-pro-max` and `/simplify-ui` first; great feel on top of a broken layout is lipstick.
|
|
9
|
+
|
|
10
|
+
## The diagnostic
|
|
11
|
+
|
|
12
|
+
Before touching anything, ask yourself what specific feeling is off. Interfaces usually fail one of five feels:
|
|
13
|
+
|
|
14
|
+
1. **Cheap** — cramped, low-contrast, unbranded, no texture, inconsistent.
|
|
15
|
+
2. **Cold / clinical** — correct but soulless. Efficient but no character.
|
|
16
|
+
3. **Heavy / laggy** — transitions stutter, state changes snap, nothing feels alive.
|
|
17
|
+
4. **Disorganized** — elements fight for attention; no clear visual hierarchy.
|
|
18
|
+
5. **Nervous / fussy** — too many animations, too many chips, too many accent colors.
|
|
19
|
+
|
|
20
|
+
The fix for each is different. Name the feeling first, then apply the matching lever below.
|
|
21
|
+
|
|
22
|
+
## Levers — in order of impact-per-minute
|
|
23
|
+
|
|
24
|
+
### 1. Typography (highest leverage)
|
|
25
|
+
|
|
26
|
+
- **Pair a display face with a body face.** One distinctive (Playfair, Fraunces, Söhne, Tiempos, Inter Display) + one refined (Inter, Figtree, Söhne, IBM Plex, Geist). Don't use Inter for everything.
|
|
27
|
+
- **Use the display only at top levels** — page title, card titles ≥ 18px. Everything else body.
|
|
28
|
+
- **Tighten line-height on display** (1.1–1.2) and loosen on body (1.5–1.65).
|
|
29
|
+
- **Letter-spacing for uppercase** — eyebrows and section labels get `letter-spacing: 0.05em` or more.
|
|
30
|
+
- **One tabular-nums for numeric data** — `font-variant-numeric: tabular-nums` on any column of amounts makes them snap into alignment.
|
|
31
|
+
|
|
32
|
+
### 2. Color & contrast
|
|
33
|
+
|
|
34
|
+
- **One dominant color, one accent, one destructive. No fourth.**
|
|
35
|
+
- **Backgrounds are off-white or off-black, never pure.** `#F7F8F6`, `#0B0D0A` — feel warmer than `#FFFFFF` / `#000000`.
|
|
36
|
+
- **Shadows with color** — `box-shadow: 0 2px 12px rgba(brand-color, 0.08)` feels branded; `rgba(0,0,0,0.08)` feels generic.
|
|
37
|
+
- **Gradients only on one element at a time.** Usually the primary CTA or the hero. Gradients everywhere = AI-slop.
|
|
38
|
+
|
|
39
|
+
### 3. Motion
|
|
40
|
+
|
|
41
|
+
- **Page-load stagger is cheap delight.** 40–80ms stagger on cards/rows hitting the viewport; nothing fancier.
|
|
42
|
+
- **Easing: `cubic-bezier(0.22, 1, 0.36, 1)` for enter/exit.** Not `ease-in-out`.
|
|
43
|
+
- **200–280ms for small transitions, 400–600ms for modals/layouts.** Outside that range feels wrong.
|
|
44
|
+
- **Never animate what the user didn't cause.** Auto-pulsing "new" badges are hostile.
|
|
45
|
+
- **Respect `prefers-reduced-motion`** — drop transitions to 0.01s, keep only opacity/color changes.
|
|
46
|
+
|
|
47
|
+
### 4. Microcopy
|
|
48
|
+
|
|
49
|
+
- **Button verbs specific to the action.** Not "Submit" — "Send quote for review."
|
|
50
|
+
- **Empty states with personality.** "No quotes yet. Start one — it takes about 3 minutes." Beats "No data."
|
|
51
|
+
- **Error messages that acknowledge** — "That's not quite right — X needs to be Y" rather than "Invalid input."
|
|
52
|
+
- **Success states that celebrate proportionally.** Saved draft = small checkmark. Bound a policy = confetti-adjacent.
|
|
53
|
+
- **Loading copy that sets expectations.** "Matching you with carriers… usually 5 seconds" > spinner alone.
|
|
54
|
+
|
|
55
|
+
### 5. Texture & depth
|
|
56
|
+
|
|
57
|
+
Not every interface needs these, but they're how screens stop feeling generic:
|
|
58
|
+
- **Noise overlay at 2–4% opacity** on dark backgrounds. Kills the plastic look.
|
|
59
|
+
- **Subtle inner shadow on inputs** — `inset 0 1px 0 rgba(255,255,255,0.4)` on light themes suggests depth.
|
|
60
|
+
- **Asymmetric card padding** — e.g., `24px 24px 20px 24px` sometimes feels better than all-24 because humans scan top-to-bottom.
|
|
61
|
+
- **Left-align eyebrow decoration** — a 2px × 18px accent-color rule before a section label reads as editorial, not chrome.
|
|
62
|
+
|
|
63
|
+
### 6. The small details
|
|
64
|
+
|
|
65
|
+
- **Focus rings that feel on-brand** — `box-shadow: 0 0 0 3px rgba(brand, 0.2)` beats the default blue browser ring.
|
|
66
|
+
- **Checkmark animation on save** — 300ms stroke-dash reveal, not a static green dot.
|
|
67
|
+
- **Hover states that change border color**, not scale or shadow (which feel toy-like on dense forms).
|
|
68
|
+
- **Caret-color matched to brand.** `caret-color: var(--brand)`.
|
|
69
|
+
- **Chip alignment with inline icons** — baseline-align the icon to the text, don't center — centering looks off for small text.
|
|
70
|
+
|
|
71
|
+
## What to avoid (AI-slop tells)
|
|
72
|
+
|
|
73
|
+
- Purple-to-pink gradients on white.
|
|
74
|
+
- "Gradient text" for things that aren't hero titles.
|
|
75
|
+
- Emoji in UI chrome (buttons, headers).
|
|
76
|
+
- `font-family: 'Inter', sans-serif` as the only typeface.
|
|
77
|
+
- Drop-shadows the same size on cards, buttons, and modals (varied elevation is the whole point).
|
|
78
|
+
- Buttons with `border-radius: 9999px` that are 32px tall (pill at small scale reads as "claimed to be premium, actually built in 20 min").
|
|
79
|
+
- Glass-morphism backdrops without a real background image behind them.
|
|
80
|
+
|
|
81
|
+
## Workflow
|
|
82
|
+
|
|
83
|
+
1. **Name the feel.** In one sentence, write what's wrong. "Feels cheap because inputs have no shadow and everything is pure white on pure white." This grounds the rest.
|
|
84
|
+
2. **Apply at most 3 levers from the list above.** Do not touch everything — diminishing returns.
|
|
85
|
+
3. **Reload and look away and back.** Judge fresh, not against your memory of before.
|
|
86
|
+
4. **Name one thing you resisted adding.** This keeps you honest — feel upgrades are often about resisting maximalism, not piling it on.
|
|
87
|
+
5. **Show the user which levers you pulled** and invite them to push back on any that felt wrong.
|
|
88
|
+
|
|
89
|
+
## Red flags during the pass
|
|
90
|
+
|
|
91
|
+
If you catch yourself adding:
|
|
92
|
+
- A new color variable that isn't in the design system
|
|
93
|
+
- A third weight of the display font
|
|
94
|
+
- An animation > 600ms
|
|
95
|
+
- A shadow on a default-state button
|
|
96
|
+
- An emoji inside a form label
|
|
97
|
+
|
|
98
|
+
…stop. You're adding where you should be tuning.
|
|
99
|
+
|
|
100
|
+
## Interactions
|
|
101
|
+
|
|
102
|
+
- Runs best on a screen that already passed `/ui-ux-pro-max` and `/simplify-ui`.
|
|
103
|
+
- Can safely coexist with `/ui` for a combined sweep.
|
|
104
|
+
- If the user wants a redesign, escalate to `frontend-design:frontend-design`.
|