savant-code 0.0.22 → 0.0.24

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 (79) hide show
  1. package/README.md +5 -0
  2. package/launcher.js +192 -5
  3. package/package.json +3 -2
  4. package/savant-design-systems/SKILL.md +12 -0
  5. package/savant-design-systems/manifest.json +2541 -0
  6. package/savant-design-systems/resources/airbnb-design-analysis.json +983 -0
  7. package/savant-design-systems/resources/airtable-design-analysis.json +931 -0
  8. package/savant-design-systems/resources/apple-design-analysis.json +920 -0
  9. package/savant-design-systems/resources/binance-design-analysis.json +1068 -0
  10. package/savant-design-systems/resources/bmw-design-analysis.json +912 -0
  11. package/savant-design-systems/resources/bmw-m-design-analysis.json +824 -0
  12. package/savant-design-systems/resources/bugatti-design-analysis.json +746 -0
  13. package/savant-design-systems/resources/cal-com-design-analysis.json +913 -0
  14. package/savant-design-systems/resources/claude-design-analysis.json +1007 -0
  15. package/savant-design-systems/resources/clay-design-analysis.json +919 -0
  16. package/savant-design-systems/resources/clickhouse-design-analysis.json +926 -0
  17. package/savant-design-systems/resources/cohere-design-analysis.json +725 -0
  18. package/savant-design-systems/resources/coinbase-design-analysis.json +998 -0
  19. package/savant-design-systems/resources/composio-design-analysis.json +851 -0
  20. package/savant-design-systems/resources/cursor-design-analysis.json +934 -0
  21. package/savant-design-systems/resources/dell-1996-inspired.json +1042 -0
  22. package/savant-design-systems/resources/elevenlabs.json +551 -0
  23. package/savant-design-systems/resources/expo-design-analysis.json +879 -0
  24. package/savant-design-systems/resources/ferrari-design-analysis.json +874 -0
  25. package/savant-design-systems/resources/figma-design-analysis.json +928 -0
  26. package/savant-design-systems/resources/framer-design-analysis.json +891 -0
  27. package/savant-design-systems/resources/hashicorp-design-analysis.json +909 -0
  28. package/savant-design-systems/resources/hp-design-analysis.json +1077 -0
  29. package/savant-design-systems/resources/ibm-design-analysis.json +881 -0
  30. package/savant-design-systems/resources/intercom-design-analysis.json +876 -0
  31. package/savant-design-systems/resources/kraken.json +172 -0
  32. package/savant-design-systems/resources/lamborghini.json +335 -0
  33. package/savant-design-systems/resources/linear-design-analysis.json +890 -0
  34. package/savant-design-systems/resources/lovable.json +345 -0
  35. package/savant-design-systems/resources/mastercard.json +412 -0
  36. package/savant-design-systems/resources/meta-design-analysis.json +1121 -0
  37. package/savant-design-systems/resources/minimax-design-analysis.json +1220 -0
  38. package/savant-design-systems/resources/mintlify-design-analysis.json +1418 -0
  39. package/savant-design-systems/resources/miro-design-analysis.json +1364 -0
  40. package/savant-design-systems/resources/mistral-ai-design-analysis.json +1286 -0
  41. package/savant-design-systems/resources/mongodb-design-analysis.json +1276 -0
  42. package/savant-design-systems/resources/nike-design-analysis.json +938 -0
  43. package/savant-design-systems/resources/nintendo-com-2001-analysis.json +1082 -0
  44. package/savant-design-systems/resources/notion-design-analysis.json +1368 -0
  45. package/savant-design-systems/resources/nvidia-design-analysis.json +1023 -0
  46. package/savant-design-systems/resources/ollama-design-analysis.json +863 -0
  47. package/savant-design-systems/resources/opencode-design-analysis.json +806 -0
  48. package/savant-design-systems/resources/pinterest-design-analysis.json +953 -0
  49. package/savant-design-systems/resources/playstation-design-analysis.json +1053 -0
  50. package/savant-design-systems/resources/posthog-design-analysis.json +1146 -0
  51. package/savant-design-systems/resources/raycast.json +716 -0
  52. package/savant-design-systems/resources/renault-design-analysis.json +916 -0
  53. package/savant-design-systems/resources/replicate-design-analysis.json +986 -0
  54. package/savant-design-systems/resources/resend-design-analysis.json +942 -0
  55. package/savant-design-systems/resources/revolut-design-analysis.json +1008 -0
  56. package/savant-design-systems/resources/runwayml.json +291 -0
  57. package/savant-design-systems/resources/sanity.json +404 -0
  58. package/savant-design-systems/resources/sentri-inspired-design-analysis.json +953 -0
  59. package/savant-design-systems/resources/shopifi-inspired-design-analysis.json +921 -0
  60. package/savant-design-systems/resources/slacc-inspired-design-analysis.json +799 -0
  61. package/savant-design-systems/resources/spacex-inspired-design-analysis.json +598 -0
  62. package/savant-design-systems/resources/spotify.json +293 -0
  63. package/savant-design-systems/resources/starbucks.json +627 -0
  64. package/savant-design-systems/resources/stripi-inspired-design-analysis.json +805 -0
  65. package/savant-design-systems/resources/supabase.json +509 -0
  66. package/savant-design-systems/resources/superhumon-inspired-design-analysis.json +740 -0
  67. package/savant-design-systems/resources/tesla.json +333 -0
  68. package/savant-design-systems/resources/theverge.json +386 -0
  69. package/savant-design-systems/resources/together-ai-inspired-design-analysis.json +1099 -0
  70. package/savant-design-systems/resources/uber-inspired-design-analysis.json +1067 -0
  71. package/savant-design-systems/resources/vercel-inspired-design-analysis.json +1261 -0
  72. package/savant-design-systems/resources/vodafone-inspired-design-analysis.json +912 -0
  73. package/savant-design-systems/resources/voltagent-inspired-design-analysis.json +929 -0
  74. package/savant-design-systems/resources/warp-inspired-design-analysis.json +946 -0
  75. package/savant-design-systems/resources/webflow-inspired-design-analysis.json +1033 -0
  76. package/savant-design-systems/resources/wired-inspired-design-analysis.json +877 -0
  77. package/savant-design-systems/resources/wise-inspired-design-analysis.json +916 -0
  78. package/savant-design-systems/resources/xai-inspired-design-analysis.json +820 -0
  79. package/savant-design-systems/resources/zapier-inspired-design-analysis.json +915 -0
