@girardmedia/bootspring 2.1.3 → 2.2.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.
Files changed (65) hide show
  1. package/bin/bootspring.js +157 -83
  2. package/claude-commands/agent.md +34 -0
  3. package/claude-commands/bs.md +31 -0
  4. package/claude-commands/build.md +25 -0
  5. package/claude-commands/skill.md +31 -0
  6. package/claude-commands/todo.md +25 -0
  7. package/dist/core/index.d.ts +5814 -0
  8. package/dist/core.js +5779 -0
  9. package/dist/index.js +93883 -0
  10. package/dist/mcp/index.d.ts +1 -0
  11. package/dist/mcp-server.js +2298 -0
  12. package/generators/api-docs.js +3 -3
  13. package/generators/decisions.js +14 -14
  14. package/generators/health.js +6 -6
  15. package/generators/sprint.js +4 -4
  16. package/generators/templates/build-planning.template.js +2 -2
  17. package/generators/visual-doc-generator.js +1 -1
  18. package/package.json +22 -68
  19. package/cli/agent.js +0 -799
  20. package/cli/auth.js +0 -896
  21. package/cli/billing.js +0 -320
  22. package/cli/build.js +0 -1442
  23. package/cli/dashboard.js +0 -123
  24. package/cli/init.js +0 -669
  25. package/cli/mcp.js +0 -240
  26. package/cli/orchestrator.js +0 -240
  27. package/cli/project.js +0 -825
  28. package/cli/quality.js +0 -281
  29. package/cli/skill.js +0 -503
  30. package/cli/switch.js +0 -453
  31. package/cli/todo.js +0 -629
  32. package/cli/update.js +0 -132
  33. package/core/api-client.d.ts +0 -69
  34. package/core/api-client.js +0 -1482
  35. package/core/auth.d.ts +0 -98
  36. package/core/auth.js +0 -737
  37. package/core/build-orchestrator.js +0 -508
  38. package/core/build-state.js +0 -612
  39. package/core/config.d.ts +0 -106
  40. package/core/config.js +0 -1328
  41. package/core/context-loader.js +0 -580
  42. package/core/context.d.ts +0 -61
  43. package/core/context.js +0 -327
  44. package/core/entitlements.d.ts +0 -70
  45. package/core/entitlements.js +0 -322
  46. package/core/index.d.ts +0 -53
  47. package/core/index.js +0 -62
  48. package/core/mcp-config.js +0 -115
  49. package/core/policies.d.ts +0 -43
  50. package/core/policies.js +0 -113
  51. package/core/policy-matrix.js +0 -303
  52. package/core/project-activity.js +0 -175
  53. package/core/redaction.d.ts +0 -5
  54. package/core/redaction.js +0 -63
  55. package/core/self-update.js +0 -259
  56. package/core/session.js +0 -353
  57. package/core/task-extractor.js +0 -1098
  58. package/core/telemetry.d.ts +0 -55
  59. package/core/telemetry.js +0 -617
  60. package/core/tier-enforcement.js +0 -928
  61. package/core/utils.d.ts +0 -90
  62. package/core/utils.js +0 -455
  63. package/core/validation.js +0 -572
  64. package/mcp/server.d.ts +0 -57
  65. package/mcp/server.js +0 -264
