@kici-dev/compiler 0.5.0 → 0.6.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 (73) hide show
  1. package/dist/cli.js +46 -8
  2. package/dist/commands/approve.d.ts +12 -0
  3. package/dist/commands/approve.js +5 -2
  4. package/dist/commands/compile.js +4 -2
  5. package/dist/commands/doctor.js +2 -2
  6. package/dist/commands/endpoints.js +4 -6
  7. package/dist/commands/feedback.d.ts +53 -0
  8. package/dist/commands/feedback.js +142 -0
  9. package/dist/commands/held-run-client.d.ts +21 -1
  10. package/dist/commands/held-run-client.js +34 -15
  11. package/dist/commands/hook.js +22 -20
  12. package/dist/commands/index.d.ts +4 -0
  13. package/dist/commands/index.js +3 -1
  14. package/dist/commands/init.d.ts +9 -2
  15. package/dist/commands/init.js +43 -16
  16. package/dist/commands/login.js +1 -1
  17. package/dist/commands/orchestrators.js +3 -2
  18. package/dist/commands/reject.d.ts +12 -0
  19. package/dist/commands/reject.js +5 -2
  20. package/dist/commands/report/collect.d.ts +82 -0
  21. package/dist/commands/report/collect.js +234 -0
  22. package/dist/commands/report/identity.d.ts +48 -0
  23. package/dist/commands/report/identity.js +49 -0
  24. package/dist/commands/report/index.d.ts +63 -0
  25. package/dist/commands/report/index.js +119 -0
  26. package/dist/commands/report/upload.d.ts +38 -0
  27. package/dist/commands/report/upload.js +64 -0
  28. package/dist/commands/run-hold-watch.js +2 -2
  29. package/dist/commands/run.js +6 -3
  30. package/dist/commands/runs/show.js +80 -1
  31. package/dist/commands/types.js +51 -8
  32. package/dist/execution/sdk-alias.js +4 -2
  33. package/dist/fixtures/compiler.js +2 -1
  34. package/dist/format.js +3 -3
  35. package/dist/generators/secrets-dts.d.ts +8 -2
  36. package/dist/generators/secrets-dts.js +3 -2
  37. package/dist/hooks/installer.js +2 -1
  38. package/dist/llm-context/llms-architecture.txt +35 -13
  39. package/dist/llm-context/llms-cli-remote.txt +2347 -0
  40. package/dist/llm-context/llms-cli.txt +284 -2470
  41. package/dist/llm-context/llms-features-execution.txt +2028 -0
  42. package/dist/llm-context/llms-features.txt +298 -1483
  43. package/dist/llm-context/llms-full.txt +6153 -4639
  44. package/dist/llm-context/llms-getting-started.txt +292 -12
  45. package/dist/llm-context/llms-patterns.txt +176 -1
  46. package/dist/llm-context/llms-providers.txt +11 -27
  47. package/dist/llm-context/llms-sdk-runtime.txt +25 -4
  48. package/dist/llm-context/llms-sdk.txt +31 -1
  49. package/dist/llm-context/llms.txt +33 -18
  50. package/dist/local-plane/paths.d.ts +15 -0
  51. package/dist/local-plane/paths.js +22 -1
  52. package/dist/local-plane/plane-manager.js +2 -2
  53. package/dist/local-plane/port-holder.js +1 -1
  54. package/dist/local-plane/postgres.d.ts +3 -16
  55. package/dist/local-plane/postgres.js +10 -15
  56. package/dist/lockfile/generator.d.ts +12 -0
  57. package/dist/lockfile/generator.js +47 -14
  58. package/dist/postinstall.js +2 -1
  59. package/dist/remote/config.d.ts +2 -15
  60. package/dist/remote/config.js +2 -16
  61. package/dist/remote/dashboard-client.d.ts +39 -0
  62. package/dist/remote/dashboard-client.js +41 -0
  63. package/dist/remote/oauth.js +7 -5
  64. package/dist/remote/uploader.js +2 -2
  65. package/dist/templates/package-json.js +1 -1
  66. package/dist/test-runner/dry-run.js +4 -2
  67. package/dist/test-runner/git-detector.js +2 -1
  68. package/dist/test-runner/job-executor.js +2 -1
  69. package/dist/test-runner/payload-builder.js +11 -17
  70. package/dist/types.d.ts +33 -3
  71. package/dist/validation/validator.js +23 -6
  72. package/package.json +16 -11
  73. package/sbom.spdx.json +953 -901
package/dist/cli.js CHANGED
@@ -4,10 +4,10 @@ import { shouldSuppressBanner } from "./cli-banner.js";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { resolve } from "node:path";
6
6
  import { realpathSync } from "node:fs";
7
- import { Argument, Command, Option } from "commander";
7
+ import { Argument, Command } from "commander";
8
8
  import pc from "picocolors";
9
9
  //#region src/cli.ts
10
- const version = "0.5.0";
10
+ const version = "0.6.1";
11
11
  /**
12
12
  * Top-level commands that were removed, mapped to their current equivalent.
13
13
  * Consulted when the CLI hits an unknown command so the user gets a precise
@@ -139,7 +139,7 @@ function buildProgram() {
139
139
  const success = await previewCommand(event, options);
140
140
  process.exit(success ? 0 : 1);
141
141
  });
142
- program.command("init").description("Initialize .kici/ directory with default workflows").option("--force", "Overwrite existing .kici/ directory", false).option("--skip-install", "Create files without installing dependencies", false).option("--package-manager <npm|pnpm|yarn>", "Force a package manager for the install step (default: auto-detect)").option("--mjs", "JavaScript-only mode (no TypeScript, no dependencies)", false).option("--workspace", "Integrate .kici/ into the detected pnpm/npm/yarn workspace so workflows can import sibling packages", false).option("--standalone", "Force a self-contained .kici/ even inside a workspace", false).option("--no-agents-md", "Skip writing .kici/AGENTS.md (LLM authoring context)").option("--private-registry <url>", "Scaffold a workflow registries: entry pointing at <url>").option("--private-registry-scope <scope>", "Optional npm package scope (e.g. @my-org) for the private registry").option("--private-registry-secret <ref>", "Qualified secret reference (env:NAME) the private registry token comes from", "production:NPM_TOKEN").addOption(new Option("--use-verdaccio-local").default(false).hideHelp()).action(async (options) => {
142
+ program.command("init").description("Initialize .kici/ directory with default workflows").option("--force", "Overwrite existing .kici/ directory", false).option("--skip-install", "Create files without installing dependencies", false).option("--package-manager <npm|pnpm|yarn>", "Force a package manager for the install step (default: auto-detect)").option("--mjs", "JavaScript-only mode (no TypeScript, no dependencies)", false).option("--workspace", "Integrate .kici/ into the detected pnpm/npm/yarn workspace so workflows can import sibling packages", false).option("--standalone", "Force a self-contained .kici/ even inside a workspace", false).option("--no-agents-md", "Skip writing .kici/AGENTS.md (LLM authoring context)").option("--private-registry <url>", "Scaffold a workflow registries: entry pointing at <url>").option("--private-registry-scope <scope>", "Optional npm package scope (e.g. @my-org) for the private registry").option("--private-registry-secret <ref>", "Qualified secret reference (env:NAME) the private registry token comes from", "production:NPM_TOKEN").action(async (options) => {
143
143
  const { initCommand } = await import("./commands/index.js");
144
144
  const success = await initCommand({
145
145
  ...options,
@@ -355,7 +355,7 @@ Environment variables:
355
355
  const success = await runsListCommand(options);
356
356
  process.exit(success ? 0 : 1);
357
357
  });
358
- runsCommand.command("show").argument("<run-id>", "Run ID to inspect").description("Show a run summary with its jobs and steps").option("--json", "Output raw JSON", false).action(async (runId, options) => {
358
+ runsCommand.command("show").argument("<run-id>", "Run ID to inspect").description("Show a run summary with its jobs and steps, why a job did not run, and any approval hold").option("--json", "Output raw JSON", false).action(async (runId, options) => {
359
359
  const { runsShowCommand } = await import("./commands/index.js");
360
360
  const success = await runsShowCommand(runId, { json: options.json });
361
361
  process.exit(success ? 0 : 1);
@@ -396,19 +396,23 @@ Environment variables:
396
396
  });
397
397
  process.exit(success ? 0 : 1);
398
398
  });
399
- program.command("approve").argument("<run-id>", "Run ID whose approval gate to approve").description("Approve a held approval gate for a run").option("--job <name>", "Approve the hold for a specific job").option("--step <index>", "Approve a step-scoped hold (requires --job)").action(async (runId, options) => {
399
+ program.command("approve").argument("<run-id>", "Run ID whose approval gate to approve").description("Approve a held approval gate for a run").option("--job <name>", "Approve the hold for a specific job").option("--step <index>", "Approve a step-scoped hold (requires --job)").option("--hold-type <type>", "Approve the hold of this type (reviewer, timer, concurrency, security) — a job can carry two").option("--hold <id>", "Approve one hold by its id, as listed when nothing else separates them").action(async (runId, options) => {
400
400
  const { approveCommand } = await import("./commands/index.js");
401
401
  const success = await approveCommand(runId, {
402
402
  job: options.job,
403
- step: options.step
403
+ step: options.step,
404
+ hold: options.hold,
405
+ holdType: options.holdType
404
406
  });
405
407
  process.exit(success ? 0 : 1);
406
408
  });
407
- program.command("reject").argument("<run-id>", "Run ID whose approval gate to reject").description("Reject a held approval gate for a run").option("--job <name>", "Reject the hold for a specific job").option("--step <index>", "Reject a step-scoped hold (requires --job)").requiredOption("--reason <text>", "Reason for the rejection").action(async (runId, options) => {
409
+ program.command("reject").argument("<run-id>", "Run ID whose approval gate to reject").description("Reject a held approval gate for a run").option("--job <name>", "Reject the hold for a specific job").option("--step <index>", "Reject a step-scoped hold (requires --job)").option("--hold-type <type>", "Reject the hold of this type (reviewer, timer, concurrency, security) — a job can carry two").option("--hold <id>", "Reject one hold by its id, as listed when nothing else separates them").requiredOption("--reason <text>", "Reason for the rejection").action(async (runId, options) => {
408
410
  const { rejectCommand } = await import("./commands/index.js");
409
411
  const success = await rejectCommand(runId, {
410
412
  job: options.job,
411
413
  step: options.step,
414
+ hold: options.hold,
415
+ holdType: options.holdType,
412
416
  reason: options.reason
413
417
  });
414
418
  process.exit(success ? 0 : 1);
@@ -440,6 +444,32 @@ Environment variables:
440
444
  });
441
445
  process.exit(success ? 0 : 1);
442
446
  });
447
+ const reportCmd = program.command("report").description("Gather a redacted diagnostic bundle to share when reporting an issue");
448
+ reportCmd.enablePositionalOptions();
449
+ reportCmd.option("--run <id>", "Scope the bundle to a failing run").option("-o, --output <path>", "Where to write the bundle ZIP").option("--metadata <key=value>", "Attach metadata (repeatable)", (v, p) => [...p, v], []).option("--no-redact", "Do NOT redact secrets (prints a loud warning)").option("--upload", "Upload the bundle privately to KiCI and print a reference id").option("--message <text>", "Describe the problem (sent with --upload)").option("--email <address>", "Contact address for follow-up (sent with --upload)").option("--kici-dir <path>", "Path to the .kici directory", ".kici").action(async (options) => {
450
+ const { reportCommand } = await import("./commands/index.js");
451
+ const ok = await reportCommand({
452
+ output: options.output,
453
+ run: options.run,
454
+ metadata: options.metadata,
455
+ redact: options.redact !== false,
456
+ upload: options.upload,
457
+ message: options.message,
458
+ email: options.email,
459
+ kiciDir: options.kiciDir
460
+ });
461
+ process.exit(ok ? 0 : 1);
462
+ });
463
+ reportCmd.command("list").description("List the issue reports you have uploaded").option("--json", "Output raw JSON", false).action(async (options) => {
464
+ const { reportListCommand } = await import("./commands/index.js");
465
+ const ok = await reportListCommand({ json: options.json });
466
+ process.exit(ok ? 0 : 1);
467
+ });
468
+ reportCmd.command("withdraw").argument("<ref>", "Reference id of the report to withdraw").description("Withdraw an uploaded report and delete its bundle").action(async (ref) => {
469
+ const { reportWithdrawCommand } = await import("./commands/index.js");
470
+ const ok = await reportWithdrawCommand({ ref });
471
+ process.exit(ok ? 0 : 1);
472
+ });
443
473
  program.command("workflows").description("Manage workflow registrations").command("list").description("List permanently registered workflows").option("--json", "Output as JSON", false).option("--stale <duration>", "Filter stale registrations (e.g., 30d, 7d)").option("--trigger-type <type>", "Filter by trigger type").option("--repo <repo>", "Filter by repository").action(async (options) => {
444
474
  const { workflowsListCommand } = await import("./commands/index.js");
445
475
  const success = await workflowsListCommand({
@@ -454,7 +484,7 @@ Environment variables:
454
484
  const { docsCommand } = await import("./commands/index.js");
455
485
  const success = await docsCommand({ open: options.open });
456
486
  process.exit(success ? 0 : 1);
457
- }).command("llm [topic]").description("Print KiCI LLM docs bundles. No topic prints the llms.txt index; <topic> prints a task bundle (e.g. sdk, cli, patterns, features, providers, architecture, getting-started); \"full\" prints the complete bundle.").option("--out <path>", "Write the bundle to a file instead of stdout").action(async (topic, options) => {
487
+ }).command("llm [topic]").description("Print KiCI LLM docs bundles. No topic prints the llms.txt index; <topic> prints a task bundle (e.g. sdk, cli, cli-remote, patterns, features, providers, architecture, getting-started); \"full\" prints the complete bundle.").option("--out <path>", "Write the bundle to a file instead of stdout").action(async (topic, options) => {
458
488
  const { docsLlmCommand } = await import("./commands/index.js");
459
489
  const success = await docsLlmCommand({
460
490
  topic,
@@ -462,6 +492,14 @@ Environment variables:
462
492
  });
463
493
  process.exit(success ? 0 : 1);
464
494
  });
495
+ program.command("feedback").description("Print how to report a discrepancy between what KiCI advertises and what it does. Files nothing.").option("--open", "Open the prefilled issue form in the default browser").option("--json", "Emit the reporting contract as JSON").action(async (options) => {
496
+ const { feedbackCommand } = await import("./commands/index.js");
497
+ const success = await feedbackCommand({
498
+ open: options.open,
499
+ json: options.json
500
+ });
501
+ process.exit(success ? 0 : 1);
502
+ });
465
503
  program.command("admin").description("Operator-facing commands for running instances").command("drain-worker").description("Trigger graceful drain on a worker instance").requiredOption("--url <url>", "Worker URL (e.g., http://worker-host:<port>)").action(async (options) => {
466
504
  const { drainWorkerCommand } = await import("./commands/index.js");
467
505
  const success = await drainWorkerCommand({ url: options.url });
@@ -11,6 +11,18 @@ export interface ApproveOptions {
11
11
  job?: string;
12
12
  /** Match a step-scoped hold by its step index. */
