zinkee 0.1.44 → 0.1.45

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 (94) hide show
  1. package/README.md +4 -0
  2. package/dist/chunk-MHEVRMDX.js +1230 -0
  3. package/dist/chunk-MHEVRMDX.js.map +1 -0
  4. package/{src/utils/examples.ts → dist/examples-E6FFEVBE.js} +856 -1141
  5. package/dist/examples-E6FFEVBE.js.map +1 -0
  6. package/dist/index.js +1145 -8456
  7. package/dist/index.js.map +1 -1
  8. package/package.json +6 -3
  9. package/.github/workflows/npm-publish.yml +0 -77
  10. package/.github/workflows/pr-checks.yml +0 -36
  11. package/AGENTS.md +0 -110
  12. package/docs/cli-contract.md +0 -41
  13. package/docs/npm-release.md +0 -64
  14. package/docs/superpowers/plans/2026-03-24-zinkee-cli-implementation.md +0 -837
  15. package/docs/superpowers/plans/2026-03-25-cli-backend-error-contract.md +0 -503
  16. package/docs/superpowers/plans/2026-03-26-display-freeform-create.md +0 -389
  17. package/docs/superpowers/specs/2026-03-24-zinkee-cli-backend-blockers.md +0 -172
  18. package/docs/superpowers/specs/2026-03-24-zinkee-cli-design.md +0 -1576
  19. package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-checklist.md +0 -215
  20. package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-design.md +0 -492
  21. package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-status.md +0 -307
  22. package/docs/superpowers/specs/2026-07-30-cli-pr-checks-design.md +0 -37
  23. package/src/api/automations.ts +0 -404
  24. package/src/api/comments.ts +0 -51
  25. package/src/api/displays.ts +0 -337
  26. package/src/api/document-templates.ts +0 -110
  27. package/src/api/files.ts +0 -50
  28. package/src/api/formulas.test.ts +0 -65
  29. package/src/api/formulas.ts +0 -67
  30. package/src/api/logs.ts +0 -34
  31. package/src/api/navigation.ts +0 -70
  32. package/src/api/records.ts +0 -184
  33. package/src/api/schemas.ts +0 -148
  34. package/src/api/teamspace.ts +0 -110
  35. package/src/cli-examples.ts +0 -130
  36. package/src/cli-runner.ts +0 -95
  37. package/src/client.test.ts +0 -189
  38. package/src/client.ts +0 -269
  39. package/src/command-registry.ts +0 -882
  40. package/src/commands/automations.test.ts +0 -1030
  41. package/src/commands/automations.ts +0 -2102
  42. package/src/commands/comments.test.ts +0 -214
  43. package/src/commands/comments.ts +0 -303
  44. package/src/commands/config.test.ts +0 -81
  45. package/src/commands/config.ts +0 -150
  46. package/src/commands/displays.test.ts +0 -1105
  47. package/src/commands/displays.ts +0 -1442
  48. package/src/commands/document-templates.test.ts +0 -569
  49. package/src/commands/document-templates.ts +0 -563
  50. package/src/commands/files.test.ts +0 -284
  51. package/src/commands/files.ts +0 -280
  52. package/src/commands/formulas.test.ts +0 -194
  53. package/src/commands/formulas.ts +0 -243
  54. package/src/commands/logs.test.ts +0 -123
  55. package/src/commands/logs.ts +0 -159
  56. package/src/commands/navigation.test.ts +0 -211
  57. package/src/commands/navigation.ts +0 -348
  58. package/src/commands/profiles.test.ts +0 -191
  59. package/src/commands/profiles.ts +0 -303
  60. package/src/commands/records.test.ts +0 -860
  61. package/src/commands/records.ts +0 -883
  62. package/src/commands/schemas.test.ts +0 -1252
  63. package/src/commands/schemas.ts +0 -890
  64. package/src/commands/teamspace.test.ts +0 -229
  65. package/src/commands/teamspace.ts +0 -546
  66. package/src/completion/engine.test.ts +0 -138
  67. package/src/completion/engine.ts +0 -168
  68. package/src/completion/install.test.ts +0 -179
  69. package/src/completion/install.ts +0 -260
  70. package/src/completion/runtime.ts +0 -150
  71. package/src/completion/scripts.ts +0 -91
  72. package/src/config.test.ts +0 -362
  73. package/src/config.ts +0 -294
  74. package/src/index.test.ts +0 -217
  75. package/src/index.ts +0 -8
  76. package/src/parsers/expressions.test.ts +0 -95
  77. package/src/parsers/expressions.ts +0 -128
  78. package/src/parsers/kv.test.ts +0 -35
  79. package/src/parsers/kv.ts +0 -49
  80. package/src/parsers/selectors.test.ts +0 -23
  81. package/src/parsers/selectors.ts +0 -29
  82. package/src/program.ts +0 -64
  83. package/src/runtime-context.ts +0 -103
  84. package/src/types.ts +0 -76
  85. package/src/utils/argv-rewrite.test.ts +0 -149
  86. package/src/utils/argv-rewrite.ts +0 -269
  87. package/src/utils/errors.test.ts +0 -85
  88. package/src/utils/errors.ts +0 -191
  89. package/src/utils/examples.test.ts +0 -374
  90. package/src/utils/output.test.ts +0 -65
  91. package/src/utils/output.ts +0 -154
  92. package/src/utils/schema-fields.ts +0 -1016
  93. package/tsconfig.json +0 -20
  94. package/tsup.config.ts +0 -13
