agents-inc 0.175.0 → 0.176.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 (81) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/{chunk-EGC7PNDI.js → chunk-3F5LT433.js} +138 -41
  3. package/dist/chunk-3F5LT433.js.map +1 -0
  4. package/dist/chunk-5CFHP6Z5.js +109 -0
  5. package/dist/chunk-5CFHP6Z5.js.map +1 -0
  6. package/dist/{chunk-MFCZARV2.js → chunk-B7AVWSK2.js} +18 -8
  7. package/dist/chunk-B7AVWSK2.js.map +1 -0
  8. package/dist/{chunk-6HJVH27C.js → chunk-C2BSCEDN.js} +2 -2
  9. package/dist/{chunk-SNXEYAFP.js → chunk-CKBWEWHZ.js} +60 -30
  10. package/dist/chunk-CKBWEWHZ.js.map +1 -0
  11. package/dist/{chunk-SI2LLEIR.js → chunk-EC3JXT23.js} +4 -4
  12. package/dist/{chunk-YM3VEDAP.js → chunk-ENET4IF6.js} +440 -192
  13. package/dist/chunk-ENET4IF6.js.map +1 -0
  14. package/dist/{chunk-ICHWAFAC.js → chunk-FACDJH6O.js} +11 -104
  15. package/dist/chunk-FACDJH6O.js.map +1 -0
  16. package/dist/{chunk-P5XEK7HC.js → chunk-H6WUOW5F.js} +2 -2
  17. package/dist/{chunk-DQB3E2SX.js → chunk-JNRACLNS.js} +3 -3
  18. package/dist/{chunk-37QFHJUW.js → chunk-JTMSFBCA.js} +106 -28
  19. package/dist/chunk-JTMSFBCA.js.map +1 -0
  20. package/dist/{chunk-GNMRXXRY.js → chunk-MQZNDK5F.js} +3 -3
  21. package/dist/chunk-MQZNDK5F.js.map +1 -0
  22. package/dist/{chunk-SRII2GMF.js → chunk-SN64WNWE.js} +2 -2
  23. package/dist/{chunk-EHEBN4NB.js → chunk-U672FZVC.js} +2 -2
  24. package/dist/{chunk-TG3SPPZY.js → chunk-UE4Z3YMB.js} +1 -1
  25. package/dist/chunk-UE4Z3YMB.js.map +1 -0
  26. package/dist/{chunk-Q4NWATGH.js → chunk-XMQIWPIR.js} +58 -10
  27. package/dist/chunk-XMQIWPIR.js.map +1 -0
  28. package/dist/{chunk-YLFKFVQP.js → chunk-ZBI6GFJD.js} +2 -2
  29. package/dist/commands/build/marketplace.js +4 -3
  30. package/dist/commands/build/marketplace.js.map +1 -1
  31. package/dist/commands/build/plugins.js +6 -5
  32. package/dist/commands/build/plugins.js.map +1 -1
  33. package/dist/commands/compile.js +15 -11
  34. package/dist/commands/compile.js.map +1 -1
  35. package/dist/commands/doctor.js +38 -14
  36. package/dist/commands/doctor.js.map +1 -1
  37. package/dist/commands/edit.js +46 -25
  38. package/dist/commands/edit.js.map +1 -1
  39. package/dist/commands/eject.js +12 -8
  40. package/dist/commands/eject.js.map +1 -1
  41. package/dist/commands/init.js +15 -14
  42. package/dist/commands/list.js +8 -6
  43. package/dist/commands/list.js.map +1 -1
  44. package/dist/commands/new/marketplace.js +4 -3
  45. package/dist/commands/new/marketplace.js.map +1 -1
  46. package/dist/commands/search.js +13 -8
  47. package/dist/commands/search.js.map +1 -1
  48. package/dist/commands/share.js +14 -16
  49. package/dist/commands/share.js.map +1 -1
  50. package/dist/commands/uninstall.js +18 -14
  51. package/dist/commands/uninstall.js.map +1 -1
  52. package/dist/commands/update.js +97 -17
  53. package/dist/commands/update.js.map +1 -1
  54. package/dist/components/common/skill-agent-summary.js +3 -2
  55. package/dist/components/common/summary-panel.js +4 -3
  56. package/dist/hooks/init.js +15 -14
  57. package/dist/hooks/init.js.map +1 -1
  58. package/dist/load-agent-defs-5BSECHE6.js +14 -0
  59. package/dist/{recompile-project-agents-2QTFMYDC.js → recompile-project-agents-U3UGHPWG.js} +7 -7
  60. package/dist/{source-loader-I6SWWDUF.js → source-loader-E2Y5MMN5.js} +3 -3
  61. package/package.json +1 -1
  62. package/dist/chunk-37QFHJUW.js.map +0 -1
  63. package/dist/chunk-EGC7PNDI.js.map +0 -1
  64. package/dist/chunk-GNMRXXRY.js.map +0 -1
  65. package/dist/chunk-ICHWAFAC.js.map +0 -1
  66. package/dist/chunk-MFCZARV2.js.map +0 -1
  67. package/dist/chunk-Q4NWATGH.js.map +0 -1
  68. package/dist/chunk-SNXEYAFP.js.map +0 -1
  69. package/dist/chunk-TG3SPPZY.js.map +0 -1
  70. package/dist/chunk-YM3VEDAP.js.map +0 -1
  71. package/dist/load-agent-defs-BJ4KBKBA.js +0 -14
  72. /package/dist/{chunk-6HJVH27C.js.map → chunk-C2BSCEDN.js.map} +0 -0
  73. /package/dist/{chunk-SI2LLEIR.js.map → chunk-EC3JXT23.js.map} +0 -0
  74. /package/dist/{chunk-P5XEK7HC.js.map → chunk-H6WUOW5F.js.map} +0 -0
  75. /package/dist/{chunk-DQB3E2SX.js.map → chunk-JNRACLNS.js.map} +0 -0
  76. /package/dist/{chunk-SRII2GMF.js.map → chunk-SN64WNWE.js.map} +0 -0
  77. /package/dist/{chunk-EHEBN4NB.js.map → chunk-U672FZVC.js.map} +0 -0
  78. /package/dist/{chunk-YLFKFVQP.js.map → chunk-ZBI6GFJD.js.map} +0 -0
  79. /package/dist/{load-agent-defs-BJ4KBKBA.js.map → load-agent-defs-5BSECHE6.js.map} +0 -0
  80. /package/dist/{recompile-project-agents-2QTFMYDC.js.map → recompile-project-agents-U3UGHPWG.js.map} +0 -0
  81. /package/dist/{source-loader-I6SWWDUF.js.map → source-loader-E2Y5MMN5.js.map} +0 -0
