mirror of
https://github.com/vczh-libraries/Release.git
synced 2026-03-23 07:42:52 +08:00
Update agent
This commit is contained in:
@@ -138,18 +138,53 @@ async function renderFlowChartMermaid(chart, container, onInspect) {
|
||||
const zoomMin = 0.2;
|
||||
const zoomMax = 3;
|
||||
const zoomStep = 0.1;
|
||||
container.addEventListener("wheel", (e) => {
|
||||
if (!e.ctrlKey) return;
|
||||
e.preventDefault();
|
||||
const delta = e.deltaY > 0 ? -zoomStep : zoomStep;
|
||||
zoomLevel = Math.min(zoomMax, Math.max(zoomMin, zoomLevel + delta));
|
||||
|
||||
function applyZoom() {
|
||||
const svgInner = container.querySelector("svg");
|
||||
if (svgInner) {
|
||||
svgInner.style.transformOrigin = "top left";
|
||||
svgInner.style.transform = `scale(${zoomLevel})`;
|
||||
}
|
||||
}
|
||||
|
||||
container.addEventListener("wheel", (e) => {
|
||||
if (!e.ctrlKey) return;
|
||||
e.preventDefault();
|
||||
const delta = e.deltaY > 0 ? -zoomStep : zoomStep;
|
||||
zoomLevel = Math.min(zoomMax, Math.max(zoomMin, zoomLevel + delta));
|
||||
applyZoom();
|
||||
}, { passive: false });
|
||||
|
||||
// Touch pinch-to-zoom (two fingers)
|
||||
let lastTouchDist = null;
|
||||
container.addEventListener("touchstart", (e) => {
|
||||
if (e.touches.length === 2) {
|
||||
e.preventDefault();
|
||||
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
||||
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
||||
lastTouchDist = Math.hypot(dx, dy);
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
container.addEventListener("touchmove", (e) => {
|
||||
if (e.touches.length === 2 && lastTouchDist !== null) {
|
||||
e.preventDefault();
|
||||
const dx = e.touches[0].clientX - e.touches[1].clientX;
|
||||
const dy = e.touches[0].clientY - e.touches[1].clientY;
|
||||
const newDist = Math.hypot(dx, dy);
|
||||
const scale = newDist / lastTouchDist;
|
||||
zoomLevel = Math.min(zoomMax, Math.max(zoomMin, zoomLevel * scale));
|
||||
lastTouchDist = newDist;
|
||||
applyZoom();
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
container.addEventListener("touchend", (e) => {
|
||||
if (e.touches.length < 2) {
|
||||
lastTouchDist = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Track currently selected TaskNode/CondNode
|
||||
let currentSelectedGroup = null;
|
||||
let currentSelectedOriginalStrokeWidth = null;
|
||||
|
||||
@@ -172,3 +172,54 @@ html, body {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* ---- Phone-specific layout ---- */
|
||||
|
||||
/* Back button: hidden on desktop, visible only in phone mode */
|
||||
.phone-back-btn {
|
||||
display: none;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #444;
|
||||
border-bottom: none;
|
||||
border-radius: 4px 4px 0 0;
|
||||
background: #dc2626;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.phone-back-btn:hover {
|
||||
background: #b91c1c;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.phone-back-btn {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#resize-bar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#right-part {
|
||||
display: none !important;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
z-index: 1000;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
#right-part.phone-visible {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
#left-part {
|
||||
width: 100% !important;
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Copilot Portal - Job Tracking</title>
|
||||
<link rel="stylesheet" href="jobTracking.css">
|
||||
<link rel="stylesheet" href="messageBlock.css">
|
||||
|
||||
@@ -21,6 +21,7 @@ let chartController = null; // returned from renderFlowChartMermaid
|
||||
let jobStatus = isPreviewMode ? "PREVIEW" : "RUNNING"; // PREVIEW | RUNNING | SUCCEEDED | FAILED | CANCELED
|
||||
let jobStopped = false;
|
||||
|
||||
|
||||
// Map: workId -> { taskId, sessions: Map<sessionId, { name, renderer, div, active }>, attemptCount }
|
||||
const workIdData = {};
|
||||
|
||||
@@ -116,13 +117,6 @@ function showJsonView() {
|
||||
function showTaskSessionTabs(workId) {
|
||||
sessionResponsePart.innerHTML = "";
|
||||
const data = workIdData[workId];
|
||||
if (!data) {
|
||||
sessionResponsePart.textContent = "No session data for this task.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset active tab tracking so the first switchTabForWork always applies
|
||||
data.activeTabSessionId = null;
|
||||
|
||||
tabContainer = document.createElement("div");
|
||||
tabContainer.className = "tab-container";
|
||||
@@ -137,33 +131,56 @@ function showTaskSessionTabs(workId) {
|
||||
|
||||
sessionResponsePart.appendChild(tabContainer);
|
||||
|
||||
// Ensure "Driving" tab always appears first
|
||||
const sortedEntries = [...data.sessions.entries()].sort((a, b) => {
|
||||
if (a[1].name === "Driving") return -1;
|
||||
if (b[1].name === "Driving") return 1;
|
||||
return 0;
|
||||
});
|
||||
if (!data || data.sessions.size === 0) {
|
||||
// No session data yet — show placeholder in tab content
|
||||
const placeholder = document.createElement("div");
|
||||
placeholder.style.padding = "16px";
|
||||
placeholder.textContent = "No session data for this task.";
|
||||
tabContent.appendChild(placeholder);
|
||||
} else {
|
||||
// Reset active tab tracking so the first switchTabForWork always applies
|
||||
data.activeTabSessionId = null;
|
||||
|
||||
for (const [sessionId, sessionInfo] of sortedEntries) {
|
||||
const tabBtn = document.createElement("button");
|
||||
tabBtn.className = "tab-header-btn";
|
||||
tabBtn.textContent = sessionInfo.name;
|
||||
tabBtn.dataset.sessionId = sessionId;
|
||||
tabBtn.addEventListener("click", () => {
|
||||
switchTabForWork(workId, tabBtn.dataset.sessionId);
|
||||
// Ensure "Driving" tab always appears first
|
||||
const sortedEntries = [...data.sessions.entries()].sort((a, b) => {
|
||||
if (a[1].name === "Driving") return -1;
|
||||
if (b[1].name === "Driving") return 1;
|
||||
return 0;
|
||||
});
|
||||
tabHeaders.appendChild(tabBtn);
|
||||
|
||||
// Append the session's div to tab content (hidden by default)
|
||||
sessionInfo.div.style.display = "none";
|
||||
tabContent.appendChild(sessionInfo.div);
|
||||
for (const [sessionId, sessionInfo] of sortedEntries) {
|
||||
const tabBtn = document.createElement("button");
|
||||
tabBtn.className = "tab-header-btn";
|
||||
tabBtn.textContent = sessionInfo.name;
|
||||
tabBtn.dataset.sessionId = sessionId;
|
||||
tabBtn.addEventListener("click", () => {
|
||||
switchTabForWork(workId, tabBtn.dataset.sessionId);
|
||||
});
|
||||
tabHeaders.appendChild(tabBtn);
|
||||
|
||||
// Append the session's div to tab content (hidden by default)
|
||||
sessionInfo.div.style.display = "none";
|
||||
tabContent.appendChild(sessionInfo.div);
|
||||
}
|
||||
|
||||
// Activate the first tab
|
||||
const firstEntry = sortedEntries[0];
|
||||
if (firstEntry) {
|
||||
switchTabForWork(workId, firstEntry[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Activate the first tab
|
||||
const firstEntry = sortedEntries[0];
|
||||
if (firstEntry) {
|
||||
switchTabForWork(workId, firstEntry[0]);
|
||||
}
|
||||
// Add "Back" button (visible only in phone mode via CSS)
|
||||
const backBtn = document.createElement("button");
|
||||
backBtn.className = "phone-back-btn";
|
||||
backBtn.textContent = "Back";
|
||||
backBtn.addEventListener("click", () => {
|
||||
// Unselect the inspected node
|
||||
inspectedWorkId = null;
|
||||
rightPart.classList.remove("phone-visible");
|
||||
showJsonView();
|
||||
});
|
||||
tabHeaders.appendChild(backBtn);
|
||||
}
|
||||
|
||||
function refreshSessionResponsePart() {
|
||||
@@ -177,6 +194,12 @@ function refreshSessionResponsePart() {
|
||||
function onInspect(workId) {
|
||||
inspectedWorkId = workId;
|
||||
refreshSessionResponsePart();
|
||||
if (workId !== null) {
|
||||
// In phone mode CSS makes this a full-screen overlay; on desktop it has no effect
|
||||
rightPart.classList.add("phone-visible");
|
||||
} else {
|
||||
rightPart.classList.remove("phone-visible");
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Live Polling Helpers ----
|
||||
@@ -317,6 +340,9 @@ function startTaskPolling(taskId, workId) {
|
||||
div.style.display = "none";
|
||||
tabContent.insertBefore(div, tabContent.firstChild);
|
||||
}
|
||||
// Ensure Back button stays at the end
|
||||
const existingBackBtn = tabHeaders.querySelector(".phone-back-btn");
|
||||
if (existingBackBtn) tabHeaders.appendChild(existingBackBtn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +375,13 @@ function startTaskPolling(taskId, workId) {
|
||||
tabBtn.addEventListener("click", () => {
|
||||
switchTabForWork(workId, sessionId);
|
||||
});
|
||||
tabHeaders.appendChild(tabBtn);
|
||||
// Insert before phone Back button if present
|
||||
const phoneBackBtn = tabHeaders.querySelector(".phone-back-btn");
|
||||
if (phoneBackBtn) {
|
||||
tabHeaders.insertBefore(tabBtn, phoneBackBtn);
|
||||
} else {
|
||||
tabHeaders.appendChild(tabBtn);
|
||||
}
|
||||
|
||||
div.style.display = "none";
|
||||
tabContent.appendChild(div);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"testExecute": "node test/runTests.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@github/copilot-sdk": "^0.1.4"
|
||||
"@github/copilot-sdk": "^0.1.29"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.49.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CopilotClient, defineTool, type CopilotSession } from "@github/copilot-sdk";
|
||||
import { CopilotClient, defineTool, approveAll, type CopilotSession } from "@github/copilot-sdk";
|
||||
|
||||
export interface ICopilotSession {
|
||||
get rawSection(): CopilotSession;
|
||||
@@ -82,6 +82,7 @@ export async function startSession(
|
||||
streaming: true,
|
||||
workingDirectory,
|
||||
tools: jobTools,
|
||||
onPermissionRequest: approveAll,
|
||||
hooks: {
|
||||
onPreToolUse: async (input) => {
|
||||
if (input.toolName === "glob") {
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
apiCopilotSessionQuery,
|
||||
apiCopilotSessionLive,
|
||||
hasRunningSessions,
|
||||
helperGetModels,
|
||||
} from "./copilotApi.js";
|
||||
import {
|
||||
apiTaskList,
|
||||
@@ -96,6 +97,19 @@ async function installJobsEntry(entryValue: Entry): Promise<void> {
|
||||
if (hasRunningSessions()) {
|
||||
throw new Error("Cannot call installJobsEntry while sessions are running.");
|
||||
}
|
||||
const models = await helperGetModels();
|
||||
const validModelIds = new Set(models.map(m => m.id));
|
||||
for (const [category, modelId] of Object.entries(entryValue.models)) {
|
||||
if (!validModelIds.has(modelId)) {
|
||||
throw new Error(`entry.models["${category}"] refers to model "${modelId}" which is not a valid model.`);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < entryValue.drivingSessionRetries.length; i++) {
|
||||
const modelId = entryValue.drivingSessionRetries[i].modelId;
|
||||
if (!validModelIds.has(modelId)) {
|
||||
throw new Error(`entry.drivingSessionRetries[${i}].modelId refers to model "${modelId}" which is not a valid model.`);
|
||||
}
|
||||
}
|
||||
installedEntry = entryValue;
|
||||
}
|
||||
|
||||
@@ -308,12 +322,15 @@ const server = http.createServer((req, res) => {
|
||||
serveStaticFile(res, filePath);
|
||||
});
|
||||
|
||||
// Install the jobs entry (only if not in test mode)
|
||||
if (!testMode) {
|
||||
installJobsEntry(entry);
|
||||
// Install the jobs entry (only if not in test mode), then start server
|
||||
async function startServer(): Promise<void> {
|
||||
if (!testMode) {
|
||||
await installJobsEntry(entry);
|
||||
}
|
||||
server.listen(port, () => {
|
||||
console.log(`http://localhost:${port}`);
|
||||
console.log(`http://localhost:${port}/api/stop`);
|
||||
});
|
||||
}
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log(`http://localhost:${port}`);
|
||||
console.log(`http://localhost:${port}/api/stop`);
|
||||
});
|
||||
startServer();
|
||||
|
||||
@@ -52,8 +52,8 @@ const entryInput: Entry = {
|
||||
planning: "gpt-5.2",
|
||||
coding: "gpt-5.2-codex",
|
||||
reviewers1: "gpt-5.3-codex",
|
||||
reviewers2: "claude-opus-4.5",
|
||||
reviewers3: "gemini-3-pro-preview"
|
||||
reviewers2: "claude-opus-4.6",
|
||||
reviewers3: "claude-sonnet-4.6"
|
||||
},
|
||||
drivingSessionRetries: [
|
||||
{ modelId: "gpt-5-mini", retries: 5 },
|
||||
@@ -64,121 +64,123 @@ const entryInput: Entry = {
|
||||
promptVariables: {
|
||||
reviewerBoardFiles: [
|
||||
"## Your Identity",
|
||||
"You are $task-model, one of the reviewers in the review board.",
|
||||
"- You are $task-model, one of the reviewers in the review board.",
|
||||
"- The review document you are going to create is Copilot_Review_Writing_{YOUR-MODEL}.md",
|
||||
"- You are going to review a target document, as well as any existing Copilot_Review_Finished_{OTHER-MODEL}.md.",
|
||||
"## Reviewer Board Files",
|
||||
"- gpt -> Copilot_Review_*_GPT.md",
|
||||
"- claude opus -> Copilot_Review_*_OPUS.md",
|
||||
"- gemini -> Copilot_Review_*_GEMINI.md",
|
||||
"- claude sonnet -> Copilot_Review_*_SONNET.md",
|
||||
],
|
||||
copilotSdkTips: [
|
||||
"NOTE: If you can't find the file, try different ways to make sure, including absolute path, relative path, powershell tool, view tool, slash and backslash, etc.",
|
||||
"AVOID the glob tool to find any files, it does not work on Windows."
|
||||
"- NOTE: If you can't find the file, try different ways to make sure, including absolute path, relative path, powershell tool, view tool, slash and backslash, etc.",
|
||||
"- AVOID the glob tool to find any files, it does not work on Windows."
|
||||
],
|
||||
defineRepoRoot: [
|
||||
"REPO-ROOT is the root directory of the repo (aka the working directory you are currently in)"
|
||||
"- REPO-ROOT is the root directory of the repo (aka the working directory you are currently in)"
|
||||
],
|
||||
noQuestion: [
|
||||
"DO NOT ask user if you can start doing something, especially after you made a plan, always perform your job automatically and proactively til the end."
|
||||
"- DO NOT ask user if you can start doing something, especially after you made a plan, always perform your job automatically and proactively til the end."
|
||||
],
|
||||
cppjob: [
|
||||
"$defineRepoRoot",
|
||||
"$copilotSdkTips",
|
||||
"YOU MUST FOLLOW REPO-ROOT/.github/copilot-instructions.md as a general guideline for all your tasks."
|
||||
"- YOU MUST FOLLOW REPO-ROOT/.github/copilot-instructions.md as a general guideline for all your tasks."
|
||||
],
|
||||
scrum: [
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/0-scrum.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/0-scrum.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
design: [
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/1-design.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/1-design.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
plan: [
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/2-planning.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/2-planning.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
summary: [
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/3-summarizing.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/3-summarizing.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
codingPrefix: [
|
||||
"**IMPORT**: It is FORBIDDEN to modify any script files in `REPO-ROOT/.github/Scripts`. If you are getting trouble, the only reason is your code has problem. Fix the code instead of any other kind of working around.",
|
||||
"- **IMPORT**: It is FORBIDDEN to modify any script files in `REPO-ROOT/.github/Scripts`. If you are getting trouble, the only reason is your code has problem. Fix the code instead of any other kind of working around.",
|
||||
],
|
||||
execute: [
|
||||
"$codingPrefix",
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/4-execution.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/4-execution.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
verify: [
|
||||
"$codingPrefix",
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/5-verifying.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/5-verifying.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
refine: [
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/refine.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/refine.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
review: [
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/review.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/review.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
ask: [
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/ask.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/ask.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
code: [
|
||||
"Execute the instruction in REPO-ROOT/.github/prompts/code.prompt.md immediately.",
|
||||
"- Execute the instruction in REPO-ROOT/.github/prompts/code.prompt.md immediately.",
|
||||
"$noQuestion"
|
||||
],
|
||||
reportDocument: [
|
||||
"YOU MUST use the job_prepare_document tool with an argument: an absolute path of the document you are about to create or update.",
|
||||
"YOU MUST use the job_prepare_document tool even when you think nothing needs to be updated, it is to make sure you are clear about which document to work on."
|
||||
"- YOU MUST use the job_prepare_document tool with an argument: an absolute path of the document you are about to create or update.",
|
||||
"- YOU MUST use the job_prepare_document tool even when you think nothing needs to be updated, it is to make sure you are clear about which document to work on."
|
||||
],
|
||||
reportBoolean: [
|
||||
"YOU MUST use either job_boolean_true tool or job_boolean_false tool to answer an yes/no question, with the reason in the argument."
|
||||
"- YOU MUST use either job_boolean_true tool or job_boolean_false tool to answer an yes/no question, with the reason in the argument."
|
||||
],
|
||||
simpleCondition: [
|
||||
"$defineRepoRoot",
|
||||
"$copilotSdkTips",
|
||||
"$reportBoolean",
|
||||
"Use job_boolean_true tool if the below condition satisfies, or use job_boolean_false tool if it does not satisfy."
|
||||
"- Use job_boolean_true tool if the below condition satisfies, or use job_boolean_false tool if it does not satisfy."
|
||||
],
|
||||
scrumDocReady: [
|
||||
"$simpleCondition",
|
||||
"REPO-ROOT/.github/TaskLogs/Copilot_Scrum.md should exist and its content should not be just a title."
|
||||
"- REPO-ROOT/.github/TaskLogs/Copilot_Scrum.md should exist and its content should not be just a title."
|
||||
],
|
||||
designDocReady: [
|
||||
"$simpleCondition",
|
||||
"REPO-ROOT/.github/TaskLogs/Copilot_Task.md should exist and its content should not be just a title."
|
||||
"- REPO-ROOT/.github/TaskLogs/Copilot_Task.md should exist and its content should not be just a title."
|
||||
],
|
||||
planDocReady: [
|
||||
"$simpleCondition",
|
||||
"REPO-ROOT/.github/TaskLogs/Copilot_Planning.md should exist and its content should not be just a title."
|
||||
"- REPO-ROOT/.github/TaskLogs/Copilot_Planning.md should exist and its content should not be just a title."
|
||||
],
|
||||
execDocReady: [
|
||||
"$simpleCondition",
|
||||
"REPO-ROOT/.github/TaskLogs/Copilot_Execution.md should exist and its content should not be just a title."
|
||||
"- REPO-ROOT/.github/TaskLogs/Copilot_Execution.md should exist and its content should not be just a title."
|
||||
],
|
||||
execDocVerified: [
|
||||
"$simpleCondition",
|
||||
"REPO-ROOT/.github/TaskLogs/Copilot_Execution.md should exist and it has a `# !!!VERIFIED!!!`."
|
||||
"- REPO-ROOT/.github/TaskLogs/Copilot_Execution.md should exist and it has a `# !!!VERIFIED!!!`."
|
||||
],
|
||||
reviewDocReady: [
|
||||
"$simpleCondition",
|
||||
"REPO-ROOT/.github/TaskLogs/Copilot_Review.md should exist and its content should not be just a title."
|
||||
"- REPO-ROOT/.github/TaskLogs/Copilot_Review.md should exist and its content should not be just a title."
|
||||
],
|
||||
reportedDocReady: [
|
||||
"$simpleCondition",
|
||||
"$reported-document should exist and its content should not be just a title."
|
||||
"- $reported-document should exist and its content should not be just a title."
|
||||
],
|
||||
clearBuildTestLog: [
|
||||
"In REPO-ROOT/.github/Scripts, delete both Build.log and Execute.log."
|
||||
"- In REPO-ROOT/.github/Scripts, delete both Build.log and Execute.log."
|
||||
],
|
||||
buildSucceededFragment: [
|
||||
"REPO-ROOT/.github/Scripts/Build.log must exist and the last several lines shows there is no error"
|
||||
"- REPO-ROOT/.github/Scripts/Build.log must exist and the last several lines shows there is no error"
|
||||
],
|
||||
testPassedFragment: [
|
||||
"REPO-ROOT/.github/Scripts/Execute.log must exist and the last several lines shows how many test files and test cases passed"
|
||||
"- REPO-ROOT/.github/Scripts/Execute.log must exist and the last several lines shows how many test files and test cases passed"
|
||||
]
|
||||
},
|
||||
tasks: {
|
||||
@@ -334,7 +336,7 @@ const entryInput: Entry = {
|
||||
},
|
||||
criteria: {
|
||||
runConditionInSameSession: false,
|
||||
condition: ["$simpleCondition", "All REPO-ROOT/.github/TaskLogs/Copilot_(Task|Planning|Execution).md must have been deleted."],
|
||||
condition: ["$simpleCondition", "- All REPO-ROOT/.github/TaskLogs/Copilot_(Task|Planning|Execution).md must have been deleted."],
|
||||
failureAction: retryFailedCondition()
|
||||
}
|
||||
},
|
||||
@@ -404,7 +406,7 @@ const entryInput: Entry = {
|
||||
prompt: ["$cppjob", "$review", "# Apply", "$reviewerBoardFiles"],
|
||||
criteria: {
|
||||
runConditionInSameSession: false,
|
||||
condition: ["$simpleCondition", "Every REPO-ROOT/.github/TaskLogs/Copilot_Review*.md must have been deleted."],
|
||||
condition: ["$simpleCondition", "- Every REPO-ROOT/.github/TaskLogs/Copilot_Review*.md must have been deleted."],
|
||||
failureAction: retryFailedCondition()
|
||||
}
|
||||
},
|
||||
@@ -419,7 +421,7 @@ const entryInput: Entry = {
|
||||
prompt: ["$cppjob", "$code", "$user-input"],
|
||||
criteria: {
|
||||
runConditionInSameSession: true,
|
||||
condition: ["$simpleCondition", "Both conditions satisfy: 1) $buildSucceededFragment; 2) $testPassedFragment."],
|
||||
condition: ["$simpleCondition", "- Both conditions satisfy: 1) $buildSucceededFragment; 2) $testPassedFragment."],
|
||||
failureAction: retryFailedCondition()
|
||||
}
|
||||
},
|
||||
@@ -428,27 +430,27 @@ const entryInput: Entry = {
|
||||
requireUserInput: false,
|
||||
prompt: [
|
||||
"$defineRepoRoot",
|
||||
"Call REPO-ROOT/.github/Scripts/copilotGitCommit.ps1",
|
||||
"DO NOT git push."
|
||||
"- Call REPO-ROOT/.github/Scripts/copilotGitCommit.ps1",
|
||||
"- DO NOT git push."
|
||||
]
|
||||
},
|
||||
"git-push": {
|
||||
model: { category: "driving" },
|
||||
requireUserInput: false,
|
||||
prompt: [
|
||||
"`git add` to add all files.",
|
||||
"`git status` to list affected files.",
|
||||
"`git commit -am` everything with this message: [BOT] Backup.",
|
||||
"`git branch` to see the current branch.",
|
||||
"`git push` to the current branch.",
|
||||
"DO NOT run multiple commands at once."
|
||||
"- `git add` to add all files.",
|
||||
"- `git status` to list affected files.",
|
||||
"- `git commit -am` everything with this message: [BOT] Backup.",
|
||||
"- `git branch` to see the current branch.",
|
||||
"- `git push` to the current branch.",
|
||||
"- DO NOT run multiple commands at once."
|
||||
],
|
||||
criteria: {
|
||||
runConditionInSameSession: true,
|
||||
condition: [
|
||||
"$simpleCondition",
|
||||
"`git status` to list file affected, make sure there is nothing uncommited.",
|
||||
"But it is fine if all uncommited changes are only whitespace related."
|
||||
"- `git status` to list file affected, make sure there is nothing uncommited.",
|
||||
"- But it is fine if all uncommited changes are only whitespace related."
|
||||
],
|
||||
failureAction: retryFailedCondition()
|
||||
}
|
||||
|
||||
@@ -306,6 +306,66 @@ describe("API: copilot/test/installJobsEntry", () => {
|
||||
assert.strictEqual(data.result, "OK", `installJobsEntry should succeed: ${JSON.stringify(data)}`);
|
||||
});
|
||||
|
||||
it("rejects entry with invalid model IDs", async () => {
|
||||
const invalidModelEntryPath = path.join(__dirname, "invalidModelEntry.json");
|
||||
const fs = await import("node:fs");
|
||||
// A structurally valid entry but with a model ID that doesn't exist
|
||||
fs.writeFileSync(invalidModelEntryPath, JSON.stringify({
|
||||
models: { driving: "nonexistent-model-xyz" },
|
||||
drivingSessionRetries: [{ modelId: "nonexistent-model-xyz", retries: 1 }],
|
||||
promptVariables: {},
|
||||
grid: [],
|
||||
tasks: {
|
||||
"dummy-task": { model: { category: "driving" }, prompt: ["hello"], requireUserInput: false }
|
||||
},
|
||||
jobs: {}
|
||||
}));
|
||||
try {
|
||||
const data = await fetchJson("/api/copilot/test/installJobsEntry", {
|
||||
method: "POST",
|
||||
body: invalidModelEntryPath,
|
||||
});
|
||||
assert.strictEqual(data.result, "Rejected", `should reject invalid model: ${JSON.stringify(data)}`);
|
||||
assert.ok(data.error, "should have error message about invalid model");
|
||||
assert.ok(data.error.includes("nonexistent-model-xyz"), "error should mention the invalid model name");
|
||||
} finally {
|
||||
fs.unlinkSync(invalidModelEntryPath);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects entry with invalid drivingSessionRetries model IDs", async () => {
|
||||
const invalidRetryEntryPath = path.join(__dirname, "invalidRetryEntry.json");
|
||||
const fs = await import("node:fs");
|
||||
const modelsData = await fetchJson("/api/copilot/models");
|
||||
const freeModel = modelsData.models.find((m) => m.multiplier === 0);
|
||||
// Use a valid model for entry.models but an invalid model in drivingSessionRetries
|
||||
fs.writeFileSync(invalidRetryEntryPath, JSON.stringify({
|
||||
models: { driving: freeModel.id },
|
||||
drivingSessionRetries: [
|
||||
{ modelId: freeModel.id, retries: 1 },
|
||||
{ modelId: "nonexistent-retry-model-xyz", retries: 2 }
|
||||
],
|
||||
promptVariables: {},
|
||||
grid: [],
|
||||
tasks: {
|
||||
"dummy-task": { model: { category: "driving" }, prompt: ["hello"], requireUserInput: false }
|
||||
},
|
||||
jobs: {}
|
||||
}));
|
||||
try {
|
||||
const data = await fetchJson("/api/copilot/test/installJobsEntry", {
|
||||
method: "POST",
|
||||
body: invalidRetryEntryPath,
|
||||
});
|
||||
assert.strictEqual(data.result, "Rejected", `should reject invalid drivingSessionRetries model: ${JSON.stringify(data)}`);
|
||||
assert.ok(data.error, "should have error message about invalid model");
|
||||
assert.ok(data.error.includes("nonexistent-retry-model-xyz"), "error should mention the invalid model name");
|
||||
assert.ok(data.error.includes("drivingSessionRetries"), "error should mention drivingSessionRetries");
|
||||
} finally {
|
||||
fs.unlinkSync(invalidRetryEntryPath);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects when session is running", async () => {
|
||||
const modelsData = await fetchJson("/api/copilot/models");
|
||||
const freeModel = modelsData.models.find((m) => m.multiplier === 0);
|
||||
|
||||
@@ -45,6 +45,8 @@ Prints the following URL for shortcut:
|
||||
### installJobsEntry
|
||||
|
||||
`async installJobsEntry(entry: Entry): Promise<void>;`
|
||||
- Verify if all `entry.model[name]` is a valid model with `helperGetModels`.
|
||||
- Verify if all `entry.drivingSessionRetries[index].modelId` is a valid model with `helperGetModels`.
|
||||
- Use the entry. It could be `entry` from `jobsData.ts` or whatever.
|
||||
- This function can only be called when no session is running, otherwise throws.
|
||||
|
||||
|
||||
@@ -234,6 +234,8 @@ When the user holds **Ctrl** and scrolls the mouse wheel over the `#chart-contai
|
||||
- CSS `transform: scale(...)` with `transform-origin: top left` is applied to the SVG element.
|
||||
- The default browser scroll/zoom behavior is suppressed (`preventDefault`).
|
||||
|
||||
User should be able to zoom in and zoom out also using two touch points (two fingers).
|
||||
|
||||
#### Interaction with `ChartNode` which has a `TaskNode` or `CondNode` hint
|
||||
|
||||
Clicking it select (exclusive) or unselect the text:
|
||||
@@ -252,3 +254,16 @@ When a task is being inspected:
|
||||
- Each task session has its own tab.
|
||||
- Clicking a tab shows responses from a session using `Session Response Rendering` from `Shared.md`.
|
||||
- When the selected `ChartNode` is restarted, tabs should be cleared before adding new sessions.
|
||||
|
||||
### Phone Specific Layout/Behavior
|
||||
|
||||
**IMPORTANT**:
|
||||
- Phone mode is defined by `max=width: 768px`. The session only applies when the webpage is in phone mode.
|
||||
- On a PC browser while resizing, it should be able to switch between phone mode and PC mode in any status.
|
||||
- Build the webpage fully reactive, which means it is not allowed to save the "mode" in anyway even temporarily.
|
||||
|
||||
- The `Session Response Part` is not visible at the beginning.
|
||||
- There is also no draggable bar between it and the `Job Part`.
|
||||
- When a `ChartNode` is clicked, the `Session Response Part` becomes visible and occupy the whole window.
|
||||
- At the very right of the tab header of the `Session Response Part`, there should be a "Back" button.
|
||||
- Clicking the button hide the `Session Response Part` and the `Job Part` becomes available again.
|
||||
|
||||
@@ -45,6 +45,8 @@ Prints the following URL for shortcut:
|
||||
### installJobsEntry
|
||||
|
||||
`async installJobsEntry(entry: Entry): Promise<void>;`
|
||||
- Verify if all `entry.model[name]` is a valid model with `helperGetModels`.
|
||||
- Verify if all `entry.drivingSessionRetries[index].modelId` is a valid model with `helperGetModels`.
|
||||
- Use the entry. It could be `entry` from `jobsData.ts` or whatever.
|
||||
- This function can only be called when no session is running, otherwise throws.
|
||||
|
||||
|
||||
15
.github/Agent/prompts/spec/CopilotPortal/Jobs.md
vendored
15
.github/Agent/prompts/spec/CopilotPortal/Jobs.md
vendored
@@ -234,6 +234,8 @@ When the user holds **Ctrl** and scrolls the mouse wheel over the `#chart-contai
|
||||
- CSS `transform: scale(...)` with `transform-origin: top left` is applied to the SVG element.
|
||||
- The default browser scroll/zoom behavior is suppressed (`preventDefault`).
|
||||
|
||||
User should be able to zoom in and zoom out also using two touch points (two fingers).
|
||||
|
||||
#### Interaction with `ChartNode` which has a `TaskNode` or `CondNode` hint
|
||||
|
||||
Clicking it select (exclusive) or unselect the text:
|
||||
@@ -252,3 +254,16 @@ When a task is being inspected:
|
||||
- Each task session has its own tab.
|
||||
- Clicking a tab shows responses from a session using `Session Response Rendering` from `Shared.md`.
|
||||
- When the selected `ChartNode` is restarted, tabs should be cleared before adding new sessions.
|
||||
|
||||
### Phone Specific Layout/Behavior
|
||||
|
||||
**IMPORTANT**:
|
||||
- Phone mode is defined by `max=width: 768px`. The session only applies when the webpage is in phone mode.
|
||||
- On a PC browser while resizing, it should be able to switch between phone mode and PC mode in any status.
|
||||
- Build the webpage fully reactive, which means it is not allowed to save the "mode" in anyway even temporarily.
|
||||
|
||||
- The `Session Response Part` is not visible at the beginning.
|
||||
- There is also no draggable bar between it and the `Job Part`.
|
||||
- When a `ChartNode` is clicked, the `Session Response Part` becomes visible and occupy the whole window.
|
||||
- At the very right of the tab header of the `Session Response Part`, there should be a "Back" button.
|
||||
- Clicking the button hide the `Session Response Part` and the `Job Part` becomes available again.
|
||||
|
||||
78
.github/Agent/yarn.lock
vendored
78
.github/Agent/yarn.lock
vendored
@@ -2,56 +2,56 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@github/copilot-darwin-arm64@0.0.403":
|
||||
version "0.0.403"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.403.tgz#e1cbc91f73639c8217fd2cc61498bc311e2af45c"
|
||||
integrity sha512-dOw8IleA0d1soHnbr/6wc6vZiYWNTKMgfTe/NET1nCfMzyKDt/0F0I7PT5y+DLujJknTla/ZeEmmBUmliTW4Cg==
|
||||
"@github/copilot-darwin-arm64@0.0.420":
|
||||
version "0.0.420"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.420.tgz#560ca002fa491c04fdb6f74f84fee87e52575c53"
|
||||
integrity sha512-sj8Oxcf3oKDbeUotm2gtq5YU1lwCt3QIzbMZioFD/PMLOeqSX/wrecI+c0DDYXKofFhALb0+DxxnWgbEs0mnkQ==
|
||||
|
||||
"@github/copilot-darwin-x64@0.0.403":
|
||||
version "0.0.403"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.403.tgz#b246b0b91f31e13650d99c59716fd74ab87465f5"
|
||||
integrity sha512-aK2jSNWgY8eiZ+TmrvGhssMCPDTKArc0ip6Ul5OaslpytKks8hyXoRbxGD0N9sKioSUSbvKUf+1AqavbDpJO+w==
|
||||
"@github/copilot-darwin-x64@0.0.420":
|
||||
version "0.0.420"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.420.tgz#1d5cf40ac4e04bbd69fb0a79abf3743897c5f795"
|
||||
integrity sha512-2acA93IqXz1uuz3TVUm0Y7BVrBr0MySh1kQa8LqMILhTsG0YHRMm8ybzTp2HA7Mi1tl5CjqMSk163kkS7OzfUA==
|
||||
|
||||
"@github/copilot-linux-arm64@0.0.403":
|
||||
version "0.0.403"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.403.tgz#5453ec3bd565cc92676b450b2edb66e49f60909d"
|
||||
integrity sha512-KhoR2iR70O6vCkzf0h8/K+p82qAgOvMTgAPm9bVEHvbdGFR7Py9qL5v03bMbPxsA45oNaZAkzDhfTAqWhIAZsQ==
|
||||
"@github/copilot-linux-arm64@0.0.420":
|
||||
version "0.0.420"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.420.tgz#e247517854927a14f5c076bfa99309160afec2d7"
|
||||
integrity sha512-h/IvEryTOYm1HzR2GNq8s2aDtN4lvT4MxldfZuS42CtWJDOfVG2jLLsoHWU1T3QV8j1++PmDgE//HX0JLpLMww==
|
||||
|
||||
"@github/copilot-linux-x64@0.0.403":
|
||||
version "0.0.403"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.403.tgz#17a7eba380be8553610ee6632d6a81ba229722eb"
|
||||
integrity sha512-eoswUc9vo4TB+/9PgFJLVtzI4dPjkpJXdCsAioVuoqPdNxHxlIHFe9HaVcqMRZxUNY1YHEBZozy+IpUEGjgdfQ==
|
||||
"@github/copilot-linux-x64@0.0.420":
|
||||
version "0.0.420"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.420.tgz#00d22974499f0fab6354fe4e22f6be59b800ab98"
|
||||
integrity sha512-iL2NpZvXIDZ+3lw7sO2fo5T0nKmP5dZbU2gdYcv+SFBm/ONhCxIY5VRX4yN/9VkFaa9ePv5JzCnsl3vZINiDxg==
|
||||
|
||||
"@github/copilot-sdk@^0.1.4":
|
||||
version "0.1.23"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-sdk/-/copilot-sdk-0.1.23.tgz#120986bf5719880dedf076c0f2a55f855566ff40"
|
||||
integrity sha512-0by81bsBQlDKE5VbcegZfUMvPyPm1aXwSGS2rGaMAFxv3ps+dACf1Voruxik7hQTae0ziVFJjuVrlxZoRaXBLw==
|
||||
"@github/copilot-sdk@^0.1.29":
|
||||
version "0.1.29"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-sdk/-/copilot-sdk-0.1.29.tgz#8809df61ab53f100f8390234d9946cdc2acae24b"
|
||||
integrity sha512-GdcN6bJTeesr1HP6IrhN2MznIf1B3ufqd3PX+uKbDLXNriOmP65Ai29/hxzTidNLHyOf6rW4NwmFfkMXiKfCBw==
|
||||
dependencies:
|
||||
"@github/copilot" "^0.0.403"
|
||||
"@github/copilot" "^0.0.420"
|
||||
vscode-jsonrpc "^8.2.1"
|
||||
zod "^4.3.6"
|
||||
|
||||
"@github/copilot-win32-arm64@0.0.403":
|
||||
version "0.0.403"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.403.tgz#3cdaee25b2454ceb6d8293f06b258c95307b94ae"
|
||||
integrity sha512-djWjzCsp2xPNafMyOZ/ivU328/WvWhdroGie/DugiJBTgQL2SP0quWW1fhTlDwE81a3g9CxfJonaRgOpFTJTcg==
|
||||
"@github/copilot-win32-arm64@0.0.420":
|
||||
version "0.0.420"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.420.tgz#733c45aced1e42c2877ae44012074abbcce3d55d"
|
||||
integrity sha512-Njlc2j9vYSBAL+lC6FIEhQ3C+VxO3xavwKnw0ecVRiNLcGLyPrTdzPfPQOmEjC63gpVCqLabikoDGv8fuLPA2w==
|
||||
|
||||
"@github/copilot-win32-x64@0.0.403":
|
||||
version "0.0.403"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.403.tgz#d22bdd50d9b0674d73981a413df881fd8229f5ce"
|
||||
integrity sha512-lju8cHy2E6Ux7R7tWyLZeksYC2MVZu9i9ocjiBX/qfG2/pNJs7S5OlkwKJ0BSXSbZEHQYq7iHfEWp201bVfk9A==
|
||||
"@github/copilot-win32-x64@0.0.420":
|
||||
version "0.0.420"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.420.tgz#d45f47f2f08d4bba87760b8afb21af19d1988780"
|
||||
integrity sha512-rZlH35oNehAP2DvQbu4vQFVNeCh/1p3rUjafBYaEY0Nkhx7RmdrYBileL5U3PtRPPRsBPaq3Qp+pVIrGoCDLzQ==
|
||||
|
||||
"@github/copilot@^0.0.403":
|
||||
version "0.0.403"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot/-/copilot-0.0.403.tgz#56e44b5a0640685f0b34507bbb12f47229798940"
|
||||
integrity sha512-v5jUdtGJReLmE1rmff/LZf+50nzmYQYAaSRNtVNr9g0j0GkCd/noQExe31i1+PudvWU0ZJjltR0B8pUfDRdA9Q==
|
||||
"@github/copilot@^0.0.420":
|
||||
version "0.0.420"
|
||||
resolved "https://registry.yarnpkg.com/@github/copilot/-/copilot-0.0.420.tgz#596349de076566a310836a7e06e6807b87ea6bfe"
|
||||
integrity sha512-UpPuSjxUxQ+j02WjZEFffWf0scLb23LvuGHzMFtaSsweR+P/BdbtDUI5ZDIA6T0tVyyt6+X1/vgfsJiRqd6jig==
|
||||
optionalDependencies:
|
||||
"@github/copilot-darwin-arm64" "0.0.403"
|
||||
"@github/copilot-darwin-x64" "0.0.403"
|
||||
"@github/copilot-linux-arm64" "0.0.403"
|
||||
"@github/copilot-linux-x64" "0.0.403"
|
||||
"@github/copilot-win32-arm64" "0.0.403"
|
||||
"@github/copilot-win32-x64" "0.0.403"
|
||||
"@github/copilot-darwin-arm64" "0.0.420"
|
||||
"@github/copilot-darwin-x64" "0.0.420"
|
||||
"@github/copilot-linux-arm64" "0.0.420"
|
||||
"@github/copilot-linux-x64" "0.0.420"
|
||||
"@github/copilot-win32-arm64" "0.0.420"
|
||||
"@github/copilot-win32-x64" "0.0.420"
|
||||
|
||||
"@playwright/test@^1.49.0":
|
||||
version "1.58.2"
|
||||
|
||||
15
.github/prompts/1-design.prompt.md
vendored
15
.github/prompts/1-design.prompt.md
vendored
@@ -10,7 +10,8 @@
|
||||
## Goal and Constraints
|
||||
|
||||
- Your goal is to finish a design document in `Copilot_Task.md` to address a problem.
|
||||
- You are only allowed to update `Copilot_Task.md` and mark a task being taken in `Copilot_Scrum.md`.
|
||||
- You are only allowed to update `Copilot_Task.md`
|
||||
- When the task comes from `Copilot_Scrum.md`, you should mark a task being taken in `Copilot_Scrum.md`.
|
||||
- You are not allowed to modify any other files.
|
||||
- The phrasing of the request may look like asking for code change, but your actual work is to write the design document.
|
||||
|
||||
@@ -37,12 +38,14 @@ Ignore this section if there is no "# Problem" in the LATEST chat message
|
||||
I am starting a fresh new request.
|
||||
|
||||
- Find and execute `copilotPrepare.ps1` to clean up everything from the last run.
|
||||
- This script will clean up everything in `Copilot_Task.md`, `Copilot_Planning.md` and `Copilot_Execution.md`.
|
||||
- It is normal to find large amount of changes in these 3 files, DO NOT panic.
|
||||
- After `copilotPrepare.ps1` finishes, copy precisely my problem description in `# Problem` from the LATEST chat message under a `# PROBLEM DESCRIPTION`.
|
||||
- If the problem description is `Next`:
|
||||
- Find the first incomplete task in `Copilot_Scrum.md`.
|
||||
- If the problem description is like `Complete task No.X`:
|
||||
- Locate the specific task in `Copilot_Scrum.md`.
|
||||
- There is a bullet list of all tasks at the beginning of `# TASKS`. Mark the specific task as being taken by changing `[ ]` to `[x]`.
|
||||
- Find task from `Copilot_Scrum.md` if the problem description is in the following format:
|
||||
- `Next`: Find the first incomplete task in `Copilot_Scrum.md`.
|
||||
- `Complete task No.X`: Locate the specific task in `Copilot_Scrum.md`.
|
||||
- There is a bullet list of all tasks at the beginning of `# TASKS`. Mark the specific task as being taken by changing `[ ]` to `[x]`.
|
||||
- Otherwise, the task is the problem description itself.
|
||||
- Find the details of the specific task, copy everything in this task to `# PROBLEM DESCRIPTION`.
|
||||
- Add an empty `# UPDATES` section after `# PROBLEM DESCRIPTION`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user