@manohub/kit 0.6.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 (36) hide show
  1. package/CONTRACT.md +597 -0
  2. package/README.md +158 -0
  3. package/bin/kit.mjs +118 -0
  4. package/dist/composables/use-client-pagination.d.ts +43 -0
  5. package/dist/composables/use-client-pagination.js +28 -0
  6. package/dist/entry/create-query-client.d.ts +8 -0
  7. package/dist/entry/create-query-client.js +14 -0
  8. package/dist/entry/create-sub-app.d.ts +81 -0
  9. package/dist/entry/create-sub-app.js +111 -0
  10. package/dist/entry/index.d.ts +4 -0
  11. package/dist/entry/index.js +13 -0
  12. package/dist/entry/initial-guard.d.ts +12 -0
  13. package/dist/entry/initial-guard.js +23 -0
  14. package/dist/index.d.ts +19 -0
  15. package/dist/index.js +7 -0
  16. package/dist/providers/locale-detection.d.ts +35 -0
  17. package/dist/providers/locale-detection.js +44 -0
  18. package/dist/providers/setup-i18n.d.ts +50 -0
  19. package/dist/providers/setup-i18n.js +42 -0
  20. package/dist/services/app-container.d.ts +31 -0
  21. package/dist/services/app-container.js +22 -0
  22. package/dist/services/app-context.d.ts +4 -0
  23. package/dist/services/app-context.js +11 -0
  24. package/dist/services/index.d.ts +7 -0
  25. package/package.json +68 -0
  26. package/skills/README.md +78 -0
  27. package/skills/install.mjs +299 -0
  28. package/skills/kit/SKILL.md +85 -0
  29. package/skills/kit/references/adoption.md +170 -0
  30. package/skills/kit/references/contract-index.md +76 -0
  31. package/skills/kit-dev/SKILL.md +117 -0
  32. package/skills/kit-dev/references/page-recipes.md +350 -0
  33. package/skills/kit-dev/references/style-rules.md +47 -0
  34. package/skills/kit-migrate/SKILL.md +125 -0
  35. package/skills/kit-migrate/references/migration-map.md +294 -0
  36. package/skills/kit-migrate/references/migration-playbook.md +188 -0
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 语言探测与持久化 —— 替代 i18next 时代的 `i18next-browser-languagedetector`。
3
+ *
4
+ * 优先级(高 → 低):
5
+ *
6
+ * 1. 宿主经 micro-app 全局数据下发的 `language`(由 `createSubApp` 直接写入 i18n 实例,
7
+ * 不参与本模块的探测顺序,只在探测结果之上覆盖);
8
+ * 2. localStorage 里上次选定/持久化的语言(key 见 `DEFAULT_LOCALE_STORAGE_KEY`);
9
+ * 3. `navigator.language`;
10
+ * 4. 兜底 `'en'` —— 与迁移前 `fallbackLng: 'en'` 逐字一致。
11
+ *
12
+ * 归一化规则与迁移前逐字一致:取 `-` 之前的主语言子标签,`zh` 判为中文,**其余一律 `'en'`**
13
+ * (`zh-CN` / `zh-Hans` → `zh`,`en-US` / `ja` → `en`)。
14
+ */
15
+ /** 语言持久化 key。与 i18next 时代的 `i18nextLng` **不同**(属有意行为,老偏好不再生效) */
16
+ export declare const DEFAULT_LOCALE_STORAGE_KEY = "manohub:locale";
17
+ /** 兜底语言(迁移前 `fallbackLng: 'en'` 的等价物) */
18
+ export declare const DEFAULT_FALLBACK_LOCALE = "en";
19
+ /** 本包支持的语言(其余一律归一化到 `en`) */
20
+ export type AppLocale = 'zh' | 'en';
21
+ /**
22
+ * 归一化任意语言标识为 `'zh' | 'en'`。
23
+ *
24
+ * 与迁移前 `String(data.language).split('-')[0] === 'zh' ? 'zh' : 'en'` 行为一致:
25
+ * 未知语言(含 `undefined` / 空串)都归到 `'en'`。
26
+ */
27
+ export declare function normalizeLocale(input: unknown): AppLocale;
28
+ /** 读取持久化语言;无值或不可用(隐私模式抛错)时返回 null */
29
+ export declare function readStoredLocale(storageKey?: string): AppLocale | null;
30
+ /** 写入持久化语言;失败静默(不影响当前会话的语言切换) */
31
+ export declare function persistLocale(locale: AppLocale, storageKey?: string): void;
32
+ /** 读取浏览器语言;无 `navigator`(非浏览器环境)时返回 null */
33
+ export declare function detectNavigatorLocale(): AppLocale | null;
34
+ /** 初始语言:localStorage → navigator → `'en'` */
35
+ export declare function detectInitialLocale(storageKey?: string): AppLocale;
@@ -0,0 +1,44 @@
1
+ const DEFAULT_LOCALE_STORAGE_KEY = "manohub:locale";
2
+ const DEFAULT_FALLBACK_LOCALE = "en";
3
+ function normalizeLocale(input) {
4
+ return String(input ?? "").split("-")[0]?.toLowerCase() === "zh" ? "zh" : "en";
5
+ }
6
+ function readStoredLocale(storageKey = DEFAULT_LOCALE_STORAGE_KEY) {
7
+ if (typeof localStorage === "undefined") {
8
+ return null;
9
+ }
10
+ try {
11
+ const raw = localStorage.getItem(storageKey);
12
+ return raw ? normalizeLocale(raw) : null;
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+ function persistLocale(locale, storageKey = DEFAULT_LOCALE_STORAGE_KEY) {
18
+ if (typeof localStorage === "undefined") {
19
+ return;
20
+ }
21
+ try {
22
+ localStorage.setItem(storageKey, locale);
23
+ } catch {
24
+ }
25
+ }
26
+ function detectNavigatorLocale() {
27
+ if (typeof navigator === "undefined") {
28
+ return null;
29
+ }
30
+ const language = navigator.language;
31
+ return language ? normalizeLocale(language) : null;
32
+ }
33
+ function detectInitialLocale(storageKey = DEFAULT_LOCALE_STORAGE_KEY) {
34
+ return readStoredLocale(storageKey) ?? detectNavigatorLocale() ?? DEFAULT_FALLBACK_LOCALE;
35
+ }
36
+ export {
37
+ DEFAULT_FALLBACK_LOCALE,
38
+ DEFAULT_LOCALE_STORAGE_KEY,
39
+ detectInitialLocale,
40
+ detectNavigatorLocale,
41
+ normalizeLocale,
42
+ persistLocale,
43
+ readStoredLocale
44
+ };
@@ -0,0 +1,50 @@
1
+ import type { App } from 'vue';
2
+ import { type AppLocale } from './locale-detection';
3
+ export interface SetupI18nOptions {
4
+ /**
5
+ * vue-i18n 消息形态:`{ en: { ... }, zh: { ... } }`。
6
+ *
7
+ * ⚠️ 与 i18next 的 `{ en: { translation: {...} } }` 不同,**没有 `translation` 包装层**
8
+ * (迁移时为破坏性变更)。
9
+ */
10
+ messages: Record<string, Record<string, unknown>>;
11
+ /** 缺省 `'en'`(等价于迁移前的 `fallbackLng: 'en'`) */
12
+ fallbackLocale?: string;
13
+ /** 语言持久化 key,缺省 `manohub:locale` */
14
+ storageKey?: string;
15
+ }
16
+ /**
17
+ * 全应用唯一的 i18n 实例(vue-i18n Composer 语义:`i18n.global.t` / `.te` / `.locale`)。
18
+ *
19
+ * ## 单实例(本包是唯一的创建者)
20
+ *
21
+ * 由本包创建并 `app.use`,消费方**不得自行 `createI18n`** —— 全应用只有一个实例,因此:
22
+ *
23
+ * - 业务组件与组件树外的服务拿到的是同一份词条与同一个当前语言;
24
+ * - 不存在「谁后注册谁生效」的覆盖不确定性,也不会两个实例重复注册同名全局组件;
25
+ * - 语言只需切换一处(本实例的 `locale`)。
26
+ *
27
+ * 模块顶层即创建(而不是等 `setupI18n` 才创建),换来两个确定性:
28
+ *
29
+ * 1. `i18n` 出口永远是同一个非空实例,消费方无需处理 `null`;
30
+ * 2. 宿主反复 mount / unmount(micro-app 重挂)时实例不漂移、消息不丢。
31
+ */
32
+ declare const i18n: import("vue-i18n").I18n<{}, {}, {}, AppLocale, false>;
33
+ export type AppI18n = typeof i18n;
34
+ export { i18n };
35
+ /**
36
+ * 应用挂载时装配 i18n(幂等)。
37
+ *
38
+ * 重复调用**只补消息、不重建实例**:宿主重挂子应用时会再次走到这里,重建会导致
39
+ * 前一次持有的引用(组件树外的服务、已注入的组件)指向旧实例。
40
+ */
41
+ export declare function setupI18n(app: App, options: SetupI18nOptions): AppI18n;
42
+ /** 读取当前语言 */
43
+ export declare function getLocale(): string;
44
+ /**
45
+ * 切换语言:写实例 + 持久化。
46
+ *
47
+ * 这是**唯一的语言切换入口**(宿主下发经 `createSubApp` 调用)。因为全应用共用一个
48
+ * vue-i18n 实例,切换一处即全局生效,不需要额外同步任何一侧。
49
+ */
50
+ export declare function applyLocale(input: unknown, storageKey?: string): AppLocale;
@@ -0,0 +1,42 @@
1
+ import { createI18n } from "vue-i18n";
2
+ import { normalizeLocale, DEFAULT_FALLBACK_LOCALE, detectInitialLocale, persistLocale, DEFAULT_LOCALE_STORAGE_KEY } from "./locale-detection.js";
3
+ const i18n = createI18n({
4
+ legacy: false,
5
+ globalInjection: false,
6
+ locale: detectInitialLocale(),
7
+ fallbackLocale: DEFAULT_FALLBACK_LOCALE,
8
+ messages: {},
9
+ // 迁移前 i18next 缺 key 是静默返回 key;vue-i18n 默认会刷控制台警告,须显式关掉
10
+ missingWarn: false,
11
+ fallbackWarn: false,
12
+ // 缺 key 时回退 key 本身:与迁移前 i18next 的静默行为一致(不抛错、不刷警告)。
13
+ // 显式写出来是为了表明「这里没有别的兜底来源」—— 词条要么由应用传全,要么就是没翻。
14
+ missing: (_locale, key) => String(key)
15
+ });
16
+ function setupI18n(app, options) {
17
+ const { messages, fallbackLocale = DEFAULT_FALLBACK_LOCALE, storageKey } = options;
18
+ for (const [locale, message] of Object.entries(messages)) {
19
+ i18n.global.mergeLocaleMessage(locale, message);
20
+ }
21
+ i18n.global.fallbackLocale.value = fallbackLocale;
22
+ setLocale(detectInitialLocale(storageKey), storageKey);
23
+ app.use(i18n);
24
+ return i18n;
25
+ }
26
+ function getLocale() {
27
+ return i18n.global.locale.value;
28
+ }
29
+ function applyLocale(input, storageKey) {
30
+ return setLocale(normalizeLocale(input), storageKey);
31
+ }
32
+ function setLocale(locale, storageKey) {
33
+ i18n.global.locale.value = locale;
34
+ persistLocale(locale, storageKey ?? DEFAULT_LOCALE_STORAGE_KEY);
35
+ return locale;
36
+ }
37
+ export {
38
+ applyLocale,
39
+ getLocale,
40
+ i18n,
41
+ setupI18n
42
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 子应用**锚容器**解析 —— `createSubApp` 统一包裹的**作用域锚点**。
3
+ *
4
+ * 容器在运行期有两个名字:类名 `app-container`(本包自己的命名前缀,仅供调试自查)与属性
5
+ * `data-manohub-ui`(**跨包唯一作用域锚**)。后者才是契约:`@manohub/theme` 的主题令牌、
6
+ * `@manohub/ui` 的组件令牌、以及 ui 服务层的宿主解析,
7
+ * 全认这一个属性。取「库品牌属性」而不是本包的 `app-` 私有类名,是为了让主题包不欠本包
8
+ * 一个命名空间 —— 几个包并列安装,谁也不该去锚别人前缀下的名字。
9
+ *
10
+ * 因此**任何要落出组件树的内容都必须落在锚容器里**:落到 `body` 上的节点既拿不到令牌
11
+ * (`var(--ui-*)` 全部失效 ⇒ 组件「样式全丢」),也匹配不到被 `scopecss` 前缀化的应用侧样式。
12
+ *
13
+ * 取值优先级:**当前应用挂载时记录的容器**(精确,门户里多应用并存时不会串到别的应用)
14
+ * → 文档里第一个 `[data-manohub-ui]`(未经 `createSubApp` 的裸挂载场景)
15
+ * → `document.body`(兜底,行为与「没有本函数」时一致,不会更差)。
16
+ *
17
+ * 与 `@manohub/ui` 服务层的关系:本包**不依赖** ui(几个包并列),
18
+ * 入口编排只在容器上写一个 `data-manohub-ui` 属性;ui 的服务层据此解析宿主。
19
+ * 应用若要自绘浮层,用本函数取容器(比 `document.querySelector('[data-manohub-ui]')` 精确)。
20
+ */
21
+ export declare function resolveAppContainer(): Element;
22
+ /**
23
+ * 同 `resolveAppContainer`,但返回**选择器字符串** —— 给「只吃字符串宿主」的场景用。
24
+ *
25
+ * 为什么不能直接给 Element:有的上游件把宿主当选择器解析(`document.querySelector(host)`),
26
+ * 传 Element 会落到「非法选择器」那条路(实测会抛 `SyntaxError` 并中断整页渲染)。
27
+ * 给字符串又要避免「门户里多应用并存时串到第一个 `[data-manohub-ui]`」,故给容器补一个
28
+ * **进程内唯一 id** 再用 `#id` 定位;容器已有 id 时直接用它的。
29
+ * 兜底到 `document.body` 时返回 `"body"`(有些上游对该值有专门分支,不查 DOM)。
30
+ */
31
+ export declare function resolveAppContainerSelector(): string;
@@ -0,0 +1,22 @@
1
+ import { getCurrentAppContainer } from "./app-context.js";
2
+ function resolveAppContainer() {
3
+ return getCurrentAppContainer() ?? document.querySelector("[data-manohub-ui]") ?? document.body;
4
+ }
5
+ const containerIds = /* @__PURE__ */ new WeakMap();
6
+ let containerSeq = 0;
7
+ function resolveAppContainerSelector() {
8
+ const container = resolveAppContainer();
9
+ if (container === document.body) return "body";
10
+ if (container.id) return `#${container.id}`;
11
+ let id = containerIds.get(container);
12
+ if (!id) {
13
+ id = `ak-app-container-${containerSeq += 1}`;
14
+ containerIds.set(container, id);
15
+ container.id = id;
16
+ }
17
+ return `#${id}`;
18
+ }
19
+ export {
20
+ resolveAppContainer,
21
+ resolveAppContainerSelector
22
+ };
@@ -0,0 +1,4 @@
1
+ import type { App } from 'vue';
2
+ export declare function setCurrentApp(app: App | null, container?: Element | null): void;
3
+ export declare function getCurrentApp(): App | null;
4
+ export declare function getCurrentAppContainer(): Element | null;
@@ -0,0 +1,11 @@
1
+ let currentContainer = null;
2
+ function setCurrentApp(app, container = null) {
3
+ currentContainer = container;
4
+ }
5
+ function getCurrentAppContainer() {
6
+ return currentContainer;
7
+ }
8
+ export {
9
+ getCurrentAppContainer,
10
+ setCurrentApp
11
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 本包内部的容器记账(**不是**对外服务层)。
3
+ *
4
+ * 命令式反馈服务(消息提示 / 确认框 / 加载遮罩)自 0.6.0 起归 `@manohub/ui` 的服务层;
5
+ * 本目录只剩「本应用容器是哪一个」这一件事 —— 入口编排创建容器时记下它,浮层与诊断按需取用。
6
+ */
7
+ export { resolveAppContainer, resolveAppContainerSelector } from './app-container';
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@manohub/kit",
3
+ "version": "0.6.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "子应用入口编排层:createSubApp(作用域容器与宿主锚点、pinia/路由/vue-query 装配、宿主挂载协议)、i18n 单实例与语言探测,外加随包分发的接入契约(CONTRACT.md,五层闭集条款 + 自检清单)与三个 AI 技能包。本包**零样式产物**:设计令牌(值)在 @manohub/theme,组件(类与行为)在 @manohub/ui,两者由消费方直接引入。",
7
+ "license": "UNLICENSED",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://git.iec.io/gsp-cloud-ds/dip/ibp/aihub/frontend-infra.git",
11
+ "directory": "packages/kit"
12
+ },
13
+ "publishConfig": {
14
+ "registry": "https://registry.npmjs.org/",
15
+ "access": "public"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "skills",
20
+ "bin",
21
+ "CONTRACT.md",
22
+ "README.md"
23
+ ],
24
+ "bin": {
25
+ "kit": "bin/kit.mjs"
26
+ },
27
+ "sideEffects": false,
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
32
+ },
33
+ "./entry": {
34
+ "types": "./dist/entry/index.d.ts",
35
+ "import": "./dist/entry/index.js"
36
+ },
37
+ "./CONTRACT.md": "./CONTRACT.md"
38
+ },
39
+ "main": "./dist/index.js",
40
+ "module": "./dist/index.js",
41
+ "types": "./dist/index.d.ts",
42
+ "scripts": {
43
+ "build": "node ../../scripts/clean-dist.mjs && vue-tsc -p tsconfig.build.json && vite build",
44
+ "type-check": "vue-tsc --noEmit -p tsconfig.json",
45
+ "test:unit": "vitest run",
46
+ "test:watch": "vitest"
47
+ },
48
+ "peerDependencies": {
49
+ "@tanstack/vue-query": "^5.0.0",
50
+ "pinia": "^4.0.0",
51
+ "vue": "^3.5.0",
52
+ "vue-i18n": "^11.0.0",
53
+ "vue-router": "^4.6.0"
54
+ },
55
+ "devDependencies": {
56
+ "@tanstack/vue-query": "catalog:",
57
+ "@vitejs/plugin-vue-jsx": "catalog:",
58
+ "jsdom": "^25.0.1",
59
+ "pinia": "catalog:",
60
+ "typescript": "catalog:",
61
+ "vite": "catalog:",
62
+ "vitest": "^3.2.7",
63
+ "vue": "catalog:",
64
+ "vue-i18n": "catalog:",
65
+ "vue-router": "catalog:",
66
+ "vue-tsc": "catalog:"
67
+ }
68
+ }
@@ -0,0 +1,78 @@
1
+ # @manohub/kit 技能包(随包分发)
2
+
3
+ 本目录是随 npm 包一起发布的 AI 代理技能包,供**消费方**(接入了本包的子应用工程)使用。
4
+
5
+ 契约条款的唯一事实源是包内 `CONTRACT.md`。技能只负责**按场景把代理导到正确的流程与查表入口**。
6
+
7
+ ## 技能里为什么不写条款
8
+
9
+ `CONTRACT.md` 是唯一的合规判据,它有两条性质:
10
+
11
+ 1. **每条条款都是闭集** —— 白名单(如 L1-5 的属性白名单)或逐字清单(如 L3-1 的允许 import 源),
12
+ 看一眼就能判定「符合 / 不符合」,不需要解释。
13
+ 2. **§7 配了一份自检清单** —— 全是能直接回答的问句,收工前逐条过一遍即可。
14
+
15
+ 所以技能里的**条款式描述一律算缺陷**:它必然与契约漂移,而契约才是唯一裁判。技能只写两件事 ——
16
+ **「读契约第 X 节」** 与 **「过 §7 自检清单」**。
17
+
18
+ ## 三个技能
19
+
20
+ | 技能 | 职责 | 可独立调用 |
21
+ |---|---|---|
22
+ | `kit` | 入口编排:识别意图 → 调 `kit-migrate` 或 `kit-dev`(另有「从零建新应用」导到接入 SOP);承载跨场景硬约束 | 是(子技能也可直接调) |
23
+ | `kit-migrate` | 存量应用改造到「`@manohub/ui` 组件 + `@manohub/kit` 骨架」(清理 `App*` 旧组件名、`.ak-*` 样式、直连底层组件库的写法):按契约逐层盘点 → 划批次 → 逐文件替换 → 逐层收口 → 验收;支持**只迁一部分**(典型是「只迁页面骨架」) | 是 |
24
+ | `kit-dev` | 按骨架层规范做日常页面开发:选模板 / 选组件与 prop / 守样式纪律 / 缺件处置 / 自检 | 是 |
25
+
26
+ ## 安装到消费方技能目录
27
+
28
+ 在消费方**工程根**执行(缺省落 `.codebuddy/skills/`);`kit` 是本包的命令入口(`bin`),
29
+ npm 消费方把 `pnpm exec` 换成 `npx`:
30
+
31
+ ```bash
32
+ pnpm exec kit install
33
+
34
+ pnpm exec kit install --also-claude # 同时落 .claude/skills/
35
+ pnpm exec kit install --target .x/skills
36
+ pnpm exec kit install --dry-run # 只预览不落盘
37
+ ```
38
+
39
+ 输出示例(每个技能都会报告内容有没有变):
40
+
41
+ ```text
42
+ [kit 技能安装]
43
+ 源:…/node_modules/@manohub/kit/skills
44
+ 目标:…/.codebuddy/skills
45
+ ….codebuddy/skills/kit —— 更新 2(共 3 个文件)
46
+ ….codebuddy/skills/kit-migrate —— 已是包内最新(内容无变化,共 3 个文件)
47
+ 完成:技能目录已与包内内容对齐(包升级后重跑本命令即刷新)
48
+ ```
49
+
50
+ 安装器是幂等的:每次执行**先清理同名技能目录再整体复制**,所以包升级后重跑一次即刷新到新版内容。
51
+ 等价写法(老脚本/钩子里可用):`node node_modules/@manohub/kit/skills/install.mjs`。
52
+ 它只管理 `kit` / `kit-migrate` / `kit-dev` 这三个目录,不触碰目标目录下的其它内容。
53
+ 每次执行都会报告**技能内容有没有变**(「更新 N / 新增 N」=本次升级改了技能、已生效;
54
+ 「已是包内最新」=本次升级没动技能包),这样消费方不必靠猜「我的技能是不是旧的」。
55
+
56
+ ## 维护约定(改本目录时遵守)
57
+
58
+ - 引用规范一律写**消费方视角**路径:`node_modules/@manohub/kit/CONTRACT.md` §x.y。
59
+ 禁止出现本机绝对路径、worktree 路径、仓库内相对路径(技能落盘后与包目录分离,这些路径会失效)。
60
+ - **不复述条款**:不抄白名单、不抄件名清单、不抄 prop 词表、不抄值。
61
+ 需要时写「读契约 §x.y」或指向读取路径(见契约 §0 权威源表)。凡本目录内出现判据式描述,视为缺陷。
62
+ - `SKILL.md` 只放流程与硬约束(控制在 5k 词内);查表内容(替换映射、场景配方)放各自的 `references/`,按需加载。
63
+ - 三个技能的 `description` 触发条件互不重叠,否则代理会命中错的那个
64
+ (`kit` 只在「意图还没落到具体任务」时命中,具体任务交给两个子技能)。
65
+ - 技能引用的 `references/` 文件必须真实存在,文件名改动要同步 `SKILL.md`
66
+ (已由 `verify-pack` 的 `checkSkillPack` 机械校验,跨技能引用 `../<skill>/references/…` 同样校验)。
67
+ - 技能目录下的每个文件都必须真进 tarball(`references/` 漏发时「查表」会整体失效,也已机械校验)。
68
+ - 技能引用的契约章节必须真实存在(改契约标题即需同步本目录,`verify-pack` 会校验章节号可解析)。
69
+ - 技能与文档里的命令一律写**消费方命令** `pnpm exec kit install`(npm 注一句 `npx`)。
70
+ - 本包**只有 `install` 一个子命令**(0.6.0 起 `kit lint` 及其四个校验子命令已下线)。
71
+ `bin/` 是工具脚本、没有自测,改后手工跑一次确认参数透传(如 `--dry-run`),
72
+ `verify-pack` 校验 `bin/kit.mjs` 随包分发。
73
+ - 改动后跑门禁与打包校验(在仓库根):
74
+
75
+ ```bash
76
+ pnpm --filter @manohub/kit test:unit # 入口编排契约单测
77
+ pnpm release:check # build + verify-pack(含技能包结构/引用校验)
78
+ ```