@indigoai-us/hq-cli 5.261.0 → 5.262.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.262.0] — 2026-09-28
6
+
7
+ ### Added
8
+
9
+ - The `checkpoint.ontology.concept-project-link` flag (off by default) lets
10
+ checkpoint ontology link a knowledge concept whose alias is ambiguous to the
11
+ one project with the same canonical name, such as HQ. It writes nothing.
12
+
13
+ ### Fixed
14
+
15
+ - `hq doctor --live-runtimes` uses a Windows `.cmd` shim only when the sibling file exists.
16
+
5
17
  ## [5.261.0] — 2026-09-28
6
18
 
7
19
  ### Fixed
@@ -370,6 +370,21 @@ export function applyOntologyRun(options) {
370
370
  }
371
371
  continue;
372
372
  }
373
+ if (resolved.tier === "ambiguous_alias") {
374
+ // checkpoint.ontology.concept-project-link: a knowledge concept whose name is the
375
+ // canonical name of exactly one project links to that project. Nothing is written.
376
+ // Like the tier 1 and 2 links above, it runs before the stopword guard,
377
+ // which only stops new entities.
378
+ const projects = plan.concept_project_link === true && input.kind === "knowledge" && candidate.type === "concept"
379
+ ? [...(index.canonical.get(entityKey("project", normalizeEntityName(canonical, "project"))) ?? [])]
380
+ : [];
381
+ if (projects.length === 1) {
382
+ const linked = index.byRel.get(projects[0]);
383
+ report.matched += 1;
384
+ report.links.push({ input: candidate.input, action: "link", type: linked.type, canonical_name: linked.canonical_name, path: projects[0] });
385
+ continue;
386
+ }
387
+ }
373
388
  // A generic word never becomes a new entity, whatever else it collides with.
