@launchsecure/launch-kit 0.0.1
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 +37 -0
- package/dist/client/assets/index-C8GAsRGO.css +32 -0
- package/dist/client/assets/index-CcHIoRl6.js +286 -0
- package/dist/client/index.html +22 -0
- package/dist/server/cli.js +8853 -0
- package/dist/server/fb-wizard.js +136 -0
- package/dist/server/graph-mcp-entry.js +1542 -0
- package/dist/server/public/app.js +1312 -0
- package/dist/server/public/icons.js +36 -0
- package/dist/server/public/index.html +159 -0
- package/dist/server/public/plan-detector.js +186 -0
- package/dist/server/public/session-manager.js +1129 -0
- package/dist/server/public/splits.js +569 -0
- package/dist/server/public/style.css +1620 -0
- package/package.json +73 -0
- package/prompts/analysis.md +992 -0
- package/prompts/architect-reconcile.md +931 -0
- package/prompts/architecture-sync.md +902 -0
- package/prompts/be-contract.md +709 -0
- package/prompts/be-impl.md +565 -0
- package/prompts/be-policy.md +551 -0
- package/prompts/be-test.md +591 -0
- package/prompts/bug-diagnosis.md +653 -0
- package/prompts/bug-intake.md +563 -0
- package/prompts/change-request-intake.md +593 -0
- package/prompts/db-contract.md +644 -0
- package/prompts/db-impl.md +522 -0
- package/prompts/db-interaction.md +569 -0
- package/prompts/db-test.md +630 -0
- package/prompts/decision-pack.md +654 -0
- package/prompts/fe-contract.md +992 -0
- package/prompts/fe-flow.md +537 -0
- package/prompts/fe-impl.md +597 -0
- package/prompts/fe-reconcile.md +506 -0
- package/prompts/fe-review.md +550 -0
- package/prompts/fe-test.md +705 -0
- package/prompts/fix-planner.md +1219 -0
- package/prompts/global-db-patterns.md +588 -0
- package/prompts/global-env-config.md +460 -0
- package/prompts/global-integrations.md +504 -0
- package/prompts/global-middleware.md +442 -0
- package/prompts/global-navigation.md +502 -0
- package/prompts/global-security.md +603 -0
- package/prompts/global-services.md +427 -0
- package/prompts/greenfield-classifier.md +590 -0
- package/prompts/llm-council.md +597 -0
- package/prompts/module-sequencer.md +529 -0
- package/prompts/normalize.md +611 -0
- package/prompts/optimization.md +633 -0
- package/prompts/prd-generation.md +544 -0
- package/prompts/prd-reconcile.md +584 -0
- package/prompts/prd-review.md +504 -0
- package/prompts/pre-code-analysis.md +565 -0
- package/prompts/pre-code-global-analysis.md +169 -0
- package/prompts/production-bootstrap.md +577 -0
- package/prompts/research.md +702 -0
- package/prompts/retrofit-analysis.md +845 -0
- package/prompts/spike.md +850 -0
- package/prompts/theming.md +835 -0
- package/prompts/triage.md +599 -0
- package/prompts/unified-reconcile.md +628 -0
- package/prompts/unified-review.md +592 -0
- package/prompts/user-stories.md +486 -0
- package/prompts/wireframe.md +576 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Minimal SVG icon helper. Returns inline SVG strings using currentColor.
|
|
2
|
+
// Usage: window.icons.name(size)
|
|
3
|
+
|
|
4
|
+
(function () {
|
|
5
|
+
const toSvg = (pathOrContent, attrs = {}) => {
|
|
6
|
+
const base = {
|
|
7
|
+
width: attrs.width || 16,
|
|
8
|
+
height: attrs.height || 16,
|
|
9
|
+
viewBox: attrs.viewBox || '0 0 24 24',
|
|
10
|
+
fill: 'none',
|
|
11
|
+
stroke: 'currentColor',
|
|
12
|
+
'stroke-width': attrs.strokeWidth || 2,
|
|
13
|
+
'stroke-linecap': 'round',
|
|
14
|
+
'stroke-linejoin': 'round'
|
|
15
|
+
};
|
|
16
|
+
const attrStr = Object.entries(base)
|
|
17
|
+
.map(([k, v]) => `${k}="${v}"`).join(' ');
|
|
18
|
+
return `<svg ${attrStr}>${pathOrContent}</svg>`;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const circle = (cx, cy, r) => `<circle cx="${cx}" cy="${cy}" r="${r}"/>`;
|
|
22
|
+
const line = (x1, y1, x2, y2) => `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}"/>`;
|
|
23
|
+
|
|
24
|
+
const icons = {
|
|
25
|
+
check: (s = 16) => toSvg('<polyline points="20 6 9 17 4 12"/>', { width: s, height: s }),
|
|
26
|
+
x: (s = 16) => toSvg('<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>', { width: s, height: s }),
|
|
27
|
+
clipboard: (s = 16) => toSvg('<rect x="4" y="3" width="16" height="18" rx="2"/><line x1="8" y1="7" x2="16" y2="7"/>', { width: s, height: s }),
|
|
28
|
+
folder: (s = 16) => toSvg('<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>', { width: s, height: s }),
|
|
29
|
+
download: (s = 16) => toSvg('<path d="M12 3v12"/><path d="M8 11l4 4 4-4"/><path d="M5 21h14"/>', { width: s, height: s }),
|
|
30
|
+
chartLine: (s = 16) => toSvg('<polyline points="3 17 9 11 13 15 21 7"/><line x1="3" y1="17" x2="3" y2="21"/><line x1="21" y1="7" x2="21" y2="11"/>', { width: s, height: s }),
|
|
31
|
+
dot: (s = 10) => toSvg(circle(12, 12, 5), { width: s, height: s, viewBox: '0 0 24 24', strokeWidth: 0 }),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
window.icons = icons;
|
|
35
|
+
})();
|
|
36
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<base href="/terminal/">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Claude Code Web Interface</title>
|
|
8
|
+
|
|
9
|
+
<meta name="description" content="Web interface for Claude Code - Run Claude commands directly from your browser">
|
|
10
|
+
<meta name="theme-color" content="#161b22">
|
|
11
|
+
<meta name="format-detection" content="telephone=no">
|
|
12
|
+
|
|
13
|
+
<!-- Icons for various platforms -->
|
|
14
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/icon-32.png">
|
|
15
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/icon-16.png">
|
|
16
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/icon-180.png">
|
|
17
|
+
<meta name="msapplication-TileColor" content="#161b22">
|
|
18
|
+
<meta name="msapplication-TileImage" content="/icon-144.png">
|
|
19
|
+
|
|
20
|
+
<!-- Apply saved theme early to avoid flash -->
|
|
21
|
+
<script>
|
|
22
|
+
(function() {
|
|
23
|
+
try {
|
|
24
|
+
const s = JSON.parse(localStorage.getItem('launchpod-terminal-settings') || '{}');
|
|
25
|
+
if (s && s.theme === 'light') {
|
|
26
|
+
document.documentElement.setAttribute('data-theme', 'light');
|
|
27
|
+
} else {
|
|
28
|
+
// Dark is default — ensure no 'light' override is set
|
|
29
|
+
document.documentElement.removeAttribute('data-theme');
|
|
30
|
+
}
|
|
31
|
+
} catch (_) { /* ignore */ }
|
|
32
|
+
})();
|
|
33
|
+
</script>
|
|
34
|
+
<link rel="stylesheet" href="style.css">
|
|
35
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
36
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
37
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
|
38
|
+
<script src="https://unpkg.com/xterm@5.3.0/lib/xterm.js"></script>
|
|
39
|
+
<script src="https://unpkg.com/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
|
|
40
|
+
<script src="https://unpkg.com/xterm-addon-web-links@0.9.0/lib/xterm-addon-web-links.js"></script>
|
|
41
|
+
<link rel="stylesheet" href="https://unpkg.com/xterm@5.3.0/css/xterm.css" />
|
|
42
|
+
</head>
|
|
43
|
+
<body>
|
|
44
|
+
<div id="app">
|
|
45
|
+
|
|
46
|
+
<!-- Session Tabs Bar -->
|
|
47
|
+
<div class="session-tabs-bar" id="sessionTabsBar">
|
|
48
|
+
<div class="tabs-section">
|
|
49
|
+
<div class="tabs-container" id="tabsContainer">
|
|
50
|
+
<!-- Tabs will be dynamically added here -->
|
|
51
|
+
</div>
|
|
52
|
+
<button class="tab-new" id="tabNewBtn" title="New Session (Ctrl+T)">
|
|
53
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
54
|
+
<line x1="12" y1="5" x2="12" y2="19"/>
|
|
55
|
+
<line x1="5" y1="12" x2="19" y2="12"/>
|
|
56
|
+
</svg>
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
<!-- Overflow dropdown for mobile - outside tabs-section to prevent scrolling issues -->
|
|
60
|
+
<div class="tab-overflow-wrapper" id="tabOverflowWrapper" style="display: none;">
|
|
61
|
+
<button class="tab-overflow-btn" id="tabOverflowBtn" title="More tabs">
|
|
62
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
63
|
+
<circle cx="12" cy="5" r="1"/>
|
|
64
|
+
<circle cx="12" cy="12" r="1"/>
|
|
65
|
+
<circle cx="12" cy="19" r="1"/>
|
|
66
|
+
</svg>
|
|
67
|
+
<span class="tab-overflow-count">0</span>
|
|
68
|
+
</button>
|
|
69
|
+
<div class="tab-overflow-menu" id="tabOverflowMenu">
|
|
70
|
+
<!-- Overflow tabs will be listed here -->
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="tab-actions"></div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
<div class="mobile-menu" id="mobileMenu">
|
|
78
|
+
<div class="mobile-menu-header">
|
|
79
|
+
<h2><span class="icon" aria-hidden="true"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="4"/><path d="M8 10h.01M16 10h.01M8 16c1.333-1 2.667-1 4 0 1.333 1 2.667 1 4 0"/></svg></span> Claude Code</h2>
|
|
80
|
+
<button class="close-menu-btn" id="closeMenuBtn">×</button>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="mobile-menu-content">
|
|
83
|
+
<button id="sessionsBtnMobile" class="mobile-menu-btn">Sessions</button>
|
|
84
|
+
<button id="closeSessionBtnMobile" class="mobile-menu-btn btn-danger" style="display: none;">Close Session</button>
|
|
85
|
+
<button id="reconnectBtnMobile" class="mobile-menu-btn" disabled>Reconnect</button>
|
|
86
|
+
<button id="clearBtnMobile" class="mobile-menu-btn">Clear Terminal</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<main class="main">
|
|
91
|
+
<div class="terminal-container" id="terminalContainer" data-view="single">
|
|
92
|
+
<div class="terminal-wrapper">
|
|
93
|
+
<div id="terminal"></div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</main>
|
|
97
|
+
|
|
98
|
+
<!-- Inline plan panel (not a modal) -->
|
|
99
|
+
<div class="plan-inline-panel" id="planInlinePanel" style="display: none;">
|
|
100
|
+
<div class="plan-inline-header">
|
|
101
|
+
<span class="plan-inline-title">
|
|
102
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
103
|
+
<rect x="4" y="3" width="16" height="18" rx="2"/>
|
|
104
|
+
<line x1="8" y1="7" x2="16" y2="7"/>
|
|
105
|
+
</svg>
|
|
106
|
+
Plan Mode Active
|
|
107
|
+
</span>
|
|
108
|
+
<div class="plan-inline-actions">
|
|
109
|
+
<button class="btn btn-success btn-small" id="acceptPlanBtn" title="Accept Plan">Accept</button>
|
|
110
|
+
<button class="btn btn-danger btn-small" id="rejectPlanBtn" title="Reject Plan">Reject</button>
|
|
111
|
+
<button class="plan-inline-toggle" id="planToggleBtn" title="Collapse">
|
|
112
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
113
|
+
<polyline points="18 15 12 9 6 15"/>
|
|
114
|
+
</svg>
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="plan-inline-content" id="planContent">
|
|
119
|
+
<!-- Plan content appears here -->
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<!-- Inline error banner -->
|
|
124
|
+
<div class="error-banner" id="errorBanner" style="display: none;">
|
|
125
|
+
<span id="errorBannerText"></span>
|
|
126
|
+
<button class="error-banner-close" id="errorBannerClose">×</button>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<!-- Inline start prompt (shown when session has no running agent) -->
|
|
130
|
+
<div class="start-prompt-panel" id="startPromptPanel" style="display: none;">
|
|
131
|
+
<div class="start-prompt-inner">
|
|
132
|
+
<p class="start-prompt-dir" id="startPromptDir"></p>
|
|
133
|
+
<div class="start-prompt-buttons">
|
|
134
|
+
<button id="startBtn" class="btn btn-primary">Start Claude</button>
|
|
135
|
+
<button id="startCodexBtn" class="btn btn-primary">Start Codex</button>
|
|
136
|
+
<button id="startAgentBtn" class="btn btn-primary">Start Cursor</button>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<!-- App-wide overlay (loading only) -->
|
|
142
|
+
<div class="terminal-overlay" id="overlay" style="display: none;">
|
|
143
|
+
<div class="overlay-content">
|
|
144
|
+
<div class="loading-spinner" id="loadingSpinner">
|
|
145
|
+
<div class="spinner"></div>
|
|
146
|
+
<p>Connecting...</p>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<script src="plan-detector.js"></script>
|
|
154
|
+
<script src="session-manager.js"></script>
|
|
155
|
+
<script src="splits.js"></script>
|
|
156
|
+
<script src="icons.js"></script>
|
|
157
|
+
<script src="app.js"></script>
|
|
158
|
+
</body>
|
|
159
|
+
</html>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
class PlanDetector {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.isMonitoring = false;
|
|
4
|
+
this.outputBuffer = [];
|
|
5
|
+
this.planModeActive = false;
|
|
6
|
+
this.currentPlan = null;
|
|
7
|
+
this.planStartMarker = '## Implementation Plan:';
|
|
8
|
+
this.planEndMarker = 'User has approved your plan';
|
|
9
|
+
this.maxBufferSize = 10000;
|
|
10
|
+
this.onPlanDetected = null;
|
|
11
|
+
this.onPlanModeChange = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
processOutput(data) {
|
|
15
|
+
if (!this.isMonitoring) return;
|
|
16
|
+
|
|
17
|
+
// Add to buffer
|
|
18
|
+
this.outputBuffer.push({
|
|
19
|
+
timestamp: Date.now(),
|
|
20
|
+
data: data
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Keep buffer size manageable
|
|
24
|
+
if (this.outputBuffer.length > this.maxBufferSize) {
|
|
25
|
+
this.outputBuffer = this.outputBuffer.slice(-this.maxBufferSize / 2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Convert buffer to text for analysis
|
|
29
|
+
const recentText = this.getRecentText();
|
|
30
|
+
|
|
31
|
+
// Check for plan mode activation
|
|
32
|
+
if (!this.planModeActive && this.detectPlanModeStart(recentText)) {
|
|
33
|
+
this.planModeActive = true;
|
|
34
|
+
if (this.onPlanModeChange) {
|
|
35
|
+
this.onPlanModeChange(true);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check for completed plan
|
|
40
|
+
if (this.planModeActive && this.detectCompletedPlan(recentText)) {
|
|
41
|
+
const plan = this.extractPlan(recentText);
|
|
42
|
+
if (plan) {
|
|
43
|
+
this.currentPlan = plan;
|
|
44
|
+
if (this.onPlanDetected) {
|
|
45
|
+
this.onPlanDetected(plan);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check for plan mode exit
|
|
51
|
+
if (this.planModeActive && this.detectPlanModeEnd(recentText)) {
|
|
52
|
+
this.planModeActive = false;
|
|
53
|
+
if (this.onPlanModeChange) {
|
|
54
|
+
this.onPlanModeChange(false);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getRecentText(maxChars = 50000) {
|
|
60
|
+
// Get recent output as text, stripping ANSI codes
|
|
61
|
+
const text = this.outputBuffer
|
|
62
|
+
.map(item => item.data)
|
|
63
|
+
.join('')
|
|
64
|
+
.replace(/\x1b\[[0-9;]*m/g, '') // Remove ANSI color codes
|
|
65
|
+
.replace(/\x1b\[[0-9]*[A-Za-z]/g, ''); // Remove other ANSI sequences
|
|
66
|
+
|
|
67
|
+
return text.slice(-maxChars);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
detectPlanModeStart(text) {
|
|
71
|
+
// Look for plan mode activation indicators
|
|
72
|
+
const indicators = [
|
|
73
|
+
'Plan mode is active',
|
|
74
|
+
'you MUST NOT make any edits',
|
|
75
|
+
'present your plan by calling the ExitPlanMode tool',
|
|
76
|
+
'Starting plan mode'
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
return indicators.some(indicator => text.includes(indicator));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
detectCompletedPlan(text) {
|
|
83
|
+
// Check if a plan has been presented
|
|
84
|
+
const planPatterns = [
|
|
85
|
+
/## Implementation Plan:/,
|
|
86
|
+
/### \d+\. /,
|
|
87
|
+
/## Plan:/,
|
|
88
|
+
/### Plan Overview/,
|
|
89
|
+
/## Proposed Solution:/
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
// Must have plan content and be recent
|
|
93
|
+
const hasPattern = planPatterns.some(pattern => pattern.test(text));
|
|
94
|
+
const recentText = text.slice(-10000);
|
|
95
|
+
|
|
96
|
+
return hasPattern && recentText.includes('###');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
extractPlan(text) {
|
|
100
|
+
// Try multiple extraction strategies
|
|
101
|
+
let plan = null;
|
|
102
|
+
|
|
103
|
+
// Strategy 1: Look for ## Implementation Plan: to next terminal prompt
|
|
104
|
+
const implMatch = text.match(/## Implementation Plan:[\s\S]*?(?=(?:User has approved|Exit plan mode|[$>]|^[a-z]+@))/i);
|
|
105
|
+
if (implMatch) {
|
|
106
|
+
plan = implMatch[0];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Strategy 2: Look for structured plan with ### sections
|
|
110
|
+
if (!plan) {
|
|
111
|
+
const structuredMatch = text.match(/##[^#].*?Plan.*?:[\s\S]*?(?:###.*?[\s\S]*?){2,}(?=(?:User has approved|Exit plan mode|[$>]|^[a-z]+@))/i);
|
|
112
|
+
if (structuredMatch) {
|
|
113
|
+
plan = structuredMatch[0];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Strategy 3: Look for recent plan-like content
|
|
118
|
+
if (!plan) {
|
|
119
|
+
const recentText = text.slice(-5000);
|
|
120
|
+
const planMatch = recentText.match(/(?:##|Plan:)[\s\S]*?(?:###[\s\S]*?){1,}/);
|
|
121
|
+
if (planMatch) {
|
|
122
|
+
plan = planMatch[0];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (plan) {
|
|
127
|
+
// Clean up the plan text
|
|
128
|
+
plan = plan
|
|
129
|
+
.replace(/\x1b\[[0-9;]*m/g, '') // Remove ANSI codes
|
|
130
|
+
.replace(/\r\n/g, '\n') // Normalize line endings
|
|
131
|
+
.replace(/\r/g, '\n')
|
|
132
|
+
.trim();
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
content: plan,
|
|
136
|
+
timestamp: Date.now(),
|
|
137
|
+
raw: plan
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
detectPlanModeEnd(text) {
|
|
145
|
+
const endIndicators = [
|
|
146
|
+
'User has approved your plan',
|
|
147
|
+
'You can now start coding',
|
|
148
|
+
'Plan mode exited',
|
|
149
|
+
'Exiting plan mode'
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
return endIndicators.some(indicator => text.includes(indicator));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
startMonitoring() {
|
|
156
|
+
this.isMonitoring = true;
|
|
157
|
+
this.outputBuffer = [];
|
|
158
|
+
this.planModeActive = false;
|
|
159
|
+
this.currentPlan = null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
stopMonitoring() {
|
|
163
|
+
this.isMonitoring = false;
|
|
164
|
+
this.outputBuffer = [];
|
|
165
|
+
this.planModeActive = false;
|
|
166
|
+
this.currentPlan = null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
clearBuffer() {
|
|
170
|
+
this.outputBuffer = [];
|
|
171
|
+
this.currentPlan = null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
getPlanModeStatus() {
|
|
175
|
+
return this.planModeActive;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
getCurrentPlan() {
|
|
179
|
+
return this.currentPlan;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Export for use in app.js
|
|
184
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
185
|
+
module.exports = PlanDetector;
|
|
186
|
+
}
|