@hesed/gchat 0.3.1 → 0.4.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/README.md CHANGED
@@ -26,7 +26,7 @@ $ npm install -g @hesed/gchat
26
26
  $ gchat COMMAND
27
27
  running command...
28
28
  $ gchat (--version)
29
- @hesed/gchat/0.3.1 linux-x64 node-v24.19.0
29
+ @hesed/gchat/0.4.0 linux-x64 node-v24.20.0
30
30
  $ gchat --help [COMMAND]
31
31
  USAGE
32
32
  $ gchat COMMAND
@@ -38,6 +38,7 @@ USAGE
38
38
 
39
39
  <!-- commands -->
40
40
  * [`gchat gchat config add-token PROFILE SPACEID TOKEN`](#gchat-gchat-config-add-token-profile-spaceid-token)
41
+ * [`gchat gchat config add-user NAME USERID`](#gchat-gchat-config-add-user-name-userid)
41
42
  * [`gchat gchat config set-key PROFILE KEY`](#gchat-gchat-config-set-key-profile-key)
42
43
  * [`gchat gchat create-message SPACEID MESSAGE`](#gchat-gchat-create-message-spaceid-message)
43
44
  * [`gchat gchat reply-message THREADNAME MESSAGE`](#gchat-gchat-reply-message-threadname-message)
@@ -62,7 +63,30 @@ EXAMPLES
62
63
  $ gchat gchat config add-token default AAQAKA6hsFw your-space-token
63
64
  ```
64
65
 
65
- _See code: [src/commands/gchat/config/add-token.ts](https://github.com/hesedcasa/gchat/blob/v0.3.1/src/commands/gchat/config/add-token.ts)_
66
+ _See code: [src/commands/gchat/config/add-token.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/config/add-token.ts)_
67
+
68
+ ## `gchat gchat config add-user NAME USERID`
69
+
70
+ Add or update a user in the users list for tagging messages
71
+
72
+ ```
73
+ USAGE
74
+ $ gchat gchat config add-user NAME USERID
75
+
76
+ ARGUMENTS
77
+ NAME Display name of the user, used with --tag
78
+ USERID Google Chat user ID (e.g. users/123456789)
79
+
80
+ DESCRIPTION
81
+ Add or update a user in the users list for tagging messages
82
+
83
+ EXAMPLES
84
+ $ gchat gchat config add-user "Jane Doe" users/123456789012345678901
85
+
86
+ $ gchat gchat config add-user "Jane Doe" 123456789012345678901
87
+ ```
88
+
89
+ _See code: [src/commands/gchat/config/add-user.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/config/add-user.ts)_
66
90
 
67
91
  ## `gchat gchat config set-key PROFILE KEY`
68
92
 
@@ -85,7 +109,7 @@ EXAMPLES
85
109
  $ gchat gchat config set-key work your-work-api-key
86
110
  ```
87
111
 
88
- _See code: [src/commands/gchat/config/set-key.ts](https://github.com/hesedcasa/gchat/blob/v0.3.1/src/commands/gchat/config/set-key.ts)_
112
+ _See code: [src/commands/gchat/config/set-key.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/config/set-key.ts)_
89
113
 
90
114
  ## `gchat gchat create-message SPACEID MESSAGE`
91
115
 
@@ -93,7 +117,7 @@ Send a message to a Google Chat space
93
117
 
94
118
  ```
95
119
  USAGE
96
- $ gchat gchat create-message SPACEID MESSAGE [-f] [-p <value>] [--toon]
120
+ $ gchat gchat create-message SPACEID MESSAGE [-f] [-p <value>] [-t <value>...] [--toon]
97
121
 
98
122
  ARGUMENTS
99
123
  SPACEID Google Chat space ID
@@ -102,6 +126,7 @@ ARGUMENTS
102
126
  FLAGS
103
127
  -f, --formatted Enable formatted text (bold, italic, links)
104
128
  -p, --profile=<value> [default: default] Config profile to use
129
+ -t, --tag=<value>... User name or users/<id> to tag (repeatable)
105
130
  --toon Format output as toon
106
131
 
107
132
  DESCRIPTION
@@ -115,9 +140,11 @@ EXAMPLES
115
140
  $ gchat gchat create-message AAQAKA6hsFw "*Bold message*" --formatted
116
141
 
117
142
  $ gchat gchat create-message AAQAKA6hsFw "<https://example.com|Click here>" -f
143
+
144
+ $ gchat gchat create-message AAQAKA6hsFw "Deploy failed" --tag "Jane Doe"
118
145
  ```
119
146
 
120
- _See code: [src/commands/gchat/create-message.ts](https://github.com/hesedcasa/gchat/blob/v0.3.1/src/commands/gchat/create-message.ts)_
147
+ _See code: [src/commands/gchat/create-message.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/create-message.ts)_
121
148
 
122
149
  ## `gchat gchat reply-message THREADNAME MESSAGE`
123
150
 
@@ -125,7 +152,7 @@ Reply to a message thread in Google Chat
125
152
 
126
153
  ```
127
154
  USAGE
128
- $ gchat gchat reply-message THREADNAME MESSAGE [-f] [-p <value>] [--toon]
155
+ $ gchat gchat reply-message THREADNAME MESSAGE [-f] [-p <value>] [-t <value>...] [--toon]
129
156
 
130
157
  ARGUMENTS
131
158
  THREADNAME Thread name (e.g. spaces/SPACE_ID/threads/THREAD_ID)
@@ -134,6 +161,7 @@ ARGUMENTS
134
161
  FLAGS
135
162
  -f, --formatted Enable formatted text (bold, italic, links)
136
163
  -p, --profile=<value> [default: default] Config profile to use
164
+ -t, --tag=<value>... User name or users/<id> to tag (repeatable)
137
165
  --toon Format output as toon
138
166
 
139
167
  DESCRIPTION
@@ -145,7 +173,9 @@ EXAMPLES
145
173
  $ gchat gchat reply-message spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Reply here" --profile work
146
174
 
147
175
  $ gchat gchat reply-message spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "*Bold reply*" --formatted
176
+
177
+ $ gchat gchat reply-message spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Check this" --tag "Jane Doe"
148
178
  ```
149
179
 
150
- _See code: [src/commands/gchat/reply-message.ts](https://github.com/hesedcasa/gchat/blob/v0.3.1/src/commands/gchat/reply-message.ts)_
180
+ _See code: [src/commands/gchat/reply-message.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/reply-message.ts)_
151
181
  <!-- commandsstop -->
@@ -10,7 +10,9 @@ export default class ConfigAddToken extends Command {
10
10
  static examples = ['<%= config.bin %> <%= command.id %> default AAQAKA6hsFw your-space-token'];
11
11
  async run() {
12
12
  const { args } = await this.parse(ConfigAddToken);
13
- const config = await readConfigOrEmpty(this.config.configDir);
13
+ const config = await readConfigOrEmpty(this.config.configDir, this.log.bind(this));
14
+ if (!config)
15
+ return;
14
16
  const profile = config.profiles[args.profile] ?? { key: '', tokens: {} };
15
17
  profile.tokens[args.spaceId] = args.token;
16
18
  config.profiles[args.profile] = profile;
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigAddUser extends Command {
3
+ static args: {
4
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ userId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,20 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { addUser } from '../../../config.js';
3
+ export default class ConfigAddUser extends Command {
4
+ static args = {
5
+ name: Args.string({ description: 'Display name of the user, used with --tag', required: true }),
6
+ userId: Args.string({ description: 'Google Chat user ID (e.g. users/123456789)', required: true }),
7
+ };
8
+ static description = 'Add or update a user in the users list for tagging messages';
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %> "Jane Doe" users/123456789012345678901',
11
+ '<%= config.bin %> <%= command.id %> "Jane Doe" 123456789012345678901',
12
+ ];
13
+ async run() {
14
+ const { args } = await this.parse(ConfigAddUser);
15
+ const config = await addUser(this.config.configDir, args.name, args.userId, this.log.bind(this));
16
+ if (!config)
17
+ return;
18
+ this.log(`User '${args.name}' saved as ${config.users[args.name]} in the users list.`);
19
+ }
20
+ }
@@ -14,7 +14,9 @@ export default class ConfigSetKey extends Command {
14
14
  ];
15
15
  async run() {
16
16
  const { args } = await this.parse(ConfigSetKey);
17
- const config = await readConfigOrEmpty(this.config.configDir);
17
+ const config = await readConfigOrEmpty(this.config.configDir, this.log.bind(this));
18
+ if (!config)
19
+ return;
18
20
  const profile = config.profiles[args.profile] ?? { key: '', tokens: {} };
19
21
  profile.key = args.key;
20
22
  config.profiles[args.profile] = profile;
@@ -9,6 +9,7 @@ export default class GChatCreateMessage extends Command {
9
9
  static flags: {
10
10
  formatted: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ tag: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
14
  };
14
15
  run(): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
- import { DEFAULT_PROFILE, readConfig, resolveProfile } from '../../config.js';
2
+ import { DEFAULT_PROFILE, readConfig, resolveProfile, resolveTags } from '../../config.js';
3
3
  import { formatAsToon } from '../../format.js';
4
4
  import { clearClients, newMessage } from '../../gchat/gchat-client.js';
5
5
  export default class GChatCreateMessage extends Command {
@@ -15,6 +15,7 @@ export default class GChatCreateMessage extends Command {
15
15
  '<%= config.bin %> <%= command.id %> AAQAKA6hsFw "Hello work" --profile work',
16
16
  '<%= config.bin %> <%= command.id %> AAQAKA6hsFw "*Bold message*" --formatted',
17
17
  '<%= config.bin %> <%= command.id %> AAQAKA6hsFw "<https://example.com|Click here>" -f',
18
+ '<%= config.bin %> <%= command.id %> AAQAKA6hsFw "Deploy failed" --tag "Jane Doe"',
18
19
  ];
19
20
  static flags = {
20
21
  formatted: Flags.boolean({ char: 'f', description: 'Enable formatted text (bold, italic, links)', required: false }),
@@ -24,6 +25,12 @@ export default class GChatCreateMessage extends Command {
24
25
  description: 'Config profile to use',
25
26
  required: false,
26
27
  }),
28
+ tag: Flags.string({
29
+ char: 't',
30
+ description: 'User name or users/<id> to tag (repeatable)',
31
+ multiple: true,
32
+ required: false,
33
+ }),
27
34
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
28
35
  };
29
36
  async run() {
@@ -34,7 +41,14 @@ export default class GChatCreateMessage extends Command {
34
41
  const auth = resolveProfile(config, flags.profile, this.log.bind(this));
35
42
  if (!auth)
36
43
  return;
37
- const result = await newMessage(auth, args.spaceId, args.message.replaceAll(String.raw `\n`, '\n'), flags.formatted);
44
+ const tags = resolveTags(config, flags.tag ?? [], this.log.bind(this));
45
+ if (!tags)
46
+ return;
47
+ let message = args.message.replaceAll(String.raw `\n`, '\n');
48
+ for (const tag of tags) {
49
+ message += `\n<${tag}>`;
50
+ }
51
+ const result = await newMessage(auth, args.spaceId, message, flags.formatted);
38
52
  clearClients();
39
53
  if (flags.toon) {
40
54
  this.log(formatAsToon(result));
@@ -9,6 +9,7 @@ export default class GChatReplyMessage extends Command {
9
9
  static flags: {
10
10
  formatted: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ tag: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
14
  };
14
15
  run(): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
- import { DEFAULT_PROFILE, readConfig, resolveProfile } from '../../config.js';
2
+ import { DEFAULT_PROFILE, readConfig, resolveProfile, resolveTags } from '../../config.js';
3
3
  import { formatAsToon } from '../../format.js';
4
4
  import { clearClients, replyMessage } from '../../gchat/gchat-client.js';
5
5
  export default class GChatReplyMessage extends Command {
@@ -17,6 +17,7 @@ export default class GChatReplyMessage extends Command {
17
17
  '<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Reply here"',
18
18
  '<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Reply here" --profile work',
19
19
  '<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "*Bold reply*" --formatted',
20
+ '<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Check this" --tag "Jane Doe"',
20
21
  ];
21
22
  static flags = {
22
23
  formatted: Flags.boolean({ char: 'f', description: 'Enable formatted text (bold, italic, links)', required: false }),
@@ -26,6 +27,12 @@ export default class GChatReplyMessage extends Command {
26
27
  description: 'Config profile to use',
27
28
  required: false,
28
29
  }),
30
+ tag: Flags.string({
31
+ char: 't',
32
+ description: 'User name or users/<id> to tag (repeatable)',
33
+ multiple: true,
34
+ required: false,
35
+ }),
29
36
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
30
37
  };
31
38
  async run() {
@@ -36,7 +43,14 @@ export default class GChatReplyMessage extends Command {
36
43
  const auth = resolveProfile(config, flags.profile, this.log.bind(this));
37
44
  if (!auth)
38
45
  return;
39
- const result = await replyMessage(auth, args.threadName, args.message.replaceAll(String.raw `\n`, '\n'), flags.formatted);
46
+ const tags = resolveTags(config, flags.tag ?? [], this.log.bind(this));
47
+ if (!tags)
48
+ return;
49
+ let message = args.message.replaceAll(String.raw `\n`, '\n');
50
+ for (const tag of tags) {
51
+ message += `\n<${tag}>`;
52
+ }
53
+ const result = await replyMessage(auth, args.threadName, message, flags.formatted);
40
54
  clearClients();
41
55
  if (flags.toon) {
42
56
  this.log(formatAsToon(result));
package/dist/config.d.ts CHANGED
@@ -5,8 +5,11 @@ export type GChatAuth = {
5
5
  };
6
6
  export type GChatConfig = {
7
7
  profiles: Record<string, GChatAuth>;
8
+ users: Record<string, string>;
8
9
  };
9
- export declare function readConfigOrEmpty(configDir: string): Promise<GChatConfig>;
10
+ export declare function readConfigOrEmpty(configDir: string, log: (msg: string) => void): Promise<GChatConfig | null>;
10
11
  export declare function writeConfig(configDir: string, config: GChatConfig): Promise<void>;
11
12
  export declare function readConfig(configDir: string, log: (msg: string) => void): Promise<GChatConfig | null>;
12
13
  export declare function resolveProfile(config: GChatConfig, profile: string, log: (msg: string) => void): GChatAuth | null;
14
+ export declare function addUser(configDir: string, name: string, userId: string, log: (msg: string) => void): Promise<GChatConfig | null>;
15
+ export declare function resolveTags(config: GChatConfig, names: string[], log: (msg: string) => void): null | string[];
package/dist/config.js CHANGED
@@ -12,26 +12,40 @@ function parseProfiles(raw) {
12
12
  // Legacy single-profile config: promote top-level key/tokens to the default profile.
13
13
  return { [DEFAULT_PROFILE]: { key: raw.key ?? '', tokens: raw.tokens ?? {} } };
14
14
  }
15
- export async function readConfigOrEmpty(configDir) {
15
+ function parseUsers(raw) {
16
+ if (raw.users && typeof raw.users === 'object' && !Array.isArray(raw.users)) {
17
+ return raw.users;
18
+ }
19
+ return {};
20
+ }
21
+ export async function readConfigOrEmpty(configDir, log) {
16
22
  const configPath = path.join(configDir, 'gchat-config.json');
17
23
  try {
18
24
  const raw = JSON.parse(await readFile(configPath, 'utf8'));
19
- return { profiles: parseProfiles(raw) };
25
+ return { profiles: parseProfiles(raw), users: parseUsers(raw) };
20
26
  }
21
- catch {
22
- return { profiles: {} };
27
+ catch (error) {
28
+ // A missing file means the user has not configured anything yet — start empty.
29
+ // Any other failure (malformed JSON, unreadable file) must not be treated as
30
+ // an empty config, or the next write would silently destroy the existing one.
31
+ if (error instanceof Error && error.code === 'ENOENT') {
32
+ return { profiles: {}, users: {} };
33
+ }
34
+ log(`Error: Could not read config from ${configPath}`);
35
+ log('Fix or remove the file and retry — nothing was overwritten.');
36
+ return null;
23
37
  }
24
38
  }
25
39
  export async function writeConfig(configDir, config) {
26
40
  const configPath = path.join(configDir, 'gchat-config.json');
27
41
  await mkdir(configDir, { recursive: true });
28
- await writeFile(configPath, JSON.stringify({ profiles: config.profiles }, null, 2));
42
+ await writeFile(configPath, JSON.stringify({ profiles: config.profiles, users: config.users }, null, 2));
29
43
  }
30
44
  export async function readConfig(configDir, log) {
31
45
  const configPath = path.join(configDir, 'gchat-config.json');
32
46
  try {
33
47
  const raw = JSON.parse(await readFile(configPath, 'utf8'));
34
- return { profiles: parseProfiles(raw) };
48
+ return { profiles: parseProfiles(raw), users: parseUsers(raw) };
35
49
  }
36
50
  catch {
37
51
  log(`Error: Could not read config from ${configPath}`);
@@ -47,3 +61,57 @@ export function resolveProfile(config, profile, log) {
47
61
  }
48
62
  return auth;
49
63
  }
64
+ const USER_PREFIX = 'users/';
65
+ function isUserId(value) {
66
+ if (!value.startsWith(USER_PREFIX))
67
+ return false;
68
+ const id = value.slice(USER_PREFIX.length);
69
+ return id.length > 0 && [...id].every((char) => char.trim().length > 0);
70
+ }
71
+ export async function addUser(configDir, name, userId, log) {
72
+ const config = await readConfigOrEmpty(configDir, log);
73
+ if (!config)
74
+ return null;
75
+ const normalized = userId.startsWith(USER_PREFIX) ? userId : `${USER_PREFIX}${userId}`;
76
+ if (!isUserId(normalized)) {
77
+ log(`Error: Invalid user ID '${userId}'. Expected a 'users/<id>' value with a non-empty, whitespace-free ID.`);
78
+ return null;
79
+ }
80
+ config.users[name] = normalized;
81
+ await writeConfig(configDir, config);
82
+ return config;
83
+ }
84
+ export function resolveTags(config, names, log) {
85
+ const resolved = [];
86
+ const invalid = [];
87
+ const unknown = [];
88
+ for (const name of names) {
89
+ // Own-property lookup only — inherited names like 'toString' must count as unknown.
90
+ const saved = Object.hasOwn(config.users, name) ? config.users[name] : undefined;
91
+ if (saved === undefined) {
92
+ if (isUserId(name)) {
93
+ resolved.push(name);
94
+ }
95
+ else {
96
+ unknown.push(name);
97
+ }
98
+ continue;
99
+ }
100
+ if (isUserId(saved)) {
101
+ resolved.push(saved);
102
+ }
103
+ else {
104
+ invalid.push(name);
105
+ }
106
+ }
107
+ if (invalid.length === 0 && unknown.length === 0)
108
+ return resolved;
109
+ for (const name of invalid) {
110
+ log(`Error: Saved user '${name}' has an invalid ID: '${config.users[name]}'.`);
111
+ }
112
+ for (const name of unknown) {
113
+ log(`Error: User '${name}' not found in the users list.`);
114
+ }
115
+ log("Run 'gchat config add-user <name> <userId>' to add or update a user.");
116
+ return null;
117
+ }
@@ -19,7 +19,8 @@
19
19
  "<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"Hello team\"",
20
20
  "<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"Hello work\" --profile work",
21
21
  "<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"*Bold message*\" --formatted",
22
- "<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"<https://example.com|Click here>\" -f"
22
+ "<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"<https://example.com|Click here>\" -f",
23
+ "<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"Deploy failed\" --tag \"Jane Doe\""
23
24
  ],
24
25
  "flags": {
25
26
  "formatted": {
@@ -40,6 +41,15 @@
40
41
  "multiple": false,
41
42
  "type": "option"
42
43
  },
44
+ "tag": {
45
+ "char": "t",
46
+ "description": "User name or users/<id> to tag (repeatable)",
47
+ "name": "tag",
48
+ "required": false,
49
+ "hasDynamicHelp": false,
50
+ "multiple": true,
51
+ "type": "option"
52
+ },
43
53
  "toon": {
44
54
  "description": "Format output as toon",
45
55
  "name": "toon",
@@ -82,7 +92,8 @@
82
92
  "examples": [
83
93
  "<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"Reply here\"",
84
94
  "<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"Reply here\" --profile work",
85
- "<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"*Bold reply*\" --formatted"
95
+ "<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"*Bold reply*\" --formatted",
96
+ "<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"Check this\" --tag \"Jane Doe\""
86
97
  ],
87
98
  "flags": {
88
99
  "formatted": {
@@ -103,6 +114,15 @@
103
114
  "multiple": false,
104
115
  "type": "option"
105
116
  },
117
+ "tag": {
118
+ "char": "t",
119
+ "description": "User name or users/<id> to tag (repeatable)",
120
+ "name": "tag",
121
+ "required": false,
122
+ "hasDynamicHelp": false,
123
+ "multiple": true,
124
+ "type": "option"
125
+ },
106
126
  "toon": {
107
127
  "description": "Format output as toon",
108
128
  "name": "toon",
@@ -168,6 +188,43 @@
168
188
  "add-token.js"
169
189
  ]
170
190
  },
191
+ "gchat:config:add-user": {
192
+ "aliases": [],
193
+ "args": {
194
+ "name": {
195
+ "description": "Display name of the user, used with --tag",
196
+ "name": "name",
197
+ "required": true
198
+ },
199
+ "userId": {
200
+ "description": "Google Chat user ID (e.g. users/123456789)",
201
+ "name": "userId",
202
+ "required": true
203
+ }
204
+ },
205
+ "description": "Add or update a user in the users list for tagging messages",
206
+ "examples": [
207
+ "<%= config.bin %> <%= command.id %> \"Jane Doe\" users/123456789012345678901",
208
+ "<%= config.bin %> <%= command.id %> \"Jane Doe\" 123456789012345678901"
209
+ ],
210
+ "flags": {},
211
+ "hasDynamicHelp": false,
212
+ "hiddenAliases": [],
213
+ "id": "gchat:config:add-user",
214
+ "pluginAlias": "@hesed/gchat",
215
+ "pluginName": "@hesed/gchat",
216
+ "pluginType": "core",
217
+ "strict": true,
218
+ "enableJsonFlag": false,
219
+ "isESM": true,
220
+ "relativePath": [
221
+ "dist",
222
+ "commands",
223
+ "gchat",
224
+ "config",
225
+ "add-user.js"
226
+ ]
227
+ },
171
228
  "gchat:config:set-key": {
172
229
  "aliases": [],
173
230
  "args": {
@@ -206,5 +263,5 @@
206
263
  ]
207
264
  }
208
265
  },
209
- "version": "0.3.1"
266
+ "version": "0.4.0"
210
267
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hesed/gchat",
3
3
  "description": "CLI for Google Chat API interaction",
4
- "version": "0.3.1",
4
+ "version": "0.4.0",
5
5
  "author": "Hesed",
6
6
  "bin": {
7
7
  "gchat": "./bin/run.js"
@@ -22,7 +22,7 @@
22
22
  "@types/mocha": "^10",
23
23
  "@types/node": "^26",
24
24
  "@types/sinon": "^22.0.0",
25
- "c8": "^11.0.0",
25
+ "c8": "^12.0.0",
26
26
  "chai": "^6",
27
27
  "eslint": "^10.8.1",
28
28
  "eslint-config-oclif": "^7.1.5",