@@ -1,191 +0,0 @@
1
- import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
-
5
- import { describe, expect, it } from "vitest";
6
-
7
- import { buildProgram } from "../index.js";
8
-
9
- function createTempConfig(contents: string): string {
10
- const dir = mkdtempSync(join(tmpdir(), "zinkee-profiles-"));
11
- const filePath = join(dir, "config.toml");
12
- writeFileSync(filePath, contents, "utf8");
13
- return filePath;
14
- }
15
-
16
- function createStdoutBuffer() {
17
- let output = "";
18
-
19
- return {
20
- stdout: {
21
- write(chunk: string) {
22
- output += chunk;
23
- return true;
24
- },
25
- },
26
- read() {
27
- return output;
28
- },
29
- };
30
- }
31
-
32
- describe("profiles commands", () => {
33
- it("registers the profiles command group", () => {
34
- const program = buildProgram();
35
-
36
- expect(program.commands.some((command) => command.name() === "profiles")).toBe(true);
37
- });
38
-
39
- it("redacts secrets when showing a profile", async () => {
40
- const configPath = createTempConfig(`
41
- default_profile = "prod"
42
-
43
- [profiles.prod]
44
- api_key = "secret-token-1234"
45
- base_url = "https://api.zinkee.test"
46
- `);
47
- const output = createStdoutBuffer();
48
- const program = buildProgram({ configPath, stdout: output.stdout });
49
-
50
- await program.parseAsync(["node", "zinkee", "profiles", "get", "prod"]);
51
-
52
- expect(output.read()).toContain("apiKey");
53
- expect(output.read()).toContain("********1234");
54
- expect(output.read()).not.toContain("secret-token-1234");
55
- });
56
-
57
- it("adds a profile and writes a TOML config file", async () => {
58
- const configPath = join(mkdtempSync(join(tmpdir(), "zinkee-profiles-add-")), "config.toml");
59
- const output = createStdoutBuffer();
60
- const program = buildProgram({ configPath, stdout: output.stdout });
61
-
62
- await program.parseAsync([
63
- "node",
64
- "zinkee",
65
- "profiles",
66
- "add",
67
- "local",
68
- "--api-key",
69
- "new-token-5678",
70
- "--base-url",
71
- "http://localhost:8088",
72
- ]);
73
-
74
- const saved = readFileSync(configPath, "utf8");
75
-
76
- expect(output.read()).toContain('Added profile "local".');
77
- expect(saved).toContain('[profiles.local]');
78
- expect(saved).toContain('api_key = "new-token-5678"');
79
- expect(saved).toContain('base_url = "http://localhost:8088"');
80
- expect(saved).toContain('default_profile = "local"');
81
- });
82
-
83
- it("rejects a malformed base URL when adding a profile", async () => {
84
- const configPath = join(
85
- mkdtempSync(join(tmpdir(), "zinkee-profiles-add-bad-url-")),
86
- "config.toml",
87
- );
88
- const output = createStdoutBuffer();
89
- const program = buildProgram({ configPath, stdout: output.stdout });
90
-
91
- await expect(
92
- program.parseAsync([
93
- "node",
94
- "zinkee",
95
- "profiles",
96
- "add",
97
- "broken",
98
- "--api-key",
99
- "any",
100
- "--base-url",
101
- "api-z2.zinkee.com",
102
- ]),
103
- ).rejects.toMatchObject({
104
- name: "CliError",
105
- code: "configuration_error",
106
- message: '"--base-url" must be an absolute http(s) URL.',
107
- });
108
- });
109
-
110
- it("rejects a non-http(s) base URL when adding a profile", async () => {
111
- const configPath = join(
112
- mkdtempSync(join(tmpdir(), "zinkee-profiles-add-ftp-")),
113
- "config.toml",
114
- );
115
- const output = createStdoutBuffer();
116
- const program = buildProgram({ configPath, stdout: output.stdout });
117
-
118
- await expect(
119
- program.parseAsync([
120
- "node",
121
- "zinkee",
122
- "profiles",
123
- "add",
124
- "broken",
125
- "--api-key",
126
- "any",
127
- "--base-url",
128
- "ftp://api-z2.zinkee.com",
129
- ]),
130
- ).rejects.toMatchObject({
131
- name: "CliError",
132
- code: "configuration_error",
133
- message: '"--base-url" must be an absolute http(s) URL.',
134
- });
135
- });
136
-
137
- it("normalizes trailing slashes when adding a profile", async () => {
138
- const configPath = join(
139
- mkdtempSync(join(tmpdir(), "zinkee-profiles-add-slash-")),
140
- "config.toml",
141
- );
142
- const output = createStdoutBuffer();
143
- const program = buildProgram({ configPath, stdout: output.stdout });
144
-
145
- await program.parseAsync([
146
- "node",
147
- "zinkee",
148
- "profiles",
149
- "add",
150
- "local",
151
- "--api-key",
152
- "any",
153
- "--base-url",
154
- "https://api-z2.zinkee.com///",
155
- ]);
156
-
157
- const saved = readFileSync(configPath, "utf8");
158
-
159
- expect(saved).toContain('base_url = "https://api-z2.zinkee.com"');
160
- });
161
-
162
- it("rejects a malformed base URL when updating a profile", async () => {
163
- const configPath = createTempConfig(`
164
- [profiles.prod]
165
- api_key = "secret"
166
- base_url = "https://api-z2.zinkee.com"
167
- `);
168
- const output = createStdoutBuffer();
169
- const program = buildProgram({ configPath, stdout: output.stdout });
170
-
171
- await expect(
172
- program.parseAsync([
173
- "node",
174
- "zinkee",
175
- "profiles",
176
- "update",
177
- "prod",
178
- "--base-url",
179
- "not a url",
180
- ]),
181
- ).rejects.toMatchObject({
182
- name: "CliError",
183
- code: "configuration_error",
184
- message: '"--base-url" must be an absolute http(s) URL.',
185
- });
186
-
187
- const saved = readFileSync(configPath, "utf8");
188
-
189
- expect(saved).toContain('base_url = "https://api-z2.zinkee.com"');
190
- });
191
- });
@@ -1,303 +0,0 @@
1
- import type { Writable } from "node:stream";
2
-
3
- import { Command } from "commander";
4
-
5
- import {
6
- defaultConfigPath,
7
- ProfileResolutionError,
8
- readConfigIfExists,
9
- redactProfile,
10
- removeProfileConfig,
11
- setDefaultProfile,
12
- setProfileConfig,
13
- validateBaseUrl,
14
- writeConfig,
15
- } from "../config.js";
16
- import type { AppConfig, ProfileConfig } from "../types.js";
17
- import { CliError } from "../utils/errors.js";
18
- import { buildJsonSuccess, printTable } from "../utils/output.js";
19
-
20
- interface CommandIo {
21
- write(chunk: string): unknown;
22
- }
23
-
24
- export interface RegisterProfilesCommandsOptions {
25
- configPath?: string;
26
- stdout?: CommandIo;
27
- }
28
-
29
- interface GlobalOptions {
30
- json?: boolean;
31
- }
32
-
33
- export interface DisplayProfile extends ProfileConfig {
34
- name: string;
35
- isDefault: boolean;
36
- }
37
-
38
- function getCommandIo(stdout: CommandIo | undefined): CommandIo {
39
- return stdout ?? (process.stdout as Writable);
40
- }
41
-
42
- function writeLine(stdout: CommandIo, value: string): void {
43
- stdout.write(`${value}\n`);
44
- }
45
-
46
- function getGlobalOptions(command: Command): GlobalOptions {
47
- return command.optsWithGlobals<GlobalOptions>();
48
- }
49
-
50
- function emitJson(
51
- stdout: CommandIo,
52
- commandName: string,
53
- data: unknown,
54
- configPath: string,
55
- ): void {
56
- writeLine(
57
- stdout,
58
- JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath })),
59
- );
60
- }
61
-
62
- function readRequiredConfig(filePath: string): AppConfig {
63
- const config = readConfigIfExists(filePath);
64
-
65
- if (config) {
66
- return config;
67
- }
68
-
69
- throw new CliError("configuration_error", `Config file not found at ${filePath}.`);
70
- }
71
-
72
- function getRequiredProfile(config: AppConfig, profileName: string): ProfileConfig {
73
- const profile = config.profiles[profileName];
74
-
75
- if (profile) {
76
- return profile;
77
- }
78
-
79
- throw new CliError("configuration_error", `Profile "${profileName}" not found in config.`);
80
- }
81
-
82
- function toDisplayProfile(
83
- profileName: string,
84
- profile: ProfileConfig,
85
- defaultProfile: string | undefined,
86
- ): DisplayProfile {
87
- return {
88
- name: profileName,
89
- isDefault: defaultProfile === profileName,
90
- ...redactProfile(profile),
91
- };
92
- }
93
-
94
- function loadConfigForAdd(filePath: string): AppConfig {
95
- return readConfigIfExists(filePath) ?? { profiles: {} };
96
- }
97
-
98
- function normalizeProfileBaseUrl(baseUrl: string | undefined): string | undefined {
99
- if (baseUrl === undefined) {
100
- return undefined;
101
- }
102
-
103
- try {
104
- return validateBaseUrl(baseUrl, '"--base-url"');
105
- } catch (error) {
106
- if (error instanceof ProfileResolutionError) {
107
- throw new CliError("configuration_error", error.message);
108
- }
109
-
110
- throw error;
111
- }
112
- }
113
-
114
- function emitProfilesTable(stdout: CommandIo, data: readonly DisplayProfile[]): void {
115
- printTable(
116
- data.map((profile) => ({
117
- name: profile.name,
118
- default: profile.isDefault ? "yes" : "",
119
- baseUrl: profile.baseUrl ?? "",
120
- apiKey: profile.apiKey ?? "",
121
- })),
122
- ["name", "default", "baseUrl", "apiKey"],
123
- stdout,
124
- );
125
- }
126
-
127
- export function registerProfilesCommands(
128
- program: Command,
129
- options: RegisterProfilesCommandsOptions = {},
130
- ): void {
131
- const filePath = options.configPath ?? defaultConfigPath;
132
- const stdout = getCommandIo(options.stdout);
133
- const profiles = program.command("profiles").description("Manage local CLI profiles");
134
-
135
- profiles
136
- .command("list")
137
- .description("List configured profiles")
138
- .action(function action(this: Command) {
139
- const config = readRequiredConfig(filePath);
140
- const data = Object.entries(config.profiles).map(([name, profile]) =>
141
- toDisplayProfile(name, profile, config.defaultProfile),
142
- );
143
-
144
- if (getGlobalOptions(this).json) {
145
- emitJson(stdout, "profiles list", data, filePath);
146
- return;
147
- }
148
-
149
- emitProfilesTable(stdout, data);
150
- });
151
-
152
- profiles
153
- .command("get")
154
- .description("Show a single configured profile")
155
- .argument("<profile>", "profile name")
156
- .action(function action(this: Command, profileName: string) {
157
- const config = readRequiredConfig(filePath);
158
- const profile = getRequiredProfile(config, profileName);
159
- const data = toDisplayProfile(profileName, profile, config.defaultProfile);
160
-
161
- if (getGlobalOptions(this).json) {
162
- emitJson(stdout, "profiles get", data, filePath);
163
- return;
164
- }
165
-
166
- emitProfilesTable(stdout, [data]);
167
- });
168
-
169
- profiles
170
- .command("use")
171
- .description("Set the default profile")
172
- .argument("<profile>", "profile name")
173
- .action(function action(this: Command, profileName: string) {
174
- const config = readRequiredConfig(filePath);
175
- getRequiredProfile(config, profileName);
176
-
177
- const nextConfig = setDefaultProfile(config, profileName);
178
- writeConfig(nextConfig, filePath);
179
- const data = {
180
- profile: profileName,
181
- defaultProfile: nextConfig.defaultProfile,
182
- };
183
-
184
- if (getGlobalOptions(this).json) {
185
- emitJson(stdout, "profiles use", data, filePath);
186
- return;
187
- }
188
-
189
- writeLine(stdout, `Using profile "${profileName}".`);
190
- });
191
-
192
- profiles
193
- .command("add")
194
- .description("Add a profile to the local config")
195
- .argument("<profile>", "profile name")
196
- .requiredOption("--api-key <token>", "API key")
197
- .option("--base-url <url>", "base URL")
198
- .action(function action(
199
- this: Command,
200
- profileName: string,
201
- commandOptions: { apiKey: string; baseUrl?: string },
202
- ) {
203
- const config = loadConfigForAdd(filePath);
204
-
205
- if (config.profiles[profileName]) {
206
- throw new CliError(
207
- "configuration_error",
208
- `Profile "${profileName}" already exists in config.`,
209
- );
210
- }
211
-
212
- const baseUrl = normalizeProfileBaseUrl(commandOptions.baseUrl);
213
-
214
- let nextConfig = setProfileConfig(config, profileName, {
215
- apiKey: commandOptions.apiKey,
216
- baseUrl,
217
- });
218
-
219
- if (!nextConfig.defaultProfile) {
220
- nextConfig = setDefaultProfile(nextConfig, profileName);
221
- }
222
-
223
- writeConfig(nextConfig, filePath);
224
- const data = toDisplayProfile(
225
- profileName,
226
- nextConfig.profiles[profileName] ?? {},
227
- nextConfig.defaultProfile,
228
- );
229
-
230
- if (getGlobalOptions(this).json) {
231
- emitJson(stdout, "profiles add", data, filePath);
232
- return;
233
- }
234
-
235
- writeLine(stdout, `Added profile "${profileName}".`);
236
- });
237
-
238
- profiles
239
- .command("update")
240
- .description("Update an existing profile")
241
- .argument("<profile>", "profile name")
242
- .option("--api-key <token>", "API key")
243
- .option("--base-url <url>", "base URL")
244
- .action(function action(
245
- this: Command,
246
- profileName: string,
247
- commandOptions: { apiKey?: string; baseUrl?: string },
248
- ) {
249
- if (commandOptions.apiKey === undefined && commandOptions.baseUrl === undefined) {
250
- throw new CliError(
251
- "configuration_error",
252
- 'Provide at least one of "--api-key" or "--base-url".',
253
- );
254
- }
255
-
256
- const config = readRequiredConfig(filePath);
257
- getRequiredProfile(config, profileName);
258
-
259
- const baseUrl = normalizeProfileBaseUrl(commandOptions.baseUrl);
260
-
261
- const nextConfig = setProfileConfig(config, profileName, {
262
- apiKey: commandOptions.apiKey,
263
- baseUrl,
264
- });
265
-
266
- writeConfig(nextConfig, filePath);
267
- const data = toDisplayProfile(
268
- profileName,
269
- nextConfig.profiles[profileName] ?? {},
270
- nextConfig.defaultProfile,
271
- );
272
-
273
- if (getGlobalOptions(this).json) {
274
- emitJson(stdout, "profiles update", data, filePath);
275
- return;
276
- }
277
-
278
- writeLine(stdout, `Updated profile "${profileName}".`);
279
- });
280
-
281
- profiles
282
- .command("remove")
283
- .description("Remove a profile from the local config")
284
- .argument("<profile>", "profile name")
285
- .action(function action(this: Command, profileName: string) {
286
- const config = readRequiredConfig(filePath);
287
- getRequiredProfile(config, profileName);
288
-
289
- const nextConfig = removeProfileConfig(config, profileName);
290
- writeConfig(nextConfig, filePath);
291
- const data = {
292
- profile: profileName,
293
- defaultProfile: nextConfig.defaultProfile,
294
- };
295
-
296
- if (getGlobalOptions(this).json) {
297
- emitJson(stdout, "profiles remove", data, filePath);
298
- return;
299
- }
300
-
301
- writeLine(stdout, `Removed profile "${profileName}".`);
302
- });
303
- }