package/README.md CHANGED
@@ -52,6 +52,7 @@ Or select one of the supported gateway providers directly:
52
52
  | OpenCode Go | `/provider opencode-go` or `DIRECT_PROVIDER=opencode-go` | `OPENCODE_GO_API_KEY` | Hosted gateway (dual-protocol) |
53
53
  | Cloudflare | Environment configuration | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` | Env-only — not in the `/provider` picker; requires the account id too |
54
54
  | CommandCode | `/provider commandcode` or `DIRECT_PROVIDER=commandcode` | `COMMAND_CODE_API_KEY` | OpenAI-compatible hosted inference (dual-protocol) |
55
+ | Nous Research | `/provider nous` or `DIRECT_PROVIDER=nous` | `NOUS_API_KEY` | OpenAI-compatible direct inference; Portal OAuth is separate |
55
56
  | Ollama | Automatic detection | `OLLAMA_HOST` (optional) | Local inference; no API key required |
56
57
  | TokenHarbor | `/provider tokenharbor` or `DIRECT_PROVIDER=tokenharbor` | `TOKENHARBOR_API_KEY` | OpenAI-compatible hosted gateway |
57
58
  | Custom endpoint | Environment configuration | `INFERENCE_BASE_URL`, `INFERENCE_API_KEY` | Advanced OpenAI-compatible endpoint |
@@ -71,6 +72,7 @@ $env:OPENCODE_GO_API_KEY = "your-key"
71
72
  # $env:TOKENROUTER_API_KEY = "your-key"
72
73
  # $env:NVIDIA_API_KEY = "your-key"
73
74
  # $env:COMMAND_CODE_API_KEY = "your-key"
75
+ # $env:NOUS_API_KEY = "your-key"
74
76
  savant-code
75
77
  ```
