@owlmeans/viable-common 0.0.32 → 0.0.34

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 (91) hide show
  1. package/README.md +3 -3
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/agent-presentation/SKILL.md +1 -1
  4. package/agent-meta/skills/viable-common/SKILL.md +109 -57
  5. package/build/ba/index.d.ts +0 -1
  6. package/build/ba/index.d.ts.map +1 -1
  7. package/build/ba/index.js +0 -1
  8. package/build/ba/index.js.map +1 -1
  9. package/build/blueprint/case.js +3 -3
  10. package/build/blueprint/consts.d.ts +38 -0
  11. package/build/blueprint/consts.d.ts.map +1 -1
  12. package/build/blueprint/consts.js +40 -0
  13. package/build/blueprint/consts.js.map +1 -1
  14. package/build/blueprint/index.d.ts +0 -3
  15. package/build/blueprint/index.d.ts.map +1 -1
  16. package/build/blueprint/index.js +0 -3
  17. package/build/blueprint/index.js.map +1 -1
  18. package/build/blueprint/types.d.ts +5 -5
  19. package/build/connect/errors.d.ts +23 -0
  20. package/build/connect/errors.d.ts.map +1 -1
  21. package/build/connect/errors.js +50 -0
  22. package/build/connect/errors.js.map +1 -1
  23. package/build/intent/consts.d.ts +57 -0
  24. package/build/intent/consts.d.ts.map +1 -0
  25. package/build/intent/consts.js +58 -0
  26. package/build/intent/consts.js.map +1 -0
  27. package/build/intent/entrypoints.d.ts +27 -0
  28. package/build/intent/entrypoints.d.ts.map +1 -0
  29. package/build/intent/entrypoints.js +28 -0
  30. package/build/intent/entrypoints.js.map +1 -0
  31. package/build/intent/errors.d.ts +20 -0
  32. package/build/intent/errors.d.ts.map +1 -0
  33. package/build/intent/errors.js +28 -0
  34. package/build/intent/errors.js.map +1 -0
  35. package/build/intent/flow.d.ts +17 -0
  36. package/build/intent/flow.d.ts.map +1 -0
  37. package/build/intent/flow.js +65 -0
  38. package/build/intent/flow.js.map +1 -0
  39. package/build/intent/index.d.ts +7 -0
  40. package/build/intent/index.d.ts.map +1 -0
  41. package/build/intent/index.js +6 -0
  42. package/build/intent/index.js.map +1 -0
  43. package/build/intent/schemas.d.ts +10 -0
  44. package/build/intent/schemas.d.ts.map +1 -0
  45. package/build/intent/schemas.js +24 -0
  46. package/build/intent/schemas.js.map +1 -0
  47. package/build/intent/types.d.ts +40 -0
  48. package/build/intent/types.d.ts.map +1 -0
  49. package/build/intent/types.js +2 -0
  50. package/build/intent/types.js.map +1 -0
  51. package/build/skills/catalogue.d.ts.map +1 -1
  52. package/build/skills/catalogue.js +15 -1
  53. package/build/skills/catalogue.js.map +1 -1
  54. package/build/skills/consts.d.ts +0 -34
  55. package/build/skills/consts.d.ts.map +1 -1
  56. package/build/skills/consts.js +0 -106
  57. package/build/skills/consts.js.map +1 -1
  58. package/build/skills/index.d.ts +0 -1
  59. package/build/skills/index.d.ts.map +1 -1
  60. package/build/skills/index.js +0 -1
  61. package/build/skills/index.js.map +1 -1
  62. package/build/skills/roles.d.ts +0 -9
  63. package/build/skills/roles.d.ts.map +1 -1
  64. package/build/skills/roles.js +0 -226
  65. package/build/skills/roles.js.map +1 -1
  66. package/package.json +18 -8
  67. package/src/ba/index.ts +0 -1
  68. package/src/blueprint/consts.ts +40 -0
  69. package/src/blueprint/index.ts +0 -3
  70. package/src/blueprint/types.ts +5 -5
  71. package/src/connect/errors.ts +55 -0
  72. package/src/intent/consts.ts +66 -0
  73. package/src/intent/entrypoints.ts +43 -0
  74. package/src/intent/errors.ts +33 -0
  75. package/src/intent/flow.ts +67 -0
  76. package/src/intent/index.ts +6 -0
  77. package/src/intent/schemas.ts +27 -0
  78. package/src/intent/types.ts +45 -0
  79. package/src/skills/consts.ts +0 -112
  80. package/src/skills/index.ts +0 -1
  81. package/src/skills/roles.ts +0 -267
  82. package/tests/blueprint.spec.ts +2 -66
  83. package/tests/connect-errors.spec.ts +31 -1
  84. package/tests/error-status.spec.ts +6 -1
  85. package/tests/intent.spec.ts +121 -0
  86. package/src/ba/schemas.ts +0 -196
  87. package/src/blueprint/case.ts +0 -320
  88. package/src/blueprint/resolve.ts +0 -136
  89. package/src/blueprint/schemas.ts +0 -44
  90. package/src/skills/catalogue.ts +0 -1850
  91. package/tests/skills.spec.ts +0 -110
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The intent-first hand-off — how a prompt typed on the public site reaches the platform.
3
+ *
4
+ * A visitor types the idea for a project on the marketing site before they have an account. The
5
+ * site cannot write into the platform's browser storage (IndexedDB is per origin) and must not put
6
+ * the prompt in a URL, so the prompt takes a short detour through the platform's own cache: the
7
+ * site stashes it behind an unguessable reference, sends the browser to the platform with only that
8
+ * reference, and the platform picks the prompt up once — after which it lives in the visitor's own
9
+ * browser until they decide what to do with it.
10
+ *
11
+ * Everything here is serializable and runtime-free: the site, the manager API and the manager web
12
+ * read the same declarations, and none of them may reinvent a name the others parse.
13
+ */
14
+
15
+ /** Every address of the hand-off. API routes are guest routes; the landing is a screen. */
16
+ export const intent = Object.freeze({
17
+ base: 'viable:manager-api:intent:base',
18
+ stash: 'viable:manager-api:intent:stash',
19
+ pickup: 'viable:manager-api:intent:pickup',
20
+ landing: 'viable:manager-web:intent:landing',
21
+ } as const)
22
+
23
+ /** Where the guest API lives, and the screen the visitor's browser is sent to. */
24
+ export const INTENT_API_PATH = '/public/intent'
25
+ export const INTENT_LANDING_PATH = '/start'
26
+
27
+ /** The flow the two sides walk. */
28
+ export const INTENT_FLOW = 'viable:intent'
29
+
30
+ /**
31
+ * How long the platform keeps a stashed prompt waiting for pickup. Short on purpose: the pickup
32
+ * happens the moment the browser lands, so anything older was abandoned, and a prompt is personal
33
+ * data the platform has no business holding once nobody is coming for it.
34
+ */
35
+ export const INTENT_TTL_SECONDS = 120
36
+
37
+ /** The longest prompt the platform will create a project from (`PromptSchema` in viable). */
38
+ export const INTENT_PROMPT_MAX = 8192
39
+
40
+ /**
41
+ * The reference's shape: `createIdOfLength(24)` — Base58, so no `0`, `O`, `I` or `l`. Anything else
42
+ * is refused before the cache is asked, which keeps a crafted reference from ever being a key.
43
+ */
44
+ export const INTENT_REF_LENGTH = 24
45
+ export const INTENT_REF_PATTERN = '^[1-9A-HJ-NP-Za-km-z]{24}$'
46
+
47
+ /** The flow payload key carrying the reference — short, because a payload is CSV-joined. */
48
+ export const INTENT_PAYLOAD_REF = 'ref'
49
+
50
+ /** How long the visitor's draft survives in their own browser waiting for a decision. */
51
+ export const INTENT_DRAFT_TTL_MS = 24 * 60 * 60 * 1000
52
+
53
+ /** How long a suspended landing waits while the visitor signs in or registers. */
54
+ export const INTENT_SUSPEND_TTL_MS = 30 * 60 * 1000
55
+
56
+ /** Steps of {@link intentFlow}, exported so a screen never spells one as a literal. */
57
+ export enum IntentFlowStep {
58
+ /** The public site: the prompt is typed and stashed. */
59
+ Compose = 'compose',
60
+ /** The platform's landing screen: the stash is picked up. */
61
+ Land = 'land',
62
+ /** The platform's own sign-in dispatcher. */
63
+ SignIn = 'sign-in',
64
+ /** The platform's home screen, where the draft is offered back. */
65
+ Review = 'review',
66
+ }
@@ -0,0 +1,43 @@
1
+ import { contract, openProtocol, protocol, typed } from '@owlmeans/entrypoint'
2
+ import { frontend, route, RouteMethod } from '@owlmeans/route'
3
+ import { INTENT_API_PATH, INTENT_LANDING_PATH, intent } from './consts.js'
4
+ import { IntentPickupBodySchema, IntentStashBodySchema } from './schemas.js'
5
+ import type {
6
+ IntentPickupBody, IntentPickupResult, IntentProtocolOptions, IntentStashBody, IntentStashResult,
7
+ } from './types.js'
8
+
9
+ /**
10
+ * Declare the intent-first hand-off: two GUEST API routes and the landing screen's address.
11
+ *
12
+ * No guard and no gate anywhere on the API — the caller is a visitor who has no account yet — and
13
+ * no `service` on any route: each consumer fills it in (the public site names the platform's
14
+ * services in its own configuration; the manager binds them to its own).
15
+ *
16
+ * TODO(cors): these routes ride the framework's global `origin: '*'` CORS policy (no credentials).
17
+ * If viable ever restricts allowed origins globally, this entrypoint must keep answering the public
18
+ * site's origins — owlmeans.com, owlmeans.pl and, in dev/stage, `http://localhost:4321`.
19
+ *
20
+ * The landing screen is `sticky`, like the OAuth consent screens: it is an ordinary in-app screen
21
+ * that a visitor arrives at directly from another origin.
22
+ */
23
+ export const makeIntentProtocols = (opts: IntentProtocolOptions = {}) => {
24
+ const base = openProtocol(route(intent.base, opts.path ?? INTENT_API_PATH))
25
+
26
+ return {
27
+ base,
28
+
29
+ stash: protocol(
30
+ route(intent.stash, '/', { parent: base, method: RouteMethod.POST }),
31
+ contract.request({ body: typed<IntentStashBody>(IntentStashBodySchema) }, typed<IntentStashResult>()),
32
+ ),
33
+
34
+ pickup: protocol(
35
+ route(intent.pickup, '/pickup', { parent: base, method: RouteMethod.POST }),
36
+ contract.request({ body: typed<IntentPickupBody>(IntentPickupBodySchema) }, typed<IntentPickupResult>()),
37
+ ),
38
+
39
+ landing: openProtocol(route(intent.landing, opts.landing ?? INTENT_LANDING_PATH, frontend()), { sticky: true }),
40
+ }
41
+ }
42
+
43
+ export type IntentProtocols = ReturnType<typeof makeIntentProtocols>
@@ -0,0 +1,33 @@
1
+ import { ResilientError } from '@owlmeans/error'
2
+
3
+ export class IntentError extends ResilientError {
4
+ public static override typeName = `ViableIntent${ResilientError.typeName}`
5
+
6
+ constructor(message: string = 'error') {
7
+ super(IntentError.typeName, `viable-intent:${message}`)
8
+ }
9
+ }
10
+
11
+ /** No stashed prompt under that reference — expired, already picked up, or never existed. */
12
+ export class IntentExpired extends IntentError {
13
+ public static override typeName = `Expired${IntentError.typeName}`
14
+ /** The reference answers nothing (any more): answered 404. */
15
+ public static httpStatus = 404
16
+
17
+ constructor(message: string = 'error') {
18
+ super(`expired:${message}`)
19
+ this.type = IntentExpired.typeName
20
+ }
21
+ }
22
+
23
+ /** Too many stashes or pickups from one address, or across everyone. */
24
+ export class IntentThrottled extends IntentError {
25
+ public static override typeName = `Throttled${IntentError.typeName}`
26
+ /** Answered 429. */
27
+ public static httpStatus = 429
28
+
29
+ constructor(message: string = 'error') {
30
+ super(`throttled:${message}`)
31
+ this.type = IntentThrottled.typeName
32
+ }
33
+ }
@@ -0,0 +1,67 @@
1
+ import { DISPATCHER } from '@owlmeans/auth'
2
+ import { HOME } from '@owlmeans/context'
3
+ import type { ShallowFlow } from '@owlmeans/flow'
4
+ import { INTENT_FLOW, INTENT_PAYLOAD_REF, intent, IntentFlowStep } from './consts.js'
5
+
6
+ const payloadMap = { [INTENT_PAYLOAD_REF]: 0 }
7
+
8
+ /**
9
+ * The hand-off flow both sides walk.
10
+ *
11
+ * `compose` runs on the public site: the prompt is stashed and `handoff` carries the reference to
12
+ * `land`, whose module is the platform's landing screen — so the site redirects to
13
+ * `land`'s address with the payload as query. The landing then enters at `land` FRESH (the
14
+ * reference arrives as `?ref=`, never as a serialized token, which the platform's own sign-in
15
+ * parses from `?flow=` on every page), and either goes on to `review` (signed in) or parks the flow
16
+ * with `suspendFlow` at `sign-in`, whose non-explicit way forward is `review` — the home screen —
17
+ * so whichever sign-in method finishes lands there.
18
+ *
19
+ * The steps address CONCRETE entrypoint aliases, never `$`-prefixed references: this flow is never
20
+ * the live `FlowService` model and nothing here needs `configureFlows`.
21
+ */
22
+ export const intentFlow: ShallowFlow = {
23
+ flow: INTENT_FLOW,
24
+ initialStep: IntentFlowStep.Compose,
25
+ steps: {
26
+ [IntentFlowStep.Compose]: {
27
+ index: 0,
28
+ step: IntentFlowStep.Compose,
29
+ service: '',
30
+ initial: true,
31
+ payloadMap,
32
+ transitions: {
33
+ handoff: { transition: 'handoff', step: IntentFlowStep.Land },
34
+ },
35
+ },
36
+ [IntentFlowStep.Land]: {
37
+ index: 1,
38
+ step: IntentFlowStep.Land,
39
+ service: '',
40
+ module: intent.landing,
41
+ initial: true,
42
+ payloadMap,
43
+ transitions: {
44
+ review: { transition: 'review', step: IntentFlowStep.Review },
45
+ 'sign-in': { transition: 'sign-in', step: IntentFlowStep.SignIn, explicit: true },
46
+ },
47
+ },
48
+ [IntentFlowStep.SignIn]: {
49
+ index: 2,
50
+ step: IntentFlowStep.SignIn,
51
+ service: '',
52
+ module: DISPATCHER,
53
+ payloadMap,
54
+ transitions: {
55
+ next: { transition: 'next', step: IntentFlowStep.Review },
56
+ },
57
+ },
58
+ [IntentFlowStep.Review]: {
59
+ index: 3,
60
+ step: IntentFlowStep.Review,
61
+ service: '',
62
+ module: HOME,
63
+ payloadMap,
64
+ transitions: {},
65
+ },
66
+ },
67
+ }
@@ -0,0 +1,6 @@
1
+ export * from './consts.js'
2
+ export * from './schemas.js'
3
+ export * from './errors.js'
4
+ export * from './entrypoints.js'
5
+ export * from './flow.js'
6
+ export type * from './types.js'
@@ -0,0 +1,27 @@
1
+ import type { JSONSchemaType } from 'ajv'
2
+ import { INTENT_PROMPT_MAX, INTENT_REF_LENGTH, INTENT_REF_PATTERN } from './consts.js'
3
+ import type { IntentPickupBody, IntentStashBody } from './types.js'
4
+
5
+ /**
6
+ * The stash body. `consent` is a constant `true`, not a boolean: the data-processing confirmation is
7
+ * a precondition of the request existing, so the wire refuses its absence instead of the handler
8
+ * having to remember to look.
9
+ */
10
+ export const IntentStashBodySchema: JSONSchemaType<IntentStashBody> = {
11
+ type: 'object',
12
+ properties: {
13
+ prompt: { type: 'string', minLength: 1, maxLength: INTENT_PROMPT_MAX },
14
+ consent: { type: 'boolean', const: true },
15
+ },
16
+ required: ['prompt', 'consent'],
17
+ additionalProperties: false,
18
+ }
19
+
20
+ export const IntentPickupBodySchema: JSONSchemaType<IntentPickupBody> = {
21
+ type: 'object',
22
+ properties: {
23
+ ref: { type: 'string', minLength: INTENT_REF_LENGTH, maxLength: INTENT_REF_LENGTH, pattern: INTENT_REF_PATTERN },
24
+ },
25
+ required: ['ref'],
26
+ additionalProperties: false,
27
+ }
@@ -0,0 +1,45 @@
1
+ import type { ResourceRecord } from '@owlmeans/resource'
2
+
3
+ /** What the public site sends: the prompt and the person's confirmation of its processing. */
4
+ export interface IntentStashBody {
5
+ prompt: string
6
+ /** Always `true` — the site's checkbox; a request without it is refused by the schema. */
7
+ consent: true
8
+ }
9
+
10
+ /** The reference the platform answers with, and the moment its record expires. */
11
+ export interface IntentStashResult {
12
+ ref: string
13
+ /** Epoch milliseconds. */
14
+ expiresAt: number
15
+ }
16
+
17
+ export interface IntentPickupBody {
18
+ ref: string
19
+ }
20
+
21
+ export interface IntentPickupResult {
22
+ prompt: string
23
+ }
24
+
25
+ /**
26
+ * The prompt as it waits in the visitor's browser between pickup and the decision on the home
27
+ * screen. `expiresAt` is checked on read — browser storage has no TTL of its own.
28
+ */
29
+ export interface IntentDraft extends ResourceRecord {
30
+ id: string
31
+ /** The reference it was picked up with; a reload of the landing reuses the draft of that ref. */
32
+ ref: string
33
+ prompt: string
34
+ expiresAt: number
35
+ }
36
+
37
+ /**
38
+ * Options of {@link makeIntentProtocols}.
39
+ */
40
+ export interface IntentProtocolOptions {
41
+ /** Path prefix of the guest API; defaults to `/public/intent`. */
42
+ path?: string
43
+ /** Path of the landing screen; defaults to `/start`. */
44
+ landing?: string
45
+ }
@@ -85,115 +85,3 @@ export enum ViableSkill {
85
85
  LibrariesAi = 'libraries-ai',
86
86
  LibrariesGame = 'libraries-game',
87
87
  }
