@scriptgun/workerc 0.1.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/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +25 -0
- package/dist/detect.d.ts +10 -0
- package/dist/detect.js +59 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +144 -0
- package/dist/merge-settings.d.ts +8 -0
- package/dist/merge-settings.js +82 -0
- package/dist/templates/commands/abort.md +63 -0
- package/dist/templates/commands/commit.md +145 -0
- package/dist/templates/commands/done.md +46 -0
- package/dist/templates/commands/handoff.md +81 -0
- package/dist/templates/commands/list.md +32 -0
- package/dist/templates/commands/new.md +108 -0
- package/dist/templates/commands/resume.md +64 -0
- package/dist/templates/commands/review.md +86 -0
- package/dist/templates/commands/scope.md +74 -0
- package/dist/templates/commands/status.md +30 -0
- package/dist/templates/hooks/post-edit-lint.sh.tmpl +27 -0
- package/dist/templates/hooks/post-edit-tracker.sh +121 -0
- package/dist/templates/hooks/post-edit-types.sh.tmpl +25 -0
- package/dist/templates/hooks/session-start-compact.sh +88 -0
- package/dist/write-files.d.ts +16 -0
- package/dist/write-files.js +78 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 workerc contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# workerc
|
|
2
|
+
|
|
3
|
+
Claude Code session management — commands, hooks, and progress tracking.
|
|
4
|
+
|
|
5
|
+
workerc adds structured session workflows to any project using [Claude Code](https://docs.anthropic.com/en/docs/claude-code). It installs slash commands for managing work sessions, hooks that enforce progress tracking and code quality, and a progress file system that persists context across sessions.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @scriptgun/workerc init
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This interactively sets up:
|
|
14
|
+
|
|
15
|
+
- **10 slash commands** in `.claude/commands/workerc/`
|
|
16
|
+
- **Hooks** in `.claude/hooks/` (tracker + optional lint/type check)
|
|
17
|
+
- **Settings** in `.claude/settings.local.json`
|
|
18
|
+
- **Progress directory** at `.claude/progress/`
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
| Command | Description |
|
|
23
|
+
|---------|-------------|
|
|
24
|
+
| `/workerc:new` | Start a new work session with optional spec |
|
|
25
|
+
| `/workerc:resume` | Resume an unclaimed session |
|
|
26
|
+
| `/workerc:status` | Show current session progress |
|
|
27
|
+
| `/workerc:list` | List all progress files with status |
|
|
28
|
+
| `/workerc:scope` | Update spec scope mid-session |
|
|
29
|
+
| `/workerc:commit` | Stage and commit with auto-generated message |
|
|
30
|
+
| `/workerc:review` | Review work against spec |
|
|
31
|
+
| `/workerc:handoff` | Pause session with handoff notes |
|
|
32
|
+
| `/workerc:done` | Mark session complete |
|
|
33
|
+
| `/workerc:abort` | Abandon session |
|
|
34
|
+
|
|
35
|
+
## Hooks
|
|
36
|
+
|
|
37
|
+
### Always installed
|
|
38
|
+
|
|
39
|
+
- **post-edit-tracker.sh** — Blocks edits if no progress file is active. Auto-claims pending sessions on first edit. Auto-tracks edited files. Enforces 15s freshness (agent must update progress regularly).
|
|
40
|
+
- **session-start-compact.sh** — Re-injects session context after Claude compacts the conversation. Restores progress file, spec, and file list so the agent can continue seamlessly.
|
|
41
|
+
|
|
42
|
+
### Optional (auto-detected)
|
|
43
|
+
|
|
44
|
+
- **post-edit-lint.sh** — Runs linter on every file edit. Detected tools: Biome, ESLint.
|
|
45
|
+
- **post-edit-types.sh** — Runs type checker on every file edit. Detected tools: TypeScript (`tsc`).
|
|
46
|
+
|
|
47
|
+
## How it works
|
|
48
|
+
|
|
49
|
+
1. Run `/workerc:new` to start a session — creates a progress file in `.claude/progress/`
|
|
50
|
+
2. The tracker hook auto-claims the session on your first file edit
|
|
51
|
+
3. As you work, the tracker logs edited files and enforces regular progress updates
|
|
52
|
+
4. Use `/workerc:commit` to commit, `/workerc:review` to check against spec
|
|
53
|
+
5. Use `/workerc:handoff` to pause or `/workerc:done` to complete
|
|
54
|
+
|
|
55
|
+
Progress files persist across sessions — use `/workerc:resume` to pick up where you left off.
|
|
56
|
+
|
|
57
|
+
## Re-running init
|
|
58
|
+
|
|
59
|
+
`workerc init` is idempotent. Running it again updates all commands and hooks without duplicating settings entries.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT
|
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { init } from "./init.js";
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const command = args[0];
|
|
6
|
+
if (command === "init") {
|
|
7
|
+
const projectDir = resolve(args[1] ?? ".");
|
|
8
|
+
init(projectDir);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
console.log(`workerc — Claude Code session management
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
workerc init [dir] Install commands, hooks, and settings
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
dir Project directory (default: current)
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
npx workerc init
|
|
21
|
+
npx workerc init ./my-project`);
|
|
22
|
+
if (command && command !== "--help" && command !== "-h") {
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface DetectedTool {
|
|
2
|
+
name: string;
|
|
3
|
+
command: string;
|
|
4
|
+
extensions: string;
|
|
5
|
+
}
|
|
6
|
+
export interface DetectionResult {
|
|
7
|
+
linter: DetectedTool | undefined;
|
|
8
|
+
typeChecker: DetectedTool | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare function detect(projectDir: string): DetectionResult;
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const LINTER_CONFIGS = [
|
|
4
|
+
{
|
|
5
|
+
patterns: ["biome.json", "biome.jsonc"],
|
|
6
|
+
tool: {
|
|
7
|
+
name: "biome",
|
|
8
|
+
command: "npx biome check --write",
|
|
9
|
+
extensions: "ts|tsx|js|jsx|json|css",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
patterns: [
|
|
14
|
+
"eslint.config.js",
|
|
15
|
+
"eslint.config.mjs",
|
|
16
|
+
"eslint.config.cjs",
|
|
17
|
+
"eslint.config.ts",
|
|
18
|
+
".eslintrc",
|
|
19
|
+
".eslintrc.js",
|
|
20
|
+
".eslintrc.json",
|
|
21
|
+
".eslintrc.yml",
|
|
22
|
+
],
|
|
23
|
+
tool: {
|
|
24
|
+
name: "eslint",
|
|
25
|
+
command: "npx eslint --fix",
|
|
26
|
+
extensions: "ts|tsx|js|jsx",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
const TYPE_CHECKER_CONFIGS = [
|
|
31
|
+
{
|
|
32
|
+
patterns: ["tsconfig.json"],
|
|
33
|
+
tool: {
|
|
34
|
+
name: "tsc",
|
|
35
|
+
command: "npx tsc --noEmit",
|
|
36
|
+
extensions: "ts|tsx",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
function findConfig(projectDir, patterns) {
|
|
41
|
+
return patterns.some((p) => existsSync(join(projectDir, p)));
|
|
42
|
+
}
|
|
43
|
+
export function detect(projectDir) {
|
|
44
|
+
let linter;
|
|
45
|
+
for (const config of LINTER_CONFIGS) {
|
|
46
|
+
if (findConfig(projectDir, config.patterns)) {
|
|
47
|
+
linter = config.tool;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
let typeChecker;
|
|
52
|
+
for (const config of TYPE_CHECKER_CONFIGS) {
|
|
53
|
+
if (findConfig(projectDir, config.patterns)) {
|
|
54
|
+
typeChecker = config.tool;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { linter, typeChecker };
|
|
59
|
+
}
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function init(projectDir: string): Promise<void>;
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { detect } from "./detect.js";
|
|
3
|
+
import { mergeSettings } from "./merge-settings.js";
|
|
4
|
+
import { writeFiles } from "./write-files.js";
|
|
5
|
+
export async function init(projectDir) {
|
|
6
|
+
p.intro("workerc init");
|
|
7
|
+
/* Step 1: Detect tools */
|
|
8
|
+
const spinner = p.spinner();
|
|
9
|
+
spinner.start("Detecting project tools");
|
|
10
|
+
const detected = detect(projectDir);
|
|
11
|
+
spinner.stop("Detection complete");
|
|
12
|
+
if (detected.linter) {
|
|
13
|
+
p.log.info(`Found linter: ${detected.linter.name}`);
|
|
14
|
+
}
|
|
15
|
+
if (detected.typeChecker) {
|
|
16
|
+
p.log.info(`Found type checker: ${detected.typeChecker.name}`);
|
|
17
|
+
}
|
|
18
|
+
if (!detected.linter && !detected.typeChecker) {
|
|
19
|
+
p.log.info("No linter or type checker detected");
|
|
20
|
+
}
|
|
21
|
+
/* Step 2: Lint hook */
|
|
22
|
+
let enableLint = false;
|
|
23
|
+
let lintCommand = "";
|
|
24
|
+
let lintExtensions = "";
|
|
25
|
+
const lintHookFilename = "post-edit-lint.sh";
|
|
26
|
+
if (detected.linter) {
|
|
27
|
+
const lintAnswer = await p.confirm({
|
|
28
|
+
message: `Enable lint hook? (${detected.linter.name}: ${detected.linter.command})`,
|
|
29
|
+
initialValue: true,
|
|
30
|
+
});
|
|
31
|
+
if (p.isCancel(lintAnswer)) {
|
|
32
|
+
p.cancel("Cancelled");
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
enableLint = lintAnswer;
|
|
36
|
+
if (enableLint) {
|
|
37
|
+
const cmdAnswer = await p.text({
|
|
38
|
+
message: "Lint command (file path appended automatically):",
|
|
39
|
+
initialValue: detected.linter.command,
|
|
40
|
+
});
|
|
41
|
+
if (p.isCancel(cmdAnswer)) {
|
|
42
|
+
p.cancel("Cancelled");
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
lintCommand = cmdAnswer;
|
|
46
|
+
const extAnswer = await p.text({
|
|
47
|
+
message: "File extensions to check (pipe-separated):",
|
|
48
|
+
initialValue: detected.linter.extensions,
|
|
49
|
+
});
|
|
50
|
+
if (p.isCancel(extAnswer)) {
|
|
51
|
+
p.cancel("Cancelled");
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
lintExtensions = extAnswer;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/* Step 3: Type check hook */
|
|
58
|
+
let enableTypes = false;
|
|
59
|
+
let typeCommand = "";
|
|
60
|
+
let typeExtensions = "";
|
|
61
|
+
const typesHookFilename = "post-edit-types.sh";
|
|
62
|
+
if (detected.typeChecker) {
|
|
63
|
+
const typesAnswer = await p.confirm({
|
|
64
|
+
message: `Enable type check hook? (${detected.typeChecker.name}: ${detected.typeChecker.command})`,
|
|
65
|
+
initialValue: true,
|
|
66
|
+
});
|
|
67
|
+
if (p.isCancel(typesAnswer)) {
|
|
68
|
+
p.cancel("Cancelled");
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
enableTypes = typesAnswer;
|
|
72
|
+
if (enableTypes) {
|
|
73
|
+
const cmdAnswer = await p.text({
|
|
74
|
+
message: "Type check command:",
|
|
75
|
+
initialValue: detected.typeChecker.command,
|
|
76
|
+
});
|
|
77
|
+
if (p.isCancel(cmdAnswer)) {
|
|
78
|
+
p.cancel("Cancelled");
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
typeCommand = cmdAnswer;
|
|
82
|
+
const extAnswer = await p.text({
|
|
83
|
+
message: "File extensions to check (pipe-separated):",
|
|
84
|
+
initialValue: detected.typeChecker.extensions,
|
|
85
|
+
});
|
|
86
|
+
if (p.isCancel(extAnswer)) {
|
|
87
|
+
p.cancel("Cancelled");
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
typeExtensions = extAnswer;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/* Step 4: Build active hooks description for compact hook */
|
|
94
|
+
const hookDescriptions = [];
|
|
95
|
+
if (enableLint) {
|
|
96
|
+
hookDescriptions.push(`- ${lintHookFilename} (blocks on lint/format errors)`);
|
|
97
|
+
}
|
|
98
|
+
if (enableTypes) {
|
|
99
|
+
hookDescriptions.push(`- ${typesHookFilename} (blocks on type errors)`);
|
|
100
|
+
}
|
|
101
|
+
hookDescriptions.push("- post-edit-tracker.sh (blocks if progress not updated within 15s)");
|
|
102
|
+
const activeHooksDescription = hookDescriptions.join("\n");
|
|
103
|
+
/* Step 5: Write files */
|
|
104
|
+
spinner.start("Writing files");
|
|
105
|
+
const result = writeFiles({
|
|
106
|
+
projectDir,
|
|
107
|
+
enableLint,
|
|
108
|
+
lintCommand,
|
|
109
|
+
lintExtensions,
|
|
110
|
+
lintHookFilename,
|
|
111
|
+
enableTypes,
|
|
112
|
+
typeCommand,
|
|
113
|
+
typeExtensions,
|
|
114
|
+
typesHookFilename,
|
|
115
|
+
activeHooksDescription,
|
|
116
|
+
});
|
|
117
|
+
spinner.stop("Files written");
|
|
118
|
+
/* Step 6: Merge settings */
|
|
119
|
+
spinner.start("Updating settings");
|
|
120
|
+
mergeSettings({
|
|
121
|
+
projectDir,
|
|
122
|
+
enableLint,
|
|
123
|
+
lintHookFilename,
|
|
124
|
+
enableTypes,
|
|
125
|
+
typesHookFilename,
|
|
126
|
+
});
|
|
127
|
+
spinner.stop("Settings updated");
|
|
128
|
+
/* Step 7: Summary */
|
|
129
|
+
p.log.success("workerc installed");
|
|
130
|
+
p.log.message([
|
|
131
|
+
"",
|
|
132
|
+
"Commands (10):",
|
|
133
|
+
...result.commands.map((f) => ` .claude/commands/workerc/${f}`),
|
|
134
|
+
"",
|
|
135
|
+
"Hooks:",
|
|
136
|
+
...result.hooks.map((f) => ` .claude/hooks/${f}`),
|
|
137
|
+
"",
|
|
138
|
+
"Settings: .claude/settings.local.json",
|
|
139
|
+
"Progress: .claude/progress/",
|
|
140
|
+
"",
|
|
141
|
+
"Get started: run /workerc:new in Claude Code",
|
|
142
|
+
].join("\n"));
|
|
143
|
+
p.outro("done");
|
|
144
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Identifies workerc-managed hooks by checking if the command path
|
|
5
|
+
* contains ".claude/hooks/" and one of our known script names.
|
|
6
|
+
*/
|
|
7
|
+
const WORKERC_HOOK_NAMES = [
|
|
8
|
+
"session-start-compact.sh",
|
|
9
|
+
"post-edit-tracker.sh",
|
|
10
|
+
"post-edit-lint.sh",
|
|
11
|
+
"post-edit-types.sh",
|
|
12
|
+
];
|
|
13
|
+
function isWorkercHook(hook) {
|
|
14
|
+
return WORKERC_HOOK_NAMES.some((name) => hook.command.includes(name));
|
|
15
|
+
}
|
|
16
|
+
function isWorkercGroup(group) {
|
|
17
|
+
return group.hooks.every(isWorkercHook);
|
|
18
|
+
}
|
|
19
|
+
export function mergeSettings(options) {
|
|
20
|
+
const settingsPath = join(options.projectDir, ".claude", "settings.local.json");
|
|
21
|
+
const settingsDir = dirname(settingsPath);
|
|
22
|
+
if (!existsSync(settingsDir)) {
|
|
23
|
+
mkdirSync(settingsDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
let settings = {};
|
|
26
|
+
if (existsSync(settingsPath)) {
|
|
27
|
+
const raw = readFileSync(settingsPath, "utf-8");
|
|
28
|
+
settings = JSON.parse(raw);
|
|
29
|
+
}
|
|
30
|
+
if (!settings.hooks) {
|
|
31
|
+
settings.hooks = {};
|
|
32
|
+
}
|
|
33
|
+
/* SessionStart: remove old workerc hooks, add compact */
|
|
34
|
+
const existingSessionStart = (settings.hooks.SessionStart ?? []).filter((g) => !isWorkercGroup(g));
|
|
35
|
+
const compactHook = {
|
|
36
|
+
matcher: "compact",
|
|
37
|
+
hooks: [
|
|
38
|
+
{
|
|
39
|
+
type: "command",
|
|
40
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/session-start-compact.sh',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
settings.hooks.SessionStart = [...existingSessionStart, compactHook];
|
|
45
|
+
/* PostToolUse: remove old workerc hooks, add new ones */
|
|
46
|
+
const existingPostToolUse = (settings.hooks.PostToolUse ?? []).filter((g) => !isWorkercGroup(g));
|
|
47
|
+
const newPostToolUse = [];
|
|
48
|
+
if (options.enableLint) {
|
|
49
|
+
newPostToolUse.push({
|
|
50
|
+
matcher: "Write|Edit",
|
|
51
|
+
hooks: [
|
|
52
|
+
{
|
|
53
|
+
type: "command",
|
|
54
|
+
command: `"$CLAUDE_PROJECT_DIR"/.claude/hooks/${options.lintHookFilename}`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (options.enableTypes) {
|
|
60
|
+
newPostToolUse.push({
|
|
61
|
+
matcher: "Write|Edit",
|
|
62
|
+
hooks: [
|
|
63
|
+
{
|
|
64
|
+
type: "command",
|
|
65
|
+
command: `"$CLAUDE_PROJECT_DIR"/.claude/hooks/${options.typesHookFilename}`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/* Tracker always goes last */
|
|
71
|
+
newPostToolUse.push({
|
|
72
|
+
matcher: "Write|Edit",
|
|
73
|
+
hooks: [
|
|
74
|
+
{
|
|
75
|
+
type: "command",
|
|
76
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/post-edit-tracker.sh',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
settings.hooks.PostToolUse = [...existingPostToolUse, ...newPostToolUse];
|
|
81
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, "\t") + "\n");
|
|
82
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workerc:abort
|
|
3
|
+
description: Abandon current work and mark progress as aborted
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Edit
|
|
7
|
+
- Glob
|
|
8
|
+
- AskUserQuestion
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Mark the current session's work as abandoned. Different from done (finished) and handoff (paused).
|
|
13
|
+
Use when: wrong approach, spec changed, work no longer needed.
|
|
14
|
+
</objective>
|
|
15
|
+
|
|
16
|
+
<process>
|
|
17
|
+
|
|
18
|
+
**Step 1: Find session progress file**
|
|
19
|
+
|
|
20
|
+
Read all `.claude/progress/*.md`. Find the one with `<!-- session: CURRENT_SESSION_ID -->` on line 2.
|
|
21
|
+
|
|
22
|
+
If none found: print "No active progress file for this session." and STOP.
|
|
23
|
+
|
|
24
|
+
**Step 2: Get reason**
|
|
25
|
+
|
|
26
|
+
Ask user:
|
|
27
|
+
```
|
|
28
|
+
AskUserQuestion(
|
|
29
|
+
header: "Reason",
|
|
30
|
+
question: "Why is this work being abandoned?",
|
|
31
|
+
options: [
|
|
32
|
+
{ label: "Wrong approach", description: "The approach didn't work out" },
|
|
33
|
+
{ label: "No longer needed", description: "Requirements changed or feature dropped" },
|
|
34
|
+
{ label: "Blocked", description: "Can't proceed due to external dependency" }
|
|
35
|
+
],
|
|
36
|
+
multiSelect: false
|
|
37
|
+
)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Step 3: Update progress file**
|
|
41
|
+
|
|
42
|
+
- Update line 1: append " [ABORTED]"
|
|
43
|
+
- Update line 2: change session ID to empty `<!-- session: -->`
|
|
44
|
+
- Add log entry: `- [ ] ({YYYY-MM-DD HH:MM}) (abort) Abandoned: {reason}`
|
|
45
|
+
|
|
46
|
+
**Step 4: Update spec if exists**
|
|
47
|
+
|
|
48
|
+
Read line 3 for spec path. If spec exists (not "None"):
|
|
49
|
+
- Read the spec file
|
|
50
|
+
- Append to `## Rejected` section: `- {YYYY-MM-DD}: Aborted — {reason}`
|
|
51
|
+
|
|
52
|
+
**Step 5: Confirm**
|
|
53
|
+
|
|
54
|
+
Print:
|
|
55
|
+
```
|
|
56
|
+
Aborted: {filename}
|
|
57
|
+
Reason: {reason}
|
|
58
|
+
Session unclaimed. Progress kept as historical record.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
STOP.
|
|
62
|
+
|
|
63
|
+
</process>
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workerc:commit
|
|
3
|
+
description: Commit tracked files with auto-generated conventional commit message
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
- Bash
|
|
8
|
+
- AskUserQuestion
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Stage and commit files from the current session's progress file.
|
|
13
|
+
Auto-generates conventional commit message from progress context.
|
|
14
|
+
NEVER add Co-Authored-By, AI attribution, or any footer to the commit message.
|
|
15
|
+
</objective>
|
|
16
|
+
|
|
17
|
+
<process>
|
|
18
|
+
|
|
19
|
+
**Step 1: Find session progress file**
|
|
20
|
+
|
|
21
|
+
Read all `.claude/progress/*.md`. Find the one with `<!-- session: CURRENT_SESSION_ID -->` on line 2.
|
|
22
|
+
|
|
23
|
+
If none found: print "No active progress file for this session." and STOP.
|
|
24
|
+
|
|
25
|
+
**Step 2: Read progress and spec**
|
|
26
|
+
|
|
27
|
+
Read the full progress file.
|
|
28
|
+
Read spec if one exists (line 3, not "None").
|
|
29
|
+
|
|
30
|
+
**Step 3: Check git status**
|
|
31
|
+
|
|
32
|
+
Run `git status --short` and `git diff --stat`.
|
|
33
|
+
|
|
34
|
+
Get the list of files from `## Files` section.
|
|
35
|
+
|
|
36
|
+
For each file in ## Files, check if it has uncommitted changes (modified, untracked, or staged).
|
|
37
|
+
Collect only files that actually have changes — skip files already committed or unchanged.
|
|
38
|
+
|
|
39
|
+
If no files have changes:
|
|
40
|
+
- Print: "No uncommitted changes in tracked files."
|
|
41
|
+
- STOP
|
|
42
|
+
|
|
43
|
+
**Step 4: Ask what to stage**
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
AskUserQuestion(
|
|
47
|
+
header: "Stage",
|
|
48
|
+
question: "Which files to commit?",
|
|
49
|
+
options: [
|
|
50
|
+
{ label: "Tracked files", description: "Only files listed in ## Files that have changes" },
|
|
51
|
+
{ label: "Let me pick", description: "I'll tell you which files" }
|
|
52
|
+
],
|
|
53
|
+
multiSelect: false
|
|
54
|
+
)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**If "Tracked files":** stage all changed files from ## Files.
|
|
58
|
+
**If "Let me pick":** wait for user input, stage only those files.
|
|
59
|
+
|
|
60
|
+
Run `git add` for the selected files. Do NOT use `git add -A` or `git add .`.
|
|
61
|
+
|
|
62
|
+
**Step 5: Generate commit message**
|
|
63
|
+
|
|
64
|
+
**Prefix** — infer from log entries and file types:
|
|
65
|
+
- `fix` if log contains: fix, bug, broken, patch, repair, resolve
|
|
66
|
+
- `feat` if log contains: add, create, implement, new, feature
|
|
67
|
+
- `refactor` if log contains: refactor, move, rename, extract, reorganize
|
|
68
|
+
- `test` if log contains: test
|
|
69
|
+
- `docs` if only .md files changed
|
|
70
|
+
- `chore` if only config/tooling files changed
|
|
71
|
+
- `style` if log contains: style, css, theme, layout
|
|
72
|
+
- fallback: `feat`
|
|
73
|
+
|
|
74
|
+
**Scope** — infer from file paths:
|
|
75
|
+
- If spec exists: use spec filename slug (e.g. `auth-refactor` from `auth-refactor.md`)
|
|
76
|
+
- Else if all files share a common parent dir: use that dir name
|
|
77
|
+
- Else: use first meaningful directory from ## Files
|
|
78
|
+
- Keep scope short (1-2 words, no path separators)
|
|
79
|
+
|
|
80
|
+
**Message** — from progress file line 1:
|
|
81
|
+
- Take the work name, lowercase it
|
|
82
|
+
- Remove "Progress for" prefix and date suffix
|
|
83
|
+
- Keep it under 50 chars total (with prefix and scope)
|
|
84
|
+
|
|
85
|
+
**Body** — from last 5 log entries:
|
|
86
|
+
- Extract the description part (after the timestamp and scope)
|
|
87
|
+
- Format as bullet points with `-`
|
|
88
|
+
- NEVER add Co-Authored-By lines
|
|
89
|
+
- NEVER add AI attribution or generated-by footers
|
|
90
|
+
- NEVER add any trailer lines
|
|
91
|
+
- Body is ONLY the bullet points from the log
|
|
92
|
+
|
|
93
|
+
Full format:
|
|
94
|
+
```
|
|
95
|
+
{prefix}({scope}): {message}
|
|
96
|
+
|
|
97
|
+
{body bullet points}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Step 6: Preview and confirm**
|
|
101
|
+
|
|
102
|
+
Print the full draft commit message in a code block.
|
|
103
|
+
Print the list of files that will be committed.
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
AskUserQuestion(
|
|
107
|
+
header: "Commit",
|
|
108
|
+
question: "How does this look?",
|
|
109
|
+
options: [
|
|
110
|
+
{ label: "Commit as-is", description: "Use this exact message" },
|
|
111
|
+
{ label: "Edit", description: "I'll adjust the message" }
|
|
112
|
+
],
|
|
113
|
+
multiSelect: false
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**If "Edit":** wait for user to provide corrected message, use that instead.
|
|
118
|
+
**If "Commit as-is":** proceed with the generated message.
|
|
119
|
+
|
|
120
|
+
**Step 7: Commit**
|
|
121
|
+
|
|
122
|
+
Run `git commit` with the final message. Use HEREDOC format:
|
|
123
|
+
```bash
|
|
124
|
+
git commit -m "$(cat <<'EOF'
|
|
125
|
+
{final message here — NO Co-Authored-By, NO trailers}
|
|
126
|
+
EOF
|
|
127
|
+
)"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
IMPORTANT: The commit message must contain ONLY the prefix/scope/message and body bullets. Nothing else. No signatures, no attribution, no footers.
|
|
131
|
+
|
|
132
|
+
**Step 8: Log the commit**
|
|
133
|
+
|
|
134
|
+
Get the short hash with `git rev-parse --short HEAD`.
|
|
135
|
+
Add to progress log: `- [x] ({YYYY-MM-DD HH:MM}) (commit) {short_hash}: {prefix}({scope}): {message}`
|
|
136
|
+
|
|
137
|
+
Print:
|
|
138
|
+
```
|
|
139
|
+
Committed: {short_hash}
|
|
140
|
+
{prefix}({scope}): {message}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
STOP.
|
|
144
|
+
|
|
145
|
+
</process>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workerc:done
|
|
3
|
+
description: Mark current session progress as complete and unclaim
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Write
|
|
7
|
+
- Edit
|
|
8
|
+
- Glob
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Mark the current session's progress file as complete. Unclaim it so it becomes a historical record.
|
|
13
|
+
</objective>
|
|
14
|
+
|
|
15
|
+
<process>
|
|
16
|
+
|
|
17
|
+
**Step 1: Find session progress file**
|
|
18
|
+
|
|
19
|
+
Read all `.claude/progress/*.md`. Find the one with `<!-- session: CURRENT_SESSION_ID -->` on line 2.
|
|
20
|
+
|
|
21
|
+
If none found: print "No active progress file for this session." and STOP.
|
|
22
|
+
|
|
23
|
+
**Step 2: Pre-done suggestions**
|
|
24
|
+
|
|
25
|
+
Check progress log and git status:
|
|
26
|
+
- If spec exists (line 3, not "None") and no `(review)` log entry: print "Tip: run /workerc:review first to verify spec coverage."
|
|
27
|
+
- If no `(commit)` log entry and tracked files have uncommitted changes: print "Tip: run /workerc:commit first to commit your changes."
|
|
28
|
+
- Continue anyway — these are suggestions, not blocks.
|
|
29
|
+
|
|
30
|
+
**Step 3: Mark complete**
|
|
31
|
+
|
|
32
|
+
- Update line 1: append " [DONE]" to the description
|
|
33
|
+
- Update line 2: change session ID to empty `<!-- session: -->`
|
|
34
|
+
- Add final log entry: `- [x] ({YYYY-MM-DD HH:MM}) (done) Session complete`
|
|
35
|
+
|
|
36
|
+
**Step 4: Confirm**
|
|
37
|
+
|
|
38
|
+
Print:
|
|
39
|
+
```
|
|
40
|
+
Complete: {filename}
|
|
41
|
+
Session unclaimed. This is now a historical record.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
STOP.
|
|
45
|
+
|
|
46
|
+
</process>
|