@uoyo/mvtt 2.0.0-beta.1 → 2.0.0-beta.3
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 +184 -193
- package/dist/build/section-loader.d.ts.map +1 -1
- package/dist/build/section-loader.js +18 -8
- package/dist/build/section-loader.js.map +1 -1
- package/dist/fs/materialize.d.ts.map +1 -1
- package/dist/fs/materialize.js +5 -0
- package/dist/fs/materialize.js.map +1 -1
- package/dist/scripts/session-update.cjs +7568 -0
- package/install-manifest.yaml +2 -0
- package/package.json +3 -2
- package/registry.yaml +6 -0
- package/sources/defaults/config.yaml +7 -7
- package/sources/defaults/session.yaml +9 -16
- package/sources/scripts/session-update.js +351 -0
- package/sources/sections/activation-load-context.md +4 -0
- package/sources/sections/footer-next-steps.md +1 -1
- package/sources/sections/output-language-constraint.md +11 -11
- package/sources/sections/session-update.md +115 -47
- package/sources/skills/mvt-analyze/business.md +7 -7
- package/sources/skills/mvt-analyze/manifest.yaml +1 -0
- package/sources/skills/mvt-analyze-code/manifest.yaml +110 -96
- package/sources/skills/mvt-bug-detect/business.md +99 -0
- package/sources/skills/mvt-bug-detect/manifest.yaml +84 -0
- package/sources/skills/mvt-check-context/business.md +3 -5
- package/sources/skills/mvt-check-context/manifest.yaml +70 -63
- package/sources/skills/mvt-cleanup/business.md +49 -23
- package/sources/skills/mvt-cleanup/manifest.yaml +15 -10
- package/sources/skills/mvt-config/business.md +1 -2
- package/sources/skills/mvt-config/manifest.yaml +103 -96
- package/sources/skills/mvt-create-skill/business.md +84 -76
- package/sources/skills/mvt-create-skill/manifest.yaml +107 -95
- package/sources/skills/mvt-design/business.md +3 -6
- package/sources/skills/mvt-design/manifest.yaml +109 -96
- package/sources/skills/mvt-fix/business.md +39 -9
- package/sources/skills/mvt-fix/manifest.yaml +88 -72
- package/sources/skills/mvt-help/business.md +2 -4
- package/sources/skills/mvt-help/manifest.yaml +75 -67
- package/sources/skills/mvt-implement/business.md +4 -5
- package/sources/skills/mvt-implement/manifest.yaml +93 -80
- package/sources/skills/mvt-init/business.md +2 -2
- package/sources/skills/mvt-init/manifest.yaml +102 -101
- package/sources/skills/mvt-manage-context/business.md +186 -175
- package/sources/skills/mvt-manage-context/manifest.yaml +134 -123
- package/sources/skills/mvt-plan-dev/business.md +101 -20
- package/sources/skills/mvt-plan-dev/manifest.yaml +90 -91
- package/sources/skills/mvt-quick-dev/business.md +2 -1
- package/sources/skills/mvt-quick-dev/manifest.yaml +84 -69
- package/sources/skills/mvt-refactor/business.md +2 -1
- package/sources/skills/mvt-refactor/manifest.yaml +104 -86
- package/sources/skills/mvt-resume/business.md +28 -68
- package/sources/skills/mvt-resume/manifest.yaml +81 -71
- package/sources/skills/mvt-review/business.md +3 -3
- package/sources/skills/mvt-review/manifest.yaml +108 -87
- package/sources/skills/mvt-status/business.md +14 -18
- package/sources/skills/mvt-status/manifest.yaml +74 -66
- package/sources/skills/mvt-sync-context/business.md +155 -150
- package/sources/skills/mvt-sync-context/manifest.yaml +99 -96
- package/sources/skills/mvt-template/business.md +0 -2
- package/sources/skills/mvt-template/manifest.yaml +68 -63
- package/sources/skills/mvt-test/business.md +3 -3
- package/sources/skills/mvt-test/manifest.yaml +115 -102
- package/sources/skills/mvt-update-plan/business.md +83 -72
- package/sources/skills/mvt-update-plan/manifest.yaml +124 -132
- package/sources/templates/analyze-output/body.md +15 -15
- package/sources/templates/design-output/body.md +17 -17
- package/sources/templates/implement-output/body.md +11 -11
- package/sources/templates/review-output/body.md +11 -11
- package/sources/templates/test-output/body.md +7 -7
- package/dist/build/plan-validator.d.ts +0 -26
- package/dist/build/plan-validator.d.ts.map +0 -1
- package/dist/build/plan-validator.js +0 -225
- package/dist/build/plan-validator.js.map +0 -1
- package/dist/commands/build.d.ts +0 -5
- package/dist/commands/build.d.ts.map +0 -1
- package/dist/commands/build.js +0 -46
- package/dist/commands/build.js.map +0 -1
- package/dist/commands/migrate.d.ts +0 -18
- package/dist/commands/migrate.d.ts.map +0 -1
- package/dist/commands/migrate.js +0 -163
- package/dist/commands/migrate.js.map +0 -1
- package/dist/fs/protection.d.ts +0 -15
- package/dist/fs/protection.d.ts.map +0 -1
- package/dist/fs/protection.js +0 -16
- package/dist/fs/protection.js.map +0 -1
package/install-manifest.yaml
CHANGED
|
@@ -11,6 +11,8 @@ generated:
|
|
|
11
11
|
source: "copy:sources/knowledge/core/_framework/"
|
|
12
12
|
- pattern: ".ai-agents/registry.yaml"
|
|
13
13
|
source: "copy:registry.yaml"
|
|
14
|
+
- pattern: ".ai-agents/scripts/session-update.cjs"
|
|
15
|
+
source: "bundle:sources/scripts/session-update.js"
|
|
14
16
|
|
|
15
17
|
create_once:
|
|
16
18
|
- path: ".ai-agents/config.yaml"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uoyo/mvtt",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.3",
|
|
4
4
|
"description": "My Virtual Tech Team - AI-guided prompt orchestration framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
|
-
"build": "tsc -p tsconfig.build.json",
|
|
38
|
+
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && node build-scripts.js",
|
|
39
39
|
"test": "vitest run",
|
|
40
40
|
"test:watch": "vitest",
|
|
41
41
|
"test:coverage": "vitest run --coverage",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"@types/node": "^25.6.0",
|
|
46
46
|
"@types/prompts": "^2.4.9",
|
|
47
47
|
"@vitest/coverage-v8": "^2.1.9",
|
|
48
|
+
"esbuild": "^0.28.0",
|
|
48
49
|
"typescript": "^5.4.0",
|
|
49
50
|
"vitest": "^2.0.0"
|
|
50
51
|
},
|
package/registry.yaml
CHANGED
|
@@ -100,6 +100,12 @@ skills:
|
|
|
100
100
|
category: project
|
|
101
101
|
|
|
102
102
|
# --- Developer Skills ---
|
|
103
|
+
mvt-bug-detect:
|
|
104
|
+
agent: analyst
|
|
105
|
+
description: "Analyze and detect bugs by investigating root cause, assessing severity and impact scope. Produces a structured diagnosis in conversation without applying fixes. This skill should be used when user suspects a bug, wants to understand a problem before fixing, or needs impact analysis."
|
|
106
|
+
path: .claude/skills/mvt-bug-detect/SKILL.md
|
|
107
|
+
template: null
|
|
108
|
+
category: shortcut
|
|
103
109
|
mvt-implement:
|
|
104
110
|
agent: developer
|
|
105
111
|
description: "Implement features based on architecture design. This skill should be used when user wants to implement a feature, write production code, or translate design blueprints into working code."
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
# MVTT Framework - User Configuration
|
|
2
|
-
# Unified config center: all skills read and enforce these settings via Activation Protocol
|
|
3
|
-
# Modify via: /mvt-config or edit directly
|
|
4
2
|
|
|
5
3
|
version: "2.0"
|
|
6
4
|
|
|
7
|
-
# ============================================================
|
|
8
|
-
# User Preferences (enforced by Activation Protocol Step 2)
|
|
9
|
-
# ============================================================
|
|
10
5
|
preferences:
|
|
11
|
-
# Language used for interactive responses
|
|
6
|
+
# Language used for interactive responses
|
|
12
7
|
# Options: en-US, zh-CN
|
|
13
8
|
interaction_language: en-US
|
|
14
9
|
|
|
15
|
-
# Language used for persistent document output
|
|
10
|
+
# Language used for persistent document output
|
|
16
11
|
# If absent, falls back to interaction_language.
|
|
17
12
|
# Options: en-US, zh-CN
|
|
18
13
|
document_output_language: en-US
|
|
@@ -25,3 +20,8 @@ preferences:
|
|
|
25
20
|
# AI routing for /mvt-manage-context add
|
|
26
21
|
context_routing:
|
|
27
22
|
relevance_threshold: 70 # Skills scored >= this are pre-checked; below are folded
|
|
23
|
+
|
|
24
|
+
# Session history limits
|
|
25
|
+
history_limits:
|
|
26
|
+
history: 20 # Max history entries (1-100)
|
|
27
|
+
changes: 20 # Max changes entries (1-100)
|
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
# Workspace Session State
|
|
2
|
-
# Supports flexible workflows -- no longer bound to a fixed pipeline
|
|
3
2
|
|
|
4
3
|
session:
|
|
5
4
|
initialized_at: ""
|
|
6
|
-
|
|
5
|
+
last_synced_at: ""
|
|
7
6
|
|
|
8
|
-
# Current active change (id/title/created_at set by /mvt-analyze;
|
|
9
|
-
# plan_path/has_plan set by /mvt-plan-dev once a plan is generated)
|
|
10
7
|
active_change:
|
|
11
8
|
id: ""
|
|
12
9
|
title: ""
|
|
13
10
|
created_at: ""
|
|
14
11
|
plan_path: ""
|
|
15
|
-
has_plan: false
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
|
|
13
|
+
changes: []
|
|
14
|
+
# - id: "chg-001"
|
|
15
|
+
# title: "User authentication"
|
|
16
|
+
# plan_path: ".ai-agents/workspace/artifacts/chg-001/plan.yaml"
|
|
17
|
+
# status: "active" # active | done | abandoned
|
|
18
|
+
# updated_at: "2026-05-23T14:30:00"
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
skill_history: []
|
|
25
|
-
# - command: "/mvt-analyze"
|
|
20
|
+
history: []
|
|
21
|
+
# - skill: "/mvt-analyze"
|
|
26
22
|
# completed_at: "2026-05-23T14:30:00"
|
|
27
23
|
# summary: "Analyzed user authentication requirements"
|
|
28
24
|
# change_id: "" # set when work belongs to an active change
|
|
29
|
-
|
|
30
|
-
# Recent actions (append-only, max 5)
|
|
31
|
-
recent_actions: []
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* session-update.js — Mechanical session state mutation script
|
|
5
|
+
*
|
|
6
|
+
* Replaces AI-driven YAML mutation with a deterministic Node.js script.
|
|
7
|
+
* All skills call this script instead of manually editing session.yaml.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: This source file uses `import from "yaml"`. During the build
|
|
10
|
+
* pipeline, esbuild bundles it into a zero-dependency single file that
|
|
11
|
+
* gets deployed to .ai-agents/scripts/session-update.cjs.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* node .ai-agents/scripts/session-update.cjs \
|
|
15
|
+
* --skill <name> \
|
|
16
|
+
* --summary <text> \
|
|
17
|
+
* [--change-id <id>] \
|
|
18
|
+
* [--new-change <title>] \
|
|
19
|
+
* [--set-initialized] \
|
|
20
|
+
* [--update-change] \
|
|
21
|
+
* [--set-plan-path <path>] \
|
|
22
|
+
* [--close-change] \
|
|
23
|
+
* [--set-change-status <status>] \
|
|
24
|
+
* [--no-change] \
|
|
25
|
+
* [--set-synced] \
|
|
26
|
+
* [--truncate-history <n>]
|
|
27
|
+
*
|
|
28
|
+
* Output:
|
|
29
|
+
* Success (exit 0): {"ok":true}
|
|
30
|
+
* Failure (exit 1): plain text error message on stderr
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readFileSync, writeFileSync, renameSync, unlinkSync, existsSync } from "node:fs";
|
|
34
|
+
import path from "node:path";
|
|
35
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
36
|
+
|
|
37
|
+
// ── Error Messages ──────────────────────────────────────────────────────────
|
|
38
|
+
// All error messages centralized here for visibility and easy maintenance.
|
|
39
|
+
// Each key maps to a function that returns the stderr message string.
|
|
40
|
+
|
|
41
|
+
const ERRORS = {
|
|
42
|
+
MISSING_SKILL: () => "Missing required argument: --skill",
|
|
43
|
+
MISSING_SUMMARY: () => "Missing required argument: --summary",
|
|
44
|
+
CHANGE_ID_REQUIRED: () => "--new-change requires --change-id",
|
|
45
|
+
NO_PROJECT_ROOT: () => "Could not find project root (.ai-agents/ directory not found). Make sure you are inside an MVTT project.",
|
|
46
|
+
NO_SESSION_YAML: () => "session.yaml not found. Run /mvt-init first to initialize the project.",
|
|
47
|
+
SESSION_PARSE_FAILED: (detail) => `Failed to parse session.yaml: ${detail}. Check the file for syntax errors.`,
|
|
48
|
+
SESSION_WRITE_FAILED: (detail) => `Failed to write session.yaml: ${detail}`,
|
|
49
|
+
CONFIG_LIMIT_INVALID: (key, val, min, max, fallback) =>
|
|
50
|
+
`Warning: config history_limits.${key} value "${val}" is invalid (must be integer ${min}-${max}). Using default ${fallback}.`,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// ── Defaults ────────────────────────────────────────────────────────────────
|
|
54
|
+
const DEFAULT_LIMITS = {
|
|
55
|
+
history: 20,
|
|
56
|
+
changes: 20,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const LIMIT_RANGES = {
|
|
60
|
+
history: { min: 1, max: 100 },
|
|
61
|
+
changes: { min: 1, max: 100 },
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// ── Project Root Resolution ─────────────────────────────────────────────────
|
|
65
|
+
function findProjectRoot(cwd) {
|
|
66
|
+
let dir = cwd;
|
|
67
|
+
while (true) {
|
|
68
|
+
if (existsSync(path.join(dir, ".ai-agents"))) return dir;
|
|
69
|
+
const parent = path.dirname(dir);
|
|
70
|
+
if (parent === dir) return null;
|
|
71
|
+
dir = parent;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── CLI Parsing ─────────────────────────────────────────────────────────────
|
|
76
|
+
function parseArgs(argv) {
|
|
77
|
+
const args = {};
|
|
78
|
+
for (let i = 2; i < argv.length; i++) {
|
|
79
|
+
if (argv[i].startsWith("--")) {
|
|
80
|
+
const key = argv[i].slice(2);
|
|
81
|
+
const next = argv[i + 1];
|
|
82
|
+
if (next && !next.startsWith("--")) {
|
|
83
|
+
args[key] = next;
|
|
84
|
+
i++;
|
|
85
|
+
} else {
|
|
86
|
+
args[key] = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return args;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── Config Loading ──────────────────────────────────────────────────────────
|
|
94
|
+
function loadHistoryLimits(configPath) {
|
|
95
|
+
const limits = { ...DEFAULT_LIMITS };
|
|
96
|
+
if (!existsSync(configPath)) return limits;
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
100
|
+
const config = parseYaml(raw);
|
|
101
|
+
const configured = config?.preferences?.history_limits;
|
|
102
|
+
if (!configured || typeof configured !== "object") return limits;
|
|
103
|
+
|
|
104
|
+
for (const key of Object.keys(DEFAULT_LIMITS)) {
|
|
105
|
+
const val = configured[key];
|
|
106
|
+
if (val == null) continue;
|
|
107
|
+
|
|
108
|
+
const num = Number(val);
|
|
109
|
+
const range = LIMIT_RANGES[key];
|
|
110
|
+
if (!Number.isInteger(num) || num < range.min || num > range.max) {
|
|
111
|
+
console.warn(ERRORS.CONFIG_LIMIT_INVALID(key, val, range.min, range.max, DEFAULT_LIMITS[key]));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
limits[key] = num;
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
// If config can't be parsed, use defaults silently
|
|
118
|
+
}
|
|
119
|
+
return limits;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Validation ──────────────────────────────────────────────────────────────
|
|
123
|
+
function validate(args) {
|
|
124
|
+
if (!args.skill) return ERRORS.MISSING_SKILL();
|
|
125
|
+
if (!args.summary) return ERRORS.MISSING_SUMMARY();
|
|
126
|
+
if (args["new-change"] && !args["change-id"]) return ERRORS.CHANGE_ID_REQUIRED();
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Main ────────────────────────────────────────────────────────────────────
|
|
131
|
+
function main() {
|
|
132
|
+
const args = parseArgs(process.argv);
|
|
133
|
+
|
|
134
|
+
const validationError = validate(args);
|
|
135
|
+
if (validationError) {
|
|
136
|
+
process.stderr.write(validationError + "\n");
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
141
|
+
if (!projectRoot) {
|
|
142
|
+
process.stderr.write(ERRORS.NO_PROJECT_ROOT() + "\n");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const sessionPath = path.join(projectRoot, ".ai-agents/workspace/session.yaml");
|
|
147
|
+
if (!existsSync(sessionPath)) {
|
|
148
|
+
process.stderr.write(ERRORS.NO_SESSION_YAML() + "\n");
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const configPath = path.join(projectRoot, ".ai-agents/config.yaml");
|
|
153
|
+
const limits = loadHistoryLimits(configPath);
|
|
154
|
+
|
|
155
|
+
// Read session
|
|
156
|
+
let session;
|
|
157
|
+
try {
|
|
158
|
+
session = parseYaml(readFileSync(sessionPath, "utf-8"));
|
|
159
|
+
} catch (e) {
|
|
160
|
+
process.stderr.write(ERRORS.SESSION_PARSE_FAILED(e.message) + "\n");
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const now = new Date().toISOString();
|
|
165
|
+
|
|
166
|
+
// ── Mandatory updates ──────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
// history append + truncate
|
|
169
|
+
session.history = session.history || [];
|
|
170
|
+
// Use --no-change to force empty change_id, otherwise fall back to active_change.id
|
|
171
|
+
const activeChangeId = args["no-change"] ? "" : (args["change-id"] || session.active_change?.id || "");
|
|
172
|
+
session.history.push({
|
|
173
|
+
skill: `/${args.skill}`,
|
|
174
|
+
completed_at: now,
|
|
175
|
+
summary: args.summary,
|
|
176
|
+
change_id: activeChangeId,
|
|
177
|
+
});
|
|
178
|
+
if (session.history.length > limits.history) {
|
|
179
|
+
session.history = session.history.slice(-limits.history);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── Conditional updates ────────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
// --new-change: auto-snapshot old active_change, then set new one
|
|
185
|
+
if (args["new-change"]) {
|
|
186
|
+
session.active_change = session.active_change || {};
|
|
187
|
+
|
|
188
|
+
// Auto-snapshot: if there's an existing active_change with an id, upsert into changes[]
|
|
189
|
+
if (session.active_change.id) {
|
|
190
|
+
session.changes = session.changes || [];
|
|
191
|
+
const existingIdx = session.changes.findIndex(
|
|
192
|
+
(e) => e.id === session.active_change.id
|
|
193
|
+
);
|
|
194
|
+
const snapshotEntry = {
|
|
195
|
+
id: session.active_change.id,
|
|
196
|
+
title: session.active_change.title || "",
|
|
197
|
+
plan_path: session.active_change.plan_path || "",
|
|
198
|
+
status: "active",
|
|
199
|
+
updated_at: now,
|
|
200
|
+
};
|
|
201
|
+
if (existingIdx >= 0) {
|
|
202
|
+
session.changes[existingIdx] = snapshotEntry;
|
|
203
|
+
} else {
|
|
204
|
+
session.changes.push(snapshotEntry);
|
|
205
|
+
}
|
|
206
|
+
// Sort + truncate changes
|
|
207
|
+
session.changes.sort((a, b) => a.updated_at.localeCompare(b.updated_at));
|
|
208
|
+
if (session.changes.length > limits.changes) {
|
|
209
|
+
session.changes = session.changes.slice(-limits.changes);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Now set new active_change
|
|
214
|
+
session.active_change.id = args["change-id"];
|
|
215
|
+
session.active_change.title = args["new-change"];
|
|
216
|
+
session.active_change.created_at = now;
|
|
217
|
+
session.active_change.plan_path = "";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// --set-initialized
|
|
221
|
+
if (args["set-initialized"]) {
|
|
222
|
+
session.session = session.session || {};
|
|
223
|
+
if (!session.session.initialized_at) {
|
|
224
|
+
session.session.initialized_at = now;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// --set-synced: set session.last_synced_at to current time
|
|
229
|
+
if (args["set-synced"]) {
|
|
230
|
+
session.session = session.session || {};
|
|
231
|
+
session.session.last_synced_at = now;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// --set-plan-path: set active_change.plan_path
|
|
235
|
+
// NOTE: Must execute BEFORE --update-change so that
|
|
236
|
+
// the upserted changes entry contains the correct plan_path.
|
|
237
|
+
if (args["set-plan-path"]) {
|
|
238
|
+
session.active_change = session.active_change || {};
|
|
239
|
+
session.active_change.plan_path = args["set-plan-path"];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// --update-change: upsert active_change into changes[] + truncate
|
|
243
|
+
if (args["update-change"]) {
|
|
244
|
+
session.changes = session.changes || [];
|
|
245
|
+
const ac = session.active_change || {};
|
|
246
|
+
const existingIdx = session.changes.findIndex(
|
|
247
|
+
(e) => e.id === ac.id
|
|
248
|
+
);
|
|
249
|
+
const entry = {
|
|
250
|
+
id: ac.id || "",
|
|
251
|
+
title: ac.title || "",
|
|
252
|
+
plan_path: ac.plan_path || "",
|
|
253
|
+
status: "active",
|
|
254
|
+
updated_at: now,
|
|
255
|
+
};
|
|
256
|
+
if (existingIdx >= 0) {
|
|
257
|
+
session.changes[existingIdx] = entry;
|
|
258
|
+
} else {
|
|
259
|
+
session.changes.push(entry);
|
|
260
|
+
}
|
|
261
|
+
// Sort by updated_at ascending, then truncate to limit
|
|
262
|
+
session.changes.sort(
|
|
263
|
+
(a, b) => a.updated_at.localeCompare(b.updated_at)
|
|
264
|
+
);
|
|
265
|
+
if (session.changes.length > limits.changes) {
|
|
266
|
+
session.changes = session.changes.slice(-limits.changes);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// --close-change: snapshot active_change to changes[] with status:done, clear active_change
|
|
271
|
+
if (args["close-change"]) {
|
|
272
|
+
session.changes = session.changes || [];
|
|
273
|
+
const ac = session.active_change || {};
|
|
274
|
+
if (ac.id) {
|
|
275
|
+
const existingIdx = session.changes.findIndex(
|
|
276
|
+
(e) => e.id === ac.id
|
|
277
|
+
);
|
|
278
|
+
const entry = {
|
|
279
|
+
id: ac.id,
|
|
280
|
+
title: ac.title || "",
|
|
281
|
+
plan_path: ac.plan_path || "",
|
|
282
|
+
status: "done",
|
|
283
|
+
updated_at: now,
|
|
284
|
+
};
|
|
285
|
+
if (existingIdx >= 0) {
|
|
286
|
+
session.changes[existingIdx] = entry;
|
|
287
|
+
} else {
|
|
288
|
+
session.changes.push(entry);
|
|
289
|
+
}
|
|
290
|
+
session.changes.sort(
|
|
291
|
+
(a, b) => a.updated_at.localeCompare(b.updated_at)
|
|
292
|
+
);
|
|
293
|
+
if (session.changes.length > limits.changes) {
|
|
294
|
+
session.changes = session.changes.slice(-limits.changes);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Clear active_change
|
|
298
|
+
session.active_change = {
|
|
299
|
+
id: "",
|
|
300
|
+
title: "",
|
|
301
|
+
created_at: "",
|
|
302
|
+
plan_path: "",
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// --set-change-status: set status on changes[] entry matching active_change.id
|
|
307
|
+
if (args["set-change-status"]) {
|
|
308
|
+
session.changes = session.changes || [];
|
|
309
|
+
const ac = session.active_change || {};
|
|
310
|
+
if (ac.id) {
|
|
311
|
+
const existingIdx = session.changes.findIndex(
|
|
312
|
+
(e) => e.id === ac.id
|
|
313
|
+
);
|
|
314
|
+
if (existingIdx >= 0) {
|
|
315
|
+
session.changes[existingIdx].status = args["set-change-status"];
|
|
316
|
+
session.changes[existingIdx].updated_at = now;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// --truncate-history: keep last N history entries, discard older
|
|
322
|
+
if (args["truncate-history"]) {
|
|
323
|
+
const n = Number(args["truncate-history"]);
|
|
324
|
+
if (Number.isInteger(n) && n > 0) {
|
|
325
|
+
session.history = session.history || [];
|
|
326
|
+
if (session.history.length > n) {
|
|
327
|
+
session.history = session.history.slice(-n);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ── Write back atomically ─────────────────────────────────────────────
|
|
333
|
+
const tmpPath = sessionPath + ".tmp";
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
writeFileSync(tmpPath, stringifyYaml(session), "utf-8");
|
|
337
|
+
renameSync(tmpPath, sessionPath);
|
|
338
|
+
} catch (e) {
|
|
339
|
+
try {
|
|
340
|
+
if (existsSync(tmpPath)) unlinkSync(tmpPath);
|
|
341
|
+
} catch {
|
|
342
|
+
// Best effort cleanup
|
|
343
|
+
}
|
|
344
|
+
process.stderr.write(ERRORS.SESSION_WRITE_FAILED(e.message) + "\n");
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
process.stdout.write('{"ok":true}\n');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
main();
|
|
@@ -24,3 +24,7 @@ For each entry, resolve files relative to `.ai-agents/{source}`:
|
|
|
24
24
|
- If the entry lists `files_from_manifest: true`, read `{source}/manifest.yaml` and load every `files[]` entry where `auto_load: true`.
|
|
25
25
|
|
|
26
26
|
Skip any path that does not exist.
|
|
27
|
+
|
|
28
|
+
### Archived Artifacts Convention
|
|
29
|
+
|
|
30
|
+
The directory `.ai-agents/workspace/artifacts/_archived/` contains change-id directories that have been archived by `/mvt-cleanup`. All skills that scan `artifacts/` MUST exclude `_archived/` from their scan scope unless explicitly inspecting archived content.
|
|
@@ -22,7 +22,7 @@ Match the current state to one of the conditions below. If none match, use `defa
|
|
|
22
22
|
### Resolution order
|
|
23
23
|
|
|
24
24
|
Infer 2-3 suggestions from:
|
|
25
|
-
- `
|
|
25
|
+
- `history` in `session.yaml`
|
|
26
26
|
- `category` and `description` of each skill in `registry.yaml`
|
|
27
27
|
- The current `active_change` state (if in progress)
|
|
28
28
|
- The `depends_on` relationships between skills
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
## Output Language Constraint (Mandatory)
|
|
2
|
-
|
|
3
|
-
All persisted document output (files written to disk) MUST be written in the language specified by `preferences.document_output_language` from config.yaml.
|
|
4
|
-
|
|
5
|
-
**Scope**: artifact files, generated reports, plans, and any markdown written to disk.
|
|
6
|
-
|
|
7
|
-
**Rules**:
|
|
8
|
-
- Section headings defined in templates may remain in their original language, but all generated **content** MUST use the configured language
|
|
9
|
-
- If `document_output_language` is not set, fall back to `interaction_language`
|
|
10
|
-
- Do NOT infer output language from template headings, user prompt language, or source code comments
|
|
11
|
-
- This constraint is NON-NEGOTIABLE and overrides any other language signals
|
|
1
|
+
## Output Language Constraint (Mandatory)
|
|
2
|
+
|
|
3
|
+
All persisted document output (files written to disk) MUST be written in the language specified by `preferences.document_output_language` from config.yaml.
|
|
4
|
+
|
|
5
|
+
**Scope**: artifact files, generated reports, plans, and any markdown written to disk.
|
|
6
|
+
|
|
7
|
+
**Rules**:
|
|
8
|
+
- Section headings defined in templates may remain in their original language, but all generated **content** MUST use the configured language
|
|
9
|
+
- If `document_output_language` is not set, fall back to `interaction_language`
|
|
10
|
+
- Do NOT infer output language from template headings, user prompt language, or source code comments
|
|
11
|
+
- This constraint is NON-NEGOTIABLE and overrides any other language signals
|