13
13
  step?: string;
14
+ /**
15
+ * Match one hold by its own id. The escape hatch for an ambiguity nothing
16
+ * else resolves; the error listing prints the ids when it needs to.
17
+ */
18
+ hold?: string;
19
+ /**
20
+ * Narrow to holds of one type (`reviewer` / `timer` / `concurrency` /
21
+ * `security`). A job carrying an SDK `requireApproval` AND a security-typed
22
+ * context gate has two pending holds under one job name, and this is what
23
+ * separates them.
24
+ */
25
+ holdType?: string;
14
26
  }
15
27
  /**
16
28
  * Approve a held approval gate.
@@ -22,9 +22,12 @@ async function approveCommand(runId, options = {}) {
22
22
  try {
23
23
  const ctx = await resolveHeldRunContext();
24
24
  if (!ctx) return false;
25
- const resolution = resolveHeldRunId(await listHeldRunsForRun(ctx, runId), {
25
+ const holds = await listHeldRunsForRun(ctx, runId);
26
+ const resolution = resolveHeldRunId(holds, {
26
27
  job: options.job,
27
- step: options.step
28
+ step: options.step,
29
+ holdId: options.hold,
30
+ holdType: options.holdType
28
31
  });
29
32
  if (!resolution.ok) {
30
33
  logger.error(pc.red(resolution.error));
@@ -60,7 +60,8 @@ async function compileCommand(options) {
60
60
  if (existsSync(path.join(absoluteKiciDir, "package.json"))) {
61
61
  const existingHash = await readExistingLockfileHash(lockPath);
62
62
  if (existingHash) {
63
- const currentHash = computeLockfileHash(detectGitRoot());
63
+ const gitRoot = detectGitRoot();
64
+ const currentHash = computeLockfileHash(gitRoot);
64
65
  if (currentHash && currentHash !== existingHash) {
65
66
  logger.info(pc.yellow("Dependencies changed") + " — reinstalling before compile...");
66
67
  await reinstallDeps(absoluteKiciDir);
@@ -75,7 +76,8 @@ async function compileCommand(options) {
75
76
  return false;
76
77
  }
77
78
  if (options.verbose) logger.debug(pc.dim("Validation passed"));
78
- const lockJson = serializeLockFile(generateLockFile(workflowsWithSource));
79
+ const lockFile = generateLockFile(workflowsWithSource);
80
+ const lockJson = serializeLockFile(lockFile);
79
81
  const windowWarning = schemaWindowWarning(BREAKING_FLOOR, SCHEMA_VERSION);
80
82
  if (windowWarning) logger.warn(pc.yellow(windowWarning));
81
83
  if (!options.check) {
@@ -20,7 +20,7 @@ import { promisify } from "node:util";
20
20
  * Platform API is required.
21
21
  */
