@elevasis/sdk 1.7.0 → 1.8.1
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/dist/cli.cjs +525 -4562
- package/dist/index.d.ts +72 -16
- package/package.json +2 -2
- package/reference/claude-config/hooks/post-edit-validate.mjs +0 -11
- package/reference/claude-config/hooks/scaffold-registry-reminder.mjs +188 -0
- package/reference/claude-config/logs/pre-edit-vibe-gate.log +17 -0
- package/reference/claude-config/logs/scaffold-registry-reminder.log +34 -0
- package/reference/claude-config/rules/active-change-index.md +80 -0
- package/reference/claude-config/rules/agent-start-here.md +254 -0
- package/reference/claude-config/rules/deployment.md +0 -1
- package/reference/claude-config/rules/observability.md +2 -2
- package/reference/claude-config/rules/operations.md +64 -0
- package/reference/claude-config/rules/organization-model.md +44 -0
- package/reference/claude-config/rules/organization-os.md +2 -4
- package/reference/claude-config/rules/task-tracking.md +4 -4
- package/reference/claude-config/rules/ui.md +202 -0
- package/reference/claude-config/rules/vibe.md +0 -8
- package/reference/claude-config/settings.json +4 -11
- package/reference/claude-config/skills/configure/SKILL.md +0 -2
- package/reference/claude-config/skills/configure/operations/features.md +10 -11
- package/reference/claude-config/skills/deploy/SKILL.md +4 -14
- package/reference/claude-config/skills/dsp/SKILL.md +2 -2
- package/reference/claude-config/skills/elevasis/SKILL.md +24 -28
- package/reference/claude-config/skills/explore/SKILL.md +5 -5
- package/reference/claude-config/skills/project/SKILL.md +33 -1
- package/reference/claude-config/skills/save/SKILL.md +8 -22
- package/reference/claude-config/skills/setup/SKILL.md +32 -16
- package/reference/claude-config/skills/status/SKILL.md +2 -3
- package/reference/claude-config/skills/submit-request/SKILL.md +1 -1
- package/reference/deployment/command-center.mdx +0 -17
- package/reference/framework/project-structure.mdx +1 -5
- package/reference/packages/ui/src/hooks/README.md +1 -2
- package/reference/scaffold/operations/propagation-pipeline.md +10 -11
- package/reference/scaffold/operations/scaffold-maintenance.md +1 -4
- package/reference/scaffold/recipes/add-a-resource.md +3 -12
- package/reference/scaffold/reference/contracts.md +1 -1
- package/reference/claude-config/hooks/__tests__/pre-edit-vibe-gate.test.mjs +0 -169
- package/reference/claude-config/hooks/pre-edit-vibe-gate.mjs +0 -128
- package/reference/claude-config/rules/docs.md +0 -26
|
@@ -30,11 +30,11 @@ Layer 3: Sync Verification (pnpm sync:verify)
|
|
|
30
30
|
|
|
31
31
|
`pnpm scaffold:sync` is the meta-script that regenerates all derived documentation and validates the output. It chains three sub-scripts:
|
|
32
32
|
|
|
33
|
-
| Script | Input | Output
|
|
34
|
-
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
-
| `generate-scaffold-contracts.js` | `packages/core/src/organization-model/types.ts`, `packages/ui/src/features/registry/types.ts`, `packages/core/src/platform/registry/types.ts` | `packages/core/src/reference/_generated/contracts.md`
|
|
36
|
-
| `generate-scaffold-feature-registry.js` | `packages/ui/src/features/*/manifest.ts`, `packages/core/src/organization-model/domains/features.ts` | `packages/ui/src/scaffold/_generated/feature-registry.md`
|
|
37
|
-
| `generate-reference-artifacts.js` | SDK manifest, navigation sources | `packages/sdk/reference/_reference-manifest.json`, `_navigation.md
|
|
33
|
+
| Script | Input | Output |
|
|
34
|
+
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
35
|
+
| `generate-scaffold-contracts.js` | `packages/core/src/organization-model/types.ts`, `packages/ui/src/features/registry/types.ts`, `packages/core/src/platform/registry/types.ts` | `packages/core/src/reference/_generated/contracts.md` |
|
|
36
|
+
| `generate-scaffold-feature-registry.js` | `packages/ui/src/features/*/manifest.ts`, `packages/core/src/organization-model/domains/features.ts` | `packages/ui/src/scaffold/_generated/feature-registry.md` |
|
|
37
|
+
| `generate-reference-artifacts.js` | SDK manifest, navigation sources | `packages/sdk/reference/_reference-manifest.json`, `_navigation.md` |
|
|
38
38
|
|
|
39
39
|
After generation, `validate-reference-artifacts.js` checks that the outputs are consistent. Exit 1 if drifted.
|
|
40
40
|
|
|
@@ -55,11 +55,11 @@ Drift is healed at the moment it would otherwise leak downstream. This is cheape
|
|
|
55
55
|
|
|
56
56
|
`/external sync` propagates the `external/_template` to downstream projects. It uses a three-tier model:
|
|
57
57
|
|
|
58
|
-
| Tier | Policy | Examples
|
|
59
|
-
| ------------------------------ | ----------------------------------------- |
|
|
58
|
+
| Tier | Policy | Examples |
|
|
59
|
+
| ------------------------------ | ----------------------------------------- | --------------------------------------------------------------------- |
|
|
60
60
|
| **Tier 1 (Infrastructure)** | Always replaced from template | Configs, shared runtime surfaces, `lib/`, `test-utils/`, entry points |
|
|
61
|
-
| **Tier 2 (Standard Features)** | Synced unless project has customized them | Standard UI features, common patterns
|
|
62
|
-
| **Tier 3 (Project-Specific)** | Never touched | `nav-items.ts`, `operations/src/`, `docs/`, `CLAUDE.md`
|
|
61
|
+
| **Tier 2 (Standard Features)** | Synced unless project has customized them | Standard UI features, common patterns |
|
|
62
|
+
| **Tier 3 (Project-Specific)** | Never touched | `nav-items.ts`, `operations/src/`, `docs/`, `CLAUDE.md` |
|
|
63
63
|
|
|
64
64
|
The sync skill doc (`.claude/skills/external/SKILL.md`) defines the full tier model and phase sequence.
|
|
65
65
|
|
|
@@ -78,8 +78,7 @@ The sync skill doc (`.claude/skills/external/SKILL.md`) defines the full tier mo
|
|
|
78
78
|
| `org-os` | Organization model exists, exports canonical symbols, imports from `@elevasis/core/organization-model`, calls `defineOrganizationModel` + `resolveOrganizationModel`, app-config references org model, `__root.tsx` uses `ElevasisFeaturesProvider` + `canonicalOrganizationModel`, `main.tsx` uses `ElevasisUIProvider`, all 3 CSS subpath imports present |
|
|
79
79
|
| `placeholders` | No unresolved `__PROJECT_SLUG__`, `__PROJECT_NAME__`, `__PROJECT_DESCRIPTION__` in key config files |
|
|
80
80
|
| `scripts` | `ui` and `operations` `package.json` have required npm scripts |
|
|
81
|
-
| `
|
|
82
|
-
| `lib` | `ui/src/lib/`, `lib/`, `test-utils/` exist with minimum file counts |
|
|
81
|
+
| `lib` | `ui/src/lib/`, `lib/`, `test-utils/` exist with minimum file counts |
|
|
83
82
|
| `tier3` | `nav-items.ts` has project-specific customization (not overwritten by template) |
|
|
84
83
|
| `conflicts` | No merge conflict markers in source files |
|
|
85
84
|
| `git` | Working tree is clean |
|
|
@@ -71,7 +71,6 @@ Generated by `scripts/monorepo/generate-reference-artifacts.js`:
|
|
|
71
71
|
|
|
72
72
|
- `packages/sdk/reference/_reference-manifest.json` -- machine-readable catalog of all reference entries
|
|
73
73
|
- `packages/sdk/reference/_navigation.md` -- navigation table
|
|
74
|
-
- `external/_template/docs/platform-navigation-map.md` -- cross-package reference map for external projects
|
|
75
74
|
|
|
76
75
|
### Running Generators
|
|
77
76
|
|
|
@@ -120,6 +119,4 @@ The output lands in `packages/sdk/reference/` which is included in the npm packa
|
|
|
120
119
|
|
|
121
120
|
## Freshness Validation
|
|
122
121
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
At the monorepo level, `pnpm scaffold:sync` followed by `pnpm sync:verify` confirms that all artifacts are current and all downstream projects are consistent.
|
|
122
|
+
At the monorepo level, `pnpm scaffold:sync` followed by `pnpm sync:verify` confirms that all artifacts are current and all downstream projects are consistent. The SDK no longer ships doc validation because external projects no longer carry a `docs/` tree.
|
|
@@ -109,7 +109,7 @@ const org: DeploymentSpec = {
|
|
|
109
109
|
}
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
Full relationship and checkpoint types are defined in `@elevasis/sdk` (`DeploymentSpec`).
|
|
112
|
+
Full relationship and checkpoint types are defined in `@elevasis/sdk` (`DeploymentSpec`). Resources are discovered live via `pnpm elevasis-sdk project:list` or by globbing `operations/resources/**` directly.
|
|
113
113
|
|
|
114
114
|
---
|
|
115
115
|
|
|
@@ -133,18 +133,9 @@ See `OrganizationModelResourceMapping` in [contracts.md](../reference/contracts.
|
|
|
133
133
|
|
|
134
134
|
---
|
|
135
135
|
|
|
136
|
-
## 5.
|
|
136
|
+
## 5. Verify resource inventory
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
pnpm exec elevasis-sdk generate-resources
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Open `docs/resources.md` and confirm:
|
|
143
|
-
|
|
144
|
-
- Section 1 (Workflows table) lists the new resource with correct `resourceId`, `version`, and `status`.
|
|
145
|
-
- Section 2 (Topology Relationships) shows any declared `relationships` edges.
|
|
146
|
-
- Sections 3-6 (Triggers, Integrations, Human Checkpoints, External Resources) show any relevant declarations.
|
|
147
|
-
- Section 7 (Schema Signatures) includes the new resource's input/output field list.
|
|
138
|
+
Resources are discovered live via `pnpm elevasis-sdk project:list` or by globbing `operations/resources/**` directly. Confirm the new resource appears with the correct `resourceId`, `version`, and `status`.
|
|
148
139
|
|
|
149
140
|
---
|
|
150
141
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Auto-generated on 2026-04-
|
|
1
|
+
<!-- Auto-generated on 2026-04-21T08:23:05.375Z by scripts/monorepo/generate-scaffold-contracts.js -->
|
|
2
2
|
---
|
|
3
3
|
title: Reference Contracts
|
|
4
4
|
description: Auto-generated TypeScript contracts for SDK consumers. Do not edit manually.
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// pre-edit-vibe-gate.test.mjs
|
|
3
|
-
// Tests for the pre-edit-vibe-gate hook.
|
|
4
|
-
// Run with: node --test .claude/hooks/__tests__/pre-edit-vibe-gate.test.mjs
|
|
5
|
-
//
|
|
6
|
-
// Tests the exported `isProtectedPath` helper directly (pure logic, no stdin/stdout
|
|
7
|
-
// mocking needed). Integration cases simulate the full hook by spawning it as a
|
|
8
|
-
// child process.
|
|
9
|
-
|
|
10
|
-
import { describe, it } from 'node:test'
|
|
11
|
-
import assert from 'node:assert/strict'
|
|
12
|
-
import { spawnSync } from 'node:child_process'
|
|
13
|
-
import { resolve, dirname } from 'node:path'
|
|
14
|
-
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
15
|
-
|
|
16
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
17
|
-
const HOOK_PATH = resolve(__dirname, '..', 'pre-edit-vibe-gate.mjs')
|
|
18
|
-
|
|
19
|
-
// Import the exported helper for unit-level tests.
|
|
20
|
-
// Using a dynamic import so the module's top-level await (stdin read) doesn't
|
|
21
|
-
// execute — the hook guards that behind the stdin loop which only runs when
|
|
22
|
-
// there is actual input; unit tests call the exported function directly.
|
|
23
|
-
// On Windows, absolute paths must be converted to file:// URLs for ESM imports.
|
|
24
|
-
const { isProtectedPath } = await import(pathToFileURL(HOOK_PATH).href)
|
|
25
|
-
|
|
26
|
-
// Template root = 3 levels up from __tests__/
|
|
27
|
-
const TEMPLATE_ROOT = resolve(__dirname, '..', '..', '..')
|
|
28
|
-
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
// Helper: run the hook as a subprocess with a synthetic PreToolUse event.
|
|
31
|
-
// filePath may be relative (to template root) or already absolute.
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
function runHook(toolName, filePath, env = {}) {
|
|
34
|
-
// Claude always sends absolute paths to hooks. Resolve relative paths
|
|
35
|
-
// against the template root so toRelative() inside the hook works correctly.
|
|
36
|
-
const absFilePath =
|
|
37
|
-
filePath.startsWith('/') || /^[A-Za-z]:/.test(filePath) ? filePath : resolve(TEMPLATE_ROOT, filePath)
|
|
38
|
-
|
|
39
|
-
const event = JSON.stringify({
|
|
40
|
-
tool_name: toolName,
|
|
41
|
-
tool_input: { file_path: absFilePath }
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
return spawnSync(process.execPath, [HOOK_PATH], {
|
|
45
|
-
input: event,
|
|
46
|
-
encoding: 'utf-8',
|
|
47
|
-
env: {
|
|
48
|
-
...process.env,
|
|
49
|
-
// Override AFTER spreading so this always wins over any ambient value
|
|
50
|
-
CLAUDE_PROJECT_DIR: TEMPLATE_ROOT,
|
|
51
|
-
...env
|
|
52
|
-
},
|
|
53
|
-
timeout: 5000
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Unit tests — isProtectedPath (pure function, no process spawning)
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
|
|
61
|
-
describe('isProtectedPath — exact protected files', () => {
|
|
62
|
-
it('blocks organization-model.ts', () => {
|
|
63
|
-
assert.equal(isProtectedPath('foundations/config/organization-model.ts'), true)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('blocks organization-model.examples.ts', () => {
|
|
67
|
-
assert.equal(isProtectedPath('foundations/config/organization-model.examples.ts'), true)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('blocks organization-model.override.ts', () => {
|
|
71
|
-
assert.equal(isProtectedPath('foundations/config/organization-model.override.ts'), true)
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
describe('isProtectedPath — extensions glob', () => {
|
|
76
|
-
it('blocks a .ts file directly under extensions/', () => {
|
|
77
|
-
assert.equal(isProtectedPath('foundations/config/extensions/deal-ecom.ts'), true)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('blocks a .ts file in a subdirectory under extensions/', () => {
|
|
81
|
-
assert.equal(isProtectedPath('foundations/config/extensions/crm/deal-ecom.ts'), true)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('blocks the discriminated-union index.ts under extensions/', () => {
|
|
85
|
-
assert.equal(isProtectedPath('foundations/config/extensions/index.ts'), true)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('does NOT block a non-.ts file under extensions/', () => {
|
|
89
|
-
assert.equal(isProtectedPath('foundations/config/extensions/README.md'), false)
|
|
90
|
-
})
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
describe('isProtectedPath — unrelated paths always pass', () => {
|
|
94
|
-
it('allows ui/ files', () => {
|
|
95
|
-
assert.equal(isProtectedPath('ui/src/routes/__root.tsx'), false)
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('allows operations/ files', () => {
|
|
99
|
-
assert.equal(isProtectedPath('operations/src/index.ts'), false)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('allows foundations/types (not config)', () => {
|
|
103
|
-
assert.equal(isProtectedPath('foundations/types/entities.ts'), false)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('does NOT block a partial-match path that is not under foundations/config', () => {
|
|
107
|
-
// "organization-model.ts" without the full prefix should not be blocked
|
|
108
|
-
assert.equal(isProtectedPath('src/organization-model.ts'), false)
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
describe('isProtectedPath — partial path / edge cases', () => {
|
|
113
|
-
it('does not block foundations/config/ itself (no .ts suffix)', () => {
|
|
114
|
-
// The directory path should not be blocked
|
|
115
|
-
assert.equal(isProtectedPath('foundations/config/'), false)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('strips leading ./ before matching', () => {
|
|
119
|
-
assert.equal(isProtectedPath('./foundations/config/organization-model.ts'), true)
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
// Integration tests — spawn the hook with synthetic events
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
|
|
127
|
-
describe('hook subprocess — blocked without VIBE_APPROVED', () => {
|
|
128
|
-
it('exits 2 for Write to organization-model.ts', () => {
|
|
129
|
-
const result = runHook('Write', 'foundations/config/organization-model.ts')
|
|
130
|
-
assert.equal(result.status, 2, `expected exit 2, got ${result.status}`)
|
|
131
|
-
assert.ok(result.stderr.includes('BLOCKED'), 'expected BLOCKED in stderr')
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it('exits 2 for Edit to extensions/deal-ecom.ts', () => {
|
|
135
|
-
const result = runHook('Edit', 'foundations/config/extensions/deal-ecom.ts')
|
|
136
|
-
assert.equal(result.status, 2)
|
|
137
|
-
assert.ok(result.stderr.includes('BLOCKED'))
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('exits 2 for MultiEdit to organization-model.override.ts', () => {
|
|
141
|
-
const result = runHook('MultiEdit', 'foundations/config/organization-model.override.ts')
|
|
142
|
-
assert.equal(result.status, 2)
|
|
143
|
-
assert.ok(result.stderr.includes('BLOCKED'))
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
describe('hook subprocess — passes with VIBE_APPROVED=1', () => {
|
|
148
|
-
it('exits 0 for Write to organization-model.ts when VIBE_APPROVED=1', () => {
|
|
149
|
-
const result = runHook('Write', 'foundations/config/organization-model.ts', { VIBE_APPROVED: '1' })
|
|
150
|
-
assert.equal(result.status, 0, `expected exit 0, got ${result.status}`)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('exits 0 for Edit to extensions/deal-ecom.ts when VIBE_APPROVED=1', () => {
|
|
154
|
-
const result = runHook('Edit', 'foundations/config/extensions/deal-ecom.ts', { VIBE_APPROVED: '1' })
|
|
155
|
-
assert.equal(result.status, 0)
|
|
156
|
-
})
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
describe('hook subprocess — unrelated path always passes', () => {
|
|
160
|
-
it('exits 0 for Write to ui/src/routes/__root.tsx without VIBE_APPROVED', () => {
|
|
161
|
-
const result = runHook('Write', 'ui/src/routes/__root.tsx')
|
|
162
|
-
assert.equal(result.status, 0)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('exits 0 for Edit to operations/src/index.ts without VIBE_APPROVED', () => {
|
|
166
|
-
const result = runHook('Edit', 'operations/src/index.ts')
|
|
167
|
-
assert.equal(result.status, 0)
|
|
168
|
-
})
|
|
169
|
-
})
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// pre-edit-vibe-gate.mjs
|
|
3
|
-
// PreToolUse backstop — blocks raw writes to protected foundations/config paths
|
|
4
|
-
// unless the VIBE_APPROVED=1 trust marker is present.
|
|
5
|
-
//
|
|
6
|
-
// Protected paths (relative to project root):
|
|
7
|
-
// foundations/config/organization-model.ts
|
|
8
|
-
// foundations/config/organization-model.examples.ts
|
|
9
|
-
// foundations/config/organization-model.override.ts
|
|
10
|
-
// foundations/config/extensions/**/*.ts
|
|
11
|
-
//
|
|
12
|
-
// To allow a write: set VIBE_APPROVED=1 in the environment before invoking the
|
|
13
|
-
// agent, or run /configure which sets the marker automatically after user
|
|
14
|
-
// confirmation.
|
|
15
|
-
|
|
16
|
-
import { appendFileSync, mkdirSync } from 'node:fs'
|
|
17
|
-
import { normalize, resolve, relative, sep } from 'node:path'
|
|
18
|
-
import { fileURLToPath } from 'node:url'
|
|
19
|
-
|
|
20
|
-
const ROOT = normalize(process.env.CLAUDE_PROJECT_DIR ?? process.cwd())
|
|
21
|
-
const LOG_DIR = ROOT + sep + '.claude' + sep + 'logs'
|
|
22
|
-
const LOG_FILE = LOG_DIR + sep + 'pre-edit-vibe-gate.log'
|
|
23
|
-
|
|
24
|
-
function log(msg) {
|
|
25
|
-
try {
|
|
26
|
-
mkdirSync(LOG_DIR, { recursive: true })
|
|
27
|
-
appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`)
|
|
28
|
-
} catch {
|
|
29
|
-
// Never crash on log failure
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Normalise an incoming file path (may be absolute or project-relative) to a
|
|
35
|
-
* forward-slash relative path from ROOT for consistent matching.
|
|
36
|
-
*/
|
|
37
|
-
function toRelative(filePath) {
|
|
38
|
-
const abs = normalize(resolve(filePath))
|
|
39
|
-
// relative() returns OS-separator paths — normalise to forward slashes
|
|
40
|
-
return relative(ROOT, abs).replace(/\\/g, '/')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Returns true if `rel` (forward-slash relative path from root) matches one of
|
|
45
|
-
* the protected path patterns. No external deps — uses simple string checks.
|
|
46
|
-
*
|
|
47
|
-
* Protected patterns:
|
|
48
|
-
* foundations/config/organization-model.ts (exact)
|
|
49
|
-
* foundations/config/organization-model.examples.ts (exact)
|
|
50
|
-
* foundations/config/organization-model.override.ts (exact)
|
|
51
|
-
* foundations/config/extensions/**\/*.ts (glob — any .ts under extensions/)
|
|
52
|
-
*/
|
|
53
|
-
export function isProtectedPath(rel) {
|
|
54
|
-
// Normalise away any leading ./
|
|
55
|
-
const r = rel.replace(/^\.\//, '')
|
|
56
|
-
|
|
57
|
-
// Exact protected files
|
|
58
|
-
const EXACT = [
|
|
59
|
-
'foundations/config/organization-model.ts',
|
|
60
|
-
'foundations/config/organization-model.examples.ts',
|
|
61
|
-
'foundations/config/organization-model.override.ts'
|
|
62
|
-
]
|
|
63
|
-
if (EXACT.includes(r)) return true
|
|
64
|
-
|
|
65
|
-
// Glob: foundations/config/extensions/**/*.ts
|
|
66
|
-
// Must start with the prefix, end with .ts, and have at least one path segment
|
|
67
|
-
// after the extensions/ directory.
|
|
68
|
-
const EXT_PREFIX = 'foundations/config/extensions/'
|
|
69
|
-
if (r.startsWith(EXT_PREFIX) && r.endsWith('.ts')) {
|
|
70
|
-
// Ensure there is at least one filename after the prefix (not just the dir itself)
|
|
71
|
-
const remainder = r.slice(EXT_PREFIX.length)
|
|
72
|
-
if (remainder.length > 3) return true // at least "x.ts"
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return false
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const BLOCK_MESSAGE =
|
|
79
|
-
'BLOCKED: This file is protected by the vibe gate.\n' +
|
|
80
|
-
'WHY: Direct edits to foundations/config/organization-model.ts and\n' +
|
|
81
|
-
' foundations/config/extensions/*.ts bypass the ambient vibe ceremony\n' +
|
|
82
|
-
' (confirm + /configure write + pnpm check-types).\n' +
|
|
83
|
-
'FIX: Run /configure to codify changes with proper ceremony (sets VIBE_APPROVED=1\n' +
|
|
84
|
-
' automatically), or set VIBE_APPROVED=1 explicitly if you are a power user\n' +
|
|
85
|
-
' who has already completed the ceremony manually.'
|
|
86
|
-
|
|
87
|
-
// Only run the stdin-reading main loop when this file is the entry point,
|
|
88
|
-
// not when it is imported as a module (e.g., by tests).
|
|
89
|
-
const isMain =
|
|
90
|
-
process.argv[1] != null && normalize(fileURLToPath(import.meta.url)) === normalize(resolve(process.argv[1]))
|
|
91
|
-
|
|
92
|
-
if (isMain) {
|
|
93
|
-
try {
|
|
94
|
-
const chunks = []
|
|
95
|
-
for await (const chunk of process.stdin) chunks.push(chunk)
|
|
96
|
-
const input = JSON.parse(Buffer.concat(chunks).toString())
|
|
97
|
-
|
|
98
|
-
const toolName = input.tool_name ?? ''
|
|
99
|
-
const filePath = input.tool_input?.file_path ?? ''
|
|
100
|
-
|
|
101
|
-
// Only gate Write, Edit, MultiEdit
|
|
102
|
-
if (!['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
103
|
-
log(`SKIP tool=${toolName} (not a write tool)`)
|
|
104
|
-
process.exit(0)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!filePath) {
|
|
108
|
-
log(`SKIP tool=${toolName} no file_path in input`)
|
|
109
|
-
process.exit(0)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const rel = toRelative(filePath)
|
|
113
|
-
const protected_ = isProtectedPath(rel)
|
|
114
|
-
const approved = process.env.VIBE_APPROVED === '1'
|
|
115
|
-
const decision = protected_ && !approved ? 'BLOCK' : 'ALLOW'
|
|
116
|
-
|
|
117
|
-
log(`tool=${toolName} path=${rel} protected=${protected_} VIBE_APPROVED=${approved} decision=${decision}`)
|
|
118
|
-
|
|
119
|
-
if (decision === 'BLOCK') {
|
|
120
|
-
process.stderr.write(BLOCK_MESSAGE)
|
|
121
|
-
process.exit(2)
|
|
122
|
-
}
|
|
123
|
-
} catch (err) {
|
|
124
|
-
log(`ERROR: ${err?.message ?? String(err)}`)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
process.exit(0)
|
|
128
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Documentation conventions -- structure, frontmatter, /save integration, auto-generated files
|
|
3
|
-
paths:
|
|
4
|
-
- docs/**
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Documentation
|
|
8
|
-
|
|
9
|
-
## Safety Invariants
|
|
10
|
-
|
|
11
|
-
- `docs/index.md` and `docs/resources.md` are auto-generated by the SDK CLI -- never edit manually
|
|
12
|
-
- Regenerate: `pnpm exec elevasis-sdk generate-docs` / `pnpm exec elevasis-sdk generate-resources`
|
|
13
|
-
- Validate: `pnpm exec elevasis-sdk validate-docs` (or `pnpm check:autogenerated-docs`)
|
|
14
|
-
- Every `.md` file in `docs/` MUST have `title` and `description` frontmatter -- docs without it are skipped by the index generator
|
|
15
|
-
- In-progress docs additionally require `status: planned | in-progress | complete`
|
|
16
|
-
|
|
17
|
-
## Structure
|
|
18
|
-
|
|
19
|
-
- All docs are Markdown (`.md`), not MDX -- external projects don't use Fumadocs
|
|
20
|
-
- UI features → `docs/ui/`, platform features → `docs/operations/`, knowledge → `docs/knowledge/`
|
|
21
|
-
- New subdirectories under `docs/` are automatically discovered by the index generator
|
|
22
|
-
|
|
23
|
-
## Detailed Reference
|
|
24
|
-
|
|
25
|
-
- `docs/index.md` -- auto-generated navigation hub (full doc map)
|
|
26
|
-
- `docs/agent-start-here.md` -- canonical agent entrypoint and task-routing guidance
|