@hyperdrive.bot/cli 1.0.13 → 1.0.16

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 (157) hide show
  1. package/README.md +1495 -474
  2. package/dist/commands/deploy.d.ts +18 -0
  3. package/dist/commands/deploy.js +239 -0
  4. package/dist/commands/deployment/create.js +10 -2
  5. package/dist/commands/domain/{switch.d.ts → set-production.d.ts} +1 -1
  6. package/dist/commands/domain/set-production.js +27 -0
  7. package/dist/commands/git/list-open-prs.d.ts +12 -0
  8. package/dist/commands/git/list-open-prs.js +87 -0
  9. package/dist/commands/hook/add.d.ts +22 -0
  10. package/dist/commands/hook/add.js +299 -0
  11. package/dist/commands/hook/list.d.ts +11 -0
  12. package/dist/commands/hook/list.js +111 -0
  13. package/dist/commands/hook/logs.d.ts +13 -0
  14. package/dist/commands/hook/logs.js +124 -0
  15. package/dist/commands/hook/remove.d.ts +12 -0
  16. package/dist/commands/hook/remove.js +115 -0
  17. package/dist/commands/hook/toggle.d.ts +12 -0
  18. package/dist/commands/hook/toggle.js +125 -0
  19. package/dist/commands/init.d.ts +1 -1
  20. package/dist/commands/init.js +49 -9
  21. package/dist/commands/module/bindings.d.ts +14 -0
  22. package/dist/commands/module/bindings.js +125 -0
  23. package/dist/commands/module/create.d.ts +3 -0
  24. package/dist/commands/module/create.js +156 -78
  25. package/dist/commands/module/list.d.ts +1 -0
  26. package/dist/commands/module/list.js +22 -1
  27. package/dist/commands/module/sync.d.ts +29 -0
  28. package/dist/commands/module/sync.js +409 -0
  29. package/dist/commands/module/unlink.d.ts +11 -0
  30. package/dist/commands/module/unlink.js +77 -0
  31. package/dist/commands/module/update.d.ts +10 -0
  32. package/dist/commands/module/update.js +168 -5
  33. package/dist/commands/network/discover.d.ts +12 -0
  34. package/dist/commands/network/discover.js +210 -0
  35. package/dist/commands/network/get.d.ts +13 -0
  36. package/dist/commands/network/get.js +90 -0
  37. package/dist/commands/{auth/logout.d.ts → network/list.d.ts} +2 -9
  38. package/dist/commands/network/list.js +71 -0
  39. package/dist/commands/network/register.d.ts +16 -0
  40. package/dist/commands/network/register.js +144 -0
  41. package/dist/commands/parameter/sync.d.ts +13 -0
  42. package/dist/commands/parameter/sync.js +69 -1
  43. package/dist/commands/project/sync.d.ts +5 -11
  44. package/dist/commands/project/sync.js +12 -381
  45. package/dist/commands/seed.d.ts +93 -0
  46. package/dist/commands/seed.js +324 -0
  47. package/dist/commands/service/backup.d.ts +17 -0
  48. package/dist/commands/service/backup.js +156 -0
  49. package/dist/commands/service/backups.d.ts +14 -0
  50. package/dist/commands/service/backups.js +110 -0
  51. package/dist/commands/service/bind.d.ts +16 -0
  52. package/dist/commands/service/bind.js +106 -0
  53. package/dist/commands/service/bindings.d.ts +13 -0
  54. package/dist/commands/service/bindings.js +78 -0
  55. package/dist/commands/service/clone.d.ts +19 -0
  56. package/dist/commands/service/clone.js +153 -0
  57. package/dist/commands/service/create.d.ts +16 -0
  58. package/dist/commands/service/create.js +212 -0
  59. package/dist/commands/service/get.d.ts +13 -0
  60. package/dist/commands/service/get.js +97 -0
  61. package/dist/commands/service/list.d.ts +12 -0
  62. package/dist/commands/service/list.js +86 -0
  63. package/dist/commands/service/register.d.ts +21 -0
  64. package/dist/commands/service/register.js +215 -0
  65. package/dist/commands/service/restore.d.ts +19 -0
  66. package/dist/commands/service/restore.js +158 -0
  67. package/dist/commands/service/seed.d.ts +17 -0
  68. package/dist/commands/service/seed.js +173 -0
  69. package/dist/commands/service/templates.d.ts +10 -0
  70. package/dist/commands/service/templates.js +66 -0
  71. package/dist/commands/service/unbind.d.ts +15 -0
  72. package/dist/commands/service/unbind.js +74 -0
  73. package/dist/commands/stage/create.d.ts +23 -0
  74. package/dist/commands/stage/create.js +145 -6
  75. package/dist/commands/stage/delete.d.ts +11 -0
  76. package/dist/commands/stage/delete.js +85 -0
  77. package/dist/commands/stage/deploy.d.ts +34 -0
  78. package/dist/commands/stage/deploy.js +294 -0
  79. package/dist/commands/stage/ensure-branches.d.ts +23 -0
  80. package/dist/commands/stage/ensure-branches.js +101 -0
  81. package/dist/commands/stage/list.js +4 -0
  82. package/dist/commands/stage/status.d.ts +14 -0
  83. package/dist/commands/stage/status.js +100 -0
  84. package/dist/commands/{jira → tracker}/connect.js +32 -23
  85. package/dist/commands/tracker/hook/add.d.ts +25 -0
  86. package/dist/commands/tracker/hook/add.js +284 -0
  87. package/dist/commands/{jira → tracker}/hook/list.js +20 -11
  88. package/dist/commands/{jira/hook/add.d.ts → tracker/hook/logs.d.ts} +2 -3
  89. package/dist/commands/tracker/hook/logs.js +126 -0
  90. package/dist/commands/{jira → tracker}/hook/remove.js +9 -8
  91. package/dist/commands/{jira → tracker}/hook/toggle.js +14 -12
  92. package/dist/commands/tracker/project/init.d.ts +17 -0
  93. package/dist/commands/tracker/project/init.js +178 -0
  94. package/dist/commands/tracker/project/link-module.d.ts +17 -0
  95. package/dist/commands/tracker/project/link-module.js +287 -0
  96. package/dist/commands/tracker/project/list-modules.d.ts +11 -0
  97. package/dist/commands/tracker/project/list-modules.js +117 -0
  98. package/dist/commands/tracker/project/list.d.ts +10 -0
  99. package/dist/commands/tracker/project/list.js +90 -0
  100. package/dist/commands/tracker/project/status.d.ts +13 -0
  101. package/dist/commands/tracker/project/status.js +168 -0
  102. package/dist/commands/tracker/project/unlink-module.d.ts +13 -0
  103. package/dist/commands/tracker/project/unlink-module.js +251 -0
  104. package/dist/commands/{jira → tracker}/status.js +3 -3
  105. package/dist/lib/ensure-branches.d.ts +53 -0
  106. package/dist/lib/ensure-branches.js +149 -0
  107. package/dist/lib/git-providers/github.d.ts +16 -0
  108. package/dist/lib/git-providers/github.js +157 -0
  109. package/dist/lib/git-providers/gitlab.d.ts +16 -0
  110. package/dist/lib/git-providers/gitlab.js +148 -0
  111. package/dist/lib/git-providers/index.d.ts +67 -0
  112. package/dist/lib/git-providers/index.js +39 -0
  113. package/dist/lib/lambda-warmer.d.ts +106 -0
  114. package/dist/lib/lambda-warmer.js +189 -0
  115. package/dist/services/hyperdrive-sigv4.d.ts +359 -5
  116. package/dist/services/hyperdrive-sigv4.js +177 -12
  117. package/dist/utils/hook-flow.d.ts +60 -3
  118. package/dist/utils/hook-flow.js +437 -2
  119. package/dist/utils/hook-normalize.d.ts +6 -0
  120. package/dist/utils/hook-normalize.js +33 -0
  121. package/dist/utils/lifecycle-poller.d.ts +32 -0
  122. package/dist/utils/lifecycle-poller.js +72 -0
  123. package/dist/utils/retry.d.ts +43 -0
  124. package/dist/utils/retry.js +88 -0
  125. package/dist/utils/summary-display.js +1 -1
  126. package/dist/utils/tracker-project-flow.d.ts +84 -0
  127. package/dist/utils/tracker-project-flow.js +564 -0
  128. package/package.json +35 -7
  129. package/dist/commands/auth/login.d.ts +0 -16
  130. package/dist/commands/auth/login.js +0 -179
  131. package/dist/commands/auth/logout.js +0 -116
  132. package/dist/commands/auth/refresh.d.ts +0 -6
  133. package/dist/commands/auth/refresh.js +0 -66
  134. package/dist/commands/auth/status.d.ts +0 -6
  135. package/dist/commands/auth/status.js +0 -63
  136. package/dist/commands/config/get.d.ts +0 -9
  137. package/dist/commands/config/get.js +0 -37
  138. package/dist/commands/config/set.d.ts +0 -10
  139. package/dist/commands/config/set.js +0 -48
  140. package/dist/commands/config/show.d.ts +0 -6
  141. package/dist/commands/config/show.js +0 -10
  142. package/dist/commands/domain/current.d.ts +0 -6
  143. package/dist/commands/domain/current.js +0 -18
  144. package/dist/commands/domain/list.d.ts +0 -6
  145. package/dist/commands/domain/list.js +0 -42
  146. package/dist/commands/domain/switch.js +0 -40
  147. package/dist/commands/jira/hook/add.js +0 -147
  148. package/dist/services/tenant-service.d.ts +0 -127
  149. package/dist/services/tenant-service.js +0 -396
  150. package/dist/utils/auth-flow.d.ts +0 -147
  151. package/dist/utils/auth-flow.js +0 -479
  152. package/oclif.manifest.json +0 -3519
  153. /package/dist/commands/{jira → tracker}/connect.d.ts +0 -0
  154. /package/dist/commands/{jira → tracker}/hook/list.d.ts +0 -0
  155. /package/dist/commands/{jira → tracker}/hook/remove.d.ts +0 -0
  156. /package/dist/commands/{jira → tracker}/hook/toggle.d.ts +0 -0
  157. /package/dist/commands/{jira → tracker}/status.d.ts +0 -0
