@d3ara1n/pi-model-roles 0.2.0 → 0.3.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/README.md CHANGED
@@ -100,6 +100,11 @@ if (resolved.model) {
100
100
 
101
101
  // Reverse lookup
102
102
  roles.findRoleByModel("anthropic/claude-opus-4"); // "heavy"
103
+
104
+ // "Which role is the currently-active model?" — recognizes the default role
105
+ // even when all roles are model=null (the common case), so callers (e.g.
106
+ // pi-scout's router) have a real baseline instead of "unknown".
107
+ roles.getCurrentRole("anthropic/claude-sonnet-4");
103
108
  ```
104
109
 
105
110
  ## License
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@d3ara1n/pi-model-roles",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
+ "type": "module",
4
5
  "description": "Model role configuration library for pi extensions — defines named model roles and resolves them to Model instances",
5
6
  "main": "src/index.ts",
6
7
  "types": "src/types.ts",
package/src/api.ts CHANGED
@@ -118,6 +118,21 @@ export function initModelRolesAPI(modelRegistry: any, currentModel: any, cwd?: s
118
118
  }
119
119
  return undefined;
120
120
  },
121
+
122
+ getCurrentRole(modelId: string): string | undefined {
123
+ const roles = getConfig().roles;
124
+ // 1. Exact match wins: a role explicitly bound to this model.
125
+ const exact = Object.entries(roles).find(([, c]) => c.model === modelId)?.[0];
126
+ if (exact) return exact;
127
+ // 2. Default role — when model=null it transparently uses the current
128
+ // model, so it is the meaningful base in the all-null config.
129
+ const defaultName = getConfig().defaultRole ?? "default";
130
+ const defaultConfig = roles[defaultName];
131
+ if (defaultConfig && !defaultConfig.model) return defaultName;
132
+ // 3. First model=null role (reached only when default is bound elsewhere).
133
+ const nullRole = Object.entries(roles).find(([, c]) => !c.model)?.[0];
134
+ return nullRole;
135
+ },
121
136
  };
122
137
 
123
138
  // Store on globalThis — survives module identity mismatches
package/src/defaults.ts CHANGED
@@ -13,22 +13,22 @@ import type { RoleConfig } from "./types.ts";
13
13
  export const BUILTIN_DEFAULT_ROLES: Record<string, RoleConfig> = {
14
14
  default: {
15
15
  model: null,
16
- description: "常规开发任务:编写新功能、修改现有代码、代码审查、添加测试、一般性调试、文件级别的修改",
16
+ description: "General development tasks: writing new features, modifying existing code, code review, adding tests, routine debugging, file-level changes",
17
17
  thinking: "medium",
18
18
  },
19
19
  heavy: {
20
20
  model: null,
21
- description: "需要深度思考的任务:跨文件重构、架构设计、复杂 bug 调试、性能优化、安全分析、数据库 schema 变更、涉及多个模块的迁移",
21
+ description: "Tasks requiring deep thinking: cross-file refactoring, architecture design, complex bug debugging, performance optimization, security analysis, database schema changes, multi-module migrations",
22
22
  thinking: "high",
23
23
  },
24
24
  fast: {
25
25
  model: null,
26
- description: "简单确定性的任务:一行修改、格式调整、简单问答、文档查阅、git 操作、确认类回复",
26
+ description: "Simple deterministic tasks: one-line edits, formatting tweaks, simple Q&A, doc lookups, git operations, confirmation replies",
27
27
  thinking: "low",
28
28
  },
29
29
  utility: {
30
30
  model: null,
31
- description: "轻量辅助:模型路由、commit 生成、标题摘要等(hidden",
31
+ description: "Lightweight utilities: model routing, commit generation, title/summary, etc. (hidden)",
32
32
  thinking: "off",
33
33
  hidden: true,
34
34
  },
package/src/types.ts CHANGED
@@ -69,9 +69,30 @@ export interface ModelRolesAPI {
69
69
  /** Get all non-hidden roles (for displaying to users). */
70
70
  getVisibleRoles(): Record<string, RoleConfig>;
71
71
  /**
72
- * Given a model identifier (e.g. "anthropic/claude-sonnet-4"),
73
- * find the first role name that uses that model.
74
- * Skips roles with model=null.
72
+ * Given a model identifier (e.g. "anthropic/claude-sonnet-4"), find the first
73
+ * role name that is explicitly bound to that model. Skips roles with
74
+ * model=null (they have no declared model to match).
75
+ *
76
+ * This is a pure declared-binding lookup — it does NOT infer "the current
77
+ * role". For that, use {@link getCurrentRole}.
78
+ *
79
+ * Returns undefined if no role declares this model.
75
80
  */
76
81
  findRoleByModel(modelId: string): string | undefined;
82
+
83
+ /**
84
+ * Identify which role the currently-active model belongs to. Resolution order:
85
+ * 1. Exact match — a role explicitly bound to `modelId`
86
+ * ({@link findRoleByModel}).
87
+ * 2. The configured default role — if it is model=null, the current model
88
+ * is transparently "the default role's model", so default is the
89
+ * meaningful base. This covers the common config where ALL roles are
90
+ * model=null (use current model): without this step, whichever null
91
+ * role iterates first would win — often a hidden utility role.
92
+ * 3. The first model=null role found (it too transparently uses the current
93
+ * model); only reached when the default role is explicitly bound to a
94
+ * different model.
95
+ * Returns undefined only if no role matches by any rule.
96
+ */
97
+ getCurrentRole(modelId: string): string | undefined;
77
98
  }