@tjdals12/agent-bolt 1.2.0 β†’ 1.3.0-next.2

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 (35) hide show
  1. package/README.ko.md +25 -17
  2. package/README.md +25 -17
  3. package/dist/catalog/commands/add-item.d.ts +24 -7
  4. package/dist/catalog/commands/add-item.d.ts.map +1 -1
  5. package/dist/catalog/commands/add-item.js +308 -23
  6. package/dist/catalog/commands/add-item.js.map +1 -1
  7. package/dist/catalog/commands/remove-item.d.ts +21 -7
  8. package/dist/catalog/commands/remove-item.d.ts.map +1 -1
  9. package/dist/catalog/commands/remove-item.js +246 -22
  10. package/dist/catalog/commands/remove-item.js.map +1 -1
  11. package/dist/cli/format.d.ts +2 -0
  12. package/dist/cli/format.d.ts.map +1 -1
  13. package/dist/cli/format.js +19 -0
  14. package/dist/cli/format.js.map +1 -1
  15. package/dist/cli/index.js +13 -6
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/cli/prompts.d.ts +4 -0
  18. package/dist/cli/prompts.d.ts.map +1 -0
  19. package/dist/cli/prompts.js +11 -0
  20. package/dist/cli/prompts.js.map +1 -0
  21. package/dist/cli/views/add-item.d.ts.map +1 -1
  22. package/dist/cli/views/add-item.js +47 -19
  23. package/dist/cli/views/add-item.js.map +1 -1
  24. package/dist/cli/views/add-pack.js +1 -1
  25. package/dist/cli/views/add-pack.js.map +1 -1
  26. package/dist/cli/views/remove-item.d.ts.map +1 -1
  27. package/dist/cli/views/remove-item.js +47 -19
  28. package/dist/cli/views/remove-item.js.map +1 -1
  29. package/dist/cli/views/remove-pack.js +1 -1
  30. package/dist/cli/views/remove-pack.js.map +1 -1
  31. package/dist/cli/views/shared/banner.d.ts +2 -0
  32. package/dist/cli/views/shared/banner.d.ts.map +1 -1
  33. package/dist/cli/views/shared/banner.js +8 -0
  34. package/dist/cli/views/shared/banner.js.map +1 -1
  35. package/package.json +1 -1
package/README.ko.md CHANGED
@@ -200,12 +200,14 @@ $ agent-bolt add-pack --source=common --packs=common
200
200
  $ agent-bolt add-item --source=common --pack=backend --skills=nestjs-expert
201
201
 
202
202
  β–Œ Agent Bolt: Added 1 item to common/backend
203
- (created pack backend in common)
203
+ (added new pack 'backend' in source 'common')
204
204
 
205
205
  + πŸ”§ nestjs-expert
206
206
  ...
207
207
  ```
208
208
 
209
+ μ•„μ΄ν…œ μ˜΅μ…˜μ„ λΉΌλ©΄ μ•„μ΄ν…œμ„ λͺ©λ‘μ—μ„œ 골라 μΆ”κ°€ν•©λ‹ˆλ‹€. `--pack`κΉŒμ§€ λΉΌλ©΄ νŒ©μ„ λ¨Όμ € κ³ λ₯΄κ³ , `--source`κΉŒμ§€ λΉΌλ©΄ μ™„μ „ λŒ€ν™”ν˜•μœΌλ‘œ λ™μž‘ν•΄ μ†ŒμŠ€μ™€ νŒ©μ„ μ˜€κ°€λ©° 계속 μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
210
+
209
211
  `remove-pack`Β·`remove-item`으둜 μΆ”κ°€ν•œ νŒ¨ν‚€μ§€Β·μ•„μ΄ν…œμ„ μ‚­μ œν•©λ‹ˆλ‹€.
210
212
 
211
213
  ```text
@@ -217,6 +219,8 @@ $ agent-bolt remove-item --source=common --pack=common --skills=pr-review
217
219
  ...
218
220
  ```
219
221
 
222
+ μ•„μ΄ν…œ μ˜΅μ…˜μ„ λΉΌλ©΄ μ•„μ΄ν…œμ„ λͺ©λ‘μ—μ„œ 골라 μ œκ±°ν•©λ‹ˆλ‹€. `--pack`κΉŒμ§€ λΉΌλ©΄ νŒ©μ„ λ¨Όμ € κ³ λ₯΄κ³ , `--source`κΉŒμ§€ λΉΌλ©΄ μ™„μ „ λŒ€ν™”ν˜•μœΌλ‘œ λ™μž‘ν•΄ μ†ŒμŠ€μ™€ νŒ©μ„ μ˜€κ°€λ©° 계속 μ œκ±°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
223
+
220
224
  이 λͺ…령듀은 μ„€μ • νŒŒμΌμ„ λ°”κΏ€ λΏμž…λ‹ˆλ‹€. μ‹€μ œ μ„€μΉ˜λŠ” `sync`μ—μ„œ μ΄λ£¨μ–΄μ§‘λ‹ˆλ‹€.
221
225
 
222
226
  ### μ—μ΄μ „νŠΈμ— μ„€μΉ˜ν•˜κΈ°
@@ -563,16 +567,18 @@ agent-bolt add-pack [--source <alias>] [--packs <list>]
563
567
  νŠΉμ • μ•„μ΄ν…œλ§Œ 섀정에 μΆ”κ°€ν•©λ‹ˆλ‹€. νŒ¨ν‚€μ§€ ν•­λͺ©μ΄ μ—†μœΌλ©΄ λ§Œλ“€μ–΄ λ‘‘λ‹ˆλ‹€.
564
568
 
565
569
  ```text
