@a-company/paradigm 3.1.0 → 3.1.2

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.
@@ -242,7 +242,6 @@ fi
242
242
  # --- Check 4: Aspect anchor files that no longer exist ---
243
243
  for purpose_file in $(find . -name ".purpose" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null); do
244
244
  if grep -q "anchors:" "$purpose_file" 2>/dev/null; then
245
- purpose_dir=$(dirname "$purpose_file")
246
245
  in_anchors=false
247
246
  while IFS= read -r line; do
248
247
  case "$line" in
@@ -251,8 +250,7 @@ for purpose_file in $(find . -name ".purpose" -not -path "*/node_modules/*" -not
251
250
  if [ "$in_anchors" = true ]; then
252
251
  anchor_path=$(echo "$line" | sed 's/.*- //' | sed 's/:.*//' | tr -d ' ')
253
252
  if [ -n "$anchor_path" ]; then
254
- resolved_path="$purpose_dir/$anchor_path"
255
- if [ ! -f "$resolved_path" ]; then
253
+ if [ ! -f "$anchor_path" ]; then
256
254
  VIOLATIONS="$VIOLATIONS
257
255
  - Aspect anchor '$anchor_path' in $purpose_file does not exist.
258
256
  Update the anchor or remove the stale aspect."
@@ -357,6 +355,13 @@ if [ "$SOURCE_COUNT" -ge 3 ] && [ -d ".paradigm/lore" ]; then
357
355
  fi
358
356
  fi
359
357
 
358
+ # --- Auto-evaluate on-stop habits via CLI ---
359
+ if command -v paradigm >/dev/null 2>&1; then
360
+ paradigm habits check --trigger on-stop --record --json 2>/dev/null || true
361
+ elif command -v npx >/dev/null 2>&1; then
362
+ npx paradigm habits check --trigger on-stop --record --json 2>/dev/null || true
363
+ fi
364
+
360
365
  # --- Check 8: Blocking habits ---
361
366
  if [ -f ".paradigm/.habits-blocking" ]; then
362
367
  HABITS_BLOCKING=$(cat ".paradigm/.habits-blocking")
@@ -690,7 +695,6 @@ fi
690
695
  # --- Check 4: Aspect anchor files that no longer exist ---
691
696
  for purpose_file in $(find . -name ".purpose" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null); do
692
697
  if grep -q "anchors:" "$purpose_file" 2>/dev/null; then
693
- purpose_dir=$(dirname "$purpose_file")
694
698
  in_anchors=false
695
699
  while IFS= read -r line; do
696
700
  case "$line" in
@@ -699,8 +703,7 @@ for purpose_file in $(find . -name ".purpose" -not -path "*/node_modules/*" -not
699
703
  if [ "$in_anchors" = true ]; then
700
704
  anchor_path=$(echo "$line" | sed 's/.*- //' | sed 's/:.*//' | tr -d ' ')
701
705
  if [ -n "$anchor_path" ]; then
702
- resolved_path="$purpose_dir/$anchor_path"
703
- if [ ! -f "$resolved_path" ]; then
706
+ if [ ! -f "$anchor_path" ]; then
704
707
  VIOLATIONS="$VIOLATIONS
705
708
  - Aspect anchor '$anchor_path' in $purpose_file does not exist.
706
709
  Update the anchor or remove the stale aspect."
@@ -805,6 +808,13 @@ if [ "$SOURCE_COUNT" -ge 3 ] && [ -d ".paradigm/lore" ]; then
805
808
  fi
806
809
  fi
807
810
 
811
+ # --- Auto-evaluate on-stop habits via CLI ---
812
+ if command -v paradigm >/dev/null 2>&1; then
813
+ paradigm habits check --trigger on-stop --record --json 2>/dev/null || true
814
+ elif command -v npx >/dev/null 2>&1; then
815
+ npx paradigm habits check --trigger on-stop --record --json 2>/dev/null || true
816
+ fi
817
+
808
818
  # --- Check 8: Blocking habits ---
809
819
  if [ -f ".paradigm/.habits-blocking" ]; then
810
820
  HABITS_BLOCKING=$(cat ".paradigm/.habits-blocking")
@@ -180,6 +180,47 @@ async function loadLoreEntry(rootDir, entryId) {
180
180
  const entries = await loadLoreEntries(rootDir);
181
181
  return entries.find((e) => e.id === entryId) || null;
182
182
  }
183
+ async function updateLoreEntry(rootDir, entryId, partial) {
184
+ const entry = await loadLoreEntry(rootDir, entryId);
185
+ if (!entry) return false;
186
+ const dateStr = entry.timestamp.slice(0, 10);
187
+ const entryPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
188
+ if (!fs.existsSync(entryPath)) return false;
189
+ if (partial.title !== void 0) entry.title = partial.title;
190
+ if (partial.summary !== void 0) entry.summary = partial.summary;
191
+ if (partial.type !== void 0) entry.type = partial.type;
192
+ if (partial.duration_minutes !== void 0) entry.duration_minutes = partial.duration_minutes;
193
+ if (partial.symbols_touched !== void 0) entry.symbols_touched = partial.symbols_touched;
194
+ if (partial.symbols_created !== void 0) entry.symbols_created = partial.symbols_created;
195
+ if (partial.files_created !== void 0) entry.files_created = partial.files_created;
196
+ if (partial.files_modified !== void 0) entry.files_modified = partial.files_modified;
197
+ if (partial.lines_added !== void 0) entry.lines_added = partial.lines_added;
198
+ if (partial.lines_removed !== void 0) entry.lines_removed = partial.lines_removed;
199
+ if (partial.commit !== void 0) entry.commit = partial.commit;
200
+ if (partial.decisions !== void 0) entry.decisions = partial.decisions;
201
+ if (partial.errors_encountered !== void 0) entry.errors_encountered = partial.errors_encountered;
202
+ if (partial.learnings !== void 0) entry.learnings = partial.learnings;
203
+ if (partial.verification !== void 0) entry.verification = partial.verification;
204
+ if (partial.tags !== void 0) entry.tags = partial.tags;
205
+ fs.writeFileSync(entryPath, yaml.dump(entry, { lineWidth: -1, noRefs: true }));
206
+ await rebuildTimeline(rootDir);
207
+ return true;
208
+ }
209
+ async function deleteLoreEntry(rootDir, entryId) {
210
+ const entry = await loadLoreEntry(rootDir, entryId);
211
+ if (!entry) return false;
212
+ const dateStr = entry.timestamp.slice(0, 10);
213
+ const entryPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR, dateStr, `${entryId}.yaml`);
214
+ if (!fs.existsSync(entryPath)) return false;
215
+ fs.unlinkSync(entryPath);
216
+ const dateDir = path.dirname(entryPath);
217
+ const remaining = fs.readdirSync(dateDir).filter((f) => f.endsWith(".yaml"));
218
+ if (remaining.length === 0) {
219
+ fs.rmdirSync(dateDir);
220
+ }
221
+ await rebuildTimeline(rootDir);
222
+ return true;
223
+ }
183
224
  function migrateLegacyEntries(rootDir) {
184
225
  const entriesPath = path.join(rootDir, LORE_DIR, ENTRIES_DIR);
185
226
  if (!fs.existsSync(entriesPath)) return 0;
@@ -249,5 +290,7 @@ export {
249
290
  recordLore,
250
291
  loadLoreEntries,
251
292
  addReview,
252
- loadLoreEntry
293
+ loadLoreEntry,
294
+ updateLoreEntry,
295
+ deleteLoreEntry
253
296
  };
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ deleteLoreEntry,
4
+ loadLoreEntry
5
+ } from "./chunk-MVXJVRFI.js";
6
+ import "./chunk-MO4EEYFW.js";
7
+
8
+ // src/commands/lore/delete.ts
9
+ import chalk from "chalk";
10
+ async function loreDeleteCommand(id, options) {
11
+ const rootDir = process.cwd();
12
+ const entry = await loadLoreEntry(rootDir, id);
13
+ if (!entry) {
14
+ console.error(chalk.red(`
15
+ Entry not found: ${id}
16
+ `));
17
+ process.exitCode = 1;
18
+ return;
19
+ }
20
+ if (!options.yes) {
21
+ console.log(chalk.yellow(`
22
+ Will delete lore entry:`));
23
+ console.log(chalk.white(` ${entry.id} - ${entry.title}`));
24
+ console.log(chalk.gray(` Type: ${entry.type} | Author: ${entry.author.id} | ${entry.timestamp}`));
25
+ console.log(chalk.gray(` Symbols: ${entry.symbols_touched.join(", ")}`));
26
+ console.log(chalk.gray(`
27
+ Use --yes to confirm deletion.
28
+ `));
29
+ return;
30
+ }
31
+ const success = await deleteLoreEntry(rootDir, id);
32
+ if (success) {
33
+ console.log(chalk.green(`
34
+ Deleted lore entry: ${id}
35
+ `));
36
+ } else {
37
+ console.error(chalk.red(`
38
+ Failed to delete: ${id}
39
+ `));
40
+ process.exitCode = 1;
41
+ }
42
+ }
43
+ export {
44
+ loreDeleteCommand
45
+ };
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadLoreEntry,
4
+ updateLoreEntry
5
+ } from "./chunk-MVXJVRFI.js";
6
+ import "./chunk-MO4EEYFW.js";
7
+
8
+ // src/commands/lore/edit.ts
9
+ import chalk from "chalk";
10
+ async function loreEditCommand(id, options) {
11
+ const rootDir = process.cwd();
12
+ const entry = await loadLoreEntry(rootDir, id);
13
+ if (!entry) {
14
+ console.error(chalk.red(`
15
+ Entry not found: ${id}
16
+ `));
17
+ process.exitCode = 1;
18
+ return;
19
+ }
20
+ const partial = {};
21
+ if (options.title) partial.title = options.title;
22
+ if (options.summary) partial.summary = options.summary;
23
+ if (options.type) {
24
+ const validTypes = ["agent-session", "human-note", "decision", "review", "incident", "milestone"];
25
+ if (!validTypes.includes(options.type)) {
26
+ console.error(chalk.red(`Invalid type: ${options.type}. Valid: ${validTypes.join(", ")}`));
27
+ process.exitCode = 1;
28
+ return;
29
+ }
30
+ partial.type = options.type;
31
+ }
32
+ if (options.symbols) {
33
+ partial.symbols_touched = options.symbols.split(",").map((s) => s.trim());
34
+ }
35
+ if (options.tags) {
36
+ partial.tags = options.tags.split(",").map((t) => t.trim());
37
+ }
38
+ if (options.learnings) {
39
+ partial.learnings = options.learnings.split(",").map((l) => l.trim());
40
+ }
41
+ if (Object.keys(partial).length === 0) {
42
+ console.log(chalk.yellow("\n No changes specified. Use --title, --summary, --type, --symbols, --tags, or --learnings.\n"));
43
+ return;
44
+ }
45
+ const success = await updateLoreEntry(rootDir, id, partial);
46
+ if (success) {
47
+ console.log(chalk.green(`
48
+ Updated lore entry: ${id}`));
49
+ for (const [key, value] of Object.entries(partial)) {
50
+ const display = Array.isArray(value) ? value.join(", ") : String(value);
51
+ console.log(chalk.gray(` ${key}: ${display}`));
52
+ }
53
+ console.log();
54
+ } else {
55
+ console.error(chalk.red(`
56
+ Failed to update: ${id}
57
+ `));
58
+ process.exitCode = 1;
59
+ }
60
+ }
61
+ export {
62
+ loreEditCommand
63
+ };