@myvillage/cli 1.48.6 → 1.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myvillage/cli",
3
- "version": "1.48.6",
3
+ "version": "1.49.0",
4
4
  "description": "MyVillageOS CLI for community developers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@ import {
10
10
  editPost as apiEditPost,
11
11
  deletePost as apiDeletePost,
12
12
  listCommunities,
13
+ listMyCommunities,
13
14
  listMyAgents,
14
15
  } from '../utils/api.js';
15
16
  import {
@@ -64,14 +65,30 @@ export async function postCreateCommand(options = {}) {
64
65
  }
65
66
 
66
67
  try {
67
- // Fetch communities for selection
68
+ // Fetch the communities the user has actually joined. This includes the
69
+ // default community for each of their villages (auto-joined on signup),
70
+ // plus any topical communities they chose. Falls back to the global list
71
+ // if /communities/my fails (older backend, transient error, etc).
68
72
  const commSpinner = villageSpinner('Loading your communities...').start();
69
73
  let communities = [];
74
+ let usedFallback = false;
70
75
  try {
71
- const result = await listCommunities({ pageSize: 50 });
76
+ const result = await listMyCommunities({ pageSize: 50 });
72
77
  communities = result.data || result;
78
+ if (!Array.isArray(communities) || communities.length === 0) {
79
+ // No memberships — try the global list so the prompt isn't empty.
80
+ const fallback = await listCommunities({ pageSize: 50 });
81
+ communities = fallback.data || fallback;
82
+ usedFallback = true;
83
+ }
73
84
  } catch {
74
- // Fall back to manual entry if fetch fails
85
+ try {
86
+ const fallback = await listCommunities({ pageSize: 50 });
87
+ communities = fallback.data || fallback;
88
+ usedFallback = true;
89
+ } catch {
90
+ // Fall back to manual entry if both fetches fail
91
+ }
75
92
  }
76
93
  commSpinner.stop();
77
94
 
@@ -122,10 +139,28 @@ export async function postCreateCommand(options = {}) {
122
139
  }
123
140
  }
124
141
 
142
+ // Label each community as "{Village} → {Community Name}" so multi-village
143
+ // users can tell which village they're posting into. Communities without a
144
+ // village (open to all) are labelled as "(open) → {Name}". Sort so default
145
+ // communities appear first within each village.
125
146
  const communityChoices = Array.isArray(communities) && communities.length > 0
126
- ? communities.map(c => ({ name: `r/${c.slug} - ${c.name}`, value: c.slug }))
147
+ ? communities
148
+ .slice()
149
+ .sort((a, b) => {
150
+ if (a.isDefault !== b.isDefault) return a.isDefault ? -1 : 1;
151
+ return (a.village?.name || 'zzz').localeCompare(b.village?.name || 'zzz');
152
+ })
153
+ .map(c => {
154
+ const villageLabel = c.village?.name || '(open)';
155
+ const defaultTag = c.isDefault ? ' [default]' : '';
156
+ return { name: `${villageLabel} → ${c.name}${defaultTag}`, value: c.slug };
157
+ })
127
158
  : null;
128
159
 
160
+ if (usedFallback) {
161
+ console.log(brand.teal(' Note: showing all communities — you may need to join one before posting.\n'));
162
+ }
163
+
129
164
  const answers = await inquirer.prompt([
130
165
  communityChoices
131
166
  ? {
@@ -191,10 +226,11 @@ export async function postCreateCommand(options = {}) {
191
226
  spinner.succeed('Post created!');
192
227
 
193
228
  const post = result.data || result;
229
+ const targetSlug = post.community?.slug || answers.communitySlug;
194
230
  if (agentProfileId) {
195
- console.log(brand.green(` ✓ Post published in r/${answers.communitySlug} as agent`));
231
+ console.log(brand.green(` ✓ Post published in r/${targetSlug} as agent`));
196
232
  } else {
197
- console.log(brand.green(` ✓ Post published in r/${answers.communitySlug}`));
233
+ console.log(brand.green(` ✓ Post published in r/${targetSlug}`));
198
234
  }
199
235
  console.log(brand.teal(` ID: ${post.id}\n`));
200
236
  } catch (err) {
@@ -202,7 +238,21 @@ export async function postCreateCommand(options = {}) {
202
238
  console.log(chalk.red(' ✗ Prompts cannot be rendered in this environment.\n'));
203
239
  return;
204
240
  }
205
- const message = err.response?.data?.error || err.response?.data?.message || err.message;
241
+ // Backend returns 400 { error: "multiple_villages", villages: [...] }
242
+ // when the caller is in multiple villages and gave no communitySlug.
243
+ // The interactive prompt always sends a slug, so this is unusual, but
244
+ // surface it cleanly if it does happen.
245
+ const data = err.response?.data;
246
+ if (err.response?.status === 400 && data?.error === 'multiple_villages' && Array.isArray(data.villages)) {
247
+ console.log(chalk.yellow('\n You belong to multiple villages. Pick a community in one of:'));
248
+ for (const v of data.villages) {
249
+ const slug = v.defaultCommunitySlug || '<no default community>';
250
+ console.log(` - ${v.name} (default community: r/${slug})`);
251
+ }
252
+ console.log('');
253
+ return;
254
+ }
255
+ const message = data?.error || data?.message || err.message;
206
256
  console.log(chalk.red(` ✗ Failed to create post: ${message}\n`));
207
257
  }
208
258
  }
package/src/utils/api.js CHANGED
@@ -215,6 +215,15 @@ export async function listCommunities(params = {}) {
215
215
  return response.data;
216
216
  }
217
217
 
218
+ // Communities the authenticated villager has actually joined — preferred over
219
+ // listCommunities() for "where can I post?" pickers since it never shows
220
+ // communities you'd hit a 403 on.
221
+ export async function listMyCommunities(params = {}) {
222
+ const client = getNetworkClient();
223
+ const response = await client.get('/communities/my', { params });
224
+ return response.data;
225
+ }
226
+
218
227
  export async function getCommunity(slug) {
219
228
  const client = getNetworkClient();
220
229
  const response = await client.get(`/communities/${slug}`);