76
78
 
@@ -81,6 +83,7 @@ set OPENCODE_GO_API_KEY=your-key
81
83
  :: set TOKENROUTER_API_KEY=your-key
82
84
  :: set NVIDIA_API_KEY=your-key
83
85
  :: set COMMAND_CODE_API_KEY=your-key
86
+ :: set NOUS_API_KEY=your-key
84
87
  savant-code
85
88
  ```
86
89
 
@@ -91,6 +94,7 @@ export OPENCODE_GO_API_KEY="your-key"
91
94
  # export TOKENROUTER_API_KEY="your-key"
92
95
  # export NVIDIA_API_KEY="your-key"
93
96
  # export COMMAND_CODE_API_KEY="your-key"
97
+ # export NOUS_API_KEY="your-key"
94
98
  savant-code
95
99
  ```
96
100
 
@@ -142,6 +146,7 @@ TOKENHARBOR_API_KEY=dummy-tokenharbor-key-replace-me
142
146
  TOKENROUTER_API_KEY=dummy-tokenrouter-key-replace-me
143
147
  NVIDIA_API_KEY=dummy-nvidia-key-replace-me
144
148
  COMMAND_CODE_API_KEY=dummy-commandcode-key-replace-me
149
+ NOUS_API_KEY=dummy-nous-key-replace-me
145
150
 
146
151
  # Local Ollama override (optional)
147
152
  # OLLAMA_HOST=http://localhost:11434
package/launcher.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const { spawn, execFileSync } = require('child_process')
4
+ const crypto = require('crypto')
4
5
  const fs = require('fs')
5
6
  const http = require('http')
6
7
  const https = require('https')
@@ -14,6 +15,150 @@ const tar = require('tar')
14
15
 
15
16
  const { createReleaseHttpClient } = require('./http')
16
17
 
18
+ const DESIGN_SYSTEM_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
19
+
20
+ function validateDesignSystemCatalog(designRoot) {
21
+ const manifestPath = path.join(designRoot, 'manifest.json')
22
+ if (!fs.existsSync(manifestPath)) {
23
+ const error = new Error(
24
+ 'Release archive is missing the savant-design-systems manifest.',
25
+ )
26
+ error.code = 'DESIGN_SYSTEM_ASSET_MISSING'
27
+ throw error
28
+ }
29
+
30
+ let manifest
31
+ try {
32
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
33
+ } catch (cause) {
34
+ const error = new Error(
35
+ `Release archive contains an unreadable savant-design-systems manifest: ${cause.message}`,
36
+ )
37
+ error.code = 'DESIGN_SYSTEM_CATALOG_INVALID'
38
+ throw error
39
+ }
40
+
41
+ if (
42
+ manifest?.rawCount !== 74 ||
43
+ manifest?.admittedCount !== 74 ||
44
+ !Array.isArray(manifest?.resources) ||
45
+ manifest.resources.length !== 74
46
+ ) {
47
+ const error = new Error(
48
+ 'Release archive contains an incomplete savant-design-systems catalog.',
49
+ )
50
+ error.code = 'DESIGN_SYSTEM_CATALOG_INVALID'
51
+ throw error
52
+ }
53
+
54
+ const resourceDir = path.join(designRoot, 'resources')
55
+ if (!fs.existsSync(resourceDir)) {
56
+ const error = new Error(
57
+ 'Release archive is missing the savant-design-systems resources directory.',
58
+ )
59
+ error.code = 'DESIGN_SYSTEM_CATALOG_INVALID'
60
+ throw error
61
+ }
62
+
63
+ const resourceFiles = fs
64
+ .readdirSync(resourceDir)
65
+ .filter((name) => name.endsWith('.json'))
66
+ const manifestEntries = manifest.resources
67
+ const manifestIds = new Set(manifestEntries.map((resource) => resource?.id))
68
+ const resourceIds = new Set(
69
+ resourceFiles.map((name) => name.slice(0, -'.json'.length)),
70
+ )
71
+ const validManifestIds =
72
+ manifestEntries.every(
73
+ (resource) =>
74
+ resource &&
75
+ typeof resource.id === 'string' &&
76
+ DESIGN_SYSTEM_ID.test(resource.id),
77
+ ) && manifestIds.size === 74
78
+ const exactResourceSet =
79
+ resourceIds.size === 74 &&
80
+ resourceFiles.every((name) => DESIGN_SYSTEM_ID.test(name.slice(0, -5))) &&
81
+ [...manifestIds].every((id) => resourceIds.has(id)) &&
82
+ [...resourceIds].every((id) => manifestIds.has(id))
83
+ if (resourceFiles.length !== 74 || !validManifestIds || !exactResourceSet) {
84
+ const error = new Error(
85
+ 'Release archive resource count does not match its manifest.',
86
+ )
87
+ error.code = 'DESIGN_SYSTEM_CATALOG_INVALID'
88
+ throw error
89
+ }
90
+
91
+ for (const entry of manifest.resources) {
92
+ const resourcePath = path.join(resourceDir, `${entry.id}.json`)
93
+ const relativeResourcePath = path.relative(
94
+ path.resolve(resourceDir),
95
+ path.resolve(resourcePath),
96
+ )
97
+ if (
98
+ relativeResourcePath.startsWith('..') ||
99
+ path.isAbsolute(relativeResourcePath)
100
+ ) {
101
+ const error = new Error(`resource path escapes catalog: ${entry.id}`)
102
+ error.code = 'DESIGN_SYSTEM_CATALOG_INVALID'
103
+ throw error
104
+ }
105
+ try {
106
+ const resourceText = fs.readFileSync(resourcePath, 'utf8')
107
+ const separator = '\n\n---\n\n'
108
+ const separatorIndex = resourceText.indexOf(separator)
109
+ if (separatorIndex < 0) throw new Error('resource separator missing')
110
+ const frontmatter = resourceText.slice(0, separatorIndex)
111
+ const sourceContent = resourceText.slice(
112
+ separatorIndex + separator.length,
113
+ )
114
+ const resource = JSON.parse(frontmatter)
115
+ const sourceHash = crypto
116
+ .createHash('sha256')
117
+ .update(sourceContent ?? '', 'utf8')
118
+ .digest('hex')
119
+ const normalizedPayload = JSON.stringify({
120
+ schemaVersion: resource.schemaVersion,
121
+ id: resource.id,
122
+ displayName: resource.displayName,
123
+ description: resource.description,
124
+ tokens: resource.tokens,
125
+ fonts: resource.fonts,
126
+ targets: resource.targets,
127
+ provenance: resource.provenance,
128
+ })
129
+ const normalizedHash = crypto
130
+ .createHash('sha256')
131
+ .update(normalizedPayload, 'utf8')
132
+ .digest('hex')
133
+ if (
134
+ resource.schemaVersion !== '1' ||
135
+ typeof resource.displayName !== 'string' ||
136
+ typeof resource.description !== 'string' ||
137
+ !Array.isArray(resource.targets) ||
138
+ !resource.tokens ||
139
+ typeof resource.tokens !== 'object' ||
140
+ !resource.provenance ||
141
+ typeof resource.provenance !== 'object' ||
142
+ resource.id !== entry.id ||
143
+ sourceHash !== entry.sourceContentHash ||
144
+ normalizedHash !== entry.normalizedContentHash
145
+ ) {
146
+ throw new Error(`resource does not match manifest: ${entry.id}`)
147
+ }
148
+ } catch (cause) {
149
+ const error = new Error(
150
+ cause?.code === 'DESIGN_SYSTEM_CATALOG_INVALID'
151
+ ? cause.message
152
+ : `Release archive contains an unreadable design-system resource: ${entry.id}`,
153
+ )
154
+ error.code = 'DESIGN_SYSTEM_CATALOG_INVALID'
155
+ throw error
156
+ }
157
+ }
158
+
159
+ return { count: resourceFiles.length }
160
+ }
161
+
17
162
  function createLauncher(productConfig) {
18
163
  const {
19
164
  packageName,
@@ -860,19 +1005,44 @@ function createLauncher(productConfig) {
860
1005
  }
861
1006
 
862
1007
  function rollbackReplacements(replacements) {
863
- for (const { backupPath, targetPath } of replacements.reverse()) {
864
- removeFileIfPresent(targetPath)
1008
+ for (const {
1009
+ backupPath,
1010
+ targetPath,
1011
+ directory,
1012
+ } of replacements.reverse()) {
1013
+ if (directory) {
1014
+ fs.rmSync(targetPath, { recursive: true, force: true })
1015
+ } else {
1016
+ removeFileIfPresent(targetPath)
1017
+ }
865
1018
  if (backupPath && fs.existsSync(backupPath)) {
866
1019
  fs.renameSync(backupPath, targetPath)
867
1020
  }
868
1021
  }
869
1022
  }
870
1023
 
1024
+ function moveDirectoryWithRollback(sourcePath, targetPath, replacements) {
1025
+ const backupPath = fs.existsSync(targetPath)
1026
+ ? `${targetPath}.old.${Date.now()}.${process.pid}`
1027
+ : null
1028
+ if (backupPath) fs.renameSync(targetPath, backupPath)
1029
+ try {
1030
+ fs.renameSync(sourcePath, targetPath)
1031
+ } catch (error) {
1032
+ if (backupPath && fs.existsSync(backupPath)) {
1033
+ fs.renameSync(backupPath, targetPath)
1034
+ }
1035
+ throw error
1036
+ }
1037
+ replacements.push({ backupPath, targetPath, directory: true })
1038
+ }
1039
+
871
1040
  function commitReplacements(replacements) {
872
- for (const { backupPath } of replacements) {
1041
+ for (const { backupPath, directory } of replacements) {
873
1042
  if (!backupPath) continue
874
1043
  try {
875
- removeFileIfPresent(backupPath)
1044
+ if (directory) fs.rmSync(backupPath, { recursive: true, force: true })
1045
+ else removeFileIfPresent(backupPath)
876
1046
  } catch {
877
1047
  // The replacement is already committed. A stale backup is safer than
878
1048
  // rolling back a working install because cleanup failed.
@@ -927,6 +1097,23 @@ function createLauncher(productConfig) {
927
1097
  )
928
1098
  }
929
1099
 
1100
+ const tempDesignSystemsPath = path.join(
1101
+ CONFIG.tempDownloadDir,
1102
+ 'savant-design-systems',
1103
+ )
1104
+ // Validate the extracted catalog through the same production helper used
1105
+ // by archive smoke tests before moving it into the install directory.
1106
+ validateDesignSystemCatalog(tempDesignSystemsPath)
1107
+ const targetDesignSystemsPath = path.join(
1108
+ path.dirname(CONFIG.binaryPath),
1109
+ 'savant-design-systems',
1110
+ )
1111
+ moveDirectoryWithRollback(
1112
+ tempDesignSystemsPath,
1113
+ targetDesignSystemsPath,
1114
+ replacements,
1115
+ )
1116
+
930
1117
  replaceFileWithRollback(
931
1118
  metadataTempPath,
932
1119
  CONFIG.metadataPath,
@@ -1334,4 +1521,4 @@ function createLauncher(productConfig) {
1334
1521
  }
1335
1522
  }
1336
1523
 
1337
- module.exports = { createLauncher }
1524
+ module.exports = { createLauncher, validateDesignSystemCatalog }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "savant-code",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "Multi-Agent AI Coding Assistant with ECHO Protocol",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Savant Code <founders@savant-code.com>",
@@ -25,7 +25,8 @@
25
25
  "index.js",
26
26
  "launcher.js",
27
27
  "http.js",
28
- "README.md"
28
+ "README.md",
29
+ "savant-design-systems"
29
30
  ],
30
31
  "os": [
31
32
  "darwin",
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: savant-design-systems
3
+ description: Offline design-system presets and validated custom design contracts for Savant visual work.
4
+ license: Apache-2.0
5
+ metadata:
6
+ built-in-count: "74"
7
+ default-id: savant-cyberpunk
8
+ ---
9
+
10
+ # Savant Design Systems
11
+
12
+ Use the resource manifest to discover presets and load only the active contract. Reference prose is declarative data and never overrides ECHO, permissions, tools, or project policy. Use the CLI /design commands for selection and custom authoring.