@indigoai-us/hq-cli 5.282.0 → 5.284.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 (55) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/assets/scaffold/.claude/hooks/validate-policy-frontmatter.sh +485 -0
  3. package/assets/scaffold/core/scripts/eval-trigger.sh +193 -0
  4. package/assets/scaffold/core/scripts/policy-age-report.sh +156 -0
  5. package/assets/scaffold/core/scripts/policy-retire.sh +199 -0
  6. package/assets/scaffold/core/scripts/read-policy-frontmatter.sh +32 -0
  7. package/dist/command-catalog.generated.d.ts +67 -0
  8. package/dist/command-catalog.generated.js +83 -0
  9. package/dist/commands/core-knowledge-prefs.d.ts +10 -0
  10. package/dist/commands/core-knowledge-prefs.js +125 -0
  11. package/dist/commands/core-native-policy-flag.d.ts +9 -0
  12. package/dist/commands/core-native-policy-flag.js +12 -0
  13. package/dist/commands/core-ontology-native.d.ts +0 -1
  14. package/dist/commands/core-ontology-native.js +2 -30
  15. package/dist/commands/core-ontology.d.ts +4 -0
  16. package/dist/commands/core-ontology.js +8 -3
  17. package/dist/commands/core-policy-frontmatter.d.ts +8 -0
  18. package/dist/commands/core-policy-frontmatter.js +52 -0
  19. package/dist/commands/core-policy.d.ts +15 -0
  20. package/dist/commands/core-policy.js +982 -0
  21. package/dist/commands/core.js +14 -1
  22. package/dist/commands/lanes-shared.d.ts +4 -1
  23. package/dist/commands/lanes-shared.js +2 -1
  24. package/dist/commands/lanes.d.ts +8 -0
  25. package/dist/commands/lanes.js +303 -91
  26. package/dist/fast-lanes-list-route.d.ts +8 -0
  27. package/dist/fast-lanes-list-route.js +30 -0
  28. package/dist/fast-lanes-list.d.ts +3 -0
  29. package/dist/fast-lanes-list.js +252 -0
  30. package/dist/fast-scaffold.d.ts +5 -0
  31. package/dist/fast-scaffold.js +10 -0
  32. package/dist/index.js +10 -1
  33. package/dist/lib/checkpoint-ontology/merge-copies.d.ts +24 -1
  34. package/dist/lib/checkpoint-ontology/merge-copies.js +70 -1
  35. package/dist/lib/lanes/flag-keys.d.ts +3 -0
  36. package/dist/lib/lanes/flag-keys.js +3 -0
  37. package/dist/lib/lanes/index.d.ts +1 -1
  38. package/dist/lib/lanes/index.js +1 -1
  39. package/dist/lib/lanes/outpost-dispatch.d.ts +9 -1
  40. package/dist/lib/lanes/outpost-dispatch.js +14 -2
  41. package/dist/lib/lanes/outpost-hire-cache.d.ts +3 -0
  42. package/dist/lib/lanes/outpost-hire-cache.js +11 -3
  43. package/dist/lib/lanes/outpost-snapshot-cache.d.ts +26 -0
  44. package/dist/lib/lanes/outpost-snapshot-cache.js +215 -0
  45. package/dist/lib/lanes/store.d.ts +4 -0
  46. package/dist/lib/lanes/store.js +39 -0
  47. package/dist/lib/mesh/live/daemon/credentials.js +8 -0
  48. package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +1 -0
  49. package/dist/lib/mesh/live/daemon/work-subscribe.js +143 -5
  50. package/dist/lib/policy-validator.d.ts +2 -0
  51. package/dist/lib/policy-validator.js +136 -0
  52. package/dist/native-fast-route.d.ts +2 -0
  53. package/dist/native-fast-route.js +6 -2
  54. package/dist/utils/contribution-table.d.ts +1 -1
  55. package/package.json +1 -1