@@ -1,20 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- getSkillEntryDisplayName,
4
3
  typedValues
5
- } from "./chunk-TG3SPPZY.js";
4
+ } from "./chunk-UE4Z3YMB.js";
6
5
  import {
7
- CLI_COLORS,
8
- EJECT_SOURCE,
9
- UI_SYMBOLS,
10
6
  isActiveAt,
11
7
  isGlobalTombstone
12
8
  } from "./chunk-U46LYYRA.js";
13
9
 
14
- // src/cli/components/common/skill-agent-summary.tsx
15
- import { Box, Text } from "ink";
16
- import "react";
17
-
18
10
  // src/cli/lib/configuration/scope-diff.ts
19
11
  import { difference, indexBy, partition, unique } from "remeda";
20
12
  var NOTHING = /* @__PURE__ */ new Set();
@@ -116,6 +108,12 @@ function toRemovedSkillRow(skill) {
116
108
  function toRemovedAgentRow(agent) {
117
109
  return { name: agent.name, status: "removed" };
118
110
  }
111
+ function namedByActiveEntries(entries) {
112
+ return indexBy(
113
+ entries.filter((entry) => !entry.excluded),
114
+ ({ id }) => id
115
+ );
116
+ }
119
117
  var AGENT_SETTINGS = [
120
118
  ["model", (agent) => agent.model],
121
119
  ["codex model", (agent) => agent.codexModel],
@@ -137,8 +135,7 @@ function namedByEntries(change, before, after) {
137
135
  return { ...change, namedBy };
138
136
  }
139
137
  function activeEntriesOf(skillIds, skills) {
140
- const active = skills.filter((entry) => !entry.excluded && skillIds.includes(entry.id));
141
- return indexBy(active, ({ id }) => id);
138
+ return namedByActiveEntries(skills.filter((entry) => skillIds.includes(entry.id)));
142
139
  }
143
140
  function agentStateIn(config, name) {
144
141
  const agent = config.agents.find((entry) => entry.name === name && !entry.excluded);
@@ -164,100 +161,10 @@ function assignedSkillIds(rows) {
164
161
  return unique(typedValues(rows ?? {}).flatMap((assignments) => assignments.map(({ id }) => id)));
165
162
  }
166
163
 
167
- // src/cli/components/common/skill-agent-summary.tsx
168
- import { jsx, jsxs } from "react/jsx-runtime";
169
- var TableHeader = ({ children }) => /* @__PURE__ */ jsx(Text, { bold: true, color: CLI_COLORS.WARNING, children });
170
- var ScopeLabel = ({ children }) => /* @__PURE__ */ jsx(Text, { color: CLI_COLORS.WHITE, backgroundColor: CLI_COLORS.LABEL_BG, children: ` ${children} ` });
171
- var EjectIcon = () => /* @__PURE__ */ jsxs(Text, { color: CLI_COLORS.WARNING, children: [
172
- " ",
173
- UI_SYMBOLS.EJECT
174
- ] });
175
- var DIFF_PREFIX = {
176
- added: `${UI_SYMBOLS.ADDED} `,
177
- "mode-changed": `${UI_SYMBOLS.CHANGED} `,
178
- overwritten: `${UI_SYMBOLS.OVERWRITTEN} `,
179
- changed: `${UI_SYMBOLS.CHANGED} `,
180
- removed: `${UI_SYMBOLS.REMOVED} `,
181
- unchanged: `${UI_SYMBOLS.BULLET} `
182
- };
183
- var DIFF_COLOR = {
184
- added: CLI_COLORS.SUCCESS,
185
- "mode-changed": CLI_COLORS.WARNING,
186
- overwritten: CLI_COLORS.WARNING,
187
- changed: CLI_COLORS.WARNING,
188
- removed: CLI_COLORS.ERROR,
189
- unchanged: CLI_COLORS.NEUTRAL
190
- };
191
- var NAME_GUTTER_COLUMNS = 3;
192
- var SkillRow = ({ row }) => /* @__PURE__ */ jsxs(Box, { width: "50%", paddingRight: NAME_GUTTER_COLUMNS, children: [
193
- /* @__PURE__ */ jsx(Box, { flexShrink: 0, children: /* @__PURE__ */ jsx(Text, { color: DIFF_COLOR[row.status], children: DIFF_PREFIX[row.status] }) }),
194
- /* @__PURE__ */ jsxs(Text, { color: DIFF_COLOR[row.status], children: [
195
- getSkillEntryDisplayName(row),
196
- row.source === EJECT_SOURCE && /* @__PURE__ */ jsx(EjectIcon, {})
197
- ] })
198
- ] });
199
- var AgentRow = ({ row }) => /* @__PURE__ */ jsxs(Text, { color: DIFF_COLOR[row.status], children: [
200
- DIFF_PREFIX[row.status],
201
- row.name
202
- ] });
203
- var skillRowKey = (row) => row.status === "removed" ? `removed-${row.id}` : row.id;
204
- var agentRowKey = (row) => row.status === "removed" ? `removed-${row.name}` : row.name;
205
- var SkillAgentSummary = ({
206
- skillConfigs,
207
- agentConfigs,
208
- ...baseline
209
- }) => {
210
- const diff = computeScopeDiff({
211
- currentSkills: skillConfigs,
212
- currentAgents: agentConfigs,
213
- ...baseline
214
- });
215
- if (!diff.hasContent) return null;
216
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", width: "100%", children: [
217
- /* @__PURE__ */ jsxs(
218
- Box,
219
- {
220
- flexDirection: "column",
221
- borderStyle: "single",
222
- flexGrow: 2,
223
- flexBasis: 0,
224
- borderRight: true,
225
- borderTop: false,
226
- borderBottom: false,
227
- borderLeft: false,
228
- borderColor: CLI_COLORS.NEUTRAL,
229
- borderRightDimColor: true,
230
- children: [
231
- /* @__PURE__ */ jsx(TableHeader, { children: "Skills" }),
232
- diff.projectSkillRows.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
233
- /* @__PURE__ */ jsx(ScopeLabel, { children: "Project" }),
234
- /* @__PURE__ */ jsx(Box, { flexWrap: "wrap", children: diff.projectSkillRows.map((row) => /* @__PURE__ */ jsx(SkillRow, { row }, skillRowKey(row))) })
235
- ] }),
236
- diff.globalSkillRows.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
237
- /* @__PURE__ */ jsx(ScopeLabel, { children: "Global" }),
238
- /* @__PURE__ */ jsx(Box, { flexWrap: "wrap", children: diff.globalSkillRows.map((row) => /* @__PURE__ */ jsx(SkillRow, { row }, skillRowKey(row))) })
239
- ] })
240
- ]
241
- }
242
- ),
243
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexBasis: 0, marginLeft: 1, paddingLeft: 1, children: [
244
- /* @__PURE__ */ jsx(TableHeader, { children: "Agents" }),
245
- diff.projectAgentRows.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
246
- /* @__PURE__ */ jsx(ScopeLabel, { children: "Project" }),
247
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: diff.projectAgentRows.map((row) => /* @__PURE__ */ jsx(AgentRow, { row }, agentRowKey(row))) })
248
- ] }),
249
- diff.globalAgentRows.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
250
- /* @__PURE__ */ jsx(ScopeLabel, { children: "Global" }),
251
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: diff.globalAgentRows.map((row) => /* @__PURE__ */ jsx(AgentRow, { row }, agentRowKey(row))) })
252
- ] })
253
- ] })
254
- ] });
255
- };
256
-
257
164
  export {
258
165
  computeScopeDiff,
259
166
  formatScopeTag,
260
- agentChangesAmong,
261
- SkillAgentSummary
167
+ namedByActiveEntries,
168
+ agentChangesAmong
262
169
  };