566
- agent-bolt add-item --source <alias> --pack <name> [options]
570
+ agent-bolt add-item [--source <alias>] [--pack <name>] [options]
567
571
  ```
568
572
 
569
- | μ˜΅μ…˜ | μ„€λͺ… | ν•„μˆ˜/선택 | κΈ°λ³Έκ°’ |
570
- | --------------------- | -------------------------------------- | --------- | ------ |
571
- | `--source <alias>` | λŒ€μƒ μΉ΄νƒˆλ‘œκ·Έ | ν•„μˆ˜ | β€” |
572
- | `--pack <name>` | λŒ€μƒ νŒ¨ν‚€μ§€ | ν•„μˆ˜ | β€” |
573
- | `--skills <list>` | μΆ”κ°€ν•  μŠ€ν‚¬ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
574
- | `--agents <list>` | μΆ”κ°€ν•  μ„œλΈŒ μ—μ΄μ „νŠΈ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
575
- | `--guidelines <list>` | μΆ”κ°€ν•  κ°€μ΄λ“œλΌμΈ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
573
+ | μ˜΅μ…˜ | μ„€λͺ… | ν•„μˆ˜/선택 | κΈ°λ³Έκ°’ |
574
+ | --------------------- | -------------------------------------- | --------- | ----------- |
575
+ | `--source <alias>` | λŒ€μƒ μΉ΄νƒˆλ‘œκ·Έ | 선택 | λŒ€ν™”ν˜• 선택 |
576
+ | `--pack <name>` | λŒ€μƒ νŒ¨ν‚€μ§€ | 선택 | λŒ€ν™”ν˜• 선택 |
577
+ | `--skills <list>` | μΆ”κ°€ν•  μŠ€ν‚¬ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
578
+ | `--agents <list>` | μΆ”κ°€ν•  μ„œλΈŒ μ—μ΄μ „νŠΈ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
579
+ | `--guidelines <list>` | μΆ”κ°€ν•  κ°€μ΄λ“œλΌμΈ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
580
+
581
+ μ•„μ΄ν…œ μ˜΅μ…˜μ„ μƒλž΅ν•˜λ©΄ μ•„μ΄ν…œμ„ λŒ€ν™”ν˜•μœΌλ‘œ κ³ λ₯΄κ³ , `--pack`κΉŒμ§€ μƒλž΅ν•˜λ©΄ νŒ©μ„ λ¨Όμ € κ³ λ₯΄λ©°, `--source`κΉŒμ§€ μƒλž΅ν•˜λ©΄ μ—¬λŸ¬ sourceλ₯Ό μ˜€κ°€λ©° λ°˜λ³΅ν•©λ‹ˆλ‹€. `--pack`은 `--source`와, μ•„μ΄ν…œ μ˜΅μ…˜μ€ `--pack`κ³Ό ν•¨κ»˜ 써야 ν•©λ‹ˆλ‹€.
576
582
 
577
583
  ### `agent-bolt remove-pack`
578
584
 
@@ -594,16 +600,18 @@ agent-bolt remove-pack [--source <alias>] [--packs <list>]
594
600
  μ„€μ •μ—μ„œ νŠΉμ • μ•„μ΄ν…œμ„ μ œκ±°ν•©λ‹ˆλ‹€.
595
601
 
596
602
  ```text
597
- agent-bolt remove-item --source <alias> --pack <name> [options]
603
+ agent-bolt remove-item [--source <alias>] [--pack <name>] [options]
598
604
  ```
599
605
 
600
- | μ˜΅μ…˜ | μ„€λͺ… | ν•„μˆ˜/선택 | κΈ°λ³Έκ°’ |
601
- | --------------------- | -------------------------------------- | --------- | ------ |
602
- | `--source <alias>` | λŒ€μƒ μΉ΄νƒˆλ‘œκ·Έ | ν•„μˆ˜ | β€” |
603
- | `--pack <name>` | λŒ€μƒ νŒ¨ν‚€μ§€ | ν•„μˆ˜ | β€” |
604
- | `--skills <list>` | μ œκ±°ν•  μŠ€ν‚¬ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
605
- | `--agents <list>` | μ œκ±°ν•  μ„œλΈŒ μ—μ΄μ „νŠΈ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
606
- | `--guidelines <list>` | μ œκ±°ν•  κ°€μ΄λ“œλΌμΈ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
606
+ | μ˜΅μ…˜ | μ„€λͺ… | ν•„μˆ˜/선택 | κΈ°λ³Έκ°’ |
607
+ | --------------------- | -------------------------------------- | --------- | ----------- |
608
+ | `--source <alias>` | λŒ€μƒ μΉ΄νƒˆλ‘œκ·Έ | 선택 | λŒ€ν™”ν˜• 선택 |
609
+ | `--pack <name>` | λŒ€μƒ νŒ¨ν‚€μ§€ | 선택 | λŒ€ν™”ν˜• 선택 |
610
+ | `--skills <list>` | μ œκ±°ν•  μŠ€ν‚¬ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
611
+ | `--agents <list>` | μ œκ±°ν•  μ„œλΈŒ μ—μ΄μ „νŠΈ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
612
+ | `--guidelines <list>` | μ œκ±°ν•  κ°€μ΄λ“œλΌμΈ 이름. 콀마둜 ꡬ뢄 | 선택 | β€” |
613
+
614
+ μ•„μ΄ν…œ μ˜΅μ…˜μ„ μƒλž΅ν•˜λ©΄ μ•„μ΄ν…œμ„ λŒ€ν™”ν˜•μœΌλ‘œ κ³ λ₯΄κ³ , `--pack`κΉŒμ§€ μƒλž΅ν•˜λ©΄ νŒ©μ„ λ¨Όμ € κ³ λ₯΄λ©°, `--source`κΉŒμ§€ μƒλž΅ν•˜λ©΄ μ—¬λŸ¬ sourceλ₯Ό μ˜€κ°€λ©° λ°˜λ³΅ν•©λ‹ˆλ‹€. `--pack`은 `--source`와, μ•„μ΄ν…œ μ˜΅μ…˜μ€ `--pack`κ³Ό ν•¨κ»˜ 써야 ν•©λ‹ˆλ‹€.
607
615
 
608
616
  ### `agent-bolt sync`
609
617
 
package/README.md CHANGED
@@ -200,12 +200,14 @@ Use `add-item` to add just specific items from a package.
200
200
  $ agent-bolt add-item --source=common --pack=backend --skills=nestjs-expert
201
201
 
202
202
  β–Œ Agent Bolt: Added 1 item to common/backend
203
- (created pack backend in common)
203
+ (added new pack 'backend' in source 'common')
204
204
 
205
205
  + πŸ”§ nestjs-expert
206
206
  ...
207
207
  ```
208
208
 
209
+ Omit the item options to pick items from a checklist. Omit `--pack` too and `add-item` picks a pack first; omit `--source` as well and it goes fully interactive, looping over sources and packs until you're done.
210
+
209
211
  Use `remove-pack` and `remove-item` to drop packages or items you've added.
210
212
 
211
213
  ```text
@@ -217,6 +219,8 @@ $ agent-bolt remove-item --source=common --pack=common --skills=pr-review
217
219
  ...
218
220
  ```
219
221
 
222
+ Omit the item options to pick items from a checklist. Omit `--pack` too and `remove-item` picks a pack first; omit `--source` as well and it goes fully interactive, looping over sources and packs until you're done.
223
+
220
224
  These commands only edit your config file. The actual installation happens with `sync`.
221
225
 
222
226
  ### Install into your agents
@@ -563,16 +567,18 @@ Omitting `--packs` picks packages interactively; omitting `--source` as well pic
563
567
  Adds specific items to your config. Creates the package entry if it doesn't exist yet.
564
568
 
565
569
  ```text
566
- agent-bolt add-item --source <alias> --pack <name> [options]
570
+ agent-bolt add-item [--source <alias>] [--pack <name>] [options]
567
571
  ```
568
572
 
569
- | Option | Description | Required | Default |
570
- | --------------------- | ------------------------------------------- | -------- | ------- |
571
- | `--source <alias>` | Target catalog | Required | β€” |
572
- | `--pack <name>` | Target package | Required | β€” |
573
- | `--skills <list>` | Names of skills to add. Comma-separated | Optional | β€” |
574
- | `--agents <list>` | Names of subagents to add. Comma-separated | Optional | β€” |
575
- | `--guidelines <list>` | Names of guidelines to add. Comma-separated | Optional | β€” |
573
+ | Option | Description | Required | Default |
574
+ | --------------------- | ------------------------------------------- | -------- | ------------------ |
575
+ | `--source <alias>` | Target catalog | Optional | Interactive prompt |
576
+ | `--pack <name>` | Target package | Optional | Interactive prompt |
577
+ | `--skills <list>` | Names of skills to add. Comma-separated | Optional | β€” |
578
+ | `--agents <list>` | Names of subagents to add. Comma-separated | Optional | β€” |
579
+ | `--guidelines <list>` | Names of guidelines to add. Comma-separated | Optional | β€” |
580
+
581
+ Omitting the item options picks items interactively; omitting `--pack` too picks a pack first; omitting `--source` as well loops over sources. `--pack` requires `--source`, and the item options require `--pack`.
576
582
 
