happy-stacks 0.6.6 → 0.6.7

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.
@@ -45,7 +45,7 @@ Progress tracking (update manually while auditing):
45
45
  - Next unreviewed commit: n/a (complete)
46
46
  - Pass 2 full-diff verification markers: `249/249` (each `## NNN` has a `Pass2FullDiff:` line; patch artifacts under `/tmp/leeroy-wip-pass2/NNN.patch`)
47
47
  - Pass 2 diff capture: `249/249` (`/tmp/leeroy-wip-pass2/NNN.patch` exists; hash/size recorded in each `Pass2FullDiff:` line)
48
- - Pass 2 notes verification (manual): `10/249` (this requires reading the patch and confirming/updating `### Manual Review Notes`)
48
+ - Pass 2 notes verification (manual): `17/249` (this requires reading the patch and confirming/updating `### Manual Review Notes`)
49
49
 
50
50
  Current state (2026-01-24):
51
51
 
@@ -363,7 +363,7 @@ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=c2d3507357dc patchFile=/
363
363
  ---
364
364
 
365
365
  ## 012 2026-01-17 207ef1b2f572 `dev`
366
- Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=207ef1b2f572 patchFile=/tmp/leeroy-wip-pass2/012.patch patchSha256=a9b3dfe31a3465be8415ccab235bb3bef26cb918530e86d596b6075891c94423 patchBytes=11240 patchLines=240 notesReviewed=no reviewedAt=- notesUpdated=no
366
+ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=207ef1b2f572 patchFile=/tmp/leeroy-wip-pass2/012.patch patchSha256=a9b3dfe31a3465be8415ccab235bb3bef26cb918530e86d596b6075891c94423 patchBytes=11240 patchLines=240 notesReviewed=yes reviewedAt=2026-01-24T18:10:19+0100 notesUpdated=no
367
367
 
368
368
  - Subject: fix(dev): gate CLI detection logging
369
369
  - Reasons: full manual review coverage (was not in the manual-review queue)
@@ -388,7 +388,7 @@ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=207ef1b2f572 patchFile=/
388
388
  ---
389
389
 
390
390
  ## 013 2026-01-17 58a892d413d1 `command-palette`
391
- Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=58a892d413d1 patchFile=/tmp/leeroy-wip-pass2/013.patch patchSha256=1a11a0e1395cd3aa82663418fb8d16080ee1348ac4e44b1c39132dd733b6ee00 patchBytes=1434 patchLines=36 notesReviewed=no reviewedAt=- notesUpdated=no
391
+ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=58a892d413d1 patchFile=/tmp/leeroy-wip-pass2/013.patch patchSha256=1a11a0e1395cd3aa82663418fb8d16080ee1348ac4e44b1c39132dd733b6ee00 patchBytes=1434 patchLines=36 notesReviewed=yes reviewedAt=2026-01-24T18:10:19+0100 notesUpdated=no
392
392
 
393
393
  - Subject: fix(command-palette): include navigate dependency
394
394
  - Reasons: full manual review coverage (was not in the manual-review queue)
@@ -412,7 +412,7 @@ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=58a892d413d1 patchFile=/
412
412
  ---
413
413
 
414
414
  ## 014 2026-01-17 41479b35415f `env`
415
- Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=41479b35415f patchFile=/tmp/leeroy-wip-pass2/014.patch patchSha256=0a14bef9963c50010d2c6baa8bdc255195f7ac07b50ecece4fcaf9c0fcd9a657 patchBytes=5851 patchLines=141 notesReviewed=no reviewedAt=- notesUpdated=no
415
+ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=41479b35415f patchFile=/tmp/leeroy-wip-pass2/014.patch patchSha256=0a14bef9963c50010d2c6baa8bdc255195f7ac07b50ecece4fcaf9c0fcd9a657 patchBytes=5851 patchLines=141 notesReviewed=yes reviewedAt=2026-01-24T18:10:19+0100 notesUpdated=no
416
416
 
417
417
  - Subject: feat(env): add env var template parsing
418
418
  - Reasons: full manual review coverage (was not in the manual-review queue)
@@ -440,7 +440,7 @@ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=41479b35415f patchFile=/
440
440
  ---
441
441
 
442
442
  ## 015 2026-01-17 695bcd09eb9e `env`