88
-
89
- /**
90
- * Sort weights. Skills render in this order inside the cached block, so the sequence is
91
- * part of the cache key — reordering them invalidates every prefix at once. Grouped in
92
- * tens to leave room for a consumer's own skills between them.
93
- */
94
- export const SKILL_ORDER: Record<ViableSkill, number> = {
95
- [ViableSkill.OutputSourceOnly]: 10,
96
- [ViableSkill.OutputTextOnly]: 10,
97
- [ViableSkill.ScopeDiscipline]: 15,
98
- [ViableSkill.MainFlowFocus]: 12,
99
- [ViableSkill.NonTypescriptOutput]: 18,
100
- [ViableSkill.TsStyle]: 20,
101
- [ViableSkill.TsImports]: 30,
102
- [ViableSkill.ReactComponents]: 40,
103
- [ViableSkill.OwlMeansEntrypoints]: 41,
104
- [ViableSkill.OwlMeansNav]: 42,
105
- [ViableSkill.PermissionModel]: 43,
106
- [ViableSkill.ShadcnUi]: 44,
107
- [ViableSkill.OwlmeansState]: 50,
108
- [ViableSkill.FormFeedback]: 51,
109
- [ViableSkill.StoreAccess]: 52,
110
- [ViableSkill.ViewModelNaming]: 55,
111
- // After the frontend rules it builds on (entrypoints, nav, shadcn, form feedback, the store), and
112
- // before the backend block — it is only ever carried by analysis and frontend personas.
113
- [ViableSkill.LandingGate]: 56,
114
- // Analysis and frontend read it the same way LandingGate is read — after the frontend rules it
115
- // references (nav, shadcn) — and a backend coder reads it as a server-side check, so it sits
116
- // just ahead of the backend block for either audience.
117
- [ViableSkill.MarketingConsent]: 57,
118
- [ViableSkill.ResourceLayer]: 60,
119
- [ViableSkill.ResourceResults]: 62,
120
- [ViableSkill.ResourceMigrations]: 64,
121
- [ViableSkill.OwlMeansServer]: 70,
122
- [ViableSkill.OwlmeansContext]: 65,
123
- [ViableSkill.OwlMeansServices]: 66,
124
- [ViableSkill.FixerHeuristics]: 75,
125
- [ViableSkill.ProjectLayout]: 5,
126
- [ViableSkill.AgentTooling]: 8,
127
- [ViableSkill.QueueDiscipline]: 67,
128
- [ViableSkill.WorkerJobs]: 68,
129
- [ViableSkill.TargetAgents]: 69,
130
- [ViableSkill.AgenticChoice]: 71,
131
- [ViableSkill.TargetLlm]: 72,
132
- [ViableSkill.TargetAgentTools]: 73,
133
- [ViableSkill.GameDesign]: 45,
134
- [ViableSkill.GameScene]: 46,
135
- [ViableSkill.GameUi]: 47,
136
- [ViableSkill.GameNetworking]: 48,
137
- [ViableSkill.LayoutDefinition]: 80,
138
- [ViableSkill.ScreenDefinition]: 82,
139
- [ViableSkill.LibrariesCommon]: 90,
140
- [ViableSkill.LibrariesUiState]: 90,
141
- [ViableSkill.LibrariesUi]: 90,
142
- [ViableSkill.LibrariesBackend]: 90,
143
- [ViableSkill.LibrariesAi]: 90,
144
- [ViableSkill.LibrariesGame]: 90,
145
- }
146
-
147
- /**
148
- * The catalogue entries whose subject a PUBLISHED `@owlmeans` package already documents.
149
- *
150
- * The catalogue and `agent-meta/skills/*` state the same contract twice — the P1 duplication —
151
- * and the duplication is not symmetric: a package skill is versioned with the package and reaches
152
- * a prompt through `owlmeansPackagesPlugin` whenever the request names that package, while a
153
- * catalogue entry is a constant here that has to be edited by hand every time the framework moves.
154
- *
155
- * Naming the overlap is what makes it a DATA decision instead of a code one. A blueprint lists
156
- * which of these its prompts carry, and a persona's skills are filtered against that list — so
157
- * dropping one is a line in a blueprint, and restoring it is the same line. Everything NOT in this
158
- * set is viable's own (output discipline, scope, the view-model convention, the fixer heuristics,
159
- * where files live) and always loads, because no published package documents it.
160
- *
161
- * The default blueprint currently lists every one of them: the reduction is a real change to what
162
- * a coder is told, and it is measured against a green end-to-end baseline rather than guessed.
163
- */
164
- export const FRAMEWORK_OWNED_SKILLS: ViableSkill[] = [
165
- ViableSkill.ResourceLayer,
166
- ViableSkill.ResourceResults,
167
- ViableSkill.ResourceMigrations,
168
- ViableSkill.OwlmeansContext,
169
- ViableSkill.OwlMeansServices,
170
- ViableSkill.OwlMeansServer,
171
- ViableSkill.OwlMeansEntrypoints,
172
- ViableSkill.OwlMeansNav,
173
- ViableSkill.PermissionModel,
174
- ViableSkill.OwlmeansState,
175
- ViableSkill.StoreAccess,
176
- ViableSkill.ShadcnUi,
177
- ViableSkill.ReactComponents,
178
- ViableSkill.FormFeedback,
179
- ]
180
-
181
- const FRAMEWORK_OWNED = new Set<ViableSkill>(FRAMEWORK_OWNED_SKILLS)
182
-
183
- /**
184
- * Filter a persona's skills against what a blueprint asks for.
185
- *
186
- * A skill nobody owns always survives. A framework-owned one survives only while the blueprint
187
- * lists it — so a blueprint built on another persistence layer stops teaching the Postgres
188
- * resource contract without anybody editing a persona, and a blueprint that lists nothing at all
189
- * (the shape a test builds) is read as "everything", because an empty allow-list almost always
190
- * means "not stated" rather than "none".
191
- */
192
- export const skillsForBlueprint = (
193
- skills: ViableSkill[], allowed?: ViableSkill[]
194
- ): ViableSkill[] => {
195
- if (allowed == null || allowed.length === 0) return skills
196
- const permitted = new Set(allowed)
197
-
198
- return skills.filter(skill => !FRAMEWORK_OWNED.has(skill) || permitted.has(skill))
199
- }
@@ -1,4 +1,3 @@
1
1
 
2
2
  export * from './consts.js'
3
- export * from './catalogue.js'
4
3
  export * from './roles.js'