374
389
  if (isRejectedName(normalizeEntityName(canonical, candidate.type))) {
375
390
  note(report.rejected, "stopword");
@@ -78,6 +78,17 @@ export declare const FIRST_NAME_PEOPLE_FLAG = "checkpoint.ontology.first-name-pe
78
78
  * to 0.803 and cost recall 0.023 on a replay of six runs.
79
79
  */
80
80
  export declare const SKIP_AMBIGUOUS_CONCEPT_LINKS_FLAG = "checkpoint.ontology.skip-ambiguous-concept-links";
81
+ /**
82
+ * A knowledge concept candidate whose name is an alias of more than one
83
+ * entity, and which apply would defer as `ambiguous_alias`, links instead to
84
+ * the one project whose canonical name it is (`HQ` typed as a concept beside
85
+ * `project/HQ` and several concepts aliased `HQ`). The plan carries
86
+ * `concept_project_link: true`. CO-51: over six runs of the shipping config
87
+ * the model typed HQ as a concept in some runs and not others, and the
88
+ * deferral lost 61 gold links; a replay of this rule raised recall from 0.639
89
+ * to 0.709 at precision 0.747 to 0.731 (61 of the 63 added links are HQ).
90
+ */
91
+ export declare const CONCEPT_PROJECT_LINK_FLAG = "checkpoint.ontology.concept-project-link";
81
92
  export declare const PLAN_FILE = "ontology-plan.json";
82
93
  export declare const CANDIDATES_FILE = "ontology-candidates.json";
83
94
  export declare const REPORT_FILE = "ontology-report.json";
@@ -150,6 +161,8 @@ export interface OntologyPlan {
150
161
  first_name_people?: true;
151
162
  /** Set by checkpoint.ontology.skip-ambiguous-concept-links: apply rejects a knowledge link to a concept whose name another type also holds. */
152
163
  skip_ambiguous_concept_links?: true;
164
+ /** Set by checkpoint.ontology.concept-project-link: apply links a knowledge concept with an ambiguous alias to the one project of that canonical name. */
165
+ concept_project_link?: true;
153
166
  inputs: PlanInput[];
154
167
  /** The session's summary, decisions and learnings, for the `session` input. */
155
168
  session_text: string | null;
@@ -82,6 +82,17 @@ export const FIRST_NAME_PEOPLE_FLAG = "checkpoint.ontology.first-name-people";
82
82
  * to 0.803 and cost recall 0.023 on a replay of six runs.
83
83
  */
84
84
  export const SKIP_AMBIGUOUS_CONCEPT_LINKS_FLAG = "checkpoint.ontology.skip-ambiguous-concept-links";
85
+ /**
86
+ * A knowledge concept candidate whose name is an alias of more than one
87
+ * entity, and which apply would defer as `ambiguous_alias`, links instead to
88
+ * the one project whose canonical name it is (`HQ` typed as a concept beside
89
+ * `project/HQ` and several concepts aliased `HQ`). The plan carries
90
+ * `concept_project_link: true`. CO-51: over six runs of the shipping config
91
+ * the model typed HQ as a concept in some runs and not others, and the
92
+ * deferral lost 61 gold links; a replay of this rule raised recall from 0.639
93
+ * to 0.709 at precision 0.747 to 0.731 (61 of the 63 added links are HQ).
94
+ */
95
+ export const CONCEPT_PROJECT_LINK_FLAG = "checkpoint.ontology.concept-project-link";
85
96
  export const PLAN_FILE = "ontology-plan.json";
86
97
  export const CANDIDATES_FILE = "ontology-candidates.json";
87
98
  export const REPORT_FILE = "ontology-report.json";
@@ -265,6 +276,7 @@ export async function planOntologyRun(options) {
265
276
  const noLinkAliases = await options.readFlag(NO_LINK_ALIASES_FLAG, company);
266
277
  const firstNamePeople = await options.readFlag(FIRST_NAME_PEOPLE_FLAG, company);
267
278
  const skipAmbiguousConceptLinks = await options.readFlag(SKIP_AMBIGUOUS_CONCEPT_LINKS_FLAG, company);
279
+ const conceptProjectLink = await options.readFlag(CONCEPT_PROJECT_LINK_FLAG, company);
268
280
  const touched = sessionTouched(hqRoot, company, payload.files);
269
281
  // A first run takes its boundary from the entity tree, so the ledger is
270
282
  // refreshed before merge-back moves any `last_updated` forward.
@@ -380,6 +392,7 @@ export async function planOntologyRun(options) {
380
392
  ...(noLinkAliases ? { link_aliases: false } : {}),
381
393
  ...(firstNamePeople ? { first_name_people: true } : {}),
382
394
  ...(skipAmbiguousConceptLinks ? { skip_ambiguous_concept_links: true } : {}),
395
+ ...(conceptProjectLink ? { concept_project_link: true } : {}),
383
396
  inputs,
384
397
  session_text: sessionText,
385
398
  pending_after_run: remaining,
@@ -76,6 +76,8 @@ export interface ProbeOutcome {
76
76
  export interface RuntimeHealthDeps {
77
77
  /** Resolve an executable against PATH; null when not found. */
78
78
  resolveBinary?: (binary: string) => string | null;
79
+ /** Check whether a Windows command shim exists next to its resolved binary. */
80
+ fileExists?: (file: string) => boolean;
79
81
  /** Execute one probe. The default spawns from the OS temp dir. */
80
82
  execProbe?: (file: string, args: string[], timeoutMs: number) => Promise<ProbeOutcome>;
81
83
  /** Per-probe timeout. Default {@link DEFAULT_PROBE_TIMEOUT_MS}. */
@@ -103,7 +103,7 @@ async function runRuntimeChecks(context, deps) {
103
103
  const resolved = {
104
104
  resolveBinary: deps.resolveBinary ?? resolveOnPath,
105
105
  execProbe: deps.execProbe ??
106
- ((file, args, timeoutMs) => defaultExecProbe(file, args, timeoutMs, deps.platform ?? process.platform)),
106
+ ((file, args, timeoutMs) => defaultExecProbe(file, args, timeoutMs, deps.platform ?? process.platform, deps.fileExists ?? fs.existsSync)),
107
107
  timeoutMs: deps.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS,
108
108
  live: context.liveRuntimes === true,
109
109
  };
@@ -270,10 +270,13 @@ export function resolveOnPath(binary) {
270
270
  * CLI that reads piped stdin in non-TTY mode — blocks forever on a pipe that
271
271
  * never reaches EOF, which turns every probe into a timeout.
272
272
  */
273
- function defaultExecProbe(file, args, timeoutMs, platform) {
273
+ function defaultExecProbe(file, args, timeoutMs, platform, fileExists) {
274
274
  return new Promise((resolve) => {
275
- const command = platform === "win32" && !/\.(?:cmd|bat|exe|com)$/i.test(file)
276
- ? `${file}.cmd`
275
+ const cmdSibling = `${file}.cmd`;
276
+ const command = platform === "win32" &&
277
+ !/\.(?:cmd|bat|exe|com)$/i.test(file) &&
278
+ fileExists(cmdSibling)
279
+ ? cmdSibling
277
280
  : file;
278
281
  const plan = planCommandSpawn(command, args, platform);
279
282
  const child = spawn(plan.cmd, plan.args, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.261.0",
3
+ "version": "5.262.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {