micro-models-agent 0.61.0 → 0.61.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/CHANGELOG.md +601 -595
- package/README.md +358 -358
- package/dist/certification/certifications.json +493 -493
- package/dist/cli/commands.js +447 -0
- package/dist/cli/completer.js +167 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/main.js +153 -0
- package/dist/cli/plugin-commands.js +36 -0
- package/dist/cli/repl-commands.js +761 -0
- package/dist/cli/repl.js +702 -0
- package/dist/cli/run-result.js +33 -0
- package/dist/cli/security-commands.js +164 -0
- package/dist/cli/setup.js +237 -0
- package/dist/config/config.js +276 -0
- package/dist/config/defaults.js +141 -0
- package/dist/config/domains.js +179 -0
- package/dist/config/experts.js +15 -0
- package/dist/config/index.js +4 -0
- package/dist/config/security.js +213 -0
- package/dist/config/types.js +1 -0
- package/dist/core/agent-moe.js +102 -0
- package/dist/core/agent.js +1018 -0
- package/dist/core/bootstrap.js +481 -0
- package/dist/core/crash-handler.js +51 -0
- package/dist/core/environment.js +199 -0
- package/dist/core/index.js +2 -0
- package/dist/core/prompt-builder.js +76 -0
- package/dist/core/session-logger.js +251 -0
- package/dist/core/types.js +1 -0
- package/dist/core/version.js +26 -0
- package/dist/core/workspace.js +76 -0
- package/dist/i18n/en.json +757 -757
- package/dist/i18n/index.js +46 -0
- package/dist/i18n/ru.json +757 -757
- package/dist/index.js +22 -0
- package/dist/llm/image-utils.js +143 -0
- package/dist/llm/index.js +4 -0
- package/dist/llm/model-loader.js +78 -0
- package/dist/llm/openai-compat.js +497 -0
- package/dist/llm/orchestrator.js +200 -0
- package/dist/llm/provider.js +10 -0
- package/dist/llm/response.js +39 -0
- package/dist/llm/token-counter.js +39 -0
- package/dist/llm/types.js +1 -0
- package/dist/logger/app-logger.js +189 -0
- package/dist/logger/file-log.js +151 -0
- package/dist/logger/index.js +1 -0
- package/dist/main.js +364 -325
- package/dist/migration/backup.js +45 -0
- package/dist/migration/detect.js +50 -0
- package/dist/migration/index.js +2 -0
- package/dist/modules/artifacts/store.js +61 -0
- package/dist/modules/browser/actions.js +76 -0
- package/dist/modules/browser/bridge-client.js +199 -0
- package/dist/modules/browser/bridge-path.js +10 -0
- package/dist/modules/browser/bridge-server.mjs +219 -219
- package/dist/modules/browser/cookie-store.js +24 -0
- package/dist/modules/browser/driver.js +136 -0
- package/dist/modules/browser/index.js +7 -0
- package/dist/modules/browser/module.js +29 -0
- package/dist/modules/browser/session.js +342 -0
- package/dist/modules/browser/snapshot.js +148 -0
- package/dist/modules/browser/types.js +12 -0
- package/dist/modules/certification/cli.js +213 -0
- package/dist/modules/certification/fact-checker.js +82 -0
- package/dist/modules/certification/loader.js +106 -0
- package/dist/modules/certification/manifest.js +58 -0
- package/dist/modules/certification/runner.js +245 -0
- package/dist/modules/certification/scenarios.js +407 -0
- package/dist/modules/certification/types.js +1 -0
- package/dist/modules/context/chunk-query.js +100 -0
- package/dist/modules/context/fact-extractor.js +168 -0
- package/dist/modules/context/history.js +15 -0
- package/dist/modules/context/index.js +1 -0
- package/dist/modules/context/manager.js +440 -0
- package/dist/modules/execution/audit-runners.js +206 -0
- package/dist/modules/execution/auditor.js +218 -0
- package/dist/modules/execution/execution-plugin.js +431 -0
- package/dist/modules/execution/index.js +8 -0
- package/dist/modules/execution/module.js +625 -0
- package/dist/modules/execution/moe-executor.js +304 -0
- package/dist/modules/execution/plan-coverage.js +68 -0
- package/dist/modules/execution/plan-persister.js +46 -0
- package/dist/modules/execution/plan-store.js +196 -0
- package/dist/modules/execution/plan-tool.js +677 -0
- package/dist/modules/execution/plan-validator.js +153 -0
- package/dist/modules/execution/planner.js +94 -0
- package/dist/modules/execution/stuck-detector.js +746 -0
- package/dist/modules/execution/tracker.js +69 -0
- package/dist/modules/execution/types.js +1 -0
- package/dist/modules/execution/verifier.js +235 -0
- package/dist/modules/execution/windows-commands.js +41 -0
- package/dist/modules/hallucination/confidence.js +66 -0
- package/dist/modules/hallucination/consistency.js +26 -0
- package/dist/modules/hallucination/detector.js +47 -0
- package/dist/modules/hallucination/factual.js +169 -0
- package/dist/modules/hallucination/index.js +5 -0
- package/dist/modules/hallucination/js-identifiers.js +262 -0
- package/dist/modules/hallucination/llm-judge.js +101 -0
- package/dist/modules/index.js +5 -0
- package/dist/modules/indexer/cache.js +40 -0
- package/dist/modules/indexer/index.js +3 -0
- package/dist/modules/indexer/module.js +246 -0
- package/dist/modules/indexer/project-profile.js +183 -0
- package/dist/modules/indexer/walker.js +101 -0
- package/dist/modules/lsp/check-tool.js +58 -0
- package/dist/modules/lsp/client.js +389 -0
- package/dist/modules/lsp/command.js +60 -0
- package/dist/modules/lsp/config.js +135 -0
- package/dist/modules/lsp/index.js +3 -0
- package/dist/modules/lsp/module.js +260 -0
- package/dist/modules/lsp/probe.js +86 -0
- package/dist/modules/lsp/project-root.js +32 -0
- package/dist/modules/lsp/startup-check.js +144 -0
- package/dist/modules/lsp/types.js +1 -0
- package/dist/modules/mcp/client.js +399 -0
- package/dist/modules/mcp/index.js +3 -0
- package/dist/modules/mcp/module.js +142 -0
- package/dist/modules/mcp/registry.js +15 -0
- package/dist/modules/memory/index.js +1 -0
- package/dist/modules/memory/module.js +96 -0
- package/dist/modules/memory/search.js +42 -0
- package/dist/modules/memory/store.js +69 -0
- package/dist/modules/pipelines/engine.js +60 -0
- package/dist/modules/pipelines/index.js +3 -0
- package/dist/modules/pipelines/parser.js +56 -0
- package/dist/modules/pipelines/template.js +14 -0
- package/dist/modules/plugins/builtin/lint-on-write.js +334 -0
- package/dist/modules/plugins/builtin/notify.js +9 -0
- package/dist/modules/plugins/index.js +1 -0
- package/dist/modules/plugins/loader.js +70 -0
- package/dist/modules/plugins/manager.js +261 -0
- package/dist/modules/plugins/types.js +1 -0
- package/dist/modules/pricing/index.js +61 -0
- package/dist/modules/pricing/prices.js +129 -0
- package/dist/modules/processes/detect.js +34 -0
- package/dist/modules/processes/index.js +2 -0
- package/dist/modules/processes/registry.js +327 -0
- package/dist/modules/processes/runner.js +23 -0
- package/dist/modules/providers/create.js +22 -0
- package/dist/modules/providers/fallback.js +79 -0
- package/dist/modules/providers/health.js +46 -0
- package/dist/modules/providers/index.js +5 -0
- package/dist/modules/providers/manager.js +161 -0
- package/dist/modules/providers/presets.js +128 -0
- package/dist/modules/providers/registry.js +22 -0
- package/dist/modules/providers/types.js +1 -0
- package/dist/modules/registry.js +48 -0
- package/dist/modules/security/audit-log.js +136 -0
- package/dist/modules/security/audit-notifier.js +292 -0
- package/dist/modules/security/command-validator.js +219 -0
- package/dist/modules/security/content-scanner.js +53 -0
- package/dist/modules/security/data-sanitizer.js +89 -0
- package/dist/modules/security/encryption.js +242 -0
- package/dist/modules/security/index.js +14 -0
- package/dist/modules/security/network-validator.js +88 -0
- package/dist/modules/security/path-validator.js +203 -0
- package/dist/modules/security/rate-limiter.js +119 -0
- package/dist/modules/security/security-policies.js +531 -0
- package/dist/modules/security/session-encryption.js +210 -0
- package/dist/modules/security/session-isolation.js +95 -0
- package/dist/modules/session/index.js +3 -0
- package/dist/modules/session/manager.js +172 -0
- package/dist/modules/session/module.js +24 -0
- package/dist/modules/session/store.js +222 -0
- package/dist/modules/session/types.js +1 -0
- package/dist/modules/skills/index.js +2 -0
- package/dist/modules/skills/loader.js +72 -0
- package/dist/modules/skills/matcher.js +27 -0
- package/dist/modules/skills/module.js +129 -0
- package/dist/modules/types.js +1 -0
- package/dist/modules/updater/checker.js +96 -0
- package/dist/modules/updater/index.js +2 -0
- package/dist/modules/updater/module.js +116 -0
- package/dist/modules/user-profile/compressor.js +16 -0
- package/dist/modules/user-profile/index.js +1 -0
- package/dist/modules/user-profile/profile.js +68 -0
- package/dist/skills/builtin/git.md +36 -36
- package/dist/skills/builtin/typescript.md +35 -35
- package/dist/tools/approve.js +33 -0
- package/dist/tools/attach-image.js +101 -0
- package/dist/tools/bash.js +519 -0
- package/dist/tools/browser.js +115 -0
- package/dist/tools/chunk-query.js +100 -0
- package/dist/tools/create-dir.js +56 -0
- package/dist/tools/delete-file.js +63 -0
- package/dist/tools/download-file.js +117 -0
- package/dist/tools/edit-file.js +80 -0
- package/dist/tools/enable-tools.js +59 -0
- package/dist/tools/executor.js +154 -0
- package/dist/tools/file-info.js +47 -0
- package/dist/tools/filter-tools.js +17 -0
- package/dist/tools/glob-tool.js +27 -0
- package/dist/tools/grep-tool.js +125 -0
- package/dist/tools/hidden-tools-block.js +37 -0
- package/dist/tools/index.js +78 -0
- package/dist/tools/list-dir.js +49 -0
- package/dist/tools/load-skill.js +43 -0
- package/dist/tools/mcp-call.js +69 -0
- package/dist/tools/move-file.js +86 -0
- package/dist/tools/path-utils.js +101 -0
- package/dist/tools/pipeline-run.js +145 -0
- package/dist/tools/preview.js +2 -0
- package/dist/tools/process-kill.js +40 -0
- package/dist/tools/process-list.js +37 -0
- package/dist/tools/process-log.js +54 -0
- package/dist/tools/question.js +141 -0
- package/dist/tools/read-file.js +179 -0
- package/dist/tools/recall.js +118 -0
- package/dist/tools/registry.js +47 -0
- package/dist/tools/remember.js +68 -0
- package/dist/tools/scope-check.js +32 -0
- package/dist/tools/search-history.js +85 -0
- package/dist/tools/subagent.js +196 -0
- package/dist/tools/types.js +1 -0
- package/dist/tools/user-input.js +123 -0
- package/dist/tools/web-browse.js +87 -0
- package/dist/tools/web-fetch.js +119 -0
- package/dist/tools/web-search.js +105 -0
- package/dist/tools/write-file.js +82 -0
- package/dist/ui/box.js +77 -0
- package/dist/ui/colors.js +4 -0
- package/dist/ui/diff.js +178 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/line-editor.js +822 -0
- package/dist/ui/line-math.js +73 -0
- package/dist/ui/md-formatter.js +212 -0
- package/dist/ui/output.js +13 -0
- package/dist/ui/plan-view.js +103 -0
- package/dist/ui/renderer.js +259 -0
- package/dist/ui/spinner.js +70 -0
- package/dist/ui/table.js +144 -0
- package/package.json +51 -51
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
export function validatePlan(plan, config) {
|
|
2
|
+
const errors = [];
|
|
3
|
+
const warnings = [];
|
|
4
|
+
const autoFixes = [];
|
|
5
|
+
if (!plan.subtasks || plan.subtasks.length === 0) {
|
|
6
|
+
return { valid: false, errors: ["Plan has no subtasks"], warnings: [], autoFixes: [] };
|
|
7
|
+
}
|
|
8
|
+
errors.push(...expertTagsExist(plan.subtasks, config));
|
|
9
|
+
errors.push(...hasCycle(plan.subtasks));
|
|
10
|
+
const overlapErrors = hasAllowedFilesOverlap(plan.subtasks);
|
|
11
|
+
errors.push(...overlapErrors);
|
|
12
|
+
const readOverlaps = deleteReadOverlap(plan.subtasks);
|
|
13
|
+
for (const overlap of readOverlaps) {
|
|
14
|
+
if (overlap.canAutoFix) {
|
|
15
|
+
autoFixes.push({ subtaskId: overlap.subtaskId, fix: `Add depends_on: ${overlap.dependsOn}` });
|
|
16
|
+
}
|
|
17
|
+
else if (overlap.error) {
|
|
18
|
+
errors.push(overlap.error);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
valid: errors.length === 0,
|
|
23
|
+
errors,
|
|
24
|
+
warnings,
|
|
25
|
+
autoFixes,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function expertTagsExist(subtasks, config) {
|
|
29
|
+
const errors = [];
|
|
30
|
+
const knownExperts = new Set(Object.keys(config.experts || {}));
|
|
31
|
+
for (const subtask of subtasks) {
|
|
32
|
+
if (!knownExperts.has(subtask.expert_tag)) {
|
|
33
|
+
errors.push(`Subtask "${subtask.id}" references unknown expert_tag "${subtask.expert_tag}". Known experts: ${Array.from(knownExperts).join(", ")}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return errors;
|
|
37
|
+
}
|
|
38
|
+
export function hasCycle(subtasks) {
|
|
39
|
+
const edges = new Map();
|
|
40
|
+
for (const s of subtasks) {
|
|
41
|
+
edges.set(s.id, s.depends_on || []);
|
|
42
|
+
}
|
|
43
|
+
const visited = new Set();
|
|
44
|
+
const inStack = new Set();
|
|
45
|
+
function dfs(node) {
|
|
46
|
+
if (inStack.has(node))
|
|
47
|
+
return true;
|
|
48
|
+
if (visited.has(node))
|
|
49
|
+
return false;
|
|
50
|
+
visited.add(node);
|
|
51
|
+
inStack.add(node);
|
|
52
|
+
const deps = edges.get(node) || [];
|
|
53
|
+
for (const dep of deps) {
|
|
54
|
+
if (edges.has(dep) && dfs(dep))
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
inStack.delete(node);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const errors = [];
|
|
61
|
+
for (const s of subtasks) {
|
|
62
|
+
visited.clear();
|
|
63
|
+
inStack.clear();
|
|
64
|
+
if (dfs(s.id)) {
|
|
65
|
+
errors.push(`Cycle detected involving subtask "${s.id}"`);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return errors;
|
|
70
|
+
}
|
|
71
|
+
export function hasAllowedFilesOverlap(subtasks) {
|
|
72
|
+
const errors = [];
|
|
73
|
+
const writeFiles = new Map();
|
|
74
|
+
for (const s of subtasks) {
|
|
75
|
+
for (const f of s.allowed_files || []) {
|
|
76
|
+
if (!writeFiles.has(f)) {
|
|
77
|
+
writeFiles.set(f, []);
|
|
78
|
+
}
|
|
79
|
+
writeFiles.get(f).push(s.id);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const [file, writers] of writeFiles) {
|
|
83
|
+
if (writers.length > 1) {
|
|
84
|
+
const noDep = writers.filter((w) => {
|
|
85
|
+
const sub = subtasks.find((s) => s.id === w);
|
|
86
|
+
return sub && !sub.depends_on?.some((d) => writers.includes(d));
|
|
87
|
+
});
|
|
88
|
+
if (noDep.length > 1) {
|
|
89
|
+
errors.push(`Write-write overlap on "${file}" between subtasks: ${writers.join(", ")} without dependency chain`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return errors;
|
|
94
|
+
}
|
|
95
|
+
export function deleteReadOverlap(subtasks) {
|
|
96
|
+
const results = [];
|
|
97
|
+
for (let i = 0; i < subtasks.length; i++) {
|
|
98
|
+
for (let j = 0; j < subtasks.length; j++) {
|
|
99
|
+
if (i === j)
|
|
100
|
+
continue;
|
|
101
|
+
const writer = subtasks[i];
|
|
102
|
+
const reader = subtasks[j];
|
|
103
|
+
if (!writer.allowed_files || !reader.read_only_files)
|
|
104
|
+
continue;
|
|
105
|
+
const readFiles = new Set(reader.read_only_files);
|
|
106
|
+
const writeFiles = writer.allowed_files;
|
|
107
|
+
const overlapping = writeFiles.filter((f) => {
|
|
108
|
+
for (const rf of readFiles) {
|
|
109
|
+
if (f.startsWith(rf) || rf.startsWith(f))
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
});
|
|
114
|
+
if (overlapping.length > 0 && !reader.depends_on?.includes(writer.id)) {
|
|
115
|
+
const canAutoFix = !reader.depends_on || reader.depends_on.length === 0;
|
|
116
|
+
if (canAutoFix) {
|
|
117
|
+
results.push({
|
|
118
|
+
subtaskId: reader.id,
|
|
119
|
+
dependsOn: writer.id,
|
|
120
|
+
canAutoFix: true,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
results.push({
|
|
125
|
+
subtaskId: reader.id,
|
|
126
|
+
dependsOn: writer.id,
|
|
127
|
+
canAutoFix: false,
|
|
128
|
+
error: `Read-after-write overlap between "${writer.id}" (writer) and "${reader.id}" (reader) on "${overlapping.join(", ")}" — add depends_on: [${writer.id}]`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
export function applyAutoFixes(plan, autoFixes) {
|
|
137
|
+
const fixed = { ...plan, subtasks: plan.subtasks.map((s) => ({ ...s })) };
|
|
138
|
+
for (const fix of autoFixes) {
|
|
139
|
+
const sub = fixed.subtasks.find((s) => s.id === fix.subtaskId);
|
|
140
|
+
if (!sub)
|
|
141
|
+
continue;
|
|
142
|
+
const dependsOnMatch = fix.fix.match(/depends_on:\s*(\S+)/);
|
|
143
|
+
if (dependsOnMatch) {
|
|
144
|
+
const dep = dependsOnMatch[1];
|
|
145
|
+
if (!sub.depends_on)
|
|
146
|
+
sub.depends_on = [];
|
|
147
|
+
if (!sub.depends_on.includes(dep)) {
|
|
148
|
+
sub.depends_on.push(dep);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return fixed;
|
|
153
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
function generatePlanId() {
|
|
2
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
3
|
+
let id = "";
|
|
4
|
+
for (let i = 0; i < 6; i++) {
|
|
5
|
+
id += chars[Math.floor(Math.random() * chars.length)];
|
|
6
|
+
}
|
|
7
|
+
return `plan_${id}`;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Single source of truth for plan progress counting (ses_mt4fn58c: `show`
|
|
11
|
+
* said "6/7 done, complete" while `list` said "7/7" for the same plan —
|
|
12
|
+
* one counted only "done", the other counted done+skipped). A skipped step
|
|
13
|
+
* needs no further work, so it counts as settled everywhere.
|
|
14
|
+
*/
|
|
15
|
+
export function getPlanProgress(plan) {
|
|
16
|
+
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
17
|
+
const skipped = plan.steps.filter((s) => s.status === "skipped").length;
|
|
18
|
+
const total = plan.steps.length;
|
|
19
|
+
const settled = done + skipped;
|
|
20
|
+
return { done, skipped, total, settled, terminal: total > 0 && settled === total };
|
|
21
|
+
}
|
|
22
|
+
export class PlanCreator {
|
|
23
|
+
static createPlan(title, stepDescriptions, baseDir, kinds) {
|
|
24
|
+
const stepCount = stepDescriptions.length;
|
|
25
|
+
return {
|
|
26
|
+
id: generatePlanId(),
|
|
27
|
+
title: `[${stepCount}] ${title}`,
|
|
28
|
+
steps: stepDescriptions.map((desc, i) => ({
|
|
29
|
+
id: i + 1,
|
|
30
|
+
description: desc,
|
|
31
|
+
status: "pending",
|
|
32
|
+
kind: kinds?.[i] ?? "create",
|
|
33
|
+
})),
|
|
34
|
+
createdAt: new Date().toISOString(),
|
|
35
|
+
baseDir,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
static replan(plan, newSteps, title) {
|
|
39
|
+
const kept = plan.steps.filter((s) => s.status === "done" || s.status === "skipped");
|
|
40
|
+
const baseTitle = plan.title.replace(/^\[\d+[^]]*\]\s*/, "");
|
|
41
|
+
const newTitle = title || baseTitle;
|
|
42
|
+
const totalSteps = kept.length + newSteps.length;
|
|
43
|
+
const added = newSteps.map((desc) => ({
|
|
44
|
+
id: -1, // renumbered below
|
|
45
|
+
description: desc,
|
|
46
|
+
status: "pending",
|
|
47
|
+
kind: "create",
|
|
48
|
+
}));
|
|
49
|
+
// Renumber every step (kept + added) sequentially. Kept steps keep their
|
|
50
|
+
// status and kind but may collide with the added ids when the original
|
|
51
|
+
// plan skipped/pending steps in the middle (e.g. done 1, done 3 → kept
|
|
52
|
+
// [1,3], added [2…] collides with kept id 3).
|
|
53
|
+
const steps = [...kept, ...added].map((s, i) => ({ ...s, id: i + 1 }));
|
|
54
|
+
return {
|
|
55
|
+
id: plan.id,
|
|
56
|
+
title: `[${totalSteps}] ${newTitle}`,
|
|
57
|
+
steps,
|
|
58
|
+
createdAt: plan.createdAt,
|
|
59
|
+
baseDir: plan.baseDir,
|
|
60
|
+
name: plan.name,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
static toPromptBlock(plan, currentStepIndex) {
|
|
64
|
+
const date = plan.createdAt.slice(0, 10);
|
|
65
|
+
const p = getPlanProgress(plan);
|
|
66
|
+
const skippedPart = p.skipped > 0 ? `, ${p.skipped} skipped` : "";
|
|
67
|
+
const progress = p.terminal
|
|
68
|
+
? `${p.settled}/${p.total} done${skippedPart}, complete`
|
|
69
|
+
: `${p.settled}/${p.total} done${skippedPart}, current: step ${currentStepIndex + 1}`;
|
|
70
|
+
const lines = [
|
|
71
|
+
`[${plan.id}] ${plan.title}`,
|
|
72
|
+
`Dir: ${plan.baseDir}`,
|
|
73
|
+
`Created: ${date} | Progress: ${progress}`,
|
|
74
|
+
``,
|
|
75
|
+
];
|
|
76
|
+
for (const step of plan.steps) {
|
|
77
|
+
const icon = step.status === "done"
|
|
78
|
+
? "[x]"
|
|
79
|
+
: step.status === "in_progress"
|
|
80
|
+
? "[*]"
|
|
81
|
+
: step.status === "failed"
|
|
82
|
+
? "[!]"
|
|
83
|
+
: step.status === "skipped"
|
|
84
|
+
? "[-]"
|
|
85
|
+
: "[ ]";
|
|
86
|
+
const note = step.note ? ` — ${step.note}` : "";
|
|
87
|
+
lines.push(`${icon} ${step.id}. ${step.description}${note}`);
|
|
88
|
+
for (const sub of step.subtasks ?? []) {
|
|
89
|
+
lines.push(` ${sub.done ? "[x]" : "[ ]"} ${sub.text}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return lines.join("\n");
|
|
93
|
+
}
|
|
94
|
+
}
|