@mandujs/cli 0.3.2 → 0.3.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.ko.md +181 -181
- package/README.md +181 -181
- package/package.json +3 -3
- package/src/commands/change/begin.ts +33 -0
- package/src/commands/change/commit.ts +23 -0
- package/src/commands/change/index.ts +6 -0
- package/src/commands/change/list.ts +57 -0
- package/src/commands/change/prune.ts +32 -0
- package/src/commands/change/rollback.ts +40 -0
- package/src/commands/change/status.ts +36 -0
- package/src/commands/guard-check.ts +27 -0
- package/src/commands/init.ts +88 -88
- package/src/main.ts +52 -0
- package/src/util/fs.ts +9 -9
- package/templates/default/apps/web/entry.tsx +35 -35
- package/templates/default/spec/routes.manifest.json +18 -18
- package/templates/default/tests/example.test.ts +58 -58
- package/templates/default/tests/helpers.ts +52 -52
- package/templates/default/tests/setup.ts +9 -9
- package/templates/default/tsconfig.json +14 -14
package/README.md
CHANGED
|
@@ -1,181 +1,181 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/konamgil/mandu/main/mandu_only_simbol.png" alt="Mandu" width="200" />
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
<h1 align="center">@mandujs/cli</h1>
|
|
6
|
-
|
|
7
|
-
<p align="center">
|
|
8
|
-
<strong>Agent-Native Fullstack Framework CLI</strong><br/>
|
|
9
|
-
A development OS where architecture stays intact even when AI agents write your code
|
|
10
|
-
</p>
|
|
11
|
-
|
|
12
|
-
<p align="center">
|
|
13
|
-
English | <a href="./README.ko.md"><strong>한국어</strong></a>
|
|
14
|
-
</p>
|
|
15
|
-
|
|
16
|
-
## Installation
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
# Bun required
|
|
20
|
-
bun add -D @mandujs/cli
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Quick Start
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
# Create a new project
|
|
27
|
-
bunx @mandujs/cli init my-app
|
|
28
|
-
cd my-app
|
|
29
|
-
|
|
30
|
-
# Start development server
|
|
31
|
-
bun run dev
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Commands
|
|
35
|
-
|
|
36
|
-
### `mandu init <project-name>`
|
|
37
|
-
|
|
38
|
-
Creates a new Mandu project.
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
bunx @mandujs/cli init my-app
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Generated structure:
|
|
45
|
-
```
|
|
46
|
-
my-app/
|
|
47
|
-
├── apps/
|
|
48
|
-
│ ├── server/main.ts # Server entry point
|
|
49
|
-
│ └── web/entry.tsx # Client entry point
|
|
50
|
-
├── spec/
|
|
51
|
-
│ └── routes.manifest.json # SSOT - Route definitions
|
|
52
|
-
├── tests/ # Test templates
|
|
53
|
-
├── package.json
|
|
54
|
-
└── tsconfig.json
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### `mandu dev`
|
|
58
|
-
|
|
59
|
-
Starts the development server (with HMR support).
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
bun run dev
|
|
63
|
-
# or
|
|
64
|
-
bunx mandu dev
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### `mandu spec`
|
|
68
|
-
|
|
69
|
-
Validates the spec file and updates the lock file.
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
bun run spec
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### `mandu generate`
|
|
76
|
-
|
|
77
|
-
Generates code based on the spec.
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
bun run generate
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### `mandu guard`
|
|
84
|
-
|
|
85
|
-
Checks architecture rules and auto-corrects violations.
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
bun run guard
|
|
89
|
-
|
|
90
|
-
# Disable auto-correction
|
|
91
|
-
bunx mandu guard --no-auto-correct
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Auto-correctable rules:
|
|
95
|
-
- `SPEC_HASH_MISMATCH` → Updates lock file
|
|
96
|
-
- `GENERATED_MANUAL_EDIT` → Regenerates code
|
|
97
|
-
- `SLOT_NOT_FOUND` → Creates slot file
|
|
98
|
-
|
|
99
|
-
## Writing Spec Files
|
|
100
|
-
|
|
101
|
-
`spec/routes.manifest.json` is the Single Source of Truth (SSOT) for all routes.
|
|
102
|
-
|
|
103
|
-
```json
|
|
104
|
-
{
|
|
105
|
-
"version": "1.0.0",
|
|
106
|
-
"routes": [
|
|
107
|
-
{
|
|
108
|
-
"id": "getUsers",
|
|
109
|
-
"pattern": "/api/users",
|
|
110
|
-
"kind": "api",
|
|
111
|
-
"module": "apps/server/api/users.ts"
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
"id": "homePage",
|
|
115
|
-
"pattern": "/",
|
|
116
|
-
"kind": "page",
|
|
117
|
-
"module": "apps/server/pages/home.ts",
|
|
118
|
-
"componentModule": "apps/web/pages/Home.tsx"
|
|
119
|
-
}
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Slot System (v0.2.0+)
|
|
125
|
-
|
|
126
|
-
Add `slotModule` to separate business logic:
|
|
127
|
-
|
|
128
|
-
```json
|
|
129
|
-
{
|
|
130
|
-
"id": "getUsers",
|
|
131
|
-
"pattern": "/api/users",
|
|
132
|
-
"kind": "api",
|
|
133
|
-
"module": "apps/server/api/users.generated.ts",
|
|
134
|
-
"slotModule": "apps/server/api/users.slot.ts"
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
- `*.generated.ts` - Managed by framework (do not modify)
|
|
139
|
-
- `*.slot.ts` - Business logic written by developers
|
|
140
|
-
|
|
141
|
-
## Development Workflow
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
# 1. Edit spec
|
|
145
|
-
# 2. Validate spec and update lock
|
|
146
|
-
bun run spec
|
|
147
|
-
|
|
148
|
-
# 3. Generate code
|
|
149
|
-
bun run generate
|
|
150
|
-
|
|
151
|
-
# 4. Check architecture
|
|
152
|
-
bun run guard
|
|
153
|
-
|
|
154
|
-
# 5. Run tests
|
|
155
|
-
bun test
|
|
156
|
-
|
|
157
|
-
# 6. Start dev server
|
|
158
|
-
bun run dev
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
## Testing
|
|
162
|
-
|
|
163
|
-
Built-in support for Bun test framework.
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
bun test # Run tests
|
|
167
|
-
bun test --watch # Watch mode
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Requirements
|
|
171
|
-
|
|
172
|
-
- Bun >= 1.0.0
|
|
173
|
-
- React >= 18.0.0
|
|
174
|
-
|
|
175
|
-
## Related Packages
|
|
176
|
-
|
|
177
|
-
- [@mandujs/core](https://www.npmjs.com/package/@mandujs/core) - Core runtime
|
|
178
|
-
|
|
179
|
-
## License
|
|
180
|
-
|
|
181
|
-
MIT
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/konamgil/mandu/main/mandu_only_simbol.png" alt="Mandu" width="200" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">@mandujs/cli</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Agent-Native Fullstack Framework CLI</strong><br/>
|
|
9
|
+
A development OS where architecture stays intact even when AI agents write your code
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
English | <a href="./README.ko.md"><strong>한국어</strong></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Bun required
|
|
20
|
+
bun add -D @mandujs/cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Create a new project
|
|
27
|
+
bunx @mandujs/cli init my-app
|
|
28
|
+
cd my-app
|
|
29
|
+
|
|
30
|
+
# Start development server
|
|
31
|
+
bun run dev
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
### `mandu init <project-name>`
|
|
37
|
+
|
|
38
|
+
Creates a new Mandu project.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bunx @mandujs/cli init my-app
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Generated structure:
|
|
45
|
+
```
|
|
46
|
+
my-app/
|
|
47
|
+
├── apps/
|
|
48
|
+
│ ├── server/main.ts # Server entry point
|
|
49
|
+
│ └── web/entry.tsx # Client entry point
|
|
50
|
+
├── spec/
|
|
51
|
+
│ └── routes.manifest.json # SSOT - Route definitions
|
|
52
|
+
├── tests/ # Test templates
|
|
53
|
+
├── package.json
|
|
54
|
+
└── tsconfig.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### `mandu dev`
|
|
58
|
+
|
|
59
|
+
Starts the development server (with HMR support).
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
bun run dev
|
|
63
|
+
# or
|
|
64
|
+
bunx mandu dev
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `mandu spec`
|
|
68
|
+
|
|
69
|
+
Validates the spec file and updates the lock file.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
bun run spec
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### `mandu generate`
|
|
76
|
+
|
|
77
|
+
Generates code based on the spec.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bun run generate
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `mandu guard`
|
|
84
|
+
|
|
85
|
+
Checks architecture rules and auto-corrects violations.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
bun run guard
|
|
89
|
+
|
|
90
|
+
# Disable auto-correction
|
|
91
|
+
bunx mandu guard --no-auto-correct
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Auto-correctable rules:
|
|
95
|
+
- `SPEC_HASH_MISMATCH` → Updates lock file
|
|
96
|
+
- `GENERATED_MANUAL_EDIT` → Regenerates code
|
|
97
|
+
- `SLOT_NOT_FOUND` → Creates slot file
|
|
98
|
+
|
|
99
|
+
## Writing Spec Files
|
|
100
|
+
|
|
101
|
+
`spec/routes.manifest.json` is the Single Source of Truth (SSOT) for all routes.
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"version": "1.0.0",
|
|
106
|
+
"routes": [
|
|
107
|
+
{
|
|
108
|
+
"id": "getUsers",
|
|
109
|
+
"pattern": "/api/users",
|
|
110
|
+
"kind": "api",
|
|
111
|
+
"module": "apps/server/api/users.ts"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"id": "homePage",
|
|
115
|
+
"pattern": "/",
|
|
116
|
+
"kind": "page",
|
|
117
|
+
"module": "apps/server/pages/home.ts",
|
|
118
|
+
"componentModule": "apps/web/pages/Home.tsx"
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Slot System (v0.2.0+)
|
|
125
|
+
|
|
126
|
+
Add `slotModule` to separate business logic:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"id": "getUsers",
|
|
131
|
+
"pattern": "/api/users",
|
|
132
|
+
"kind": "api",
|
|
133
|
+
"module": "apps/server/api/users.generated.ts",
|
|
134
|
+
"slotModule": "apps/server/api/users.slot.ts"
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- `*.generated.ts` - Managed by framework (do not modify)
|
|
139
|
+
- `*.slot.ts` - Business logic written by developers
|
|
140
|
+
|
|
141
|
+
## Development Workflow
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# 1. Edit spec
|
|
145
|
+
# 2. Validate spec and update lock
|
|
146
|
+
bun run spec
|
|
147
|
+
|
|
148
|
+
# 3. Generate code
|
|
149
|
+
bun run generate
|
|
150
|
+
|
|
151
|
+
# 4. Check architecture
|
|
152
|
+
bun run guard
|
|
153
|
+
|
|
154
|
+
# 5. Run tests
|
|
155
|
+
bun test
|
|
156
|
+
|
|
157
|
+
# 6. Start dev server
|
|
158
|
+
bun run dev
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Testing
|
|
162
|
+
|
|
163
|
+
Built-in support for Bun test framework.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
bun test # Run tests
|
|
167
|
+
bun test --watch # Watch mode
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Requirements
|
|
171
|
+
|
|
172
|
+
- Bun >= 1.0.0
|
|
173
|
+
- React >= 18.0.0
|
|
174
|
+
|
|
175
|
+
## Related Packages
|
|
176
|
+
|
|
177
|
+
- [@mandujs/core](https://www.npmjs.com/package/@mandujs/core) - Core runtime
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mandujs/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Agent-Native Fullstack Framework - 에이전트가 코딩해도 아키텍처가 무너지지 않는 개발 OS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/main.ts",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@mandujs/core": "
|
|
35
|
+
"@mandujs/core": "workspace:*"
|
|
36
36
|
},
|
|
37
|
-
"
|
|
37
|
+
"engines": {
|
|
38
38
|
"bun": ">=1.0.0"
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { beginChange } from "@mandujs/core";
|
|
2
|
+
import { getRootDir } from "../../util/fs";
|
|
3
|
+
|
|
4
|
+
export interface ChangeBeginOptions {
|
|
5
|
+
message?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function changeBegin(options: ChangeBeginOptions = {}): Promise<boolean> {
|
|
9
|
+
const rootDir = getRootDir();
|
|
10
|
+
|
|
11
|
+
console.log(`🥟 Mandu Change Begin`);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const change = await beginChange(rootDir, {
|
|
15
|
+
message: options.message,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
console.log(`✅ 트랜잭션 시작됨`);
|
|
19
|
+
console.log(` ID: ${change.id}`);
|
|
20
|
+
console.log(` 스냅샷: ${change.snapshotId}`);
|
|
21
|
+
if (change.message) {
|
|
22
|
+
console.log(` 메시지: ${change.message}`);
|
|
23
|
+
}
|
|
24
|
+
console.log(`\n💡 변경 작업 후 다음 명령을 실행하세요:`);
|
|
25
|
+
console.log(` 확정: bunx mandu change commit`);
|
|
26
|
+
console.log(` 롤백: bunx mandu change rollback`);
|
|
27
|
+
|
|
28
|
+
return true;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(`❌ 트랜잭션 시작 실패: ${error instanceof Error ? error.message : String(error)}`);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { commitChange } from "@mandujs/core";
|
|
2
|
+
import { getRootDir } from "../../util/fs";
|
|
3
|
+
|
|
4
|
+
export async function changeCommit(): Promise<boolean> {
|
|
5
|
+
const rootDir = getRootDir();
|
|
6
|
+
|
|
7
|
+
console.log(`🥟 Mandu Change Commit`);
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const result = await commitChange(rootDir);
|
|
11
|
+
|
|
12
|
+
console.log(`✅ 변경 확정됨`);
|
|
13
|
+
console.log(` ID: ${result.changeId}`);
|
|
14
|
+
if (result.message) {
|
|
15
|
+
console.log(` 메시지: ${result.message}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return true;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error(`❌ 커밋 실패: ${error instanceof Error ? error.message : String(error)}`);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { changeBegin, type ChangeBeginOptions } from "./begin";
|
|
2
|
+
export { changeCommit } from "./commit";
|
|
3
|
+
export { changeRollback, type ChangeRollbackOptions } from "./rollback";
|
|
4
|
+
export { changeStatus } from "./status";
|
|
5
|
+
export { changeList } from "./list";
|
|
6
|
+
export { changePrune, type ChangePruneOptions } from "./prune";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { listChanges, getChangeStats } from "@mandujs/core";
|
|
2
|
+
import { getRootDir } from "../../util/fs";
|
|
3
|
+
|
|
4
|
+
export async function changeList(): Promise<boolean> {
|
|
5
|
+
const rootDir = getRootDir();
|
|
6
|
+
|
|
7
|
+
console.log(`🥟 Mandu Change List\n`);
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const changes = await listChanges(rootDir);
|
|
11
|
+
const stats = await getChangeStats(rootDir);
|
|
12
|
+
|
|
13
|
+
if (changes.length === 0) {
|
|
14
|
+
console.log(`📭 변경 이력이 없습니다`);
|
|
15
|
+
console.log(`\n💡 새 트랜잭션 시작: bunx mandu change begin`);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 통계
|
|
20
|
+
console.log(`📊 통계:`);
|
|
21
|
+
console.log(` 총 기록: ${stats.total}개`);
|
|
22
|
+
console.log(` 활성: ${stats.active}개 | 확정: ${stats.committed}개 | 롤백: ${stats.rolledBack}개`);
|
|
23
|
+
console.log(` 스냅샷: ${stats.snapshotCount}개\n`);
|
|
24
|
+
|
|
25
|
+
// 최근 기록 (최신 순)
|
|
26
|
+
console.log(`📜 변경 이력:`);
|
|
27
|
+
|
|
28
|
+
const sortedChanges = [...changes].reverse(); // 최신 순
|
|
29
|
+
|
|
30
|
+
for (const change of sortedChanges) {
|
|
31
|
+
const statusIcon =
|
|
32
|
+
change.status === "active"
|
|
33
|
+
? "🔄"
|
|
34
|
+
: change.status === "committed"
|
|
35
|
+
? "✅"
|
|
36
|
+
: "↩️";
|
|
37
|
+
|
|
38
|
+
const date = new Date(change.createdAt).toLocaleString();
|
|
39
|
+
|
|
40
|
+
console.log(` ${statusIcon} ${change.id}`);
|
|
41
|
+
console.log(` 상태: ${change.status}`);
|
|
42
|
+
console.log(` 시간: ${date}`);
|
|
43
|
+
if (change.message) {
|
|
44
|
+
console.log(` 메시지: ${change.message}`);
|
|
45
|
+
}
|
|
46
|
+
if (change.autoGenerated) {
|
|
47
|
+
console.log(` 생성: Auto-correct`);
|
|
48
|
+
}
|
|
49
|
+
console.log("");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return true;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(`❌ 이력 조회 실패: ${error instanceof Error ? error.message : String(error)}`);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { pruneHistory, DEFAULT_HISTORY_CONFIG } from "@mandujs/core";
|
|
2
|
+
import { getRootDir } from "../../util/fs";
|
|
3
|
+
|
|
4
|
+
export interface ChangePruneOptions {
|
|
5
|
+
keep?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function changePrune(options: ChangePruneOptions = {}): Promise<boolean> {
|
|
9
|
+
const rootDir = getRootDir();
|
|
10
|
+
const keepCount = options.keep ?? DEFAULT_HISTORY_CONFIG.maxSnapshots;
|
|
11
|
+
|
|
12
|
+
console.log(`🥟 Mandu Change Prune`);
|
|
13
|
+
console.log(` 유지할 스냅샷: ${keepCount}개\n`);
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const deletedIds = await pruneHistory(rootDir, keepCount);
|
|
17
|
+
|
|
18
|
+
if (deletedIds.length === 0) {
|
|
19
|
+
console.log(`✅ 정리할 스냅샷이 없습니다`);
|
|
20
|
+
} else {
|
|
21
|
+
console.log(`🗑️ 삭제된 스냅샷: ${deletedIds.length}개`);
|
|
22
|
+
for (const id of deletedIds) {
|
|
23
|
+
console.log(` - ${id}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return true;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(`❌ 정리 실패: ${error instanceof Error ? error.message : String(error)}`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { rollbackChange } from "@mandujs/core";
|
|
2
|
+
import { getRootDir } from "../../util/fs";
|
|
3
|
+
|
|
4
|
+
export interface ChangeRollbackOptions {
|
|
5
|
+
id?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function changeRollback(options: ChangeRollbackOptions = {}): Promise<boolean> {
|
|
9
|
+
const rootDir = getRootDir();
|
|
10
|
+
|
|
11
|
+
console.log(`🥟 Mandu Change Rollback`);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const result = await rollbackChange(rootDir, options.id);
|
|
15
|
+
|
|
16
|
+
if (result.success) {
|
|
17
|
+
console.log(`✅ 롤백 완료`);
|
|
18
|
+
console.log(` ID: ${result.changeId}`);
|
|
19
|
+
console.log(` 복원된 파일: ${result.restoreResult.restoredFiles.length}개`);
|
|
20
|
+
|
|
21
|
+
for (const file of result.restoreResult.restoredFiles) {
|
|
22
|
+
console.log(` - ${file}`);
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
console.log(`⚠️ 롤백 부분 완료`);
|
|
26
|
+
console.log(` ID: ${result.changeId}`);
|
|
27
|
+
console.log(` 복원된 파일: ${result.restoreResult.restoredFiles.length}개`);
|
|
28
|
+
console.log(` 실패한 파일: ${result.restoreResult.failedFiles.length}개`);
|
|
29
|
+
|
|
30
|
+
for (const error of result.restoreResult.errors) {
|
|
31
|
+
console.error(` - ${error}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return result.success;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error(`❌ 롤백 실패: ${error instanceof Error ? error.message : String(error)}`);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getTransactionStatus } from "@mandujs/core";
|
|
2
|
+
import { getRootDir } from "../../util/fs";
|
|
3
|
+
|
|
4
|
+
export async function changeStatus(): Promise<boolean> {
|
|
5
|
+
const rootDir = getRootDir();
|
|
6
|
+
|
|
7
|
+
console.log(`🥟 Mandu Change Status\n`);
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const { state, change } = await getTransactionStatus(rootDir);
|
|
11
|
+
|
|
12
|
+
if (state.active && change) {
|
|
13
|
+
console.log(`🔄 활성 트랜잭션:`);
|
|
14
|
+
console.log(` ID: ${change.id}`);
|
|
15
|
+
console.log(` 스냅샷: ${change.snapshotId}`);
|
|
16
|
+
console.log(` 시작: ${new Date(change.createdAt).toLocaleString()}`);
|
|
17
|
+
if (change.message) {
|
|
18
|
+
console.log(` 메시지: ${change.message}`);
|
|
19
|
+
}
|
|
20
|
+
if (change.autoGenerated) {
|
|
21
|
+
console.log(` 생성: Auto-correct`);
|
|
22
|
+
}
|
|
23
|
+
console.log(`\n💡 사용 가능한 명령:`);
|
|
24
|
+
console.log(` 확정: bunx mandu change commit`);
|
|
25
|
+
console.log(` 롤백: bunx mandu change rollback`);
|
|
26
|
+
} else {
|
|
27
|
+
console.log(`✅ 활성 트랜잭션 없음`);
|
|
28
|
+
console.log(`\n💡 새 트랜잭션 시작: bunx mandu change begin`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return true;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(`❌ 상태 조회 실패: ${error instanceof Error ? error.message : String(error)}`);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -58,9 +58,36 @@ export async function guardCheck(options: GuardCheckOptions = {}): Promise<boole
|
|
|
58
58
|
|
|
59
59
|
if (autoCorrectResult.fixed) {
|
|
60
60
|
console.log(`\n✅ Auto-correct 완료 (${autoCorrectResult.retriedCount}회 재시도)`);
|
|
61
|
+
if (autoCorrectResult.changeId) {
|
|
62
|
+
console.log(` 트랜잭션: ${autoCorrectResult.changeId} (커밋됨)`);
|
|
63
|
+
}
|
|
61
64
|
|
|
62
65
|
// 최종 Guard 재검사
|
|
63
66
|
checkResult = await runGuardCheck(result.data, rootDir);
|
|
67
|
+
} else if (autoCorrectResult.rolledBack) {
|
|
68
|
+
console.log(`\n⚠️ Auto-correct 실패 - 롤백됨`);
|
|
69
|
+
if (autoCorrectResult.changeId) {
|
|
70
|
+
console.log(` 트랜잭션: ${autoCorrectResult.changeId} (롤백됨)`);
|
|
71
|
+
}
|
|
72
|
+
console.log(` 원래 상태로 복원되었습니다.`);
|
|
73
|
+
|
|
74
|
+
const manualViolations = autoCorrectResult.remainingViolations.filter(
|
|
75
|
+
(v) => !isAutoCorrectableViolation(v)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (manualViolations.length > 0) {
|
|
79
|
+
console.log(`\n⚠️ 수동 수정이 필요한 위반:`);
|
|
80
|
+
for (const v of manualViolations) {
|
|
81
|
+
console.log(` - [${v.ruleId}] ${v.file}`);
|
|
82
|
+
console.log(` 💡 ${v.suggestion}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 남은 위반으로 업데이트
|
|
87
|
+
checkResult = {
|
|
88
|
+
passed: false,
|
|
89
|
+
violations: autoCorrectResult.remainingViolations,
|
|
90
|
+
};
|
|
64
91
|
} else {
|
|
65
92
|
console.log(`\n⚠️ 일부 위반은 수동 수정이 필요합니다:`);
|
|
66
93
|
|