aiwcli 0.10.3 → 0.11.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/bin/run.js +1 -1
- package/dist/commands/clear.js +28 -131
- package/dist/commands/init/index.js +3 -3
- package/dist/lib/gitignore-manager.d.ts +32 -0
- package/dist/lib/gitignore-manager.js +141 -2
- package/dist/templates/CLAUDE.md +8 -8
- package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
- package/dist/templates/_shared/.claude/settings.json +7 -7
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
- package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
- package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
- package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
- package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
- package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
- package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
- package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
- package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
- package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
- package/dist/templates/_shared/lib-ts/base/logger.ts +31 -15
- package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +139 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
- package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +61 -37
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
- package/dist/templates/_shared/lib-ts/types.ts +68 -55
- package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
- package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +21 -21
- package/dist/templates/_shared/scripts/status_line.ts +733 -0
- package/dist/templates/cc-native/.claude/settings.json +175 -185
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -9
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/templates/_shared/hooks/__init__.py +0 -16
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +0 -177
- package/dist/templates/_shared/hooks/context_monitor.py +0 -270
- package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
- package/dist/templates/_shared/hooks/pre_compact.py +0 -104
- package/dist/templates/_shared/hooks/session_end.py +0 -173
- package/dist/templates/_shared/hooks/session_start.py +0 -206
- package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
- package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
- package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
- package/dist/templates/_shared/lib/__init__.py +0 -1
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__init__.py +0 -65
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
- package/dist/templates/_shared/lib/base/constants.py +0 -358
- package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
- package/dist/templates/_shared/lib/base/inference.py +0 -307
- package/dist/templates/_shared/lib/base/logger.py +0 -305
- package/dist/templates/_shared/lib/base/stop_words.py +0 -221
- package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
- package/dist/templates/_shared/lib/base/utils.py +0 -263
- package/dist/templates/_shared/lib/context/__init__.py +0 -102
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
- package/dist/templates/_shared/lib/context/context_selector.py +0 -508
- package/dist/templates/_shared/lib/context/context_store.py +0 -653
- package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
- package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
- package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
- package/dist/templates/_shared/lib/templates/README.md +0 -206
- package/dist/templates/_shared/lib/templates/__init__.py +0 -36
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/formatters.py +0 -146
- package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +0 -357
- package/dist/templates/_shared/scripts/status_line.py +0 -716
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/MIGRATION.md +0 -86
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
- package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
- package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
- package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { sanitizeTitle } from "./constants.js";
|
|
7
|
+
import { logDebug, logError, logWarn } from "./logger.js";
|
|
7
8
|
import { STOP_WORDS } from "./stop-words.js";
|
|
8
|
-
import { logDebug, logWarn, logError } from "./logger.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Print to stderr. For terminal-only UX messages, not diagnostics.
|
|
@@ -77,22 +77,79 @@ export function parseIsoTimestamp(isoStr: string): Date | null {
|
|
|
77
77
|
export function cleanTextForSlug(text: string): string {
|
|
78
78
|
if (!text) return "";
|
|
79
79
|
let result = text.toLowerCase();
|
|
80
|
-
result = result.
|
|
81
|
-
result = result.
|
|
82
|
-
result = result.
|
|
80
|
+
result = result.replaceAll('\'', ""); // i'm -> im, you're -> youre
|
|
81
|
+
result = result.replaceAll(/[^a-z0-9\s]/g, " "); // punctuation -> spaces
|
|
82
|
+
result = result.replaceAll(/\s+/g, " ").trim();
|
|
83
83
|
return result;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Generate a slug from text using AI inference with stop-word fallbacks.
|
|
88
|
+
* Pipeline: AI inference → stop-word post-filter → stop-word fallback → word-length fallback.
|
|
89
|
+
* Reusable by both context ID generation and plan archival.
|
|
90
|
+
* See SPEC.md §14.2
|
|
91
|
+
*/
|
|
92
|
+
export function generateSlug(
|
|
93
|
+
text: string,
|
|
94
|
+
maxLen = 150,
|
|
95
|
+
fallbackSlug = "context",
|
|
96
|
+
): string {
|
|
97
|
+
if (!text || !text.trim()) return fallbackSlug;
|
|
98
|
+
|
|
99
|
+
let slug: null | string = null;
|
|
100
|
+
const cleanedText = cleanTextForSlug(text);
|
|
101
|
+
|
|
102
|
+
// Tier 1: AI inference via generateContextIdSlug (sync — uses execFileSync)
|
|
103
|
+
try {
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef
|
|
105
|
+
const { generateContextIdSlug } = require("./inference.js");
|
|
106
|
+
const aiSlug = generateContextIdSlug(text);
|
|
107
|
+
if (aiSlug) {
|
|
108
|
+
const filteredWords = aiSlug
|
|
109
|
+
.split(/\s+/)
|
|
110
|
+
.filter(
|
|
111
|
+
(w: string) => !STOP_WORDS.has(w.toLowerCase()) && w.length > 1,
|
|
112
|
+
);
|
|
113
|
+
if (filteredWords.length >= 5) {
|
|
114
|
+
slug = sanitizeTitle(filteredWords.join(" "), maxLen);
|
|
115
|
+
} else {
|
|
116
|
+
logDebug(
|
|
117
|
+
"utils",
|
|
118
|
+
`AI slug too generic after stop-word filter (${filteredWords.length} words remain), using fallback`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (error: any) {
|
|
123
|
+
logWarn("utils", `AI slug generation failed, using fallback: ${error}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Tier 2: Stop-word filtering on cleaned text
|
|
127
|
+
if (!slug) {
|
|
128
|
+
const words = cleanedText
|
|
129
|
+
.split(/\s+/)
|
|
130
|
+
.filter((w) => !STOP_WORDS.has(w) && w.length > 1)
|
|
131
|
+
.slice(0, 12);
|
|
132
|
+
slug = words.length >= 3
|
|
133
|
+
? sanitizeTitle(words.join(" "), maxLen)
|
|
134
|
+
: sanitizeTitle(
|
|
135
|
+
cleanedText.split(/\s+/).filter((w) => w.length > 2).slice(0, 6).join(" "),
|
|
136
|
+
maxLen,
|
|
137
|
+
) || fallbackSlug;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return slug;
|
|
141
|
+
}
|
|
142
|
+
|
|
86
143
|
/**
|
|
87
144
|
* Generate a context ID from a summary string.
|
|
88
145
|
* Format: YYMMDD-HHMM-slug
|
|
89
|
-
*
|
|
146
|
+
* Delegates slug generation to generateSlug().
|
|
90
147
|
* See SPEC.md §14.2
|
|
91
148
|
*/
|
|
92
|
-
export
|
|
149
|
+
export function generateContextId(
|
|
93
150
|
summary: string,
|
|
94
151
|
existingIds?: Set<string>,
|
|
95
|
-
):
|
|
152
|
+
): string {
|
|
96
153
|
const now = new Date();
|
|
97
154
|
const yy = String(now.getFullYear()).slice(2);
|
|
98
155
|
const mm = String(now.getMonth() + 1).padStart(2, "0");
|
|
@@ -104,70 +161,12 @@ export async function generateContextId(
|
|
|
104
161
|
let baseId: string;
|
|
105
162
|
|
|
106
163
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
let slug: string | null = null;
|
|
111
|
-
// Pre-clean for Tier 2/3: strip punctuation so stop words match
|
|
112
|
-
const cleanedSummary = cleanTextForSlug(summary);
|
|
113
|
-
|
|
114
|
-
// Tier 1: AI inference (imported dynamically to avoid circular deps)
|
|
115
|
-
try {
|
|
116
|
-
const { generateContextIdSlug } = await import("./inference.js");
|
|
117
|
-
const aiSlug = generateContextIdSlug(summary);
|
|
118
|
-
if (aiSlug) {
|
|
119
|
-
const filteredWords = aiSlug
|
|
120
|
-
.split(/\s+/)
|
|
121
|
-
.filter(
|
|
122
|
-
(w: string) => !STOP_WORDS.has(w.toLowerCase()) && w.length > 1,
|
|
123
|
-
);
|
|
124
|
-
if (filteredWords.length >= 5) {
|
|
125
|
-
slug = sanitizeTitle(filteredWords.join(" "), 150);
|
|
126
|
-
} else {
|
|
127
|
-
logDebug(
|
|
128
|
-
"utils",
|
|
129
|
-
`AI slug too generic after stop-word filter (${filteredWords.length} words remain), using fallback`,
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
} catch (e: any) {
|
|
134
|
-
logWarn("utils", `AI context ID slug failed, using fallback: ${e}`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Tier 2: Stop-word filtering on cleaned text
|
|
138
|
-
if (!slug) {
|
|
139
|
-
try {
|
|
140
|
-
const words = cleanedSummary
|
|
141
|
-
.split(/\s+/)
|
|
142
|
-
.filter((w) => !STOP_WORDS.has(w) && w.length > 1)
|
|
143
|
-
.slice(0, 12);
|
|
144
|
-
if (words.length >= 3) {
|
|
145
|
-
slug = sanitizeTitle(words.join(" "), 150);
|
|
146
|
-
} else {
|
|
147
|
-
logDebug("utils", `Tier 2 too few content words (${words.length}), falling through to Tier 3`);
|
|
148
|
-
}
|
|
149
|
-
} catch (e: any) {
|
|
150
|
-
logWarn("utils", `Stop-word fallback failed: ${e}`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Tier 3: Simple word-length filter on cleaned text
|
|
155
|
-
if (!slug || slug === "unknown") {
|
|
156
|
-
const words = cleanedSummary
|
|
157
|
-
.split(/\s+/)
|
|
158
|
-
.filter((w) => w.length > 2)
|
|
159
|
-
.slice(0, 6);
|
|
160
|
-
slug = words.length > 0
|
|
161
|
-
? sanitizeTitle(words.join(" "), 150)
|
|
162
|
-
: "context";
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
baseId = `${timestamp}-${slug}`;
|
|
166
|
-
}
|
|
167
|
-
} catch (e: any) {
|
|
164
|
+
const slug = generateSlug(summary);
|
|
165
|
+
baseId = `${timestamp}-${slug}`;
|
|
166
|
+
} catch (error: any) {
|
|
168
167
|
logError(
|
|
169
168
|
"utils",
|
|
170
|
-
`Context ID generation failed entirely, using timestamp: ${
|
|
169
|
+
`Context ID generation failed entirely, using timestamp: ${error}`,
|
|
171
170
|
);
|
|
172
171
|
baseId = `${timestamp}-context`;
|
|
173
172
|
}
|
|
@@ -180,5 +179,6 @@ export async function generateContextId(
|
|
|
180
179
|
while (existingIds.has(`${baseId}-${counter}`)) {
|
|
181
180
|
counter++;
|
|
182
181
|
}
|
|
182
|
+
|
|
183
183
|
return `${baseId}-${counter}`;
|
|
184
184
|
}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
|
-
import * as
|
|
11
|
+
import * as _path from "node:path";
|
|
12
|
+
|
|
12
13
|
import { parseIsoTimestamp } from "../base/utils.js";
|
|
13
14
|
import type { ContextState, Task } from "../types.js";
|
|
14
15
|
|
|
@@ -41,10 +42,10 @@ export function getModeDisplay(mode: string): string {
|
|
|
41
42
|
* Format ISO timestamp as '2 hours ago', 'yesterday', etc.
|
|
42
43
|
* See SPEC.md §11.3
|
|
43
44
|
*/
|
|
44
|
-
export function formatRelativeTime(isoTimestamp:
|
|
45
|
+
export function formatRelativeTime(isoTimestamp: null | string): string {
|
|
45
46
|
if (!isoTimestamp) return "unknown";
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
const dt = parseIsoTimestamp(isoTimestamp);
|
|
48
49
|
if (!dt) return isoTimestamp.slice(0, 16);
|
|
49
50
|
|
|
50
51
|
const now = new Date();
|
|
@@ -61,8 +62,10 @@ export function formatRelativeTime(isoTimestamp: string | null): string {
|
|
|
61
62
|
if (diffMin === 0) return "just now";
|
|
62
63
|
return diffMin === 1 ? "1 minute ago" : `${diffMin} minutes ago`;
|
|
63
64
|
}
|
|
65
|
+
|
|
64
66
|
return diffHours === 1 ? "1 hour ago" : `${diffHours} hours ago`;
|
|
65
67
|
}
|
|
68
|
+
|
|
66
69
|
if (diffDays === 1) return "yesterday";
|
|
67
70
|
if (diffDays < 7) return `${diffDays} days ago`;
|
|
68
71
|
|
|
@@ -77,21 +80,23 @@ export function formatRelativeTime(isoTimestamp: string | null): string {
|
|
|
77
80
|
// Internal helpers
|
|
78
81
|
// ---------------------------------------------------------------------------
|
|
79
82
|
|
|
80
|
-
function taskAttr(task:
|
|
83
|
+
function taskAttr(task: Record<string, any> | Task, key: string, defaultVal = ""): string {
|
|
81
84
|
if (typeof task === "object" && task !== null) {
|
|
82
85
|
return (task as any)[key] ?? defaultVal;
|
|
83
86
|
}
|
|
87
|
+
|
|
84
88
|
return defaultVal;
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
function readPlanContent(planPath: string): [
|
|
91
|
+
function readPlanContent(planPath: string): [null | string, boolean, number] {
|
|
88
92
|
try {
|
|
89
93
|
if (!fs.existsSync(planPath)) return [null, false, 0];
|
|
90
|
-
const content = fs.readFileSync(planPath, "
|
|
94
|
+
const content = fs.readFileSync(planPath, "utf8");
|
|
91
95
|
const total = content.length;
|
|
92
96
|
if (total > MAX_PLAN_INLINE_CHARS) {
|
|
93
97
|
return [content.slice(0, MAX_PLAN_INLINE_CHARS), true, total];
|
|
94
98
|
}
|
|
99
|
+
|
|
95
100
|
return [content, false, total];
|
|
96
101
|
} catch {
|
|
97
102
|
return [null, false, 0];
|
|
@@ -100,7 +105,7 @@ function readPlanContent(planPath: string): [string | null, boolean, number] {
|
|
|
100
105
|
|
|
101
106
|
function modeLabel(ctx: ContextState): string {
|
|
102
107
|
const d = getModeDisplay(ctx.mode ?? "idle");
|
|
103
|
-
return d ? d.
|
|
108
|
+
return d ? d.replaceAll(/^\[|\]$/g, "") : "Active";
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
/**
|
|
@@ -119,7 +124,7 @@ export function buildRestoreSections(
|
|
|
119
124
|
const savedAt = lastSession.saved_at ?? "";
|
|
120
125
|
if (savedAt) {
|
|
121
126
|
const reason = lastSession.save_reason ?? "";
|
|
122
|
-
const reasonDisplay = reason ? reason.
|
|
127
|
+
const reasonDisplay = reason ? reason.replaceAll('_', " ") : "unknown";
|
|
123
128
|
sections.push(`**Last session ended:** ${formatRelativeTime(savedAt)} (${reasonDisplay})`);
|
|
124
129
|
}
|
|
125
130
|
}
|
|
@@ -138,6 +143,7 @@ export function buildRestoreSections(
|
|
|
138
143
|
buckets[s]!.push(taskAttr(t, "subject"));
|
|
139
144
|
}
|
|
140
145
|
}
|
|
146
|
+
|
|
141
147
|
if (Object.values(buckets).some(b => b.length > 0)) {
|
|
142
148
|
sections.push("", `### Previous Work (${tasks.length} tasks)`, "");
|
|
143
149
|
const marks: Record<string, string> = {
|
|
@@ -195,8 +201,7 @@ function resumeBlock(ctx: ContextState, projectRoot: string | undefined, modeTex
|
|
|
195
201
|
];
|
|
196
202
|
const restore = buildRestoreSections(ctx, projectRoot, true);
|
|
197
203
|
if (restore) lines.push(restore);
|
|
198
|
-
lines.push("", "---", "", "**Instructions:**");
|
|
199
|
-
lines.push(...instructions);
|
|
204
|
+
lines.push("", "---", "", "**Instructions:**", ...instructions);
|
|
200
205
|
return lines.join("\n");
|
|
201
206
|
}
|
|
202
207
|
|
|
@@ -218,12 +223,12 @@ export function formatHandoffContinuation(ctx: ContextState, projectRoot?: strin
|
|
|
218
223
|
|
|
219
224
|
try {
|
|
220
225
|
if (handoffPath && fs.existsSync(handoffPath)) {
|
|
221
|
-
lines.push("### Previous Session Handoff", "", fs.readFileSync(handoffPath, "
|
|
226
|
+
lines.push("### Previous Session Handoff", "", fs.readFileSync(handoffPath, "utf8"), "");
|
|
222
227
|
} else {
|
|
223
228
|
lines.push(`*Handoff document not found at \`${handoffPath}\`*`, "");
|
|
224
229
|
}
|
|
225
|
-
} catch (
|
|
226
|
-
lines.push(`*Handoff document at \`${handoffPath}\` could not be read: ${
|
|
230
|
+
} catch (error: any) {
|
|
231
|
+
lines.push(`*Handoff document at \`${handoffPath}\` could not be read: ${error}*`, "");
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
const restore = buildRestoreSections(ctx, projectRoot, true);
|
|
@@ -266,20 +271,21 @@ export function formatContextList(contexts: ContextState[]): string {
|
|
|
266
271
|
if (contexts.length === 0) return "No active contexts found.";
|
|
267
272
|
|
|
268
273
|
const lines = ["## Active Contexts\n"];
|
|
269
|
-
for (
|
|
270
|
-
const ctx =
|
|
274
|
+
for (const [i, context_] of contexts.entries()) {
|
|
275
|
+
const ctx = context_!;
|
|
271
276
|
const timeStr = formatRelativeTime(ctx.last_active);
|
|
272
277
|
const md = getModeDisplay(ctx.mode ?? "idle");
|
|
273
278
|
const si = md ? ` ${md}` : "";
|
|
274
|
-
lines.push(`**${i + 1}. ${ctx.id}**${si}`);
|
|
275
|
-
lines.push(` ${ctx.summary}`);
|
|
279
|
+
lines.push(`**${i + 1}. ${ctx.id}**${si}`, ` ${ctx.summary}`);
|
|
276
280
|
if (ctx.method) {
|
|
277
281
|
lines.push(` Method: ${ctx.method} | Last active: ${timeStr}`);
|
|
278
282
|
} else {
|
|
279
283
|
lines.push(` Last active: ${timeStr}`);
|
|
280
284
|
}
|
|
285
|
+
|
|
281
286
|
lines.push("");
|
|
282
287
|
}
|
|
288
|
+
|
|
283
289
|
return lines.join("\n");
|
|
284
290
|
}
|
|
285
291
|
|
|
@@ -349,11 +355,11 @@ export function formatContextPickerStderr(contexts: ContextState[]): string {
|
|
|
349
355
|
];
|
|
350
356
|
|
|
351
357
|
let selectableCount = 0;
|
|
352
|
-
for (
|
|
353
|
-
const ctx =
|
|
358
|
+
for (const [i, context_] of contexts.entries()) {
|
|
359
|
+
const ctx = context_!;
|
|
354
360
|
const timeStr = formatRelativeTime(ctx.last_active);
|
|
355
361
|
const mode = ctx.mode ?? "idle";
|
|
356
|
-
const isSelectable = mode === "active" ||
|
|
362
|
+
const isSelectable = mode === "active" || Boolean(ctx.handoff_path);
|
|
357
363
|
if (isSelectable) selectableCount++;
|
|
358
364
|
|
|
359
365
|
let status = "";
|
|
@@ -366,10 +372,7 @@ export function formatContextPickerStderr(contexts: ContextState[]): string {
|
|
|
366
372
|
const summary = ctx.summary.length > 48 ? ctx.summary.slice(0, 45) + "..." : ctx.summary;
|
|
367
373
|
const selTag = isSelectable ? " [selectable]" : " [end only]";
|
|
368
374
|
|
|
369
|
-
lines.push(`| ^${i + 1} ${ctx.id}${status}${selTag}
|
|
370
|
-
lines.push(`| ${summary}`);
|
|
371
|
-
lines.push(`| [${timeStr}]`);
|
|
372
|
-
lines.push("|");
|
|
375
|
+
lines.push(`| ^${i + 1} ${ctx.id}${status}${selTag}`, `| ${summary}`, `| [${timeStr}]`, "|");
|
|
373
376
|
}
|
|
374
377
|
|
|
375
378
|
lines.push(
|
|
@@ -394,6 +397,7 @@ export function formatContextPickerStderr(contexts: ContextState[]): string {
|
|
|
394
397
|
"+----------------------------------------------------------------+",
|
|
395
398
|
);
|
|
396
399
|
}
|
|
400
|
+
|
|
397
401
|
lines.push("");
|
|
398
402
|
return lines.join("\n");
|
|
399
403
|
}
|
|
@@ -413,6 +417,7 @@ export function formatCommandFeedback(
|
|
|
413
417
|
const s = ctx.summary.length > 50 ? ctx.summary.slice(0, 50) + "..." : ctx.summary;
|
|
414
418
|
lines.push(`- **${ctx.id}**: ${s}`);
|
|
415
419
|
}
|
|
420
|
+
|
|
416
421
|
lines.push("");
|
|
417
422
|
}
|
|
418
423
|
|
|
@@ -428,5 +433,6 @@ export function formatCommandFeedback(
|
|
|
428
433
|
"Tasks created with TaskCreate will be persisted to this context.",
|
|
429
434
|
);
|
|
430
435
|
}
|
|
436
|
+
|
|
431
437
|
return lines.join("\n");
|
|
432
438
|
}
|
|
@@ -14,29 +14,30 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import * as crypto from "node:crypto";
|
|
17
|
-
|
|
18
|
-
getContext,
|
|
19
|
-
getAllContexts,
|
|
20
|
-
getContextBySessionId,
|
|
21
|
-
createContextFromPrompt,
|
|
22
|
-
createContext,
|
|
23
|
-
completeContext,
|
|
24
|
-
bindSession,
|
|
25
|
-
updateMode,
|
|
26
|
-
} from "./context-store.js";
|
|
17
|
+
|
|
27
18
|
import {
|
|
28
19
|
formatActiveContextReminder,
|
|
20
|
+
formatActiveContinuation as _formatActiveContinuation,
|
|
21
|
+
formatCommandFeedback,
|
|
29
22
|
formatContextCreated,
|
|
30
23
|
formatContextPickerStderr,
|
|
31
|
-
formatCommandFeedback,
|
|
32
24
|
formatHandoffContinuation,
|
|
33
25
|
formatPlanContinuation,
|
|
34
|
-
formatActiveContinuation,
|
|
35
26
|
} from "./context-formatter.js";
|
|
27
|
+
import {
|
|
28
|
+
bindSession,
|
|
29
|
+
completeContext,
|
|
30
|
+
createContext,
|
|
31
|
+
createContextFromPrompt,
|
|
32
|
+
getAllContexts,
|
|
33
|
+
getContext,
|
|
34
|
+
getContextBySessionId,
|
|
35
|
+
updateMode,
|
|
36
|
+
} from "./context-store.js";
|
|
36
37
|
import { normalizePlanContent } from "./plan-manager.js";
|
|
38
|
+
import { logDebug, logError, logInfo, logWarn as _logWarn } from "../base/logger.js";
|
|
37
39
|
import { isInternalCall } from "../base/subprocess-utils.js";
|
|
38
|
-
import {
|
|
39
|
-
import type { ContextState, CaretCommand } from "../types.js";
|
|
40
|
+
import type { CaretCommand, ContextState } from "../types.js";
|
|
40
41
|
|
|
41
42
|
/** Minimum characters required for new context description. */
|
|
42
43
|
const MIN_NEW_CONTEXT_CHARS = 10;
|
|
@@ -65,7 +66,7 @@ export class BlockRequest extends Error {
|
|
|
65
66
|
export function resolveContextByPrefix(
|
|
66
67
|
query: string,
|
|
67
68
|
contexts: ContextState[],
|
|
68
|
-
): [
|
|
69
|
+
): [null | number, null | string] {
|
|
69
70
|
const q = query.toLowerCase();
|
|
70
71
|
const available = contexts.map(c => c.id).join(", ");
|
|
71
72
|
|
|
@@ -107,7 +108,7 @@ export function resolveContextByPrefix(
|
|
|
107
108
|
export function parseChainedCaret(
|
|
108
109
|
prompt: string,
|
|
109
110
|
contexts: ContextState[],
|
|
110
|
-
): [CaretCommand | null,
|
|
111
|
+
): [CaretCommand | null, null | string] {
|
|
111
112
|
if (!prompt.startsWith("^")) return [null, null];
|
|
112
113
|
|
|
113
114
|
const match = prompt.match(/^\^(\S+)(?:\s+(.*))?$/s);
|
|
@@ -120,7 +121,7 @@ export function parseChainedCaret(
|
|
|
120
121
|
|
|
121
122
|
// ^N shorthand
|
|
122
123
|
if (/^\d+$/.test(commandStr)) {
|
|
123
|
-
const num = parseInt(commandStr, 10);
|
|
124
|
+
const num = Number.parseInt(commandStr, 10);
|
|
124
125
|
if (num === 0) {
|
|
125
126
|
if (remaining.length < MIN_NEW_CONTEXT_CHARS) {
|
|
126
127
|
return [null,
|
|
@@ -130,21 +131,25 @@ export function parseChainedCaret(
|
|
|
130
131
|
`Example: ^0 implement user authentication with JWT tokens`,
|
|
131
132
|
];
|
|
132
133
|
}
|
|
134
|
+
|
|
133
135
|
return [{ ends: [], select: null, new_context_desc: remaining, remaining_prompt: "" }, null];
|
|
134
136
|
}
|
|
137
|
+
|
|
135
138
|
if (num < 1 || num > contexts.length) {
|
|
136
139
|
if (contexts.length === 0) {
|
|
137
140
|
return [null, "No existing contexts. Use ^0 <description> to create a new one."];
|
|
138
141
|
}
|
|
142
|
+
|
|
139
143
|
return [null, `Invalid selection. Choose 1-${contexts.length} for existing contexts, or ^0 for new.`];
|
|
140
144
|
}
|
|
145
|
+
|
|
141
146
|
const ctx = contexts[num - 1]!;
|
|
142
147
|
return [{ ends: [], select: ctx.id, new_context_desc: null, remaining_prompt: remaining }, null];
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
// Parse chained commands
|
|
146
151
|
const ends: string[] = [];
|
|
147
|
-
let select:
|
|
152
|
+
let select: null | string = null;
|
|
148
153
|
let pos = 0;
|
|
149
154
|
|
|
150
155
|
while (pos < commandStr.length) {
|
|
@@ -174,11 +179,13 @@ export function parseChainedCaret(
|
|
|
174
179
|
if (numStart === pos) {
|
|
175
180
|
return [null, `Expected number, '*', or ':prefix' after 'E' at position ${numStart + 1}`];
|
|
176
181
|
}
|
|
177
|
-
|
|
182
|
+
|
|
183
|
+
const num = Number.parseInt(commandStr.slice(numStart, pos), 10);
|
|
178
184
|
if (num < 1 || num > contexts.length) {
|
|
179
185
|
if (contexts.length === 0) return [null, "No contexts to end."];
|
|
180
186
|
return [null, `Context ^E${num} invalid. Choose 1-${contexts.length}.`];
|
|
181
187
|
}
|
|
188
|
+
|
|
182
189
|
if (pos < commandStr.length && commandStr[pos] === "+") {
|
|
183
190
|
pos++;
|
|
184
191
|
for (let i = num; i <= contexts.length; i++) {
|
|
@@ -208,13 +215,16 @@ export function parseChainedCaret(
|
|
|
208
215
|
if (numStart === pos) {
|
|
209
216
|
return [null, `Expected number or ':prefix' after 'S' at position ${numStart + 1}`];
|
|
210
217
|
}
|
|
211
|
-
|
|
218
|
+
|
|
219
|
+
const num = Number.parseInt(commandStr.slice(numStart, pos), 10);
|
|
212
220
|
if (num < 1 || num > contexts.length) {
|
|
213
221
|
if (contexts.length === 0) return [null, "No contexts to select."];
|
|
214
222
|
return [null, `Context ^S${num} invalid. Choose 1-${contexts.length}.`];
|
|
215
223
|
}
|
|
224
|
+
|
|
216
225
|
ctx = contexts[num - 1]!;
|
|
217
226
|
}
|
|
227
|
+
|
|
218
228
|
if (select === null) select = ctx.id;
|
|
219
229
|
} else {
|
|
220
230
|
return [null,
|
|
@@ -274,9 +284,9 @@ function matchPlanContent(prompt: string, hasPlanContexts: ContextState[]): Cont
|
|
|
274
284
|
}
|
|
275
285
|
|
|
276
286
|
// Tier 4 (legacy): Signature match
|
|
277
|
-
const promptHead = prompt.slice(0, 500);
|
|
287
|
+
const promptHead = new Set(prompt.slice(0, 500));
|
|
278
288
|
for (const ctx of hasPlanContexts) {
|
|
279
|
-
if (ctx.plan_signature && promptHead.
|
|
289
|
+
if (ctx.plan_signature && promptHead.has(ctx.plan_signature)) {
|
|
280
290
|
logDebug("context_selector", `Tier 4 legacy signature match: ${ctx.id}`);
|
|
281
291
|
return ctx;
|
|
282
292
|
}
|
|
@@ -292,15 +302,15 @@ function matchPlanContent(prompt: string, hasPlanContexts: ContextState[]): Cont
|
|
|
292
302
|
function createNewContext(
|
|
293
303
|
prompt: string,
|
|
294
304
|
projectRoot?: string,
|
|
295
|
-
): [
|
|
305
|
+
): [null | string, string, null | string] {
|
|
296
306
|
try {
|
|
297
307
|
const newCtx = createContextFromPrompt(prompt, projectRoot);
|
|
298
308
|
updateMode(newCtx.id, "active", projectRoot);
|
|
299
309
|
newCtx.mode = "active";
|
|
300
310
|
logInfo("context_selector", `Auto-created context: ${newCtx.id}`);
|
|
301
311
|
return [newCtx.id, "auto_created", formatContextCreated(newCtx)];
|
|
302
|
-
} catch (
|
|
303
|
-
logError("context_selector", `Primary context creation failed: ${
|
|
312
|
+
} catch (error: any) {
|
|
313
|
+
logError("context_selector", `Primary context creation failed: ${error}`);
|
|
304
314
|
try {
|
|
305
315
|
const now = new Date();
|
|
306
316
|
const yy = String(now.getFullYear()).slice(2);
|
|
@@ -320,8 +330,8 @@ function createNewContext(
|
|
|
320
330
|
newCtx.mode = "active";
|
|
321
331
|
logInfo("context_selector", `Fallback context created: ${newCtx.id}`);
|
|
322
332
|
return [newCtx.id, "auto_created_fallback", formatContextCreated(newCtx)];
|
|
323
|
-
} catch (
|
|
324
|
-
logError("context_selector", `ALL context creation failed: ${
|
|
333
|
+
} catch (error: any) {
|
|
334
|
+
logError("context_selector", `ALL context creation failed: ${error}`);
|
|
325
335
|
return [null, "creation_failed", null];
|
|
326
336
|
}
|
|
327
337
|
}
|
|
@@ -335,7 +345,7 @@ function handleCaretCommand(
|
|
|
335
345
|
prompt: string,
|
|
336
346
|
contexts: ContextState[],
|
|
337
347
|
projectRoot?: string,
|
|
338
|
-
): [
|
|
348
|
+
): [null | string, string, null | string] {
|
|
339
349
|
if (contexts.length === 0) {
|
|
340
350
|
const match = prompt.match(/^\^(\S+)(?:\s+(.*))?$/s);
|
|
341
351
|
if (!match) {
|
|
@@ -344,14 +354,16 @@ function handleCaretCommand(
|
|
|
344
354
|
"Example: ^0 implement user authentication system",
|
|
345
355
|
);
|
|
346
356
|
}
|
|
357
|
+
|
|
347
358
|
const prefixValue = match[1]!;
|
|
348
359
|
const remaining = match[2] ?? "";
|
|
349
|
-
if (!/^\d+$/.test(prefixValue) || parseInt(prefixValue, 10) !== 0) {
|
|
360
|
+
if (!/^\d+$/.test(prefixValue) || Number.parseInt(prefixValue, 10) !== 0) {
|
|
350
361
|
throw new BlockRequest(
|
|
351
362
|
"No existing contexts to select. Use ^0 <description> to create a new context.\n" +
|
|
352
363
|
"Example: ^0 implement user authentication system",
|
|
353
364
|
);
|
|
354
365
|
}
|
|
366
|
+
|
|
355
367
|
const description = remaining.trim();
|
|
356
368
|
if (description.length < MIN_NEW_CONTEXT_CHARS) {
|
|
357
369
|
throw new BlockRequest(
|
|
@@ -361,6 +373,7 @@ function handleCaretCommand(
|
|
|
361
373
|
`Example: ^0 implement user authentication with JWT tokens`,
|
|
362
374
|
);
|
|
363
375
|
}
|
|
376
|
+
|
|
364
377
|
return createNewContext(description, projectRoot);
|
|
365
378
|
}
|
|
366
379
|
|
|
@@ -374,6 +387,7 @@ function handleCaretCommand(
|
|
|
374
387
|
if (!ctxToEnd) {
|
|
375
388
|
throw new BlockRequest(`Context '${ctxId}' no longer exists.\n` + formatContextPickerStderr(contexts));
|
|
376
389
|
}
|
|
390
|
+
|
|
377
391
|
completeContext(ctxToEnd.id, projectRoot);
|
|
378
392
|
endedContexts.push(ctxToEnd);
|
|
379
393
|
logInfo("context_selector", `Ended context: ${ctxToEnd.id}`);
|
|
@@ -384,9 +398,10 @@ function handleCaretCommand(
|
|
|
384
398
|
if (ctxId && endedContexts.length > 0) {
|
|
385
399
|
const newCtx = getContext(ctxId, projectRoot);
|
|
386
400
|
const feedback = formatCommandFeedback(endedContexts, newCtx);
|
|
387
|
-
return [ctxId, method
|
|
401
|
+
return [ctxId, method === "creation_failed" ? method : "caret_new", feedback];
|
|
388
402
|
}
|
|
389
|
-
|
|
403
|
+
|
|
404
|
+
return [ctxId, method === "creation_failed" ? method : "caret_new", output];
|
|
390
405
|
}
|
|
391
406
|
|
|
392
407
|
if (cmd.select) {
|
|
@@ -394,6 +409,7 @@ function handleCaretCommand(
|
|
|
394
409
|
if (!selectedCtx) {
|
|
395
410
|
throw new BlockRequest(`Context '${cmd.select}' no longer exists.\n` + formatContextPickerStderr(contexts));
|
|
396
411
|
}
|
|
412
|
+
|
|
397
413
|
logInfo("context_selector", `Caret-selected context: ${selectedCtx.id}`);
|
|
398
414
|
return [selectedCtx.id, "caret_select", formatCommandFeedback(endedContexts, selectedCtx)];
|
|
399
415
|
}
|
|
@@ -408,6 +424,7 @@ function handleCaretCommand(
|
|
|
408
424
|
"Example: implement user authentication system",
|
|
409
425
|
);
|
|
410
426
|
}
|
|
427
|
+
|
|
411
428
|
throw new BlockRequest(
|
|
412
429
|
feedback + "\nNo context selected.\n\nSelect a context to continue:\n" +
|
|
413
430
|
formatContextPickerStderr(remainingContexts),
|
|
@@ -432,7 +449,7 @@ export function determineContext(
|
|
|
432
449
|
prompt: string,
|
|
433
450
|
sessionId?: string,
|
|
434
451
|
projectRoot?: string,
|
|
435
|
-
): [
|
|
452
|
+
): [null | string, string, null | string] {
|
|
436
453
|
if (isInternalCall()) {
|
|
437
454
|
logDebug("context_selector", "Skipping: internal subprocess call");
|
|
438
455
|
return [null, "skip_internal", null];
|
|
@@ -460,6 +477,7 @@ export function determineContext(
|
|
|
460
477
|
"Example: implement user authentication system",
|
|
461
478
|
);
|
|
462
479
|
}
|
|
480
|
+
|
|
463
481
|
throw new BlockRequest(formatContextPickerStderr(contexts));
|
|
464
482
|
}
|
|
465
483
|
|