@thieung/agentkit-helper 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -31,8 +31,11 @@ In the TUI, use:
31
31
  - Esc to return to the previous step
32
32
 
33
33
  Choose a project or user/global scope, Engineer or Marketing Kit, one or more
34
- runtimes, and the Stable or Beta channel. AgentKit remains responsible for Kit
35
- verification, ownership, snapshots, and runtime-specific files.
34
+ runtimes, and the Stable or Beta channel. When the current directory is a safe
35
+ project path (not `/` or your home directory), the TUI offers **Use current
36
+ project**; the equivalent CLI flag is `--project .`. AgentKit remains
37
+ responsible for Kit verification, ownership, snapshots, and runtime-specific
38
+ files.
36
39
 
37
40
  For global install, the helper first runs without `--force`. If the target
38
41
  already exists or drift is detected, the TUI shows a WARNING and asks for
@@ -46,6 +49,15 @@ it passes a real-machine smoke test; the repository includes Windows-oriented
46
49
  command handling and CI coverage, but does not yet claim provider-backed E2E
47
50
  verification.
48
51
 
52
+ Windows smoke checklist (manual; keep experimental until this passes on a real
53
+ machine):
54
+
55
+ 1. Install `ak` with `irm https://agentkit.best/install.ps1 | iex`, then open a
56
+ new terminal.
57
+ 2. Run `npx --yes @thieung/agentkit-helper` (or `akh` after a global npm install).
58
+ 3. In the TUI, choose **Use current project** (or `akh install --project .`).
59
+ 4. Install one runtime, then run `update-all` for that install.
60
+
49
61
  <details>
50
62
  <summary><strong>Advanced CLI usage</strong></summary>
51
63
 
@@ -79,10 +91,12 @@ akh update --project /path/to/project --dry-run
79
91
 
80
92
  Targets:
81
93
 
82
- - Install: `claude-code`, `codex`, `cursor`, `dsh`, `grok`, `omp`, `pi`
83
- - Update: `claude-code`, `codex`, `cursor`, `grok`, `omp`, `pi`
94
+ - Install: `claude-code`, `codex`, `cursor`, `dsh`, `grok`, `omp`, `pi`, `pi-ak`, `pi-omp`
95
+ - Update: `claude-code`, `codex`, `cursor`, `dsh`, `grok`, `omp`, `pi`, `pi-ak`, `pi-omp`
84
96
  - Export: `agy`, `portable`
85
97
 
98
+ `dsh` updates use `ak kit refresh` because remote `ak update` still rejects that runtime. `pi-ak` and `pi-omp` are global-only profile aliases (`--global`); they install/update into the custom Pi/OMP homes instead of the default `pi`/`omp` directories.
99
+
86
100
  Run `akh --help` for every command and option.
87
101
 
88
102
  </details>
package/README.vi.md CHANGED
@@ -30,8 +30,10 @@ Trong TUI, dùng:
30
30
  - Esc để trở về bước trước
31
31
 
32
32
  Bạn chỉ cần chọn scope project hoặc user/global, Engineer hoặc Marketing Kit,
33
- một hay nhiều runtime và release channel Stable hoặc Beta. AgentKit vẫn quản
34
- việc xác minh Kit, ownership, snapshot file riêng của từng runtime.
33
+ một hay nhiều runtime và release channel Stable hoặc Beta. Khi thư mục hiện tại
34
+ project hợp lệ (không phải `/` hay home), TUI **Dùng project hiện tại**;
35
+ CLI tương đương là `--project .`. AgentKit vẫn quản lý việc xác minh Kit,
36
+ ownership, snapshot và file riêng của từng runtime.
35
37
 
36
38
  Khi cài global scope, helper chạy trước mà không có `--force`. Nếu target đã
37
39
  tồn tại hoặc có drift, TUI hiển thị WARNING và hỏi consent riêng, mặc định No.
@@ -43,6 +45,16 @@ qua code path Node.js portable. Native Windows PowerShell đang ở mức
43
45
  experimental cho đến khi được smoke-test trên máy thật; repository đã có xử lý
44
46
  command và CI dành cho Windows nhưng chưa claim provider-backed E2E.