@@ -0,0 +1,144 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { HyperdriveSigV4Service } from '../../services/hyperdrive-sigv4.js';
5
+ const SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
6
+ export default class NetworkRegister extends Command {
7
+ static description = 'Register a network manually';
8
+ static examples = [
9
+ '<%= config.bin %> network register',
10
+ '<%= config.bin %> network register --slug prod-us-east-1 --account-id 084309335408 --region us-east-1 --vpc-id vpc-abc123',
11
+ ];
12
+ static flags = {
13
+ 'account-id': Flags.string({ description: 'AWS Account ID' }),
14
+ domain: Flags.string({ char: 'd', description: 'Tenant domain (for multi-domain setups)' }),
15
+ 'private-subnets': Flags.string({ description: 'Comma-separated private subnet IDs' }),
16
+ 'public-subnets': Flags.string({ description: 'Comma-separated public subnet IDs' }),
17
+ region: Flags.string({ char: 'r', description: 'AWS region' }),
18
+ 'security-groups': Flags.string({ description: 'Security groups as JSON (e.g. {"database":"sg-123","lambda":"sg-456"})' }),
19
+ slug: Flags.string({ char: 's', description: 'Network slug' }),
20
+ 'vpc-id': Flags.string({ description: 'VPC ID' }),
21
+ };
22
+ async run() {
23
+ const { flags } = await this.parse(NetworkRegister);
24
+ const service = new HyperdriveSigV4Service(flags.domain);
25
+ let slug = flags.slug;
26
+ let accountId = flags['account-id'];
27
+ let region = flags.region;
28
+ let vpcId = flags['vpc-id'];
29
+ let privateSubnetIds = flags['private-subnets']?.split(',').map(s => s.trim()).filter(Boolean);
30
+ let publicSubnetIds = flags['public-subnets']?.split(',').map(s => s.trim()).filter(Boolean);
31
+ let securityGroups;
32
+ if (flags['security-groups']) {
33
+ try {
34
+ securityGroups = JSON.parse(flags['security-groups']);
35
+ }
36
+ catch {
37
+ this.log(chalk.red('❌ Invalid JSON for --security-groups'));
38
+ this.exit(1);
39
+ return;
40
+ }
41
+ }
42
+ // Interactive mode if missing required fields
43
+ if (!slug || !accountId || !region || !vpcId) {
44
+ const inquirer = (await import('inquirer')).default;
45
+ if (!accountId) {
46
+ const { inputAccountId } = await inquirer.prompt([{
47
+ message: chalk.yellow('AWS Account ID (12 digits):'),
48
+ name: 'inputAccountId',
49
+ validate: (input) => /^\d{12}$/.test(input) || 'Must be exactly 12 digits',
50
+ }]);
51
+ accountId = inputAccountId;
52
+ }
53
+ if (!region) {
54
+ const { inputRegion } = await inquirer.prompt([{
55
+ choices: [
56
+ 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2',
57
+ 'sa-east-1', 'eu-west-1', 'eu-west-2', 'eu-central-1',
58
+ 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1',
59
+ ],
60
+ default: 'us-east-1',
61
+ message: chalk.yellow('AWS Region:'),
62
+ name: 'inputRegion',
63
+ type: 'list',
64
+ }]);
65
+ region = inputRegion;
66
+ }
67
+ if (!vpcId) {
68
+ const { inputVpcId } = await inquirer.prompt([{
69
+ message: chalk.yellow('VPC ID:'),
70
+ name: 'inputVpcId',
71
+ validate: (input) => input.startsWith('vpc-') || 'VPC ID must start with "vpc-"',
72
+ }]);
73
+ vpcId = inputVpcId;
74
+ }
75
+ if (!slug) {
76
+ const { inputSlug } = await inquirer.prompt([{
77
+ default: `vpc-${region}`,
78
+ message: chalk.yellow('Network slug:'),
79
+ name: 'inputSlug',
80
+ validate: (input) => SLUG_REGEX.test(input) || 'Must be lowercase alphanumeric with hyphens (1-63 chars)',
81
+ }]);
82
+ slug = inputSlug;
83
+ }
84
+ if (!privateSubnetIds) {
85
+ const { inputPrivateSubnets } = await inquirer.prompt([{
86
+ message: chalk.yellow('Private subnet IDs (comma-separated, or leave empty):'),
87
+ name: 'inputPrivateSubnets',
88
+ }]);
89
+ if (inputPrivateSubnets) {
90
+ privateSubnetIds = inputPrivateSubnets.split(',').map((s) => s.trim()).filter(Boolean);
91
+ }
92
+ }
93
+ if (!publicSubnetIds) {
94
+ const { inputPublicSubnets } = await inquirer.prompt([{
95
+ message: chalk.yellow('Public subnet IDs (comma-separated, or leave empty):'),
96
+ name: 'inputPublicSubnets',
97
+ }]);
98
+ if (inputPublicSubnets) {
99
+ publicSubnetIds = inputPublicSubnets.split(',').map((s) => s.trim()).filter(Boolean);
100
+ }
101
+ }
102
+ if (!securityGroups) {
103
+ const { inputSGs } = await inquirer.prompt([{
104
+ message: chalk.yellow('Security groups JSON (e.g. {"database":"sg-123"}, or leave empty):'),
105
+ name: 'inputSGs',
106
+ }]);
107
+ if (inputSGs) {
108
+ try {
109
+ securityGroups = JSON.parse(inputSGs);
110
+ }
111
+ catch {
112
+ this.log(chalk.yellow('Invalid JSON — skipping security groups.'));
113
+ }
114
+ }
115
+ }
116
+ }
117
+ // Validate slug format
118
+ if (!SLUG_REGEX.test(slug)) {
119
+ this.log(chalk.red(`❌ Invalid slug format: "${slug}". Must match: ${SLUG_REGEX}`));
120
+ this.exit(1);
121
+ return;
122
+ }
123
+ const spinner = ora(`Registering network "${slug}"...`).start();
124
+ try {
125
+ await service.networkRegister({
126
+ slug: slug,
127
+ accountId: accountId,
128
+ region: region,
129
+ vpcId: vpcId,
130
+ privateSubnetIds,
131
+ publicSubnetIds,
132
+ securityGroups,
133
+ source: 'manual',
134
+ });
135
+ spinner.succeed(chalk.green(`Network registered: ${slug}`));
136
+ }
137
+ catch (error) {
138
+ spinner.fail('Registration failed');
139
+ const axiosError = error;
140
+ this.log(chalk.red(`\n❌ ${axiosError.response?.data?.message ?? axiosError.message}`));
141
+ this.exit(1);
142
+ }
143
+ }
144
+ }
@@ -4,11 +4,24 @@ export default class ParameterSync extends Command {
4
4
  static examples: string[];
5
5
  static flags: {
6
6
  accountId: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'apply-to-lambda': import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
8
  domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
10
  file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
11
  moduleSlug: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
12
  specific: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
13
  stage: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
14
  };
13
15
  run(): Promise<void>;
16
+ /**
17
+ * Render the Lambda env update report returned by the backend.
18
+ *
19
+ * Contract (API-111 AC5 / API-106 AC6):
20
+ * - When `applyToLambda` was requested, every targeted Lambda appears here
21
+ * even if no keys changed (so the user sees idempotent runs explicitly).
22
+ * - `updatedKeys` is the testable contract — what actually got written
23
+ * (or would be written under --dry-run).
24
+ * - `applied: false` indicates a no-op (dry-run or already-in-sync).
25
+ */
26
+ private renderLambdaUpdates;
14
27
  }