577
583
  ### `agent-bolt remove-pack`
578
584
 
@@ -594,16 +600,18 @@ Omitting `--packs` picks packages interactively; omitting `--source` as well pic
594
600
  Removes specific items from your config.
595
601
 
596
602
  ```text
597
- agent-bolt remove-item --source <alias> --pack <name> [options]
603
+ agent-bolt remove-item [--source <alias>] [--pack <name>] [options]
598
604
  ```
599
605
 
600
- | Option | Description | Required | Default |
601
- | --------------------- | ---------------------------------------------- | -------- | ------- |
602
- | `--source <alias>` | Target catalog | Required | β€” |
603
- | `--pack <name>` | Target package | Required | β€” |
604
- | `--skills <list>` | Names of skills to remove. Comma-separated | Optional | β€” |
605
- | `--agents <list>` | Names of subagents to remove. Comma-separated | Optional | β€” |
606
- | `--guidelines <list>` | Names of guidelines to remove. Comma-separated | Optional | β€” |
606
+ | Option | Description | Required | Default |
607
+ | --------------------- | ---------------------------------------------- | -------- | ------------------ |
608
+ | `--source <alias>` | Target catalog | Optional | Interactive prompt |
609
+ | `--pack <name>` | Target package | Optional | Interactive prompt |
610
+ | `--skills <list>` | Names of skills to remove. Comma-separated | Optional | β€” |
611
+ | `--agents <list>` | Names of subagents to remove. Comma-separated | Optional | β€” |
612
+ | `--guidelines <list>` | Names of guidelines to remove. Comma-separated | Optional | β€” |
613
+
614
+ Omitting the item options picks items interactively; omitting `--pack` too picks a pack first; omitting `--source` as well loops over sources. `--pack` requires `--source`, and the item options require `--pack`.
607
615
 
608
616
  ### `agent-bolt sync`
609
617
 
@@ -1,25 +1,42 @@
1
1
  import { type ItemType } from '#catalog/content/item/model.js';