45
47
 
48
+ Checklist smoke Windows (thủ công; giữ experimental cho đến khi chạy xong trên
49
+ máy thật):
50
+
51
+ 1. Cài `ak` bằng `irm https://agentkit.best/install.ps1 | iex`, rồi mở terminal
52
+ mới.
53
+ 2. Chạy `npx --yes @thieung/agentkit-helper` (hoặc `akh` sau khi npm install
54
+ global).
55
+ 3. Trong TUI, chọn **Dùng project hiện tại** (hoặc `akh install --project .`).
56
+ 4. Cài một runtime, rồi chạy `update-all` cho install đó.
57
+
46
58
  <details>
47
59
  <summary><strong>Cách dùng CLI nâng cao</strong></summary>
48
60
 
@@ -76,10 +88,12 @@ akh update --project /path/to/project --dry-run
76
88
 
77
89
  Các target:
78
90
 
79
- - Cài đặt: `claude-code`, `codex`, `cursor`, `dsh`, `grok`, `omp`, `pi`
80
- - Cập nhật: `claude-code`, `codex`, `cursor`, `grok`, `omp`, `pi`
91
+ - Cài đặt: `claude-code`, `codex`, `cursor`, `dsh`, `grok`, `omp`, `pi`, `pi-ak`, `pi-omp`
92
+ - Cập nhật: `claude-code`, `codex`, `cursor`, `dsh`, `grok`, `omp`, `pi`, `pi-ak`, `pi-omp`
81
93
  - Export: `agy`, `portable`
82
94
 
95
+ Update `dsh` đi qua `ak kit refresh` vì `ak update` remote vẫn reject runtime này. `pi-ak` và `pi-omp` là alias profile chỉ dùng với `--global`; chúng cài/cập nhật vào home Pi/OMP custom, không phải thư mục `pi`/`omp` mặc định.
96
+
83
97
  Chạy `akh --help` để xem toàn bộ command và option.
84
98
 
85
99
  </details>
