cclaw-cli 3.0.0 → 5.0.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.
Files changed (41) hide show
  1. package/dist/artifact-linter/brainstorm.js +51 -2
  2. package/dist/artifact-linter/design.js +14 -3
  3. package/dist/artifact-linter/review-army.d.ts +25 -0
  4. package/dist/artifact-linter/review-army.js +155 -0
  5. package/dist/artifact-linter/review.js +13 -0
  6. package/dist/artifact-linter/scope.js +27 -48
  7. package/dist/artifact-linter/shared.d.ts +98 -11
  8. package/dist/artifact-linter/shared.js +280 -113
  9. package/dist/artifact-linter.d.ts +12 -2
  10. package/dist/artifact-linter.js +29 -13
  11. package/dist/content/core-agents.js +6 -1
  12. package/dist/content/examples.js +8 -0
  13. package/dist/content/hooks.js +2 -1
  14. package/dist/content/idea.js +14 -2
  15. package/dist/content/review-prompts.js +3 -3
  16. package/dist/content/skills-elicitation.js +61 -20
  17. package/dist/content/skills.js +19 -6
  18. package/dist/content/stage-schema.js +46 -18
  19. package/dist/content/stages/_lint-metadata/index.js +1 -2
  20. package/dist/content/stages/brainstorm.js +6 -3
  21. package/dist/content/stages/design.js +13 -12
  22. package/dist/content/stages/plan.js +1 -1
  23. package/dist/content/stages/review.js +21 -21
  24. package/dist/content/stages/schema-types.d.ts +9 -0
  25. package/dist/content/stages/scope.js +22 -20
  26. package/dist/content/stages/spec.js +3 -3
  27. package/dist/content/stages/tdd.js +1 -0
  28. package/dist/content/templates.d.ts +8 -1
  29. package/dist/content/templates.js +115 -43
  30. package/dist/flow-state.d.ts +12 -0
  31. package/dist/gate-evidence.d.ts +37 -1
  32. package/dist/gate-evidence.js +37 -3
  33. package/dist/harness-adapters.js +8 -0
  34. package/dist/install.js +22 -11
  35. package/dist/internal/advance-stage/advance.d.ts +1 -0
  36. package/dist/internal/advance-stage/advance.js +5 -2
  37. package/dist/internal/advance-stage/parsers.d.ts +8 -0
  38. package/dist/internal/advance-stage/parsers.js +27 -1
  39. package/dist/internal/advance-stage/start-flow.js +13 -0
  40. package/dist/run-persistence.js +14 -2
  41. package/package.json +1 -1
@@ -93,6 +93,18 @@ export interface StageInteractionHint {
93
93
  skipQuestions?: boolean;
94
94
  sourceStage?: FlowStage;
95
95
  recordedAt?: string;
96
+ /**
97
+ * Wave 23 (v5.0.0) — `/cc-ideate` handoff carry-forward.
98
+ * When a brainstorm run is started from a `/cc-ideate` recommendation,
99
+ * `start-flow` records the originating idea artifact so brainstorm can
100
+ * reuse the divergent + critique + rank work instead of re-generating it.
101
+ *
102
+ * `fromIdeaArtifact` is a workspace-relative POSIX path to
103
+ * `.cclaw/ideas/idea-YYYY-MM-DD-<slug>.md` (or wherever `/cc-ideate`
104
+ * wrote its artifact). `fromIdeaCandidateId` is the chosen `I-#` row.
105
+ */
106
+ fromIdeaArtifact?: string;
107
+ fromIdeaCandidateId?: string;
96
108
  }
