halfcycle 0.3.26 → 0.3.27

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "halfcycle",
3
- "version": "0.3.26",
3
+ "version": "0.3.27",
4
4
  "description": "Halfcycle Method bundle — resolution-stub slash commands, remote method-delivery registration, and governance-hook wiring for a Halfcycle engagement repo. It ships NO worker role files: a project's roles are authored from that project's own recorded decisions at orchestration kickoff (FX-2, W3-F-27).",
5
5
  "commands": [
6
6
  {
package/README.md CHANGED
@@ -44,15 +44,13 @@ Credit where it is due, and it is due. GStack, Matt Pocock's skills, GitHub Spec
44
44
 
45
45
  The objection this usually meets is that it must be too much for a small project. It would be, if there were one setting. A folder you download has exactly one, so whoever wrote it had to choose who to disappoint, and it is usually the small project that gets buried and quietly drops the process in week three. This reads the work instead and sizes itself to it, which is the section further down.
46
46
 
47
- Writing the process down was never the hard part. What a folder you download structurally cannot have is these four things.
47
+ Writing the process down was never the hard part. What a folder you download structurally cannot have is these three things.
48
48
 
49
49
  **1. Something other than you checks the work.** A document cannot audit itself, and the session that wrote the draft is the worst available reviewer of it. So Halfcycle's consistency check runs in a session that has never seen your draft, and the method makes that non-negotiable rather than advisory: after every round of fixes it runs again in another new session, never the one that asked for those fixes, because the reviewer that asked for a fix is the one who cannot see what the fix stranded. The guard checks are the harder edge — they run off your machine, on ours, on every real change, and one that fires either warns you or stops the work outright, depending on how serious the rule it broke is.
50
50
 
51
- **2. Evidence that a piece of work actually finished.** Each phase closes with a Build Record: what was decided, what fired, what was accepted, assembled from your repo's own artefacts. That is a document an auditor, a board or a client can read. "The agent said it was done" is not.
51
+ **2. The same gates on every contributor.** The requirement lives in the repository, so everybody inherits identical checks with nothing to set up, and a new hire is compliant in one command. No framework is single-player by accident; they are single-player by construction, and several people changing the same codebase faster than they can synchronise is the one problem a single-session rulebook cannot see.
52
52
 
53
- **3. The same gates on every contributor.** The requirement lives in the repository, so everybody inherits identical checks with nothing to set up, and a new hire is compliant in one command. No framework is single-player by accident; they are single-player by construction, and several people changing the same codebase faster than they can synchronise is the one problem a single-session rulebook cannot see.
54
-
55
- **4. A record of how builds break that keeps growing.** This is the part that compounds, and it is why a fork is a snapshot that starts depreciating the day you take it. See below.
53
+ **3. A record of how builds break that keeps growing.** This is the part that compounds, and it is why a fork is a snapshot that starts depreciating the day you take it. See below.
56
54
 
57
55
  ---
58
56
 
@@ -158,7 +156,7 @@ This matters enough to be specific about.
158
156
 
159
157
  ## What you keep
160
158
 
161
- Everything the method produces is yours, under your own `docs/`, in your git history, in plain markdown: the product spec, the architecture, the phase plans, the feature specs, the ordered work and the Build Records. It reads as documentation your team wrote, because it is.
159
+ Everything the method produces is yours, under your own `docs/`, in your git history, in plain markdown: the product spec, the architecture, the phase plans, the feature specs and the ordered work. It reads as documentation your team wrote, because it is.
162
160
 
163
161
  Nothing you already had is changed. Existing settings are preserved, and a name collision is reported rather than resolved on your behalf.
164
162
 
@@ -14549,6 +14549,11 @@ var HALFCYCLE_DIR_NAME = ".halfcycle";
14549
14549
  var ENGAGEMENTS_DIR_NAME = "engagements";
14550
14550
  var ENGAGEMENT_ENV_FILENAME = "env";
14551
14551
  var ACCOUNT_STORE_FILENAME = "account.json";
14552
+ var ENGAGEMENT_ID_PATTERN = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
14553
+ var ENGAGEMENT_ID_SHAPE = new RegExp(ENGAGEMENT_ID_PATTERN);
14554
+ function isEngagementId(value) {
14555
+ return typeof value === "string" && ENGAGEMENT_ID_SHAPE.test(value);
14556
+ }
14552
14557
  var ENGAGEMENT_ENV_HEADER = "# Halfcycle per-engagement credential \u2014 machine level, owner-only, never in a repository.";
14553
14558
  function shq(value) {
14554
14559
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -14684,6 +14689,9 @@ function readConfig() {
14684
14689
  function readNeverConfiguredReason() {
14685
14690
  return process.env["HALFCYCLE_NOT_THIS_ACCOUNT"] === "1" ? "not-this-account" : "no-credential-yet";
14686
14691
  }
14692
+ function isHalfcycleOwnCi() {
14693
+ return process.env["HALFCYCLE_INTERNAL_CI"] === "1";
14694
+ }
14687
14695
  function readGuardOrigin() {
14688
14696
  return resolveGuardOrigin(process.env);
14689
14697
  }
@@ -14813,7 +14821,15 @@ var evaluationRequestSchema = external_exports.object({
14813
14821
  * Omitting the field means "unknown", never "everything" — a client that
14814
14822
  * says nothing is treated as an older one, not a capable one.
14815
14823
  */
14816
- clientCapabilities: external_exports.array(external_exports.string()).optional()
14824
+ clientCapabilities: external_exports.array(external_exports.string()).optional(),
14825
+ /**
14826
+ * Whether this repository has any deploy file — a Dockerfile, a fly.toml or
14827
+ * a .env.example — anywhere in its tree. A check that compares code against
14828
+ * those files reports itself as not run when this is false.
14829
+ *
14830
+ * Omitting the field means "unknown": every check runs as it always has.
14831
+ */
14832
+ repoHasDeployFiles: external_exports.boolean().optional()
14817
14833
  }).strict();
14818
14834
 
14819
14835
  // ../events/dist/result.js
@@ -15143,9 +15159,10 @@ var HOOK_REMEDY = `Fix it by running "npx halfcycle" in this repository: it may
15143
15159
  var PERMISSIONS_BLOCK = "\n permissions:\n contents: read\n id-token: write\n\n";
15144
15160
  var BOTH_LINES_SENTENCE = "BOTH LINES. Declaring any permission replaces the defaults rather than adding to them, so a block naming only the identity token takes read access away from the checkout step and a private repository stops checking out before this check is reached.";
15145
15161
  var CI_CANNOT_PROVE_PROJECT = "has no way to prove which project it belongs to";
15146
- var CI_REMEDY_LEAD = "Add this to the workflow \u2014 at the top of the file, or on this job:\n" + PERMISSIONS_BLOCK + `${BOTH_LINES_SENTENCE} Then, once, on your own machine in this project, if you have not already: \`npx halfcycle ci bind <owner>/<repo>\`. That is the whole configuration \u2014 there is no secret to store.`;
15162
+ var CI_REMEDY_LEAD = "Add this to the workflow \u2014 at the top of the file, or on this job:\n" + PERMISSIONS_BLOCK + `${BOTH_LINES_SENTENCE} Binding a repository so a run means something is internal to Halfcycle \u2014 contact hello@halfcycle.ai to have this one bound. There is no secret to store.`;
15147
15163
  var CI_OLDER_SETUP = "The older setup, GUARD_SERVICE_TOKEN and GUARD_ENGAGEMENT_ID supplied as repository secrets, still works unchanged, and a job that has both needs neither line.";
15148
15164
  var CI_REMEDY = `This job cannot vouch for anything, because it ${CI_CANNOT_PROVE_PROJECT}. ${CI_REMEDY_LEAD} ${CI_OLDER_SETUP}`;
15165
+ var CI_NOT_SUPPORTED_MESSAGE = "[Halfcycle CI] This is Halfcycle-internal and is not supported in your CI.\n";
15149
15166
  var NEVER_CONFIGURED_HEADLINE = "GUARD NEVER CONFIGURED";
15150
15167
  var NOT_A_SERVICE_OUTAGE = "This is not a service outage.";
15151
15168
  function neverConfiguredMessage(missing, remedy, label = "[Halfcycle]") {
@@ -15624,8 +15641,8 @@ function screenExcerpts(excerpts) {
15624
15641
  }
15625
15642
 
15626
15643
  // dist/extract/env-refs.js
15627
- function extractEnvRefs(diffContent, context = {}) {
15628
- const names = detectEnvReads(diffContent);
15644
+ function extractEnvRefs(diffContent, context = {}, filePath) {
15645
+ const names = detectEnvReads(diffContent, filePath !== void 0 && usesJsComments(filePath));
15629
15646
  if (names.size === 0)
15630
15647
  return [];
15631
15648
  const result = [];
@@ -15634,19 +15651,31 @@ function extractEnvRefs(diffContent, context = {}) {
15634
15651
  }
15635
15652
  return result;
15636
15653
  }
15637
- function detectEnvReads(diffContent) {
15654
+ var JS_COMMENT_EXTENSIONS = /\.(?:[cm]?[jt]sx?|vue|svelte|astro)$/i;
15655
+ function usesJsComments(filePath) {
15656
+ return JS_COMMENT_EXTENSIONS.test(filePath);
15657
+ }
15658
+ function detectEnvReads(diffContent, jsComments) {
15638
15659
  const found = /* @__PURE__ */ new Set();
15639
15660
  const dotPattern = /(?:process\.env|import\.meta\.env)\.([A-Z_][A-Z0-9_]*)/g;
15640
15661
  const bracketPattern = /(?:process\.env|import\.meta\.env)\s*\[\s*['"]([A-Z_][A-Z0-9_]*)['"]\s*\]/g;
15641
- const destructurePattern = /\{([^}]*)\}\s*=\s*(?:process\.env|import\.meta\.env)\b/g;
15642
- for (const line of diffContent.split("\n")) {
15643
- if (line.startsWith("-") && !line.startsWith("---"))
15662
+ const destructurePattern = /\{([^{}]*)\}\s*=\s*(?:process\.env|import\.meta\.env)\b/g;
15663
+ for (const raw of diffContent.split("\n")) {
15664
+ if (raw.startsWith("-") && !raw.startsWith("---"))
15644
15665
  continue;
15666
+ const isSource = raw.startsWith("+") && !raw.startsWith("+++") || raw.startsWith(" ");
15667
+ const line = jsComments && isSource ? raw.slice(1) : raw;
15668
+ const comment = jsComments && isSource ? commentedOut(line) : void 0;
15669
+ const live = (match) => comment === void 0 || !comment(match.index, match.index + match[0].length);
15645
15670
  for (const match of line.matchAll(dotPattern))
15646
- found.add(match[1]);
15671
+ if (live(match))
15672
+ found.add(match[1]);
15647
15673
  for (const match of line.matchAll(bracketPattern))
15648
- found.add(match[1]);
15674
+ if (live(match))
15675
+ found.add(match[1]);
15649
15676
  for (const destructure of line.matchAll(destructurePattern)) {
15677
+ if (!live(destructure))
15678
+ continue;
15650
15679
  for (const part of destructure[1].split(",")) {
15651
15680
  const nameMatch = part.trim().match(/^([A-Z_][A-Z0-9_]*)\s*(?:[:=]|$)/);
15652
15681
  if (nameMatch)
@@ -15656,6 +15685,28 @@ function detectEnvReads(diffContent) {
15656
15685
  }
15657
15686
  return found;
15658
15687
  }
15688
+ var LINE_TERMINATOR = /[\r\u2028\u2029]/g;
15689
+ function commentedOut(line) {
15690
+ const lead = line.length - line.trimStart().length;
15691
+ let body;
15692
+ let close;
15693
+ let expression;
15694
+ if (line.startsWith("//", lead)) {
15695
+ body = lead + 2;
15696
+ LINE_TERMINATOR.lastIndex = body;
15697
+ close = LINE_TERMINATOR.exec(line)?.index ?? line.length;
15698
+ expression = "{";
15699
+ } else if (line.startsWith("/*", lead) || line.startsWith("{/*", lead)) {
15700
+ body = lead + (line[lead] === "{" ? 3 : 2);
15701
+ const found = line.indexOf("*/", body);
15702
+ close = found === -1 ? line.length : found;
15703
+ expression = line[lead] === "{" ? "${" : "{";
15704
+ } else {
15705
+ return void 0;
15706
+ }
15707
+ const opened = line.indexOf(expression, body);
15708
+ return (start, end) => start >= body && end <= close && (opened === -1 || opened >= start || opened >= close);
15709
+ }
15659
15710
  function collectDeclarationFiles(context) {
15660
15711
  const files = [];
15661
15712
  if (context.dockerfileContent) {
@@ -15667,10 +15718,16 @@ function collectDeclarationFiles(context) {
15667
15718
  if (context.envExampleContent) {
15668
15719
  files.push({ kind: "env-example", path: ".env.example", content: context.envExampleContent });
15669
15720
  }
15670
- if (context.declarationFiles)
15671
- files.push(...context.declarationFiles);
15721
+ for (const file2 of context.declarationFiles ?? [])
15722
+ files.push(file2);
15672
15723
  return files;
15673
15724
  }
15725
+ function repoHasDeployFiles(context) {
15726
+ const looked = context.declarationFiles !== void 0 || context.dockerfileContent !== void 0 || context.flyTomlContent !== void 0 || context.envExampleContent !== void 0;
15727
+ if (!looked)
15728
+ return void 0;
15729
+ return collectDeclarationFiles(context).length > 0;
15730
+ }
15674
15731
  function resolveArgType(name, context) {
15675
15732
  const files = collectDeclarationFiles(context);
15676
15733
  const build = isBuildTimeSite(name, files);
@@ -15745,10 +15802,18 @@ function envExampleHas(name, content, argType) {
15745
15802
 
15746
15803
  // dist/extract/strip-comments.js
15747
15804
  function stripComments(code) {
15748
- let out = code.replace(/\/\*[\s\S]*?\*\//g, " ");
15749
- const openBlock = out.indexOf("/*");
15750
- if (openBlock !== -1)
15751
- out = out.slice(0, openBlock);
15805
+ let out = "";
15806
+ let from = 0;
15807
+ for (; ; ) {
15808
+ const open = code.indexOf("/*", from);
15809
+ const close = open === -1 ? -1 : code.indexOf("*/", open + 2);
15810
+ if (close === -1) {
15811
+ out += code.slice(from, open === -1 ? code.length : open);
15812
+ break;
15813
+ }
15814
+ out += code.slice(from, open) + " ";
15815
+ from = close + 2;
15816
+ }
15752
15817
  const lineComment = out.match(/(^|[^:])\/\//);
15753
15818
  if (lineComment && lineComment.index !== void 0) {
15754
15819
  const cut = lineComment.index + lineComment[1].length;
@@ -16422,26 +16487,16 @@ function stripDiffPrefix4(line) {
16422
16487
 
16423
16488
  // dist/changeset.js
16424
16489
  var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".txt", ".rst", ".adoc"]);
16425
- var DOC_DIR_PATTERNS = [
16426
- "docs/",
16427
- "/docs/",
16428
- "skills/",
16429
- "/skills/",
16430
- "runbooks/",
16431
- "/runbooks/",
16432
- "method/",
16433
- "/method/"
16434
- ];
16490
+ var DOC_DIR_NAMES = /* @__PURE__ */ new Set(["docs", "skills", "runbooks", "method"]);
16435
16491
  function isCodeFile(filePath) {
16436
16492
  const lower = filePath.toLowerCase();
16437
16493
  for (const ext of DOC_EXTENSIONS) {
16438
16494
  if (lower.endsWith(ext))
16439
16495
  return false;
16440
16496
  }
16441
- for (const pat of DOC_DIR_PATTERNS) {
16442
- if (lower.includes(pat))
16443
- return false;
16444
- }
16497
+ const dirs = lower.replace(/\\/g, "/").split("/").slice(0, -1);
16498
+ if (dirs.some((dir) => DOC_DIR_NAMES.has(dir)))
16499
+ return false;
16445
16500
  return true;
16446
16501
  }
16447
16502
  var INSTALLER_OUTPUT_DIR = ".halfcycle/";
@@ -16479,7 +16534,7 @@ function buildChangeSet(files, context = {}) {
16479
16534
  };
16480
16535
  }
16481
16536
  const astShapes = toAstShapes(detectBlastRadiusMarkers(filePath, diffContent));
16482
- const envRefs = extractEnvRefs(diffContent, context);
16537
+ const envRefs = extractEnvRefs(diffContent, context, filePath);
16483
16538
  const sqlWrites = extractSqlWrites(diffContent);
16484
16539
  const thirdPartyEndpoints = extractThirdPartyEndpoints(diffContent, {
16485
16540
  repoRoot: context.repoRoot
@@ -16517,6 +16572,15 @@ function deriveClientCapabilities(changeSet) {
16517
16572
  }
16518
16573
  return [...seen];
16519
16574
  }
16575
+ function buildEvaluationRequest(engagementId, changeSet, context) {
16576
+ const hasDeployFiles = repoHasDeployFiles(context);
16577
+ return {
16578
+ engagementId,
16579
+ changeSet,
16580
+ clientCapabilities: deriveClientCapabilities(changeSet),
16581
+ ...hasDeployFiles === void 0 ? {} : { repoHasDeployFiles: hasDeployFiles }
16582
+ };
16583
+ }
16520
16584
  function produceExcerpts(diffContent, descriptors, caps) {
16521
16585
  const excerpts = [];
16522
16586
  const seen = /* @__PURE__ */ new Set();
@@ -16817,24 +16881,21 @@ async function runCi() {
16817
16881
  guardServiceUrl: readGuardOrigin().origin,
16818
16882
  guardServiceToken: acquired.credential.guardServiceToken,
16819
16883
  guardEngagementId: acquired.credential.guardEngagementId,
16820
- changeSet
16884
+ changeSet,
16885
+ context
16821
16886
  });
16822
16887
  }
16823
16888
  }
16824
16889
  if (!configResult.config) {
16825
16890
  await emitRunRecord({ runType: "ci", outcome: "unconfigured", changeSet });
16826
- process.stderr.write(ciNeverConfiguredMessage());
16891
+ process.stderr.write(isHalfcycleOwnCi() ? ciNeverConfiguredMessage() : CI_NOT_SUPPORTED_MESSAGE);
16827
16892
  return 1;
16828
16893
  }
16829
- return await evaluateAndAct({ ...configResult.config, changeSet });
16894
+ return await evaluateAndAct({ ...configResult.config, changeSet, context });
16830
16895
  }
16831
16896
  async function evaluateAndAct(args2) {
16832
- const { guardServiceUrl, guardServiceToken, guardEngagementId, changeSet } = args2;
16833
- const clientResult = await evaluate(guardServiceUrl, guardServiceToken, {
16834
- engagementId: guardEngagementId,
16835
- changeSet,
16836
- clientCapabilities: deriveClientCapabilities(changeSet)
16837
- });
16897
+ const { guardServiceUrl, guardServiceToken, guardEngagementId, changeSet, context } = args2;
16898
+ const clientResult = await evaluate(guardServiceUrl, guardServiceToken, buildEvaluationRequest(guardEngagementId, changeSet, context));
16838
16899
  if (!clientResult.ok) {
16839
16900
  const failure = classifyFailure(clientResult);
16840
16901
  await emitRunRecord({
@@ -16846,7 +16907,7 @@ async function evaluateAndAct(args2) {
16846
16907
  credentialRefused: failure.kind === "credential"
16847
16908
  });
16848
16909
  if (failure.kind === "credential") {
16849
- process.stderr.write(credentialRejectedMessage(failure.statusCode, failure.message, CI_REMEDY, "[Halfcycle CI]"));
16910
+ process.stderr.write(isHalfcycleOwnCi() ? credentialRejectedMessage(failure.statusCode, failure.message, CI_REMEDY, "[Halfcycle CI]") : CI_NOT_SUPPORTED_MESSAGE);
16850
16911
  return 1;
16851
16912
  }
16852
16913
  if (failure.kind === "service") {
@@ -16951,7 +17012,6 @@ import { homedir as homedir2, platform } from "node:os";
16951
17012
  import { dirname as dirname3, join as join5 } from "node:path";
16952
17013
  var REFRESHED_TOKEN_KEYS = ["HALFCYCLE_TOKEN", "GUARD_SERVICE_TOKEN"];
16953
17014
  var TOKEN_READ_KEY = "GUARD_SERVICE_TOKEN";
16954
- var UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
16955
17015
  function composeEngagementEnvPath(engagementId, home) {
16956
17016
  return join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, ENGAGEMENT_ENV_FILENAME);
16957
17017
  }
@@ -16979,7 +17039,7 @@ function readStoredEngagementToken(envFilePath) {
16979
17039
  return value && value.trim() !== "" ? value : void 0;
16980
17040
  }
16981
17041
  function writeRefreshedToken(opts) {
16982
- if (!UUID_SHAPE.test(opts.engagementId)) {
17042
+ if (!isEngagementId(opts.engagementId)) {
16983
17043
  return { written: false, reason: "the engagement this session names is not one this store can be keyed by" };
16984
17044
  }
16985
17045
  let declaredReal;
@@ -17179,11 +17239,7 @@ async function runPostToolUse() {
17179
17239
  guardServiceUrl,
17180
17240
  token: guardServiceToken,
17181
17241
  engagementId: guardEngagementId,
17182
- request: {
17183
- engagementId: guardEngagementId,
17184
- changeSet,
17185
- clientCapabilities: deriveClientCapabilities(changeSet)
17186
- }
17242
+ request: buildEvaluationRequest(guardEngagementId, changeSet, context)
17187
17243
  });
17188
17244
  const clientResult = evaluation.result;
17189
17245
  if (!clientResult.ok) {
@@ -17468,11 +17524,7 @@ async function runSessionDiff(input, kind) {
17468
17524
  guardServiceUrl,
17469
17525
  token: guardServiceToken,
17470
17526
  engagementId: guardEngagementId,
17471
- request: {
17472
- engagementId: guardEngagementId,
17473
- changeSet,
17474
- clientCapabilities: deriveClientCapabilities(changeSet)
17475
- }
17527
+ request: buildEvaluationRequest(guardEngagementId, changeSet, context)
17476
17528
  });
17477
17529
  const clientResult = evaluation.result;
17478
17530
  if (!clientResult.ok) {
@@ -17866,6 +17918,7 @@ function readStdin3() {
17866
17918
  }
17867
17919
  main().catch((err) => {
17868
17920
  process.stderr.write(`halfcycle-runner: unexpected error: ${err instanceof Error ? err.message : String(err)}
17921
+ Contact hello@halfcycle.ai if this keeps happening.
17869
17922
  `);
17870
17923
  process.exit(1);
17871
17924
  });
package/dist/bin.d.ts CHANGED
@@ -11,13 +11,19 @@
11
11
  * halfcycle build-record <phase> — assemble the phase Build Record (D1)
12
12
  * halfcycle open-phase <phase> — open a phase, with its entry decision
13
13
  * halfcycle close-phase <phase> — record a phase's acceptance and close it
14
- * halfcycle ci bind <owner>/<repo> — trust that repository's CI for this engagement
15
- * halfcycle ci unbind <owner>/<repo> — withdraw that trust
14
+ * halfcycle uninstall [--remove-credential] — take back what the installer wrote here
16
15
  *
17
- * EVERY NON-INSTALL SUBCOMMAND MUST BE NAMED IN `bareTarget` BELOW. The bare form
16
+ * NO `ci` VERB (T-25, operator decision 2026-09-22(c)) — `ci bind`/`ci unbind`
17
+ * generalised a fix for Halfcycle's own dogfood CI into a client-facing feature;
18
+ * setting up a project's CI is project scope, not something this product does for
19
+ * a client. See `ci-oidc-token-exchange.md`.
20
+ *
21
+ * `bareTarget` BELOW IS DERIVED FROM `CLI_VERBS` (T-21). The bare form
18
22
  * (`npx halfcycle` / `npx halfcycle ./repo`) treats the first positional as an
19
- * install target, so a subcommand missing from that list is not "unknown command" —
20
- * it is silently an install into a directory named after the verb.
23
+ * install target, so a verb it did not recognise would not be "unknown command" —
24
+ * it would silently be an install into a directory named after the verb. It was a
25
+ * second, hand-typed list of verbs until `uninstall` joined, which is the verb whose
26
+ * misreading would be worst: `halfcycle uninstall` INSTALLING. One list now.
21
27
  *
22
28
  * TWO GUARDS ON THAT SHAPE (W3-F-4, walk 4). The bare-form-installs behaviour is
23
29
  * deliberate and stays — it is the primary distribution path — but walk 4 ran the
package/dist/bin.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG"}
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG"}