263
- //# sourceMappingURL=chunk-ICHWAFAC.js.map
170
+ //# sourceMappingURL=chunk-FACDJH6O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/configuration/scope-diff.ts"],"sourcesContent":["import { difference, indexBy, partition, unique } from \"remeda\";\nimport type { AgentScopeConfig, SkillConfig, SkillScope } from \"../../types/config\";\nimport type { AgentName, SkillId, StackAgentConfig } from \"../../types/index\";\nimport { typedValues } from \"../../utils/typed-object\";\nimport { isActiveAt, isGlobalTombstone, type ScopedEntry } from \"./scope-predicates\";\n\nexport type ScopeDiffInput = {\n currentSkills: SkillConfig[];\n currentAgents: AgentScopeConfig[];\n installedSkillConfigs: SkillConfig[] | null;\n installedAgentConfigs: AgentScopeConfig[] | null;\n isInitMode: boolean;\n /** Skills whose files the run writes over where they stand. */\n overwrittenSkillIds?: ReadonlySet<SkillId>;\n /** Sub-agents that keep their slot and still change. */\n changedAgentNames?: ReadonlySet<AgentName>;\n};\n\nexport type DiffRowStatus =\n \"added\" | \"mode-changed\" | \"overwritten\" | \"changed\" | \"removed\" | \"unchanged\";\n\nexport type SkillDiffRow = {\n id: SkillId;\n source: string;\n status: Exclude<DiffRowStatus, \"changed\">;\n /** The name the entry the row was drawn from records; absent where it records none. */\n displayName?: string;\n};\n\nexport type AgentDiffRow = {\n name: AgentName;\n status: Exclude<DiffRowStatus, \"mode-changed\" | \"overwritten\">;\n};\n\nexport type ScopeDiff = {\n projectSkillRows: SkillDiffRow[];\n globalSkillRows: SkillDiffRow[];\n projectAgentRows: AgentDiffRow[];\n globalAgentRows: AgentDiffRow[];\n hasContent: boolean;\n};\n\nconst NOTHING: ReadonlySet<never> = new Set();\n\n/** One scope's share of a roster's diff: the entries listed with a status, then the removals. */\ntype ScopeEntries<Entry> = { listed: Entry[]; removed: Entry[] };\n\ntype RosterDiff<Entry> = Record<SkillScope, ScopeEntries<Entry>>;\n\n/**\n * A tombstone occupies its (id, scope) slot without filling it: it silences a global install at\n * project scope. Slot checks count tombstones; removal and `~` read the active baseline only.\n */\nexport function computeScopeDiff(input: ScopeDiffInput): ScopeDiff {\n const { currentSkills, currentAgents, installedSkillConfigs, installedAgentConfigs, isInitMode } =\n input;\n const overwritten = input.overwrittenSkillIds ?? NOTHING;\n const changed = input.changedAgentNames ?? NOTHING;\n\n const skills = diffRoster(currentSkills, installedSkillConfigs, (skill) => skill.id, isInitMode);\n const agents = diffRoster(\n currentAgents,\n installedAgentConfigs,\n (agent) => agent.name,\n isInitMode,\n );\n const skillRow = skillRowClassifier(installedSkillConfigs, overwritten);\n const agentRow = agentRowClassifier(installedAgentConfigs, changed);\n\n const projectSkillRows = rowsOf(skills.project, skillRow, toRemovedSkillRow);\n const globalSkillRows = rowsOf(skills.global, skillRow, toRemovedSkillRow);\n const projectAgentRows = rowsOf(agents.project, agentRow, toRemovedAgentRow);\n const globalAgentRows = rowsOf(agents.global, agentRow, toRemovedAgentRow);\n\n const hasContent = [projectSkillRows, globalSkillRows, projectAgentRows, globalAgentRows].some(\n (rows) => rows.length > 0,\n );\n\n return { projectSkillRows, globalSkillRows, projectAgentRows, globalAgentRows, hasContent };\n}\n\nfunction diffRoster<Entry extends ScopedEntry>(\n current: Entry[],\n installed: Entry[] | null,\n identityOf: (entry: Entry) => string,\n isInitMode: boolean,\n): RosterDiff<Entry> {\n // The installs the baseline actually records; tombstones are not installs.\n const activeBaseline = (installed ?? []).filter((entry) => !entry.excluded);\n const vacated = vacatedInstalls(activeBaseline, current, identityOf);\n const projectEntries = current.filter((entry) => isActiveAt(entry, \"project\"));\n const claimedByProject = new Set(projectEntries.map(identityOf));\n // A vacated global the project claims is inherited: listed once under Global, never removed.\n const [inheritedGlobals, removedGlobals] = partition(\n vacated.filter((entry) => entry.scope === \"global\"),\n (entry) => claimedByProject.has(identityOf(entry)),\n );\n\n return {\n project: {\n listed: projectEntries,\n removed: vacated.filter((entry) => entry.scope === \"project\"),\n },\n global: {\n listed: [\n ...current.filter((entry) => isActiveAt(entry, \"global\")),\n ...inheritedGlobals,\n ...current.filter(isGlobalTombstone),\n ],\n removed: isInitMode ? [] : removedGlobals,\n },\n };\n}\n\n/** Baseline installs whose slot nothing in current, active or tombstone, occupies. */\nfunction vacatedInstalls<Entry extends ScopedEntry>(\n activeBaseline: Entry[],\n current: Entry[],\n identityOf: (entry: Entry) => string,\n): Entry[] {\n const slotOf = (entry: Entry) => slotKey(identityOf(entry), entry.scope);\n const occupiedSlots = new Set(current.map(slotOf));\n return activeBaseline.filter((install) => !occupiedSlots.has(slotOf(install)));\n}\n\nfunction rowsOf<Entry, Row>(\n entries: ScopeEntries<Entry>,\n toRow: (entry: Entry) => Row,\n toRemovedRow: (entry: Entry) => Row,\n): Row[] {\n return [...entries.listed.map(toRow), ...entries.removed.map(toRemovedRow)];\n}\n\n/** The `(id, scope)` SLOT key this module diffs on, rather than the id alone. */\nfunction slotKey(identity: string, scope: SkillScope | undefined): string {\n return `${identity}:${scope}`;\n}\n\nexport function formatScopeTag(scope: SkillScope): \"[G]\" | \"[P]\" {\n return scope === \"global\" ? \"[G]\" : \"[P]\";\n}\n\nfunction skillRowClassifier(\n installed: SkillConfig[] | null,\n overwritten: ReadonlySet<SkillId>,\n): (skill: SkillConfig) => SkillDiffRow {\n const installedSlots = installed\n ? new Set(installed.map((skill) => slotKey(skill.id, skill.scope)))\n : null;\n const installedOriginBySlot = installed\n ? new Map(\n installed\n .filter((skill) => !skill.excluded)\n .map((skill) => [slotKey(skill.id, skill.scope), skill.origin]),\n )\n : null;\n return (skill) => ({\n id: skill.id,\n source: skill.origin,\n status: skillStatus(skill, installedSlots, installedOriginBySlot, overwritten),\n ...recordedNameOf(skill),\n });\n}\n\nfunction recordedNameOf(skill: SkillConfig): Pick<SkillDiffRow, \"displayName\"> {\n return skill.displayName === undefined ? {} : { displayName: skill.displayName };\n}\n\nfunction agentRowClassifier(\n installed: AgentScopeConfig[] | null,\n changed: ReadonlySet<AgentName>,\n): (agent: AgentScopeConfig) => AgentDiffRow {\n const installedSlots = installed\n ? new Set(installed.map((agent) => slotKey(agent.name, agent.scope)))\n : null;\n return (agent) => ({ name: agent.name, status: agentStatus(agent, installedSlots, changed) });\n}\n\n/**\n * Compares `origin`, which is where a skill's install mode is recorded. An overwrite outranks a\n * mode change: a copy its plugin replaces is deleted, not merely switched. A mask is never written.\n */\nfunction skillStatus(\n skill: SkillConfig,\n installedSlots: Set<string> | null,\n installedOriginBySlot: Map<string, string> | null,\n overwritten: ReadonlySet<SkillId>,\n): SkillDiffRow[\"status\"] {\n const slot = slotKey(skill.id, skill.scope);\n if (installedSlots === null || !installedSlots.has(slot)) return \"added\";\n if (!skill.excluded && overwritten.has(skill.id)) return \"overwritten\";\n\n const installedOrigin = installedOriginBySlot?.get(slot);\n const modeChanged = installedOrigin != null && installedOrigin !== skill.origin;\n return modeChanged ? \"mode-changed\" : \"unchanged\";\n}\n\n/** A mask is never the sub-agent that changes: its active half is. */\nfunction agentStatus(\n agent: AgentScopeConfig,\n installedSlots: Set<string> | null,\n changed: ReadonlySet<AgentName>,\n): AgentDiffRow[\"status\"] {\n const isNew = installedSlots === null || !installedSlots.has(slotKey(agent.name, agent.scope));\n if (isNew) return \"added\";\n return !agent.excluded && changed.has(agent.name) ? \"changed\" : \"unchanged\";\n}\n\nfunction toRemovedSkillRow(skill: SkillConfig): SkillDiffRow {\n return { id: skill.id, source: skill.origin, status: \"removed\", ...recordedNameOf(skill) };\n}\n\nfunction toRemovedAgentRow(agent: AgentScopeConfig): AgentDiffRow {\n return { name: agent.name, status: \"removed\" };\n}\n\n/** The entry each skill is named by, as a panel row drawn from that entry names it. */\nexport type NamedBy = Partial<Record<SkillId, Pick<SkillConfig, \"id\" | \"displayName\">>>;\n\nexport function namedByActiveEntries(\n entries: readonly Pick<SkillConfig, \"id\" | \"displayName\" | \"excluded\">[],\n): NamedBy {\n return indexBy(\n entries.filter((entry) => !entry.excluded),\n ({ id }) => id,\n );\n}\n\n/** A sub-agent that keeps its slot and still changes: the skills its rows gain and lose, and its retunes. */\nexport type AgentChange = {\n name: AgentName;\n scope: SkillScope;\n gains: SkillId[];\n loses: SkillId[];\n settings: SettingChange[];\n /** The run's entry for a skill it gains, the install's for one it loses. */\n namedBy?: NamedBy;\n};\n\n/** `undefined` where the setting is left to its default. */\nexport type SettingChange = {\n setting: AgentSetting;\n from: string | undefined;\n to: string | undefined;\n};\n\ntype AgentSetting = \"model\" | \"codex model\" | \"effort\";\n\nconst AGENT_SETTINGS: ReadonlyArray<\n [AgentSetting, (agent: AgentScopeConfig) => string | undefined]\n> = [\n [\"model\", (agent) => agent.model],\n [\"codex model\", (agent) => agent.codexModel],\n [\"effort\", (agent) => agent.effort],\n];\n\n/** One sub-agent as a config records it: its entry and its skill rows. */\nexport type AgentState = { agent: AgentScopeConfig; rows: StackAgentConfig | undefined };\n\ntype AgentsAndRows = {\n agents: AgentScopeConfig[];\n skills: SkillConfig[];\n stack?: Partial<Record<string, StackAgentConfig>>;\n};\n\n/** How each named sub-agent moves between two configs; one either config lacks is not named. */\nexport function agentChangesAmong(\n names: readonly AgentName[],\n before: AgentsAndRows,\n after: AgentsAndRows,\n): AgentChange[] {\n return names.flatMap((name) => {\n const was = agentStateIn(before, name);\n const is = agentStateIn(after, name);\n if (was === undefined || is === undefined) return [];\n return [namedByEntries(agentChange(was, is), before.skills, after.skills)];\n });\n}\n\nfunction namedByEntries(\n change: AgentChange,\n before: SkillConfig[],\n after: SkillConfig[],\n): AgentChange {\n const namedBy = {\n ...activeEntriesOf(change.loses, before),\n ...activeEntriesOf(change.gains, after),\n };\n return { ...change, namedBy };\n}\n\nfunction activeEntriesOf(skillIds: readonly SkillId[], skills: SkillConfig[]): NamedBy {\n return namedByActiveEntries(skills.filter((entry) => skillIds.includes(entry.id)));\n}\n\n/** Its active entry, never the mask a project's own copy leaves; `undefined` where none is. */\nfunction agentStateIn(config: AgentsAndRows, name: AgentName): AgentState | undefined {\n const agent = config.agents.find((entry) => entry.name === name && !entry.excluded);\n return agent === undefined ? undefined : { agent, rows: config.stack?.[name] };\n}\n\nexport function agentChange(before: AgentState, after: AgentState): AgentChange {\n const beforeIds = assignedSkillIds(before.rows);\n const afterIds = assignedSkillIds(after.rows);\n\n return {\n name: after.agent.name,\n scope: after.agent.scope,\n gains: difference(afterIds, beforeIds),\n loses: difference(beforeIds, afterIds),\n settings: settingChanges(before.agent, after.agent),\n };\n}\n\nfunction settingChanges(before: AgentScopeConfig, after: AgentScopeConfig): SettingChange[] {\n return AGENT_SETTINGS.filter(([, read]) => read(before) !== read(after)).map(\n ([setting, read]) => ({ setting, from: read(before), to: read(after) }),\n );\n}\n\nfunction assignedSkillIds(rows: StackAgentConfig | undefined): SkillId[] {\n return unique(typedValues(rows ?? {}).flatMap((assignments) => assignments.map(({ id }) => id)));\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  execCommand,
4
4
  getErrorMessage