22
22
  const execFileAsync = promisify(execFile);
23
- const SEVEN_DAYS_MS = 10080 * 60 * 1e3;
23
+ const SEVEN_DAYS_MS = 6048e5;
24
24
  /** Is a stored token + endpoint present (regardless of expiry)? */
25
25
  function hasCredentials(config) {
26
26
  return Boolean((config.pat ?? config.token) && (config.platformEndpoint ?? config.endpoint));
@@ -47,7 +47,7 @@ function checkLogin(config, now) {
47
47
  if (expiresAt - now < SEVEN_DAYS_MS) return {
48
48
  name,
49
49
  status: "warn",
50
- message: `Access token expires in ${Math.ceil((expiresAt - now) / (1440 * 60 * 1e3))} day(s).`,
50
+ message: `Access token expires in ${Math.ceil((expiresAt - now) / 864e5)} day(s).`,
51
51
  nextCommand: "kici login"
52
52
  };
53
53
  }
@@ -119,12 +119,10 @@ function categorizeTrigger(workflowName, trigger, groups) {
119
119
  eventName: `workflows_failed_batch${trigger.name ? `:${trigger.name}` : ""}`
120
120
  });
121
121
  break;
122
- case "job_complete":
123
- groups.kiciEvent.push({
124
- workflowName,
125
- eventName: `job_complete${trigger.workflow ? `:${trigger.workflow}` : ""}${trigger.job ? `.${trigger.job}` : ""}`
126
- });
127
- break;
122
+ case "job_complete": groups.kiciEvent.push({
123
+ workflowName,
124
+ eventName: `job_complete${trigger.workflow ? `:${trigger.workflow}` : ""}${trigger.job ? `.${trigger.job}` : ""}`
125
+ });
128
126
  }
129
127
  }
