create-three-blocks-starter 0.0.10 → 0.0.11

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 (3) hide show
  1. package/LICENSE +199 -74
  2. package/bin/index.js +507 -157
  3. package/package.json +1 -1
package/LICENSE CHANGED
@@ -1,7 +1,10 @@
1
1
  Three.js Blocks Software License Agreement
2
+ Version 2.0 — Effective January 2026
2
3
 
3
4
  Proprietary Software — All Rights Reserved
4
- Copyright (c) 2025 three-blocks
5
+ Copyright (c) 2026 three-blocks
6
+
7
+ Full License Terms: https://threejs-blocks.com/license
5
8
 
6
9
  ================================================================================
7
10
 
@@ -9,25 +12,26 @@ LICENSE NOTICE
9
12
 
10
13
  This software and all associated packages, including but not limited to:
11
14
  - @three-blocks/core
15
+ - @three-blocks/pro
12
16
  - @three-blocks/starter
13
17
  - create-three-blocks-starter
14
18
  - All related tools, examples, and documentation code
15
19
 
16
- are licensed exclusively to active, paid subscribers of the threejs-blocks
17
- project. Without a valid and current subscription, you are NOT permitted to
20
+ are licensed exclusively to active, paid subscribers of the Three.js Blocks
21
+ project. Without a valid and current subscription, you are NOT permitted to
18
22
  use this software.
19
23
 
20
24
  ================================================================================
21
25
 
22
26
  PERMITTED USAGE (Active Subscribers Only)
23
27
 
24
- Subject to maintaining an active, paid subscription, you are granted a
25
- non-exclusive, non-transferable license to:
28
+ Subject to maintaining an active, paid subscription, you are granted a
29
+ non-exclusive, non-transferable, non-sublicensable license to:
26
30
 
27
- Use the software in personal and commercial projects via the public API
28
- Deploy applications incorporating the software to production environments
29
- Integrate the software into your applications as a functional component
30
- Access updates, bug fixes, and new features during active subscription
31
+ + Use the software in personal and commercial projects via the public API
32
+ + Deploy applications incorporating the software to production environments
33
+ + Integrate the software into your applications as a functional component
34
+ + Access updates, bug fixes, and new features during active subscription
31
35
 
32
36
  ================================================================================
33
37
 
@@ -35,58 +39,112 @@ RESTRICTIONS
35
39
 
36
40
  You may NOT:
37
41
 
38
- Reverse engineer, decompile, disassemble, or attempt to derive the source
42
+ - Reverse engineer, decompile, disassemble, or attempt to derive the source
39
43
  code, underlying algorithms, or techniques employed in this software
40
- Study, analyze, inspect, or learn from the implementation details, code
44
+ - Study, analyze, inspect, or learn from the implementation details, code
41
45
  structure, or internal workings of this software
42
- Modify, adapt, translate, or create derivative works
43
- Copy, share, distribute, publish, or resell any part of the software
44
- Sublicense or transfer rights to any third party
45
- Remove, alter, or obscure this license header or any copyright notices
46
- Use this software after your subscription has expired or been terminated
46
+ - Modify, adapt, translate, or create derivative works
47
+ - Copy, share, distribute, publish, or resell any part of the software
48
+ - Sublicense or transfer rights to any third party
49
+ - Remove, alter, or obscure this license header or any copyright notices
50
+ - Use this software after your subscription has expired or been terminated
47
51
 
48
52
  ================================================================================
49
53
 
50
- AI AND MACHINE LEARNING PROHIBITION
51
-
52
- The use of this source code for artificial intelligence and machine learning
53
- purposes is strictly prohibited. Specifically, you may NOT:
54
+ SUBLICENSING AND REDISTRIBUTION PROHIBITION
55
+
56
+ This license is strictly non-transferable and non-sublicensable. You may NOT:
57
+
58
+ - Sublicense, relicense, or transfer any rights granted herein to any third
59
+ party under any circumstances
60
+ - Create any SDK, library, framework, plugin, extension, or middleware that
61
+ redistributes, bundles, or incorporates this software for use by third
62
+ parties
63
+ - Bundle this software into products, applications, or services where
64
+ end-users do not hold their own valid subscription to Three.js Blocks
65
+ - Offer this software as part of any hosted service, software-as-a-service
66
+ (SaaS), platform-as-a-service (PaaS), or similar cloud-based offering
67
+ where third parties access the software's functionality
68
+ - Create wrapper libraries, abstraction layers, or integration packages that
69
+ obscure, circumvent, or eliminate the subscription requirement from
70
+ downstream users
71
+ - Include this software in any product, template, boilerplate, starter kit,
72
+ code generator, or scaffolding tool intended for distribution to third
73
+ parties
74
+ - Provide access to this software through any API, service endpoint, or
75
+ programmatic interface that enables third-party usage
76
+ - License, sell, rent, lease, or otherwise commercially exploit access to
77
+ this software to any third party
78
+
79
+ THIRD-PARTY INTEGRATION REQUIREMENT:
80
+ If you develop any software, SDK, library, framework, application, or service
81
+ that incorporates, depends upon, or integrates with this software, each
82
+ end-user of your software must independently hold their own valid, active
83
+ subscription to Three.js Blocks. You may not use your subscription to provide,
84
+ enable, or facilitate access to this software to any third party. Any attempt
85
+ to circumvent this requirement through technical or contractual means is
86
+ expressly prohibited and constitutes a material breach of this license.
54
87
 
55
- ✗ Use this source code, in whole or in part, to train, fine-tune, or develop
56
- any artificial intelligence models, machine learning systems, or large
57
- language models (LLMs)
58
-
59
- ✗ Feed this code into AI-assisted development tools for the purpose of
60
- generating derivative code or training said tools
61
-
62
- ✗ Use automated transformation tools or AI systems to create modified versions
63
- or derivatives of this software
64
-
65
- ✗ Extract patterns, algorithms, or techniques from this code using automated
66
- or AI-assisted analysis for incorporation into other software
67
-
68
- ✗ Use this code as training data for code completion, code generation, or
69
- code suggestion tools
88
+ ================================================================================
70
89
 
71
- This prohibition extends to any form of automated learning, pattern extraction,
72
- or knowledge transfer that involves processing this source code through machine
73
- learning pipelines, regardless of whether the output is used commercially or
74
- privately.
90
+ AI, MACHINE LEARNING, AND AUTOMATED ANALYSIS PROHIBITION
91
+
92
+ The use of this software, source code, compiled code, documentation, outputs,
93
+ or any data generated by this software for artificial intelligence, machine
94
+ learning, or automated analysis purposes is strictly prohibited. You may NOT:
95
+
96
+ - Use this code, in whole or in part, to train, fine-tune, pre-train, or
97
+ develop any artificial intelligence models, machine learning systems,
98
+ neural networks, or large language models (LLMs)
99
+ - Use this code for reinforcement learning from human feedback (RLHF),
100
+ constitutional AI training, or any similar AI alignment or training
101
+ methodology
102
+ - Create embeddings, vector representations, semantic indices, or any other
103
+ mathematical or computational representations of this code
104
+ - Store this code in vector databases, retrieval systems, knowledge bases,
105
+ or any storage mechanism intended for AI/ML retrieval-augmented generation
106
+ (RAG) or similar AI-assisted retrieval
107
+ - Generate synthetic data, pseudocode, paraphrased code, or derivative
108
+ representations from this codebase for any purpose
109
+ - Use automated tools, scripts, or AI systems to extract patterns,
110
+ algorithms, architectural knowledge, or implementation techniques from
111
+ this software
112
+ - Input this code into AI coding assistants, code completion tools, or
113
+ any AI-powered development environment for the purpose of analysis,
114
+ learning, pattern extraction, or code generation
115
+ - Use this code as context, examples, few-shot prompts, or reference
116
+ material for AI code generation, code completion, or code suggestion
117
+ - Feed this code into any automated transformation tool, transpiler, or
118
+ code converter that uses AI/ML techniques
119
+ - Use any output, behavior, or data generated by this software to train
120
+ or improve any AI/ML system
121
+
122
+ This prohibition applies to all AI/ML systems including but not limited to:
123
+ large language models (LLMs), code completion tools (e.g., GitHub Copilot,
124
+ Amazon CodeWhisperer, and similar tools), code generation systems, neural
125
+ networks, transformer models, diffusion models, and any future AI technologies
126
+ regardless of their architecture or methodology.
127
+
128
+ This prohibition is perpetual and survives any termination or expiration of
129
+ this license agreement.
75
130
 
76
131
  ================================================================================
77
132
 
78
133
  SUBSCRIPTION REQUIREMENT
79
134
 
80
135
  This license is valid only while you maintain an active, paid subscription to
81
- the threejs-blocks service. Upon expiration or termination of your subscription:
136
+ the Three.js Blocks service. Upon expiration or termination of your
137
+ subscription:
82
138
 
83
- 1. Your license to use this software immediately terminates
84
- 2. You must cease all use of the software
85
- 3. You must remove the software from all projects and systems
86
- 4. You must not deploy or distribute any applications containing this software
139
+ 1. Your license to use this software immediately and automatically terminates
140
+ 2. You must immediately cease all use of the software
141
+ 3. You must remove the software from all projects, systems, and deployments
142
+ 4. You must not deploy, distribute, or make available any applications
143
+ containing this software
144
+ 5. You must delete all copies of this software in your possession or control
87
145
 
88
146
  Failure to comply with these requirements constitutes copyright infringement
89
- and breach of contract.
147
+ and breach of contract, and may result in legal action.
90
148
 
91
149
  ================================================================================
92
150
 
@@ -95,48 +153,73 @@ PACKAGE-SPECIFIC NOTES
95
153
  @three-blocks/core:
96
154
  - Core library with proprietary algorithms and implementations
97
155
  - Protected by code obfuscation and this license
98
- - UNLICENSED - Subscription required
156
+ - Subscription required
157
+
158
+ @three-blocks/pro:
159
+ - Advanced physics engine with proprietary implementations
160
+ - Worker-based architecture with SharedArrayBuffer
161
+ - Protected by code obfuscation and this license
162
+ - Subscription required
99
163
 
100
164
  @three-blocks/starter:
101
165
  - Starter template for building Three.js applications
102
166
  - Includes licensed code and dependencies on @three-blocks/core
103
- - UNLICENSED - Subscription required
167
+ - Subscription required
104
168
 
105
169
  create-three-blocks-starter:
106
170
  - Scaffolding tool for creating new projects
107
171
  - Publicly available for convenience but proprietary
108
- - UNLICENSED - Creates projects that require subscription
109
-
110
- three-blocks-login:
111
- - Authentication helper for accessing private packages
112
- - MIT Licensed - May be used independently
172
+ - Creates projects that require subscription
113
173
 
114
174
  ================================================================================
115
175
 
116
176
  ENFORCEMENT
117
177
 
118
- Unauthorized use, reproduction, distribution, or violation of the AI/ML
119
- prohibition is strictly prohibited and may result in:
178
+ Unauthorized use, reproduction, distribution, reverse engineering, sublicensing,
179
+ or violation of the AI/ML prohibition is strictly prohibited and may result in:
120
180
 
121
- Immediate termination of your license and subscription
122
- Legal action for copyright infringement
123
- Claims for damages and recovery of costs
124
- Civil liability under applicable copyright law
125
- Criminal penalties under applicable law
181
+ * Immediate termination of your license and subscription without refund
182
+ * Legal action for copyright infringement
183
+ * Legal action for breach of contract
184
+ * Claims for damages, including actual damages, lost profits, and statutory
185
+ damages where available
186
+ * Recovery of attorneys' fees and costs of enforcement
187
+ * Injunctive relief to prevent further unauthorized use
188
+ * Civil liability under applicable copyright, trade secret, and contract law
189
+ * Criminal penalties under applicable law, including but not limited to the
190
+ Computer Fraud and Abuse Act (CFAA) and equivalent international statutes
126
191
 
127
192
  We employ various technical and legal measures to detect unauthorized use,
128
- including but not limited to code fingerprinting, usage analytics, and
129
- regular compliance audits.
193
+ including but not limited to code fingerprinting, license validation, usage
194
+ analytics, and regular compliance audits. We reserve the right to pursue all
195
+ available legal remedies against violators.
196
+
197
+ ================================================================================
198
+
199
+ INDEMNIFICATION
200
+
201
+ You agree to indemnify, defend, and hold harmless three-blocks, its affiliates,
202
+ officers, directors, employees, and agents from and against any and all claims,
203
+ damages, obligations, losses, liabilities, costs, and expenses (including
204
+ reasonable attorneys' fees) arising from: (a) your use of the software;
205
+ (b) your violation of any term of this license; (c) your violation of any
206
+ third-party right, including any intellectual property or privacy right; or
207
+ (d) any claim that your use of the software caused damage to a third party.
130
208
 
131
209
  ================================================================================
132
210
 
133
211
  ACCEPTANCE OF TERMS
134
212
 
135
- By using this software, you acknowledge that you have read, understood, and
136
- agree to be bound by the complete terms of this license agreement.
213
+ By downloading, installing, copying, accessing, or otherwise using this
214
+ software, you acknowledge that you have read, understood, and agree to be
215
+ bound by the complete terms of this license agreement.
137
216
 
138
217
  If you do not agree to these terms, you are not authorized to use, access,
139
- or download this software.
218
+ download, install, or copy this software.
219
+
220
+ If you are accepting these terms on behalf of an organization, you represent
221
+ and warrant that you have the authority to bind that organization to these
222
+ terms.
140
223
 
141
224
  ================================================================================
142
225
 
@@ -144,16 +227,56 @@ DISCLAIMER OF WARRANTY
144
227
 
145
228
  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
146
229
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
147
- FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
230
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NONINFRINGEMENT.
231
+
232
+ THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU.
233
+ SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
234
+ SERVICING, REPAIR, OR CORRECTION.
148
235
 
149
236
  ================================================================================
150
237
 
151
238
  LIMITATION OF LIABILITY
152
239
 
153
- IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR
154
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
155
- FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
156
- IN THE SOFTWARE.
240
+ IN NO EVENT SHALL THREE-BLOCKS, ITS AFFILIATES, LICENSORS, OR SUPPLIERS BE
241
+ LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, PUNITIVE, OR
242
+ EXEMPLARY DAMAGES, INCLUDING BUT NOT LIMITED TO DAMAGES FOR LOSS OF PROFITS,
243
+ GOODWILL, USE, DATA, OR OTHER INTANGIBLE LOSSES, ARISING OUT OF OR IN
244
+ CONNECTION WITH THIS LICENSE OR THE USE OR INABILITY TO USE THE SOFTWARE,
245
+ EVEN IF THREE-BLOCKS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
246
+
247
+ IN NO EVENT SHALL THREE-BLOCKS'S TOTAL CUMULATIVE LIABILITY TO YOU FOR ALL
248
+ CLAIMS ARISING OUT OF OR RELATED TO THIS LICENSE OR THE SOFTWARE EXCEED THE
249
+ AMOUNTS PAID BY YOU TO THREE-BLOCKS FOR THE SOFTWARE DURING THE TWELVE (12)
250
+ MONTHS IMMEDIATELY PRECEDING THE EVENT GIVING RISE TO LIABILITY.
251
+
252
+ SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
253
+ CONSEQUENTIAL DAMAGES, SO THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU.
254
+
255
+ ================================================================================
256
+
257
+ SEVERABILITY
258
+
259
+ If any provision of this license is held to be unenforceable or invalid, that
260
+ provision shall be enforced to the maximum extent possible, and the other
261
+ provisions shall remain in full force and effect.
262
+
263
+ ================================================================================
264
+
265
+ WAIVER
266
+
267
+ The failure of three-blocks to enforce any right or provision of this license
268
+ shall not constitute a waiver of such right or provision. No waiver of any
269
+ term of this license shall be deemed a further or continuing waiver of such
270
+ term or any other term.
271
+
272
+ ================================================================================
273
+
274
+ ENTIRE AGREEMENT
275
+
276
+ This license constitutes the entire agreement between you and three-blocks
277
+ regarding the software and supersedes all prior agreements, understandings,
278
+ and communications, whether written or oral, regarding the subject matter
279
+ hereof.
157
280
 
158
281
  ================================================================================
159
282
 
@@ -169,14 +292,16 @@ For license violations or to report unauthorized use:
169
292
 
170
293
  ================================================================================
171
294
 
172
- GOVERNING LAW
295
+ GOVERNING LAW AND JURISDICTION
173
296
 
174
297
  This license shall be governed by and construed in accordance with the laws
175
- of the jurisdiction in which three-blocks operates, without regard to its
176
- conflict of law provisions.
298
+ of Japan, without regard to its conflict of law provisions. Any dispute arising
299
+ out of or relating to this license or the software shall be subject to the
300
+ exclusive jurisdiction of the courts located in Tokyo, Japan, and you hereby
301
+ consent to the personal jurisdiction of such courts.
177
302
 
178
303
  ================================================================================
179
304
 
180
- Last Updated: January 2025
181
- Version: 1.0.0
305
+ Last Updated: January 2026
306
+ Version: 2.0
182
307
 
package/bin/index.js CHANGED
@@ -15,10 +15,7 @@ import path from 'node:path';
15
15
  import os from 'node:os';
16
16
  import { spawnSync } from 'node:child_process';
17
17
  import readline from 'node:readline';
18
- import { bold, cyan, dim, green, red, yellow } from 'kolorist';
19
-
20
- const ESC = ( n ) => `\u001b[${n}m`;
21
- const reset = ESC( 0 );
18
+ import { bold, cyan, dim, green, red } from 'kolorist';
22
19
 
23
20
  const STARTER_PKG = '@three-blocks/starter'; // private starter (lives in CodeArtifact)
24
21
  const LOGIN_CLI = 'three-blocks-login'; // public login CLI
@@ -139,7 +136,7 @@ const logSuccess = ( msg ) => {
139
136
 
140
137
  const logWarn = ( msg ) => {
141
138
 
142
- console.log( `${yellow( STEP_ICON )}${msg}` );
139
+ console.log( `${cyan( STEP_ICON )}${msg}` );
143
140
 
144
141
  };
145
142
 
@@ -588,20 +585,21 @@ const browserLogin = async ( channel ) => {
588
585
  }
589
586
 
590
587
  console.log( '' );
591
- logInfo( yellow( 'Browser didn\'t open? Use the URL below to sign in:' ) );
588
+ logInfo( cyan( 'Browser didn\'t open? Use the URL below to sign in:' ) );
592
589
  console.log( '' );
593
590
  console.log( ` ${cyan( verification_uri_complete || verification_uri )}` );
594
591
  console.log( '' );
595
592
  logInfo( dim( 'Waiting for authorization...' ) );
596
593
 
597
- const spinnerFrames = [ '', '', '', '', '', '', '', '', '', '' ];
594
+ const spinnerFrames = [ '', '', '', '', '', '', '', '', '', '', '■', '□' ];
598
595
  let spinnerIdx = 0;
599
596
  const spinnerInterval = setInterval( () => {
600
597
 
601
- process.stdout.write( `\r${cyan( spinnerFrames[ spinnerIdx ] )} Waiting for browser authorization...` );
598
+ const frame = spinnerFrames[ spinnerIdx ];
599
+ process.stdout.write( `\r${cyan( frame )} Waiting for browser authorization...` );
602
600
  spinnerIdx = ( spinnerIdx + 1 ) % spinnerFrames.length;
603
601
 
604
- }, 100 );
602
+ }, 60 );
605
603
 