5
- } from "./chunk-YM3VEDAP.js";
5
+ } from "./chunk-ENET4IF6.js";
6
6
 
7
7
  // src/cli/utils/open-url.ts
8
8
  function browserOpenerCommand(platform, url) {
@@ -32,4 +32,4 @@ async function openUrl(url) {
32
32
  export {
33
33
  openUrl
34
34
  };
35
- //# sourceMappingURL=chunk-P5XEK7HC.js.map
35
+ //# sourceMappingURL=chunk-H6WUOW5F.js.map
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getLocalAgentDefinitions
4
- } from "./chunk-SI2LLEIR.js";
4
+ } from "./chunk-EC3JXT23.js";
5
5
  import {
6
6
  loadMergedAgents
7
- } from "./chunk-YM3VEDAP.js";
7
+ } from "./chunk-ENET4IF6.js";
8
8
 
9
9
  // src/cli/lib/operations/project/load-agent-defs.ts
10
10
  async function loadAgentDefs() {
@@ -20,4 +20,4 @@ async function loadAgentDefs() {
20
20
  export {
21
21
  loadAgentDefs
22
22
  };
23
- //# sourceMappingURL=chunk-DQB3E2SX.js.map
23
+ //# sourceMappingURL=chunk-JNRACLNS.js.map
@@ -1,12 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ namedByActiveEntries
4
+ } from "./chunk-FACDJH6O.js";
5
+ import {
6
+ ConfigLoadError,
7
+ FailedLeavingPluginsSwitchedOnError,
3
8
  INFO_MESSAGES,
9
+ PluginsLeftSwitchedOnError,
4
10
  STATUS_MESSAGES,
5
11
  addedToGlobalSubAgents,
6
12
  buildMarketplacePluginRef,
7
13
  chooseProviderForThisRun,
8
14
  claimSlug,
15
+ codexPluginLeftSwitchedOn,
9
16
  completedWithFailures,
17
+ configDeclaresNothing,
10
18
  copy,
11
19
  defaultUsageGuidance,
12
20
  detectInstallations,
@@ -28,6 +36,7 @@ import {
28
36
  loadStackById,
29
37
  parseFrontmatter,
30
38
  pluginsInstalled,
39
+ pluginsLeftSwitchedOn,
31
40
  propagatedRecompileSummary,
32
41
  providerInUse,
33
42
  providerToNameAt,
@@ -54,7 +63,7 @@ import {
54
63
  verbose,
55
64
  warn,
56
65
  writeFile
57
- } from "./chunk-YM3VEDAP.js";
66
+ } from "./chunk-ENET4IF6.js";
58
67
  import {
59
68
  BUILT_IN_MATRIX,
60
69
  getCategoryDomain,
@@ -64,7 +73,7 @@ import {
64
73
  typedEntries,
65
74
  typedKeys,
66
75
  typedValues
67
- } from "./chunk-TG3SPPZY.js";
76
+ } from "./chunk-UE4Z3YMB.js";
68
77
  import {
69
78
  CLI_COLORS,
70
79
  CLI_INVOKE_COMMAND,
@@ -161,14 +170,24 @@ async function refuseAnAmbiguousInstallation(cwd, subject, named, onAmbiguous) {
161
170
  if (providers.length <= 1) return;
162
171
  onAmbiguous(ambiguousInstallation(subject, providers, scopeRoot));
163
172
  }
164
- async function globalInstallationStandsIn(cwd, named) {
173
+ async function holdsOnlyAConfigDeclaringNothing(cwd) {
165
174
  if (!await holdsAConfigDeclaringNothing(cwd)) return false;
166
- const { scopeRoot, providers } = await installationsInPlay(cwd);
167
- const theProjectHoldsNone = scopeRoot !== cwd;
168
- const homeHoldsOneToWorkOn = providers.some(
175
+ return atScope(await detectInstallations(cwd), "project").length === 0;
176
+ }
177
+ async function homeHoldsAnInstallationFor(cwd, named) {
178
+ const atHome = providersOf(atScope(await detectInstallations(cwd), "global")).filter(
169
179
  (provider) => named === void 0 || provider === named
170
180
  );
171
- return theProjectHoldsNone && homeHoldsOneToWorkOn;
181
+ const declaringContent = await Promise.all(atHome.map(homeConfigDeclaresContent));
182
+ return declaringContent.includes(true);
183
+ }
184
+ async function homeConfigDeclaresContent(provider) {
185
+ try {
186
+ return !await configDeclaresNothing(os.homedir(), provider);
187
+ } catch (error) {
188
+ if (error instanceof ConfigLoadError) return true;
189
+ throw error;
190
+ }
172
191
  }
173
192
  async function providersInPlay(cwd) {
174
193
  return (await installationsInPlay(cwd)).providers;
@@ -428,11 +447,24 @@ function tooLongForADirectoryError(ids) {
428
447
  `A skill id is the directory the skill is written into, and a directory name may hold at most ${String(DIRECTORY_NAME_MAX_BYTES)} bytes, so none of these could be written. Re-share it without each skill above.`
429
448
  ].join("\n");
430
449
  }
450
+ var CARRIED_ID_PREFIX = "external-";
451
+ function externalIdsLackingThePrefix(payload) {
452
+ return Object.keys(payload.external ?? {}).filter((id) => !id.startsWith(CARRIED_ID_PREFIX));
453
+ }
454
+ function lacksThePrefixError(ids) {
455
+ return [
456
+ "This configuration cannot be installed: these skills travel inside it under ids that do not start with 'external-':",
457
+ ...ids.map((id) => ` ${id}`),
458
+ "A skill id is the directory the skill installs into, and no marketplace may publish under the name 'external', so an id starting 'external-' is the only kind that can never take the place of a marketplace's skill. Re-share it with each skill above added again, which mints such an id \u2014 'external-<category>-<name>'."
459
+ ].join("\n");
460
+ }
431
461
  function registerExternalSkills(payload, matrix2, projectDir) {
432
462
  const notDirectoryNames = externalIdsNamingNoDirectory(payload);
433
463
  if (notDirectoryNames.length > 0) throw new Error(notADirectoryNameError(notDirectoryNames));
434
464
  const tooLong = externalIdsTooLongForADirectory(payload);
435
465
  if (tooLong.length > 0) throw new Error(tooLongForADirectoryError(tooLong));
466
+ const unprefixed = externalIdsLackingThePrefix(payload);
467
+ if (unprefixed.length > 0) throw new Error(lacksThePrefixError(unprefixed));
436
468
  const externals = selectedExternals(payload);
437
469
  const asPlugin = askedForAsPlugin(externals);
438
470
  const colliding = claimingACatalogueId(externals, matrix2);
@@ -600,7 +632,7 @@ async function readCarriedSkills(owned) {
600
632
 
601
633
  // src/cli/lib/seed/seed-apply.ts
602
634
  import os3 from "os";
603
- import { indexBy, isDeepEqual, omit, partition, pick } from "remeda";
635
+ import { isDeepEqual, omit, partition, pick } from "remeda";
604
636
  var NOTHING_KEPT = {
605
637
  authoredSkillIds: [],
606
638
  unplaceableSkillIds: []
@@ -855,8 +887,7 @@ function entriesActiveAt(skills, agents, scope) {
855
887
  };
856
888
  }
857
889
  function panelRowEntries(held) {
858
- const active = held.result.skills.filter((skill) => !skill.excluded);
859
- return indexBy(active, (skill) => skill.id);
890
+ return namedByActiveEntries(held.result.skills);
860
891
  }
861
892
  function joinedStatement(held) {
862
893
  if (held.joined.length === 0) return [];
@@ -878,19 +909,37 @@ import { unique } from "remeda";
878
909
  // src/cli/lib/operations/source/ensure-marketplace.ts
879
910
  async function ensureMarketplace(sourceResult, host) {
880
911
  const marketplace = await resolveMarketplaceName(sourceResult);
881
- if (marketplace === null) return { marketplace: null, registered: false };
912
+ if (marketplace === null) return { marketplace: null, registered: false, leftOn: [] };
882
913
  const exists = await host.marketplaceExists(marketplace);
883
914
  if (!exists) {
884
915
  const marketplaceSource = sourceResult.sourceConfig.source.replace(/^github:/, "");
885
916
  await host.addMarketplace(marketplaceSource);
886
- return { marketplace, registered: true };
917
+ return { marketplace, registered: true, leftOn: [] };
887
918
  }
919
+ const outcome = await refreshOutcome(marketplace, host);
920
+ warnOfShortfalls(outcome);
921
+ return { marketplace, registered: false, leftOn: outcome.leftOn };
922
+ }
923
+ async function refreshOutcome(marketplace, host) {
888
924
  try {
889
925
  await host.refreshMarketplace(marketplace);
890
- } catch {
891
- warn("Could not update marketplace \u2014 continuing with cached version");
926
+ return { refreshed: true, leftOn: [] };
927
+ } catch (error) {
928
+ return outcomeOfRefusal(error);
929
+ }
930
+ }
931
+ function outcomeOfRefusal(error) {
932
+ if (error instanceof PluginsLeftSwitchedOnError) return { refreshed: true, leftOn: error.leftOn };
933
+ if (error instanceof FailedLeavingPluginsSwitchedOnError) {
934
+ return { refreshed: false, leftOn: error.leftOn };
935
+ }
936
+ return { refreshed: false, leftOn: [] };
937
+ }
938
+ function warnOfShortfalls({ refreshed, leftOn }) {
939
+ if (!refreshed) warn("Could not update marketplace \u2014 continuing with cached version");
940
+ for (const { pluginKey, configFile, reason } of leftOn) {
941
+ warn(codexPluginLeftSwitchedOn(pluginKey, configFile, reason));
892
942
  }
893
- return { marketplace, registered: false };
894
943
  }
895
944
  async function resolveMarketplaceName(sourceResult) {
896
945
  if (sourceResult.marketplace) return sourceResult.marketplace;
@@ -914,7 +963,12 @@ async function requireMarketplace(sourceResult, purpose, host) {
914
963
  error: `Cannot ${purpose}: marketplace could not be resolved from '${sourceResult.sourceConfig.source}'. Plugin install mode requires a marketplace \u2014 fix the marketplace or switch the affected skills to eject mode.`
915
964
  };
916
965
  }
917
- return { ok: true, marketplace: mpResult.marketplace, registered: mpResult.registered };
966
+ return {
967
+ ok: true,
968
+ marketplace: mpResult.marketplace,
969
+ registered: mpResult.registered,
970
+ leftOn: mpResult.leftOn
971
+ };
918
972
  }
919
973
 
920
974
  // src/cli/lib/operations/skills/install-plugin-skills.ts
@@ -948,6 +1002,10 @@ async function installPluginSkills(skills, marketplace, projectDir) {
948
1002
  var TERMINAL_RESIZE_POLL_MS = 500;
949
1003
  var terminalColumns = () => process.stdout.columns ?? MIN_TERMINAL_SIZE.COLS;
950
1004
  var terminalRows = () => process.stdout.rows ?? MIN_TERMINAL_SIZE.ROWS;
1005
+ function sayFirst(line) {
1006
+ process.stderr.write(`${line}
1007
+ `);
1008
+ }
951
1009
  function isTerminalBigEnough() {
952
1010
  return isTerminalLargeEnough(terminalColumns(), terminalRows());
953
1011
  }
@@ -981,19 +1039,26 @@ function hasActiveProjectEntry(skills, skillId) {
981
1039
  var BaseCommand = class extends Command {
982
1040
  /** Non-empty decides the exit code; see {@link exitIfWorkIncomplete}. */
983
1041
  incompleteWork = [];
1042
+ /** Warned of by the refresh that left them; non-empty fails the run, as in `update`. */
1043
+ switchesLeftOn = [];
984
1044
  async init() {
985
1045
  await super.init();
986
1046
  await this.ensureTerminalSize();
987
1047
  }
988
1048
  /**
989
1049
  * Called first where a command starts work on the installation in play, which for a project whose
990
- * config declares nothing is the global one. On stderr, as the init hook's old-folder lines are,
991
- * so piped output is unchanged.
1050
+ * config declares nothing is the global one, or none where HOME holds none the run could work on.
992
1051
  */
993
1052
  async sayWhenTheGlobalInstallationStandsIn(named) {
994
- if (!await globalInstallationStandsIn(process.cwd(), named)) return;
995
- process.stderr.write(`${INFO_MESSAGES.GLOBAL_INSTALLATION_STANDS_IN}
996
- `);
1053
+ if (!await holdsOnlyAConfigDeclaringNothing(process.cwd())) return;
1054
+ const standsIn = await homeHoldsAnInstallationFor(process.cwd(), named);
1055
+ sayFirst(
1056
+ standsIn ? INFO_MESSAGES.GLOBAL_INSTALLATION_STANDS_IN : INFO_MESSAGES.NO_GLOBAL_INSTALLATION_STANDS_IN
1057
+ );
1058
+ }
1059
+ /** For a command that never works on the global installation: `line` says what it does instead. */
1060
+ async sayWhenTheProjectsConfigDeclaresNothing(line) {
1061
+ if (await holdsOnlyAConfigDeclaringNothing(process.cwd())) sayFirst(line);
997
1062
  }
998
1063
  async ensureTerminalSize() {
999
1064
  if (isTerminalBigEnough()) return;
@@ -1027,6 +1092,7 @@ var BaseCommand = class extends Command {
1027
1092
  if (!required.ok) {
1028
1093
  this.error(required.error, { exit: EXIT_CODES.ERROR });
1029
1094
  }
1095
+ this.switchesLeftOn.push(...required.leftOn);
1030
1096
  if (required.registered) {
1031
1097
  onRegistered?.(required.marketplace);
1032
1098
  }
@@ -1090,10 +1156,11 @@ var BaseCommand = class extends Command {
1090
1156
  this.log(propagatedRecompileSummary(rewrittenCount + unchangedCount, failedCount));
1091
1157
  }
1092
1158
  reportUnassignedSkills(config, selected) {
1159
+ const namedBy = namedByActiveEntries(selected);
1093
1160
  for (const { skillId, blockedBy } of findUnassignedSkills(config, selected)) {
1094
- this.warn(skillAssignedToNoAgent(skillId));
1161
+ this.warn(skillAssignedToNoAgent(skillId, namedBy));
1095
1162
  if (blockedBy.length > 0) {
1096
- this.warn(scopeBlockedStackAssignment(blockedBy, skillId));
1163
+ this.warn(scopeBlockedStackAssignment(blockedBy, skillId, namedBy));
1097
1164
  }
1098
1165
  }
1099
1166
  }
@@ -1133,9 +1200,11 @@ var BaseCommand = class extends Command {
1133
1200
  if (!isHomeDirectory(projectDir) || installsAtHome(result)) return;
1134
1201
  const { skillIds, agentNames } = projectScopedContent(result);
1135
1202
  const provider = providerToNameAt(projectDir, providerInUse(projectDir));
1136
- this.error(sharedConfigProjectScopeAtHome(skillIds, agentNames, installedHere, provider), {
1137
- exit: EXIT_CODES.ERROR
1138
- });
1203
+ const namedBy = namedByActiveEntries(result.skills);
1204
+ this.error(
1205
+ sharedConfigProjectScopeAtHome(skillIds, agentNames, installedHere, provider, namedBy),
1206
+ { exit: EXIT_CODES.ERROR }
1207
+ );
1139
1208
  }
1140
1209
  /** `unloadedSkillIds`: skills the marketplace ships and could not load, never unknown ones. */
1141
1210
  warnOfSkippedEntries(mapping, unloadedSkillIds) {
@@ -1188,11 +1257,20 @@ var BaseCommand = class extends Command {
1188
1257
  }
1189
1258
  /** Every command that records must call this; `this.exit` skips the `Error:` framing. */
1190
1259
  exitIfWorkIncomplete() {
1260
+ this.listIncompleteWork();
1261
+ this.failIfPluginsLeftSwitchedOn();
1262
+ if (this.hasIncompleteWork) this.exit(EXIT_CODES.COMPLETED_WITH_FAILURES);
1263
+ }
1264
+ listIncompleteWork() {
1191
1265
  if (!this.hasIncompleteWork) return;
1192
1266
  this.log(`
1193
1267
  ${chalk.hex(CLI_COLORS.WARNING)(completedWithFailures(this.incompleteWork))}
1194
1268
  `);
1195
- this.exit(EXIT_CODES.COMPLETED_WITH_FAILURES);
1269
+ }
1270
+ failIfPluginsLeftSwitchedOn() {
1271
+ if (this.switchesLeftOn.length === 0) return;
1272
+ const pluginKeys = this.switchesLeftOn.map(({ pluginKey }) => pluginKey);
1273
+ this.error(pluginsLeftSwitchedOn(pluginKeys), { exit: EXIT_CODES.ERROR });
1196
1274
  }
1197
1275
  };
1198
1276
 
@@ -1222,4 +1300,4 @@ export {
1222
1300
  installsAtHome,
1223
1301
  BaseCommand
1224
1302
  };
1225
- //# sourceMappingURL=chunk-37QFHJUW.js.map
1303
+ //# sourceMappingURL=chunk-JTMSFBCA.js.map