97
109
  export interface InitialFlowStateOptions {
98
110
  activeRunId?: string;
@@ -1,5 +1,35 @@
1
1
  import type { FlowState, StageGateState } from "./flow-state.js";
2
2
  import { type FlowStage } from "./types.js";
3
+ /**
4
+ * Structured signal for the harness UI describing the adaptive
5
+ * elicitation Q&A floor for the current stage. Always present on
6
+ * brainstorm/scope/design verifications; null on other stages.
7
+ *
8
+ * Mirrors the `evaluateQaLogFloor` linter helper. Harness can render
9
+ * `count / min` progress, surface stop-signal/skip-questions hints, and
10
+ * differentiate between blocking and advisory.
11
+ */
12
+ export interface QaLogFloorSignal {
13
+ ok: boolean;
14
+ count: number;
15
+ /**
16
+ * Wave 23 (v5.0.0): always 0. The convergence floor no longer enforces
17
+ * a fixed count. Harness UIs may render `questionBudgetHint(track,
18
+ * stage).recommended` separately as a soft hint.
19
+ */
20
+ min: number;
21
+ hasStopSignal: boolean;
22
+ /** Wave 23: always false. See `min` note above. */
23
+ liteShortCircuit: boolean;
24
+ skipQuestionsAdvisory: boolean;
25
+ blocking: boolean;
26
+ /** Forcing-question topics deemed addressed in `## Q&A Log`. */
27
+ forcingCovered: string[];
28
+ /** Forcing-question topics still pending (no Q&A row matched). */
29
+ forcingPending: string[];
30
+ /** Ralph-Loop convergence detector verdict for the last 2 rows. */
31
+ noNewDecisions: boolean;
32
+ }
3
33
  export interface GateEvidenceCheckResult {
4
34
  ok: boolean;
5
35
  stage: FlowStage;
@@ -18,6 +48,8 @@ export interface GateEvidenceCheckResult {
18
48
  missingRecommended: string[];
19
49
  /** Triggered conditional gates that are not yet passed. */
20
50
  missingTriggeredConditional: string[];
51
+ /** Q&A floor signal for adaptive elicitation stages, null otherwise. */
52
+ qaLogFloor: QaLogFloorSignal | null;
21
53
  }
22
54
  export interface CompletedStagesClosureResult {
23
55
  ok: boolean;
@@ -29,7 +61,11 @@ export interface CompletedStagesClosureResult {
29
61
  blocked: string[];
30
62
  }>;
31
63
  }
32
- export declare function verifyCurrentStageGateEvidence(projectRoot: string, flowState: FlowState): Promise<GateEvidenceCheckResult>;
64
+ export interface VerifyCurrentStageGateEvidenceOptions {
65
+ /** Extra stage flags propagated from the in-flight CLI args (e.g. `--skip-questions`). */
66
+ extraStageFlags?: string[];
67
+ }
68
+ export declare function verifyCurrentStageGateEvidence(projectRoot: string, flowState: FlowState, options?: VerifyCurrentStageGateEvidenceOptions): Promise<GateEvidenceCheckResult>;
33
69
  export declare function verifyCompletedStagesGateClosure(flowState: FlowState): CompletedStagesClosureResult;
34
70
  export interface GateReconciliationResult {
35
71
  stage: FlowStage;
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { checkReviewSecurityNoChangeAttestation, checkReviewVerdictConsistency, extractMarkdownSectionBody, lintArtifact, validateReviewArmy } from "./artifact-linter.js";
4
+ import { ELICITATION_STAGES, evaluateQaLogFloor } from "./artifact-linter/shared.js";
4
5
  import { resolveArtifactPath } from "./artifact-paths.js";
5
6
  import { RUNTIME_ROOT } from "./constants.js";
6
7
  import { stageSchema } from "./content/stage-schema.js";
@@ -207,7 +208,7 @@ const DESIGN_RESEARCH_REQUIRED_SECTIONS = [
207
208
  "Pitfalls & Risks",
208
209
  "Synthesis"
209
210
  ];
210
- export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
211
+ export async function verifyCurrentStageGateEvidence(projectRoot, flowState, options = {}) {
211
212
  const stage = flowState.currentStage;
212
213
  const schema = stageSchema(stage, flowState.track);
213
214
  const catalog = flowState.stageGateCatalog[stage];
@@ -283,7 +284,9 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
283
284
  const artifactPresent = await currentStageArtifactExists(projectRoot, stage, flowState.track);
284
285
  const shouldValidateArtifact = artifactPresent || catalog.passed.length > 0 || flowState.completedStages.includes(stage);
285
286
  if (shouldValidateArtifact) {
286
- const lint = await lintArtifact(projectRoot, stage, flowState.track);
287
+ const lint = await lintArtifact(projectRoot, stage, flowState.track, {
288
+ extraStageFlags: options.extraStageFlags
289
+ });
287
290
  if (!lint.passed) {
288
291
  const failedRequiredFindings = lint.findings
289
292
  .filter((finding) => finding.required && !finding.found);
@@ -444,6 +447,36 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
444
447
  issues.push(`stage "${stage}" is marked completed but has blocking blocked gates: ${blockingBlocked.join(", ")}.`);
445
448
  }
446
449
  }
450
+ let qaLogFloor = null;
451
+ if (ELICITATION_STAGES.has(stage)) {
452
+ let qaLogBody = null;
453
+ try {
454
+ const stageMd = await readStageArtifactMarkdown(projectRoot, stage, flowState.track);
455
+ qaLogBody = stageMd
456
+ ? extractMarkdownSectionBody(stageMd, "Q&A Log")
457
+ : null;
458
+ }
459
+ catch {
460
+ qaLogBody = null;
461
+ }
462
+ const skipQuestionsHint = flowState.interactionHints?.[stage]?.skipQuestions === true ||
463
+ (options.extraStageFlags ?? []).includes("--skip-questions");
464
+ const floor = evaluateQaLogFloor(qaLogBody, flowState.track, stage, {
465
+ skipQuestions: skipQuestionsHint
466
+ });
467
+ qaLogFloor = {
468
+ ok: floor.ok,
469
+ count: floor.count,
470
+ min: floor.min,
471
+ hasStopSignal: floor.hasStopSignal,
472
+ liteShortCircuit: floor.liteShortCircuit,
473
+ skipQuestionsAdvisory: floor.skipQuestionsAdvisory,
474
+ blocking: !floor.ok && !floor.skipQuestionsAdvisory,
475
+ forcingCovered: floor.forcingCovered,
476
+ forcingPending: floor.forcingPending,
477
+ noNewDecisions: floor.noNewDecisions
478
+ };
479
+ }
447
480
  return {
448
481
  ok: issues.length === 0,
449
482
  stage,
@@ -457,7 +490,8 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
457
490
  complete,
458
491
  missingRequired,
459
492
  missingRecommended,
460
- missingTriggeredConditional
493
+ missingTriggeredConditional,
494
+ qaLogFloor
461
495
  };
462
496
  }
463
497
  export function verifyCompletedStagesGateClosure(flowState) {
@@ -345,6 +345,14 @@ Before responding to a coding request:
345
345
  2. Use \`/cc\` to start, resume, or continue the flow.
346
346
  3. If no stage applies, respond normally.
347
347
 
348
+ ### Cclaw Baseline (always-on)
349
+
350
+ Three rules apply to every cclaw stage in this project, regardless of which skills loaded:
351
+
352
+ 1. **Q&A convergence before drafting** — for brainstorm / scope / design, walk the stage forcing questions one at a time via the harness-native question tool (Claude \`AskUserQuestion\`, Cursor \`AskQuestion\`, Codex \`request_user_input\`, Gemini \`ask_user\`). The \`qa_log_unconverged\` linter rule will block \`stage-complete\` when convergence has not been reached. Convergence is satisfied when ANY of: (a) all forcing-question topics are addressed in \`## Q&A Log\`, (b) the last 2 substantive rows produce no decision-changing impact (Ralph-Loop), or (c) an explicit user stop-signal row is recorded. The fixed count floor (10 for standard) was removed in Wave 23.
353
+ 2. **Subagents run after Q&A approval** — mandatory subagents in brainstorm / scope / design (\`product-discovery\`, \`critic\`, \`planner\`, \`architect\`, \`test-author\`) run only AFTER the user approves the elicitation outcome. See each stage's "Run Phase: post-elicitation" rows in the materialized Automatic Subagent Dispatch table.
354
+ 3. **No command-line echo to chat** — the user does not run cclaw helpers manually. Never paste \`node .cclaw/hooks/...\` invocations, \`--evidence-json '{...}'\` payloads, or shell hash commands (\`shasum\`, \`sha256sum\`, \`Get-FileHash\`, \`certutil\`, etc.) into chat. Run helpers via the tool layer; report only the resulting summary.
355
+
348
356
  ${ironLawsAgentsMdBlock()}
349
357
 
350
358
  ### Task Classification (before \`/cc\`)
package/dist/install.js CHANGED
@@ -16,7 +16,7 @@ import { ironLawsSkillMarkdown } from "./content/iron-laws.js";
16
16
  import { stageCompleteScript, startFlowScript, cancelRunScript, runHookCmdScript, delegationRecordScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
17
17
  import { nodeHookRuntimeScript } from "./content/node-hooks.js";
18
18
  import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
19
- import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
19
+ import { ARTIFACT_TEMPLATES, CURSOR_GUIDELINES_RULE_MDC, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
20
20
  import { STATE_CONTRACTS } from "./content/state-contracts.js";
21
21
  import { REVIEW_PROMPTS } from "./content/review-prompts.js";
22
22
  import { stageSkillFolder, stageSkillMarkdown, executingWavesSkillMarkdown } from "./content/skills.js";
@@ -37,6 +37,7 @@ import { CorruptFlowStateError, ensureRunSystem } from "./runs.js";
37
37
  import { FLOW_STAGES } from "./types.js";
38
38
  const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
39
39
  const CURSOR_RULE_REL_PATH = ".cursor/rules/cclaw-workflow.mdc";
40
+ const CURSOR_GUIDELINES_REL_PATH = ".cursor/rules/cclaw-guidelines.mdc";
40
41
  const GIT_HOOK_MANAGED_MARKER = "cclaw-managed-git-hook";
41
42
  const GIT_HOOK_RUNTIME_REL_DIR = `${RUNTIME_ROOT}/hooks/git`;
42
43
  const INIT_SENTINEL_FILE = ".init-in-progress";
@@ -942,17 +943,22 @@ async function writeRulebook(projectRoot) {
942
943
  }
943
944
  async function writeCursorWorkflowRule(projectRoot, harnesses) {
944
945
  const rulePath = path.join(projectRoot, CURSOR_RULE_REL_PATH);
946
+ const guidelinesPath = path.join(projectRoot, CURSOR_GUIDELINES_REL_PATH);
945
947
  if (!harnesses.includes("cursor")) {
946
- try {
947
- await fs.rm(rulePath, { force: true });
948
- }
949
- catch {
950
- // best-effort cleanup
948
+ for (const target of [rulePath, guidelinesPath]) {
949
+ try {
950
+ await fs.rm(target, { force: true });
951
+ }
952
+ catch {
953
+ // best-effort cleanup
954
+ }
951
955
  }
952
956
  return;
953
957
  }
954
958
  await ensureDir(path.dirname(rulePath));
955
959
  await writeFileSafe(rulePath, CURSOR_WORKFLOW_RULE_MDC);
960
+ await ensureDir(path.dirname(guidelinesPath));
961
+ await writeFileSafe(guidelinesPath, CURSOR_GUIDELINES_RULE_MDC);
956
962
  }
957
963
  async function syncDisabledHarnessArtifacts(projectRoot, harnesses) {
958
964
  const enabled = new Set(harnesses);
@@ -1464,11 +1470,16 @@ export async function uninstallCclaw(projectRoot) {
1464
1470
  }
1465
1471
  }
1466
1472
  await removeManagedOpenCodePluginConfig(projectRoot, OPENCODE_PLUGIN_REL_PATH);
1467
- try {
1468
- await fs.rm(path.join(projectRoot, CURSOR_RULE_REL_PATH), { force: true });
1469
- }
1470
- catch {
1471
- // best-effort cleanup
1473
+ for (const target of [
1474
+ path.join(projectRoot, CURSOR_RULE_REL_PATH),
1475
+ path.join(projectRoot, CURSOR_GUIDELINES_REL_PATH)
1476
+ ]) {
1477
+ try {
1478
+ await fs.rm(target, { force: true });
1479
+ }
1480
+ catch {
1481
+ // best-effort cleanup
1482
+ }
1472
1483
  }
1473
1484
  const managedDirs = [
1474
1485
  ".claude/hooks",
@@ -35,6 +35,7 @@ interface InternalValidationReport {
35
35
  export declare function hydrateReviewLoopEvidenceFromArtifact(projectRoot: string, stage: FlowStage, track: FlowState["track"], selectedGateIds: string[], evidenceByGate: Record<string, string>): Promise<void>;
36
36
  export declare function buildValidationReport(projectRoot: string, flowState: FlowState, options?: {
37
37
  allowBlockedReviewRoute?: boolean;
38
+ extraStageFlags?: string[];
38
39
  }): Promise<InternalValidationReport>;
39
40
  interface HarvestLearningsResult {
40
41
  ok: boolean;
@@ -89,7 +89,9 @@ export async function hydrateReviewLoopEvidenceFromArtifact(projectRoot, stage,
89
89
  }
90
90
  export async function buildValidationReport(projectRoot, flowState, options = {}) {
91
91
  const delegation = await checkMandatoryDelegations(projectRoot, flowState.currentStage);
92
- const gates = await verifyCurrentStageGateEvidence(projectRoot, flowState);
92
+ const gates = await verifyCurrentStageGateEvidence(projectRoot, flowState, {
93
+ extraStageFlags: options.extraStageFlags
94
+ });
93
95
  const completedStages = verifyCompletedStagesGateClosure(flowState);
94
96
  const blockedReviewRouteComplete = options.allowBlockedReviewRoute === true
95
97
  && flowState.currentStage === "review"
@@ -332,7 +334,8 @@ export async function runAdvanceStage(projectRoot, args, io) {
332
334
  }
333
335
  };
334
336
  const validation = await buildValidationReport(projectRoot, candidateState, {
335
- allowBlockedReviewRoute: blockedReviewRoute
337
+ allowBlockedReviewRoute: blockedReviewRoute,
338
+ extraStageFlags: args.skipQuestions ? ["--skip-questions"] : undefined
336
339
  });
337
340
  if (!validation.ok) {
338
341
  const ledgerForDiag = await readDelegationLedger(projectRoot).catch(() => ({ entries: [] }));
@@ -39,6 +39,14 @@ export interface StartFlowArgs {
39
39
  forceReset: boolean;
40
40
  reclassify: boolean;
41
41
  quiet: boolean;
42
+ /**
43
+ * Wave 23 (v5.0.0) — `/cc-ideate` handoff carry-forward.
44
+ * Workspace-relative POSIX path to `.cclaw/ideas/idea-YYYY-MM-DD-<slug>.md`
45
+ * (or wherever `/cc-ideate` wrote its artifact).
46
+ */
47
+ fromIdeaArtifact?: string;
48
+ /** Optional `I-#` row id chosen from the idea artifact's ranked list. */
49
+ fromIdeaCandidateId?: string;
42
50
  }
43
51
  export interface CancelRunArgs {
44
52
  reason: string;
@@ -211,6 +211,8 @@ export function parseStartFlowArgs(tokens) {
211
211
  let forceReset = false;
212
212
  let reclassify = false;
213
213
  let quiet = false;
214
+ let fromIdeaArtifact;
215
+ let fromIdeaCandidateId;
214
216
  for (let i = 0; i < tokens.length; i += 1) {
215
217
  const token = tokens[i];
216
218
  const nextToken = tokens[i + 1];
@@ -259,12 +261,36 @@ export function parseStartFlowArgs(tokens) {
259
261
  stack = readValue("--stack").trim();
260
262
  continue;
261
263
  }
264
+ if (token === "--from-idea-artifact" || token.startsWith("--from-idea-artifact=")) {
265
+ const raw = readValue("--from-idea-artifact").trim();
266
+ fromIdeaArtifact = raw.length > 0 ? raw : undefined;
267
+ continue;
268
+ }
269
+ if (token === "--from-idea-candidate" || token.startsWith("--from-idea-candidate=")) {
270
+ const raw = readValue("--from-idea-candidate").trim();
271
+ fromIdeaCandidateId = raw.length > 0 ? raw : undefined;
272
+ continue;
273
+ }
262
274
  throw new Error(`Unknown flag for internal start-flow: ${token}`);
263
275
  }
264
276
  if (!track) {
265
277
  throw new Error("internal start-flow requires --track=<standard|medium|quick>.");
266
278
  }
267
- return { track, className, prompt, reason, stack, forceReset, reclassify, quiet };
279
+ if (fromIdeaCandidateId && !fromIdeaArtifact) {
280
+ throw new Error("--from-idea-candidate requires --from-idea-artifact=<path> to be set as well.");
281
+ }
282
+ return {
283
+ track,
284
+ className,
285
+ prompt,
286
+ reason,
287
+ stack,
288
+ forceReset,
289
+ reclassify,
290
+ quiet,
291
+ fromIdeaArtifact,
292
+ fromIdeaCandidateId
293
+ };
268
294
  }
269
295
  export function parseCancelRunArgs(tokens) {
270
296
  let reason;
@@ -119,6 +119,19 @@ export async function runStartFlow(projectRoot, args, io) {
119
119
  else {
120
120
  nextState = createInitialFlowState({ track: args.track });
121
121
  }
122
+ if (args.fromIdeaArtifact) {
123
+ const existingHints = nextState.interactionHints ?? {};
124
+ const existingBrainstorm = existingHints.brainstorm ?? {};
125
+ nextState.interactionHints = {
126
+ ...existingHints,
127
+ brainstorm: {
128
+ ...existingBrainstorm,
129
+ fromIdeaArtifact: args.fromIdeaArtifact,
130
+ ...(args.fromIdeaCandidateId ? { fromIdeaCandidateId: args.fromIdeaCandidateId } : {}),
131
+ recordedAt: new Date().toISOString()
132
+ }
133
+ };
134
+ }
122
135
  await writeFlowState(projectRoot, nextState, { allowReset: true });
123
136
  await appendIdeaArtifact(projectRoot, args, current);
124
137
  if (!args.quiet) {
@@ -246,13 +246,25 @@ function sanitizeInteractionHints(value) {
246
246
  const skipQuestions = typed.skipQuestions === true ? true : undefined;
247
247
  const sourceStage = isFlowStage(typed.sourceStage) ? typed.sourceStage : undefined;
248
248
  const recordedAt = typeof typed.recordedAt === "string" ? typed.recordedAt : undefined;
249
- if (skipQuestions !== true && !sourceStage && !recordedAt) {
249
+ const fromIdeaArtifact = typeof typed.fromIdeaArtifact === "string" && typed.fromIdeaArtifact.trim().length > 0
250
+ ? typed.fromIdeaArtifact.trim()
251
+ : undefined;
252
+ const fromIdeaCandidateId = typeof typed.fromIdeaCandidateId === "string" && typed.fromIdeaCandidateId.trim().length > 0
253
+ ? typed.fromIdeaCandidateId.trim()
254
+ : undefined;
255
+ if (skipQuestions !== true &&
256
+ !sourceStage &&
257
+ !recordedAt &&
258
+ !fromIdeaArtifact &&
259
+ !fromIdeaCandidateId) {
250
260
  continue;
251
261
  }
252
262
  out[stage] = {
253
263
  ...(skipQuestions ? { skipQuestions } : {}),
254
264
  ...(sourceStage ? { sourceStage } : {}),
255
- ...(recordedAt ? { recordedAt } : {})
265
+ ...(recordedAt ? { recordedAt } : {}),
266
+ ...(fromIdeaArtifact ? { fromIdeaArtifact } : {}),
267
+ ...(fromIdeaCandidateId ? { fromIdeaCandidateId } : {})
256
268
  };
257
269
  }
258
270
  return out;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "3.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {