vyriy 0.3.4 → 0.3.5

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/README.md CHANGED
@@ -8,6 +8,9 @@ Interactive project master for calm cloud-ready applications.
8
8
 
9
9
  The CLI checks the local environment, runs a small project wizard, creates a normalized `VyriyProjectPlan`, builds generated project files in memory, prints a file plan, and writes only files that are safe to create or explicitly allowed to overwrite.
10
10
 
11
+ For a detailed map of CLI choices, diagrams, current generated files, and next
12
+ generator targets, see [PROJECT-MASTER.md](./PROJECT-MASTER.md).
13
+
11
14
  ## Install
12
15
 
13
16
  Install globally:
@@ -103,13 +106,15 @@ The wizard collects:
103
106
  - project preset
104
107
  - API style for API-capable presets
105
108
  - CI/CD provider
106
- - optional extra features
109
+ - optional infrastructure choices
107
110
  - confirmation
108
111
 
109
112
  After confirmation, the CLI prints the project plan, creates generated files in memory, builds a conflict-aware file plan, and writes the accepted file plan.
110
113
 
111
114
  Presets do not write to disk directly.
112
115
 
116
+ Generated projects always include `AGENTS.md` based on the shared Vyriy package agent guide.
117
+
113
118
  ## Project Presets
114
119
 
115
120
  Supported presets:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vyriy",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Interactive project master for calm cloud-ready applications.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -289,6 +289,16 @@
289
289
  "import": "./index.js",
290
290
  "default": "./index.js"
291
291
  },
292
+ "./presets/agentsTemplate": {
293
+ "types": "./presets/agentsTemplate.d.ts",
294
+ "import": "./presets/agentsTemplate.js",
295
+ "default": "./presets/agentsTemplate.js"
296
+ },
297
+ "./presets/agentsTemplate.js": {
298
+ "types": "./presets/agentsTemplate.d.ts",
299
+ "import": "./presets/agentsTemplate.js",
300
+ "default": "./presets/agentsTemplate.js"
301
+ },
292
302
  "./presets/createProjectFiles": {
293
303
  "types": "./presets/createProjectFiles.d.ts",
294
304
  "import": "./presets/createProjectFiles.js",
@@ -0,0 +1 @@
1
+ export declare const agentsTemplate: string;
@@ -0,0 +1,105 @@
1
+ export const agentsTemplate = [
2
+ '# Project Agent Guide',
3
+ '',
4
+ 'This repository follows a calm engineering style: changes should be explicit, reusable, typed, documented, tested, and easy to reason about.',
5
+ '',
6
+ 'Use this guide as the default behavior for AI agents and contributors working in this repository. Prefer local package conventions when they are more specific than this document.',
7
+ '',
8
+ '## Core Principles',
9
+ '',
10
+ '- Prefer simple modules over clever frameworks or hidden conventions.',
11
+ '- Keep package and project boundaries explicit.',
12
+ '- Avoid project-specific coupling in reusable code.',
13
+ '- Extract only proven reusable behavior.',
14
+ '- Keep public APIs small, typed, documented, and stable.',
15
+ '- Prefer SSR-friendly and SSG-friendly code paths when working with frontend or shared code.',
16
+ '- Keep integrations replaceable and avoid hard coupling to a CMS, framework, vendor, or runtime host.',
17
+ '- Prefer infrastructure assumptions that are easy to deploy, observe, and replace.',
18
+ '- Prefer the option that is simpler to explain, easier to evolve, and calmer to maintain.',
19
+ '',
20
+ '## File Shape',
21
+ '',
22
+ '- Prefer one exported runtime method, component, helper, or class per production file when it stays readable.',
23
+ '- Prefer one matching test file per production file, for example `feature.ts` and `feature.test.ts`.',
24
+ '- Use focused folders when behavior naturally splits into several related files.',
25
+ '- Keep `index.ts` as a public re-export surface only. Do not place implementation logic in it.',
26
+ '- Use relative import and export specifiers that match the package module style.',
27
+ '- Use `.js` relative specifiers in TypeScript source for ESM/NodeNext packages.',
28
+ '- Add `types.ts` when public shared types are part of the package contract.',
29
+ '- Keep constants near the code that owns them unless they are shared or clarify repeated behavior.',
30
+ '',
31
+ '## Public Surface',
32
+ '',
33
+ '- Every new public export must be re-exported from the package or module public entry point.',
34
+ '- Add or update public-surface tests when exports change.',
35
+ '- Add JSDoc for public exports when behavior, parameters, return values, or usage expectations need explanation.',
36
+ '- Avoid exporting internal helpers only to make tests easier.',
37
+ '- Do not hand-maintain package `exports` maps unless the project has a real custom publishing need.',
38
+ '',
39
+ '## Tests',
40
+ '',
41
+ '- Cover public behavior and meaningful edge cases.',
42
+ '- Prefer behavior-focused tests over private implementation lock-in.',
43
+ '- Keep tests deterministic.',
44
+ '- Avoid real network, filesystem, timers, browser, or cloud dependencies unless the behavior specifically requires them.',
45
+ '- When mocking modules, install mocks before loading the module under test.',
46
+ '- Use focused validation when changing behavior.',
47
+ '',
48
+ 'Example validation commands:',
49
+ '',
50
+ '```bash',
51
+ 'yarn test',
52
+ '```',
53
+ '',
54
+ 'For workspaces, prefer the project convention, for example:',
55
+ '',
56
+ '```bash',
57
+ 'yarn workspace <package-name> test',
58
+ '```',
59
+ '',
60
+ 'For Jest-based packages, focused validation may look like:',
61
+ '',
62
+ '```bash',
63
+ 'yarn jest packages/<package> --runInBand --coverage=false',
64
+ '```',
65
+ '',
66
+ '## Documentation',
67
+ '',
68
+ '- Keep `README.md` concise and usage-oriented.',
69
+ '- Start package READMEs with `# <package>`.',
70
+ '- Document real public exports, supported options, and examples that actually work.',
71
+ '- Update docs when public behavior changes.',
72
+ '- Keep generated docs wrappers, such as `doc.mdx`, aligned with the README when the project uses them.',
73
+ '- For component packages, include visual documentation or stories for supported states and common usage.',
74
+ '',
75
+ '## Components',
76
+ '',
77
+ '- Prefer lightweight React components with TypeScript when working in React packages.',
78
+ '- Keep components SSR-friendly and avoid browser globals during render.',
79
+ '- Prefer composable props and predictable ergonomics.',
80
+ '- Put each public component in its own file with a matching test.',
81
+ '- Add stories or examples when a component has visual states, variants, or interaction states.',
82
+ '- Keep styling explicit and reusable. Avoid hidden theme assumptions unless they are part of the package contract.',
83
+ '',
84
+ '## Change Discipline',
85
+ '',
86
+ '- Keep changes scoped to the requested behavior.',
87
+ '- Avoid unrelated refactors and metadata churn.',
88
+ '- Sync implementation, tests, docs, examples, and public re-exports together.',
89
+ '- Do not introduce new dependencies unless they clearly reduce complexity or are already part of the project direction.',
90
+ '- Prefer small, reviewable changes over broad rewrites.',
91
+ '- Preserve existing conventions unless there is a clear reason to change them.',
92
+ '',
93
+ '## Before Finishing',
94
+ '',
95
+ 'Check that the change is complete:',
96
+ '',
97
+ '- Public exports are updated.',
98
+ '- Public-surface tests are updated when exports change.',
99
+ '- Matching unit tests exist for new behavior.',
100
+ '- README examples still match the real API.',
101
+ '- Visual docs, stories, or examples are updated for visible component behavior.',
102
+ '- TypeScript imports follow the package module style.',
103
+ '- No unrelated files, formatting churn, or generated artifacts were changed.',
104
+ '',
105
+ ].join('\n');
@@ -1,5 +1,6 @@
1
+ import { agentsTemplate } from './agentsTemplate.js';
1
2
  const json = (value) => `${JSON.stringify(value, null, 2)}\n`;
2
- const createPackageJson = ({ description, packageScope, projectName, }) => ({
3
+ const createRootPackageJson = ({ description, packageScope, projectName, }) => ({
3
4
  path: 'package.json',
4
5
  content: json({
5
6
  name: `${packageScope}/${projectName}`,
@@ -12,22 +13,102 @@ const createPackageJson = ({ description, packageScope, projectName, }) => ({
12
13
  node: '>=24.0.0',
13
14
  },
14
15
  scripts: {
15
- build: 'tsc --pretty false',
16
- test: 'jest --coverage=false',
17
16
  lint: 'eslint .',
17
+ test: 'jest --coverage=false',
18
+ build: 'tsc --pretty false',
19
+ deploy: 'echo "Deploy is not configured yet."',
20
+ smoke: 'echo "Smoke checks are not configured yet."',
21
+ e2e: 'echo "E2E checks are not configured yet."',
18
22
  },
19
23
  devDependencies: {},
24
+ workspaces: [
25
+ 'packages/*',
26
+ 'workspaces/*',
27
+ ],
28
+ }),
29
+ });
30
+ const createPackageManifest = ({ packageScope, workspaceName, }) => ({
31
+ path: `packages/${workspaceName}/package.json`,
32
+ content: json({
33
+ name: `${packageScope}/${workspaceName}`,
34
+ version: '0.1.0',
35
+ private: true,
36
+ type: 'module',
37
+ main: 'index.js',
20
38
  }),
21
39
  });
40
+ const createPackageFiles = (plan, packagePlan) => [
41
+ createPackageManifest({
42
+ packageScope: plan.packageScope,
43
+ workspaceName: packagePlan.name,
44
+ }),
45
+ {
46
+ path: `packages/${packagePlan.name}/README.md`,
47
+ content: `# ${plan.packageScope}/${packagePlan.name}\n\n${plan.description}\n`,
48
+ },
49
+ {
50
+ path: `packages/${packagePlan.name}/index.ts`,
51
+ content: packagePlan.kind === 'stack' ? "export * from './stack.js';\n" : "export type * from './types.js';\n",
52
+ },
53
+ ...(packagePlan.kind === 'stack'
54
+ ? [
55
+ {
56
+ path: `packages/${packagePlan.name}/stack.ts`,
57
+ content: 'export type StackName = string;\n',
58
+ },
59
+ {
60
+ path: `packages/${packagePlan.name}/stack.test.ts`,
61
+ content: "import { describe, expect, it } from '@jest/globals';\n\ndescribe('stack', () => {\n it('has a test harness', () => {\n expect(true).toBe(true);\n });\n});\n",
62
+ },
63
+ ]
64
+ : [
65
+ {
66
+ path: `packages/${packagePlan.name}/types.ts`,
67
+ content: 'export type PackageName = string;\n',
68
+ },
69
+ {
70
+ path: `packages/${packagePlan.name}/${packagePlan.name}.test.ts`,
71
+ content: "import { describe, expect, it } from '@jest/globals';\n\ndescribe('package', () => {\n it('has a test harness', () => {\n expect(true).toBe(true);\n });\n});\n",
72
+ },
73
+ ]),
74
+ ];
75
+ const createWorkspaceFiles = (plan, workspacePlan) => [
76
+ {
77
+ path: `workspaces/${workspacePlan.name}/package.json`,
78
+ content: json({
79
+ name: `${plan.packageScope}/${workspacePlan.name}-workspace`,
80
+ version: '0.1.0',
81
+ private: true,
82
+ type: 'module',
83
+ main: 'index.js',
84
+ }),
85
+ },
86
+ {
87
+ path: `workspaces/${workspacePlan.name}/index.ts`,
88
+ content: 'export type WorkspaceName = string;\n',
89
+ },
90
+ {
91
+ path: `workspaces/${workspacePlan.name}/${workspacePlan.name}.test.ts`,
92
+ content: "import { describe, expect, it } from '@jest/globals';\n\ndescribe('workspace', () => {\n it('has a test harness', () => {\n expect(true).toBe(true);\n });\n});\n",
93
+ },
94
+ ];
95
+ const shouldCreateStylelintConfig = (plan) => plan.features.some((feature) => [
96
+ 'react',
97
+ 'webpack',
98
+ ].includes(feature));
22
99
  export const createProjectFiles = (plan) => [
23
- createPackageJson(plan),
100
+ createRootPackageJson(plan),
24
101
  {
25
102
  path: 'README.md',
26
103
  content: `# ${plan.projectName}\n\n${plan.description}\n`,
27
104
  },
105
+ {
106
+ path: 'doc.mdx',
107
+ content: "import { Meta, Markdown } from '@storybook/addon-docs/blocks';\nimport ReadMe from './README.md?raw';\n\n<Meta title=\"Project/README\" />\n\n<Markdown>{ReadMe}</Markdown>\n",
108
+ },
28
109
  {
29
110
  path: 'AGENTS.md',
30
- content: `# ${plan.projectName} Agent Context\n\nKeep changes scoped, explicit, and easy to validate.\n`,
111
+ content: agentsTemplate,
31
112
  },
32
113
  {
33
114
  path: '.editorconfig',
@@ -38,15 +119,61 @@ export const createProjectFiles = (plan) => [
38
119
  content: 'node_modules/\ndist/\ncoverage/\n.yarn/cache/\n.env\n',
39
120
  },
40
121
  {
41
- path: 'src/index.ts',
42
- content: "export type * from './types.js';\n",
122
+ path: '.npmrc',
123
+ content: 'engine-strict=true\n',
124
+ },
125
+ {
126
+ path: '.nvmrc',
127
+ content: '24\n',
128
+ },
129
+ {
130
+ path: '.yarnrc.yml',
131
+ content: 'nodeLinker: node-modules\n',
132
+ },
133
+ {
134
+ path: 'yarn.lock',
135
+ content: '',
136
+ },
137
+ {
138
+ path: 'tsconfig.json',
139
+ content: json({
140
+ extends: '@vyriy/typescript-config/index.json',
141
+ compilerOptions: {
142
+ noEmit: false,
143
+ },
144
+ include: [
145
+ 'packages/**/*.ts',
146
+ 'packages/**/*.tsx',
147
+ 'workspaces/**/*.ts',
148
+ 'workspaces/**/*.tsx',
149
+ '*.ts',
150
+ ],
151
+ }),
152
+ },
153
+ {
154
+ path: 'prettier.config.ts',
155
+ content: "export { default } from '@vyriy/prettier-config';\n",
156
+ },
157
+ {
158
+ path: '.prettierignore',
159
+ content: 'node_modules\ndist\ncoverage\nstorybook-static\n',
43
160
  },
44
161
  {
45
- path: 'src/index.test.ts',
46
- content: "import { describe, expect, it } from '@jest/globals';\n\ndescribe('project', () => {\n it('has a test harness', () => {\n expect(true).toBe(true);\n });\n});\n",
162
+ path: 'eslint.config.ts',
163
+ content: "export { default } from '@vyriy/eslint-config';\n",
47
164
  },
48
165
  {
49
- path: 'src/types.ts',
50
- content: 'export type ProjectName = string;\n',
166
+ path: 'jest.config.ts',
167
+ content: "export { default } from '@vyriy/jest-config';\n",
51
168
  },
169
+ ...(shouldCreateStylelintConfig(plan)
170
+ ? [
171
+ {
172
+ path: 'stylelint.config.ts',
173
+ content: "export { default } from '@vyriy/stylelint-config';\n",
174
+ },
175
+ ]
176
+ : []),
177
+ ...plan.packages.flatMap((packagePlan) => createPackageFiles(plan, packagePlan)),
178
+ ...plan.workspaces.flatMap((workspacePlan) => createWorkspaceFiles(plan, workspacePlan)),
52
179
  ];
@@ -1,3 +1,3 @@
1
1
  import { CreateCiPlan } from './types.js';
2
- export declare const defaultValidationPipelines: readonly ["install", "typecheck", "lint", "prettier", "test", "build"];
2
+ export declare const defaultValidationPipelines: readonly ["install", "lint", "test", "build", "deploy", "smoke", "e2e"];
3
3
  export declare const createCiPlan: CreateCiPlan;
@@ -1,10 +1,11 @@
1
1
  export const defaultValidationPipelines = [
2
2
  'install',
3
- 'typecheck',
4
3
  'lint',
5
- 'prettier',
6
4
  'test',
7
5
  'build',
6
+ 'deploy',
7
+ 'smoke',
8
+ 'e2e',
8
9
  ];
9
10
  export const createCiPlan = ({ provider = 'none' } = {}) => provider === 'none'
10
11
  ? {
@@ -6,9 +6,10 @@ const baseFeatures = [
6
6
  'eslint',
7
7
  'prettier',
8
8
  'jest',
9
+ 'storybook',
9
10
  ];
10
11
  const presetFeatures = {
11
- library: ['react', 'storybook'],
12
+ library: ['react'],
12
13
  api: [],
13
14
  'react-csr': ['react', 'webpack'],
14
15
  'react-ssr': ['react', 'webpack'],
@@ -23,7 +24,11 @@ const presetFeatures = {
23
24
  'bff',
24
25
  ],
25
26
  fullstack: ['react', 'webpack'],
26
- 'aws-serverless': ['aws-cdk', 'lambda', 'dynamodb'],
27
+ 'aws-serverless': [
28
+ 'aws-cdk',
29
+ 'lambda',
30
+ 'apigateway',
31
+ ],
27
32
  empty: [],
28
33
  };
29
34
  const packagePlans = {
@@ -71,7 +76,7 @@ const packagePlans = {
71
76
  ],
72
77
  'aws-serverless': [
73
78
  { name: 'api', kind: 'api', publishable: false },
74
- { name: 'infrastructure', kind: 'core', publishable: false },
79
+ { name: 'stack', kind: 'stack', publishable: false },
75
80
  ],
76
81
  empty: [],
77
82
  };
@@ -105,7 +110,7 @@ const workspacePlans = {
105
110
  { name: 'web', kind: 'web' },
106
111
  { name: 'api', kind: 'api' },
107
112
  ],
108
- 'aws-serverless': [{ name: 'cdk', kind: 'cdk' }],
113
+ 'aws-serverless': [{ name: 'stack', kind: 'stack' }],
109
114
  empty: [],
110
115
  };
111
116
  const uniqueFeatures = (features) => [...new Set(features)];
@@ -1,24 +1,24 @@
1
1
  export type VyriyProjectKind = 'library' | 'api' | 'csr' | 'ssr' | 'ssg' | 'mfe' | 'fullstack' | 'aws-serverless' | 'empty';
2
2
  export type VyriyPreset = 'library' | 'api' | 'react-csr' | 'react-ssr' | 'react-ssg' | 'mfe' | 'openmfe' | 'mfe-bff' | 'openmfe-bff' | 'fullstack' | 'aws-serverless' | 'empty';
3
- export type VyriyFeature = 'typescript' | 'eslint' | 'prettier' | 'jest' | 'rest-api' | 'graphql-api' | 'react' | 'storybook' | 'webpack' | 'docker' | 'aws-cdk' | 'dynamodb' | 'lambda' | 'fargate' | 's3' | 'cloudfront' | 'openmfe' | 'bff';
3
+ export type VyriyFeature = 'typescript' | 'eslint' | 'prettier' | 'jest' | 'rest-api' | 'graphql-api' | 'react' | 'storybook' | 'webpack' | 'docker' | 'aws-cdk' | 'apigateway' | 'lambda' | 'fargate' | 's3' | 'cloudfront' | 'openmfe' | 'bff';
4
4
  export type VyriyPackagePlan = {
5
5
  readonly name: string;
6
- readonly kind: 'core' | 'ui' | 'api' | 'bff' | 'ssr' | 'ssg' | 'mfe' | 'contract';
6
+ readonly kind: 'core' | 'ui' | 'api' | 'bff' | 'ssr' | 'ssg' | 'mfe' | 'contract' | 'stack';
7
7
  readonly publishable: boolean;
8
8
  };
9
9
  export type VyriyWorkspacePlan = {
10
10
  readonly name: string;
11
- readonly kind: 'web' | 'api' | 'ssr' | 'ssg' | 'storybook' | 'bff' | 'mfe' | 'openmfe' | 'cdk';
11
+ readonly kind: 'web' | 'api' | 'ssr' | 'ssg' | 'storybook' | 'bff' | 'mfe' | 'openmfe' | 'stack';
12
12
  };
13
13
  export type VyriyCiProvider = 'gitlab' | 'github';
14
- export type VyriyCiPipeline = 'install' | 'typecheck' | 'lint' | 'prettier' | 'test' | 'build' | 'storybook' | 'docker' | 'npm-publish' | 'aws-deploy';
14
+ export type VyriyCiPipeline = 'install' | 'lint' | 'test' | 'build' | 'deploy' | 'smoke' | 'e2e';
15
15
  export type VyriyCiPlan = {
16
16
  readonly enabled: boolean;
17
17
  readonly providers: VyriyCiProvider[];
18
18
  readonly pipelines: VyriyCiPipeline[];
19
19
  };
20
20
  export type VyriyApiStyle = 'rest' | 'graphql' | 'mixed';
21
- export type VyriyApiRuntime = 'node' | 'lambda' | 'fargate';
21
+ export type VyriyApiRuntime = 'node' | 'lambda';
22
22
  export type VyriyApiPlan = {
23
23
  readonly enabled: boolean;
24
24
  readonly style: VyriyApiStyle;
@@ -16,10 +16,33 @@ const presets = [
16
16
  'empty',
17
17
  ];
18
18
  const extraFeatures = [
19
- 'storybook',
19
+ 'docker',
20
+ 'aws-api',
21
+ 'aws-fargate',
22
+ 'aws-static',
23
+ ];
24
+ const extraFeatureMap = {
25
+ docker: ['docker'],
26
+ 'aws-api': [
27
+ 'aws-cdk',
28
+ 'lambda',
29
+ 'apigateway',
30
+ ],
31
+ 'aws-fargate': [
32
+ 'aws-cdk',
33
+ 'fargate',
34
+ 'docker',
35
+ ],
36
+ 'aws-static': [
37
+ 'aws-cdk',
38
+ 's3',
39
+ 'cloudfront',
40
+ ],
41
+ };
42
+ const directFeatureInputs = [
20
43
  'docker',
21
44
  'aws-cdk',
22
- 'dynamodb',
45
+ 'apigateway',
23
46
  'lambda',
24
47
  'fargate',
25
48
  's3',
@@ -64,10 +87,15 @@ const parsePreset = (value, defaultValue) => {
64
87
  }
65
88
  return presets.includes(normalizedValue) ? normalizedValue : defaultValue;
66
89
  };
67
- const parseFeatures = (value) => value
68
- .split(',')
69
- .map((feature) => feature.trim())
70
- .filter((feature) => extraFeatures.includes(feature));
90
+ const parseFeatures = (value) => [
91
+ ...new Set(value
92
+ .split(',')
93
+ .map((feature) => feature.trim())
94
+ .flatMap((feature) => extraFeatureMap[feature] ??
95
+ (directFeatureInputs.includes(feature)
96
+ ? [feature]
97
+ : []))),
98
+ ];
71
99
  const parseApiStyle = (value, defaultValue) => {
72
100
  const normalizedValue = value.trim().toLowerCase();
73
101
  const numericValue = Number.parseInt(normalizedValue, 10);
@@ -109,9 +137,9 @@ export const askProjectPlan = async ({ defaults = {}, input = stdin, output = st
109
137
  output.write(' 3. github\n');
110
138
  const defaultCiProvider = defaults.ciProvider ?? 'none';
111
139
  const ciProvider = parseCiProvider(await promptWithDefault(question, 'CI/CD provider number or name', defaultCiProvider), defaultCiProvider);
112
- output.write('\nAdditional features, comma-separated:\n');
140
+ output.write('\nAdditional infrastructure, comma-separated:\n');
113
141
  output.write(` ${extraFeatures.join(', ')}\n`);
114
- const featuresAnswer = await promptWithDefault(question, 'Features', defaults.features?.join(', ') ?? '');
142
+ const featuresAnswer = await promptWithDefault(question, 'Infrastructure', defaults.features?.join(', ') ?? '');
115
143
  const features = parseFeatures(featuresAnswer);
116
144
  const plan = createProjectPlanFromPreset({
117
145
  projectName,