@onefl666/dsh-great-plan 0.0.0-stage → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +102 -2
- package/README.zh-CN.md +103 -0
- package/cordis.patch.yml +3 -0
- package/icon.svg +19 -0
- package/lib/client.actions.js +963 -0
- package/lib/client.js +366 -0
- package/lib/client.model.js +232 -0
- package/lib/client.panel.js +749 -0
- package/lib/client.preset.js +115 -0
- package/lib/client.settings.js +168 -0
- package/lib/client.ui.js +528 -0
- package/lib/host/apply.js +25 -0
- package/lib/host/config.js +19 -0
- package/lib/host/plan-compaction.js +135 -0
- package/lib/host/plan-evidence.js +195 -0
- package/lib/host/plan-range.js +35 -0
- package/lib/index.js +2 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +69 -4
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { EXIT_PLAN_MODE_TOOL, selectPlanCompactionRange } from './plan-range.js';
|
|
2
|
+
|
|
3
|
+
// The canonical name lives in plan-range.js; re-exported for existing importers.
|
|
4
|
+
export { EXIT_PLAN_MODE_TOOL };
|
|
5
|
+
|
|
6
|
+
/** The command accepts no arguments; this is its user-visible usage line. */
|
|
7
|
+
export const USAGE = 'Usage: /great-plan-compact (no arguments)';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Stable manual-compaction error codes mapped to user text.
|
|
11
|
+
*
|
|
12
|
+
* Every value is user-visible; the Client mirrors each string in
|
|
13
|
+
* `COMPACT_MESSAGE_KEYS` so it can be localized (cross-side test).
|
|
14
|
+
*/
|
|
15
|
+
export const FAILURES = {
|
|
16
|
+
busy: 'Compaction is unavailable because another compaction is running.',
|
|
17
|
+
changed: 'The history selected for compaction changed during the attempt.',
|
|
18
|
+
summary: 'Compaction could not produce a useful summary.',
|
|
19
|
+
commit: 'Compaction did not finish cleanly.',
|
|
20
|
+
persistence: 'Compaction finished, but the session could not be saved.',
|
|
21
|
+
cancelled: 'Compaction was cancelled.',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Static command failure messages, also mirrored by the Client's
|
|
26
|
+
* `COMPACT_MESSAGE_KEYS`. Dynamic messages (`...: ${messageOf(error)}`) are
|
|
27
|
+
* intentionally not listed and fall back to raw English when unmapped.
|
|
28
|
+
*/
|
|
29
|
+
export const MESSAGES = {
|
|
30
|
+
planStateUnavailable: 'Plan state is unavailable in this composition.',
|
|
31
|
+
planInactive: 'Plan mode is not active; nothing to compact.',
|
|
32
|
+
backendUnavailable: 'Compaction is unavailable because no compaction backend is composed.',
|
|
33
|
+
noReview: 'There is no pending plan review to compact.',
|
|
34
|
+
empty: 'No compactable history exists before the pending plan.',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function messageOf(error) {
|
|
38
|
+
return error instanceof Error ? error.message : String(error);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the composed compaction service for one agent.
|
|
43
|
+
*
|
|
44
|
+
* Presets isolate their mounted services, so the agent's preset group owns its
|
|
45
|
+
* `compaction` instance while the host-plane read only sees globally mounted
|
|
46
|
+
* compositions. The agent-scoped context is the middle step; the preset
|
|
47
|
+
* registry is the authoritative source when it exposes the service.
|
|
48
|
+
*
|
|
49
|
+
* @param {object | undefined} agent the agent executing the command.
|
|
50
|
+
* @param {object} [accessors] service readers supplied by the composition.
|
|
51
|
+
* @param {() => object | undefined} [accessors.getAgentPresets] host-plane preset registry reader.
|
|
52
|
+
* @param {(agent: object | undefined) => object | undefined} [accessors.getCompaction] host-plane compaction reader.
|
|
53
|
+
* @returns {object | undefined} the first service that exposes `compactRegion`.
|
|
54
|
+
*/
|
|
55
|
+
export function resolveCompactionService(agent, { getAgentPresets, getCompaction } = {}) {
|
|
56
|
+
if (agent !== undefined && agent !== null) {
|
|
57
|
+
// 1. The service mounted in the agent's preset group.
|
|
58
|
+
try {
|
|
59
|
+
const scoped = typeof getAgentPresets === 'function'
|
|
60
|
+
? getAgentPresets()?.serviceFor?.(agent, 'compaction')
|
|
61
|
+
: undefined;
|
|
62
|
+
if (scoped?.compactRegion !== undefined) return scoped;
|
|
63
|
+
} catch {
|
|
64
|
+
// An absent or unreadable preset registry falls through.
|
|
65
|
+
}
|
|
66
|
+
// 2. The service mounted in the agent's scoped context.
|
|
67
|
+
try {
|
|
68
|
+
const scoped = agent?.ctx?.get?.('compaction');
|
|
69
|
+
if (scoped?.compactRegion !== undefined) return scoped;
|
|
70
|
+
} catch {
|
|
71
|
+
// An unreadable scoped context falls through.
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// 3. The host-plane service; a throwing read stays visible to the caller.
|
|
75
|
+
const fallback = typeof getCompaction === 'function' ? getCompaction(agent) : undefined;
|
|
76
|
+
return fallback?.compactRegion !== undefined ? fallback : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function createCompactCommand({ getProjections, getCompaction, getBalancedAfter, getAgentPresets }) {
|
|
80
|
+
return {
|
|
81
|
+
name: 'great-plan-compact',
|
|
82
|
+
description: 'Compact older plan history before executing (used by Great Plan).',
|
|
83
|
+
async handler(invocation) {
|
|
84
|
+
const args = invocation ?? {};
|
|
85
|
+
const rawInput = typeof args.rawInput === 'string' ? args.rawInput.trim() : '';
|
|
86
|
+
if (rawInput !== '') return { kind: 'error', text: USAGE };
|
|
87
|
+
const { agent, signal } = args;
|
|
88
|
+
|
|
89
|
+
let plan;
|
|
90
|
+
try {
|
|
91
|
+
const projections = getProjections();
|
|
92
|
+
if (typeof projections?.snapshot !== 'function') return { kind: 'error', text: MESSAGES.planStateUnavailable };
|
|
93
|
+
plan = projections.snapshot(agent.session, ['plan'])?.values?.plan;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return { kind: 'error', text: `Plan state is unavailable: ${messageOf(error)}` };
|
|
96
|
+
}
|
|
97
|
+
if (plan?.active !== true) return { kind: 'error', text: MESSAGES.planInactive };
|
|
98
|
+
|
|
99
|
+
let compaction;
|
|
100
|
+
try {
|
|
101
|
+
compaction = resolveCompactionService(agent, { getAgentPresets, getCompaction });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return { kind: 'error', text: `Compaction is unavailable: ${messageOf(error)}` };
|
|
104
|
+
}
|
|
105
|
+
if (compaction?.compactRegion === undefined) return { kind: 'error', text: MESSAGES.backendUnavailable };
|
|
106
|
+
|
|
107
|
+
let balancedAfter;
|
|
108
|
+
try {
|
|
109
|
+
balancedAfter = await getBalancedAfter();
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return { kind: 'error', text: `Compaction is unavailable because the compaction package could not be loaded: ${messageOf(error)}` };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let range;
|
|
115
|
+
try {
|
|
116
|
+
range = selectPlanCompactionRange(agent.session, balancedAfter, { planReviewTool: EXIT_PLAN_MODE_TOOL });
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return { kind: 'error', text: `Unable to inspect plan history: ${messageOf(error)}` };
|
|
119
|
+
}
|
|
120
|
+
if (range.error === 'no-review') return { kind: 'error', text: MESSAGES.noReview };
|
|
121
|
+
if (range.error === 'empty') return { kind: 'error', text: MESSAGES.empty };
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const result = await compaction.compactRegion(range.start, range.end, agent, signal);
|
|
125
|
+
const items = Array.isArray(result?.shadowedSeqs) ? result.shadowedSeqs.length : 0;
|
|
126
|
+
const tokens = Number.isFinite(result?.shadowedTokenCount) ? result.shadowedTokenCount : 0;
|
|
127
|
+
return { kind: 'success', text: `Compacted ${items} history items (~${tokens} tokens).` };
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (signal?.aborted) return { kind: 'error', text: FAILURES.cancelled };
|
|
130
|
+
const code = typeof error?.code === 'string' ? error.code : undefined;
|
|
131
|
+
return { kind: 'error', text: Object.hasOwn(FAILURES, code) ? FAILURES[code] : messageOf(error) };
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evidence-first plan handoff prompt section.
|
|
3
|
+
*
|
|
4
|
+
* Plan mode already renders the deployment's native `plan:policy` guidance.
|
|
5
|
+
* This plugin adds one independent section immediately after it, asking the
|
|
6
|
+
* planner to hand over its investigation together with the plan. It also carries
|
|
7
|
+
* an optional unlimited-token-budget reminder and a brainstorming paragraph,
|
|
8
|
+
* each independently switchable from the Settings page. The native policy text
|
|
9
|
+
* is never replaced or duplicated.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Prompt section name; unique in the global prompt layer. */
|
|
13
|
+
export const PLAN_EVIDENCE_SECTION = 'great-plan:plan-evidence';
|
|
14
|
+
|
|
15
|
+
/** Used when the instance cannot resolve the centrally owned PLAN_POLICY order. */
|
|
16
|
+
export const PLAN_EVIDENCE_ORDER_FALLBACK = 501;
|
|
17
|
+
|
|
18
|
+
/** Model-facing collaborative-discovery and Team-Lead guidance; the Settings page can switch it off. */
|
|
19
|
+
export const PLAN_BRAINSTORMING_GUIDANCE = `Discovery and delegation:
|
|
20
|
+
- Assess the goal, success criteria, constraints, and current state; do not quiz the user with a checklist.
|
|
21
|
+
- If an answer could materially change scope or approach, ask one concise question per turn and wait. If the request is clear or the user delegates the choice, proceed.
|
|
22
|
+
- You are the Team Lead. Use \`spawn_teammate\` to start at least 2 exploration teammates in parallel, each with one distinct, concrete scope, and require concise findings cited as path:line-range. Do not investigate their scopes yourself; collect all results and resolve conflicts.
|
|
23
|
+
- Then spawn 1 separate planning teammate to draft a candidate plan from the consolidated evidence. Verify the evidence and the draft yourself before submitting.
|
|
24
|
+
- If \`spawn_teammate\` is unavailable or fails, say so in the plan and continue alone; never invent teammate work.
|
|
25
|
+
- Show alternatives and tradeoffs only when they help the user decide; state the key reason for your recommendation.
|
|
26
|
+
- Separate user requirements from your recommendations, assumptions, and unverified points. Give a brief rationale, not step-by-step chain-of-thought.`;
|
|
27
|
+
|
|
28
|
+
/** Model-facing unlimited-token-budget reminder; the Settings page can switch it off. */
|
|
29
|
+
export const PLAN_UNLIMITED_BUDGET_GUIDANCE = 'Token budget: unlimited. Never cut investigation, tool calls, or needed questions to save tokens; keep the final plan concise.';
|
|
30
|
+
|
|
31
|
+
/** Model-facing evidence and executor handoff guidance; always rendered in plan mode. */
|
|
32
|
+
export const PLAN_HANDOFF_GUIDANCE = `Plan as handoff (the executor may get only this plan or a compacted history, so make it self-contained):
|
|
33
|
+
- Background: request, current behavior, constraints, assumptions.
|
|
34
|
+
- Evidence: files/symbols with line ranges (e.g. path/file.js:12-30), searches and commands with key results, docs/skills/URLs used, and how conflicts were resolved. Cite evidence for each design claim; mark anything unverified as an assumption or open question.
|
|
35
|
+
- Handoff: what is settled and must not be re-investigated; which unknowns to verify first.
|
|
36
|
+
Never present a guess as fact or invent paths, APIs, command output, or test results. Re-check citations before submitting. Keep evidence compact: precise pointers, not raw logs or whole files.`;
|
|
37
|
+
|
|
38
|
+
/** Default guidance (every switch on) appended while plan mode is active. */
|
|
39
|
+
export const PLAN_EVIDENCE_GUIDANCE = `${PLAN_UNLIMITED_BUDGET_GUIDANCE}\n\n${PLAN_BRAINSTORMING_GUIDANCE}\n\n${PLAN_HANDOFF_GUIDANCE}`;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Compose the plan-mode guidance from its independently switchable parts.
|
|
43
|
+
*
|
|
44
|
+
* The budget reminder comes first as a general instruction, then the optional
|
|
45
|
+
* brainstorming guidance, and the evidence/handoff guidance always closes the
|
|
46
|
+
* section. Disabled parts are omitted entirely, never replaced by placeholders.
|
|
47
|
+
* With every switch on the precomputed `PLAN_EVIDENCE_GUIDANCE` constant is
|
|
48
|
+
* returned so the common path allocates nothing per prompt assembly.
|
|
49
|
+
*/
|
|
50
|
+
export function composePlanGuidance({ brainstorming = true, unlimitedBudget = true } = {}) {
|
|
51
|
+
if (brainstorming !== false && unlimitedBudget !== false) return PLAN_EVIDENCE_GUIDANCE;
|
|
52
|
+
const parts = [];
|
|
53
|
+
if (unlimitedBudget !== false) parts.push(PLAN_UNLIMITED_BUDGET_GUIDANCE);
|
|
54
|
+
if (brainstorming !== false) parts.push(PLAN_BRAINSTORMING_GUIDANCE);
|
|
55
|
+
parts.push(PLAN_HANDOFF_GUIDANCE);
|
|
56
|
+
return parts.join('\n\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read one switch from the plugin config.
|
|
61
|
+
*
|
|
62
|
+
* A volatile field arrives as a reference object; plain booleans are accepted
|
|
63
|
+
* for tests and hand-written rows. Every missing, malformed, or unreadable
|
|
64
|
+
* value keeps the fallback so prompt assembly never fails.
|
|
65
|
+
*/
|
|
66
|
+
function readConfigSwitch(config, field, fallback) {
|
|
67
|
+
if (config === undefined || config === null || typeof config !== 'object') return fallback;
|
|
68
|
+
let value;
|
|
69
|
+
try {
|
|
70
|
+
value = config[field];
|
|
71
|
+
} catch {
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
if (value === undefined || value === null) return fallback;
|
|
75
|
+
if (typeof value === 'boolean') return value;
|
|
76
|
+
if (typeof value.get === 'function') {
|
|
77
|
+
try {
|
|
78
|
+
const live = value.get();
|
|
79
|
+
return typeof live === 'boolean' ? live : fallback;
|
|
80
|
+
} catch {
|
|
81
|
+
return fallback;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return fallback;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Read the brainstorming switch; defaults to enabled. */
|
|
88
|
+
export function readBrainstormingEnabled(config) {
|
|
89
|
+
return readConfigSwitch(config, 'injectBrainstormingPrompt', true);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Read the unlimited-token-budget switch; defaults to enabled. */
|
|
93
|
+
export function readUnlimitedBudgetEnabled(config) {
|
|
94
|
+
return readConfigSwitch(config, 'injectUnlimitedBudgetPrompt', true);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolve one boolean-validated plan-mode state from a planMode-like service.
|
|
99
|
+
*
|
|
100
|
+
* A usable state exposes a boolean `active`; a pending selection overrides the
|
|
101
|
+
* logged state. Missing, malformed, or throwing reads return `undefined` so the
|
|
102
|
+
* caller can try its next source.
|
|
103
|
+
*/
|
|
104
|
+
function readPlanModeState(readService, agent) {
|
|
105
|
+
try {
|
|
106
|
+
const service = readService();
|
|
107
|
+
if (service === undefined || service === null || typeof service.get !== 'function') return undefined;
|
|
108
|
+
const state = service.get(agent);
|
|
109
|
+
if (state !== undefined && state !== null && typeof state === 'object' && typeof state.active === 'boolean') {
|
|
110
|
+
return state.pending === undefined ? state.active : state.pending === true;
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Decide whether plan mode is active for the step being assembled.
|
|
120
|
+
*
|
|
121
|
+
* The Host plane cannot rely on its own `planMode` instance: agent presets
|
|
122
|
+
* commonly mount the plan-mode plugin in an isolated group, so the host-plane
|
|
123
|
+
* instance does not see the state that the preset drives. The agent's own
|
|
124
|
+
* scoped context (`agent.ctx`) carries the instance that owns the session's
|
|
125
|
+
* plan state and the pending selection's direction — the same data source the
|
|
126
|
+
* native `plan:policy` section reads. The host-plane instance is the fallback
|
|
127
|
+
* for compositions where plan mode is host-owned, and the shared `plan`
|
|
128
|
+
* session projection (the unit the compaction command also reads) is the final
|
|
129
|
+
* synchronous fallback. Every source degrades to inactive instead of throwing.
|
|
130
|
+
*/
|
|
131
|
+
export function readPlanModeActive(agent, getPlanMode, getSessionProjections) {
|
|
132
|
+
if (agent === undefined || agent === null) return false;
|
|
133
|
+
// 1. The agent-scoped planMode instance: `get()` returns the logged state
|
|
134
|
+
// plus a pending selection whose boolean is the selected direction.
|
|
135
|
+
const scoped = readPlanModeState(() => agent.ctx?.get?.('planMode'), agent);
|
|
136
|
+
if (scoped !== undefined) return scoped;
|
|
137
|
+
// 2. The host-plane planMode instance.
|
|
138
|
+
const host = readPlanModeState(() => (typeof getPlanMode === 'function' ? getPlanMode() : undefined), agent);
|
|
139
|
+
if (host !== undefined) return host;
|
|
140
|
+
// 3. The shared `plan` projection's cropped view { active, pending }. The
|
|
141
|
+
// pending direction is not exposed here, so a pending selection counts as
|
|
142
|
+
// active; by assembly time the pre-step has already applied pending
|
|
143
|
+
// selections, which makes this equivalent to the native section's gate.
|
|
144
|
+
try {
|
|
145
|
+
const projections = typeof getSessionProjections === 'function' ? getSessionProjections() : undefined;
|
|
146
|
+
const session = agent?.session;
|
|
147
|
+
if (projections !== undefined && projections !== null && session !== undefined && typeof projections.snapshot === 'function') {
|
|
148
|
+
const state = projections.snapshot(session, ['plan'])?.values?.plan;
|
|
149
|
+
if (state !== undefined && state !== null && typeof state === 'object' && typeof state.active === 'boolean') {
|
|
150
|
+
return state.active === true || state.pending === true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// No usable plan state anywhere; treat the step as outside plan mode.
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Register the section right after the native plan policy.
|
|
161
|
+
*
|
|
162
|
+
* @param {object} options
|
|
163
|
+
* @param {object} options.systemPrompt the Host `systemPrompt` service.
|
|
164
|
+
* @param {() => object | undefined} options.getPlanMode optional host-plane `planMode` reader.
|
|
165
|
+
* @param {() => object | undefined} [options.getSessionProjections] optional projection service reader.
|
|
166
|
+
* @param {() => object | undefined} options.getConfig optional live plugin config reader.
|
|
167
|
+
*/
|
|
168
|
+
export function registerPlanEvidenceSection({ systemPrompt, getPlanMode, getSessionProjections, getConfig }) {
|
|
169
|
+
let order = PLAN_EVIDENCE_ORDER_FALLBACK;
|
|
170
|
+
try {
|
|
171
|
+
const resolved = typeof systemPrompt.getSectionOrder === 'function'
|
|
172
|
+
? systemPrompt.getSectionOrder('PLAN_POLICY')
|
|
173
|
+
: undefined;
|
|
174
|
+
if (Number.isFinite(resolved)) order = resolved + 1;
|
|
175
|
+
} catch {
|
|
176
|
+
// Keep the fallback order; an unavailable central table must not break the plugin.
|
|
177
|
+
}
|
|
178
|
+
return systemPrompt.section({
|
|
179
|
+
name: PLAN_EVIDENCE_SECTION,
|
|
180
|
+
order,
|
|
181
|
+
text: (context) => {
|
|
182
|
+
let config;
|
|
183
|
+
try {
|
|
184
|
+
config = typeof getConfig === 'function' ? getConfig() : undefined;
|
|
185
|
+
} catch {
|
|
186
|
+
config = undefined;
|
|
187
|
+
}
|
|
188
|
+
if (!readPlanModeActive(context?.agent, getPlanMode, getSessionProjections)) return '';
|
|
189
|
+
return composePlanGuidance({
|
|
190
|
+
brainstorming: readBrainstormingEnabled(config),
|
|
191
|
+
unlimitedBudget: readUnlimitedBudgetEnabled(config),
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** The plan-review tool call that marks the pending plan review surface node. */
|
|
2
|
+
export const EXIT_PLAN_MODE_TOOL = 'exit_plan_mode';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Pure plan-compaction range selection.
|
|
6
|
+
*
|
|
7
|
+
* The pending plan review is the last surface node: an assistant message
|
|
8
|
+
* carrying the plan-review tool call (`exit_plan_mode`). The compactable range
|
|
9
|
+
* starts right after the leading system messages and ends at the last balanced
|
|
10
|
+
* node before that review, snapping backwards until the tool pairing holds.
|
|
11
|
+
*/
|
|
12
|
+
export function selectPlanCompactionRange(session, balancedAfter, options = {}) {
|
|
13
|
+
const planReviewTool = typeof options.planReviewTool === 'string'
|
|
14
|
+
? options.planReviewTool
|
|
15
|
+
: EXIT_PLAN_MODE_TOOL;
|
|
16
|
+
const nodes = session.surface.nodes;
|
|
17
|
+
if (nodes.length === 0) return { error: 'empty' };
|
|
18
|
+
const last = session.eventAt(nodes[nodes.length - 1]);
|
|
19
|
+
if (last?.type !== 'assistant/message') return { error: 'no-review' };
|
|
20
|
+
const content = last.data?.message?.content;
|
|
21
|
+
if (!Array.isArray(content) || !content.some((block) => block?.type === 'tool-call' && block.name === planReviewTool)) {
|
|
22
|
+
return { error: 'no-review' };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let startIndex = 0;
|
|
26
|
+
while (startIndex < nodes.length && session.eventAt(nodes[startIndex])?.type === 'system/message') startIndex += 1;
|
|
27
|
+
|
|
28
|
+
let endIndex = nodes.length - 2;
|
|
29
|
+
while (endIndex >= startIndex) {
|
|
30
|
+
if (balancedAfter(session, nodes[endIndex]) === true) break;
|
|
31
|
+
endIndex -= 1;
|
|
32
|
+
}
|
|
33
|
+
if (endIndex < startIndex) return { error: 'empty' };
|
|
34
|
+
return { start: nodes[startIndex], end: nodes[endIndex] };
|
|
35
|
+
}
|
package/lib/index.js
ADDED
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,71 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onefl666/dsh-great-plan",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "onefl666",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"deepseek-harness",
|
|
8
|
+
"dsh",
|
|
9
|
+
"dsh-plugin",
|
|
10
|
+
"dsh-bundle",
|
|
11
|
+
"plan-mode",
|
|
12
|
+
"agent",
|
|
13
|
+
"web-client"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/onefl666/dsh-great-plan#readme",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/onefl666/dsh-great-plan.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/onefl666/dsh-great-plan/issues"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "lib/index.js",
|
|
28
|
+
"description": "Choose the model and reasoning effort, then execute, compact-and-execute, or start fresh with a reviewed plan.",
|
|
29
|
+
"icon": "./icon.svg",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": "./lib/index.js",
|
|
32
|
+
"./client": "./lib/client.js",
|
|
33
|
+
"./locale/*.json": "./locale/*.json",
|
|
34
|
+
"./icon": "./icon.svg",
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"lib/index.js",
|
|
39
|
+
"lib/host/*.js",
|
|
40
|
+
"lib/client.js",
|
|
41
|
+
"lib/client.*.js",
|
|
42
|
+
"cordis.patch.yml",
|
|
43
|
+
"locale/*.json",
|
|
44
|
+
"icon.svg",
|
|
45
|
+
"README.md",
|
|
46
|
+
"README.zh-CN.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@deepseek-ai/dsh-compaction": "^0.2.1-rc.3",
|
|
51
|
+
"@deepseek-ai/schemastery": "~3.18.5"
|
|
52
|
+
},
|
|
53
|
+
"dsh": {
|
|
54
|
+
"bundle": {
|
|
55
|
+
"patch": "./cordis.patch.yml"
|
|
56
|
+
},
|
|
57
|
+
"client": {
|
|
58
|
+
"platform": "web",
|
|
59
|
+
"immediately": true,
|
|
60
|
+
"inject": [
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-user-questions",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-model-selection",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-workspace",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-settings-general",
|
|
67
|
+
"@deepseek-ai/dsh-client-locale"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|