130
128
  /**
@@ -0,0 +1,53 @@
1
+ /** The public tracker. Reports about KiCI itself go here — never customer data. */
2
+ export declare const FEEDBACK_TRACKER_URL = "https://github.com/kici-dev/kici-public";
3
+ /** The issue form tuned for an advertised-versus-actual report. */
4
+ export declare const FEEDBACK_TEMPLATE = "agent_report.yml";
5
+ export declare const FEEDBACK_NEW_ISSUE_URL = "https://github.com/kici-dev/kici-public/issues/new?template=agent_report.yml";
6
+ /** Suspected vulnerabilities go here instead, privately. */
7
+ export declare const FEEDBACK_SECURITY_ADVISORY_URL = "https://github.com/kici-dev/kici-public/security/advisories/new";
8
+ export interface FeedbackField {
9
+ id: string;
10
+ label: string;
11
+ description: string;
12
+ }
13
+ export interface FeedbackContract {
14
+ tracker: string;
15
+ newIssueUrl: string;
16
+ template: string;
17
+ securityAdvisoryUrl: string;
18
+ guideUrl: string;
19
+ searchCommand: string;
20
+ approval: {
21
+ required: boolean;
22
+ rule: string;
23
+ };
24
+ qualifies: string[];
25
+ doesNotQualify: string[];
26
+ requiredFields: FeedbackField[];
27
+ prohibited: string[];
28
+ privateReportCommand: string;
29
+ }
30
+ /**
31
+ * The single definition of what a reportable discrepancy is and what a report
32
+ * must carry. `kici feedback` prints it, `--json` emits it verbatim, and
33
+ * hack/feedback-contract.test.ts asserts the published guide says the same
34
+ * thing — so the CLI and the doc cannot drift apart.
35
+ */
36
+ export declare const FEEDBACK_CONTRACT: FeedbackContract;
37
+ export interface FeedbackOptions {
38
+ /** Open the prefilled issue form in the default browser. */
39
+ open?: boolean;
40
+ /** Emit the contract as JSON on stdout instead of prose. */
41
+ json?: boolean;
42
+ }
43
+ /**
44
+ * Print the contract for reporting a KiCI discrepancy: what qualifies, what a
45
+ * report must carry, what must never appear in a public issue, and the rule
46
+ * that an agent drafts a report but a human decides to file it.
47
+ *
48
+ * The command reaches no network and files nothing. `--open` opens the
49
+ * prefilled issue form; `--json` emits the same contract for an agent to
50
+ * consume without parsing prose.
51
+ */
52
+ export declare function feedbackCommand(options?: FeedbackOptions): Promise<boolean>;
53
+ //# sourceMappingURL=feedback.d.ts.map
@@ -0,0 +1,142 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import pc from "picocolors";
3
+ import { logger, toErrorMessage } from "@kici-dev/core";
4
+ import open from "open";
5
+ //#region src/commands/feedback.ts
6
+ /** The public tracker. Reports about KiCI itself go here — never customer data. */
7
+ const FEEDBACK_TRACKER_URL = "https://github.com/kici-dev/kici-public";
8
+ /** The issue form tuned for an advertised-versus-actual report. */
9
+ const FEEDBACK_TEMPLATE = "agent_report.yml";
10
+ const FEEDBACK_NEW_ISSUE_URL = `${FEEDBACK_TRACKER_URL}/issues/new?template=${FEEDBACK_TEMPLATE}`;
11
+ /** Suspected vulnerabilities go here instead, privately. */
12
+ const FEEDBACK_SECURITY_ADVISORY_URL = `${FEEDBACK_TRACKER_URL}/security/advisories/new`;
13
+ /**
14
+ * The single definition of what a reportable discrepancy is and what a report
15
+ * must carry. `kici feedback` prints it, `--json` emits it verbatim, and
16
+ * hack/feedback-contract.test.ts asserts the published guide says the same
17
+ * thing — so the CLI and the doc cannot drift apart.
18
+ */
19
+ const FEEDBACK_CONTRACT = {
20
+ tracker: FEEDBACK_TRACKER_URL,
21
+ newIssueUrl: FEEDBACK_NEW_ISSUE_URL,
22
+ template: FEEDBACK_TEMPLATE,
23
+ securityAdvisoryUrl: FEEDBACK_SECURITY_ADVISORY_URL,
24
+ guideUrl: "https://kici.dev/docs/user/reporting-discrepancies/",
25
+ searchCommand: "gh issue list --repo kici-dev/kici-public --search \"<terms>\" --state all",
26
+ approval: {
27
+ required: true,
28
+ rule: "Draft the issue, show the full body to the person you are working with, and file it only after they say yes."
29
+ },
30
+ qualifies: [
31
+ "A documented flag, command, or option that does not exist in the version you ran.",
32
+ "Documented output — a shape, a field, an exit code — that differs from what the command produced.",
33
+ "A CLI --help description that contradicts the published docs.",
34
+ "A documented behaviour that does not happen, or a documented guarantee that does not hold.",
35
+ "A documented error or limit that the tool does not actually enforce."
36
+ ],
37
+ doesNotQualify: [
38
+ "Usage questions, or behaviour you find surprising but that the docs describe correctly.",
39
+ "Feature requests and design preferences.",
40
+ "Anything you inferred from reading docs without running the command.",
41
+ "Anything reproduced only on a locally built or unreleased version.",
42
+ "A failure that is your workflow, your credentials, or your environment."
43
+ ],
44
+ requiredFields: [
45
+ {
46
+ id: "advertised",
47
+ label: "What the docs or CLI advertise",
48
+ description: "The exact claim, quoted, plus its source: a docs URL or the command whose --help says it."
49
+ },
50
+ {
51
+ id: "observed",
52
+ label: "What actually happened",
53
+ description: "The real output or behaviour, quoted, with any error text."
54
+ },
55
+ {
56
+ id: "reproduction",
57
+ label: "Minimal reproduction, including setup",
58
+ description: "Every step from an empty directory: the setup commands, a minimal synthetic workflow, and the exact command you ran."
59
+ },
60
+ {
61
+ id: "version",
62
+ label: "Version and environment",
63
+ description: "Output of `kici --version`, plus Node version and OS."
64
+ },
65
+ {
66
+ id: "justification",
67
+ label: "Why this is a discrepancy",
68
+ description: "One or two sentences ruling out the likely misreads — why the docs cannot be read to match what you observed."
69
+ }
70
+ ],
71
+ prohibited: [
72
+ "No secrets, tokens, or credentials — not even redacted-looking ones.",
73
+ "No private repository names, internal hostnames, organization ids, or run ids.",
74
+ "No log excerpts you have not read line by line.",
75
+ "Reproduce with a minimal synthetic workflow, never the real one you were working on."
76
+ ],
77
+ privateReportCommand: "kici report --run <run-id> --upload"
78
+ };
79
+ function printContract() {
80
+ const c = FEEDBACK_CONTRACT;
81
+ logger.info(pc.bold("Reporting a KiCI discrepancy"));
82
+ logger.info("");
83
+ logger.info("Use this when the published docs or the CLI advertise one behaviour and KiCI does another.");
84
+ logger.info(`Tracker: ${c.tracker}`);
85
+ logger.info(`Full guide: ${c.guideUrl}`);
86
+ logger.info("");
87
+ logger.info(pc.bold("1. Search first"));
88
+ logger.info("Comment on an existing report rather than opening a second one.");
89
+ logger.info(` ${c.searchCommand}`);
90
+ logger.info("");
91
+ logger.info(pc.bold("2. Check it qualifies"));
92
+ for (const item of c.qualifies) logger.info(` ${pc.green("+")} ${item}`);
93
+ logger.info(" Not a discrepancy:");
94
+ for (const item of c.doesNotQualify) logger.info(` ${pc.gray("-")} ${item}`);
95
+ logger.info("");
96
+ logger.info(pc.bold("3. Never file these publicly"));
97
+ logger.info(` A suspected vulnerability is never a public issue — open a private advisory instead:\n ${c.securityAdvisoryUrl}`);
98
+ logger.info(` A problem with your own runs is not a tracker issue — send it privately instead:\n ${c.privateReportCommand}`);
99
+ for (const rule of c.prohibited) logger.info(` ${pc.yellow("!")} ${rule}`);
100
+ logger.info("");
101
+ logger.info(pc.bold("4. Draft the report"));
102
+ for (const field of c.requiredFields) {
103
+ logger.info(` ${field.label}`);
104
+ logger.info(pc.gray(` ${field.description}`));
105
+ }
106
+ logger.info("");
107
+ logger.info(pc.bold("5. Get approval, then file"));
108
+ logger.info(` ${c.approval.rule}`);
109
+ logger.info(` Form: ${c.newIssueUrl}`);
110
+ logger.info(pc.gray(` Or: kici feedback --open`));
111
+ logger.info("");
112
+ logger.info(pc.gray("Machine-readable: kici feedback --json"));
113
+ }
114
+ /**
115
+ * Print the contract for reporting a KiCI discrepancy: what qualifies, what a
116
+ * report must carry, what must never appear in a public issue, and the rule
117
+ * that an agent drafts a report but a human decides to file it.
118
+ *
119
+ * The command reaches no network and files nothing. `--open` opens the
120
+ * prefilled issue form; `--json` emits the same contract for an agent to
121
+ * consume without parsing prose.
122
+ */
123
+ async function feedbackCommand(options = {}) {
124
+ if (options.json) {
125
+ process.stdout.write(`${JSON.stringify(FEEDBACK_CONTRACT, null, 2)}\n`);
126
+ return true;
127
+ }
128
+ printContract();
129
+ if (!options.open) return true;
130
+ try {
131
+ await open(FEEDBACK_NEW_ISSUE_URL);
132
+ return true;
133
+ } catch (error) {
134
+ logger.error(pc.red(`Could not open a browser: ${toErrorMessage(error)}`));
135
+ logger.info(pc.gray(`Open ${FEEDBACK_NEW_ISSUE_URL} manually.`));
136
+ return false;
137
+ }
138
+ }
139
+ //#endregion
140
+ export { FEEDBACK_CONTRACT, FEEDBACK_NEW_ISSUE_URL, FEEDBACK_SECURITY_ADVISORY_URL, FEEDBACK_TEMPLATE, FEEDBACK_TRACKER_URL, feedbackCommand };
141
+
142
+ //# sourceMappingURL=feedback.js.map
@@ -6,6 +6,19 @@
6
6
  * API.