@@ -11,6 +11,8 @@ export default class ParameterSync extends Command {
11
11
  '<%= config.bin %> <%= command.id %> --stage="dev"',
12
12
  '<%= config.bin %> <%= command.id %> --file=".env.production" --stage="prod"',
13
13
  '<%= config.bin %> <%= command.id %> --no-specific --stage="all"',
14
+ '<%= config.bin %> <%= command.id %> --apply-to-lambda --stage="dev"',
15
+ '<%= config.bin %> <%= command.id %> --apply-to-lambda --dry-run --stage="dev"',
14
16
  ];
15
17
  static flags = {
16
18
  accountId: Flags.string({
@@ -18,10 +20,18 @@ export default class ParameterSync extends Command {
18
20
  env: 'AWS_ACCOUNT_ID',
19
21
  required: true,
20
22
  }),
23
+ 'apply-to-lambda': Flags.boolean({
24
+ default: false,
25
+ description: 'After writing to DDB, also update each bound Lambda function\'s env vars (preserves existing vars; see API-111 AC5)',
26
+ }),
21
27
  domain: Flags.string({
22
28
  char: 'd',
23
29
  description: 'Tenant domain (for multi-domain setups)',
24
30
  }),
31
+ 'dry-run': Flags.boolean({
32
+ default: false,
33
+ description: 'Preview env key changes without writing to DDB or Lambda. Best paired with --apply-to-lambda to see Lambda diffs.',
34
+ }),
25
35
  file: Flags.string({
26
36
  char: 'f',
27
37
  default: '.env',
@@ -54,6 +64,8 @@ export default class ParameterSync extends Command {
54
64
  async run() {
55
65
  const { flags } = await this.parse(ParameterSync);
56
66
  const { accountId, domain, file, moduleSlug, specific, stage } = flags;
67
+ const applyToLambda = flags['apply-to-lambda'];
68
+ const dryRun = flags['dry-run'];
57
69
  const envPath = path.resolve(process.cwd(), file);
58
70
  if (!existsSync(envPath)) {
59
71
  this.error(`The file "${file}" does not exist.`);
@@ -81,9 +93,17 @@ export default class ParameterSync extends Command {
81
93
  this.log(chalk.gray(` Module: ${moduleSlug}`));
82
94
  this.log(chalk.gray(` Stage: ${stage}`));
83
95
  this.log(chalk.gray(` Scope: ${specific ? 'Project-specific' : 'Global'}`));
96
+ if (applyToLambda) {
97
+ this.log(chalk.gray(` Apply to Lambda: yes${dryRun ? ' (dry-run)' : ''}`));
98
+ }
99
+ else if (dryRun) {
100
+ this.log(chalk.gray(' Dry run: yes (DDB-only preview)'));
101
+ }
84
102
  // Initiate async sync
85
103
  const initResult = await service.parameterSync({
86
104
  accountId,
105
+ applyToLambda,
106
+ dryRun,
87
107
  encodedContent,
88
108
  projectSlug: moduleSlug,
89
109
  specific,
@@ -111,7 +131,7 @@ export default class ParameterSync extends Command {
111
131
  lastProgress = status.progress.percentage;
112
132
  }
113
133
  if (status.status === 'completed') {
114
- spinner.succeed('Parameters synchronized successfully!');
134
+ spinner.succeed(dryRun ? 'Parameter sync preview complete (dry-run, nothing written).' : 'Parameters synchronized successfully!');
115
135
  if (status.errors && status.errors.length > 0) {
116
136
  // Filter out non-fatal errors (commented/empty keys)
117
137
  const fatalErrors = status.errors.filter(e => !e.error.includes('was ignored'));
@@ -129,6 +149,7 @@ export default class ParameterSync extends Command {
129
149
  }
130
150
  }
131
151
  }
152
+ this.renderLambdaUpdates(status.lambdaUpdates, { applyToLambda, dryRun });
132
153
  break;
133
154
  }
134
155
  if (status.status === 'failed') {
@@ -139,6 +160,7 @@ export default class ParameterSync extends Command {
139
160
  this.log(chalk.red(` - ${error.key}: ${error.error}`));
140
161
  }
141
162
  }
163
+ this.renderLambdaUpdates(status.lambdaUpdates, { applyToLambda, dryRun });
142
164
  this.exit(1);
143
165
  }
144
166
  // Wait before next poll
@@ -150,4 +172,50 @@ export default class ParameterSync extends Command {
150
172
  this.error('An error occurred while synchronizing parameters');
151
173
  }
152
174
  }
175
+ /**
176
+ * Render the Lambda env update report returned by the backend.
177
+ *
178
+ * Contract (API-111 AC5 / API-106 AC6):
179
+ * - When `applyToLambda` was requested, every targeted Lambda appears here
180
+ * even if no keys changed (so the user sees idempotent runs explicitly).
181
+ * - `updatedKeys` is the testable contract — what actually got written
182
+ * (or would be written under --dry-run).
183
+ * - `applied: false` indicates a no-op (dry-run or already-in-sync).
184
+ */
185
+ renderLambdaUpdates(reports, opts) {
186
+ if (!opts.applyToLambda) {
187
+ return;
188
+ }
189
+ if (!reports || reports.length === 0) {
190
+ this.log(chalk.gray('\n• No Lambda functions found bound to this module/stage — nothing to update.'));
191
+ return;
192
+ }
193
+ const header = opts.dryRun ? 'Lambda env updates (dry-run preview):' : 'Lambda env updates:';
194
+ this.log(chalk.blue(`\n${header}`));
195
+ for (const report of reports) {
196
+ if (report.skipped) {
197
+ this.log(chalk.gray(` ⊘ ${report.functionName} — skipped: ${report.skipped}`));
198
+ continue;
199
+ }
200
+ if (report.error) {
201
+ this.log(chalk.red(` ✗ ${report.functionName} — error: ${report.error}`));
202
+ continue;
203
+ }
204
+ const updated = report.updatedKeys ?? [];
205
+ const removed = report.removedKeys ?? [];
206
+ const totalChanged = updated.length + removed.length;
207
+ if (totalChanged === 0) {
208
+ this.log(chalk.gray(` = ${report.functionName} — no changes (idempotent)`));
209
+ continue;
210
+ }
211
+ const verb = opts.dryRun || report.applied === false ? 'would update' : 'updated';
212
+ this.log(chalk.green(` ✓ ${report.functionName} — ${verb} ${totalChanged} key${totalChanged === 1 ? '' : 's'}`));
213
+ if (updated.length > 0) {
214
+ this.log(chalk.gray(` + ${updated.join(', ')}`));
215
+ }
216
+ if (removed.length > 0) {
217
+ this.log(chalk.gray(` - removed: ${removed.join(', ')}`));
218
+ }
219
+ }
220
+ }
153
221
  }
@@ -1,26 +1,20 @@
1
1
  /**
2
- * Project Sync Command
2
+ * Project Sync Command (DEPRECATED)
3
3
  *
4
- * Generates structured architecture summaries for repos in a project.
5
- * For each repo: clone/pull detect _bmad or invoke Claude validate YAML → upload S3 → update DynamoDB.
4
+ * This command has been replaced by `hd module sync`.
5
+ * Kept in place to show deprecation warning to users of the old workflow.
6
6
  */
7
7
  import { Command } from '@oclif/core';
8
8
  export default class ProjectSync extends Command {
9
9
  static args: {
10
- project: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
10
+ project: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
11
11
  };
12
12
  static description: string;
13
- static examples: string[];
13
+ static hidden: boolean;
14
14
  static flags: {
15
15
  domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
16
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
17
  repo: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
18
  };
19
19
  run(): Promise<void>;
20
- private syncRepo;
21
- private detectBmadDocs;
22
- private runClaude;
23
- private validateYaml;
24
- private readGutEntities;
25
- private mergeEntityRegistries;
26
20
  }