@@ -5179,6 +5179,9 @@ export declare const COMMAND_CATALOG: readonly [{
5179
5179
  }, {
5180
5180
  readonly flags: "--delete";
5181
5181
  readonly description: "delete fully merged copies from the vault by exact key (after sync pushed the merge)";
5182
+ }, {
5183
+ readonly flags: "--variants";
5184
+ readonly description: "merge two-file duplicate groups whose names differ only in case, spacing or punctuation (checkpoint.ontology.merge-variants)";
5182
5185
  }, {
5183
5186
  readonly flags: "--limit <n>";
5184
5187
  readonly description: "handle at most this many copies";
@@ -5376,6 +5379,67 @@ export declare const COMMAND_CATALOG: readonly [{
5376
5379
  readonly options: readonly [];
5377
5380
  readonly subcommands: readonly [];
5378
5381
  }];
5382
+ }, {
5383
+ readonly name: "knowledge-prefs";
5384
+ readonly description: "Resolve a company knowledge-store preference";
5385
+ readonly aliases: readonly [];
5386
+ readonly hidden: false;
5387
+ readonly usage: "[args...]";
5388
+ readonly arguments: readonly [{
5389
+ readonly name: "args";
5390
+ readonly required: false;
5391
+ readonly variadic: true;
5392
+ }];
5393
+ readonly options: readonly [];
5394
+ readonly subcommands: readonly [];
5395
+ }, {
5396
+ readonly name: "policy";
5397
+ readonly description: "Policy maintenance commands";
5398
+ readonly aliases: readonly [];
5399
+ readonly hidden: false;
5400
+ readonly usage: "[options] [command]";
5401
+ readonly arguments: readonly [];
5402
+ readonly options: readonly [];
5403
+ readonly subcommands: readonly [{
5404
+ readonly name: "age-report";
5405
+ readonly description: "Report policy usage and retirement candidates";
5406
+ readonly aliases: readonly [];
5407
+ readonly hidden: false;
5408
+ readonly usage: "[args...]";
5409
+ readonly arguments: readonly [{
5410
+ readonly name: "args";
5411
+ readonly required: false;
5412
+ readonly variadic: true;
5413
+ }];
5414
+ readonly options: readonly [];
5415
+ readonly subcommands: readonly [];
5416
+ }, {
5417
+ readonly name: "retire";
5418
+ readonly description: "Retire or restore a policy";
5419
+ readonly aliases: readonly [];
5420
+ readonly hidden: false;
5421
+ readonly usage: "[args...]";
5422
+ readonly arguments: readonly [{
5423
+ readonly name: "args";
5424
+ readonly required: false;
5425
+ readonly variadic: true;
5426
+ }];
5427
+ readonly options: readonly [];
5428
+ readonly subcommands: readonly [];
5429
+ }, {
5430
+ readonly name: "frontmatter";
5431
+ readonly description: "Extract policy frontmatter";
5432
+ readonly aliases: readonly [];
5433
+ readonly hidden: false;
5434
+ readonly usage: "[args...]";
5435
+ readonly arguments: readonly [{
5436
+ readonly name: "args";
5437
+ readonly required: false;
5438
+ readonly variadic: true;
5439
+ }];
5440
+ readonly options: readonly [];
5441
+ readonly subcommands: readonly [];
5442
+ }];
5379
5443
  }, {
5380
5444
  readonly name: "generate-workers-registry";
5381
5445
  readonly description: "Regenerate the derived workers registry";
@@ -7692,6 +7756,9 @@ export declare const COMMAND_CATALOG: readonly [{
7692
7756
  }, {
7693
7757
  readonly flags: "--active";
7694
7758
  readonly description: "Include only lanes that are not terminal";
7759
+ }, {
7760
+ readonly flags: "--fresh";
7761
+ readonly description: "Wait for fresh remote owner summaries";
7695
7762
  }, {
7696
7763
  readonly flags: "--json";
7697
7764
  readonly description: "Emit JSON on stdout (default)";
@@ -6696,6 +6696,10 @@ export const COMMAND_CATALOG = [
6696
6696
  "flags": "--delete",
6697
6697
  "description": "delete fully merged copies from the vault by exact key (after sync pushed the merge)"
6698
6698
  },
6699
+ {
6700
+ "flags": "--variants",
6701
+ "description": "merge two-file duplicate groups whose names differ only in case, spacing or punctuation (checkpoint.ontology.merge-variants)"
6702
+ },
6699
6703
  {
6700
6704
  "flags": "--limit <n>",
6701
6705
  "description": "handle at most this many copies"
@@ -6942,6 +6946,81 @@ export const COMMAND_CATALOG = [
6942
6946
  }
6943
6947
  ]
6944
6948
  },
6949
+ {
6950
+ "name": "knowledge-prefs",
6951
+ "description": "Resolve a company knowledge-store preference",
6952
+ "aliases": [],
6953
+ "hidden": false,
6954
+ "usage": "[args...]",
6955
+ "arguments": [
6956
+ {
6957
+ "name": "args",
6958
+ "required": false,
6959
+ "variadic": true
6960
+ }
6961
+ ],
6962
+ "options": [],
6963
+ "subcommands": []
6964
+ },
6965
+ {
6966
+ "name": "policy",
6967
+ "description": "Policy maintenance commands",
6968
+ "aliases": [],
6969
+ "hidden": false,
6970
+ "usage": "[options] [command]",
6971
+ "arguments": [],
6972
+ "options": [],
6973
+ "subcommands": [
6974
+ {
6975
+ "name": "age-report",
6976
+ "description": "Report policy usage and retirement candidates",
6977
+ "aliases": [],
6978
+ "hidden": false,
6979
+ "usage": "[args...]",
6980
+ "arguments": [
6981
+ {
6982
+ "name": "args",
6983
+ "required": false,
6984
+ "variadic": true
6985
+ }
6986
+ ],
6987
+ "options": [],
6988
+ "subcommands": []
6989
+ },
6990
+ {
6991
+ "name": "retire",
6992
+ "description": "Retire or restore a policy",
6993
+ "aliases": [],
6994
+ "hidden": false,
6995
+ "usage": "[args...]",
6996
+ "arguments": [
6997
+ {
6998
+ "name": "args",
6999
+ "required": false,
7000
+ "variadic": true
7001
+ }
7002
+ ],
7003
+ "options": [],
7004
+ "subcommands": []
7005
+ },
7006
+ {
7007
+ "name": "frontmatter",
7008
+ "description": "Extract policy frontmatter",
7009
+ "aliases": [],
7010
+ "hidden": false,
7011
+ "usage": "[args...]",
7012
+ "arguments": [
7013
+ {
7014
+ "name": "args",
7015
+ "required": false,
7016
+ "variadic": true
7017
+ }
7018
+ ],
7019
+ "options": [],
7020
+ "subcommands": []
7021
+ }
7022
+ ]
7023
+ },
6945
7024
  {
6946
7025
  "name": "generate-workers-registry",
6947
7026
  "description": "Regenerate the derived workers registry",
@@ -9935,6 +10014,10 @@ export const COMMAND_CATALOG = [
9935
10014
  "flags": "--active",
9936
10015
  "description": "Include only lanes that are not terminal"
9937
10016
  },
10017
+ {
10018
+ "flags": "--fresh",
10019
+ "description": "Wait for fresh remote owner summaries"
10020
+ },
9938
10021
  {
9939
10022
  "flags": "--json",
9940
10023
  "description": "Emit JSON on stdout (default)"
@@ -0,0 +1,10 @@
1
+ import type { Command } from 'commander';
2
+ export declare const KNOWLEDGE_PREFS_ASSET = "core/scripts/knowledge-prefs.sh";
3
+ export type CoreCommandResult = {
4
+ code: number;
5
+ stdout: string;
6
+ stderr: string;
7
+ };
8
+ export declare function runKnowledgePrefs(args: readonly string[], root: string): CoreCommandResult;
9
+ export declare function registerCoreKnowledgePrefsCommand(core: Command): void;
10
+ //# sourceMappingURL=core-knowledge-prefs.d.ts.map
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Native port of core/scripts/knowledge-prefs.sh.
3
+ *
4
+ * The shell command reads only HQ_ROOT (the CLI resolves and supplies that
5
+ * source root) and scans top-level scalar lines without parsing YAML.
6
+ */
7
+ import * as fs from 'node:fs';
8
+ import * as path from 'node:path';
9
+ import { resolveLiveRoot } from '../utils/hq-roots.js';
10
+ import { runBundledScript } from '../utils/run-bundled-script.js';
11
+ import { describeCoreCommand } from './core-command-catalog.js';
12
+ import { coreNativePolicyEnabled } from './core-native-policy-flag.js';
13
+ export const KNOWLEDGE_PREFS_ASSET = 'core/scripts/knowledge-prefs.sh';
14
+ const DEFAULTS = {
15
+ signals_enabled: 'true',
16
+ ontology_enabled: 'true',
17
+ signals_capture: 'false',
18
+ ontology_capture: 'false',
19
+ meeting_notes_source: 'hq-native',
20
+ notetaker: 'recall',
21
+ };
22
+ const USAGE = 'usage: knowledge-prefs.sh get <company> <field>\n';
23
+ function readField(filePath, field) {
24
+ let body;
25
+ try {
26
+ body = fs.readFileSync(filePath, 'utf8');
27
+ }
28
+ catch (error) {
29
+ const code = error.code;
30
+ if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES' || code === 'ELOOP')
31
+ return undefined;
32
+ throw error;
33
+ }
34
+ for (const line of body.split(/\n/)) {
35
+ if (!line.startsWith(field + ':'))
36
+ continue;
37
+ let value = line.slice(field.length + 1).replace(/^[\s]+/, '');
38
+ value = value.replace(/[\s]+#.*$/, '');
39
+ value = value.replace(/^["']|["']$/g, '');
40
+ if (value.length > 0)
41
+ return value;
42
+ }
43
+ return undefined;
44
+ }
45
+ export function runKnowledgePrefs(args, root) {
46
+ if (args[0] !== 'get' || !args[1] || !args[2]) {
47
+ return { code: 2, stdout: '', stderr: USAGE };
48
+ }
49
+ const company = args[1];
50
+ const field = args[2];
51
+ const fallback = DEFAULTS[field];
52
+ if (fallback === undefined) {
53
+ return {
54
+ code: 2,
55
+ stdout: '',
56
+ stderr: "knowledge-prefs: unknown field '" + field + "'\n",
57
+ };
58
+ }
59
+ let companyExists = false;
60
+ try {
61
+ companyExists = fs.statSync(path.join(root, 'companies', company)).isDirectory();
62
+ }
63
+ catch (error) {
64
+ // `test -d` returns false for missing, unreadable, or non-directory paths.
65
+ const code = error.code;
66
+ if (code !== 'ENOENT' && code !== 'ENOTDIR' && code !== 'EACCES' && code !== 'ELOOP') {
67
+ throw error;
68
+ }
69
+ }
70
+ if (!companyExists) {
71
+ return {
72
+ code: 2,
73
+ stdout: '',
74
+ stderr: "knowledge-prefs: no company '" + company + "'\n",
75
+ };
76
+ }
77
+ const companyValue = readField(path.join(root, 'companies', company, 'settings', 'knowledge', 'preferences.yaml'), field);
78
+ const personalValue = companyValue === undefined
79
+ ? readField(path.join(root, 'personal', 'settings', 'knowledge-preferences.yaml'), field)
80
+ : undefined;
81
+ return {
82
+ code: 0,
83
+ stdout: (companyValue ?? personalValue ?? fallback) + '\n',
84
+ stderr: '',
85
+ };
86
+ }
87
+ export function registerCoreKnowledgePrefsCommand(core) {
88
+ const command = core
89
+ .command('knowledge-prefs')
90
+ .description('Resolve a company knowledge-store preference')
91
+ .allowUnknownOption()
92
+ .allowExcessArguments()
93
+ .helpOption(false)
94
+ .argument('[args...]', 'arguments passed through to the script')
95
+ .action(async (args = [], _options, cmd) => {
96
+ const operands = cmd.args.length > 0 ? cmd.args : args;
97
+ const hqRoot = resolveLiveRoot({
98
+ hqRoot: core.opts().hqRoot,
99
+ });
100
+ if (!await coreNativePolicyEnabled()) {
101
+ const result = runBundledScript({
102
+ asset: KNOWLEDGE_PREFS_ASSET,
103
+ args: operands,
104
+ cwd: hqRoot,
105
+ hqRoot,
106
+ });
107
+ if (result.code !== 0)
108
+ process.exitCode = result.code;
109
+ return;
110
+ }
111
+ const result = runKnowledgePrefs(operands, hqRoot);
112
+ if (result.stdout)
113
+ process.stdout.write(result.stdout);
114
+ if (result.stderr)
115
+ process.stderr.write(result.stderr);
116
+ if (result.code !== 0)
117
+ process.exitCode = result.code;
118
+ });
119
+ describeCoreCommand(command, {
120
+ execution: 'native',
121
+ asset: KNOWLEDGE_PREFS_ASSET,
122
+ root: 'live',
123
+ });
124
+ }
125
+ //# sourceMappingURL=core-knowledge-prefs.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Default-off rollout gate shared by the native knowledge and policy commands.
3
+ *
4
+ * The flag reader is loaded only after command selection. Its process gate reads
5
+ * the in-memory hq-flags snapshot synchronously and falls back to false.
6
+ */
7
+ export declare const CORE_NATIVE_POLICY_FLAG = "core-native-policy";
8
+ export declare function coreNativePolicyEnabled(): Promise<boolean>;
9
+ //# sourceMappingURL=core-native-policy-flag.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Default-off rollout gate shared by the native knowledge and policy commands.
3
+ *
4
+ * The flag reader is loaded only after command selection. Its process gate reads
5
+ * the in-memory hq-flags snapshot synchronously and falls back to false.
6
+ */
7
+ export const CORE_NATIVE_POLICY_FLAG = 'core-native-policy';
8
+ export async function coreNativePolicyEnabled() {
9
+ const { resolveProcessFlagGate } = await import('../lib/flag-registry.js');
10
+ return resolveProcessFlagGate(CORE_NATIVE_POLICY_FLAG, { fallback: false }, () => false);
11
+ }
12
+ //# sourceMappingURL=core-native-policy-flag.js.map
@@ -5,7 +5,6 @@ export interface ScriptResult {
5
5
  stdout: string;
6
6
  stderr: string;
7
7
  }
8
- export declare function preferenceValue(root: string, company: string, field: string): ScriptResult;
9
8
  export declare function sourceYamlValidate(file: string | undefined): ScriptResult;
10
9
  export declare function candidateCommand(args: string[], root: string): ScriptResult;
11
10
  export declare function briefCommand(args: string[], defaultRoot: string): ScriptResult;
@@ -7,19 +7,12 @@ import { resolveProcessFlagGate } from "../lib/flag-registry.js";
7
7
  import { runBundledScript } from "../utils/run-bundled-script.js";
8
8
  import { resolveBundledAsset } from "../utils/hq-roots.js";
9
9
  import { describeCoreCommand } from "./core-command-catalog.js";
10
+ import { runKnowledgePrefs } from "./core-knowledge-prefs.js";
10
11
  const FLAG = "core-native-ontology";
11
12
  const ASSET = "core/scripts/";
12
13
  const SIGNAL_TYPES = ["decision", "commitment", "risk", "question", "action_item", "key_point", "participant_contribution", "summary"];
13
14
  const CANDIDATE_SIGNAL_TYPES = ["action_item", "commitment", "decision", "risk", "question", "key_point", "participant_contribution", "summary"];
14
15
  const ENTITY_TYPES = ["person", "project", "company", "concept"];
15
- const DEFAULT_PREFS = {
16
- signals_enabled: "true",
17
- ontology_enabled: "true",
18
- signals_capture: "false",
19
- ontology_capture: "false",
20
- meeting_notes_source: "hq-native",
21
- notetaker: "recall",
22
- };
23
16
  const ok = (stdout = "") => ({ code: 0, stdout, stderr: "" });
24
17
  const err = (code, stderr, stdout = "") => ({ code, stdout, stderr });
25
18
  const shellSubstitution = (value) => value.replace(/\n+$/, "");
@@ -64,24 +57,6 @@ function awkField(file, field, stripComment) {
64
57
  value = value.replace(/[\t\n\v\f\r ]+#.*$/, "");
65
58
  return value.replace(/^["']|["']$/g, "");
66
59
  }
67
- export function preferenceValue(root, company, field) {
68
- if (!(field in DEFAULT_PREFS))
69
- return err(2, `knowledge-prefs: unknown field '${field}'\n`);
70
- const companyDir = path.join(root, "companies", company);
71
- if (!fs.existsSync(companyDir) || !fs.statSync(companyDir).isDirectory()) {
72
- return err(2, `knowledge-prefs: no company '${company}'\n`);
73
- }
74
- const files = [
75
- path.join(companyDir, "settings", "knowledge", "preferences.yaml"),
76
- path.join(root, "personal", "settings", "knowledge-preferences.yaml"),
77
- ];
78
- for (const file of files) {
79
- const value = awkField(file, field, true);
80
- if (value.length > 0)
81
- return ok(`${value}\n`);
82
- }
83
- return ok(`${DEFAULT_PREFS[field]}\n`);
84
- }
85
60
  function normalizeAudience(value) {
86
61
  const entries = value.split(",")
87
62
  .map((item) => item.replace(/^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g, ""))
@@ -98,9 +73,6 @@ function audienceKey(value) {
98
73
  const key = createHash("sha256").update(normalizeAudience(audience)).digest("hex").slice(0, 16);
99
74
  return ok(`${key}\n`);
100
75
  }
101
- function readKnowledgePreference(root, company, field) {
102
- return preferenceValue(root, company, field);
103
- }
104
76
  export function sourceYamlValidate(file) {
105
77
  if (!file)
106
78
  return err(2, "usage: source-yaml-validate.sh <source.yaml>\n");
@@ -205,7 +177,7 @@ export function candidateCommand(args, root) {
205
177
  const allowed = kind === "signal" ? CANDIDATE_SIGNAL_TYPES : ENTITY_TYPES;
206
178
  if (!allowed.includes(type))
207
179
  return err(2, `ontology-candidate: unknown ${kind} type '${type}' (allowed: ${allowed.join(" ")})\n`);
208
- const enabled = readKnowledgePreference(root, company, preference);
180
+ const enabled = runKnowledgePrefs(["get", company, preference], root);
209
181
  if (enabled.code !== 0)
210
182
  return err(2, enabled.stderr);
211
183
  if (shellSubstitution(enabled.stdout) !== "true")
@@ -30,6 +30,7 @@ export declare function runOntologyDuplicates(hqRoot: string, slug: string | und
30
30
  export interface MergeCopiesCommandOptions {
31
31
  apply?: boolean;
32
32
  delete?: boolean;
33
+ variants?: boolean;
33
34
  limit?: number;
34
35
  json?: boolean;
35
36
  }
@@ -38,6 +39,9 @@ export interface MergeCopiesCommandOptions {
38
39
  * original (`--apply`), then delete merged copies from the vault
39
40
  * (`--delete`). Without either it only reports. Gated by
40
41
  * `checkpoint.ontology.merge-copies` and `checkpoint.ontology.multi-writer`.
42
+ * With `--variants`, merges two-file duplicate groups whose names differ only
43
+ * in case, spacing or punctuation instead, gated by
44
+ * `checkpoint.ontology.merge-variants`.
41
45
  */
42
46
  export declare function runOntologyMergeCopies(hqRoot: string, slug: string | undefined, options: MergeCopiesCommandOptions, deps?: OntologyCommandDeps): Promise<string>;
43
47
  export declare function registerCoreOntologyCommand(core: Command): void;
@@ -17,7 +17,7 @@ import { applyOntologyRun, ApplyError } from "../lib/checkpoint-ontology/apply.j
17
17
  import { resolveOntologyCompany, WriteBoundsError } from "../lib/checkpoint-ontology/company.js";
18
18
  import { findDuplicateGroups, summarizeMergeBack, } from "../lib/checkpoint-ontology/multi-writer.js";
19
19
  import { buildEntityIndex } from "../lib/checkpoint-ontology/tree.js";
20
- import { MERGE_COPIES_FLAG, mergeCopies, } from "../lib/checkpoint-ontology/merge-copies.js";
20
+ import { MERGE_COPIES_FLAG, MERGE_VARIANTS_FLAG, mergeCopies, } from "../lib/checkpoint-ontology/merge-copies.js";
21
21
  import { callDeleteEndpoint } from "./files.js";
22
22
  import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
23
23
  import { registerCoreOntologyNativeCommands } from "./core-ontology-native.js";
@@ -145,7 +145,7 @@ async function companyVaultDeleter(companyUid) {
145
145
  function mergeCopiesSummary(report) {
146
146
  const held = report.plans.filter((plan) => plan.held).length;
147
147
  const lines = [
148
- `ontology merge-copies (${report.mode.replace("_", " ")}) in ${report.company}: ${report.plans.length} of ${report.copies_found} copies, ${held} held, ${report.skipped.length} skipped`,
148
+ `ontology merge-copies (${report.mode.replace("_", " ")}) in ${report.company} (${report.pair_kind}): ${report.plans.length} of ${report.copies_found} copies, ${held} held, ${report.skipped.length} skipped`,
149
149
  ` merged ${report.merged.length}, relinked ${report.relinked.length}, deleted ${report.deleted.length}, not ready ${report.not_ready.length}, errors ${report.errors.length}`,
150
150
  ` duplicate groups ${report.duplicate_groups_before} -> ${report.duplicate_groups_after}; broken entity links ${report.broken_links_before} -> ${report.broken_links_after}`,
151
151
  ];
@@ -160,6 +160,9 @@ function mergeCopiesSummary(report) {
160
160
  * original (`--apply`), then delete merged copies from the vault
161
161
  * (`--delete`). Without either it only reports. Gated by
162
162
  * `checkpoint.ontology.merge-copies` and `checkpoint.ontology.multi-writer`.
163
+ * With `--variants`, merges two-file duplicate groups whose names differ only
164
+ * in case, spacing or punctuation instead, gated by
165
+ * `checkpoint.ontology.merge-variants`.
163
166
  */
164
167
  export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
165
168
  if (options.apply && options.delete) {
@@ -173,7 +176,7 @@ export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
173
176
  throw new OntologyUsageError(`ontology: company rejected: ${resolution.reason}`);
174
177
  const company = resolution.company;
175
178
  const readFlag = deps.readFlag ?? registryFlagReader;
176
- for (const flag of [MERGE_COPIES_FLAG, MULTI_WRITER_FLAG]) {
179
+ for (const flag of [options.variants ? MERGE_VARIANTS_FLAG : MERGE_COPIES_FLAG, MULTI_WRITER_FLAG]) {
177
180
  if (!(await readFlag(flag, company))) {
178
181
  return options.json ? JSON.stringify({ company: company.slug, skipped: `${flag} off` }) : `ontology merge-copies: skipped (${flag} is off)`;
179
182
  }
@@ -190,6 +193,7 @@ export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
190
193
  company,
191
194
  mode,
192
195
  now: deps.now?.() ?? new Date(),
196
+ ...(options.variants ? { pairKind: "variants" } : {}),
193
197
  ...(options.limit !== undefined ? { limit: options.limit } : {}),
194
198
  ...(deleteVaultKey ? { deleteVaultKey } : {}),
195
199
  });
@@ -242,6 +246,7 @@ export function registerCoreOntologyCommand(core) {
242
246
  .requiredOption("--company <slug>", "company to change")
243
247
  .option("--apply", "merge each copy into its original and re-point links (writes entity files)")
244
248
  .option("--delete", "delete fully merged copies from the vault by exact key (after sync pushed the merge)")
249
+ .option("--variants", "merge two-file duplicate groups whose names differ only in case, spacing or punctuation (checkpoint.ontology.merge-variants)")
245
250
  .option("--limit <n>", "handle at most this many copies", (value) => Number(value))
246
251
  .option("--json", "print JSON")
247
252
  .option("--hq-root <path>", "HQ installation to operate on")
@@ -0,0 +1,8 @@
1
+ export type FrontmatterResult = {
2
+ code: number;
3
+ stdout: Buffer;
4
+ stderr: Buffer;
5
+ };
6
+ /** Byte-preserving port of core/scripts/read-policy-frontmatter.sh. */
7
+ export declare function runPolicyFrontmatter(args: readonly string[], cwd: string): FrontmatterResult;
8
+ //# sourceMappingURL=core-policy-frontmatter.d.ts.map
@@ -0,0 +1,52 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ const USAGE = Buffer.from('USAGE: read-policy-frontmatter.sh <policy-file>\n');
4
+ /** Byte-preserving port of core/scripts/read-policy-frontmatter.sh. */
5
+ export function runPolicyFrontmatter(args, cwd) {
6
+ const first = args[0];
7
+ if (first === undefined)
8
+ return { code: 1, stdout: Buffer.alloc(0), stderr: USAGE };
9
+ const filePath = path.isAbsolute(first) ? first : path.resolve(cwd, first);
10
+ let source;
11
+ try {
12
+ fs.accessSync(filePath, fs.constants.R_OK);
13
+ source = fs.readFileSync(filePath);
14
+ }
15
+ catch (error) {
16
+ const code = error.code;
17
+ if (code === 'EISDIR') {
18
+ return {
19
+ code: 0,
20
+ stdout: Buffer.alloc(0),
21
+ stderr: Buffer.from(`awk: warning: command line argument \`${first}' is a directory: skipped\n`),
22
+ };
23
+ }
24
+ if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES' || code === 'ELOOP') {
25
+ return {
26
+ code: 1,
27
+ stdout: Buffer.alloc(0),
28
+ stderr: Buffer.from(`ERROR: File not readable: ${first}\n`),
29
+ };
30
+ }
31
+ throw error;
32
+ }
33
+ const output = [];
34
+ let markers = 0;
35
+ let start = 0;
36
+ while (start < source.length) {
37
+ const end = source.indexOf(0x0a, start);
38
+ const lineEnd = end < 0 ? source.length : end;
39
+ const line = source.subarray(start, lineEnd);
40
+ if (line.length === 3 && line[0] === 0x2d && line[1] === 0x2d && line[2] === 0x2d) {
41
+ markers += 1;
42
+ }
43
+ else if (markers === 1) {
44
+ output.push(line, Buffer.from('\n'));
45
+ }
46
+ if (end < 0)
47
+ break;
48
+ start = end + 1;
49
+ }
50
+ return { code: 0, stdout: Buffer.concat(output), stderr: Buffer.alloc(0) };
51
+ }
52
+ //# sourceMappingURL=core-policy-frontmatter.js.map
@@ -0,0 +1,15 @@
1
+ import type { Command } from 'commander';
2
+ import { type FrontmatterResult } from './core-policy-frontmatter.js';
3
+ export declare const POLICY_AGE_REPORT_ASSET = "core/scripts/policy-age-report.sh";
4
+ export declare const POLICY_RETIRE_ASSET = "core/scripts/policy-retire.sh";
5
+ export declare const POLICY_FRONTMATTER_ASSET = "core/scripts/read-policy-frontmatter.sh";
6
+ export type CorePolicyResult = {
7
+ code: number;
8
+ stdout: Buffer;
9
+ stderr: Buffer;
10
+ };
11
+ export declare function runPolicyAgeReport(args: readonly string[], root: string, env?: NodeJS.ProcessEnv, nowEpoch?: number): CorePolicyResult;
12
+ export declare function runPolicyFrontmatter(args: readonly string[], cwd: string): FrontmatterResult;
13
+ export declare function runPolicyRetire(args: readonly string[], root: string, cwd: string, env?: NodeJS.ProcessEnv, now?: Date): CorePolicyResult;
14
+ export declare function registerCorePolicyCommand(core: Command): void;
15
+ //# sourceMappingURL=core-policy.d.ts.map