606
604
  try {
607
605
 
@@ -759,98 +757,6 @@ const fetchTokenMetadata = async ( license, channel ) => {
759
757
 
760
758
  };
761
759
 
762
- const formatRegistryShort = ( value ) => {
763
-
764
- if ( ! value ) return '';
765
- try {
766
-
767
- const u = new URL( value );
768
- const pathname = ( u.pathname || '' ).replace( /\/$/, '' );
769
- return `${u.host}${pathname}`;
770
-
771
- } catch {
772
-
773
- return String( value );
774
-
775
- }
776
-
777
- };
778
-
779
- const formatExpiryLabel = ( iso ) => {
780
-
781
- if ( ! iso ) return 'Expires: —';
782
- const dt = new Date( iso );
783
- if ( Number.isNaN( dt.getTime() ) ) return `Expires: ${iso}`;
784
- return `Expires: ${dt.toISOString().replace( 'T', ' ' ).replace( 'Z', 'Z' )}`;
785
-
786
- };
787
-
788
- const HEADER_WIDTH = 118;
789
- const LEFT_WIDTH = 72;
790
- const RIGHT_WIDTH = HEADER_WIDTH - LEFT_WIDTH - 3;
791
-
792
- const repeatChar = ( ch, len ) => ch.repeat( Math.max( 0, len ) );
793
- const stripAnsi = ( value ) => String( value ?? '' ).replace( /\u001b\[[0-9;]*m/g, '' );
794
- const ellipsize = ( value, width ) => {
795
-
796
- const str = String( value ?? '' );
797
- const plain = stripAnsi( str );
798
- if ( plain.length <= width ) return str;
799
- if ( width <= 1 ) return plain.slice( 0, width );
800
- const truncated = plain.slice( 0, width - 1 ) + '…';
801
- return plain === str ? truncated : truncated;
802
-
803
- };
804
-
805
- const visibleLength = ( value ) => stripAnsi( String( value ?? '' ) ).length;
806
- const padText = ( value, width, align = 'left' ) => {
807
-
808
- const text = ellipsize( value, width );
809
- const remaining = width - visibleLength( text );
810
- if ( remaining <= 0 ) return text;
811
- if ( align === 'center' ) {
812
-
813
- const left = Math.floor( remaining / 2 );
814
- const right = remaining - left;
815
- return `${' '.repeat( left )}${text}${' '.repeat( right )}`;
816
-
817
- }
818
-
819
- if ( align === 'right' ) {
820
-
821
- return `${' '.repeat( remaining )}${text}`;
822
-
823
- }
824
-
825
- return `${text}${' '.repeat( remaining )}`;
826
-
827
- };
828
-
829
- const makeHeaderRow = ( left, right = '', leftAlign = 'left', rightAlign = 'left' ) =>
830
- `│${padText( left, LEFT_WIDTH, leftAlign )}│${padText( right, RIGHT_WIDTH, rightAlign )}│`;
831
-
832
- const HEADER_COLOR = ESC( '38;2;21;69;245' ); // threejs-blue (#1545F5)
833
- const CONTENT_COLOR = ESC( 90 );
834
- const reapplyColor = ( value, color ) => {
835
-
836
- const str = String( value ?? '' );
837
- return `${color}${str.split( reset ).join( `${reset}${color}` )}${reset}`;
838
-
839
- };
840
-
841
- const applyHeaderColor = ( row, { keepContentYellow = false, tintContent = true } = {} ) => {
842
-
843
- if ( keepContentYellow ) return reapplyColor( row, HEADER_COLOR );
844
- if ( ! row.startsWith( '│' ) || ! row.endsWith( '│' ) ) return reapplyColor( row, HEADER_COLOR );
845
- const match = row.match( /^│(.*)│(.*)│$/ );
846
- if ( ! match ) return reapplyColor( row, HEADER_COLOR );
847
- const [ , leftContent, rightContent ] = match;
848
- const leftSegment = tintContent ? reapplyColor( leftContent, CONTENT_COLOR ) : leftContent;
849
- const rightSegment = tintContent ? reapplyColor( rightContent, CONTENT_COLOR ) : rightContent;
850
- return `${HEADER_COLOR}│${reset}${leftSegment}${HEADER_COLOR}│${reset}${rightSegment}${HEADER_COLOR}│${reset}`;
851
-
852
- };
853
-
854
760
  const capitalize = ( value ) => {
855
761
 
856
762
  const str = String( value || '' ).trim();
@@ -921,61 +827,240 @@ const renderHeader = ( {
921
827
  starterVersion,
922
828
  userDisplayName,
923
829
  planName,
924
- repoPath,
925
830
  projectName,
926
- channel,
927
- coreVersion,
928
- license,
929
- registry,
930
- domain,
931
- region,
932
- repository,
933
- expiresAt,
934
- teamId,
935
- teamName,
936
- licenseId,
831
+ repoPath,
937
832
  } ) => {
938
833
 
939
- const welcome = userDisplayName ? `Welcome back ${userDisplayName}!` : 'Welcome!';
940
- const normalizedPlan = planName || 'Unknown plan';
941
- const resolvedTeamName = teamName || teamId || '';
942
- const planSuffix = resolvedTeamName ? ` · Team: ${resolvedTeamName}` : '';
943
- const planLine = `@three-blocks/core ${coreVersion || ( channel === 'stable' ? 'latest' : channel )} · ${normalizedPlan}${planSuffix}`;
944
- const channelDisplay = ( channel || 'stable' ).toUpperCase();
945
- const channelLine = `Channel: ${channelDisplay}${region ? ` · Region: ${region}` : ''}`;
946
- const repositoryLineBase = repository ? `Repository: ${repository}` : 'Repository: —';
947
- const registryShort = formatRegistryShort( registry );
948
- const repositoryLine = registryShort ? `${repositoryLineBase} → ${registryShort}` : repositoryLineBase;
949
- const registryLine = `Registry: ${registryShort || '—'}`;
950
- const licenseLine = `License: ${mask( license )}${licenseId ? ` · ${licenseId}` : ''}`;
951
- const expiresLine = formatExpiryLabel( expiresAt );
834
+ const welcome = userDisplayName ? `Welcome, ${userDisplayName}!` : 'Welcome!';
952
835
  const projectLabel = projectName || path.basename( repoPath );
953
- const projectLine = `Project: ${projectLabel}`;
954
- const domainLine = domain ? `Domain: ${domain}` : '';
955
- const regionLine = region ? `Region: ${region}` : '';
956
- const title = `─── Three.js Blocks Starter v${starterVersion || '?.?.?'} `;
957
- const separatorRow = makeHeaderRow( repeatChar( '', LEFT_WIDTH ), repeatChar( '─', RIGHT_WIDTH ) );
958
- const ascii = [
959
- 'THREE.JS',
960
- ' ______ __ ______ ______ __ __ ______ ',
961
- '/\\ == \\ /\\ \\ /\\ __ \\ /\\ ___\\ /\\ \\/ / /\\ ___\\ ',
962
- '\\ \\ __< \\ \\ \\____ \\ \\ \\/\\ \\ \\ \\ \\____ \\ \\ _"-. \\ \\___ \\ ',
963
- ' \\ \\_____\\ \\ \\_____\\ \\ \\_____\\ \\ \\_____\\ \\ \\_\\ \\_\\ \\/\\_____\\',
964
- ' \\/_____\/ \\/_____/ \\/_____/ \\/_____/ \\/_/ \/_/ \\/_____/'
965
- ];
966
- const lines = [
967
- applyHeaderColor( `╭${title}${repeatChar( '─', HEADER_WIDTH - 2 - title.length )}╮`, { keepContentYellow: true } ),
968
- ...ascii.map( ( row ) => applyHeaderColor( `│${padText( row, LEFT_WIDTH + RIGHT_WIDTH + 1, 'center' )}│`, { keepContentYellow: true } ) ),
969
- applyHeaderColor( separatorRow, { keepContentYellow: true } ),
970
- applyHeaderColor( makeHeaderRow( welcome, projectLine, 'center', 'center' ) ),
971
- applyHeaderColor( separatorRow, { keepContentYellow: true } ),
972
- applyHeaderColor( makeHeaderRow( planLine, channelLine ) ),
973
- applyHeaderColor( makeHeaderRow( repositoryLine, registryLine ) ),
974
- ...( domainLine || regionLine ? [ applyHeaderColor( makeHeaderRow( domainLine || '', regionLine, 'left', 'center' ) ) ] : [] ),
975
- applyHeaderColor( makeHeaderRow( licenseLine, expiresLine ) ),
976
- applyHeaderColor( `╰${repeatChar( '─', HEADER_WIDTH - 2 )}╯`, { keepContentYellow: true } ),
977
- ];
978
- for ( const row of lines ) console.log( row );
836
+ const normalizedPlan = planName || 'Unknown plan';
837
+
838
+ console.log( '' );
839
+ console.log( `${cyan( '▲' )} ${bold( 'three-blocks-starter' )} ${dim( `v${starterVersion || '?.?.?'}` )}` );
840
+ console.log( '' );
841
+ console.log( welcome );
842
+ console.log( dim( `${projectLabel} · ${normalizedPlan}` ) );
843
+ console.log( '' );
844
+
845
+ };
846
+
847
+ // ─────────────────────────────────────────────────────────────────────────────
848
+ // TEMPLATE SYSTEM
849
+ // ─────────────────────────────────────────────────────────────────────────────
850
+
851
+ const loadManifest = async ( targetDir ) => {
852
+
853
+ const manifestPath = path.join( targetDir, 'templates', 'manifest.json' );
854
+ try {
855
+
856
+ const raw = await fsp.readFile( manifestPath, 'utf8' );
857
+ return JSON.parse( raw );
858
+
859
+ } catch {
860
+
861
+ return null;
862
+
863
+ }
864
+
865
+ };
866
+
867
+ const copyDir = async ( src, dest, skipPatterns = [ 'node_modules', '.git', 'dist', 'pnpm-lock.yaml' ] ) => {
868
+
869
+ const entries = await fsp.readdir( src, { withFileTypes: true } );
870
+ for ( const entry of entries ) {
871
+
872
+ // Skip patterns (node_modules, .git, etc.)
873
+ if ( skipPatterns.includes( entry.name ) ) continue;
874
+
875
+ const srcPath = path.join( src, entry.name );
876
+ const destPath = path.join( dest, entry.name );
877
+
878
+ // Handle symlinks
879
+ if ( entry.isSymbolicLink() ) {
880
+
881
+ try {
882
+
883
+ const linkTarget = await fsp.readlink( srcPath );
884
+ await fsp.symlink( linkTarget, destPath ).catch( () => {} );
885
+
886
+ } catch {
887
+
888
+ // Skip broken symlinks
889
+ }
890
+
891
+ } else if ( entry.isDirectory() ) {
892
+
893
+ await fsp.mkdir( destPath, { recursive: true } );
894
+ await copyDir( srcPath, destPath, skipPatterns );
895
+
896
+ } else {
897
+
898
+ await fsp.copyFile( srcPath, destPath );
899
+
900
+ }
901
+
902
+ }
903
+
904
+ };
905
+
906
+ const applyTemplate = async ( targetDir, templateName ) => {
907
+
908
+ const manifest = await loadManifest( targetDir );
909
+ if ( ! manifest ) {
910
+
911
+ logWarn( 'No template manifest found, using base template.' );
912
+ return;
913
+
914
+ }
915
+
916
+ const template = manifest.templates.find( ( t ) => t.name === templateName );
917
+ if ( ! template ) {
918
+
919
+ throw new CliError(
920
+ `Template "${templateName}" not found.`,
921
+ { suggestion: `Available templates: ${manifest.templates.map( ( t ) => t.name ).join( ', ' )}` }
922
+ );
923
+
924
+ }
925
+
926
+ // 1. Copy overlay files (if any)
927
+ if ( template.overlay ) {
928
+
929
+ const overlayPath = path.join( targetDir, template.overlay );
930
+ if ( fs.existsSync( overlayPath ) ) {
931
+
932
+ logProgress( `Applying ${template.label || template.name} template overlay...` );
933
+ await copyDir( overlayPath, targetDir );
934
+
935
+ }
936
+
937
+ }
938
+
939
+ // 2. Merge dependencies into package.json
940
+ if ( template.dependencies && Object.keys( template.dependencies ).length > 0 ) {
941
+
942
+ const pkgPath = path.join( targetDir, 'package.json' );
943
+ try {
944
+
945
+ const pkgRaw = await fsp.readFile( pkgPath, 'utf8' );
946
+ const pkg = JSON.parse( pkgRaw );
947
+ pkg.dependencies = { ...pkg.dependencies, ...template.dependencies };
948
+ await fsp.writeFile( pkgPath, JSON.stringify( pkg, null, 2 ) );
949
+ logSuccess( `Added template dependencies: ${Object.keys( template.dependencies ).join( ', ' )}` );
950
+
951
+ } catch ( e ) {
952
+
953
+ logWarn( `Warning: could not merge template dependencies: ${e?.message || e}` );
954
+
955
+ }
956
+
957
+ }
958
+
959
+ // 3. Remove templates directory from generated app
960
+ const templatesDir = path.join( targetDir, 'templates' );
961
+ if ( fs.existsSync( templatesDir ) ) {
962
+
963
+ await fsp.rm( templatesDir, { recursive: true, force: true } );
964
+
965
+ }
966
+
967
+ };
968
+
969
+ const promptTemplateSelection = async ( manifest ) => {
970
+
971
+ const templates = manifest.templates;
972
+ const defaultIndex = templates.findIndex( ( t ) => t.name === manifest.default );
973
+
974
+ console.log( '' );
975
+ logInfo( dim( 'Select a template:' ) );
976
+ console.log( '' );
977
+
978
+ return await new Promise( ( resolve ) => {
979
+
980
+ const stdin = process.stdin;
981
+ const stdout = process.stdout;
982
+ let selected = defaultIndex >= 0 ? defaultIndex : 0;
983
+
984
+ const cleanup = () => {
985
+
986
+ stdin.removeListener( 'data', onData );
987
+ if ( stdin.isTTY ) stdin.setRawMode( false );
988
+ stdin.pause();
989
+
990
+ };
991
+
992
+ const render = () => {
993
+
994
+ for ( let i = 0; i < templates.length; i ++ ) {
995
+
996
+ const t = templates[ i ];
997
+ const prefix = i === selected ? cyan( '› ' ) : ' ';
998
+ const label = i === selected ? bold( t.label || t.name ) : dim( t.label || t.name );
999
+ const desc = dim( ` - ${t.description || ''}` );
1000
+ stdout.write( `${prefix}${label}${desc}\n` );
1001
+
1002
+ }
1003
+
1004
+ };
1005
+
1006
+ const clearLines = () => {
1007
+
1008
+ for ( let i = 0; i < templates.length; i ++ ) {
1009
+
1010
+ readline.moveCursor( stdout, 0, - 1 );
1011
+ readline.clearLine( stdout, 0 );
1012
+
1013
+ }
1014
+
1015
+ };
1016
+
1017
+ const onData = ( chunk ) => {
1018
+
1019
+ const str = String( chunk );
1020
+ for ( const ch of str ) {
1021
+
1022
+ if ( ch === '\u0003' ) {
1023
+
1024
+ cleanup();
1025
+ process.exit( 1 );
1026
+
1027
+ }
1028
+
1029
+ if ( ch === '\r' || ch === '\n' ) {
1030
+
1031
+ cleanup();
1032
+ console.log( '' );
1033
+ return resolve( templates[ selected ].name );
1034
+
1035
+ }
1036
+
1037
+ if ( ch === '\u001b' ) continue;
1038
+ if ( ch === '[' ) continue;
1039
+ if ( ch === 'A' || ch === 'k' ) {
1040
+
1041
+ clearLines();
1042
+ selected = ( selected - 1 + templates.length ) % templates.length;
1043
+ render();
1044
+
1045
+ } else if ( ch === 'B' || ch === 'j' ) {
1046
+
1047
+ clearLines();
1048
+ selected = ( selected + 1 ) % templates.length;
1049
+ render();
1050
+
1051
+ }
1052
+
1053
+ }
1054
+
1055
+ };
1056
+
1057
+ render();
1058
+ stdin.setEncoding( 'utf8' );
1059
+ if ( stdin.isTTY ) stdin.setRawMode( true );
1060
+ stdin.resume();
1061
+ stdin.on( 'data', onData );
1062
+
1063
+ } );
979
1064
 
980
1065
  };
981
1066
 
@@ -985,6 +1070,9 @@ async function main() {
985
1070
  let appName = '';
986
1071
  let channel = String( process.env.THREE_BLOCKS_CHANNEL || 'stable' ).toLowerCase();
987
1072
  let debug = DEBUG;
1073
+ let template = null;
1074
+ let listTemplates = false;
1075
+ let starterPath = process.env.THREE_BLOCKS_STARTER_PATH || '';
988
1076
  const userDisplayName = getUserDisplayName();
989
1077
  const repoPath = process.cwd();
990
1078
  for ( let i = 0; i < args.length; i ++ ) {
@@ -1002,6 +1090,50 @@ async function main() {
1002
1090
 
1003
1091
  }
1004
1092
 
1093
+ if ( a === '--template' || a === '-t' ) {
1094
+
1095
+ const v = args[ i + 1 ]; if ( v && ! v.startsWith( '-' ) ) {
1096
+
1097
+ template = String( v ).toLowerCase(); i ++;
1098
+
1099
+ }
1100
+
1101
+ continue;
1102
+
1103
+ }
1104
+
1105
+ const tm = a.match( /^--template=(.+)$/ );
1106
+ if ( tm ) {
1107
+
1108
+ template = String( tm[ 1 ] ).toLowerCase(); continue;
1109
+
1110
+ }
1111
+
1112
+ if ( a === '--list-templates' ) {
1113
+
1114
+ listTemplates = true; continue;
1115
+
1116
+ }
1117
+
1118
+ if ( a === '--starter-path' ) {
1119
+
1120
+ const v = args[ i + 1 ]; if ( v && ! v.startsWith( '-' ) ) {
1121
+
1122
+ starterPath = v; i ++;
1123
+
1124
+ }
1125
+
1126
+ continue;
1127
+
1128
+ }
1129
+
1130
+ const sp = a.match( /^--starter-path=(.+)$/ );
1131
+ if ( sp ) {
1132
+
1133
+ starterPath = sp[ 1 ]; continue;
1134
+
1135
+ }
1136
+
1005
1137
  if ( a === '--channel' || a === '-c' ) {
1006
1138
 
1007
1139
  const v = args[ i + 1 ]; if ( v ) {
@@ -1027,13 +1159,36 @@ async function main() {
1027
1159
  if ( DEBUG ) logDebug( 'Debug logging enabled.' );
1028
1160
 
1029
1161
  if ( ! [ 'stable', 'beta', 'alpha' ].includes( channel ) ) channel = 'stable';
1162
+
1163
+ // Handle --list-templates early exit
1164
+ if ( listTemplates ) {
1165
+
1166
+ console.log( '' );
1167
+ logInfo( bold( cyan( 'Available Templates' ) ) );
1168
+ console.log( '' );
1169
+ console.log( ` ${bold( 'starter' ).padEnd( 20 )} Base Three.js WebGPU template with PBF demo ${dim( '(default)' )}` );
1170
+ console.log( ` ${bold( 'game' ).padEnd( 20 )} Physics-driven scene with @three-blocks/pro` );
1171
+ console.log( '' );
1172
+ logInfo( dim( 'Usage: npx create-three-blocks-starter my-app --template game' ) );
1173
+ console.log( '' );
1174
+ process.exit( 0 );
1175
+
1176
+ }
1177
+
1030
1178
  if ( ! appName ) {
1031
1179
 
1032
1180
  die(
1033
1181
  `Usage:\n` +
1034
- ` THREE_BLOCKS_SECRET_KEY=sk_live_... npx create-three-blocks-starter <directory> [--channel <stable|beta|alpha>]\n` +
1035
- ` npx create-three-blocks-starter <directory> (will prompt for the license key)\n` +
1036
- ` Add --debug for verbose logging.`
1182
+ ` npx create-three-blocks-starter <directory> [options]\n\n` +
1183
+ `Options:\n` +
1184
+ ` --template, -t <name> Use a specific template (starter, game)\n` +
1185
+ ` --list-templates List available templates\n` +
1186
+ ` --channel, -c <channel> Release channel (stable|beta|alpha)\n` +
1187
+ ` --starter-path <path> Use local starter path (for development)\n` +
1188
+ ` --debug, -d Enable verbose logging\n\n` +
1189
+ `Examples:\n` +
1190
+ ` npx create-three-blocks-starter my-app\n` +
1191
+ ` npx create-three-blocks-starter my-app --template game`
1037
1192
  );
1038
1193
 
1039
1194
  }
@@ -1041,6 +1196,160 @@ async function main() {
1041
1196
  const targetDir = path.resolve( process.cwd(), appName );
1042
1197
  await ensureEmptyDir( targetDir );
1043
1198
 
1199
+ // Early exit for local development with --starter-path (skip auth entirely)
1200
+ if ( starterPath ) {
1201
+
1202
+ const localPath = path.resolve( starterPath );
1203
+ if ( ! fs.existsSync( localPath ) ) {
1204
+
1205
+ throw new CliError(
1206
+ `Local starter path not found: ${localPath}`,
1207
+ { suggestion: 'Check that --starter-path points to a valid directory.' }
1208
+ );
1209
+
1210
+ }
1211
+
1212
+ console.log( '' );
1213
+ logInfo( bold( cyan( 'Three Blocks Starter' ) ) + ' ' + dim( '[LOCAL DEV MODE]' ) );
1214
+ console.log( '' );
1215
+ logProgress( `Using local starter from ${dim( localPath )} ...` );
1216
+
1217
+ // Read version from local package.json
1218
+ let localVersion = 'local';
1219
+ try {
1220
+
1221
+ const localPkg = JSON.parse( fs.readFileSync( path.join( localPath, 'package.json' ), 'utf8' ) );
1222
+ localVersion = localPkg.version || 'local';
1223
+
1224
+ } catch {}
1225
+
1226
+ logProgress( 'Copying files ...' );
1227
+ await copyDir( localPath, targetDir );
1228
+
1229
+ // Handle interactive template selection (if TTY and no --template specified)
1230
+ let selectedTemplate = template;
1231
+ if ( ! selectedTemplate && process.stdin.isTTY ) {
1232
+
1233
+ const manifest = await loadManifest( targetDir );
1234
+ if ( manifest && manifest.templates && manifest.templates.length > 1 ) {
1235
+
1236
+ selectedTemplate = await promptTemplateSelection( manifest );
1237
+
1238
+ } else {
1239
+
1240
+ selectedTemplate = manifest?.default || 'starter';
1241
+
1242
+ }
1243
+
1244
+ } else if ( ! selectedTemplate ) {
1245
+
1246
+ selectedTemplate = 'starter';
1247
+
1248
+ }
1249
+
1250
+ // Apply template overlay
1251
+ if ( selectedTemplate && selectedTemplate !== 'starter' ) {
1252
+
1253
+ await applyTemplate( targetDir, selectedTemplate );
1254
+
1255
+ } else {
1256
+
1257
+ const templatesDir = path.join( targetDir, 'templates' );
1258
+ if ( fs.existsSync( templatesDir ) ) {
1259
+
1260
+ await fsp.rm( templatesDir, { recursive: true, force: true } );
1261
+
1262
+ }
1263
+
1264
+ }
1265
+
1266
+ // Add pnpm overrides for local development (link to parent monorepo packages)
1267
+ const pkgPath = path.join( targetDir, 'package.json' );
1268
+ const parentDir = path.dirname( localPath );
1269
+ const coreRelPath = path.relative( targetDir, path.join( parentDir, 'three-blocks-core' ) );
1270
+ const proRelPath = path.relative( targetDir, path.join( parentDir, 'three-blocks-pro' ) );
1271
+
1272
+ try {
1273
+
1274
+ const pkgRaw = await fsp.readFile( pkgPath, 'utf8' );
1275
+ const pkg = JSON.parse( pkgRaw );
1276
+
1277
+ pkg.pnpm = pkg.pnpm || {};
1278
+ pkg.pnpm.overrides = pkg.pnpm.overrides || {};
1279
+
1280
+ // Link to local packages if they exist
1281
+ if ( fs.existsSync( path.join( parentDir, 'three-blocks-core' ) ) ) {
1282
+
1283
+ pkg.pnpm.overrides[ '@three-blocks/core' ] = `link:${coreRelPath}`;
1284
+
1285
+ }
1286
+
1287
+ if ( fs.existsSync( path.join( parentDir, 'three-blocks-pro' ) ) ) {
1288
+
1289
+ pkg.pnpm.overrides[ '@three-blocks/pro' ] = `link:${proRelPath}`;
1290
+
1291
+ }
1292
+
1293
+ await fsp.writeFile( pkgPath, JSON.stringify( pkg, null, 2 ) );
1294
+ logProgress( 'Added pnpm overrides for local package linking' );
1295
+
1296
+ } catch ( e ) {
1297
+
1298
+ logWarn( `Warning: could not add pnpm overrides: ${e?.message || e}` );
1299
+
1300
+ }
1301
+
1302
+ // Add Vite aliases to use source files directly (no build required)
1303
+ const viteConfigPath = path.join( targetDir, 'vite.config.js' );
1304
+ try {
1305
+
1306
+ let viteConfig = await fsp.readFile( viteConfigPath, 'utf8' );
1307
+
1308
+ // Build alias entries for source directories
1309
+ const aliases = [];
1310
+ if ( fs.existsSync( path.join( parentDir, 'three-blocks-core', 'src' ) ) ) {
1311
+
1312
+ aliases.push( `\t\t\t{\n\t\t\t\tfind: '@three-blocks/core',\n\t\t\t\treplacement: fileURLToPath( new URL( '${coreRelPath}/src', import.meta.url ) ),\n\t\t\t}` );
1313
+
1314
+ }
1315
+
1316
+ if ( fs.existsSync( path.join( parentDir, 'three-blocks-pro', 'src' ) ) ) {
1317
+
1318
+ aliases.push( `\t\t\t{\n\t\t\t\tfind: '@three-blocks/pro',\n\t\t\t\treplacement: fileURLToPath( new URL( '${proRelPath}/src', import.meta.url ) ),\n\t\t\t}` );
1319
+
1320
+ }
1321
+
1322
+ if ( aliases.length > 0 ) {
1323
+
1324
+ // Insert aliases after the existing @ alias
1325
+ const aliasInsert = ',\n' + aliases.join( ',\n' );
1326
+ viteConfig = viteConfig.replace(
1327
+ /(\{\s*find:\s*'@',\s*replacement:[^}]+\})/,
1328
+ `$1${aliasInsert}`
1329
+ );
1330
+
1331
+ await fsp.writeFile( viteConfigPath, viteConfig );
1332
+ logProgress( 'Added Vite aliases for source-level development (no build required)' );
1333
+
1334
+ }
1335
+
1336
+ } catch ( e ) {
1337
+
1338
+ logWarn( `Warning: could not add Vite aliases: ${e?.message || e}` );
1339
+
1340
+ }
1341
+
1342
+ logSuccess( `Starter scaffolded in ${bold( targetDir )} (template: ${selectedTemplate})` );
1343
+ console.log( '' );
1344
+ logInfo( dim( 'Next steps:' ) );
1345
+ logInfo( dim( ` cd ${appName}` ) );
1346
+ logInfo( dim( ' pnpm install' ) );
1347
+ logInfo( dim( ' pnpm dev' ) );
1348
+ console.log( '' );
1349
+ process.exit( 0 );
1350
+
1351
+ }
1352
+
1044
1353
  // Check for --browser or --key flags
1045
1354
  const useBrowserFlag = args.includes( '--browser' ) || args.includes( '-b' );
1046
1355
  const useKeyFlag = args.includes( '--key' ) || args.includes( '-k' );
@@ -1200,7 +1509,10 @@ async function main() {
1200
1509
  if ( ! headerRegistry && registryUrl ) headerRegistry = registryUrl;
1201
1510
  const authHostList = Array.from( tempAuthHosts );
1202
1511
 
1203
- // 3) Scaffold the private starter by packing and extracting the tarball (avoids npm create naming transform)
1512
+ // 3) Scaffold the starter (fetch from npm)
1513
+ let headerStarterVersion = '';
1514
+
1515
+ // Use npm pack to fetch the tarball
1204
1516
  const starterSpec = `${STARTER_PKG}@${channel === 'stable' ? 'latest' : channel}`;
1205
1517
  logProgress( `Fetching starter tarball ${starterSpec} ...` );
1206
1518
  const createEnv = {
@@ -1279,7 +1591,7 @@ async function main() {
1279
1591
 
1280
1592
  }
1281
1593
 
1282
- const headerStarterVersion = starterVersion || extractVersionFromTarball( tarName );
1594
+ headerStarterVersion = starterVersion || extractVersionFromTarball( tarName );
1283
1595
  const headerCoreVersion = headerChannel === 'stable' ? 'latest' : headerChannel;
1284
1596
  renderHeader( {
1285
1597
  starterVersion: headerStarterVersion,
@@ -1325,6 +1637,44 @@ async function main() {
1325
1637
 
1326
1638
  }
1327
1639
 
1640
+ // 3.5) Handle interactive template selection (if TTY and no --template specified)
1641
+ let selectedTemplate = template;
1642
+ if ( ! selectedTemplate && process.stdin.isTTY ) {
1643
+
1644
+ const manifest = await loadManifest( targetDir );
1645
+ if ( manifest && manifest.templates && manifest.templates.length > 1 ) {
1646
+
1647
+ selectedTemplate = await promptTemplateSelection( manifest );
1648
+
1649
+ } else {
1650
+
1651
+ selectedTemplate = manifest?.default || 'starter';
1652
+
1653
+ }
1654
+
1655
+ } else if ( ! selectedTemplate ) {
1656
+
1657
+ selectedTemplate = 'starter'; // Default for non-TTY
1658
+
1659
+ }
1660
+
1661
+ // 3.6) Apply template overlay
1662
+ if ( selectedTemplate && selectedTemplate !== 'starter' ) {
1663
+
1664
+ await applyTemplate( targetDir, selectedTemplate );
1665
+
1666
+ } else {
1667
+
1668
+ // Still clean up templates directory for base template
1669
+ const templatesDir = path.join( targetDir, 'templates' );
1670
+ if ( fs.existsSync( templatesDir ) ) {
1671
+
1672
+ await fsp.rm( templatesDir, { recursive: true, force: true } );
1673
+
1674
+ }
1675
+
1676
+ }
1677
+
1328
1678
  // 4) Write .env.local and .gitignore entries
1329
1679
  await fsp.writeFile( path.join( targetDir, '.env.local' ),
1330
1680
  `THREE_BLOCKS_SECRET_KEY=${license}\nTHREE_BLOCKS_CHANNEL=${channel}\n`, { mode: 0o600 } ).catch( () => {} );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-three-blocks-starter",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Create a new Three Blocks starter app",