gsd-pi 2.17.0 → 2.18.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 +39 -0
- package/dist/onboarding.js +2 -2
- package/dist/remote-questions-config.d.ts +10 -0
- package/dist/remote-questions-config.js +36 -0
- package/dist/resources/extensions/gsd/activity-log.ts +37 -7
- package/dist/resources/extensions/gsd/auto-prompts.ts +20 -1
- package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/dist/resources/extensions/gsd/auto.ts +123 -10
- package/dist/resources/extensions/gsd/commands.ts +245 -22
- package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +201 -2
- package/dist/resources/extensions/gsd/files.ts +123 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +237 -4
- package/dist/resources/extensions/gsd/index.ts +47 -3
- package/dist/resources/extensions/gsd/paths.ts +9 -0
- package/dist/resources/extensions/gsd/preferences.ts +59 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/dist/resources/extensions/gsd/prompts/system.md +2 -0
- package/dist/resources/extensions/gsd/queue-order.ts +231 -0
- package/dist/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/dist/resources/extensions/gsd/state.ts +15 -3
- package/dist/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +14 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/dist/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/dist/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/dist/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/dist/resources/extensions/gsd/worktree.ts +22 -0
- package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/cli/args.d.ts +5 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +21 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts +14 -3
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.js +52 -17
- package/packages/pi-coding-agent/dist/cli/list-models.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts +27 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +79 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +140 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js +162 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +100 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +113 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +26 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +98 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts +62 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js +145 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js +118 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +5 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +4 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -2
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +25 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +121 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +21 -0
- package/packages/pi-coding-agent/src/cli/list-models.ts +70 -17
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +170 -0
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +97 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +125 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +231 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +135 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +107 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.test.ts +145 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.ts +188 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +21 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/index.ts +5 -0
- package/packages/pi-coding-agent/src/main.ts +19 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +163 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +37 -0
- package/src/resources/extensions/gsd/activity-log.ts +37 -7
- package/src/resources/extensions/gsd/auto-prompts.ts +20 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/src/resources/extensions/gsd/auto.ts +123 -10
- package/src/resources/extensions/gsd/commands.ts +245 -22
- package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/src/resources/extensions/gsd/docs/preferences-reference.md +201 -2
- package/src/resources/extensions/gsd/files.ts +123 -1
- package/src/resources/extensions/gsd/guided-flow.ts +237 -4
- package/src/resources/extensions/gsd/index.ts +47 -3
- package/src/resources/extensions/gsd/paths.ts +9 -0
- package/src/resources/extensions/gsd/preferences.ts +59 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/src/resources/extensions/gsd/prompts/system.md +2 -0
- package/src/resources/extensions/gsd/queue-order.ts +231 -0
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/src/resources/extensions/gsd/state.ts +15 -3
- package/src/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/src/resources/extensions/gsd/templates/preferences.md +14 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/src/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/src/resources/extensions/gsd/worktree.ts +22 -0
- package/src/resources/extensions/shared/next-action-ui.ts +16 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end integration tests for the Queue Reorder feature.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the full chain: QUEUE-ORDER.json + findMilestoneIds() + deriveState()
|
|
5
|
+
* + depends_on removal from CONTEXT.md files.
|
|
6
|
+
*
|
|
7
|
+
* These tests simulate what happens when a user reorders milestones and confirms:
|
|
8
|
+
* 1. QUEUE-ORDER.json is written with the new order
|
|
9
|
+
* 2. depends_on is removed from CONTEXT.md frontmatter
|
|
10
|
+
* 3. deriveState() picks the correct milestone as active
|
|
11
|
+
* 4. A fresh deriveState() call (simulating new session) also works
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
|
|
18
|
+
import { deriveState, invalidateStateCache } from '../state.ts';
|
|
19
|
+
import { findMilestoneIds } from '../guided-flow.ts';
|
|
20
|
+
import { saveQueueOrder, loadQueueOrder } from '../queue-order.ts';
|
|
21
|
+
import { parseContextDependsOn } from '../files.ts';
|
|
22
|
+
import { createTestContext } from './test-helpers.ts';
|
|
23
|
+
|
|
24
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
25
|
+
|
|
26
|
+
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
function createFixtureBase(): string {
|
|
29
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-reorder-e2e-'));
|
|
30
|
+
mkdirSync(join(base, '.gsd', 'milestones'), { recursive: true });
|
|
31
|
+
return base;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cleanup(base: string): void {
|
|
35
|
+
rmSync(base, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function writeMilestoneDir(base: string, mid: string): void {
|
|
39
|
+
mkdirSync(join(base, '.gsd', 'milestones', mid), { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function writeContext(base: string, mid: string, frontmatter: string, body: string = ''): void {
|
|
43
|
+
const dir = join(base, '.gsd', 'milestones', mid);
|
|
44
|
+
mkdirSync(dir, { recursive: true });
|
|
45
|
+
const fm = frontmatter ? `---\n${frontmatter}\n---\n\n` : '';
|
|
46
|
+
writeFileSync(join(dir, `${mid}-CONTEXT.md`), `${fm}# ${mid}: Test\n\n${body}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function writeCompleteMilestone(base: string, mid: string): void {
|
|
50
|
+
const dir = join(base, '.gsd', 'milestones', mid);
|
|
51
|
+
mkdirSync(dir, { recursive: true });
|
|
52
|
+
writeFileSync(join(dir, `${mid}-ROADMAP.md`), `# ${mid}: Complete
|
|
53
|
+
|
|
54
|
+
**Vision:** Done.
|
|
55
|
+
|
|
56
|
+
## Slices
|
|
57
|
+
|
|
58
|
+
- [x] **S01: Done** \`risk:low\` \`depends:[]\`
|
|
59
|
+
> After this: Done.
|
|
60
|
+
`);
|
|
61
|
+
writeFileSync(join(dir, `${mid}-SUMMARY.md`), `# ${mid} Summary\n\nComplete.`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function readContextFile(base: string, mid: string): string {
|
|
65
|
+
return readFileSync(join(base, '.gsd', 'milestones', mid, `${mid}-CONTEXT.md`), 'utf-8');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
|
+
// Test: Queue order changes milestone activation
|
|
70
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
console.log('\n=== E2E: queue-order changes active milestone ===');
|
|
73
|
+
{
|
|
74
|
+
const base = createFixtureBase();
|
|
75
|
+
try {
|
|
76
|
+
// Setup: M007 complete, M008 and M009 pending (no context, no roadmap)
|
|
77
|
+
writeCompleteMilestone(base, 'M007');
|
|
78
|
+
writeMilestoneDir(base, 'M008');
|
|
79
|
+
writeContext(base, 'M008', '', 'Multi-Session Parallel Orchestration');
|
|
80
|
+
writeMilestoneDir(base, 'M009');
|
|
81
|
+
writeContext(base, 'M009', '', 'Context-Budget Visibility');
|
|
82
|
+
|
|
83
|
+
// Without custom order: M008 comes first (numeric sort)
|
|
84
|
+
invalidateStateCache();
|
|
85
|
+
const stateBefore = await deriveState(base);
|
|
86
|
+
assertEq(stateBefore.activeMilestone?.id, 'M008', 'before reorder: M008 is active');
|
|
87
|
+
|
|
88
|
+
// Save custom order: M009 before M008
|
|
89
|
+
saveQueueOrder(base, ['M009', 'M008']);
|
|
90
|
+
|
|
91
|
+
// With custom order: M009 should be active
|
|
92
|
+
invalidateStateCache();
|
|
93
|
+
const stateAfter = await deriveState(base);
|
|
94
|
+
assertEq(stateAfter.activeMilestone?.id, 'M009', 'after reorder: M009 is active');
|
|
95
|
+
|
|
96
|
+
// findMilestoneIds respects the order
|
|
97
|
+
const ids = findMilestoneIds(base);
|
|
98
|
+
const m008Idx = ids.indexOf('M008');
|
|
99
|
+
const m009Idx = ids.indexOf('M009');
|
|
100
|
+
assertTrue(m009Idx < m008Idx, 'findMilestoneIds: M009 comes before M008');
|
|
101
|
+
|
|
102
|
+
} finally {
|
|
103
|
+
cleanup(base);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
108
|
+
// Test: Reorder + depends_on removal = correct state
|
|
109
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
110
|
+
|
|
111
|
+
console.log('\n=== E2E: reorder with depends_on removal ===');
|
|
112
|
+
{
|
|
113
|
+
const base = createFixtureBase();
|
|
114
|
+
try {
|
|
115
|
+
// Setup: M007 complete, M008 depends_on M009, M009 no deps
|
|
116
|
+
writeCompleteMilestone(base, 'M007');
|
|
117
|
+
writeContext(base, 'M008', 'depends_on: [M009]', 'Multi-Session Parallel');
|
|
118
|
+
writeContext(base, 'M009', '', 'Context-Budget Visibility');
|
|
119
|
+
|
|
120
|
+
// Before: M008 depends on M009, so deriveState skips M008, M009 is active
|
|
121
|
+
invalidateStateCache();
|
|
122
|
+
const stateBefore = await deriveState(base);
|
|
123
|
+
assertEq(stateBefore.activeMilestone?.id, 'M009', 'before: M009 active (M008 dep-blocked)');
|
|
124
|
+
|
|
125
|
+
// Simulate reorder confirm: save order M009→M008, remove depends_on from M008
|
|
126
|
+
saveQueueOrder(base, ['M009', 'M008']);
|
|
127
|
+
|
|
128
|
+
// Remove depends_on from M008-CONTEXT.md (simulating what handleQueueReorder does)
|
|
129
|
+
const contextContent = readContextFile(base, 'M008');
|
|
130
|
+
const newContent = contextContent.replace(/---\ndepends_on: \[M009\]\n---\n\n/, '');
|
|
131
|
+
writeFileSync(join(base, '.gsd', 'milestones', 'M008', 'M008-CONTEXT.md'), newContent);
|
|
132
|
+
|
|
133
|
+
// Verify: depends_on is gone
|
|
134
|
+
const updatedContent = readContextFile(base, 'M008');
|
|
135
|
+
const deps = parseContextDependsOn(updatedContent);
|
|
136
|
+
assertEq(deps.length, 0, 'depends_on removed from M008-CONTEXT.md');
|
|
137
|
+
|
|
138
|
+
// Verify: deriveState still picks M009 (it's first in queue order)
|
|
139
|
+
invalidateStateCache();
|
|
140
|
+
const stateAfter = await deriveState(base);
|
|
141
|
+
assertEq(stateAfter.activeMilestone?.id, 'M009', 'after: M009 still active (first in queue)');
|
|
142
|
+
|
|
143
|
+
// Verify: M008 is now pending (not dep-blocked)
|
|
144
|
+
const m008Entry = stateAfter.registry.find(m => m.id === 'M008');
|
|
145
|
+
assertEq(m008Entry?.status, 'pending', 'M008 is pending (not dep-blocked)');
|
|
146
|
+
assertTrue(!m008Entry?.dependsOn || m008Entry.dependsOn.length === 0, 'M008 has no dependsOn');
|
|
147
|
+
|
|
148
|
+
} finally {
|
|
149
|
+
cleanup(base);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
154
|
+
// Test: Fresh deriveState (simulating new session) respects queue order
|
|
155
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
156
|
+
|
|
157
|
+
console.log('\n=== E2E: fresh session respects queue order ===');
|
|
158
|
+
{
|
|
159
|
+
const base = createFixtureBase();
|
|
160
|
+
try {
|
|
161
|
+
writeCompleteMilestone(base, 'M007');
|
|
162
|
+
writeContext(base, 'M008', '', 'Parallel Orchestration');
|
|
163
|
+
writeContext(base, 'M009', '', 'Budget Visibility');
|
|
164
|
+
|
|
165
|
+
// Save queue order
|
|
166
|
+
saveQueueOrder(base, ['M009', 'M008']);
|
|
167
|
+
|
|
168
|
+
// Simulate fresh session — invalidate all caches
|
|
169
|
+
invalidateStateCache();
|
|
170
|
+
|
|
171
|
+
// Derive state — should read QUEUE-ORDER.json from disk
|
|
172
|
+
const state = await deriveState(base);
|
|
173
|
+
assertEq(state.activeMilestone?.id, 'M009', 'fresh session: M009 is active');
|
|
174
|
+
|
|
175
|
+
// Verify queue order persisted
|
|
176
|
+
const order = loadQueueOrder(base);
|
|
177
|
+
assertEq(order, ['M009', 'M008'], 'QUEUE-ORDER.json persisted correctly');
|
|
178
|
+
|
|
179
|
+
} finally {
|
|
180
|
+
cleanup(base);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
185
|
+
// Test: Queue order with newly added milestones
|
|
186
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
187
|
+
|
|
188
|
+
console.log('\n=== E2E: new milestones appended to queue ===');
|
|
189
|
+
{
|
|
190
|
+
const base = createFixtureBase();
|
|
191
|
+
try {
|
|
192
|
+
writeCompleteMilestone(base, 'M007');
|
|
193
|
+
writeContext(base, 'M008', '', 'Parallel');
|
|
194
|
+
writeContext(base, 'M009', '', 'Visibility');
|
|
195
|
+
|
|
196
|
+
// Custom order only has M009, M008
|
|
197
|
+
saveQueueOrder(base, ['M009', 'M008']);
|
|
198
|
+
|
|
199
|
+
// Add M010 (not in queue order)
|
|
200
|
+
writeContext(base, 'M010', '', 'New feature');
|
|
201
|
+
|
|
202
|
+
invalidateStateCache();
|
|
203
|
+
const ids = findMilestoneIds(base);
|
|
204
|
+
|
|
205
|
+
// M009 first, M008 second, M010 appended at end
|
|
206
|
+
const m009Idx = ids.indexOf('M009');
|
|
207
|
+
const m008Idx = ids.indexOf('M008');
|
|
208
|
+
const m010Idx = ids.indexOf('M010');
|
|
209
|
+
assertTrue(m009Idx < m008Idx, 'M009 before M008');
|
|
210
|
+
assertTrue(m008Idx < m010Idx, 'M008 before M010 (new milestone appended)');
|
|
211
|
+
|
|
212
|
+
// M009 is still active (first non-complete in queue order)
|
|
213
|
+
const state = await deriveState(base);
|
|
214
|
+
assertEq(state.activeMilestone?.id, 'M009', 'M009 still active after M010 added');
|
|
215
|
+
|
|
216
|
+
} finally {
|
|
217
|
+
cleanup(base);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
222
|
+
// Test: No queue order file = default numeric sort (backward compat)
|
|
223
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
224
|
+
|
|
225
|
+
console.log('\n=== E2E: backward compat without QUEUE-ORDER.json ===');
|
|
226
|
+
{
|
|
227
|
+
const base = createFixtureBase();
|
|
228
|
+
try {
|
|
229
|
+
writeCompleteMilestone(base, 'M007');
|
|
230
|
+
writeContext(base, 'M008', '', 'Parallel');
|
|
231
|
+
writeContext(base, 'M009', '', 'Visibility');
|
|
232
|
+
|
|
233
|
+
// No QUEUE-ORDER.json — default numeric sort
|
|
234
|
+
invalidateStateCache();
|
|
235
|
+
const state = await deriveState(base);
|
|
236
|
+
assertEq(state.activeMilestone?.id, 'M008', 'no queue order: M008 active (numeric)');
|
|
237
|
+
|
|
238
|
+
const ids = findMilestoneIds(base);
|
|
239
|
+
assertTrue(ids.indexOf('M008') < ids.indexOf('M009'), 'default sort: M008 before M009');
|
|
240
|
+
|
|
241
|
+
} finally {
|
|
242
|
+
cleanup(base);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
247
|
+
// Test: depends_on inline array format removal
|
|
248
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
249
|
+
|
|
250
|
+
console.log('\n=== E2E: depends_on inline format preserved after partial removal ===');
|
|
251
|
+
{
|
|
252
|
+
const base = createFixtureBase();
|
|
253
|
+
try {
|
|
254
|
+
writeCompleteMilestone(base, 'M007');
|
|
255
|
+
// M008 depends on both M009 and M010
|
|
256
|
+
writeContext(base, 'M008', 'depends_on: [M009, M010]', 'Parallel');
|
|
257
|
+
writeContext(base, 'M009', '', 'Visibility');
|
|
258
|
+
writeContext(base, 'M010', '', 'Other');
|
|
259
|
+
|
|
260
|
+
// Verify both deps are parsed
|
|
261
|
+
const contentBefore = readContextFile(base, 'M008');
|
|
262
|
+
const depsBefore = parseContextDependsOn(contentBefore);
|
|
263
|
+
assertEq(depsBefore.length, 2, 'M008 has 2 deps before');
|
|
264
|
+
|
|
265
|
+
// Simulate removing only M009 dep (keep M010)
|
|
266
|
+
const content = readContextFile(base, 'M008');
|
|
267
|
+
const updated = content.replace('depends_on: [M009, M010]', 'depends_on: [M010]');
|
|
268
|
+
writeFileSync(join(base, '.gsd', 'milestones', 'M008', 'M008-CONTEXT.md'), updated);
|
|
269
|
+
|
|
270
|
+
// Verify only M010 remains
|
|
271
|
+
const contentAfter = readContextFile(base, 'M008');
|
|
272
|
+
const depsAfter = parseContextDependsOn(contentAfter);
|
|
273
|
+
assertEq(depsAfter.length, 1, 'M008 has 1 dep after removal');
|
|
274
|
+
assertEq(depsAfter[0], 'M010', 'remaining dep is M010');
|
|
275
|
+
|
|
276
|
+
} finally {
|
|
277
|
+
cleanup(base);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
report();
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stale-worktree-cwd.test.ts — Tests for #608 fix.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that when process.cwd() is inside a stale .gsd/worktrees/ path,
|
|
5
|
+
* startAuto escapes back to the project root before proceeding.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { mkdtempSync, mkdirSync, rmSync, existsSync, realpathSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join, sep } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { execSync } from "node:child_process";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
createAutoWorktree,
|
|
17
|
+
teardownAutoWorktree,
|
|
18
|
+
mergeMilestoneToMain,
|
|
19
|
+
} from "../auto-worktree.ts";
|
|
20
|
+
|
|
21
|
+
function run(command: string, cwd: string): string {
|
|
22
|
+
return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createTempRepo(): string {
|
|
26
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "stale-wt-test-")));
|
|
27
|
+
run("git init", dir);
|
|
28
|
+
run("git config user.email test@test.com", dir);
|
|
29
|
+
run("git config user.name Test", dir);
|
|
30
|
+
writeFileSync(join(dir, "README.md"), "# test\n");
|
|
31
|
+
run("git add .", dir);
|
|
32
|
+
run("git commit -m init", dir);
|
|
33
|
+
run("git branch -M main", dir);
|
|
34
|
+
return dir;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ─── escapeStaleWorktree is called by startAuto, test the detection logic ────
|
|
38
|
+
|
|
39
|
+
test("detects stale worktree path and extracts project root", () => {
|
|
40
|
+
// Simulate the path pattern: /project/.gsd/worktrees/M004/...
|
|
41
|
+
const projectRoot = "/Users/test/myproject";
|
|
42
|
+
const stalePath = `${projectRoot}${sep}.gsd${sep}worktrees${sep}M004`;
|
|
43
|
+
|
|
44
|
+
const marker = `${sep}.gsd${sep}worktrees${sep}`;
|
|
45
|
+
const idx = stalePath.indexOf(marker);
|
|
46
|
+
|
|
47
|
+
assert.ok(idx !== -1, "marker found in stale path");
|
|
48
|
+
assert.equal(stalePath.slice(0, idx), projectRoot, "project root extracted correctly");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("does not trigger on normal project path", () => {
|
|
52
|
+
const normalPath = "/Users/test/myproject";
|
|
53
|
+
const marker = `${sep}.gsd${sep}worktrees${sep}`;
|
|
54
|
+
const idx = normalPath.indexOf(marker);
|
|
55
|
+
|
|
56
|
+
assert.equal(idx, -1, "marker not found in normal path");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ─── Integration: mergeMilestoneToMain restores cwd ─────────────────────────
|
|
60
|
+
|
|
61
|
+
test("mergeMilestoneToMain restores cwd to project root", () => {
|
|
62
|
+
const savedCwd = process.cwd();
|
|
63
|
+
let tempDir = "";
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
tempDir = createTempRepo();
|
|
67
|
+
|
|
68
|
+
// Create milestone planning artifacts
|
|
69
|
+
const msDir = join(tempDir, ".gsd", "milestones", "M050");
|
|
70
|
+
mkdirSync(msDir, { recursive: true });
|
|
71
|
+
writeFileSync(join(msDir, "CONTEXT.md"), "# M050 Context\n");
|
|
72
|
+
const roadmap = [
|
|
73
|
+
"# M050: Test Milestone",
|
|
74
|
+
"**Vision**: testing",
|
|
75
|
+
"## Success Criteria",
|
|
76
|
+
"- It works",
|
|
77
|
+
"## Slices",
|
|
78
|
+
"- [x] S01 — First slice",
|
|
79
|
+
].join("\n");
|
|
80
|
+
writeFileSync(join(msDir, "ROADMAP.md"), roadmap);
|
|
81
|
+
run("git add .", tempDir);
|
|
82
|
+
run("git commit -m \"add milestone\"", tempDir);
|
|
83
|
+
|
|
84
|
+
// Create auto-worktree (enters the worktree dir)
|
|
85
|
+
const wtPath = createAutoWorktree(tempDir, "M050");
|
|
86
|
+
assert.equal(process.cwd(), wtPath, "cwd is in worktree after create");
|
|
87
|
+
|
|
88
|
+
// Add a change in the worktree
|
|
89
|
+
writeFileSync(join(wtPath, "feature.txt"), "new feature\n");
|
|
90
|
+
run("git add .", wtPath);
|
|
91
|
+
run("git commit -m \"feat: add feature\"", wtPath);
|
|
92
|
+
|
|
93
|
+
// Merge back — should restore cwd to tempDir
|
|
94
|
+
mergeMilestoneToMain(tempDir, "M050", roadmap);
|
|
95
|
+
|
|
96
|
+
assert.equal(process.cwd(), tempDir, "cwd restored to project root after merge");
|
|
97
|
+
assert.ok(!existsSync(wtPath), "worktree directory removed after merge");
|
|
98
|
+
} finally {
|
|
99
|
+
process.chdir(savedCwd);
|
|
100
|
+
if (tempDir && existsSync(tempDir)) {
|
|
101
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ─── Integration: stale worktree directory is detectable ────────────────────
|
|
107
|
+
|
|
108
|
+
test("process.cwd() inside removed worktree is recoverable", () => {
|
|
109
|
+
const savedCwd = process.cwd();
|
|
110
|
+
let tempDir = "";
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
tempDir = createTempRepo();
|
|
114
|
+
|
|
115
|
+
// Create a .gsd/worktrees/M099 directory to simulate stale state
|
|
116
|
+
const staleWtDir = join(tempDir, ".gsd", "worktrees", "M099");
|
|
117
|
+
mkdirSync(staleWtDir, { recursive: true });
|
|
118
|
+
|
|
119
|
+
// Enter the stale directory
|
|
120
|
+
process.chdir(staleWtDir);
|
|
121
|
+
const cwdBefore = process.cwd();
|
|
122
|
+
assert.ok(cwdBefore.includes(`${sep}.gsd${sep}worktrees${sep}`), "cwd is inside worktree dir");
|
|
123
|
+
|
|
124
|
+
// Simulate escapeStaleWorktree logic
|
|
125
|
+
const marker = `${sep}.gsd${sep}worktrees${sep}`;
|
|
126
|
+
const idx = cwdBefore.indexOf(marker);
|
|
127
|
+
assert.ok(idx !== -1, "marker found");
|
|
128
|
+
|
|
129
|
+
const projectRoot = cwdBefore.slice(0, idx);
|
|
130
|
+
process.chdir(projectRoot);
|
|
131
|
+
|
|
132
|
+
assert.equal(process.cwd(), tempDir, "successfully escaped to project root");
|
|
133
|
+
} finally {
|
|
134
|
+
process.chdir(savedCwd);
|
|
135
|
+
if (tempDir && existsSync(tempDir)) {
|
|
136
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
@@ -94,7 +94,7 @@ export function worktreeBranchName(name: string): string {
|
|
|
94
94
|
*
|
|
95
95
|
* @param opts.branch — override the default `worktree/<name>` branch name
|
|
96
96
|
*/
|
|
97
|
-
export function createWorktree(basePath: string, name: string, opts: { branch?: string } = {}): WorktreeInfo {
|
|
97
|
+
export function createWorktree(basePath: string, name: string, opts: { branch?: string; startPoint?: string } = {}): WorktreeInfo {
|
|
98
98
|
// Validate name: alphanumeric, hyphens, underscores only
|
|
99
99
|
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
100
100
|
throw new Error(`Invalid worktree name "${name}". Use only letters, numbers, hyphens, and underscores.`);
|
|
@@ -114,9 +114,12 @@ export function createWorktree(basePath: string, name: string, opts: { branch?:
|
|
|
114
114
|
// Prune any stale worktree entries from a previous removal
|
|
115
115
|
nativeWorktreePrune(basePath);
|
|
116
116
|
|
|
117
|
+
// Use the explicit start point (e.g. integration branch) if provided,
|
|
118
|
+
// otherwise fall back to the repo's detected main branch.
|
|
119
|
+
const startPoint = opts.startPoint ?? nativeDetectMainBranch(basePath);
|
|
120
|
+
|
|
117
121
|
// Check if the branch already exists (leftover from a previous worktree)
|
|
118
122
|
const branchAlreadyExists = nativeBranchExists(basePath, branch);
|
|
119
|
-
const mainBranch = nativeDetectMainBranch(basePath);
|
|
120
123
|
|
|
121
124
|
if (branchAlreadyExists) {
|
|
122
125
|
// Check if the branch is actively used by an existing worktree.
|
|
@@ -130,11 +133,11 @@ export function createWorktree(basePath: string, name: string, opts: { branch?:
|
|
|
130
133
|
);
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
// Reset the stale branch to
|
|
134
|
-
nativeBranchForceReset(basePath, branch,
|
|
136
|
+
// Reset the stale branch to the start point, then attach worktree to it
|
|
137
|
+
nativeBranchForceReset(basePath, branch, startPoint);
|
|
135
138
|
nativeWorktreeAdd(basePath, wtPath, branch);
|
|
136
139
|
} else {
|
|
137
|
-
nativeWorktreeAdd(basePath, wtPath, branch, true,
|
|
140
|
+
nativeWorktreeAdd(basePath, wtPath, branch, true, startPoint);
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
return {
|
|
@@ -76,6 +76,28 @@ export function detectWorktreeName(basePath: string): string | null {
|
|
|
76
76
|
return name || null;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the project root from a path that may be inside a worktree.
|
|
81
|
+
* If the path contains `/.gsd/worktrees/<name>/`, returns the portion
|
|
82
|
+
* before `/.gsd/`. Otherwise returns the input unchanged.
|
|
83
|
+
*
|
|
84
|
+
* Use this in commands that call `process.cwd()` to ensure they always
|
|
85
|
+
* operate against the real project root, not a worktree subdirectory.
|
|
86
|
+
*/
|
|
87
|
+
export function resolveProjectRoot(basePath: string): string {
|
|
88
|
+
const normalizedPath = basePath.replaceAll("\\", "/");
|
|
89
|
+
const marker = "/.gsd/worktrees/";
|
|
90
|
+
const idx = normalizedPath.indexOf(marker);
|
|
91
|
+
if (idx === -1) return basePath;
|
|
92
|
+
// Return the original path up to the .gsd/ marker (un-normalized)
|
|
93
|
+
// Account for potential OS-specific separators
|
|
94
|
+
const sep = basePath.includes("\\") ? "\\" : "/";
|
|
95
|
+
const markerOs = `${sep}.gsd${sep}worktrees${sep}`;
|
|
96
|
+
const idxOs = basePath.indexOf(markerOs);
|
|
97
|
+
if (idxOs !== -1) return basePath.slice(0, idxOs);
|
|
98
|
+
return basePath.slice(0, idx);
|
|
99
|
+
}
|
|
100
|
+
|
|
79
101
|
/**
|
|
80
102
|
* Get the slice branch name, namespaced by worktree when inside one.
|
|
81
103
|
*
|
|
@@ -118,7 +118,7 @@ export async function showNextAction(
|
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
const result = await ctx.ui.custom<string>((_tui: TUI, theme: Theme, _kb, done) => {
|
|
122
122
|
let cursorIdx = defaultIdx;
|
|
123
123
|
let cachedLines: string[] | undefined;
|
|
124
124
|
|
|
@@ -194,4 +194,19 @@ export async function showNextAction(
|
|
|
194
194
|
|
|
195
195
|
return { render, invalidate: () => { cachedLines = undefined; }, handleInput };
|
|
196
196
|
});
|
|
197
|
+
|
|
198
|
+
// Fallback for RPC mode where ctx.ui.custom() returns undefined (#447).
|
|
199
|
+
// Fall back to ctx.ui.select() which IS implemented in RPC mode.
|
|
200
|
+
if (result === undefined || result === null) {
|
|
201
|
+
const labels = allActions.map(a => {
|
|
202
|
+
const tag = a.recommended ? " (recommended)" : "";
|
|
203
|
+
return `${a.label}${tag}: ${a.description}`;
|
|
204
|
+
});
|
|
205
|
+
const selected = await ctx.ui.select(opts.title, labels);
|
|
206
|
+
if (selected === undefined || selected === null) return "not_yet";
|
|
207
|
+
const idx = labels.indexOf(selected as string);
|
|
208
|
+
return idx >= 0 ? allActions[idx].id : "not_yet";
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return result;
|
|
197
212
|
}
|
package/package.json
CHANGED
|
@@ -33,6 +33,11 @@ export interface Args {
|
|
|
33
33
|
themes?: string[];
|
|
34
34
|
noThemes?: boolean;
|
|
35
35
|
listModels?: string | true;
|
|
36
|
+
discover?: boolean;
|
|
37
|
+
addProvider?: string;
|
|
38
|
+
addProviderBaseUrl?: string;
|
|
39
|
+
addProviderApiKey?: string;
|
|
40
|
+
discoverModels?: string | true;
|
|
36
41
|
offline?: boolean;
|
|
37
42
|
verbose?: boolean;
|
|
38
43
|
messages: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;CAC5C;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE,CAAC,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;CAC5C;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE,CAAC,GAAG,IAAI,CAsI5G;AAED,wBAAgB,SAAS,IAAI,IAAI,CA8IhC"}
|
|
@@ -129,6 +129,23 @@ export function parseArgs(args, extensionFlags) {
|
|
|
129
129
|
result.listModels = true;
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
else if (arg === "--discover") {
|
|
133
|
+
result.discover = true;
|
|
134
|
+
}
|
|
135
|
+
else if (arg === "--add-provider" && i + 1 < args.length) {
|
|
136
|
+
result.addProvider = args[++i];
|
|
137
|
+
}
|
|
138
|
+
else if (arg === "--base-url" && i + 1 < args.length) {
|
|
139
|
+
result.addProviderBaseUrl = args[++i];
|
|
140
|
+
}
|
|
141
|
+
else if (arg === "--discover-models") {
|
|
142
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("-") && !args[i + 1].startsWith("@")) {
|
|
143
|
+
result.discoverModels = args[++i];
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
result.discoverModels = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
132
149
|
else if (arg === "--verbose") {
|
|
133
150
|
result.verbose = true;
|
|
134
151
|
}
|
|
@@ -201,6 +218,10 @@ ${chalk.bold("Options:")}
|
|
|
201
218
|
--no-themes Disable theme discovery and loading
|
|
202
219
|
--export <file> Export session file to HTML and exit
|
|
203
220
|
--list-models [search] List available models (with optional fuzzy search)
|
|
221
|
+
--discover Include discovered models in --list-models output
|
|
222
|
+
--discover-models [provider] Discover models from provider APIs (all or specific)
|
|
223
|
+
--add-provider <name> Add a provider to models.json (use with --base-url, --api-key)
|
|
224
|
+
--base-url <url> Base URL for --add-provider
|
|
204
225
|
--verbose Force verbose startup (overrides quietStartup setting)
|
|
205
226
|
--offline Disable startup network operations (same as PI_OFFLINE=1)
|
|
206
227
|
--help, -h Show this help
|