safeword 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/dist/check-J6DFVBCE.js +129 -0
- package/dist/check-J6DFVBCE.js.map +1 -0
- package/dist/chunk-24OB57NJ.js +78 -0
- package/dist/chunk-24OB57NJ.js.map +1 -0
- package/dist/chunk-DB4CMUFD.js +157 -0
- package/dist/chunk-DB4CMUFD.js.map +1 -0
- package/dist/chunk-UQMQ64CB.js +107 -0
- package/dist/chunk-UQMQ64CB.js.map +1 -0
- package/dist/chunk-W66Z3C5H.js +21 -0
- package/dist/chunk-W66Z3C5H.js.map +1 -0
- package/dist/chunk-WWQ4YRZN.js +7 -0
- package/dist/chunk-WWQ4YRZN.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +34 -0
- package/dist/cli.js.map +1 -0
- package/dist/diff-U4IELWRL.js +163 -0
- package/dist/diff-U4IELWRL.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/reset-XETOHTCK.js +126 -0
- package/dist/reset-XETOHTCK.js.map +1 -0
- package/dist/setup-CLDCHROZ.js +237 -0
- package/dist/setup-CLDCHROZ.js.map +1 -0
- package/dist/upgrade-DOKWRK7J.js +146 -0
- package/dist/upgrade-DOKWRK7J.js.map +1 -0
- package/package.json +50 -0
- package/templates/doc-templates/ticket-template.md +82 -0
- package/templates/hooks/inject-timestamp.sh +7 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareVersions
|
|
3
|
+
} from "./chunk-W66Z3C5H.js";
|
|
4
|
+
import {
|
|
5
|
+
AGENTS_MD_LINK,
|
|
6
|
+
HOOK_AGENTS_CHECK,
|
|
7
|
+
HOOK_POST_TOOL,
|
|
8
|
+
HOOK_PRE_COMMIT,
|
|
9
|
+
SAFEWORD_MD,
|
|
10
|
+
SETTINGS_HOOKS,
|
|
11
|
+
SKILL_QUALITY_REVIEWER
|
|
12
|
+
} from "./chunk-DB4CMUFD.js";
|
|
13
|
+
import {
|
|
14
|
+
VERSION
|
|
15
|
+
} from "./chunk-WWQ4YRZN.js";
|
|
16
|
+
import {
|
|
17
|
+
installGitHook,
|
|
18
|
+
isGitRepo
|
|
19
|
+
} from "./chunk-24OB57NJ.js";
|
|
20
|
+
import {
|
|
21
|
+
ensureDir,
|
|
22
|
+
error,
|
|
23
|
+
exists,
|
|
24
|
+
header,
|
|
25
|
+
info,
|
|
26
|
+
listItem,
|
|
27
|
+
makeExecutable,
|
|
28
|
+
readFile,
|
|
29
|
+
readFileSafe,
|
|
30
|
+
success,
|
|
31
|
+
updateJson,
|
|
32
|
+
writeFile
|
|
33
|
+
} from "./chunk-UQMQ64CB.js";
|
|
34
|
+
|
|
35
|
+
// src/commands/upgrade.ts
|
|
36
|
+
import { join } from "path";
|
|
37
|
+
async function upgrade() {
|
|
38
|
+
const cwd = process.cwd();
|
|
39
|
+
const safewordDir = join(cwd, ".safeword");
|
|
40
|
+
if (!exists(safewordDir)) {
|
|
41
|
+
error("Not configured. Run `safeword setup` first.");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const versionPath = join(safewordDir, "version");
|
|
45
|
+
const projectVersion = readFileSafe(versionPath)?.trim() ?? "0.0.0";
|
|
46
|
+
if (compareVersions(VERSION, projectVersion) < 0) {
|
|
47
|
+
error(`CLI v${VERSION} is older than project v${projectVersion}.`);
|
|
48
|
+
error("Update the CLI first: npm install -g safeword");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
header("Safeword Upgrade");
|
|
52
|
+
info(`Upgrading from v${projectVersion} to v${VERSION}`);
|
|
53
|
+
const updated = [];
|
|
54
|
+
const unchanged = [];
|
|
55
|
+
try {
|
|
56
|
+
info("\nUpdating .safeword directory...");
|
|
57
|
+
ensureDir(join(safewordDir, "guides"));
|
|
58
|
+
ensureDir(join(safewordDir, "templates"));
|
|
59
|
+
ensureDir(join(safewordDir, "hooks"));
|
|
60
|
+
writeFile(join(safewordDir, "SAFEWORD.md"), SAFEWORD_MD);
|
|
61
|
+
writeFile(join(safewordDir, "version"), VERSION);
|
|
62
|
+
updated.push(".safeword/SAFEWORD.md");
|
|
63
|
+
updated.push(".safeword/version");
|
|
64
|
+
writeFile(join(safewordDir, "hooks", "agents-md-check.sh"), HOOK_AGENTS_CHECK);
|
|
65
|
+
writeFile(join(safewordDir, "hooks", "pre-commit.sh"), HOOK_PRE_COMMIT);
|
|
66
|
+
writeFile(join(safewordDir, "hooks", "post-tool.sh"), HOOK_POST_TOOL);
|
|
67
|
+
makeExecutable(join(safewordDir, "hooks", "agents-md-check.sh"));
|
|
68
|
+
makeExecutable(join(safewordDir, "hooks", "pre-commit.sh"));
|
|
69
|
+
makeExecutable(join(safewordDir, "hooks", "post-tool.sh"));
|
|
70
|
+
updated.push(".safeword/hooks/");
|
|
71
|
+
success("Updated .safeword directory");
|
|
72
|
+
info("\nVerifying AGENTS.md...");
|
|
73
|
+
const agentsMdPath = join(cwd, "AGENTS.md");
|
|
74
|
+
if (exists(agentsMdPath)) {
|
|
75
|
+
const content = readFile(agentsMdPath);
|
|
76
|
+
if (!content.includes("@./.safeword/SAFEWORD.md")) {
|
|
77
|
+
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
78
|
+
|
|
79
|
+
${content}`);
|
|
80
|
+
updated.push("AGENTS.md");
|
|
81
|
+
success("Restored link to AGENTS.md");
|
|
82
|
+
} else {
|
|
83
|
+
unchanged.push("AGENTS.md");
|
|
84
|
+
info("AGENTS.md link is present");
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
88
|
+
`);
|
|
89
|
+
updated.push("AGENTS.md");
|
|
90
|
+
success("Created AGENTS.md");
|
|
91
|
+
}
|
|
92
|
+
info("\nUpdating Claude Code hooks...");
|
|
93
|
+
const claudeDir = join(cwd, ".claude");
|
|
94
|
+
const settingsPath = join(claudeDir, "settings.json");
|
|
95
|
+
ensureDir(claudeDir);
|
|
96
|
+
updateJson(settingsPath, (existing) => {
|
|
97
|
+
const hooks = existing?.hooks ?? {};
|
|
98
|
+
for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {
|
|
99
|
+
const existingHooks = hooks[event] ?? [];
|
|
100
|
+
const nonSafewordHooks = existingHooks.filter(
|
|
101
|
+
(h) => typeof h === "object" && h !== null && "command" in h && typeof h.command === "string" && !h.command.includes(".safeword")
|
|
102
|
+
);
|
|
103
|
+
hooks[event] = [...nonSafewordHooks, ...newHooks];
|
|
104
|
+
}
|
|
105
|
+
return { ...existing, hooks };
|
|
106
|
+
});
|
|
107
|
+
updated.push(".claude/settings.json");
|
|
108
|
+
success("Updated hooks in .claude/settings.json");
|
|
109
|
+
info("\nUpdating skills...");
|
|
110
|
+
const skillsDir = join(claudeDir, "skills", "safeword-quality-reviewer");
|
|
111
|
+
ensureDir(skillsDir);
|
|
112
|
+
writeFile(join(skillsDir, "SKILL.md"), SKILL_QUALITY_REVIEWER);
|
|
113
|
+
updated.push(".claude/skills/safeword-quality-reviewer/");
|
|
114
|
+
success("Updated skills");
|
|
115
|
+
if (isGitRepo(cwd)) {
|
|
116
|
+
info("\nUpdating git hooks...");
|
|
117
|
+
installGitHook(cwd);
|
|
118
|
+
updated.push(".git/hooks/pre-commit");
|
|
119
|
+
success("Updated git pre-commit hook");
|
|
120
|
+
}
|
|
121
|
+
header("Upgrade Complete");
|
|
122
|
+
info(`
|
|
123
|
+
Version: v${projectVersion} \u2192 v${VERSION}`);
|
|
124
|
+
if (updated.length > 0) {
|
|
125
|
+
info("\nUpdated:");
|
|
126
|
+
for (const file of updated) {
|
|
127
|
+
listItem(file);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (unchanged.length > 0) {
|
|
131
|
+
info("\nUnchanged:");
|
|
132
|
+
for (const file of unchanged) {
|
|
133
|
+
listItem(file);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
success(`
|
|
137
|
+
Safeword upgraded to v${VERSION}`);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
error(`Upgrade failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export {
|
|
144
|
+
upgrade
|
|
145
|
+
};
|
|
146
|
+
//# sourceMappingURL=upgrade-DOKWRK7J.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/upgrade.ts"],"sourcesContent":["/**\n * Upgrade command - Update safeword configuration to latest version\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport {\n exists,\n ensureDir,\n writeFile,\n readFile,\n readFileSafe,\n readJson,\n updateJson,\n makeExecutable,\n} from '../utils/fs.js';\nimport { info, success, warn, error, header, listItem } from '../utils/output.js';\nimport { isGitRepo, installGitHook } from '../utils/git.js';\nimport { compareVersions } from '../utils/version.js';\nimport {\n SAFEWORD_MD,\n AGENTS_MD_LINK,\n HOOK_AGENTS_CHECK,\n HOOK_PRE_COMMIT,\n HOOK_POST_TOOL,\n SETTINGS_HOOKS,\n SKILL_QUALITY_REVIEWER,\n} from '../templates/index.js';\n\nexport async function upgrade(): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = join(safewordDir, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? '0.0.0';\n\n // Check for downgrade\n if (compareVersions(VERSION, projectVersion) < 0) {\n error(`CLI v${VERSION} is older than project v${projectVersion}.`);\n error('Update the CLI first: npm install -g safeword');\n process.exit(1);\n }\n\n header('Safeword Upgrade');\n info(`Upgrading from v${projectVersion} to v${VERSION}`);\n\n const updated: string[] = [];\n const unchanged: string[] = [];\n\n try {\n // 1. Update .safeword directory\n info('\\nUpdating .safeword directory...');\n\n ensureDir(join(safewordDir, 'guides'));\n ensureDir(join(safewordDir, 'templates'));\n ensureDir(join(safewordDir, 'hooks'));\n\n // Update core files\n writeFile(join(safewordDir, 'SAFEWORD.md'), SAFEWORD_MD);\n writeFile(join(safewordDir, 'version'), VERSION);\n updated.push('.safeword/SAFEWORD.md');\n updated.push('.safeword/version');\n\n // Update hook scripts\n writeFile(join(safewordDir, 'hooks', 'agents-md-check.sh'), HOOK_AGENTS_CHECK);\n writeFile(join(safewordDir, 'hooks', 'pre-commit.sh'), HOOK_PRE_COMMIT);\n writeFile(join(safewordDir, 'hooks', 'post-tool.sh'), HOOK_POST_TOOL);\n\n makeExecutable(join(safewordDir, 'hooks', 'agents-md-check.sh'));\n makeExecutable(join(safewordDir, 'hooks', 'pre-commit.sh'));\n makeExecutable(join(safewordDir, 'hooks', 'post-tool.sh'));\n\n updated.push('.safeword/hooks/');\n success('Updated .safeword directory');\n\n // 2. Verify AGENTS.md link\n info('\\nVerifying AGENTS.md...');\n const agentsMdPath = join(cwd, 'AGENTS.md');\n\n if (exists(agentsMdPath)) {\n const content = readFile(agentsMdPath);\n if (!content.includes('@./.safeword/SAFEWORD.md')) {\n writeFile(agentsMdPath, `${AGENTS_MD_LINK}\\n\\n${content}`);\n updated.push('AGENTS.md');\n success('Restored link to AGENTS.md');\n } else {\n unchanged.push('AGENTS.md');\n info('AGENTS.md link is present');\n }\n } else {\n writeFile(agentsMdPath, `${AGENTS_MD_LINK}\\n`);\n updated.push('AGENTS.md');\n success('Created AGENTS.md');\n }\n\n // 3. Update Claude Code hooks\n info('\\nUpdating Claude Code hooks...');\n\n const claudeDir = join(cwd, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n ensureDir(claudeDir);\n\n updateJson<{ hooks?: Record<string, unknown[]> }>(settingsPath, existing => {\n const hooks = existing?.hooks ?? {};\n\n // Merge hooks, preserving non-safeword hooks\n for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {\n const existingHooks = (hooks[event] as unknown[]) ?? [];\n\n // Filter out existing safeword hooks\n const nonSafewordHooks = existingHooks.filter(\n (h: unknown) =>\n typeof h === 'object' &&\n h !== null &&\n 'command' in h &&\n typeof (h as { command: string }).command === 'string' &&\n !(h as { command: string }).command.includes('.safeword'),\n );\n\n // Add safeword hooks\n hooks[event] = [...nonSafewordHooks, ...newHooks];\n }\n\n return { ...existing, hooks };\n });\n\n updated.push('.claude/settings.json');\n success('Updated hooks in .claude/settings.json');\n\n // 4. Update skills\n info('\\nUpdating skills...');\n\n const skillsDir = join(claudeDir, 'skills', 'safeword-quality-reviewer');\n ensureDir(skillsDir);\n writeFile(join(skillsDir, 'SKILL.md'), SKILL_QUALITY_REVIEWER);\n\n updated.push('.claude/skills/safeword-quality-reviewer/');\n success('Updated skills');\n\n // 5. Update git hooks if repo exists\n if (isGitRepo(cwd)) {\n info('\\nUpdating git hooks...');\n installGitHook(cwd);\n updated.push('.git/hooks/pre-commit');\n success('Updated git pre-commit hook');\n }\n\n // Print summary\n header('Upgrade Complete');\n\n info(`\\nVersion: v${projectVersion} → v${VERSION}`);\n\n if (updated.length > 0) {\n info('\\nUpdated:');\n for (const file of updated) {\n listItem(file);\n }\n }\n\n if (unchanged.length > 0) {\n info('\\nUnchanged:');\n for (const file of unchanged) {\n listItem(file);\n }\n }\n\n success(`\\nSafeword upgraded to v${VERSION}`);\n } catch (err) {\n error(`Upgrade failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "safeword",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for setting up and managing safeword development environments",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"safeword": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"templates"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"lint": "eslint src tests",
|
|
30
|
+
"clean": "rm -rf dist"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"commander": "^12.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^20.10.0",
|
|
37
|
+
"tsup": "^8.0.0",
|
|
38
|
+
"typescript": "^5.3.0",
|
|
39
|
+
"vitest": "^2.0.0"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"cli",
|
|
43
|
+
"safeword",
|
|
44
|
+
"developer-experience",
|
|
45
|
+
"linting",
|
|
46
|
+
"claude-code"
|
|
47
|
+
],
|
|
48
|
+
"author": "",
|
|
49
|
+
"license": "MIT"
|
|
50
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: XXX
|
|
3
|
+
status: in_progress
|
|
4
|
+
created: YYYY-MM-DDTHH:MM:SSZ
|
|
5
|
+
last_modified: YYYY-MM-DDTHH:MM:SSZ
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Title
|
|
9
|
+
|
|
10
|
+
**Goal:** {One sentence: what are we trying to achieve?}
|
|
11
|
+
|
|
12
|
+
**Why:** {One sentence: why does this matter?}
|
|
13
|
+
|
|
14
|
+
## Work Log
|
|
15
|
+
|
|
16
|
+
**Purpose:** Track what you've tried so you don't repeat dead ends or lose context.
|
|
17
|
+
|
|
18
|
+
**CRITICAL: Re-read this ticket before each significant action to stay on track.**
|
|
19
|
+
|
|
20
|
+
**Log immediately after:**
|
|
21
|
+
|
|
22
|
+
- Starting work
|
|
23
|
+
- Completing a step
|
|
24
|
+
- Trying an approach (document result: success or failure)
|
|
25
|
+
- Discovering a blocker, tradeoff, or decision point
|
|
26
|
+
- Writing a test (RED) or making it pass (GREEN)
|
|
27
|
+
- Committing code
|
|
28
|
+
|
|
29
|
+
**Format:** `YYYY-MM-DDTHH:MM:SSZ Action: Description (refs: commit/file/PR)`
|
|
30
|
+
|
|
31
|
+
**Examples:**
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
- 2025-11-24T18:50:00Z Started: Changing button background to red
|
|
35
|
+
- 2025-11-24T18:51:30Z Tried: Added `background: red` to Button.css
|
|
36
|
+
- 2025-11-24T18:52:00Z Found: Button now has white text on red (unreadable)
|
|
37
|
+
- 2025-11-24T18:53:00Z Tried: Changed text color to white
|
|
38
|
+
- 2025-11-24T18:54:15Z Found: Hover state still blue (conflicts)
|
|
39
|
+
- 2025-11-24T18:55:00Z Complete: Updated all button states to red theme (refs: commit 9a3f2c1)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
{Keep work log in reverse-chronological order. Newest entries at top.}
|
|
45
|
+
{Re-read before each action. Check what you've tried. Stay aligned with Goal.}
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Optional Sections (Add When Needed)
|
|
50
|
+
|
|
51
|
+
### Planning Docs
|
|
52
|
+
|
|
53
|
+
{Only for complex features that need user stories, test definitions, design docs}
|
|
54
|
+
|
|
55
|
+
- .safeword/planning/user-stories/XXX-feature-name.md
|
|
56
|
+
- .safeword/planning/test-definitions/XXX-feature-name.md
|
|
57
|
+
- .safeword/planning/design/XXX-feature-name.md
|
|
58
|
+
|
|
59
|
+
### Scope
|
|
60
|
+
|
|
61
|
+
{Only for complex features with unclear boundaries}
|
|
62
|
+
|
|
63
|
+
**In scope:**
|
|
64
|
+
|
|
65
|
+
-
|
|
66
|
+
|
|
67
|
+
**Out of scope:**
|
|
68
|
+
|
|
69
|
+
-
|
|
70
|
+
|
|
71
|
+
### Acceptance Criteria
|
|
72
|
+
|
|
73
|
+
{Only for features or complex bugs where "done" isn't obvious}
|
|
74
|
+
|
|
75
|
+
- [ ]
|
|
76
|
+
- [ ]
|
|
77
|
+
|
|
78
|
+
### Root Cause
|
|
79
|
+
|
|
80
|
+
{Only for bugs that required investigation}
|
|
81
|
+
|
|
82
|
+
{What caused this issue? Document for future reference.}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Inject Timestamp - Notification Hook
|
|
3
|
+
# Outputs current Unix timestamp at session start for Claude's context awareness
|
|
4
|
+
# Helps with accurate ticket timestamps and time-based reasoning
|
|
5
|
+
|
|
6
|
+
echo "Current time: $(date +%s) ($(date -u +%Y-%m-%dT%H:%M:%SZ))"
|
|
7
|
+
|