2
- export type AddItemResult = {
3
- sourceAlias: string;
2
+ import type { ProgressReporter } from '#core/progress.js';
3
+ export type AddedItemPack = {
4
4
  packName: string;
5
5
  packCreated: boolean;
6
6
  addedItems: Record<ItemType, string[]>;
7
7
  skippedItems: Record<ItemType, string[]>;
8
8
  };
9
+ export type AddedItemSourceResult = {
10
+ sourceAlias: string;
11
+ packs: AddedItemPack[];
12
+ };
13
+ export type AddItemResult = {
14
+ results: AddedItemSourceResult[];
15
+ failures: string[];
16
+ };
9
17
  export declare class AddItemCommand {
10
- private readonly _source;
11
- private readonly _pack;
18
+ private readonly _source?;
19
+ private readonly _pack?;
12
20
  private readonly _skills?;
13
21
  private readonly _agents?;
14
22
  private readonly _guidelines?;
15
23
  constructor(options: {
16
- source: string;
17
- pack: string;
24
+ source?: string;
25
+ pack?: string;
18
26
  skills?: string;
19
27
  agents?: string;
20
28
  guidelines?: string;
21
29
  });
22
- execute(projectPath: string): AddItemResult;
30
+ execute(projectPath: string, reporter: ProgressReporter): Promise<AddItemResult>;
31
+ private addItemsFromSource;
32
+ private mergePackInto;
33
+ private addItemsToPack;
34
+ private resolveCatalogDetail;
35
+ private promptSourceSelection;
36
+ private countConfiguredItems;
37
+ private promptPackSelection;
38
+ private promptItemSelection;
39
+ private countAddedItems;
23
40
  private parseItemNames;
24
41
  }
25
42
  //# sourceMappingURL=add-item.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"add-item.d.ts","sourceRoot":"","sources":["../../../src/catalog/commands/add-item.ts"],"names":[],"mappings":"AAKA,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAG3E,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,YAAY,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;CAC1C,CAAC;AAEF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;gBAE1B,OAAO,EAAE;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB;IAQD,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa;IAwI3C,OAAO,CAAC,cAAc;CAQvB"}
1
+ {"version":3,"file":"add-item.d.ts","sourceRoot":"","sources":["../../../src/catalog/commands/add-item.ts"],"names":[],"mappings":"AASA,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAM3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,YAAY,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;gBAE1B,OAAO,EAAE;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB;IAQK,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;YA0GxE,kBAAkB;IAuFhC,OAAO,CAAC,aAAa;YAcP,cAAc;IA2H5B,OAAO,CAAC,oBAAoB;YAwBd,qBAAqB;IA6BnC,OAAO,CAAC,oBAAoB;YAsBd,mBAAmB;YA8CnB,mBAAmB;IAgFjC,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,cAAc;CAQvB"}
@@ -1,9 +1,14 @@
1
+ import { checkbox, select, Separator } from '@inquirer/prompts';
2
+ import chalk from 'chalk';
1
3
  import { buildCatalogConfigPath } from '#core/paths.js';
4
+ import { canPrompt } from '#core/tty.js';
2
5
  import { loadConfig } from '#catalog/config/load.js';
3
6
  import { editConfigFile } from '#catalog/config/write.js';
4
7
  import { resolveCatalogDir } from '#catalog/source/resolve.js';
5
8
  import { ITEM_TYPES } from '#catalog/content/item/model.js';
6
9
  import { parseCatalogDetail } from '#catalog/content/parse.js';
10
+ import { alignPackRows, pageSizeFor, truncate, visibleWidth } from '#cli/format.js';
11
+ import { itemSectionHeader } from '#cli/prompts.js';
7
12
  export class AddItemCommand {
8
13
  _source;
9
14
  _pack;
@@ -17,41 +22,179 @@ export class AddItemCommand {
17
22
  this._agents = options.agents;
18
23
  this._guidelines = options.guidelines;
19
24
  }
20
- execute(projectPath) {
25
+ async execute(projectPath, reporter) {
26
+ if (this._source !== undefined) {
27
+ const promptPack = this._pack === undefined;
28
+ const promptItems = this._skills === undefined && this._agents === undefined && this._guidelines === undefined;
29
+ if (promptPack && !promptItems) {
30
+ throw new Error('The --pack option is required with --skills, --agents, or --guidelines. e.g. --pack=git-workflow');
31
+ }
32
+ if ((promptPack || promptItems) && !canPrompt()) {
33
+ if (promptPack) {
34
+ throw new Error('The --pack option is required (or run add-item in an interactive terminal). e.g. --pack=git-workflow');
35
+ }
36
+ throw new Error('No items to add. Specify at least one of --skills, --agents, or --guidelines (or run add-item in an interactive terminal).');
37
+ }
38
+ const optionItemsByType = promptItems
39
+ ? undefined
40
+ : {
41
+ skills: this.parseItemNames(this._skills),
42
+ agents: this.parseItemNames(this._agents),
43
+ guidelines: this.parseItemNames(this._guidelines),
44
+ };
45
+ if (optionItemsByType) {
46
+ const totalInput = ITEM_TYPES.reduce((sum, type) => sum + optionItemsByType[type].length, 0);
47
+ if (totalInput === 0) {
48
+ throw new Error('No items to add. Specify at least one of --skills, --agents, or --guidelines.');
49
+ }
50
+ }
51
+ const result = await this.addItemsFromSource({
52
+ projectPath,
53
+ sourceAlias: this._source,
54
+ packName: this._pack,
55
+ optionItemsByType,
56
+ reporter,
57
+ });
58
+ return { results: [result], failures: [] };
59
+ }
60
+ if (this._pack !== undefined) {
61
+ throw new Error('The --source option is required with --pack. e.g. --source=common');
62
+ }
63
+ const hasItemOptions = this._skills !== undefined || this._agents !== undefined || this._guidelines !== undefined;
64
+ if (hasItemOptions) {
65
+ throw new Error('The --source option is required with --skills, --agents, or --guidelines. e.g. --source=common');
66
+ }
67
+ if (!canPrompt()) {
68
+ throw new Error('The --source option is required (or run add-item in an interactive terminal). e.g. --source=common');
69
+ }
70
+ const configPath = buildCatalogConfigPath(projectPath);
71
+ const bySource = new Map();
72
+ const failures = [];
73
+ while (true) {
74
+ const config = loadConfig(configPath);
75
+ if (Object.keys(config.sources).length === 0) {
76
+ throw new Error(`no sources configured. run 'agent-bolt init' to add one.`);
77
+ }
78
+ const sourceAlias = await this.promptSourceSelection(config.sources, bySource);
79
+ if (sourceAlias === null)
80
+ break;
81
+ try {
82
+ const result = await this.addItemsFromSource({
83
+ projectPath,
84
+ sourceAlias,
85
+ packName: undefined,
86
+ optionItemsByType: undefined,
87
+ reporter,
88
+ });
89
+ if (result.packs.length > 0) {
90
+ const merged = bySource.get(sourceAlias);
91
+ if (merged) {
92
+ for (const pack of result.packs) {
93
+ this.mergePackInto(merged.packs, pack);
94
+ }
95
+ }
96
+ else {
97
+ bySource.set(sourceAlias, result);
98
+ }
99
+ }
100
+ }
101
+ catch (e) {
102
+ failures.push(e instanceof Error ? e.message : String(e));
103
+ }
104
+ }
105
+ return { results: [...bySource.values()], failures };
106
+ }
107
+ async addItemsFromSource(options) {
108
+ const { projectPath, sourceAlias, packName, optionItemsByType, reporter } = options;
21
109
  const configPath = buildCatalogConfigPath(projectPath);
22
110
  const config = loadConfig(configPath);
23
- const source = config.sources[this._source];
111
+ const source = config.sources[sourceAlias];
24
112
  if (!source) {
25
- throw new Error(`source '${this._source}' not found in ${configPath}`);
113
+ throw new Error(`source '${sourceAlias}' not found in ${configPath}`);
26
114
  }
27
- const inputItemsByType = {
28
- skills: this.parseItemNames(this._skills),
29
- agents: this.parseItemNames(this._agents),
30
- guidelines: this.parseItemNames(this._guidelines),
31
- };
32
- const totalInput = ITEM_TYPES.reduce((sum, type) => sum + inputItemsByType[type].length, 0);
33
- if (totalInput === 0) {
34
- throw new Error('No items to add. Specify at least one of --skills, --agents, or --guidelines.');
115
+ const interactive = packName === undefined || optionItemsByType === undefined;
116
+ const catalogDetail = this.resolveCatalogDetail(projectPath, sourceAlias, source, reporter, interactive);
117
+ if (packName !== undefined) {
118
+ const packDetail = catalogDetail.find((pack) => pack.name === packName);
119
+ if (!packDetail) {
120
+ throw new Error(`unknown pack in source '${sourceAlias}': ${packName}. run 'agent-bolt list-packs --source=${sourceAlias}' to see available packs.`);
121
+ }
122
+ const pack = await this.addItemsToPack({
123
+ configPath,
124
+ config,
125
+ sourceAlias,
126
+ packDetail,
127
+ optionItemsByType,
128
+ });
129
+ return { sourceAlias, packs: [pack] };
130
+ }
131
+ if (catalogDetail.length === 0) {
132
+ throw new Error(`source '${sourceAlias}' has no packs.`);
133
+ }
134
+ const packs = [];
135
+ while (true) {
136
+ const freshConfig = loadConfig(configPath);
137
+ const existingPacks = freshConfig.packs[sourceAlias] ?? {};
138
+ const hasSelectablePack = catalogDetail.some((pack) => {
139
+ const configuredPack = existingPacks[pack.name];
140
+ const { added, total } = this.countConfiguredItems(pack, configuredPack);
141
+ return added < total;
142
+ });
143
+ if (!hasSelectablePack) {
144
+ if (packs.length === 0) {
145
+ console.log(chalk.dim(` all items in source '${sourceAlias}' are already added`));
146
+ }
147
+ break;
148
+ }
149
+ const selectedPackName = await this.promptPackSelection(catalogDetail, sourceAlias, existingPacks);
150
+ if (selectedPackName === null)
151
+ break;
152
+ const packDetail = catalogDetail.find((pack) => pack.name === selectedPackName);
153
+ const pack = await this.addItemsToPack({
154
+ configPath,
155
+ config: freshConfig,
156
+ sourceAlias,
157
+ packDetail,
158
+ optionItemsByType: undefined,
159
+ });
160
+ if (this.countAddedItems(pack) > 0) {
161
+ this.mergePackInto(packs, pack);
162
+ }
35
163
  }
36
- const catalogDir = resolveCatalogDir(projectPath, this._source, source);
37
- const catalogDetail = parseCatalogDetail(catalogDir);
38
- const packDetail = catalogDetail.find((packDetail) => packDetail.name === this._pack);
39
- if (!packDetail) {
40
- throw new Error(`unknown pack in source '${this._source}': ${this._pack}. run 'agent-bolt list-packs --source=${this._source}' to see available packs.`);
164
+ return { sourceAlias, packs };
165
+ }
166
+ mergePackInto(packs, pack) {
167
+ const existing = packs.find((existingPack) => existingPack.packName === pack.packName);
168
+ if (!existing) {
169
+ packs.push(pack);
170
+ return;
171
+ }
172
+ existing.packCreated = existing.packCreated || pack.packCreated;
173
+ for (const itemType of ITEM_TYPES) {
174
+ existing.addedItems[itemType].push(...pack.addedItems[itemType]);
175
+ existing.skippedItems[itemType].push(...pack.skippedItems[itemType]);
41
176
  }
177
+ }
178
+ async addItemsToPack(options) {
179
+ const { configPath, config, sourceAlias, packDetail, optionItemsByType } = options;
180
+ const packName = packDetail.name;
42
181
  const validItemsByType = {
43
182
  skills: new Set(packDetail.items.skills.map((item) => item.name)),
44
183
  agents: new Set(packDetail.items.agents.map((item) => item.name)),
45
184
  guidelines: new Set(packDetail.items.guidelines.map((item) => item.name)),
46
185
  };
47
186
  const guidelineRecommendations = new Map(packDetail.items.guidelines.map((guideline) => [guideline.name, guideline.recommended]));
48
- const existingPacks = config.packs[this._source];
49
- const existingPack = existingPacks ? existingPacks[this._pack] : undefined;
187
+ const existingPacks = config.packs[sourceAlias];
188
+ const existingPack = existingPacks ? existingPacks[packName] : undefined;
50
189
  const existingItemsByType = {
51
190
  skills: new Set(existingPack?.skills ?? []),
52
191
  agents: new Set(existingPack?.agents ?? []),
53
192
  guidelines: new Set(Object.keys(existingPack?.guidelines ?? {})),
54
193
  };
194
+ let inputItemsByType = optionItemsByType;
195
+ if (inputItemsByType === undefined) {
196
+ inputItemsByType = await this.promptItemSelection(sourceAlias, packDetail, existingItemsByType);
197
+ }
55
198
  const unknownItems = { skills: [], agents: [], guidelines: [] };
56
199
  const skippedItems = { skills: [], agents: [], guidelines: [] };
57
200
  const addedItems = { skills: [], agents: [], guidelines: [] };
@@ -75,15 +218,15 @@ export class AddItemCommand {
75
218
  if (hasUnknown) {
76
219
  const lines = ITEM_TYPES.filter((itemType) => unknownItems[itemType].length > 0).map((itemType) => ` ${itemType}: ${unknownItems[itemType].join(', ')}`);
77
220
  throw new Error([
78
- `unknown items in pack '${this._pack}':`,
221
+ `unknown items in pack '${packName}':`,
79
222
  lines.join('\n'),
80
- `run 'agent-bolt list-items --source=${this._source} --pack=${this._pack}' to see available items.`,
223
+ `run 'agent-bolt list-items --source=${sourceAlias} --pack=${packName}' to see available items.`,
81
224
  ].join('\n'));
82
225
  }
83
226
  const addedCount = ITEM_TYPES.reduce((sum, type) => sum + addedItems[type].length, 0);
84
227
  if (addedCount > 0) {
85
228
  editConfigFile(configPath, (document) => {
86
- const packPath = ['packs', this._source, this._pack];
229
+ const packPath = ['packs', sourceAlias, packName];
87
230
  if (existingPack) {
88
231
  for (const itemType of ITEM_TYPES) {
89
232
  const itemNames = addedItems[itemType];
@@ -123,13 +266,155 @@ export class AddItemCommand {
123
266
  });
124
267
  }
125
268
  return {
126
- sourceAlias: this._source,
127
- packName: this._pack,
269
+ packName,
128
270
  packCreated: existingPack === undefined,
129
271
  addedItems,
130
272
  skippedItems,
131
273
  };
132
274
  }
275
+ resolveCatalogDetail(projectPath, sourceAlias, source, reporter, interactive) {
276
+ if (!interactive) {
277
+ const catalogDir = resolveCatalogDir(projectPath, sourceAlias, source);
278
+ return parseCatalogDetail(catalogDir);
279
+ }
280
+ reporter.start(`Resolving source '${sourceAlias}'`);
281
+ try {
282
+ const catalogDir = resolveCatalogDir(projectPath, sourceAlias, source);
283
+ const catalogDetail = parseCatalogDetail(catalogDir);
284
+ reporter.succeed(`source '${sourceAlias}'`);
285
+ return catalogDetail;
286
+ }
287
+ catch (e) {
288
+ reporter.stop();
289
+ throw e;
290
+ }
291
+ }
292
+ async promptSourceSelection(sources, bySource) {
293
+ const entries = Object.entries(sources);
294
+ const aliasWidth = Math.max(0, ...entries.map(([alias]) => alias.length));
295
+ const sourceChoices = entries.map(([alias, source]) => {
296
+ const location = source.type === 'local' ? source.path : source.url;
297
+ const added = bySource.get(alias);
298
+ const addedCount = added
299
+ ? added.packs.reduce((sum, pack) => sum + this.countAddedItems(pack), 0)
300
+ : 0;
301
+ const addedNote = addedCount > 0 ? chalk.dim(` Β· added ${addedCount}`) : '';
302
+ return {
303
+ name: `${alias.padEnd(aliasWidth)} ${chalk.dim(`(${source.type} Β· ${location})`)}${addedNote}`,
304
+ value: alias,
305
+ short: alias,
306
+ };
307
+ });
308
+ return select({
309
+ message: 'Select a source (or finish)',
310
+ choices: [...sourceChoices, new Separator(), { name: 'Done', value: null }],
311
+ loop: false,
312
+ pageSize: pageSizeFor(sourceChoices.length + 2),
313
+ });
314
+ }
315
+ countConfiguredItems(packDetail, existingPack) {
316
+ const existingItemsByType = {
317
+ skills: new Set(existingPack?.skills ?? []),
318
+ agents: new Set(existingPack?.agents ?? []),
319
+ guidelines: new Set(Object.keys(existingPack?.guidelines ?? {})),
320
+ };
321
+ let added = 0;
322
+ let total = 0;
323
+ for (const itemType of ITEM_TYPES) {
324
+ const catalogItems = packDetail.items[itemType];
325
+ const configuredItemNames = existingItemsByType[itemType];
326
+ for (const item of catalogItems) {
327
+ total += 1;
328
+ if (configuredItemNames.has(item.name))
329
+ added += 1;
330
+ }
331
+ }
332
+ return { added, total };
333
+ }
334
+ async promptPackSelection(catalogDetail, sourceAlias, existingPacks) {
335
+ const rows = alignPackRows(catalogDetail.map((pack) => ({
336
+ name: pack.name,
337
+ counts: {
338
+ skills: pack.items.skills.length,
339
+ agents: pack.items.agents.length,
340
+ guidelines: pack.items.guidelines.length,
341
+ },
342
+ })));
343
+ const packChoices = rows.map((row) => {
344
+ const packDetail = catalogDetail.find((pack) => pack.name === row.name);
345
+ const { added, total } = this.countConfiguredItems(packDetail, existingPacks[row.name]);
346
+ let disabled = false;
347
+ let addedNote = '';
348
+ if (total === 0) {
349
+ disabled = '(no items)';
350
+ }
351
+ else if (added >= total) {
352
+ disabled = `(${added}/${total} added)`;
353
+ }
354
+ else if (added > 0) {
355
+ addedNote = chalk.dim(` (${added}/${total} added)`);
356
+ }
357
+ return {
358
+ name: `${row.paddedName} ${row.counts}${addedNote}`,
359
+ value: row.name,
360
+ short: row.name,
361
+ disabled,
362
+ };
363
+ });
364
+ return select({
365
+ message: `Select a pack in source '${sourceAlias}' (or finish)`,
366
+ choices: [...packChoices, new Separator(), { name: 'Done', value: null }],
367
+ loop: false,
368
+ pageSize: pageSizeFor(packChoices.length + 2),
369
+ });
370
+ }
371
+ async promptItemSelection(sourceAlias, packDetail, existingItemsByType) {
372
+ const empty = { skills: [], agents: [], guidelines: [] };
373
+ const nameWidth = Math.max(0, ...ITEM_TYPES.flatMap((itemType) => packDetail.items[itemType].map((item) => item.name.length)));
374
+ const hasSelectable = ITEM_TYPES.some((itemType) => packDetail.items[itemType].some((item) => !existingItemsByType[itemType].has(item.name)));
375
+ if (!hasSelectable) {
376
+ const totalItems = ITEM_TYPES.reduce((sum, itemType) => sum + packDetail.items[itemType].length, 0);
377
+ const note = totalItems === 0
378
+ ? `pack '${packDetail.name}' has no items`
379
+ : `all items in '${packDetail.name}' are already added`;
380
+ console.log(chalk.dim(` ${note}`));
381
+ return empty;
382
+ }
383
+ const choices = [];
384
+ const rowWidth = Math.max(0, ...ITEM_TYPES.flatMap((itemType) => packDetail.items[itemType].map((item) => nameWidth + 2 + visibleWidth(truncate(item.description, 50)))));
385
+ for (const itemType of ITEM_TYPES) {
386
+ const items = packDetail.items[itemType];
387
+ if (items.length === 0)
388
+ continue;
389
+ if (choices.length > 0)
390
+ choices.push(new Separator(' '));
391
+ choices.push(itemSectionHeader(itemType, rowWidth));
392
+ for (const item of items) {
393
+ const alreadyAdded = existingItemsByType[itemType].has(item.name);
394
+ choices.push({
395
+ name: `${item.name.padEnd(nameWidth)} ${chalk.dim(truncate(item.description, 50))}`,
396
+ value: { type: itemType, name: item.name },
397
+ short: item.name,
398
+ disabled: alreadyAdded ? '(already added)' : false,
399
+ });
400
+ }
401
+ }
402
+ const selected = await checkbox({
403
+ message: `Select items to add to '${sourceAlias}/${packDetail.name}'`,
404
+ choices,
405
+ loop: false,
406
+ pageSize: pageSizeFor(choices.length),
407
+ theme: { icon: { disabledUnchecked: 'βœ“' } },
408
+ });
409
+ const result = { skills: [], agents: [], guidelines: [] };
410
+ for (const { type, name } of selected) {
411
+ result[type].push(name);
412
+ }
413
+ return result;
414
+ }
415
+ countAddedItems(pack) {
416
+ return ITEM_TYPES.reduce((sum, itemType) => sum + pack.addedItems[itemType].length, 0);
417
+ }
133
418
  parseItemNames(value) {
134
419
  if (!value)
135
420
  return [];