443
- Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=695bcd09eb9e patchFile=/tmp/leeroy-wip-pass2/015.patch patchSha256=2ce8f5113f7e010edb0f7ddce7fa3f1ad2ef7e7f57b8074c9ad0d46707d5d2b5 patchBytes=24232 patchLines=540 notesReviewed=no reviewedAt=- notesUpdated=no
443
+ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=695bcd09eb9e patchFile=/tmp/leeroy-wip-pass2/015.patch patchSha256=2ce8f5113f7e010edb0f7ddce7fa3f1ad2ef7e7f57b8074c9ad0d46707d5d2b5 patchBytes=24232 patchLines=540 notesReviewed=yes reviewedAt=2026-01-24T18:10:19+0100 notesUpdated=no
444
444
 
445
445
  - Subject: fix(env): improve remote env resolution and previews
446
446
  - Reasons: full manual review coverage (was not in the manual-review queue)
@@ -474,7 +474,7 @@ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=695bcd09eb9e patchFile=/
474
474
  ---
475
475
 
476
476
  ## 016 2026-01-17 6cdb90a49aad `env`
477
- Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=6cdb90a49aad patchFile=/tmp/leeroy-wip-pass2/016.patch patchSha256=c1429ac752ff6a5828610cd00f89c3490559f5ebdde07fa158afececee5239fb patchBytes=90044 patchLines=2144 notesReviewed=no reviewedAt=- notesUpdated=no
477
+ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=6cdb90a49aad patchFile=/tmp/leeroy-wip-pass2/016.patch patchSha256=c1429ac752ff6a5828610cd00f89c3490559f5ebdde07fa158afececee5239fb patchBytes=90044 patchLines=2144 notesReviewed=yes reviewedAt=2026-01-24T18:10:19+0100 notesUpdated=no
478
478
 
479
479
  - Subject: feat(env): update env var list, cards, and preview modal
480
480
  - Reasons: full manual review coverage (was not in the manual-review queue)
@@ -510,7 +510,7 @@ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=6cdb90a49aad patchFile=/
510
510
  ---
511
511
 
512
512
  ## 017 2026-01-17 65bae55a0b61 `i18n`
513
- Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=65bae55a0b61 patchFile=/tmp/leeroy-wip-pass2/017.patch patchSha256=9734e2764a6330f2d829d9fd3a1e995d14127da3449088ffb2335f5a732ba331 patchBytes=181014 patchLines=3802 notesReviewed=no reviewedAt=- notesUpdated=yes
513
+ Pass2FullDiff: CAPTURED at 2026-01-24T17:39:06+0100 sha=65bae55a0b61 patchFile=/tmp/leeroy-wip-pass2/017.patch patchSha256=9734e2764a6330f2d829d9fd3a1e995d14127da3449088ffb2335f5a732ba331 patchBytes=181014 patchLines=3802 notesReviewed=yes reviewedAt=2026-01-24T18:10:19+0100 notesUpdated=yes
514
514
 
515
515
  - Subject: refactor(i18n): separate translation types and content
516
516
  - Reasons: large change (+1756/-1006)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "happy-stacks",
3
3
  "type": "module",
4
- "version": "0.6.6",
4
+ "version": "0.6.7",
5
5
  "packageManager": "pnpm@10.18.3",
6
6
  "bin": {
7
7
  "happys": "./bin/happys.mjs",
package/scripts/setup.mjs CHANGED
@@ -262,59 +262,37 @@ async function maybeConfigureAuthDefaults({ rootDir, profile, interactive }) {
262
262
  `Recommended: set up a dedicated ${cyan('dev-auth')} seed stack so you authenticate once, then new stacks “just work”.`
263
263
  )
264
264
  );
265
+ const seedChoice = 'dev-auth';
266
+ const linkChoice = 'link';
265
267
 