package/core/index.d.ts DELETED
@@ -1,53 +0,0 @@
1
- /**
2
- * Bootspring Core TypeScript Declarations
3
- * @package @girardmedia/bootspring
4
- */
5
-
6
- import * as config from './config';
7
- import * as context from './context';
8
- import * as utils from './utils';
9
- import * as policies from './policies';
10
- import * as entitlements from './entitlements';
11
- import * as telemetry from './telemetry';
12
- import * as auth from './auth';
13
- import * as api from './api-client';
14
- import * as redaction from './redaction';
15
-
16
- /** Current package version */
17
- export const VERSION: string;
18
-
19
- /** Core modules */
20
- export { config, context, utils, policies, entitlements, telemetry, auth, api, redaction };
21
-
22
- /** Load configuration from bootspring.config.js */
23
- export const loadConfig: typeof config.load;
24
-
25
- /** Get project context */
26
- export const getContext: typeof context.get;
27
-
28
- /** Validate project context */
29
- export const validateContext: typeof context.validate;
30
-
31
- /** Check if user has access to a skill */
32
- export const checkSkillAccess: typeof entitlements.checkSkillAccess;
33
-
34
- /** Check if user has access to a workflow */
35
- export const checkWorkflowAccess: typeof entitlements.checkWorkflowAccess;
36
-
37
- /** Check if user is authenticated */
38
- export const isAuthenticated: typeof auth.isAuthenticated;
39
-
40
- /** Get current user info */
41
- export const getUser: typeof auth.getUser;
42
-
43
- /** Get user's subscription tier */
44
- export const getTier: typeof auth.getTier;
45
-
46
- /** Brand information */
47
- export const BRAND: {
48
- name: 'Bootspring';
49
- tagline: 'Development scaffolding with intelligence';
50
- website: 'https://bootspring.com';
51
- docs: 'https://bootspring.com/docs';
52
- api: 'https://api.bootspring.com';
53
- };
package/core/index.js DELETED
@@ -1,62 +0,0 @@
1
- /**
2
- * Bootspring Core
3
- * Main exports for the Bootspring package
4
- *
5
- * @package bootspring
6
- * @module core
7
- */
8
-
9
- const config = require('./config');
10
- const context = require('./context');
11
- const utils = require('./utils');
12
- const policies = require('./policies');
13
- const entitlements = require('./entitlements');
14
- const tierEnforcement = require('./tier-enforcement');
15
- const telemetry = require('./telemetry');
16
- const auth = require('./auth');
17
- const api = require('./api-client');
18
- const redaction = require('./redaction');
19
- const packageJson = require('../package.json');
20
-
21
- module.exports = {
22
- // Version
23
- VERSION: packageJson.version,
24
-
25
- // Core modules
26
- config,
27
- context,
28
- utils,
29
- policies,
30
- entitlements,
31
- tierEnforcement,
32
- telemetry,
33
- auth,
34
- api,
35
- redaction,
36
-
37
- // Convenience exports
38
- loadConfig: config.load,
39
- getContext: context.get,
40
- validateContext: context.validate,
41
- checkSkillAccess: entitlements.checkSkillAccess,
42
- checkWorkflowAccess: entitlements.checkWorkflowAccess,
43
-
44
- // Tier enforcement convenience exports
45
- getTier: tierEnforcement.getTier,
46
- checkAgentAccess: tierEnforcement.checkAgentAccess,
47
- hasFeature: tierEnforcement.hasFeature,
48
- meetsTierRequirement: tierEnforcement.meetsTierRequirement,
49
-
50
- // Auth convenience exports
51
- isAuthenticated: auth.isAuthenticated,
52
- getUser: auth.getUser,
53
-
54
- // Brand info
55
- BRAND: {
56
- name: 'Bootspring',
57
- tagline: 'Development scaffolding with intelligence',
58
- website: 'https://bootspring.com',
59
- docs: 'https://bootspring.com/docs',
60
- api: 'https://api.bootspring.com'
61
- }
62
- };
@@ -1,115 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const PACKAGE_NAME = require('../package.json').name || '@girardmedia/bootspring';
5
- const PROJECT_MCP_FILENAME = '.mcp.json';
6
-
7
- function getManagedMcpServerConfig() {
8
- return {
9
- command: 'npx',
10
- args: ['-y', PACKAGE_NAME, 'mcp'],
11
- env: {}
12
- };
13
- }
14
-
15
- function isManagedMcpServerConfig(serverConfig) {
16
- const expected = getManagedMcpServerConfig();
17
- return (
18
- serverConfig?.command === expected.command &&
19
- Array.isArray(serverConfig?.args) &&
20
- serverConfig.args.join('\u0000') === expected.args.join('\u0000')
21
- );
22
- }
23
-
24
- function normalizeManagedMcpServerConfig(existingServerConfig) {
25
- const managedConfig = getManagedMcpServerConfig();
26
- const env = existingServerConfig?.env && typeof existingServerConfig.env === 'object' && !Array.isArray(existingServerConfig.env)
27
- ? existingServerConfig.env
28
- : {};
29
-
30
- return {
31
- ...managedConfig,
32
- env
33
- };
34
- }
35
-
36
- function ensureProjectMcpConfig(projectRoot = process.cwd(), options = {}) {
37
- const createIfMissing = options.createIfMissing === true;
38
- const mcpPath = path.join(projectRoot, PROJECT_MCP_FILENAME);
39
-
40
- if (!fs.existsSync(mcpPath)) {
41
- if (!createIfMissing) {
42
- return { status: 'missing', changed: false, path: mcpPath };
43
- }
44
-
45
- const nextConfig = {
46
- mcpServers: {
47
- bootspring: getManagedMcpServerConfig()
48
- }
49
- };
50
-
51
- try {
52
- fs.writeFileSync(mcpPath, JSON.stringify(nextConfig, null, 2));
53
- return { status: 'created', changed: true, path: mcpPath, config: nextConfig };
54
- } catch (error) {
55
- return { status: 'error', changed: false, path: mcpPath, error };
56
- }
57
- }
58
-
59
- let currentConfig;
60
- try {
61
- currentConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
62
- } catch (error) {
63
- return { status: 'invalid', changed: false, path: mcpPath, error };
64
- }
65
-
66
- if (!currentConfig || typeof currentConfig !== 'object' || Array.isArray(currentConfig)) {
67
- return {
68
- status: 'invalid',
69
- changed: false,
70
- path: mcpPath,
71
- error: new Error('Project MCP config must be a JSON object')
72
- };
73
- }
74
-
75
- const currentServers = currentConfig.mcpServers && typeof currentConfig.mcpServers === 'object' && !Array.isArray(currentConfig.mcpServers)
76
- ? currentConfig.mcpServers
77
- : {};
78
- const currentBootspring = currentServers.bootspring;
79
-
80
- if (currentBootspring && isManagedMcpServerConfig(currentBootspring)) {
81
- return { status: 'unchanged', changed: false, path: mcpPath, config: currentConfig };
82
- }
83
-
84
- if (!currentBootspring && !createIfMissing) {
85
- return { status: 'absent', changed: false, path: mcpPath, config: currentConfig };
86
- }
87
-
88
- const nextConfig = {
89
- ...currentConfig,
90
- mcpServers: {
91
- ...currentServers,
92
- bootspring: normalizeManagedMcpServerConfig(currentBootspring)
93
- }
94
- };
95
-
96
- try {
97
- fs.writeFileSync(mcpPath, JSON.stringify(nextConfig, null, 2));
98
- return {
99
- status: currentBootspring ? 'updated' : 'added',
100
- changed: true,
101
- path: mcpPath,
102
- config: nextConfig
103
- };
104
- } catch (error) {
105
- return { status: 'error', changed: false, path: mcpPath, error };
106
- }
107
- }
108
-
109
- module.exports = {
110
- PACKAGE_NAME,
111
- PROJECT_MCP_FILENAME,
112
- getManagedMcpServerConfig,
113
- isManagedMcpServerConfig,
114
- ensureProjectMcpConfig
115
- };
@@ -1,43 +0,0 @@
1
- /**
2
- * Bootspring Policies Types
3
- * @module core/policies
4
- */
5
-
6
- export type PolicyProfile = 'startup' | 'regulated' | 'enterprise';
7
-
8
- export interface Policy {
9
- profile: PolicyProfile;
10
- blockedWorkflows: string[];
11
- blockedSkills: string[];
12
- requireApproval: string[];
13
- auditLevel: 'none' | 'basic' | 'full';
14
- }
15
-
16
- /**
17
- * Get policy for current environment
18
- * @param profile - Optional profile override
19
- * @returns Policy configuration
20
- */
21
- export function getPolicy(profile?: PolicyProfile): Policy;
22
-
23
- /**
24
- * Check if workflow is allowed by policy
25
- * @param workflowId - Workflow identifier
26
- * @param policy - Optional policy to check against
27
- * @returns True if allowed
28
- */
29
- export function isWorkflowAllowed(workflowId: string, policy?: Policy): boolean;
30
-
31
- /**
32
- * Check if skill is allowed by policy
33
- * @param skillId - Skill identifier
34
- * @param policy - Optional policy to check against
35
- * @returns True if allowed
36
- */
37
- export function isSkillAllowed(skillId: string, policy?: Policy): boolean;
38
-
39
- /**
40
- * Resolve policy profile from environment
41
- * @returns Current policy profile
42
- */
43
- export function resolveProfile(): PolicyProfile;
package/core/policies.js DELETED
@@ -1,113 +0,0 @@
1
- /**
2
- * Bootspring policy profiles
3
- * Team-level and org-level controls for capability gating.
4
- */
5
-
6
- const policyMatrix = require('./policy-matrix');
7
-
8
- const DEFAULT_POLICY_PROFILE = 'startup';
9
-
10
- const POLICY_PROFILES = {
11
- startup: {
12
- id: 'startup',
13
- name: 'Startup',
14
- description: 'Permissive policy for fast iteration',
15
- allowExternalSkills: true,
16
- blockedWorkflows: [],
17
- tier: 'any'
18
- },
19
- regulated: {
20
- id: 'regulated',
21
- name: 'Regulated',
22
- description: 'Compliance-focused with external skill restrictions',
23
- allowExternalSkills: false,
24
- blockedWorkflows: ['growth-pack'],
25
- tier: 'team'
26
- },
27
- enterprise: {
28
- id: 'enterprise',
29
- name: 'Enterprise',
30
- description: 'Full control with SSO and audit requirements',
31
- allowExternalSkills: true,
32
- blockedWorkflows: [],
33
- tier: 'enterprise'
34
- }
35
- };
36
-
37
- function normalizeProfile(profile) {
38
- const key = String(profile || DEFAULT_POLICY_PROFILE).trim().toLowerCase();
39
- return POLICY_PROFILES[key] ? key : DEFAULT_POLICY_PROFILE;
40
- }
41
-
42
- function parseCsvList(value) {
43
- if (!value) return [];
44
- return String(value)
45
- .split(',')
46
- .map(item => item.trim())
47
- .filter(Boolean);
48
- }
49
-
50
- function resolvePolicyProfile(options = {}) {
51
- return normalizeProfile(options.policyProfile || process.env.BOOTSPRING_POLICY_PROFILE);
52
- }
53
-
54
- function getPolicyProfile(profile, options = {}) {
55
- const key = normalizeProfile(profile);
56
- const base = POLICY_PROFILES[key];
57
- const blockedFromEnv = parseCsvList(options.blockedWorkflows || process.env.BOOTSPRING_POLICY_BLOCKED_WORKFLOWS);
58
- return {
59
- ...base,
60
- blockedWorkflows: Array.from(new Set([...(base.blockedWorkflows || []), ...blockedFromEnv]))
61
- };
62
- }
63
-
64
- function isWorkflowBlocked(workflow, profile) {
65
- const workflowKey = String(workflow?.key || workflow || '').trim();
66
- if (!workflowKey) return false;
67
- return (profile.blockedWorkflows || []).includes(workflowKey);
68
- }
69
-
70
- /**
71
- * Check if a scope is blocked by policy
72
- * @param {string} scope - Scope to check (e.g., 'skills.external')
73
- * @param {string} tier - Current tier
74
- * @param {string} profile - Policy profile
75
- * @param {object} memberOverrides - Per-member overrides
76
- * @returns {object} Access result
77
- */
78
- function checkScopeAccess(scope, tier = 'free', profile = 'startup', memberOverrides = {}) {
79
- const effectivePolicy = policyMatrix.buildEffectivePolicy(tier, profile, memberOverrides);
80
- return policyMatrix.checkPolicyAccess(scope, effectivePolicy);
81
- }
82
-
83
- /**
84
- * Get all available policy profiles
85
- * @returns {object[]} List of profiles
86
- */
87
- function listPolicyProfiles() {
88
- return Object.values(POLICY_PROFILES).map(p => ({
89
- id: p.id,
90
- name: p.name,
91
- description: p.description,
92
- tier: p.tier
93
- }));
94
- }
95
-
96
- /**
97
- * Get policy scopes
98
- * @returns {object} Available scopes
99
- */
100
- function getPolicyScopes() {
101
- return policyMatrix.POLICY_SCOPES;
102
- }
103
-
104
- module.exports = {
105
- DEFAULT_POLICY_PROFILE,
106
- POLICY_PROFILES,
107
- resolvePolicyProfile,
108
- getPolicyProfile,
109
- isWorkflowBlocked,
110
- checkScopeAccess,
111
- listPolicyProfiles,
112
- getPolicyScopes
113
- };
@@ -1,303 +0,0 @@
1
- /**
2
- * Policy Matrix
3
- * Comprehensive policy definitions for org-level gates
4
- * @package bootspring
5
- */
6
-
7
- /**
8
- * Policy scopes define what can be controlled
9
- */
10
- const POLICY_SCOPES = {
11
- skills: {
12
- external: 'skills.external', // Third-party/external skills
13
- premium: 'skills.premium', // Premium skill categories
14
- ai: 'skills.ai', // AI-powered skills
15
- all: 'skills.*'
16
- },
17
- workflows: {
18
- parallel: 'workflows.parallel', // Parallel execution
19
- premium: 'workflows.premium', // Premium workflow packs
20
- custom: 'workflows.custom', // Custom workflow definitions
21
- all: 'workflows.*'
22
- },
23
- agents: {
24
- technical: 'agents.technical', // Technical experts
25
- business: 'agents.business', // Business/legal experts
26
- enterprise: 'agents.enterprise', // Enterprise-only agents
27
- all: 'agents.*'
28
- },
29
- features: {
30
- telemetry: 'features.telemetry', // Usage telemetry
31
- cloudSync: 'features.cloud_sync', // Cloud synchronization
32
- teamSharing: 'features.team_sharing', // Team context sharing
33
- auditLogs: 'features.audit_logs', // Audit logging
34
- apiAccess: 'features.api_access', // Direct API access
35
- all: 'features.*'
36
- }
37
- };
38
-
39
- /**
40
- * Default policy matrix by tier
41
- * Defines what's allowed/blocked per tier
42
- */
43
- const TIER_POLICY_DEFAULTS = {
44
- free: {
45
- allowed: [
46
- 'skills.external',
47
- 'agents.technical',
48
- 'features.telemetry'
49
- ],
50
- blocked: [
51
- 'skills.premium',
52
- 'skills.ai',
53
- 'workflows.premium',
54
- 'workflows.parallel',
55
- 'agents.business',
56
- 'agents.enterprise',
57
- 'features.cloud_sync',
58
- 'features.team_sharing',
59
- 'features.audit_logs'
60
- ],
61
- limits: {
62
- skillsPerDay: 50,
63
- workflowsPerDay: 10,
64
- agentInvocationsPerDay: 20
65
- }
66
- },
67
- pro: {
68
- allowed: [
69
- 'skills.*',
70
- 'workflows.*',
71
- 'agents.technical',
72
- 'agents.business',
73
- 'features.telemetry',
74
- 'features.cloud_sync'
75
- ],
76
- blocked: [
77
- 'agents.enterprise',
78
- 'features.team_sharing',
79
- 'features.audit_logs'
80
- ],
81
- limits: {
82
- skillsPerDay: 500,
83
- workflowsPerDay: 100,
84
- agentInvocationsPerDay: 200
85
- }
86
- },
87
- team: {
88
- allowed: [
89
- 'skills.*',
90
- 'workflows.*',
91
- 'agents.*',
92
- 'features.telemetry',
93
- 'features.cloud_sync',
94
- 'features.team_sharing',
95
- 'features.audit_logs'
96
- ],
97
- blocked: [],
98
- limits: {
99
- skillsPerDay: 2000,
100
- workflowsPerDay: 500,
101
- agentInvocationsPerDay: 1000,
102
- teamMembers: 10
103
- }
104
- },
105
- enterprise: {
106
- allowed: ['*'],
107
- blocked: [],
108
- limits: {
109
- skillsPerDay: -1, // Unlimited
110
- workflowsPerDay: -1,
111
- agentInvocationsPerDay: -1,
112
- teamMembers: -1
113
- }
114
- }
115
- };
116
-
117
- /**
118
- * Profile-specific policy overrides
119
- * Applied on top of tier defaults
120
- */
121
- const PROFILE_OVERRIDES = {
122
- startup: {
123
- // Startup profile: permissive, fast iteration
124
- overrides: {},
125
- additionalBlocked: []
126
- },
127
- regulated: {
128
- // Regulated profile: compliance-focused
129
- overrides: {
130
- requireApproval: ['workflows.custom', 'skills.external'],
131
- auditAll: true,
132
- dataResidency: true
133
- },
134
- additionalBlocked: [
135
- 'skills.external',
136
- 'workflows.growth-pack'
137
- ]
138
- },
139
- enterprise: {
140
- // Enterprise profile: full control
141
- overrides: {
142
- ssoRequired: true,
143
- auditAll: true,
144
- approvalWorkflow: true
145
- },
146
- additionalBlocked: []
147
- }
148
- };
149
-
150
- /**
151
- * Member role permissions
152
- * What each role can do within an org
153
- */
154
- const ROLE_PERMISSIONS = {
155
- owner: {
156
- canManageOrg: true,
157
- canManageMembers: true,
158
- canManagePolicies: true,
159
- canManageBilling: true,
160
- canUseAllFeatures: true
161
- },
162
- admin: {
163
- canManageOrg: false,
164
- canManageMembers: true,
165
- canManagePolicies: true,
166
- canManageBilling: false,
167
- canUseAllFeatures: true
168
- },
169
- member: {
170
- canManageOrg: false,
171
- canManageMembers: false,
172
- canManagePolicies: false,
173
- canManageBilling: false,
174
- canUseAllFeatures: true
175
- },
176
- viewer: {
177
- canManageOrg: false,
178
- canManageMembers: false,
179
- canManagePolicies: false,
180
- canManageBilling: false,
181
- canUseAllFeatures: false
182
- }
183
- };
184
-
185
- /**
186
- * Check if a scope matches a pattern
187
- * @param {string} scope - Specific scope (e.g., 'skills.external')
188
- * @param {string} pattern - Pattern to match (e.g., 'skills.*' or 'skills.external')
189
- * @returns {boolean}
190
- */
191
- function matchesScope(scope, pattern) {
192
- if (pattern === '*') return true;
193
- if (pattern === scope) return true;
194
- if (pattern.endsWith('.*')) {
195
- const prefix = pattern.slice(0, -2);
196
- return scope.startsWith(prefix + '.');
197
- }
198
- return false;
199
- }
200
-
201
- /**
202
- * Check if a scope is allowed by policy
203
- * @param {string} scope - Scope to check
204
- * @param {string[]} allowed - Allowed patterns
205
- * @param {string[]} blocked - Blocked patterns
206
- * @returns {boolean}
207
- */
208
- function isScopeAllowed(scope, allowed, blocked) {
209
- // Check blocked first (blocked takes precedence)
210
- for (const pattern of blocked) {
211
- if (matchesScope(scope, pattern)) {
212
- return false;
213
- }
214
- }
215
- // Check allowed
216
- for (const pattern of allowed) {
217
- if (matchesScope(scope, pattern)) {
218
- return true;
219
- }
220
- }
221
- return false;
222
- }
223
-
224
- /**
225
- * Build effective policy for an org member
226
- * @param {string} tier - Org tier (free/pro/team/enterprise)
227
- * @param {string} profile - Policy profile (startup/regulated/enterprise)
228
- * @param {object} memberOverrides - Per-member policy overrides
229
- * @returns {object} Effective policy
230
- */
231
- function buildEffectivePolicy(tier, profile, memberOverrides = {}) {
232
- const tierDefaults = TIER_POLICY_DEFAULTS[tier] || TIER_POLICY_DEFAULTS.free;
233
- const profileOverrides = PROFILE_OVERRIDES[profile] || PROFILE_OVERRIDES.startup;
234
-
235
- // Merge allowed/blocked lists
236
- const allowed = [...tierDefaults.allowed];
237
- const blocked = [
238
- ...tierDefaults.blocked,
239
- ...profileOverrides.additionalBlocked
240
- ];
241
-
242
- // Apply member overrides
243
- if (memberOverrides.additionalAllowed) {
244
- allowed.push(...memberOverrides.additionalAllowed);
245
- }
246
- if (memberOverrides.additionalBlocked) {
247
- blocked.push(...memberOverrides.additionalBlocked);
248
- }
249
-
250
- return {
251
- tier,
252
- profile,
253
- allowed: [...new Set(allowed)],
254
- blocked: [...new Set(blocked)],
255
- limits: { ...tierDefaults.limits, ...(memberOverrides.limits || {}) },
256
- overrides: { ...profileOverrides.overrides, ...(memberOverrides.overrides || {}) }
257
- };
258
- }
259
-
260
- /**
261
- * Check access against effective policy
262
- * @param {string} scope - Scope to check
263
- * @param {object} policy - Effective policy
264
- * @returns {object} Access result
265
- */
266
- function checkPolicyAccess(scope, policy) {
267
- const allowed = isScopeAllowed(scope, policy.allowed, policy.blocked);
268
-
269
- if (!allowed) {
270
- return {
271
- allowed: false,
272
- code: 'policy_blocked',
273
- scope,
274
- reason: `Scope "${scope}" is blocked by ${policy.profile} policy`
275
- };
276
- }
277
-
278
- // Check if approval is required
279
- if (policy.overrides.requireApproval?.some(p => matchesScope(scope, p))) {
280
- return {
281
- allowed: true,
282
- requiresApproval: true,
283
- scope,
284
- reason: `Scope "${scope}" requires approval under ${policy.profile} policy`
285
- };
286
- }
287
-
288
- return {
289
- allowed: true,
290
- scope
291
- };
292
- }
293
-
294
- module.exports = {
295
- POLICY_SCOPES,
296
- TIER_POLICY_DEFAULTS,
297
- PROFILE_OVERRIDES,
298
- ROLE_PERMISSIONS,
299
- matchesScope,
300
- isScopeAllowed,
301
- buildEffectivePolicy,
302
- checkPolicyAccess
303
- };