7
7
  */
8
8
  import type { HeldRunSummary } from '@kici-dev/engine';
9
+ /**
10
+ * A failed held-runs request, carrying the HTTP status so a caller can tell a
11
+ * caller who will NEVER have the data (401 / 403 — no permission on the
12
+ * held-runs surface) from a transport or server failure that may succeed next
13
+ * time. `kici runs show` reads holds as optional detail, and reminding such a
14
+ * caller on every single invocation is noise, not information.
15
+ */
16
+ export declare class HeldRunRequestError extends Error {
17
+ readonly status: number;
18
+ constructor(message: string, status: number);
19
+ /** True when the caller lacks permission — retrying changes nothing. */
20
+ get isPermissionDenied(): boolean;
21
+ }
9
22
  /** Resolved auth context for a held-run command. */
10
23
  export interface HeldRunContext {
11
24
  endpoint: string;
@@ -15,8 +28,15 @@ export interface HeldRunContext {
15
28
  /**
16
29
  * Resolve the auth context, printing a clear error and returning null when the
17
30
  * CLI is not authenticated / no active org is set.
31
+ *
32
+ * `quiet` suppresses the error output for a caller whose own command does not
33
+ * depend on the held-run surface — `kici runs show` reads holds as extra
34
+ * detail on a run it can already display, so an unresolvable context must not
35
+ * make it print an authentication error for a command that worked.
18
36
  */
19
- export declare function resolveHeldRunContext(): Promise<HeldRunContext | null>;
37
+ export declare function resolveHeldRunContext(options?: {
38
+ quiet?: boolean;
39
+ }): Promise<HeldRunContext | null>;
20
40
  /** List the held runs for a single run id. */
21
41
  export declare function listHeldRunsForRun(ctx: HeldRunContext, runId: string): Promise<HeldRunSummary[]>;
22
42
  /**
@@ -11,25 +11,44 @@ import { logger } from "@kici-dev/core";
11
11
  * API.
12
12
  */
13
13
  /**
14
+ * A failed held-runs request, carrying the HTTP status so a caller can tell a
15
+ * caller who will NEVER have the data (401 / 403 — no permission on the
16
+ * held-runs surface) from a transport or server failure that may succeed next
17
+ * time. `kici runs show` reads holds as optional detail, and reminding such a
18
+ * caller on every single invocation is noise, not information.
19
+ */
20
+ var HeldRunRequestError = class extends Error {
21
+ status;
22
+ constructor(message, status) {
23
+ super(message);
24
+ this.status = status;
25
+ this.name = "HeldRunRequestError";
26
+ }
27
+ /** True when the caller lacks permission — retrying changes nothing. */
28
+ get isPermissionDenied() {
29
+ return this.status === 401 || this.status === 403;
30
+ }
31
+ };
32
+ /**
14
33
  * Resolve the auth context, printing a clear error and returning null when the
15
34
  * CLI is not authenticated / no active org is set.
35
+ *
36
+ * `quiet` suppresses the error output for a caller whose own command does not
37
+ * depend on the held-run surface — `kici runs show` reads holds as extra
38
+ * detail on a run it can already display, so an unresolvable context must not
39
+ * make it print an authentication error for a command that worked.
16
40
  */
17
- async function resolveHeldRunContext() {
41
+ async function resolveHeldRunContext(options = {}) {
18
42
  const config = await loadGlobalConfig();
19
- const token = config.pat ?? config.token;
20
- if (!token) {
21
- logger.error(pc.red("Not authenticated. Run `kici login` to get started."));
43
+ const fail = (message) => {
44
+ if (!options.quiet) logger.error(pc.red(message));
22
45
  return null;
23
- }
46
+ };
47
+ const token = config.pat ?? config.token;
48
+ if (!token) return fail("Not authenticated. Run `kici login` to get started.");
24
49
  const endpoint = config.platformEndpoint ?? config.endpoint;
25
- if (!endpoint) {
26
- logger.error(pc.red("No endpoint configured. Run `kici login` to configure."));
27
- return null;
28
- }
29
- if (!config.activeOrgId) {
30
- logger.error(pc.red("No active organization. Run `kici org use <name>` to set one."));
31
- return null;
32
- }
50
+ if (!endpoint) return fail("No endpoint configured. Run `kici login` to configure.");
51
+ if (!config.activeOrgId) return fail("No active organization. Run `kici org use <name>` to set one.");
33
52
  return {
34
53
  endpoint,
35
54
  token,
@@ -49,7 +68,7 @@ async function listHeldRunsForRun(ctx, runId) {
49
68
  method: "GET",
50
69
  headers: authHeaders(ctx.token)
51
70
  });
52
- if (!response.ok) throw new Error(await describeError(response));
71
+ if (!response.ok) throw new HeldRunRequestError(await describeError(response), response.status);
53
72
  return (await response.json()).heldRuns ?? [];
54
73
  }
55
74
  /**
@@ -99,6 +118,6 @@ async function describeError(response) {
99
118
  }
100
119
  }
101
120
  //#endregion
102
- export { listHeldRunsForRun, postApprove, postReject, resolveHeldRunContext };
121
+ export { HeldRunRequestError, listHeldRunsForRun, postApprove, postReject, resolveHeldRunContext };
103
122
 
104
123
  //# sourceMappingURL=held-run-client.js.map
@@ -31,27 +31,28 @@ async function hookInstallCommand(options = {}) {
31
31
  if (options.git) selectedTool = "git";
32
32
  else {
33
33
  const tools = await detectHookTools();
34
- if (tools.length === 0) if (process.stdout.isTTY && !isCiEnvironment()) {
35
- const choice = await select({
36
- message: "No pre-commit tool detected. How would you like to install the hook?",
37
- choices: [{
38
- name: "Raw git hook (.git/hooks/pre-commit)",
39
- value: "git"
40
- }, {
41
- name: "Skip installation",
42
- value: "skip"
43
- }]
44
- });
45
- if (choice === "skip") {
46
- logger.info(pc.yellow("Hook installation skipped."));
34
+ if (tools.length === 0) {
35
+ if (process.stdout.isTTY && !isCiEnvironment()) {
36
+ const choice = await select({
37
+ message: "No pre-commit tool detected. How would you like to install the hook?",
38
+ choices: [{
39
+ name: "Raw git hook (.git/hooks/pre-commit)",
40
+ value: "git"
41
+ }, {
42
+ name: "Skip installation",
43
+ value: "skip"
44
+ }]
45
+ });
46
+ if (choice === "skip") {
47
+ logger.info(pc.yellow("Hook installation skipped."));
48
+ return true;
49
+ }
50
+ selectedTool = choice;
51
+ } else {
52
+ logger.info(pc.yellow("No pre-commit tool detected. Use --git to install raw git hook."));
47
53
  return true;
48
54
  }
49
- selectedTool = choice;
50
- } else {
51
- logger.info(pc.yellow("No pre-commit tool detected. Use --git to install raw git hook."));
52
- return true;
53
- }
54
- else if (tools.length === 1) {
55
+ } else if (tools.length === 1) {
55
56
  selectedTool = tools[0].name;
56
57
  logger.info(pc.gray(`Detected ${selectedTool}, installing hook...`));
57
58
  } else if (process.stdout.isTTY && !isCiEnvironment()) selectedTool = await select({
@@ -87,7 +88,8 @@ async function hookInstallCommand(options = {}) {
87
88
  */
88
89
  async function detectVerdaccioNpmrc() {
89
90
  try {
90
- return (await readFile(path.resolve(".kici", ".npmrc"), "utf-8")).includes("verdaccio");
91
+ const npmrcPath = path.resolve(".kici", ".npmrc");
92
+ return (await readFile(npmrcPath, "utf-8")).includes("verdaccio");
91
93
  } catch {
92
94
  return false;
93
95
  }
@@ -40,6 +40,8 @@ export { diagnosticsCommand } from './diagnostics.js';
40
40
  export type { DiagnosticsOptions } from './diagnostics.js';
41
41
  export { doctorCommand } from './doctor.js';
42
42
  export type { DoctorOptions } from './doctor.js';
43
+ export { reportCommand, reportListCommand, reportWithdrawCommand } from './report/index.js';
44
+ export type { ReportOptions } from './report/index.js';
43
45
  export { orgListCommand, orgUseCommand, orgCurrentCommand } from './org.js';
44
46
  export { orchestratorsListCommand, orchestratorsUseCommand } from './orchestrators.js';
45
47
  export type { OrchestratorsOptions } from './orchestrators.js';
@@ -55,7 +57,9 @@ export type { WorkflowsListOptions } from './workflows.js';
55
57
  export { drainWorkerCommand } from './drain-worker.js';
56
58
  export type { DrainWorkerOptions } from './drain-worker.js';
57
59
  export { docsCommand, docsLlmCommand } from './docs.js';
60
+ export { feedbackCommand, FEEDBACK_CONTRACT } from './feedback.js';
58
61
  export type { DocsOptions, DocsLlmOptions } from './docs.js';
62
+ export type { FeedbackOptions, FeedbackContract, FeedbackField } from './feedback.js';
59
63
  export { verifyAttestationCommand } from './verify-attestation.js';
60
64
  export type { VerifyAttestationOptions } from './verify-attestation.js';
61
65
  export { notificationsChannelsListCommand, notificationsChannelsAddCommand, notificationsChannelsRemoveCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsRemoveCommand, notificationsRosterListCommand, notificationsRosterAddCommand, notificationsRosterRemoveCommand, } from './notifications.js';
@@ -6,6 +6,7 @@ import { docsCommand, docsLlmCommand } from "./docs.js";
6
6
  import { doctorCommand } from "./doctor.js";
7
7
  import { drainWorkerCommand } from "./drain-worker.js";
8
8
  import { endpointsCommand } from "./endpoints.js";
9
+ import { FEEDBACK_CONTRACT, feedbackCommand } from "./feedback.js";
9
10
  import { fixtureCommand } from "./fixture.js";
10
11
  import { hookInstallCommand } from "./hook.js";
11
12
  import { watchCommand } from "./watch.js";
@@ -24,6 +25,7 @@ import { runsCancelCommand } from "./runs/cancel.js";
24
25
  import { runsArtifactsListCommand } from "./runs/artifacts/list.js";
25
26
  import { runsArtifactsDownloadCommand } from "./runs/artifacts/download.js";
26
27
  import { typesCommand } from "./types.js";
28
+ import { reportCommand, reportListCommand, reportWithdrawCommand } from "./report/index.js";
27
29
  import { orgCurrentCommand, orgListCommand, orgUseCommand } from "./org.js";
28
30
  import { orchestratorsListCommand, orchestratorsUseCommand } from "./orchestrators.js";
29
31
  import { localAttachCommand, localDetachCommand, localDownCommand, localLogsCommand, localStatusCommand, localUpCommand } from "./local.js";
@@ -33,4 +35,4 @@ import { rejectCommand } from "./reject.js";
33
35
  import { workflowsListCommand } from "./workflows.js";
34
36
  import { verifyAttestationCommand } from "./verify-attestation.js";
35
37
  import { notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand } from "./notifications.js";
36
- export { approveCommand, compileCommand, diagnosticsCommand, docsCommand, docsLlmCommand, doctorCommand, drainWorkerCommand, endpointsCommand, fixtureCommand, hookInstallCommand, initCommand, localAttachCommand, localDetachCommand, localDownCommand, localLogsCommand, localStatusCommand, localTrustRootCommand, localUpCommand, loginCommand, logoutCommand, notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand, orchestratorsListCommand, orchestratorsUseCommand, orgCurrentCommand, orgListCommand, orgUseCommand, patCreateCommand, previewCommand, previewEvent, rejectCommand, runRemoteCommand, runRoutedCommand, runsArtifactsDownloadCommand, runsArtifactsListCommand, runsCancelCommand, runsListCommand, runsLogsCommand, runsRerunCommand, runsShowCommand, secretsListCommand, typesCommand, verifyAttestationCommand, watchCommand, workflowsListCommand };
38
+ export { FEEDBACK_CONTRACT, approveCommand, compileCommand, diagnosticsCommand, docsCommand, docsLlmCommand, doctorCommand, drainWorkerCommand, endpointsCommand, feedbackCommand, fixtureCommand, hookInstallCommand, initCommand, localAttachCommand, localDetachCommand, localDownCommand, localLogsCommand, localStatusCommand, localTrustRootCommand, localUpCommand, loginCommand, logoutCommand, notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand, orchestratorsListCommand, orchestratorsUseCommand, orgCurrentCommand, orgListCommand, orgUseCommand, patCreateCommand, previewCommand, previewEvent, rejectCommand, reportCommand, reportListCommand, reportWithdrawCommand, runRemoteCommand, runRoutedCommand, runsArtifactsDownloadCommand, runsArtifactsListCommand, runsCancelCommand, runsListCommand, runsLogsCommand, runsRerunCommand, runsShowCommand, secretsListCommand, typesCommand, verifyAttestationCommand, watchCommand, workflowsListCommand };
@@ -19,8 +19,6 @@ export interface InitOptions {
19
19
  packageManager?: string;
20
20
  /** JavaScript-only mode (no TypeScript, no dependencies) */
21
21
  mjs?: boolean;
22
- /** Write .npmrc pointing @kici-dev scope to local Verdaccio */
23
- useVerdaccioLocal?: boolean;
24
22
  /** Private registry URL to scaffold a `registries:` entry for. */
25
23
  privateRegistry?: string;
26
24
  /** Optional npm scope (e.g. `@my-org`) the private registry serves. */
@@ -41,4 +39,13 @@ export interface InitOptions {
41
39
  * @returns true on success, false on error
42
40
  */
43
41
  export declare function initCommand(options?: InitOptions): Promise<boolean>;
42
+ /**
43
+ * Scaffold `.kici/.gitignore` so the generated `types/` declarations stay
44
+ * untracked. Never overwrites an existing file — the customer may have edited
45
+ * it. Deliberately ignores only `types/`, NOT `kici.lock.json` (the
46
+ * orchestrator fetches the lock from the repo, so it genuinely is source).
47
+ *
48
+ * @param kiciDir - The resolved `.kici` directory path.
49
+ */
50
+ export declare function writeKiciGitignore(kiciDir: string): Promise<void>;
44
51
  //# sourceMappingURL=init.d.ts.map