266
- const options = [];
267
- if (sources.hasDevAuthAccessKey) {
268
- options.push({
269
- label: `${green('✓')} use ${cyan('dev-auth')} seed stack (${green('recommended')}) — already authenticated`,
270
- value: 'use-dev-auth',
271
- });
272
- } else {
273
- options.push({
274
- label: `create ${cyan('dev-auth')} seed stack (${green('recommended')}) — login once, reuse across stacks`,
275
- value: 'create-dev-auth',
268
+ if (!sources.hasDevAuthAccessKey) {
269
+ const wantLoginNow = await withRl(async (rl) => {
270
+ return await promptSelect(rl, {
271
+ title:
272
+ `${bold('Sign in now?')}\n` +
273
+ `${dim('This will create a dedicated dev-auth seed stack and walk you through a guided login in the browser.')}\n` +
274
+ `${dim('After this, new stacks can reuse your auth automatically (recommended).')}`,
275
+ options: [
276
+ { label: `yes (${green('recommended')}) — sign in now`, value: true },
277
+ { label: `no — I will do this later`, value: false },
278
+ ],
279
+ defaultIndex: 0,
280
+ });
276
281
  });
277
- }
278
- if (sources.hasMainAccessKey) {
279
- options.push({ label: `use ${cyan('main')} as seed — fast, but shares identity with main`, value: 'main' });
280
- }
281
- if (sources.hasLegacyAccessKey) {
282
- options.push({ label: `use legacy ${cyan('~/.happy')} as seed — best-effort`, value: 'legacy' });
283
- }
284
- options.push({ label: `skip for now — you can do this later`, value: 'skip' });
285
282
 
286
- const choice = await withRl(async (rl) => {
287
- return await promptSelect(rl, {
288
- title: bold('Choose an auth strategy'),
289
- options,
290
- defaultIndex: 0,
291
- });
292
- });
283
+ if (!wantLoginNow) {
284
+ // eslint-disable-next-line no-console
285
+ console.log(dim(`Tip: run ${yellow('happys stack create-dev-auth-seed dev-auth --login')} anytime to sign in.`));
286
+ return;
287
+ }
293
288
 
294
- if (choice === 'skip') {
289
+ // Guided wizard: creates stack, starts temporary UI/server, stores dev key (optional), logs in CLI.
290
+ await runNodeScript({ rootDir, rel: 'scripts/stack.mjs', args: ['create-dev-auth-seed', 'dev-auth', '--login'] });
291
+ } else {
295
292
  // eslint-disable-next-line no-console
296
- console.log(dim(`Tip: run ${yellow('happys stack create-dev-auth-seed')} anytime to set this up.`));
297
- return;
298
- }
299
-
300
- const seedChoice = choice === 'create-dev-auth' || choice === 'use-dev-auth' ? 'dev-auth' : String(choice);
301
- if (choice === 'create-dev-auth') {
302
- // Guided wizard: creates stack, starts temporary UI/server, saves dev key (optional), logs in CLI.
303
- await runNodeScript({ rootDir, rel: 'scripts/stack.mjs', args: ['create-dev-auth-seed', 'dev-auth'] });
293
+ console.log(dim(`Found an existing ${cyan('dev-auth')} seed stack; configuring auth reuse for new stacks.`));
304
294
  }
305
295
 
306
- // Symlink vs copy for seeded stacks (preferred: symlink so credentials stay up to date).
307
- const linkChoice = await withRl(async (rl) => {
308
- return await promptSelect(rl, {
309
- title: `${bold('Auth seeding mode')}\n${dim('When seeding credentials into stacks, should we symlink or copy?')}`,
310
- options: [
311
- { label: `symlink (${green('recommended')}) — stays up to date`, value: 'link' },
312
- { label: `copy — more isolated per stack`, value: 'copy' },
313
- ],
314
- defaultIndex: 0,
315
- });
316
- });
317
-
318
296
  await ensureEnvLocalUpdated({
319
297
  rootDir,
320
298
  updates: [
@@ -333,14 +311,15 @@ async function maybeConfigureAuthDefaults({ rootDir, profile, interactive }) {
333
311
  if (candidateTargets.length) {
334
312
  const seedNow = await withRl(async (rl) => {
335
313
  return await promptSelect(rl, {
336
- title: `${bold('Seed existing stacks?')}\n${dim(
337
- `We found ${candidateTargets.length} existing stack(s) that could reuse auth from ${cyan(seedChoice)}.`
338
- )}\n${dim('This can fix “auth required / no machine” without re-login.')}`,
314
+ title:
315
+ `${bold('Apply sign-in to existing stacks?')}\n` +
316
+ `${dim(`We found ${candidateTargets.length} existing stack(s) that could reuse your auth automatically.`)}\n` +
317
+ `${dim('This can fix “auth required / no machine” without re-login.')}`,
339
318
  options: [
340
- { label: `yes — seed ${candidateTargets.length} stack(s) now`, value: true },
341
- { label: 'no (default)', value: false },
319
+ { label: `yes (${green('recommended')}) apply to ${candidateTargets.length} stack(s) now`, value: true },
320
+ { label: 'no — leave them as-is', value: false },
342
321
  ],
343
- defaultIndex: 1,
322
+ defaultIndex: 0,
344
323
  });
345
324
  });
346
325
  if (seedNow) {
@@ -360,7 +339,7 @@ async function maybeConfigureAuthDefaults({ rootDir, profile, interactive }) {
360
339
  console.log(dim('No existing stacks detected that need seeding (nothing to do).'));
361
340
  }
362
341
 
363
- // Dev key UX (for phone/Playwright restores). Keep it explicit because it’s sensitive.
342
+ // Dev key UX (for phone/Playwright restores).
364
343
  const sourcesAfter = detectAuthSources();
365
344
  if (sourcesAfter.hasDevKey) {
366
345
  // eslint-disable-next-line no-console
@@ -371,19 +350,8 @@ async function maybeConfigureAuthDefaults({ rootDir, profile, interactive }) {
371
350
  console.log(dim('This lets you restore the UI account quickly (and can help automation).'));
372
351
  // eslint-disable-next-line no-console
373
352
  console.log(dim(`Stored at: ${sourcesAfter.devKeyPath}`));
374
- const keyChoice = await withRl(async (rl) => {
375
- return await promptSelect(rl, {
376
- title: 'Do you want to print it now?',
377
- options: [
378
- { label: `yes — print dev key (${yellow('will display a secret')})`, value: 'print' },
379
- { label: 'no (default) — keep it private', value: 'skip' },
380
- ],
381
- defaultIndex: 1,
382
- });
383
- });
384
- if (keyChoice === 'print') {
385
- await runNodeScript({ rootDir, rel: 'scripts/auth.mjs', args: ['dev-key', '--print'] });
386
- }
353
+ // eslint-disable-next-line no-console
354
+ console.log(dim(`Tip: to print it later, run: ${yellow('happys auth dev-key --print')}`));
387
355
  } else {
388
356
  // eslint-disable-next-line no-console
389
357
  console.log(dim(`Tip: to store a dev key later, run: ${yellow('happys auth dev-key --set "<key>"')}`));
package/scripts/stack.mjs CHANGED
@@ -2158,6 +2158,8 @@ async function cmdCreateDevAuthSeed({ rootDir, argv }) {
2158
2158
  const serverComponent = (kv.get('--server') ?? '').trim() || 'happy-server-light';
2159
2159
  const interactive = !flags.has('--non-interactive') && (flags.has('--interactive') || isTty());
2160
2160
  const bindMode = resolveBindModeFromArgs({ flags, kv });
2161
+ const forceLogin =
2162
+ flags.has('--login') ? true : flags.has('--no-login') || flags.has('--skip-login') ? false : null;
2161
2163
 
2162
2164
  if (json) {
2163
2165
  // Keep JSON mode non-interactive and stable by using the existing stack command output.
@@ -2206,14 +2208,17 @@ async function cmdCreateDevAuthSeed({ rootDir, argv }) {
2206
2208
  if (interactive) {
2207
2209
  await withRl(async (rl) => {
2208
2210
  let savedDevKey = false;
2209
- const wantLogin = await promptSelect(rl, {
2210
- title: `${bold('dev-auth seed stack')}\n${dim('Recommended: do the guided login now so the seed is ready immediately.')}`,
2211
- options: [
2212
- { label: `yes (${green('recommended')}) — start temporary server + UI and log in`, value: true },
2213
- { label: `no I will do this later`, value: false },
2214
- ],
2215
- defaultIndex: 0,
2216
- });
2211
+ const wantLogin =
2212
+ forceLogin != null
2213
+ ? forceLogin
2214
+ : await promptSelect(rl, {
2215
+ title: `${bold('dev-auth seed stack')}\n${dim('Recommended: do the guided login now so the seed is ready immediately.')}`,
2216
+ options: [
2217
+ { label: `yes (${green('recommended')}) — start temporary server + UI and log in`, value: true },
2218
+ { label: `no — I will do this later`, value: false },
2219
+ ],
2220
+ defaultIndex: 0,
2221
+ });
2217
2222
 
2218
2223
  if (wantLogin) {
2219
2224
  console.log('');
@@ -3531,7 +3536,7 @@ async function main() {
3531
3536
  ' happys stack duplicate <from> <to> [--duplicate-worktrees] [--deps=none|link|install|link-or-install] [--json]',
3532
3537
  ' happys stack info <name> [--json]',
3533
3538
  ' happys stack pr <name> --happy=<pr-url|number> [--happy-server-light=<pr-url|number>] [--dev|--start] [--json] [-- ...]',
3534
- ' happys stack create-dev-auth-seed [name] [--server=happy-server|happy-server-light] [--non-interactive] [--json]',
3539
+ ' happys stack create-dev-auth-seed [name] [--server=happy-server|happy-server-light] [--login|--no-login] [--non-interactive] [--json]',
3535
3540
  ' happys stack daemon <name> start|stop|restart|status [--json]',
3536
3541
  ' happys stack happy <name> [-- ...]',
3537
3542
  ' happys stack env <name> set KEY=VALUE [KEY2=VALUE2...] | unset KEY [KEY2...] | get KEY | list | path [--json]',