Skip to content

Commit 7e5cf2f

Browse files
committed
Linting setup and fixes
1 parent 7b53069 commit 7e5cf2f

24 files changed

Lines changed: 321 additions & 211 deletions

.claude/hooks/lint-and-test.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/bash
2+
# Run linting and tests before task stop
3+
# Exit code 1: warning (show results but don't block stop)
4+
# Exit code 0: all checks passed
5+
6+
echo "🔍 Running pre-stop checks..." >&2
7+
8+
# Track overall status
9+
STATUS=0
10+
11+
echo "→ Linting (with auto-fix)..." >&2
12+
if ! bun run lint:fix --error-on-warnings --reporter=summary; then
13+
STATUS=1
14+
fi
15+
16+
echo "" >&2
17+
echo "→ Running tests..." >&2
18+
if ! bun test --only-failures; then
19+
STATUS=1
20+
fi
21+
22+
if [ $STATUS -ne 0 ]; then
23+
echo "⚠️ Checks failed. Review output above." >&2
24+
else
25+
echo "✅ All checks passed!" >&2
26+
fi
27+
28+
exit $STATUS

.claude/settings.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"hooks": {
3+
"Stop": [
4+
{
5+
"hooks": [
6+
{
7+
"type": "command",
8+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/lint-and-test.sh"
9+
}
10+
]
11+
}
12+
]
13+
}
14+
}

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ jobs:
1414
with:
1515
bun-version: latest
1616
- run: bun install --frozen-lockfile
17+
- run: bun run lint
1718
- run: bun test

CLAUDE.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,25 @@ Opportunity Solution Tree (OST) validation and diagram generation tooling.
44

55
## Development
66

7-
```bash
8-
bun run src/index.ts validate <space-or-dir>
9-
bun run src/index.ts diagram <space-or-dir>
10-
bun run src/index.ts template-sync <template-dir>
11-
```
12-
7+
Get a list of commands: `bun run src/index.ts --help`
138
Space aliases (e.g. `personal`, `politics`) are resolved via `config.json`.
149

1510
## Project Context
1611

17-
This project validates OST node markdown files against a JSON schema. Node types: `vision`, `mission`, `goal`, `opportunity`, `solution`, `dashboard`.
18-
19-
As a convenience, directories can be registered as "space" aliases in `config.json`.
12+
This project validates OST node markdown files against a JSON schema.
2013

2114
## Tooling
2215

2316
- `gray-matter` - Parse YAML frontmatter from markdown
2417
- `ajv` - JSON Schema validation
2518
- `glob` - File discovery
2619
- `commander` - CLI interface
20+
- `bun test` - testing
2721

2822
## Key Files
2923

3024
- `config.json` — Space registry (alias → absolute path)
3125
- `schema.json` — Entity type definitions and validation rules
26+
27+
## Hooks
28+
A Stop hook runs linting, autoformatting and tests. If it reports issues related to change you made, address them.

