baldart 3.9.0 → 3.12.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 (38) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +15 -3
  3. package/VERSION +1 -1
  4. package/framework/.claude/agents/REGISTRY.md +3 -3
  5. package/framework/.claude/agents/code-reviewer.md +47 -11
  6. package/framework/.claude/agents/codebase-architect.md +8 -0
  7. package/framework/.claude/agents/ui-expert.md +355 -43
  8. package/framework/.claude/commands/design-review.md +7 -0
  9. package/framework/.claude/skills/bug/SKILL.md +5 -1
  10. package/framework/.claude/skills/context-primer/SKILL.md +4 -0
  11. package/framework/.claude/skills/design-system-init/SKILL.md +232 -0
  12. package/framework/.claude/skills/design-system-init/scripts/component-spec.template.md +79 -0
  13. package/framework/.claude/skills/frontend-design/SKILL.md +124 -0
  14. package/framework/.claude/skills/lsp-bootstrap/SKILL.md +113 -0
  15. package/framework/.claude/skills/motion-design/reference/accessibility-and-modern-apis.md +114 -0
  16. package/framework/.claude/skills/new/SKILL.md +1 -1
  17. package/framework/.claude/skills/prd/SKILL.md +3 -1
  18. package/framework/.claude/skills/simplify/SKILL.md +6 -0
  19. package/framework/.claude/skills/ui-design/SKILL.md +43 -0
  20. package/framework/agents/code-search-protocol.md +126 -0
  21. package/framework/agents/design-review.md +51 -0
  22. package/framework/agents/design-system-protocol.md +363 -0
  23. package/framework/agents/index.md +4 -0
  24. package/framework/docs/LSP-LAYER.md +405 -0
  25. package/framework/docs/PROJECT-CONFIGURATION.md +17 -0
  26. package/framework/docs/UPGRADE-3.12-UI-COHERENCE.md +268 -0
  27. package/framework/templates/baldart.config.template.yml +22 -0
  28. package/framework/templates/overlays/agents/codebase-architect.lsp-example.md +53 -0
  29. package/package.json +1 -1
  30. package/src/commands/configure.js +69 -0
  31. package/src/commands/doctor.js +55 -0
  32. package/src/utils/lsp-adapters/go.js +29 -0
  33. package/src/utils/lsp-adapters/index.js +49 -0
  34. package/src/utils/lsp-adapters/python.js +42 -0
  35. package/src/utils/lsp-adapters/ruby.js +30 -0
  36. package/src/utils/lsp-adapters/rust.js +26 -0
  37. package/src/utils/lsp-adapters/typescript.js +54 -0
  38. package/src/utils/lsp-installer.js +118 -0
@@ -0,0 +1,30 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Ruby LSP adapter — ruby-lsp (Shopify).
6
+ *
7
+ * Installed as a gem; system-mode. Users on Bundler-managed projects may
8
+ * prefer to add `gem 'ruby-lsp', group: :development` to their Gemfile and
9
+ * `bundle install` instead — we print the simple gem install command for
10
+ * portability.
11
+ */
12
+ class RubyAdapter {
13
+ constructor(cwd = process.cwd()) { this.cwd = cwd; }
14
+
15
+ get name() { return 'ruby'; }
16
+ get label() { return 'Ruby (ruby-lsp)'; }
17
+ get binary() { return 'ruby-lsp'; }
18
+ get installMode() { return 'system'; }
19
+
20
+ installCommand() { return 'gem install ruby-lsp'; }
21
+ verifyCommand() { return 'ruby-lsp --version'; }
22
+ claudePluginId() { return null; }
23
+
24
+ static detect(cwd = process.cwd()) {
25
+ const markers = ['Gemfile', '.ruby-version', 'config.ru'];
26
+ return markers.some(m => fs.existsSync(path.join(cwd, m)));
27
+ }
28
+ }
29
+
30
+ module.exports = RubyAdapter;
@@ -0,0 +1,26 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Rust LSP adapter — rust-analyzer.
6
+ *
7
+ * Installed through rustup as a toolchain component. System-mode only.
8
+ */
9
+ class RustAdapter {
10
+ constructor(cwd = process.cwd()) { this.cwd = cwd; }
11
+
12
+ get name() { return 'rust'; }
13
+ get label() { return 'Rust (rust-analyzer)'; }
14
+ get binary() { return 'rust-analyzer'; }
15
+ get installMode() { return 'system'; }
16
+
17
+ installCommand() { return 'rustup component add rust-analyzer'; }
18
+ verifyCommand() { return 'rust-analyzer --version'; }
19
+ claudePluginId() { return null; }
20
+
21
+ static detect(cwd = process.cwd()) {
22
+ return fs.existsSync(path.join(cwd, 'Cargo.toml'));
23
+ }
24
+ }
25
+
26
+ module.exports = RustAdapter;
@@ -0,0 +1,54 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * TypeScript / JavaScript LSP adapter.
6
+ *
7
+ * Language server: `typescript-language-server` (Node, npm).
8
+ * Install mode: project-local devDep — keeps the binary inside the consumer
9
+ * repo's node_modules and avoids polluting the user's global env.
10
+ *
11
+ * Detection signal: presence of `tsconfig.json`, `jsconfig.json`, or a
12
+ * `package.json` referencing typescript in deps/devDeps.
13
+ */
14
+ class TypeScriptAdapter {
15
+ constructor(cwd = process.cwd()) { this.cwd = cwd; }
16
+
17
+ get name() { return 'typescript'; }
18
+ get label() { return 'TypeScript / JavaScript'; }
19
+ get binary() { return 'typescript-language-server'; }
20
+
21
+ /** 'npm-dev' = npm devDependency, 'system' = printed install instructions. */
22
+ get installMode() { return 'npm-dev'; }
23
+
24
+ /** npm package to install when mode='npm-dev'. */
25
+ get npmPackage() { return 'typescript-language-server typescript'; }
26
+
27
+ /** Shell command the user (or installer) should run. */
28
+ installCommand() {
29
+ return 'npm install --save-dev typescript-language-server typescript';
30
+ }
31
+
32
+ /** Verify the binary is reachable (no execution side effects). */
33
+ verifyCommand() { return 'npx --no-install typescript-language-server --version'; }
34
+
35
+ /** Claude Code code-intelligence plugin id, if any. Null = use generic LSP. */
36
+ claudePluginId() { return null; }
37
+
38
+ /** Autodetect whether this language is in scope for the project. */
39
+ static detect(cwd = process.cwd()) {
40
+ const markers = ['tsconfig.json', 'jsconfig.json'];
41
+ for (const m of markers) {
42
+ if (fs.existsSync(path.join(cwd, m))) return true;
43
+ }
44
+ try {
45
+ const pkgPath = path.join(cwd, 'package.json');
46
+ if (!fs.existsSync(pkgPath)) return false;
47
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
48
+ const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
49
+ return Boolean(allDeps.typescript);
50
+ } catch { return false; }
51
+ }
52
+ }
53
+
54
+ module.exports = TypeScriptAdapter;
@@ -0,0 +1,118 @@
1
+ const { execSync } = require('child_process');
2
+ const { detectAll, getAdapter, listAdapters } = require('./lsp-adapters');
3
+ const UI = require('./ui');
4
+
5
+ /**
6
+ * High-level LSP installer used by `baldart configure` and the `lsp-bootstrap`
7
+ * skill / doctor step. Owns the install + verify side effects so the command
8
+ * layers stay thin.
9
+ *
10
+ * Contract:
11
+ * - Pure detection (no install) when only `detectLanguages` is called.
12
+ * - All mutating ops respect `--non-interactive` via the `interactive` flag.
13
+ * - Returns plain objects (no throws) so the caller can render results.
14
+ */
15
+ class LspInstaller {
16
+ constructor(cwd = process.cwd()) { this.cwd = cwd; }
17
+
18
+ /** Languages whose marker files exist under cwd. */
19
+ detectLanguages() { return detectAll(this.cwd); }
20
+
21
+ /** Adapters that BALDART knows about (regardless of detection). */
22
+ knownAdapters() { return listAdapters(); }
23
+
24
+ /**
25
+ * Build a recommendation list from detected languages. Each entry carries
26
+ * the install command and the install mode so the UI can decide whether
27
+ * to run it automatically (npm-dev) or just print it (system).
28
+ */
29
+ recommend(langs = this.detectLanguages()) {
30
+ return langs.map(name => {
31
+ const a = getAdapter(name, this.cwd);
32
+ return {
33
+ name: a.name,
34
+ label: a.label,
35
+ binary: a.binary,
36
+ installMode: a.installMode,
37
+ installCommand: a.installCommand(),
38
+ verifyCommand: a.verifyCommand(),
39
+ claudePluginId: a.claudePluginId()
40
+ };
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Install one or more language servers. Returns `{ installed, skipped, manual }`.
46
+ * - npm-dev adapters: executed in-process (npm install --save-dev …).
47
+ * - system adapters: never executed automatically; surfaced under `manual`
48
+ * so the caller can print the command for the user to run.
49
+ *
50
+ * `opts.interactive`: when true, asks confirmation per server (default true).
51
+ * `opts.dryRun`: when true, no commands are executed.
52
+ */
53
+ async installServers(serverNames, opts = {}) {
54
+ const { interactive = true, dryRun = false } = opts;
55
+ const installed = [];
56
+ const skipped = [];
57
+ const manual = [];
58
+
59
+ for (const name of serverNames) {
60
+ let a;
61
+ try { a = getAdapter(name, this.cwd); }
62
+ catch (err) { skipped.push({ name, reason: err.message }); continue; }
63
+
64
+ if (a.installMode === 'system') {
65
+ manual.push({
66
+ name: a.name,
67
+ label: a.label,
68
+ command: a.installCommand()
69
+ });
70
+ continue;
71
+ }
72
+
73
+ if (interactive) {
74
+ const ok = await UI.confirm(`Install ${a.label} via \`${a.installCommand()}\`?`, true);
75
+ if (!ok) { skipped.push({ name, reason: 'user declined' }); continue; }
76
+ }
77
+
78
+ if (dryRun) { installed.push({ name, dryRun: true }); continue; }
79
+
80
+ const spinner = UI.spinner(`Installing ${a.label}…`).start();
81
+ try {
82
+ execSync(a.installCommand(), { cwd: this.cwd, stdio: 'pipe' });
83
+ spinner.succeed(`Installed ${a.label}`);
84
+ installed.push({ name });
85
+ } catch (err) {
86
+ spinner.fail(`Failed to install ${a.label}`);
87
+ skipped.push({ name, reason: err.message.split('\n')[0] });
88
+ }
89
+ }
90
+
91
+ return { installed, skipped, manual };
92
+ }
93
+
94
+ /**
95
+ * Verify each named server's binary is reachable. Returns
96
+ * `[{ name, ok, output | error }]`. Never throws.
97
+ */
98
+ verifyServers(serverNames) {
99
+ return serverNames.map(name => {
100
+ let a;
101
+ try { a = getAdapter(name, this.cwd); }
102
+ catch (err) { return { name, ok: false, error: err.message }; }
103
+
104
+ try {
105
+ const out = execSync(a.verifyCommand(), {
106
+ cwd: this.cwd,
107
+ stdio: ['ignore', 'pipe', 'pipe'],
108
+ timeout: 8000
109
+ }).toString().trim();
110
+ return { name, ok: true, output: out };
111
+ } catch (err) {
112
+ return { name, ok: false, error: (err.message || '').split('\n')[0] };
113
+ }
114
+ });
115
+ }
116
+ }
117
+
118
+ module.exports = LspInstaller;