thinkwork-cli 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +197 -61
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -485,19 +485,67 @@ function registerOutputsCommand(program2) {
485
485
  }
486
486
 
487
487
  // src/commands/config.ts
488
- import { readFileSync, writeFileSync, existsSync as existsSync2 } from "fs";
488
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
489
+ import chalk3 from "chalk";
490
+
491
+ // src/environments.ts
492
+ import { existsSync as existsSync2, mkdirSync, writeFileSync, readFileSync, readdirSync } from "fs";
493
+ import { join } from "path";
494
+ import { homedir } from "os";
495
+ var THINKWORK_HOME = join(homedir(), ".thinkwork");
496
+ var ENVIRONMENTS_DIR = join(THINKWORK_HOME, "environments");
497
+ function ensureDir(dir) {
498
+ if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
499
+ }
500
+ function saveEnvironment(config) {
501
+ ensureDir(ENVIRONMENTS_DIR);
502
+ const envDir = join(ENVIRONMENTS_DIR, config.stage);
503
+ ensureDir(envDir);
504
+ writeFileSync(
505
+ join(envDir, "config.json"),
506
+ JSON.stringify(config, null, 2) + "\n"
507
+ );
508
+ }
509
+ function loadEnvironment(stage) {
510
+ const configPath = join(ENVIRONMENTS_DIR, stage, "config.json");
511
+ if (!existsSync2(configPath)) return null;
512
+ return JSON.parse(readFileSync(configPath, "utf-8"));
513
+ }
514
+ function listEnvironments() {
515
+ if (!existsSync2(ENVIRONMENTS_DIR)) return [];
516
+ return readdirSync(ENVIRONMENTS_DIR).filter((name) => {
517
+ return existsSync2(join(ENVIRONMENTS_DIR, name, "config.json"));
518
+ }).map((name) => {
519
+ return JSON.parse(
520
+ readFileSync(join(ENVIRONMENTS_DIR, name, "config.json"), "utf-8")
521
+ );
522
+ }).sort((a, b) => a.stage.localeCompare(b.stage));
523
+ }
524
+ function resolveTerraformDir(stage) {
525
+ const env = loadEnvironment(stage);
526
+ if (env?.terraformDir && existsSync2(env.terraformDir)) {
527
+ return env.terraformDir;
528
+ }
529
+ const envVar = process.env.THINKWORK_TERRAFORM_DIR;
530
+ if (envVar && existsSync2(envVar)) return envVar;
531
+ const cwdTf = join(process.cwd(), "terraform");
532
+ if (existsSync2(join(cwdTf, "main.tf"))) return cwdTf;
533
+ return null;
534
+ }
535
+
536
+ // src/commands/config.ts
489
537
  var VALID_MEMORY_ENGINES = ["managed", "hindsight"];
490
538
  function readTfVar(tfvarsPath, key) {
491
- if (!existsSync2(tfvarsPath)) return null;
492
- const content = readFileSync(tfvarsPath, "utf-8");
539
+ if (!existsSync3(tfvarsPath)) return null;
540
+ const content = readFileSync2(tfvarsPath, "utf-8");
493
541
  const match = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, "m"));
494
542
  return match ? match[1] : null;
495
543
  }
496
544
  function setTfVar(tfvarsPath, key, value) {
497
- if (!existsSync2(tfvarsPath)) {
545
+ if (!existsSync3(tfvarsPath)) {
498
546
  throw new Error(`terraform.tfvars not found at ${tfvarsPath}`);
499
547
  }
500
- let content = readFileSync(tfvarsPath, "utf-8");
548
+ let content = readFileSync2(tfvarsPath, "utf-8");
501
549
  const regex = new RegExp(`^(${key}\\s*=\\s*)"[^"]*"`, "m");
502
550
  if (regex.test(content)) {
503
551
  content = content.replace(regex, `$1"${value}"`);
@@ -506,19 +554,93 @@ function setTfVar(tfvarsPath, key, value) {
506
554
  ${key} = "${value}"
507
555
  `;
508
556
  }
509
- writeFileSync(tfvarsPath, content);
557
+ writeFileSync2(tfvarsPath, content);
558
+ }
559
+ function resolveTfvarsPath(stage) {
560
+ const tfDir = resolveTerraformDir(stage);
561
+ if (tfDir) {
562
+ const direct = `${tfDir}/terraform.tfvars`;
563
+ if (existsSync3(direct)) return direct;
564
+ }
565
+ const terraformDir = process.env.THINKWORK_TERRAFORM_DIR || process.cwd();
566
+ const cwd = resolveTierDir(terraformDir, stage, "app");
567
+ return `${cwd}/terraform.tfvars`;
510
568
  }
511
569
  function registerConfigCommand(program2) {
512
570
  const config = program2.command("config").description("View or change stack configuration");
571
+ config.command("list").description("List all environments, or show config for a specific stage").option("-s, --stage <name>", "Show config for a specific stage").action((opts) => {
572
+ if (opts.stage) {
573
+ const env = loadEnvironment(opts.stage);
574
+ if (!env) {
575
+ printError(`Environment "${opts.stage}" not found. Run \`thinkwork init -s ${opts.stage}\` first.`);
576
+ process.exit(1);
577
+ }
578
+ console.log("");
579
+ console.log(chalk3.bold.cyan(` \u2B21 ${env.stage}`));
580
+ console.log(chalk3.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
581
+ console.log(` ${chalk3.bold("Region:")} ${env.region}`);
582
+ console.log(` ${chalk3.bold("Account:")} ${env.accountId}`);
583
+ console.log(` ${chalk3.bold("Database:")} ${env.databaseEngine}`);
584
+ console.log(` ${chalk3.bold("Memory:")} ${env.memoryEngine}`);
585
+ console.log(` ${chalk3.bold("Terraform dir:")} ${env.terraformDir}`);
586
+ console.log(` ${chalk3.bold("Created:")} ${env.createdAt}`);
587
+ console.log(` ${chalk3.bold("Updated:")} ${env.updatedAt}`);
588
+ console.log(chalk3.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
589
+ const tfvarsPath = `${env.terraformDir}/terraform.tfvars`;
590
+ if (existsSync3(tfvarsPath)) {
591
+ console.log("");
592
+ console.log(chalk3.dim(" terraform.tfvars:"));
593
+ const content = readFileSync2(tfvarsPath, "utf-8");
594
+ for (const line of content.split("\n")) {
595
+ if (line.trim() && !line.trim().startsWith("#")) {
596
+ const masked = line.replace(
597
+ /^(db_password\s*=\s*)".*"/,
598
+ '$1"********"'
599
+ ).replace(
600
+ /^(api_auth_secret\s*=\s*)".*"/,
601
+ '$1"********"'
602
+ ).replace(
603
+ /^(google_oauth_client_secret\s*=\s*)".*"/,
604
+ '$1"********"'
605
+ );
606
+ console.log(` ${chalk3.dim(masked)}`);
607
+ }
608
+ }
609
+ }
610
+ console.log("");
611
+ return;
612
+ }
613
+ const envs = listEnvironments();
614
+ if (envs.length === 0) {
615
+ console.log("");
616
+ console.log(" No environments found.");
617
+ console.log(` Run ${chalk3.cyan("thinkwork init -s <stage>")} to create one.`);
618
+ console.log("");
619
+ return;
620
+ }
621
+ console.log("");
622
+ console.log(chalk3.bold(" Environments"));
623
+ console.log(chalk3.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
624
+ for (const env of envs) {
625
+ const memBadge = env.memoryEngine === "hindsight" ? chalk3.magenta("hindsight") : chalk3.dim("managed");
626
+ const dbBadge = env.databaseEngine === "rds-postgres" ? chalk3.yellow("rds") : chalk3.dim("aurora");
627
+ console.log(
628
+ ` ${chalk3.bold.cyan(env.stage.padEnd(16))}${env.region.padEnd(14)}${env.accountId.padEnd(16)}${dbBadge.padEnd(20)}${memBadge}`
629
+ );
630
+ }
631
+ console.log(chalk3.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
632
+ console.log(chalk3.dim(` ${envs.length} environment(s)`));
633
+ console.log("");
634
+ console.log(` Show details: ${chalk3.cyan("thinkwork config list -s <stage>")}`);
635
+ console.log("");
636
+ });
513
637
  config.command("get <key>").description("Get a configuration value (e.g. memory-engine)").requiredOption("-s, --stage <name>", "Deployment stage").action((key, opts) => {
514
638
  const stageCheck = validateStage(opts.stage);
515
639
  if (!stageCheck.valid) {
516
640
  printError(stageCheck.error);
517
641
  process.exit(1);
518
642
  }
519
- const terraformDir = process.env.THINKWORK_TERRAFORM_DIR || process.cwd();
520
- const cwd = resolveTierDir(terraformDir, opts.stage, "app");
521
- const tfvarsPath = `${cwd}/terraform.tfvars`;
643
+ const tfvarsPath = resolveTfvarsPath(opts.stage);
522
644
  const tfKey = key.replace(/-/g, "_");
523
645
  const value = readTfVar(tfvarsPath, tfKey);
524
646
  if (value === null) {
@@ -527,7 +649,7 @@ function registerConfigCommand(program2) {
527
649
  console.log(` ${key} = ${value}`);
528
650
  }
529
651
  });
530
- config.command("set <key> <value>").description("Set a configuration value and optionally deploy (e.g. config set memory-engine hindsight)").requiredOption("-s, --stage <name>", "Deployment stage").option("--apply", "Run terraform apply after changing the value").action(async (key, value, opts) => {
652
+ config.command("set <key> <value>").description("Set a configuration value and optionally deploy").requiredOption("-s, --stage <name>", "Deployment stage").option("--apply", "Run terraform apply after changing the value").action(async (key, value, opts) => {
531
653
  const stageCheck = validateStage(opts.stage);
532
654
  if (!stageCheck.valid) {
533
655
  printError(stageCheck.error);
@@ -540,18 +662,21 @@ function registerConfigCommand(program2) {
540
662
  }
541
663
  const identity = getAwsIdentity();
542
664
  printHeader("config set", opts.stage, identity);
543
- const terraformDir = process.env.THINKWORK_TERRAFORM_DIR || process.cwd();
544
- const cwd = resolveTierDir(terraformDir, opts.stage, "app");
545
- const tfvarsPath = `${cwd}/terraform.tfvars`;
665
+ const tfvarsPath = resolveTfvarsPath(opts.stage);
546
666
  const oldValue = readTfVar(tfvarsPath, tfKey);
547
667
  setTfVar(tfvarsPath, tfKey, value);
548
668
  console.log(` ${key}: ${oldValue ?? "(unset)"} \u2192 ${value}`);
549
669
  if (opts.apply) {
670
+ const tfDir = resolveTerraformDir(opts.stage);
671
+ if (!tfDir) {
672
+ printError("Cannot find terraform directory. Run `thinkwork init` first.");
673
+ process.exit(1);
674
+ }
550
675
  console.log("");
551
676
  console.log(" Applying configuration change...");
552
- await ensureInit(cwd);
553
- await ensureWorkspace(cwd, opts.stage);
554
- const code = await runTerraform(cwd, [
677
+ await ensureInit(tfDir);
678
+ await ensureWorkspace(tfDir, opts.stage);
679
+ const code = await runTerraform(tfDir, [
555
680
  "apply",
556
681
  "-auto-approve",
557
682
  `-var=stage=${opts.stage}`
@@ -721,16 +846,16 @@ function registerLoginCommand(program2) {
721
846
  }
722
847
 
723
848
  // src/commands/init.ts
724
- import { existsSync as existsSync3, mkdirSync, writeFileSync as writeFileSync2, cpSync } from "fs";
725
- import { resolve as resolve2, join, dirname } from "path";
849
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3, cpSync } from "fs";
850
+ import { resolve as resolve2, join as join2, dirname } from "path";
726
851
  import { execSync as execSync4 } from "child_process";
727
852
  import { fileURLToPath } from "url";
728
853
  import { createInterface as createInterface3 } from "readline";
729
- import chalk3 from "chalk";
854
+ import chalk4 from "chalk";
730
855
  var __dirname = dirname(fileURLToPath(import.meta.url));
731
856
  function ask2(prompt, defaultVal = "") {
732
857
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
733
- const suffix = defaultVal ? chalk3.dim(` [${defaultVal}]`) : "";
858
+ const suffix = defaultVal ? chalk4.dim(` [${defaultVal}]`) : "";
734
859
  return new Promise((resolve3) => {
735
860
  rl.question(` ${prompt}${suffix}: `, (answer) => {
736
861
  rl.close();
@@ -739,7 +864,7 @@ function ask2(prompt, defaultVal = "") {
739
864
  });
740
865
  }
741
866
  function choose(prompt, options, defaultVal) {
742
- const optStr = options.map((o) => o === defaultVal ? chalk3.bold(o) : chalk3.dim(o)).join(" / ");
867
+ const optStr = options.map((o) => o === defaultVal ? chalk4.bold(o) : chalk4.dim(o)).join(" / ");
743
868
  return ask2(`${prompt} (${optStr})`, defaultVal);
744
869
  }
745
870
  function generateSecret(length = 32) {
@@ -752,9 +877,9 @@ function generateSecret(length = 32) {
752
877
  }
753
878
  function findBundledTerraform() {
754
879
  const bundled = resolve2(__dirname, "..", "terraform");
755
- if (existsSync3(join(bundled, "modules"))) return bundled;
880
+ if (existsSync4(join2(bundled, "modules"))) return bundled;
756
881
  const repoTf = resolve2(__dirname, "..", "..", "..", "..", "terraform");
757
- if (existsSync3(join(repoTf, "modules"))) return repoTf;
882
+ if (existsSync4(join2(repoTf, "modules"))) return repoTf;
758
883
  throw new Error(
759
884
  "Terraform modules not found. The CLI package may be incomplete.\nTry reinstalling: npm install -g thinkwork-cli@latest"
760
885
  );
@@ -818,9 +943,9 @@ function registerInitCommand(program2) {
818
943
  process.exit(1);
819
944
  }
820
945
  const targetDir = resolve2(opts.dir);
821
- const tfDir = join(targetDir, "terraform");
822
- const tfvarsPath = join(tfDir, "terraform.tfvars");
823
- if (existsSync3(tfvarsPath)) {
946
+ const tfDir = join2(targetDir, "terraform");
947
+ const tfvarsPath = join2(tfDir, "terraform.tfvars");
948
+ if (existsSync4(tfvarsPath)) {
824
949
  printWarning(`terraform.tfvars already exists at ${tfvarsPath}`);
825
950
  const overwrite = await ask2("Overwrite?", "N");
826
951
  if (overwrite.toLowerCase() !== "y") {
@@ -844,19 +969,19 @@ function registerInitCommand(program2) {
844
969
  config.admin_url = "http://localhost:5174";
845
970
  config.mobile_scheme = "thinkwork";
846
971
  } else {
847
- console.log(chalk3.bold(" Configure your Thinkwork environment\n"));
972
+ console.log(chalk4.bold(" Configure your Thinkwork environment\n"));
848
973
  const defaultRegion = identity.region !== "unknown" ? identity.region : "us-east-1";
849
974
  config.region = await ask2("AWS Region", defaultRegion);
850
975
  console.log("");
851
- console.log(chalk3.dim(" \u2500\u2500 Database \u2500\u2500"));
976
+ console.log(chalk4.dim(" \u2500\u2500 Database \u2500\u2500"));
852
977
  config.database_engine = await choose("Database engine", ["aurora-serverless", "rds-postgres"], "aurora-serverless");
853
978
  console.log("");
854
- console.log(chalk3.dim(" \u2500\u2500 Memory \u2500\u2500"));
855
- console.log(chalk3.dim(" managed = Built-in AgentCore memory (remember/recall/forget)"));
856
- console.log(chalk3.dim(" hindsight = ECS Fargate service with semantic + graph retrieval"));
979
+ console.log(chalk4.dim(" \u2500\u2500 Memory \u2500\u2500"));
980
+ console.log(chalk4.dim(" managed = Built-in AgentCore memory (remember/recall/forget)"));
981
+ console.log(chalk4.dim(" hindsight = ECS Fargate service with semantic + graph retrieval"));
857
982
  config.memory_engine = await choose("Memory engine", ["managed", "hindsight"], "managed");
858
983
  console.log("");
859
- console.log(chalk3.dim(" \u2500\u2500 Auth \u2500\u2500"));
984
+ console.log(chalk4.dim(" \u2500\u2500 Auth \u2500\u2500"));
860
985
  const useGoogle = await ask2("Enable Google OAuth login? (y/N)", "N");
861
986
  if (useGoogle.toLowerCase() === "y") {
862
987
  config.google_oauth_client_id = await ask2("Google OAuth Client ID");
@@ -866,13 +991,13 @@ function registerInitCommand(program2) {
866
991
  config.google_oauth_client_secret = "";
867
992
  }
868
993
  console.log("");
869
- console.log(chalk3.dim(" \u2500\u2500 Frontend URLs \u2500\u2500"));
994
+ console.log(chalk4.dim(" \u2500\u2500 Frontend URLs \u2500\u2500"));
870
995
  config.admin_url = await ask2("Admin UI URL", "http://localhost:5174");
871
996
  config.mobile_scheme = await ask2("Mobile app URL scheme", "thinkwork");
872
997
  console.log("");
873
- console.log(chalk3.dim(" \u2500\u2500 Secrets (auto-generated) \u2500\u2500"));
874
- console.log(chalk3.dim(` DB password: ${config.db_password.slice(0, 8)}...`));
875
- console.log(chalk3.dim(` API auth secret: ${config.api_auth_secret.slice(0, 16)}...`));
998
+ console.log(chalk4.dim(" \u2500\u2500 Secrets (auto-generated) \u2500\u2500"));
999
+ console.log(chalk4.dim(` DB password: ${config.db_password.slice(0, 8)}...`));
1000
+ console.log(chalk4.dim(` API auth secret: ${config.api_auth_secret.slice(0, 16)}...`));
876
1001
  }
877
1002
  console.log("");
878
1003
  console.log(" Scaffolding Terraform modules...");
@@ -883,24 +1008,24 @@ function registerInitCommand(program2) {
883
1008
  printError(String(err));
884
1009
  process.exit(1);
885
1010
  }
886
- mkdirSync(tfDir, { recursive: true });
1011
+ mkdirSync2(tfDir, { recursive: true });
887
1012
  const copyDirs = ["modules", "examples"];
888
1013
  for (const dir of copyDirs) {
889
- const src = join(bundledTf, dir);
890
- const dst = join(tfDir, dir);
891
- if (existsSync3(src) && !existsSync3(dst)) {
1014
+ const src = join2(bundledTf, dir);
1015
+ const dst = join2(tfDir, dir);
1016
+ if (existsSync4(src) && !existsSync4(dst)) {
892
1017
  cpSync(src, dst, { recursive: true });
893
1018
  }
894
1019
  }
895
- const schemaPath = join(bundledTf, "schema.graphql");
896
- if (existsSync3(schemaPath) && !existsSync3(join(tfDir, "schema.graphql"))) {
897
- cpSync(schemaPath, join(tfDir, "schema.graphql"));
1020
+ const schemaPath = join2(bundledTf, "schema.graphql");
1021
+ if (existsSync4(schemaPath) && !existsSync4(join2(tfDir, "schema.graphql"))) {
1022
+ cpSync(schemaPath, join2(tfDir, "schema.graphql"));
898
1023
  }
899
1024
  const tfvars = buildTfvars(config);
900
- writeFileSync2(tfvarsPath, tfvars);
901
- const mainTfPath = join(tfDir, "main.tf");
902
- if (!existsSync3(mainTfPath)) {
903
- writeFileSync2(mainTfPath, `################################################################################
1025
+ writeFileSync3(tfvarsPath, tfvars);
1026
+ const mainTfPath = join2(tfDir, "main.tf");
1027
+ if (!existsSync4(mainTfPath)) {
1028
+ writeFileSync3(mainTfPath, `################################################################################
904
1029
  # Thinkwork \u2014 ${config.stage}
905
1030
  # Generated by: thinkwork init -s ${config.stage}
906
1031
  ################################################################################
@@ -977,17 +1102,17 @@ output "memory_engine" { value = module.thinkwork.memory_engine }
977
1102
  output "hindsight_endpoint" { value = module.thinkwork.hindsight_endpoint }
978
1103
  `);
979
1104
  }
980
- console.log(` Wrote ${chalk3.cyan(tfDir + "/")}`);
1105
+ console.log(` Wrote ${chalk4.cyan(tfDir + "/")}`);
981
1106
  console.log("");
982
- console.log(chalk3.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
983
- console.log(` ${chalk3.bold("Stage:")} ${config.stage}`);
984
- console.log(` ${chalk3.bold("Region:")} ${config.region}`);
985
- console.log(` ${chalk3.bold("Account:")} ${config.account_id}`);
986
- console.log(` ${chalk3.bold("Database:")} ${config.database_engine}`);
987
- console.log(` ${chalk3.bold("Memory:")} ${config.memory_engine}`);
988
- console.log(` ${chalk3.bold("Google OAuth:")} ${config.google_oauth_client_id ? "enabled" : "disabled"}`);
989
- console.log(` ${chalk3.bold("Directory:")} ${tfDir}`);
990
- console.log(chalk3.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1107
+ console.log(chalk4.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1108
+ console.log(` ${chalk4.bold("Stage:")} ${config.stage}`);
1109
+ console.log(` ${chalk4.bold("Region:")} ${config.region}`);
1110
+ console.log(` ${chalk4.bold("Account:")} ${config.account_id}`);
1111
+ console.log(` ${chalk4.bold("Database:")} ${config.database_engine}`);
1112
+ console.log(` ${chalk4.bold("Memory:")} ${config.memory_engine}`);
1113
+ console.log(` ${chalk4.bold("Google OAuth:")} ${config.google_oauth_client_id ? "enabled" : "disabled"}`);
1114
+ console.log(` ${chalk4.bold("Directory:")} ${tfDir}`);
1115
+ console.log(chalk4.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
991
1116
  console.log("\n Initializing Terraform...\n");
992
1117
  try {
993
1118
  execSync4("terraform init", { cwd: tfDir, stdio: "inherit" });
@@ -995,13 +1120,24 @@ output "hindsight_endpoint" { value = module.thinkwork.hindsight_endpoint }
995
1120
  printWarning("Terraform init failed. Run `thinkwork doctor -s " + opts.stage + "` to check prerequisites.");
996
1121
  return;
997
1122
  }
1123
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1124
+ saveEnvironment({
1125
+ stage: config.stage,
1126
+ region: config.region,
1127
+ accountId: config.account_id,
1128
+ terraformDir: tfDir,
1129
+ databaseEngine: config.database_engine,
1130
+ memoryEngine: config.memory_engine,
1131
+ createdAt: now,
1132
+ updatedAt: now
1133
+ });
998
1134
  printSuccess(`Environment "${opts.stage}" initialized`);
999
1135
  console.log("");
1000
1136
  console.log(" Next steps:");
1001
- console.log(` ${chalk3.cyan("1.")} thinkwork plan -s ${opts.stage} ${chalk3.dim("# Review infrastructure plan")}`);
1002
- console.log(` ${chalk3.cyan("2.")} thinkwork deploy -s ${opts.stage} ${chalk3.dim("# Deploy to AWS (~5 min)")}`);
1003
- console.log(` ${chalk3.cyan("3.")} thinkwork bootstrap -s ${opts.stage} ${chalk3.dim("# Seed workspace files + skills")}`);
1004
- console.log(` ${chalk3.cyan("4.")} thinkwork outputs -s ${opts.stage} ${chalk3.dim("# Show API URL, Cognito IDs, etc.")}`);
1137
+ console.log(` ${chalk4.cyan("1.")} thinkwork plan -s ${opts.stage} ${chalk4.dim("# Review infrastructure plan")}`);
1138
+ console.log(` ${chalk4.cyan("2.")} thinkwork deploy -s ${opts.stage} ${chalk4.dim("# Deploy to AWS (~5 min)")}`);
1139
+ console.log(` ${chalk4.cyan("3.")} thinkwork bootstrap -s ${opts.stage} ${chalk4.dim("# Seed workspace files + skills")}`);
1140
+ console.log(` ${chalk4.cyan("4.")} thinkwork outputs -s ${opts.stage} ${chalk4.dim("# Show API URL, Cognito IDs, etc.")}`);
1005
1141
  console.log("");
1006
1142
  });
1007
1143
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkwork-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Thinkwork CLI — deploy, manage, and interact with your Thinkwork stack",
5
5
  "license": "MIT",
6
6
  "type": "module",