@@ -6,9 +6,10 @@ import { dirname, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import {
8
8
  EXPORT_TARGETS,
9
- INSTALL_TARGETS,
9
+ helperRuntimeTargets,
10
10
  KITS,
11
11
  parseArgs,
12
+ specHasProfileTarget,
12
13
  splitTargetSpec,
13
14
  targetSpecIsSupported,
14
15
  UPDATE_TARGETS,
@@ -21,14 +22,17 @@ import {
21
22
  globalUpdateApplyArgs,
22
23
  globalUpdatePreviewArgs,
23
24
  installArgs,
25
+ kitRefreshArgs,
24
26
  projectUpdateApplyArgs,
25
27
  projectUpdatePreviewArgs,
26
28
  selfUpdateCheckArgs,
27
29
  selfUpdateApplyArgs,
28
30
  selfUpdateJsonApplyArgs,
29
31
  selfUpdateJsonCheckArgs,
30
- updateApplyArgs,
31
- updatePreviewArgs,
32
+ splitRuntimesForUpdate,
33
+ updateApplyArgsForRuntime,
34
+ updatePreviewArgsForRuntime,
35
+ usesKitRefresh,
32
36
  } from "../lib/commands.mjs";
33
37
  import { readProjectConfig, writeProjectConfig } from "../lib/config.mjs";
34
38
  import { colorText } from "../lib/colors.mjs";
@@ -48,6 +52,7 @@ import { t } from "../lib/i18n.mjs";
48
52
  import { isUnsafeProjectPath, resolveProjectPath } from "../lib/project.mjs";
49
53
  import {
50
54
  classifyPiProfiles,
55
+ materializeHelperTarget,
51
56
  parsePiProfileInventory,
52
57
  piProfileManagerInvocation,
53
58
  piProfileProcessOptions,
@@ -140,8 +145,8 @@ Tùy chọn:
140
145
  --version, -v Hiện phiên bản
141
146
 
142
147
  Target groups:
143
- Install claude-code, codex, cursor, dsh, grok, omp, pi
144
- Update claude-code, codex, cursor, grok, omp, pi (dsh: unsupported)
148
+ Install claude-code, codex, cursor, dsh, grok, omp, pi, pi-ak, pi-omp
149
+ Update claude-code, codex, cursor, dsh, grok, omp, pi, pi-ak, pi-omp
145
150
  Export agy, portable
146
151
  `);
147
152
  return;
@@ -177,8 +182,8 @@ Options:
177
182
  --version, -v Show version
178
183
 
179
184
  Target groups:
180
- Install claude-code, codex, cursor, dsh, grok, omp, pi
181
- Update claude-code, codex, cursor, grok, omp, pi (dsh: unsupported)
185
+ Install claude-code, codex, cursor, dsh, grok, omp, pi, pi-ak, pi-omp
186
+ Update claude-code, codex, cursor, dsh, grok, omp, pi, pi-ak, pi-omp
182
187
  Export agy, portable
183
188
  `);
184
189
  }
@@ -369,15 +374,11 @@ async function selectTarget(
369
374
  const allRuntimeValue = "__all_runtimes__";
370
375
  const choices = [
371
376
  { label: ui("allRuntimes"), value: allRuntimeValue },
372
- ...[...targets].map((target) => ({ label: target, value: target })),
377
+ ...[...targets].map((target) => ({
378
+ label: target === "pi-ak" ? ui("piAkProfile") : target === "pi-omp" ? ui("piOmpProfile") : target,
379
+ value: target,
380
+ })),
373
381
  ];
374
- if (promptKey === "updateTargetPrompt") {
375
- choices.push({
376
- label: ui("dshUpdateUnsupported"),
377
- value: "dsh",
378
- disabled: true,
379
- });
380
- }
381
382
  const initialValues = [];
382
383
  const selected = await (allowBack ? multiChooseWithBack : multiChoose)(
383
384
  ui(promptKey, promptValues),
@@ -415,10 +416,6 @@ function warnBetaLifecycle(channel) {
415
416
  }
416
417
 
417
418
  async function prepareBetaBinary(commandOptions, { requiresPreview = false } = {}) {
418
- const currentVersion = installedAkVersion.replace(/^ak\s+/i, "");
419
- if (!currentVersion || currentVersion.includes("-")) {
420
- return { proceed: true, updated: false };
421
- }
422
419
  warnBetaLifecycle("beta");
423
420
  const check = parseSelfUpdateOutput((await runCapture(
424
421
  akBinary, selfUpdateJsonCheckArgs("beta"),
@@ -472,7 +469,9 @@ async function install(commandOptions, allowBack = false) {
472
469
  {
473
470
  key: "target",
474
471
  select: (values) => selectTarget(
475
- commandOptions, config, INSTALL_TARGETS, "targetPrompt", routeCanGoBack, allowBack,
472
+ commandOptions, config, helperRuntimeTargets({
473
+ command: "install", global: scope.global,
474
+ }), "targetPrompt", routeCanGoBack, allowBack,
476
475
  { kit: kitName(values.kit) },
477
476
  ),
478
477
  },
@@ -482,12 +481,16 @@ async function install(commandOptions, allowBack = false) {
482
481
  if (!scopeWasPrompted) return BACK;
483
482
  }
484
483
  const selection = { ...scope, ...route };
484
+ if (specHasProfileTarget(selection.target) && !selection.global) {
485
+ throw new Error(ui("profileTargetsNeedGlobal"));
486
+ }
487
+ const profiles = specHasProfileTarget(selection.target) ? await loadPiProfilesQuiet() : [];
485
488
  const installSelections = splitTargetSpec(selection.target)
486
- .map((target) => ({ ...selection, target }));
489
+ .map((target) => materializeHelperTarget({ ...selection, target }, profiles));
487
490
 
488
491
  printSection(ui("installPlan"));
489
492
  for (const targetSelection of installSelections) {
490
- printPlan(installArgs(targetSelection), targetSelection.project);
493
+ printPlan(installArgs(targetSelection), targetSelection.project, targetSelection.envOverrides);
491
494
  }
492
495
  if (commandOptions.dryRun) {
493
496
  process.stdout.write(`\n${ui("dryRunFiles")}\n`);
@@ -504,12 +507,17 @@ async function install(commandOptions, allowBack = false) {
504
507
 
505
508
  for (const targetSelection of installSelections) {
506
509
  const cwd = targetSelection.project || process.cwd();
510
+ const runOptions = {
511
+ cwd,
512
+ envOverrides: targetSelection.envOverrides,
513
+ envUnset: targetSelection.envUnset,
514
+ };
507
515
  try {
508
- await runAkCommand(installArgs(targetSelection), { cwd });
516
+ await runAkCommand(installArgs(targetSelection), runOptions);
509
517
  } catch (error) {
510
518
  if (!requiresForceConsent(error)) throw error;
511
519
  warning(ui(targetSelection.global ? "globalForceWarning" : "projectForceWarning", {
512
- target: targetSelection.target,
520
+ target: targetSelection.helperTarget || targetSelection.target,
513
521
  }));
514
522
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
515
523
  error.message = `${error.message}\n${ui("forceNeedsConsent")}`;
@@ -521,8 +529,8 @@ async function install(commandOptions, allowBack = false) {
521
529
  }
522
530
  const forceArgs = installArgs(targetSelection, { force: true });
523
531
  printSection(ui("forceInstallPlan"));
524
- printPlan(forceArgs, targetSelection.project);
525
- await runAkCommand(forceArgs, { cwd });
532
+ printPlan(forceArgs, targetSelection.project, targetSelection.envOverrides);
533
+ await runAkCommand(forceArgs, runOptions);
526
534
  }
527
535
  }
528
536
  if (selection.project && !commandOptions.noSave) {
@@ -695,7 +703,9 @@ async function update(commandOptions, allowBack = false) {
695
703
  {
696
704
  key: "target",
697
705
  select: (values) => selectTarget(
698
- commandOptions, config, UPDATE_TARGETS, "updateTargetPrompt", routeCanGoBack, allowBack,
706
+ commandOptions, config, helperRuntimeTargets({
707
+ command: "update", global: scope.global,
708
+ }), "updateTargetPrompt", routeCanGoBack, allowBack,
699
709
  { kit: kitName(values.kit) },
700
710
  ),
701
711
  },
@@ -718,9 +728,13 @@ async function update(commandOptions, allowBack = false) {
718
728
  ...scope,
719
729
  channel,
720
730
  };
721
- const updateSelections = selection.global
722
- ? [selection]
723
- : splitTargetSpec(selection.target).map((target) => ({ ...selection, target }));
731
+ if (specHasProfileTarget(selection.target) && !selection.global) {
732
+ throw new Error(ui("profileTargetsNeedGlobal"));
733
+ }
734
+ const profiles = specHasProfileTarget(selection.target) ? await loadPiProfilesQuiet() : [];
735
+ const updateSelections = splitTargetSpec(selection.target).map((target) => (
736
+ materializeHelperTarget({ ...selection, target }, profiles)
737
+ ));
724
738
  if (selection.global) warning(ui("globalUpdateSafety"));
725
739
  if (channel === "beta") {
726
740
  const betaBinary = await prepareBetaBinary(commandOptions, { requiresPreview: true });
@@ -730,15 +744,24 @@ async function update(commandOptions, allowBack = false) {
730
744
  printSection(ui("updatePreview"));
731
745
  for (const targetSelection of updateSelections) {
732
746
  if (updateSelections.length > 1) {
733
- process.stdout.write(`${colorText(targetSelection.target, "target")}\n`);
747
+ process.stdout.write(`${colorText(targetSelection.helperTarget, "target")}\n`);
748
+ }
749
+ const previewArgs = updatePreviewArgsForRuntime(targetSelection);
750
+ if (previewArgs) {
751
+ await runAkCommand(previewArgs, {
752
+ cwd: targetSelection.project || process.cwd(),
753
+ envOverrides: targetSelection.envOverrides,
754
+ envUnset: targetSelection.envUnset,
755
+ });
734
756
  }
735
- await runAkCommand(updatePreviewArgs(targetSelection), {
736
- cwd: targetSelection.project || process.cwd(),
737
- });
738
757
  }
739
758
  printSection(ui("applyPlan"));
740
759
  for (const targetSelection of updateSelections) {
741
- printPlan(updateApplyArgs(targetSelection), targetSelection.project);
760
+ printPlan(
761
+ updateApplyArgsForRuntime(targetSelection),
762
+ targetSelection.project,
763
+ targetSelection.envOverrides,
764
+ );
742
765
  }
743
766
  if (commandOptions.dryRun) {
744
767
  process.stdout.write(`\n${ui("dryRunFiles")}\n`);
@@ -750,8 +773,10 @@ async function update(commandOptions, allowBack = false) {
750
773
  }
751
774
 
752
775
  for (const targetSelection of updateSelections) {
753
- await runAkCommand(updateApplyArgs(targetSelection), {
776
+ await runAkCommand(updateApplyArgsForRuntime(targetSelection), {
754
777
  cwd: targetSelection.project || process.cwd(),
778
+ envOverrides: targetSelection.envOverrides,
779
+ envUnset: targetSelection.envUnset,
755
780
  });
756
781
  }
757
782
  if (selection.project && !commandOptions.noSave) {
@@ -760,18 +785,34 @@ async function update(commandOptions, allowBack = false) {
760
785
  process.stdout.write(`${ui("updateComplete")}\n`);
761
786
  }
762
787
 
763
- function updateAllPreviewArgs(candidate, channel) {
764
- if (candidate.kind === "global" || candidate.kind === "profile") {
765
- return globalUpdatePreviewArgs(channel, candidate.runtimes, candidate.kit);
788
+ function updateAllCommandPlans(candidate, channel) {
789
+ const kit = candidate.kit;
790
+ if (candidate.kind === "project") {
791
+ if (usesKitRefresh(candidate.runtime)) {
792
+ return [{ preview: null, apply: kitRefreshArgs({
793
+ global: false, kit, channel, target: candidate.runtime,
794
+ }) }];
795
+ }
796
+ return [{
797
+ preview: projectUpdatePreviewArgs(candidate.path, candidate.runtime, channel, kit),
798
+ apply: projectUpdateApplyArgs(candidate.path, candidate.runtime, channel, kit),
799
+ }];
800
+ }
801
+ const { update, refresh } = splitRuntimesForUpdate(candidate.runtimes);
802
+ const plans = [];
803
+ if (update.length > 0) {
804
+ plans.push({
805
+ preview: globalUpdatePreviewArgs(channel, update, kit),
806
+ apply: globalUpdateApplyArgs(channel, update, kit),
807
+ });
766
808
  }
767
- return projectUpdatePreviewArgs(candidate.path, candidate.runtime, channel, candidate.kit);
768
- }
769
-
770
- function updateAllApplyArgs(candidate, channel) {
771
- if (candidate.kind === "global" || candidate.kind === "profile") {
772
- return globalUpdateApplyArgs(channel, candidate.runtimes, candidate.kit);
809
+ for (const runtime of refresh) {
810
+ plans.push({
811
+ preview: null,
812
+ apply: kitRefreshArgs({ global: true, kit, channel, target: runtime }),
813
+ });
773
814
  }
774
- return projectUpdateApplyArgs(candidate.path, candidate.runtime, channel, candidate.kit);
815
+ return plans;
775
816
  }
776
817
 
777
818
  async function askDeepScanRoots() {
@@ -858,6 +899,18 @@ function orderUpdateCandidates(candidates) {
858
899
  return [...candidates].sort((left, right) => priority[left.kind] - priority[right.kind]);
859
900
  }
860
901
 
902
+ async function loadPiProfilesQuiet() {
903
+ try {
904
+ const inventory = await runCapture(piProfileManager.binary, [
905
+ ...piProfileManager.prefixArgs,
906
+ "profiles", "list", "--json",
907
+ ]);
908
+ return parsePiProfileInventory(inventory.stdout);
909
+ } catch {
910
+ return [];
911
+ }
912
+ }
913
+
861
914
  async function loadUpdateInventory(commandOptions, deepScanRoots) {
862
915
  const registry = await withSpinner(
863
916
  ui("loadingRegistry"),
@@ -1012,11 +1065,15 @@ async function updateAll(commandOptions, allowBack = false) {
1012
1065
  printSection(ui("updateAllPreview"));
1013
1066
  for (const candidate of selected) {
1014
1067
  process.stdout.write(`\n${colorText(candidate.label, "target")}\n`);
1015
- await runAkCommand(updateAllPreviewArgs(candidate, channel), candidate);
1068
+ for (const plan of updateAllCommandPlans(candidate, channel)) {
1069
+ if (plan.preview) await runAkCommand(plan.preview, candidate);
1070
+ }
1016
1071
  }
1017
1072
  printSection(ui("updateAllApplyPlan"));
1018
1073
  for (const candidate of selected) {
1019
- printPlan(updateAllApplyArgs(candidate, channel), null, candidate.envOverrides);
1074
+ for (const plan of updateAllCommandPlans(candidate, channel)) {
1075
+ printPlan(plan.apply, null, candidate.envOverrides);
1076
+ }
1020
1077
  }
1021
1078
  if (commandOptions.dryRun) {
1022
1079
  process.stdout.write(`\n${ui("dryRunFiles")}\n`);
@@ -1032,7 +1089,9 @@ async function updateAll(commandOptions, allowBack = false) {
1032
1089
  total: selected.length,
1033
1090
  label: candidate.label,
1034
1091
  })}\n`);
1035
- await runAkCommand(updateAllApplyArgs(candidate, channel), candidate);
1092
+ for (const plan of updateAllCommandPlans(candidate, channel)) {
1093
+ await runAkCommand(plan.apply, candidate);
1094
+ }
1036
1095
  }
1037
1096
  process.stdout.write(`${ui("updateAllComplete")}\n`);
1038
1097
  return;
package/lib/args.mjs CHANGED
@@ -4,10 +4,16 @@ export const COMMANDS = new Set([
4
4
  export const INSTALL_TARGETS = new Set([
5
5
  "claude-code", "codex", "cursor", "dsh", "grok", "omp", "pi",
6
6
  ]);
7
- export const UPDATE_TARGETS = new Set(["claude-code", "codex", "cursor", "grok", "omp", "pi"]);
7
+ export const UPDATE_TARGETS = new Set([
8
+ "claude-code", "codex", "cursor", "dsh", "grok", "omp", "pi",
9
+ ]);
10
+ export const PROFILE_TARGETS = new Set(["pi-ak", "pi-omp"]);
11
+ export const PROFILE_RUNTIME = { "pi-ak": "pi", "pi-omp": "omp" };
12
+ export const HELPER_INSTALL_TARGETS = new Set([...INSTALL_TARGETS, ...PROFILE_TARGETS]);
13
+ export const HELPER_UPDATE_TARGETS = new Set([...UPDATE_TARGETS, ...PROFILE_TARGETS]);
8
14
  export const EXPORT_TARGETS = new Set(["agy", "portable"]);
9
- export const RUNTIME_TARGETS = new Set([...INSTALL_TARGETS]);
10
- export const KNOWN_TARGETS = new Set([...INSTALL_TARGETS, ...EXPORT_TARGETS]);
15
+ export const RUNTIME_TARGETS = new Set([...HELPER_INSTALL_TARGETS]);
16
+ export const KNOWN_TARGETS = new Set([...HELPER_INSTALL_TARGETS, ...EXPORT_TARGETS]);
11
17
  export const CHANNELS = new Set(["stable", "beta"]);
12
18
  export const LANGUAGES = new Set(["vi", "en"]);
13
19
  export const KITS = new Set(["engineer", "marketing"]);
@@ -21,6 +27,22 @@ export function targetSpecIsSupported(value, supportedTargets) {
21
27
  return targets.length > 0 && targets.every((target) => supportedTargets.has(target));
22
28
  }
23
29
 
30
+ export function specHasProfileTarget(value) {
31
+ return splitTargetSpec(value).some((target) => PROFILE_TARGETS.has(target));
32
+ }
33
+
34
+ export function helperRuntimeTargets({ command, global = false } = {}) {
35
+ const base = command === "update" ? UPDATE_TARGETS : INSTALL_TARGETS;
36
+ return global ? new Set([...base, ...PROFILE_TARGETS]) : new Set(base);
37
+ }
38
+
39
+ export function assertProfileTargetsAreGlobal(options) {
40
+ const selectedTarget = options.target || options.runtime;
41
+ if (specHasProfileTarget(selectedTarget) && options.project) {
42
+ throw new Error("pi-ak and pi-omp are global profile targets; use --global");
43
+ }
44
+ }
45
+
24
46
  export function validateForCommand(options) {
25
47
  const selectedTarget = options.target || options.runtime;
26
48
  const hasDiscoveryOptions = options.deepScanRoots.length > 0 ||
@@ -36,18 +58,20 @@ export function validateForCommand(options) {
36
58
  return;
37
59
  }
38
60
  if (options.command === "install") {
39
- if (selectedTarget && !targetSpecIsSupported(selectedTarget, INSTALL_TARGETS)) {
61
+ if (selectedTarget && !targetSpecIsSupported(selectedTarget, HELPER_INSTALL_TARGETS)) {
40
62
  throw new Error(`target ${selectedTarget} is export-only; use the export command`);
41
63
  }
64
+ assertProfileTargetsAreGlobal(options);
42
65
  if (options.out) throw new Error("--out is valid only with export");
43
66
  if (options.allowDowngrade) throw new Error("--allow-downgrade is valid only with self-update or update --binary-only");
44
67
  if (hasDiscoveryOptions) throw new Error("deep scan options are valid only with update-all");
45
68
  return;
46
69
  }
47
70
  if (options.command === "update") {
48
- if (selectedTarget && !targetSpecIsSupported(selectedTarget, UPDATE_TARGETS)) {
71
+ if (selectedTarget && !targetSpecIsSupported(selectedTarget, HELPER_UPDATE_TARGETS)) {
49
72
  throw new Error(`target ${selectedTarget} is not supported by ak update`);
50
73
  }
74
+ assertProfileTargetsAreGlobal(options);
51
75
  if (options.out) throw new Error("--out is valid only with export");
52
76
  if (options.allowDowngrade && !options.binaryOnly) {
53
77
  throw new Error("--allow-downgrade requires update --binary-only");
package/lib/commands.mjs CHANGED
@@ -51,6 +51,42 @@ export function updateApplyArgs(selection) {
51
51
  return [...updateBase(selection), "--yes", "--verbose"];
52
52
  }
53
53
 
54
+ export function usesKitRefresh(runtime) {
55
+ return runtime === "dsh";
56
+ }
57
+
58
+ export function kitRefreshArgs({
59
+ global, kit = "engineer", channel, target = "dsh",
60
+ }) {
61
+ return [
62
+ "kit",
63
+ "refresh",
64
+ kit,
65
+ "--target",
66
+ target,
67
+ ...(global ? ["--global"] : []),
68
+ "--channel",
69
+ channel,
70
+ "--yes",
71
+ "--verbose",
72
+ ];
73
+ }
74
+
75
+ export function updatePreviewArgsForRuntime(selection) {
76
+ return usesKitRefresh(selection.target) ? null : updatePreviewArgs(selection);
77
+ }
78
+
79
+ export function updateApplyArgsForRuntime(selection) {
80
+ return usesKitRefresh(selection.target) ? kitRefreshArgs(selection) : updateApplyArgs(selection);
81
+ }
82
+
83
+ export function splitRuntimesForUpdate(runtimes) {
84
+ return {
85
+ update: runtimes.filter((runtime) => !usesKitRefresh(runtime)),
86
+ refresh: runtimes.filter((runtime) => usesKitRefresh(runtime)),
87
+ };
88
+ }
89
+
54
90
  export function selfUpdateCheckArgs(channel) {
55
91
  return ["self-update", "--check", "--channel", channel];
56
92
  }
package/lib/i18n.mjs CHANGED
@@ -32,7 +32,9 @@ const messages = {
32
32
  targetPrompt: "Which coding runtimes should receive {kit}?",
33
33
  updateTargetPrompt: "Which installed runtimes should update {kit}?",
34
34
  allRuntimes: "All supported runtimes",
35
- dshUpdateUnsupported: "dsh not supported by ak update",
35
+ piAkProfile: "pi-ak (Pi profile)",
36
+ piOmpProfile: "pi-omp (OMP profile)",
37
+ profileTargetsNeedGlobal: "pi-ak and pi-omp are global profile targets; use --global",
36
38
  exportTargetPrompt: "Export creates a copy; it does not install a runtime. Choose a format:",
37
39
  savedTargetNeedsUpdateRuntime: "Saved project target {target} is install-only. Update supports {supported}. Re-run with --runtime <target> or choose interactively.",
38
40
  agyExport: "agy (global-only export)",
@@ -167,7 +169,9 @@ const messages = {
167
169
  targetPrompt: "Các runtime nào sẽ nhận {kit}?",
168
170
  updateTargetPrompt: "Các runtime nào sẽ cập nhật {kit}?",
169
171
  allRuntimes: "Tất cả runtime được hỗ trợ",
170
- dshUpdateUnsupported: "dsh — ak update chưa hỗ trợ",
172
+ piAkProfile: "pi-ak (profile Pi)",
173
+ piOmpProfile: "pi-omp (profile OMP)",
174
+ profileTargetsNeedGlobal: "pi-ak và pi-omp là profile global; dùng --global",
171
175
  exportTargetPrompt: "Export tạo một bản sao, không cài vào runtime. Chọn dạng export:",
172
176
  savedTargetNeedsUpdateRuntime: "Target đã lưu của project là {target} và chỉ dùng cho install. Update chỉ hỗ trợ {supported}. Hãy chạy lại với --runtime <target> hoặc chọn lại trong chế độ interactive.",
173
177
  agyExport: "agy (chỉ export global)",
@@ -1,6 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { extname, isAbsolute, resolve, win32 } from "node:path";
3
+ import { extname, isAbsolute, join, resolve, win32 } from "node:path";
4
+ import { PROFILE_RUNTIME } from "./args.mjs";
4
5
 
5
6
  function validPath(value) {
6
7
  return typeof value === "string" && (isAbsolute(value) || win32.isAbsolute(value));
@@ -100,3 +101,46 @@ export function classifyPiProfiles(profiles) {
100
101
  )),
101
102
  };
102
103
  }
104
+
105
+ export function isProfileTarget(target) {
106
+ return Object.hasOwn(PROFILE_RUNTIME, target);
107
+ }
108
+
109
+ export function akRuntimeForHelperTarget(target) {
110
+ return PROFILE_RUNTIME[target] || target;
111
+ }
112
+
113
+ export function defaultProfileProcessOptions(id, { home = homedir() } = {}) {
114
+ if (id === "pi-ak") {
115
+ const agentDir = join(home, ".pi", "profiles", "pi-ak");
116
+ return piProfileProcessOptions({
117
+ runtime: "pi", agentDir, sessionDir: join(agentDir, "sessions"),
118
+ });
119
+ }
120
+ if (id === "pi-omp") {
121
+ return piProfileProcessOptions({
122
+ runtime: "omp",
123
+ agentDir: join(home, ".omp", "profiles", "pi-omp", "agent"),
124
+ sessionDir: null,
125
+ });
126
+ }
127
+ return { envOverrides: {}, envUnset: [] };
128
+ }
129
+
130
+ export function resolveProfileProcessOptions(id, profiles = [], { home = homedir() } = {}) {
131
+ const found = profiles.find((profile) => profile.id === id);
132
+ return found ? piProfileProcessOptions(found) : defaultProfileProcessOptions(id, { home });
133
+ }
134
+
135
+ export function materializeHelperTarget(selection, profiles = [], { home = homedir() } = {}) {
136
+ const helperTarget = selection.target;
137
+ const env = isProfileTarget(helperTarget)
138
+ ? resolveProfileProcessOptions(helperTarget, profiles, { home })
139
+ : { envOverrides: {}, envUnset: [] };
140
+ return {
141
+ ...selection,
142
+ helperTarget,
143
+ target: akRuntimeForHelperTarget(helperTarget),
144
+ ...env,
145
+ };
146
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thieung/agentkit-helper",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Community helper for installing and updating AgentKit kits by runtime and channel",
5
5
  "type": "module",
6
6
  "bin": {