biome.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@
1111
"formatter": {
1212
"enabled": true,
1313
"indentStyle": "space",
14-
"indentWidth": 2
14+
"indentWidth": 2,
15+
"lineWidth": 120
1516
},
1617
"linter": {
1718
"enabled": true,
1819
"rules": {
1920
"recommended": true,
20-
"style": {}
21+
"style": {
22+
"noNonNullAssertion": "off"
23+
}
2124
}
2225
},
2326
"javascript": {

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
"scripts": {
1111
"validate": "bun run src/index.ts validate",
1212
"diagram": "bun run src/index.ts diagram",
13-
"test": "bun test"
13+
"test": "bun test",
14+
"lint": "biome check src/ tests/",
15+
"lint:fix": "biome check --write src/ tests/"
1416
},
1517
"devDependencies": {
1618
"@biomejs/biome": "^2.4.4",

src/config.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { readFileSync, writeFileSync, existsSync } from 'fs';
2-
import { join } from 'path';
1+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2+
import { join } from 'node:path';
33
import Ajv from 'ajv';
44

55
const CONFIG_SCHEMA = {
@@ -63,13 +63,13 @@ export function loadConfig(): Config {
6363

6464
/** Resolve alias-or-path to a filesystem path. Falls through if not an alias. */
6565
export function resolveSpacePath(aliasOrPath: string, config: Config): string {
66-
const space = config.spaces.find(s => s.alias === aliasOrPath);
66+
const space = config.spaces.find((s) => s.alias === aliasOrPath);
6767
return space ? space.path : aliasOrPath;
6868
}
6969

7070
/** Get the full space config entry by alias. Throws if not found. */
7171
export function getSpaceConfig(alias: string, config: Config): SpaceConfig {
72-
const space = config.spaces.find(s => s.alias === alias);
72+
const space = config.spaces.find((s) => s.alias === alias);
7373
if (!space) {
7474
throw new Error(`Unknown space config: "${alias}". Check config.json.`);
7575
}
@@ -96,5 +96,5 @@ export function updateSpaceField(alias: string, field: keyof SpaceConfig, value:
9696
const config = loadConfig();
9797
const space = getSpaceConfig(alias, config);
9898
space[field] = value;
99-
writeFileSync(configPath(), JSON.stringify(config, null, 2) + '\n');
99+
writeFileSync(configPath(), `${JSON.stringify(config, null, 2)}\n`);
100100
}

src/diagram.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { readFileSync, writeFileSync, statSync } from 'fs';
1+
import { readFileSync, statSync, writeFileSync } from 'node:fs';
22
import Ajv from 'ajv';
3-
import { readSpace } from './read-space.js';
43
import { readOstPage } from './read-ost-page.js';
4+
import { readSpace } from './read-space.js';
55
import type { OstNode } from './types.js';
66

77
interface DiagramNode {
@@ -42,9 +42,7 @@ export async function diagram(path: string, options: { schema: string; output?:
4242
continue;
4343
}
4444

45-
const parent = node.data.parent
46-
? parseWikilink(node.data.parent as string)
47-
: undefined;
45+
const parent = node.data.parent ? parseWikilink(node.data.parent as string) : undefined;
4846

4947
nodes.push({
5048
id: node.data.title as string,
@@ -65,8 +63,8 @@ export async function diagram(path: string, options: { schema: string; output?:
6563
let mmd = 'graph TD\n';
6664

6765
// Find roots (no parent) and orphans
68-
const roots = nodes.filter(n => !n.parent);
69-
const orphans = roots.filter(n => n.type !== 'vision');
66+
const roots = nodes.filter((n) => !n.parent);
67+
const orphans = roots.filter((n) => n.type !== 'vision');
7068

7169
// Add styling
7270
mmd += ' classDef vision fill:#ff9999,stroke:#ff0000,stroke-width:2px\n';
@@ -125,6 +123,6 @@ export async function diagram(path: string, options: { schema: string; output?:
125123
}
126124
if (invalid.length > 0) {
127125
console.error(` Invalid (skipped): ${invalid.length}`);
128-
invalid.forEach(f => console.error(` ${f}`));
126+
for (const f of invalid) console.error(` ${f}`);
129127
}
130128
}

src/dump.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { statSync } from 'fs';
2-
import { readSpace } from './read-space.js';
1+
import { statSync } from 'node:fs';
32
import { readOstPage } from './read-ost-page.js';
3+
import { readSpace } from './read-space.js';
44

55
export async function dump(path: string) {
66
if (statSync(path).isFile()) {

src/index.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#!/usr/bin/env bun
22
import { Command } from 'commander';
3-
import { validate } from './validate.js';
3+
import { loadConfig, resolveSchema, resolveSpacePath, resolveTemplateDir } from './config.js';
44
import { diagram } from './diagram.js';
5-
import { show } from './show.js';
65
import { dump } from './dump.js';
7-
import { templateSync } from './template-sync.js';
86
import { miroSync } from './miro/sync.js';
9-
import { loadConfig, resolveSchema, resolveSpacePath, resolveTemplateDir } from './config.js';
7+
import { show } from './show.js';
8+
import { templateSync } from './template-sync.js';
9+
import { validate } from './validate.js';
1010

1111
const program = new Command();
1212

@@ -22,7 +22,10 @@ program
2222
.option('-s, --schema <path>', 'Path to JSON schema file')
2323
.action((spaceOrDir, options) => {
2424
const config = loadConfig();
25-
validate(resolveSpacePath(spaceOrDir, config), { ...options, schema: resolveSchema(options.schema, config) });
25+
validate(resolveSpacePath(spaceOrDir, config), {
26+
...options,
27+
schema: resolveSchema(options.schema, config),
28+
});
2629
});
2730

2831
program
@@ -33,7 +36,10 @@ program
3336
.option('-s, --schema <path>', 'Path to JSON schema file')
3437
.action((spaceOrDir, options) => {
3538
const config = loadConfig();
36-
diagram(resolveSpacePath(spaceOrDir, config), { ...options, schema: resolveSchema(options.schema, config) });
39+
diagram(resolveSpacePath(spaceOrDir, config), {
40+
...options,
41+
schema: resolveSchema(options.schema, config),
42+
});
3743
});
3844

3945
program
@@ -65,7 +71,10 @@ program
6571
.option('--dry-run', 'Preview changes without writing files')
6672
.action((templateDir, options) => {
6773
const config = loadConfig();
68-
templateSync(resolveTemplateDir(templateDir, config), { ...options, schema: resolveSchema(options.schema, config) });
74+
templateSync(resolveTemplateDir(templateDir, config), {
75+
...options,
76+
schema: resolveSchema(options.schema, config),
77+
});
6978
});
7079

7180
program.parse();

0 commit comments

Comments
 (0)