openclaw-telegram-manager 2.8.0 → 2.9.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/README.md +8 -13
- package/dist/commands/daily-report.d.ts +1 -1
- package/dist/commands/daily-report.d.ts.map +1 -1
- package/dist/commands/daily-report.js +7 -5
- package/dist/commands/daily-report.js.map +1 -1
- package/dist/commands/doctor-all.d.ts.map +1 -1
- package/dist/commands/doctor-all.js +21 -3
- package/dist/commands/doctor-all.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +1 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/status.d.ts +0 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +3 -5
- package/dist/commands/status.js.map +1 -1
- package/dist/lib/capsule.d.ts +4 -2
- package/dist/lib/capsule.d.ts.map +1 -1
- package/dist/lib/capsule.js +80 -37
- package/dist/lib/capsule.js.map +1 -1
- package/dist/lib/doctor-checks.d.ts +7 -11
- package/dist/lib/doctor-checks.d.ts.map +1 -1
- package/dist/lib/doctor-checks.js +34 -109
- package/dist/lib/doctor-checks.js.map +1 -1
- package/dist/lib/include-generator.d.ts.map +1 -1
- package/dist/lib/include-generator.js +6 -7
- package/dist/lib/include-generator.js.map +1 -1
- package/dist/lib/types.d.ts +2 -2
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +4 -9
- package/dist/lib/types.js.map +1 -1
- package/dist/plugin.js +122 -260
- package/package.json +1 -1
- package/skills/tm/SKILL.md +15 -9
package/dist/plugin.js
CHANGED
|
@@ -8816,7 +8816,7 @@ __export(value_exports2, {
|
|
|
8816
8816
|
|
|
8817
8817
|
// src/lib/types.ts
|
|
8818
8818
|
var CURRENT_REGISTRY_VERSION = 6;
|
|
8819
|
-
var CAPSULE_VERSION =
|
|
8819
|
+
var CAPSULE_VERSION = 4;
|
|
8820
8820
|
var MAX_POST_ERROR_LENGTH = 500;
|
|
8821
8821
|
var MAX_NAME_LENGTH = 100;
|
|
8822
8822
|
var DOCTOR_ALL_COOLDOWN_MS = 60 * 60 * 1e3;
|
|
@@ -8867,20 +8867,9 @@ var RegistrySchema = Type.Object({
|
|
|
8867
8867
|
maxTopics: Type.Integer({ minimum: 1 }),
|
|
8868
8868
|
topics: Type.Record(Type.String(), TopicEntrySchema)
|
|
8869
8869
|
});
|
|
8870
|
-
var OVERLAY_FILES = {
|
|
8871
|
-
coding: ["ARCHITECTURE.md", "DEPLOY.md"],
|
|
8872
|
-
research: ["SOURCES.md", "FINDINGS.md"],
|
|
8873
|
-
marketing: ["CAMPAIGNS.md", "METRICS.md"],
|
|
8874
|
-
general: []
|
|
8875
|
-
};
|
|
8876
8870
|
var BASE_FILES = [
|
|
8877
8871
|
"README.md",
|
|
8878
8872
|
"STATUS.md",
|
|
8879
|
-
"TODO.md",
|
|
8880
|
-
"COMMANDS.md",
|
|
8881
|
-
"LINKS.md",
|
|
8882
|
-
"CRON.md",
|
|
8883
|
-
"NOTES.md",
|
|
8884
8873
|
"LEARNINGS.md"
|
|
8885
8874
|
];
|
|
8886
8875
|
function topicKey(groupId, threadId) {
|
|
@@ -9147,151 +9136,87 @@ function buildAuditEntry(userId, cmd, slug, detail) {
|
|
|
9147
9136
|
// src/lib/capsule.ts
|
|
9148
9137
|
import * as fs4 from "node:fs";
|
|
9149
9138
|
import * as path4 from "node:path";
|
|
9150
|
-
var
|
|
9151
|
-
"README.md": (name) => `# ${name}
|
|
9139
|
+
var README_UNIVERSAL = (name) => `# ${name}
|
|
9152
9140
|
|
|
9153
|
-
|
|
9154
|
-
`,
|
|
9155
|
-
"STATUS.md": (name) => `# Status: ${name}
|
|
9156
|
-
|
|
9157
|
-
## Last done (UTC)
|
|
9158
|
-
|
|
9159
|
-
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
9141
|
+
## What is this about?
|
|
9160
9142
|
|
|
9161
|
-
|
|
9143
|
+
_Describe what this topic is about._
|
|
9162
9144
|
|
|
9163
|
-
##
|
|
9145
|
+
## Goal
|
|
9164
9146
|
|
|
9165
|
-
|
|
9147
|
+
_What does success look like?_
|
|
9166
9148
|
|
|
9167
|
-
##
|
|
9149
|
+
## Key resources
|
|
9168
9150
|
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9151
|
+
_URLs, repos, paths, dashboards \u2014 anything the AI needs after a reset._
|
|
9152
|
+
`;
|
|
9153
|
+
var README_TYPE_SECTIONS = {
|
|
9154
|
+
coding: () => `
|
|
9155
|
+
## Architecture
|
|
9172
9156
|
|
|
9173
|
-
|
|
9157
|
+
_Components, data flow, key decisions._
|
|
9174
9158
|
|
|
9175
|
-
|
|
9176
|
-
- [T-2] _Waiting for next task_
|
|
9177
|
-
- [T-3] _Waiting for next task_
|
|
9159
|
+
## Deployment
|
|
9178
9160
|
|
|
9179
|
-
|
|
9161
|
+
_Environments, deploy steps, rollback._
|
|
9180
9162
|
|
|
9181
|
-
|
|
9182
|
-
`,
|
|
9183
|
-
"COMMANDS.md": (name) => `# Commands: ${name}
|
|
9163
|
+
## Commands
|
|
9184
9164
|
|
|
9185
|
-
_Build,
|
|
9165
|
+
_Build, test, deploy \u2014 kept here so they survive a reset._
|
|
9186
9166
|
`,
|
|
9187
|
-
|
|
9167
|
+
research: () => `
|
|
9168
|
+
## Sources
|
|
9188
9169
|
|
|
9189
|
-
|
|
9170
|
+
_Papers, articles, datasets, APIs._
|
|
9190
9171
|
|
|
9191
|
-
|
|
9192
|
-
`,
|
|
9193
|
-
"CRON.md": (name) => `# Cron: ${name}
|
|
9172
|
+
## Findings
|
|
9194
9173
|
|
|
9195
|
-
|
|
9174
|
+
_Key findings, evidence, recommendations._
|
|
9196
9175
|
`,
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
_Anything worth remembering about this topic._
|
|
9200
|
-
`,
|
|
9201
|
-
"LEARNINGS.md": (name) => `# Learnings: ${name}
|
|
9202
|
-
|
|
9203
|
-
_Hard-won insights, mistakes, and workarounds._
|
|
9204
|
-
_Agent prepends here automatically. Most recent entries first._
|
|
9205
|
-
`
|
|
9206
|
-
};
|
|
9207
|
-
var OVERLAY_TEMPLATES = {
|
|
9208
|
-
"ARCHITECTURE.md": (name) => `# Architecture: ${name}
|
|
9176
|
+
marketing: () => `
|
|
9177
|
+
## Campaigns
|
|
9209
9178
|
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
_None yet._
|
|
9179
|
+
_Active campaigns, audiences, channels._
|
|
9213
9180
|
|
|
9214
|
-
##
|
|
9181
|
+
## Metrics
|
|
9215
9182
|
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
## Key decisions
|
|
9219
|
-
|
|
9220
|
-
_None yet._
|
|
9183
|
+
_KPIs, targets, tracking dashboards._
|
|
9221
9184
|
`,
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
## Deploy steps
|
|
9233
|
-
|
|
9234
|
-
_None yet._
|
|
9235
|
-
|
|
9236
|
-
## Rollback
|
|
9237
|
-
|
|
9238
|
-
_None yet._
|
|
9239
|
-
`,
|
|
9240
|
-
"SOURCES.md": (name) => `# Sources: ${name}
|
|
9241
|
-
|
|
9242
|
-
## Papers & articles
|
|
9243
|
-
|
|
9244
|
-
_None yet._
|
|
9245
|
-
|
|
9246
|
-
## Datasets & APIs
|
|
9247
|
-
|
|
9248
|
-
_None yet._
|
|
9249
|
-
|
|
9250
|
-
## Other references
|
|
9251
|
-
|
|
9252
|
-
_None yet._
|
|
9253
|
-
`,
|
|
9254
|
-
"FINDINGS.md": (name) => `# Findings: ${name}
|
|
9255
|
-
|
|
9256
|
-
## Key findings
|
|
9257
|
-
|
|
9258
|
-
_None yet._
|
|
9185
|
+
general: () => ""
|
|
9186
|
+
};
|
|
9187
|
+
var BASE_TEMPLATES = {
|
|
9188
|
+
"README.md": (name, type) => {
|
|
9189
|
+
const universal = README_UNIVERSAL(name);
|
|
9190
|
+
const extra = type ? README_TYPE_SECTIONS[type](name) : "";
|
|
9191
|
+
return universal + extra;
|
|
9192
|
+
},
|
|
9193
|
+
"STATUS.md": (name) => `# Status: ${name}
|
|
9259
9194
|
|
|
9260
|
-
##
|
|
9195
|
+
## Last done (UTC)
|
|
9261
9196
|
|
|
9262
|
-
|
|
9197
|
+
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
9198
|
+
Topic created. Waiting for first instructions.
|
|
9263
9199
|
|
|
9264
|
-
##
|
|
9200
|
+
## Next actions (now)
|
|
9265
9201
|
|
|
9266
9202
|
_None yet._
|
|
9267
|
-
`,
|
|
9268
|
-
"CAMPAIGNS.md": (name) => `# Campaigns: ${name}
|
|
9269
9203
|
|
|
9270
|
-
##
|
|
9204
|
+
## Upcoming actions
|
|
9271
9205
|
|
|
9272
9206
|
_None yet._
|
|
9273
9207
|
|
|
9274
|
-
##
|
|
9208
|
+
## Backlog
|
|
9275
9209
|
|
|
9276
|
-
|
|
9210
|
+
- [T-1] _e.g. Set up project scaffolding_
|
|
9277
9211
|
|
|
9278
|
-
##
|
|
9212
|
+
## Completed
|
|
9279
9213
|
|
|
9280
9214
|
_None yet._
|
|
9281
9215
|
`,
|
|
9282
|
-
"
|
|
9283
|
-
|
|
9284
|
-
## KPIs & targets
|
|
9285
|
-
|
|
9286
|
-
_None yet._
|
|
9287
|
-
|
|
9288
|
-
## Tracking & dashboards
|
|
9289
|
-
|
|
9290
|
-
_None yet._
|
|
9291
|
-
|
|
9292
|
-
## Performance data
|
|
9216
|
+
"LEARNINGS.md": (name) => `# Learnings: ${name}
|
|
9293
9217
|
|
|
9294
|
-
|
|
9218
|
+
_Hard-won insights, mistakes, and workarounds._
|
|
9219
|
+
_Agent prepends here automatically. Most recent entries first._
|
|
9295
9220
|
`
|
|
9296
9221
|
};
|
|
9297
9222
|
var CAPSULE_FILE_MODE = 416;
|
|
@@ -9308,15 +9233,7 @@ function scaffoldCapsule(projectsBase, slug, name, type) {
|
|
|
9308
9233
|
const templateFn = BASE_TEMPLATES[file];
|
|
9309
9234
|
if (templateFn) {
|
|
9310
9235
|
const filePath = path4.join(capsuleDir, file);
|
|
9311
|
-
fs4.writeFileSync(filePath, templateFn(name), { mode: CAPSULE_FILE_MODE });
|
|
9312
|
-
}
|
|
9313
|
-
}
|
|
9314
|
-
const overlays = OVERLAY_FILES[type];
|
|
9315
|
-
for (const file of overlays) {
|
|
9316
|
-
const templateFn = OVERLAY_TEMPLATES[file];
|
|
9317
|
-
if (templateFn) {
|
|
9318
|
-
const filePath = path4.join(capsuleDir, file);
|
|
9319
|
-
fs4.writeFileSync(filePath, templateFn(name), { mode: CAPSULE_FILE_MODE });
|
|
9236
|
+
fs4.writeFileSync(filePath, templateFn(name, type), { mode: CAPSULE_FILE_MODE });
|
|
9320
9237
|
}
|
|
9321
9238
|
}
|
|
9322
9239
|
}
|
|
@@ -9337,19 +9254,28 @@ function upgradeCapsule(projectsBase, slug, name, type, currentVersion) {
|
|
|
9337
9254
|
if (!fs4.existsSync(filePath)) {
|
|
9338
9255
|
const templateFn = BASE_TEMPLATES[file];
|
|
9339
9256
|
if (templateFn) {
|
|
9340
|
-
fs4.writeFileSync(filePath, templateFn(name), { mode: CAPSULE_FILE_MODE });
|
|
9257
|
+
fs4.writeFileSync(filePath, templateFn(name, type), { mode: CAPSULE_FILE_MODE });
|
|
9341
9258
|
addedFiles.push(file);
|
|
9342
9259
|
}
|
|
9343
9260
|
}
|
|
9344
9261
|
}
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
fs4.writeFileSync(
|
|
9352
|
-
|
|
9262
|
+
if (currentVersion < 4) {
|
|
9263
|
+
const statusPath = path4.join(capsuleDir, "STATUS.md");
|
|
9264
|
+
if (fs4.existsSync(statusPath)) {
|
|
9265
|
+
const content = fs4.readFileSync(statusPath, "utf-8");
|
|
9266
|
+
if (!content.includes("## Backlog")) {
|
|
9267
|
+
const appendix = "\n## Backlog\n\n- [T-1] _e.g. Set up project scaffolding_\n\n## Completed\n\n_None yet._\n";
|
|
9268
|
+
fs4.writeFileSync(statusPath, content.trimEnd() + "\n" + appendix, { mode: CAPSULE_FILE_MODE });
|
|
9269
|
+
}
|
|
9270
|
+
}
|
|
9271
|
+
const readmePath = path4.join(capsuleDir, "README.md");
|
|
9272
|
+
if (fs4.existsSync(readmePath)) {
|
|
9273
|
+
const content = fs4.readFileSync(readmePath, "utf-8");
|
|
9274
|
+
if (content.includes("_Describe what this topic is about._") && !content.includes("## Goal")) {
|
|
9275
|
+
const templateFn = BASE_TEMPLATES["README.md"];
|
|
9276
|
+
if (templateFn) {
|
|
9277
|
+
fs4.writeFileSync(readmePath, templateFn(name, type), { mode: CAPSULE_FILE_MODE });
|
|
9278
|
+
}
|
|
9353
9279
|
}
|
|
9354
9280
|
}
|
|
9355
9281
|
}
|
|
@@ -9710,8 +9636,8 @@ Context hierarchy (strictly ordered):
|
|
|
9710
9636
|
|
|
9711
9637
|
Determinism rules:
|
|
9712
9638
|
- After /reset, /new, or context compaction: ALWAYS re-read your topic files
|
|
9713
|
-
first \u2014 STATUS.md, then
|
|
9714
|
-
|
|
9639
|
+
first \u2014 STATUS.md, then LEARNINGS.md (last 20 entries), then README.md for
|
|
9640
|
+
topic context \u2014 before doing anything else. These are your ground truth.
|
|
9715
9641
|
Do not rely on summarized memory or workspace-level files for this topic's
|
|
9716
9642
|
tasks, status, or goals.
|
|
9717
9643
|
- Before context compaction or when the conversation is long: proactively
|
|
@@ -9719,13 +9645,12 @@ Determinism rules:
|
|
|
9719
9645
|
"Next actions (now)") so compaction cannot erase critical state.
|
|
9720
9646
|
Use the standard file write tool directly \u2014 do not route through /tm.
|
|
9721
9647
|
- Keep STATUS.md accurate: always maintain "Last done (UTC)", "Next actions (now)",
|
|
9722
|
-
|
|
9723
|
-
-
|
|
9724
|
-
|
|
9725
|
-
- If automation/cron is involved, record job IDs + schedules in CRON.md.
|
|
9726
|
-
- Task IDs (e.g., [T-1]) must stay consistent between STATUS.md and TODO.md.
|
|
9648
|
+
"Upcoming actions", "Backlog", and "Completed".
|
|
9649
|
+
- Task IDs (e.g., [T-1]) must stay consistent across STATUS.md sections.
|
|
9650
|
+
Move completed tasks from Backlog to Completed.
|
|
9727
9651
|
- STATUS.md has two priority sections: "Next actions (now)" for immediate work
|
|
9728
9652
|
and "Upcoming actions" for the near-future pipeline.
|
|
9653
|
+
- Key resources, commands, and context belong in README.md.
|
|
9729
9654
|
|
|
9730
9655
|
Learning capture:
|
|
9731
9656
|
- When you discover something unexpected, a mistake, a workaround, or a
|
|
@@ -10434,19 +10359,6 @@ function runCapsuleChecks(entry, projectsBase) {
|
|
|
10434
10359
|
check(Severity.ERROR, "statusMissing", "Status file is missing", true, "Run /tm upgrade to recreate it")
|
|
10435
10360
|
);
|
|
10436
10361
|
}
|
|
10437
|
-
if (!fs8.existsSync(path8.join(capsuleDir, "TODO.md"))) {
|
|
10438
|
-
results.push(
|
|
10439
|
-
check(Severity.WARN, "todoMissing", "TODO file is missing", true, "Run /tm upgrade to recreate it")
|
|
10440
|
-
);
|
|
10441
|
-
}
|
|
10442
|
-
const overlays = OVERLAY_FILES[entry.type] ?? [];
|
|
10443
|
-
for (const file of overlays) {
|
|
10444
|
-
if (!fs8.existsSync(path8.join(capsuleDir, file))) {
|
|
10445
|
-
results.push(
|
|
10446
|
-
check(Severity.INFO, `overlayMissing:${file}`, `Optional overlay ${file} missing for type "${entry.type}"`, true)
|
|
10447
|
-
);
|
|
10448
|
-
}
|
|
10449
|
-
}
|
|
10450
10362
|
if (entry.capsuleVersion < CAPSULE_VERSION) {
|
|
10451
10363
|
results.push(
|
|
10452
10364
|
check(
|
|
@@ -10523,7 +10435,8 @@ function runStatusQualityChecks(statusContent, entry) {
|
|
|
10523
10435
|
}
|
|
10524
10436
|
return results;
|
|
10525
10437
|
}
|
|
10526
|
-
|
|
10438
|
+
var BACKLOG_RE = /^##\s*Backlog/im;
|
|
10439
|
+
function runNextVsBacklogChecks(statusContent) {
|
|
10527
10440
|
const results = [];
|
|
10528
10441
|
const nextActionsIndex = statusContent.search(NEXT_ACTIONS_RE);
|
|
10529
10442
|
if (nextActionsIndex < 0) return results;
|
|
@@ -10532,14 +10445,19 @@ function runNextVsTodoChecks(statusContent, todoContent) {
|
|
|
10532
10445
|
const nextActionsSection = nextSectionIndex > 0 ? sectionAfter.slice(0, nextSectionIndex) : sectionAfter;
|
|
10533
10446
|
const nextTaskIds = nextActionsSection.match(TASK_ID_RE) ?? [];
|
|
10534
10447
|
if (nextTaskIds.length === 0) return results;
|
|
10535
|
-
const
|
|
10536
|
-
|
|
10448
|
+
const backlogIndex = statusContent.search(BACKLOG_RE);
|
|
10449
|
+
if (backlogIndex < 0) return results;
|
|
10450
|
+
const backlogAfter = statusContent.slice(backlogIndex);
|
|
10451
|
+
const backlogNextSection = backlogAfter.indexOf("\n## ", 1);
|
|
10452
|
+
const backlogSection = backlogNextSection > 0 ? backlogAfter.slice(0, backlogNextSection) : backlogAfter;
|
|
10453
|
+
const backlogTaskIds = new Set(backlogSection.match(TASK_ID_RE) ?? []);
|
|
10454
|
+
const missing = nextTaskIds.filter((id) => !backlogTaskIds.has(id));
|
|
10537
10455
|
if (missing.length >= 2) {
|
|
10538
10456
|
results.push(
|
|
10539
10457
|
check(
|
|
10540
10458
|
Severity.WARN,
|
|
10541
|
-
"
|
|
10542
|
-
`${missing.length} tasks referenced in next actions don't exist in the
|
|
10459
|
+
"nextNotInBacklog",
|
|
10460
|
+
`${missing.length} tasks referenced in next actions don't exist in the backlog: ${missing.join(", ")}`,
|
|
10543
10461
|
false,
|
|
10544
10462
|
"The AI will clean these up on next interaction"
|
|
10545
10463
|
)
|
|
@@ -10547,62 +10465,18 @@ function runNextVsTodoChecks(statusContent, todoContent) {
|
|
|
10547
10465
|
}
|
|
10548
10466
|
return results;
|
|
10549
10467
|
}
|
|
10550
|
-
function
|
|
10551
|
-
const results = [];
|
|
10552
|
-
if (entry.type === "coding") {
|
|
10553
|
-
const commandsContent = capsuleFiles.get("COMMANDS.md");
|
|
10554
|
-
if (commandsContent !== void 0 && isEffectivelyEmpty(commandsContent)) {
|
|
10555
|
-
results.push(
|
|
10556
|
-
check(Severity.INFO, "commandsEmpty", "COMMANDS.md is empty for a coding topic", false, "Add build/test/deploy commands to COMMANDS.md")
|
|
10557
|
-
);
|
|
10558
|
-
}
|
|
10559
|
-
}
|
|
10560
|
-
if (entry.type === "coding" || entry.type === "research") {
|
|
10561
|
-
const linksContent = capsuleFiles.get("LINKS.md");
|
|
10562
|
-
if (linksContent !== void 0 && isEffectivelyEmpty(linksContent)) {
|
|
10563
|
-
results.push(
|
|
10564
|
-
check(Severity.INFO, "linksEmpty", "LINKS.md is empty for a coding/research topic", false, "Add URLs and endpoints to LINKS.md")
|
|
10565
|
-
);
|
|
10566
|
-
}
|
|
10567
|
-
}
|
|
10568
|
-
return results;
|
|
10569
|
-
}
|
|
10570
|
-
function isEffectivelyEmpty(content) {
|
|
10571
|
-
const stripped = content.replace(/^#.*$/gm, "").replace(/^_.*_$/gm, "").replace(/\s+/g, "").trim();
|
|
10572
|
-
return stripped.length === 0;
|
|
10573
|
-
}
|
|
10574
|
-
var JOB_ID_RE = /[a-zA-Z0-9_-]{8,}/;
|
|
10575
|
-
function runCronChecks(cronContent, cronJobsPath) {
|
|
10468
|
+
function runUnfilledContextCheck(capsuleFiles) {
|
|
10576
10469
|
const results = [];
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
const hasJobIds = lines.some((line) => JOB_ID_RE.test(line));
|
|
10580
|
-
if (!hasJobIds) {
|
|
10470
|
+
const readmeContent = capsuleFiles.get("README.md");
|
|
10471
|
+
if (readmeContent && readmeContent.includes("_Describe what this topic is about._")) {
|
|
10581
10472
|
results.push(
|
|
10582
|
-
check(
|
|
10473
|
+
check(
|
|
10474
|
+
Severity.INFO,
|
|
10475
|
+
"contextUnfilled",
|
|
10476
|
+
"Topic context is empty \u2014 tell the AI about your project to get better help.",
|
|
10477
|
+
false
|
|
10478
|
+
)
|
|
10583
10479
|
);
|
|
10584
|
-
return results;
|
|
10585
|
-
}
|
|
10586
|
-
if (cronJobsPath && fs8.existsSync(cronJobsPath)) {
|
|
10587
|
-
try {
|
|
10588
|
-
const jobsRaw = fs8.readFileSync(cronJobsPath, "utf-8");
|
|
10589
|
-
const jobs = JSON.parse(jobsRaw);
|
|
10590
|
-
const knownJobIds = new Set(Object.keys(jobs));
|
|
10591
|
-
for (const line of lines) {
|
|
10592
|
-
const match = line.match(JOB_ID_RE);
|
|
10593
|
-
if (match && !knownJobIds.has(match[0])) {
|
|
10594
|
-
results.push(
|
|
10595
|
-
check(
|
|
10596
|
-
Severity.WARN,
|
|
10597
|
-
"cronJobNotFound",
|
|
10598
|
-
`Scheduled job "${match[0]}" not found in the jobs registry`,
|
|
10599
|
-
false
|
|
10600
|
-
)
|
|
10601
|
-
);
|
|
10602
|
-
}
|
|
10603
|
-
}
|
|
10604
|
-
} catch {
|
|
10605
|
-
}
|
|
10606
10480
|
}
|
|
10607
10481
|
return results;
|
|
10608
10482
|
}
|
|
@@ -10700,7 +10574,7 @@ function runPostErrorCheck(entry) {
|
|
|
10700
10574
|
}
|
|
10701
10575
|
return results;
|
|
10702
10576
|
}
|
|
10703
|
-
function runAllChecksForTopic(entry, projectsBase, includeContent, registry
|
|
10577
|
+
function runAllChecksForTopic(entry, projectsBase, includeContent, registry) {
|
|
10704
10578
|
const results = [];
|
|
10705
10579
|
const capsuleDir = path8.join(projectsBase, entry.slug);
|
|
10706
10580
|
results.push(...runRegistryChecks(entry, projectsBase));
|
|
@@ -10711,16 +10585,9 @@ function runAllChecksForTopic(entry, projectsBase, includeContent, registry, cro
|
|
|
10711
10585
|
const statusContent = capsuleFiles.get("STATUS.md");
|
|
10712
10586
|
if (statusContent) {
|
|
10713
10587
|
results.push(...runStatusQualityChecks(statusContent, entry));
|
|
10714
|
-
|
|
10715
|
-
if (todoContent) {
|
|
10716
|
-
results.push(...runNextVsTodoChecks(statusContent, todoContent));
|
|
10717
|
-
}
|
|
10718
|
-
}
|
|
10719
|
-
results.push(...runCommandsLinksChecks(entry, capsuleFiles));
|
|
10720
|
-
const cronContent = capsuleFiles.get("CRON.md");
|
|
10721
|
-
if (cronContent) {
|
|
10722
|
-
results.push(...runCronChecks(cronContent, cronJobsPath));
|
|
10588
|
+
results.push(...runNextVsBacklogChecks(statusContent));
|
|
10723
10589
|
}
|
|
10590
|
+
results.push(...runUnfilledContextCheck(capsuleFiles));
|
|
10724
10591
|
if (includeContent && registry) {
|
|
10725
10592
|
results.push(...runConfigChecks(entry, includeContent, registry));
|
|
10726
10593
|
}
|
|
@@ -10731,7 +10598,7 @@ function runAllChecksForTopic(entry, projectsBase, includeContent, registry, cro
|
|
|
10731
10598
|
return results;
|
|
10732
10599
|
}
|
|
10733
10600
|
var BACKUP_DIR = ".tm-backup";
|
|
10734
|
-
var BACKUP_FILES = ["STATUS.md"
|
|
10601
|
+
var BACKUP_FILES = ["STATUS.md"];
|
|
10735
10602
|
function backupCapsuleIfHealthy(projectsBase, slug, results) {
|
|
10736
10603
|
const hasIssues = results.some((r) => r.severity === Severity.ERROR || r.severity === Severity.WARN);
|
|
10737
10604
|
if (hasIssues) return;
|
|
@@ -10750,22 +10617,7 @@ function backupCapsuleIfHealthy(projectsBase, slug, results) {
|
|
|
10750
10617
|
}
|
|
10751
10618
|
function readCapsuleFiles(capsuleDir) {
|
|
10752
10619
|
const files = /* @__PURE__ */ new Map();
|
|
10753
|
-
const filenames = [
|
|
10754
|
-
"STATUS.md",
|
|
10755
|
-
"TODO.md",
|
|
10756
|
-
"COMMANDS.md",
|
|
10757
|
-
"LINKS.md",
|
|
10758
|
-
"CRON.md",
|
|
10759
|
-
"NOTES.md",
|
|
10760
|
-
"README.md",
|
|
10761
|
-
"LEARNINGS.md",
|
|
10762
|
-
"ARCHITECTURE.md",
|
|
10763
|
-
"DEPLOY.md",
|
|
10764
|
-
"SOURCES.md",
|
|
10765
|
-
"FINDINGS.md",
|
|
10766
|
-
"CAMPAIGNS.md",
|
|
10767
|
-
"METRICS.md"
|
|
10768
|
-
];
|
|
10620
|
+
const filenames = ["README.md", "STATUS.md", "LEARNINGS.md"];
|
|
10769
10621
|
for (const name of filenames) {
|
|
10770
10622
|
const filePath = path8.join(capsuleDir, name);
|
|
10771
10623
|
try {
|
|
@@ -10810,13 +10662,11 @@ async function handleDoctor(ctx) {
|
|
|
10810
10662
|
}
|
|
10811
10663
|
} catch {
|
|
10812
10664
|
}
|
|
10813
|
-
const cronJobsPath = path9.join(configDir, "cron", "jobs.json");
|
|
10814
10665
|
const results = runAllChecksForTopic(
|
|
10815
10666
|
entry,
|
|
10816
10667
|
projectsBase,
|
|
10817
10668
|
includeContent,
|
|
10818
|
-
registry
|
|
10819
|
-
cronJobsPath
|
|
10669
|
+
registry
|
|
10820
10670
|
);
|
|
10821
10671
|
backupCapsuleIfHealthy(projectsBase, entry.slug, results);
|
|
10822
10672
|
const reportText = buildDoctorReport(entry.name, results);
|
|
@@ -10869,9 +10719,8 @@ async function handleDailyReport(ctx) {
|
|
|
10869
10719
|
return { text: "Topic files not found. Run /tm init to set up this topic." };
|
|
10870
10720
|
}
|
|
10871
10721
|
const statusContent = readFileOrNull(path10.join(capsuleDir, "STATUS.md"));
|
|
10872
|
-
const todoContent = readFileOrNull(path10.join(capsuleDir, "TODO.md"));
|
|
10873
10722
|
const doneContent = extractDoneSection(statusContent);
|
|
10874
|
-
const blockers = extractBlockers(
|
|
10723
|
+
const blockers = extractBlockers(statusContent);
|
|
10875
10724
|
const nextContent = extractNextActions(statusContent);
|
|
10876
10725
|
const reportData = {
|
|
10877
10726
|
name: entry.name,
|
|
@@ -10925,9 +10774,11 @@ function extractDoneSection(statusContent) {
|
|
|
10925
10774
|
const text = match[1]?.trim();
|
|
10926
10775
|
return text || "Empty.";
|
|
10927
10776
|
}
|
|
10928
|
-
function extractBlockers(
|
|
10929
|
-
if (!
|
|
10930
|
-
const
|
|
10777
|
+
function extractBlockers(statusContent) {
|
|
10778
|
+
if (!statusContent) return "No tasks recorded yet.";
|
|
10779
|
+
const backlogMatch = statusContent.match(/^##\s*Backlog\b.*\n((?:(?!\n## )[\s\S])*)/im);
|
|
10780
|
+
const backlogSection = backlogMatch ? backlogMatch[1] ?? "" : statusContent;
|
|
10781
|
+
const lines = backlogSection.split("\n");
|
|
10931
10782
|
const blockerLines = lines.filter(
|
|
10932
10783
|
(l) => /\[BLOCKED\]/i.test(l) || /\bblocked\b/i.test(l)
|
|
10933
10784
|
);
|
|
@@ -11087,13 +10938,13 @@ async function handleDoctorAll(ctx) {
|
|
|
11087
10938
|
}
|
|
11088
10939
|
} catch {
|
|
11089
10940
|
}
|
|
11090
|
-
const cronJobsPath = path11.join(configDir, "cron", "jobs.json");
|
|
11091
10941
|
const allEntries = Object.entries(registry.topics);
|
|
11092
10942
|
const reports = [];
|
|
11093
10943
|
const errors = [];
|
|
11094
10944
|
const checkedTopics = [];
|
|
11095
10945
|
const skippedTopics = [];
|
|
11096
10946
|
const groupPostResults = /* @__PURE__ */ new Map();
|
|
10947
|
+
const upgradedSlugs = /* @__PURE__ */ new Set();
|
|
11097
10948
|
for (const [_key, entry] of allEntries) {
|
|
11098
10949
|
const capsuleDir = path11.join(projectsBase, entry.slug);
|
|
11099
10950
|
const statusForEligibility = readFileOrNull(path11.join(capsuleDir, "STATUS.md"));
|
|
@@ -11104,12 +10955,22 @@ async function handleDoctorAll(ctx) {
|
|
|
11104
10955
|
continue;
|
|
11105
10956
|
}
|
|
11106
10957
|
try {
|
|
10958
|
+
if (entry.capsuleVersion < CAPSULE_VERSION) {
|
|
10959
|
+
const oldVersion = entry.capsuleVersion;
|
|
10960
|
+
try {
|
|
10961
|
+
upgradeCapsule(projectsBase, entry.slug, entry.name, entry.type, entry.capsuleVersion);
|
|
10962
|
+
upgradedSlugs.add(entry.slug);
|
|
10963
|
+
logger.info(`[doctor-all] Auto-upgraded ${entry.slug} v${oldVersion} \u2192 v${CAPSULE_VERSION}`);
|
|
10964
|
+
} catch (err) {
|
|
10965
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10966
|
+
logger.error(`[doctor-all] Auto-upgrade failed for ${entry.slug}: ${msg}`);
|
|
10967
|
+
}
|
|
10968
|
+
}
|
|
11107
10969
|
const results = runAllChecksForTopic(
|
|
11108
10970
|
entry,
|
|
11109
10971
|
projectsBase,
|
|
11110
10972
|
includeContent,
|
|
11111
|
-
registry
|
|
11112
|
-
cronJobsPath
|
|
10973
|
+
registry
|
|
11113
10974
|
);
|
|
11114
10975
|
const isSpam = entry.consecutiveSilentDoctors >= SPAM_THRESHOLD;
|
|
11115
10976
|
if (isSpam) {
|
|
@@ -11211,6 +11072,9 @@ async function handleDoctorAll(ctx) {
|
|
|
11211
11072
|
entry.consecutiveSilentDoctors++;
|
|
11212
11073
|
}
|
|
11213
11074
|
entry.lastDoctorRunAt = now.toISOString();
|
|
11075
|
+
if (upgradedSlugs.has(entry.slug)) {
|
|
11076
|
+
entry.capsuleVersion = CAPSULE_VERSION;
|
|
11077
|
+
}
|
|
11214
11078
|
if (entry.consecutiveSilentDoctors >= SPAM_THRESHOLD) {
|
|
11215
11079
|
entry.snoozeUntil = new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString();
|
|
11216
11080
|
entry.status = "snoozed";
|
|
@@ -11310,10 +11174,10 @@ function hasBlockers(blockers) {
|
|
|
11310
11174
|
return blockers !== "None." && blockers !== "No tasks recorded yet.";
|
|
11311
11175
|
}
|
|
11312
11176
|
function formatStatus(data) {
|
|
11313
|
-
const { name, type, statusContent,
|
|
11177
|
+
const { name, type, statusContent, expanded } = data;
|
|
11314
11178
|
const timestamp = extractTimestamp(statusContent);
|
|
11315
11179
|
const nextRaw = extractSection(statusContent, NEXT_ACTIONS_RE2);
|
|
11316
|
-
const blockers = extractBlockers(
|
|
11180
|
+
const blockers = extractBlockers(statusContent);
|
|
11317
11181
|
const doneMatch = statusContent.match(LAST_DONE_RE2);
|
|
11318
11182
|
let lastDoneBody = "";
|
|
11319
11183
|
if (doneMatch) {
|
|
@@ -11378,13 +11242,11 @@ async function handleStatus2(ctx, args) {
|
|
|
11378
11242
|
const expanded = args.trim() === "--expanded";
|
|
11379
11243
|
try {
|
|
11380
11244
|
const statusContent = fs12.readFileSync(statusPath, "utf-8");
|
|
11381
|
-
const todoContent = readFileOrNull(path12.join(capsuleDir, "TODO.md"));
|
|
11382
11245
|
return {
|
|
11383
11246
|
text: truncateMessage(formatStatus({
|
|
11384
11247
|
name: entry.name,
|
|
11385
11248
|
type: entry.type,
|
|
11386
11249
|
statusContent,
|
|
11387
|
-
todoContent,
|
|
11388
11250
|
expanded
|
|
11389
11251
|
}))
|
|
11390
11252
|
};
|
package/package.json
CHANGED
package/skills/tm/SKILL.md
CHANGED
|
@@ -15,27 +15,33 @@ If you detect any of these conditions, invoke `topic_manager` proactively:
|
|
|
15
15
|
|
|
16
16
|
1. **After /reset, /new, or context compaction**: call `topic_manager` with
|
|
17
17
|
command "status" to re-read the topic files and rehydrate context.
|
|
18
|
-
Rehydration order: STATUS.md,
|
|
19
|
-
|
|
18
|
+
Rehydration order: STATUS.md, then LEARNINGS.md (last 20 entries),
|
|
19
|
+
then README.md for topic context.
|
|
20
20
|
2. **Before context gets large**: proactively flush current progress to
|
|
21
21
|
STATUS.md using the standard file write tool (update "Last done (UTC)"
|
|
22
22
|
and "Next actions (now)"). Do NOT route this through /tm — write directly.
|
|
23
23
|
3. **When you notice a topic has no persistent memory**: suggest `/tm init`.
|
|
24
24
|
4. **When you discover something unexpected** (a mistake, workaround, or
|
|
25
25
|
constraint): prepend a dated entry to LEARNINGS.md in the topic folder.
|
|
26
|
-
5. **When
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
5. **When you see a fresh topic** (STATUS.md says "Waiting for first instructions"
|
|
27
|
+
or README.md has unfilled context sections like `_Describe what this topic is about._`),
|
|
28
|
+
proactively ask the user 2-3 questions about what this topic is about, what the
|
|
29
|
+
goal is, and where the key resources are. Persist answers to README.md immediately.
|
|
30
|
+
6. **When the user shares important project information** that would be needed
|
|
31
|
+
after a session reset, persist it immediately to the appropriate section
|
|
32
|
+
in README.md. Don't wait for the user to ask — this is the first context
|
|
33
|
+
lost on reset.
|
|
29
34
|
|
|
30
35
|
Examples by topic type:
|
|
31
36
|
- **Coding**: repository paths, runtime/data paths, branch names, service URLs,
|
|
32
|
-
environment details →
|
|
37
|
+
environment details, build/test/deploy commands → README.md sections
|
|
38
|
+
(Architecture, Deployment, Commands, Key resources)
|
|
33
39
|
- **Research**: key sources, data locations, API endpoints, methodology decisions
|
|
34
|
-
→
|
|
40
|
+
→ README.md sections (Sources, Findings, Key resources)
|
|
35
41
|
- **Marketing**: campaign URLs, analytics dashboards, social accounts, brand
|
|
36
|
-
guidelines location →
|
|
42
|
+
guidelines location → README.md sections (Campaigns, Metrics, Key resources)
|
|
37
43
|
- **General / any type**: reference URLs, contacts, key decisions, file paths
|
|
38
|
-
→
|
|
44
|
+
→ README.md sections (Key resources)
|
|
39
45
|
|
|
40
46
|
Rule of thumb: if losing this information would cause the agent to make a
|
|
41
47
|
wrong assumption after reset, it must be written down now.
|