aircana 1.1.0.rc1 → 1.2.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.
- checksums.yaml +4 -4
- data/.rspec_status +199 -161
- data/CLAUDE.md +18 -4
- data/lib/aircana/cli/app.rb +5 -0
- data/lib/aircana/cli/commands/agents.rb +115 -6
- data/lib/aircana/contexts/confluence.rb +0 -1
- data/lib/aircana/contexts/manifest.rb +20 -0
- data/lib/aircana/contexts/web.rb +341 -0
- data/lib/aircana/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd309e7d9e7bb2eb52d38e1e4fcc87038b37e2712aef7ccf2c66fd07d03c693e
|
4
|
+
data.tar.gz: fe066eb5c1c4dc493b711d437879c5f97c1b00be5df97a9c036b6ad9ea5a1bd9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e44f4f135a4a4aa329fa708e2403e71c656fd4063b2e2e5ee74914e1f06dd2b039cafb28dbb7b64774845f463192c5b010c8131c9180a926152d297fd59f900
|
7
|
+
data.tar.gz: 4dfdf3422c2261ff41c9e4f966b14995bfe7cda36b582b4f65f8372b9a6392dca5fd3c2a230d29ac3c7d4a43f9daabffdec20667457bd75be6239d052379ea90
|
data/.rspec_status
CHANGED
@@ -1,166 +1,204 @@
|
|
1
1
|
example_id | status | run_time |
|
2
2
|
------------------------------------------------------------ | ------ | --------------- |
|
3
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:1:1] | passed | 0.
|
4
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:2:1] | passed | 0.
|
5
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:3:1] | passed | 0.
|
6
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:4:1] | passed | 0.
|
7
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:4:2:1] | passed | 0.
|
8
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:5:1] | passed | 0.
|
9
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:2:1] | passed | 0.
|
10
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:2:2] | passed | 0.
|
11
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:1] | passed | 0.
|
12
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:2] | passed | 0.
|
13
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:3] | passed | 0.
|
14
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:4] | passed | 0.
|
15
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:5] | passed | 0.
|
16
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:6] | passed | 0.
|
17
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:7] | passed | 0.
|
18
|
-
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:8] | passed | 0.
|
19
|
-
./spec/aircana/cli/commands/agents_spec.rb[1:1:1:1] | passed | 0.
|
20
|
-
./spec/aircana/cli/commands/agents_spec.rb[1:1:1:2] | passed | 0.
|
21
|
-
./spec/aircana/cli/commands/agents_spec.rb[1:1:1:3] | passed | 0.
|
22
|
-
./spec/aircana/cli/commands/agents_spec.rb[1:1:2:1] | passed | 0.
|
23
|
-
./spec/aircana/cli/commands/agents_spec.rb[1:1:3:1] | passed | 0.
|
24
|
-
./spec/aircana/cli/commands/agents_spec.rb[1:1:3:2] | passed | 0.
|
25
|
-
./spec/aircana/cli/commands/agents_spec.rb[1:2:1]
|
26
|
-
./spec/aircana/cli/commands/
|
27
|
-
./spec/aircana/cli/commands/
|
28
|
-
./spec/aircana/cli/commands/
|
29
|
-
./spec/aircana/cli/commands/
|
30
|
-
./spec/aircana/cli/commands/
|
31
|
-
./spec/aircana/cli/commands/
|
32
|
-
./spec/aircana/cli/commands/
|
33
|
-
./spec/aircana/cli/commands/
|
34
|
-
./spec/aircana/cli/commands/
|
35
|
-
./spec/aircana/cli/commands/hooks_spec.rb[1:
|
36
|
-
./spec/aircana/cli/commands/hooks_spec.rb[1:
|
37
|
-
./spec/aircana/cli/commands/hooks_spec.rb[1:
|
38
|
-
./spec/aircana/cli/commands/hooks_spec.rb[1:
|
39
|
-
./spec/aircana/cli/commands/hooks_spec.rb[1:
|
40
|
-
./spec/aircana/cli/commands/hooks_spec.rb[1:
|
41
|
-
./spec/aircana/cli/commands/hooks_spec.rb[1:
|
42
|
-
./spec/aircana/cli/commands/
|
43
|
-
./spec/aircana/cli/commands/
|
44
|
-
./spec/aircana/cli/commands/
|
45
|
-
./spec/aircana/cli/commands/
|
46
|
-
./spec/aircana/cli/commands/
|
47
|
-
./spec/aircana/cli/commands/
|
48
|
-
./spec/aircana/cli/commands/
|
49
|
-
./spec/aircana/cli/commands/
|
50
|
-
./spec/aircana/cli/commands/
|
51
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
52
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
53
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
54
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
55
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
56
|
-
./spec/aircana/cli/commands/install_spec.rb[1:4:
|
57
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
58
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
59
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
60
|
-
./spec/aircana/cli/commands/install_spec.rb[1:
|
61
|
-
./spec/aircana/cli/commands/
|
62
|
-
./spec/aircana/cli/commands/
|
63
|
-
./spec/aircana/cli/commands/
|
64
|
-
./spec/aircana/cli/commands/
|
65
|
-
./spec/aircana/cli/commands/
|
66
|
-
./spec/aircana/cli/commands/
|
67
|
-
./spec/aircana/
|
68
|
-
./spec/aircana/
|
69
|
-
./spec/aircana/
|
70
|
-
./spec/aircana/
|
71
|
-
./spec/aircana/
|
72
|
-
./spec/aircana/
|
73
|
-
./spec/aircana/
|
74
|
-
./spec/aircana/
|
75
|
-
./spec/aircana/
|
76
|
-
./spec/aircana/
|
77
|
-
./spec/aircana/
|
3
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:1:1] | passed | 0.00582 seconds |
|
4
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:2:1] | passed | 0.00084 seconds |
|
5
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:3:1] | passed | 0.00108 seconds |
|
6
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:4:1] | passed | 0.00293 seconds |
|
7
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:4:2:1] | passed | 0.0156 seconds |
|
8
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:1:5:1] | passed | 0.00305 seconds |
|
9
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:2:1] | passed | 0.00206 seconds |
|
10
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:2:2] | passed | 0.00277 seconds |
|
11
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:1] | passed | 0.00082 seconds |
|
12
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:2] | passed | 0.0007 seconds |
|
13
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:3] | passed | 0.00107 seconds |
|
14
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:4] | passed | 0.00076 seconds |
|
15
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:5] | passed | 0.00076 seconds |
|
16
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:6] | passed | 0.00068 seconds |
|
17
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:7] | passed | 0.00081 seconds |
|
18
|
+
./spec/aircana/cli/commands/add_directory_spec.rb[1:3:8] | passed | 0.0009 seconds |
|
19
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:1:1:1] | passed | 0.00553 seconds |
|
20
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:1:1:2] | passed | 0.00034 seconds |
|
21
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:1:1:3] | passed | 0.00048 seconds |
|
22
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:1:2:1] | passed | 0.00116 seconds |
|
23
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:1:3:1] | passed | 0.0004 seconds |
|
24
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:1:3:2] | passed | 0.00036 seconds |
|
25
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:2:1:1] | passed | 0.0012 seconds |
|
26
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:2:1:2] | passed | 0.0022 seconds |
|
27
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:2:1:3:1] | passed | 0.00191 seconds |
|
28
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:2:2:1] | passed | 0.00255 seconds |
|
29
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:2:3:1] | passed | 0.00113 seconds |
|
30
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:2:4:1] | passed | 0.00039 seconds |
|
31
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:3:1] | passed | 0.00096 seconds |
|
32
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:3:2] | passed | 0.00042 seconds |
|
33
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:3:3] | passed | 0.00046 seconds |
|
34
|
+
./spec/aircana/cli/commands/agents_spec.rb[1:4:1] | passed | 0.0002 seconds |
|
35
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:1:1:1] | passed | 0.00083 seconds |
|
36
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:1:2:1:1] | passed | 0.00074 seconds |
|
37
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:1:2:2:1] | passed | 0.00155 seconds |
|
38
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:2:1:1] | passed | 0.00088 seconds |
|
39
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:2:2:1] | passed | 0.00088 seconds |
|
40
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:3:1:1] | passed | 0.00072 seconds |
|
41
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:3:2:1] | passed | 0.00137 seconds |
|
42
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:4:1] | passed | 0.00122 seconds |
|
43
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:4:2:1] | passed | 0.00112 seconds |
|
44
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:5:1:1] | passed | 0.00059 seconds |
|
45
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:5:2:1] | passed | 0.00072 seconds |
|
46
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:5:3:1] | passed | 0.00078 seconds |
|
47
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:5:4:1] | passed | 0.00073 seconds |
|
48
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:6:1:1] | passed | 0.00056 seconds |
|
49
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:6:2:1] | passed | 0.00092 seconds |
|
50
|
+
./spec/aircana/cli/commands/hooks_spec.rb[1:7:1] | passed | 0.00091 seconds |
|
51
|
+
./spec/aircana/cli/commands/install_spec.rb[1:1:1:1] | passed | 0.00107 seconds |
|
52
|
+
./spec/aircana/cli/commands/install_spec.rb[1:1:2:1] | passed | 0.00167 seconds |
|
53
|
+
./spec/aircana/cli/commands/install_spec.rb[1:2:1:1] | passed | 0.0033 seconds |
|
54
|
+
./spec/aircana/cli/commands/install_spec.rb[1:2:2:1] | passed | 0.00267 seconds |
|
55
|
+
./spec/aircana/cli/commands/install_spec.rb[1:2:3:1] | passed | 0.00505 seconds |
|
56
|
+
./spec/aircana/cli/commands/install_spec.rb[1:2:4:1] | passed | 0.00216 seconds |
|
57
|
+
./spec/aircana/cli/commands/install_spec.rb[1:3:1] | passed | 0.00201 seconds |
|
58
|
+
./spec/aircana/cli/commands/install_spec.rb[1:3:2:1] | passed | 0.00198 seconds |
|
59
|
+
./spec/aircana/cli/commands/install_spec.rb[1:3:3:1] | passed | 0.00269 seconds |
|
60
|
+
./spec/aircana/cli/commands/install_spec.rb[1:3:4:1] | passed | 0.00248 seconds |
|
61
|
+
./spec/aircana/cli/commands/install_spec.rb[1:4:1] | passed | 0.00099 seconds |
|
62
|
+
./spec/aircana/cli/commands/install_spec.rb[1:4:2] | passed | 0.00128 seconds |
|
63
|
+
./spec/aircana/cli/commands/install_spec.rb[1:4:3] | passed | 0.00112 seconds |
|
64
|
+
./spec/aircana/cli/commands/install_spec.rb[1:4:4] | passed | 0.00082 seconds |
|
65
|
+
./spec/aircana/cli/commands/install_spec.rb[1:4:5] | passed | 0.00083 seconds |
|
66
|
+
./spec/aircana/cli/commands/install_spec.rb[1:5:1:1] | passed | 0.00121 seconds |
|
67
|
+
./spec/aircana/cli/commands/install_spec.rb[1:5:2:1] | passed | 0.00081 seconds |
|
68
|
+
./spec/aircana/cli/commands/install_spec.rb[1:5:3:1] | passed | 0.00111 seconds |
|
69
|
+
./spec/aircana/cli/commands/install_spec.rb[1:6:1] | passed | 0.00106 seconds |
|
70
|
+
./spec/aircana/cli/commands/plan_spec.rb[1:1:1:1] | passed | 0.01347 seconds |
|
71
|
+
./spec/aircana/cli/commands/plan_spec.rb[1:1:2:1] | passed | 0.00625 seconds |
|
72
|
+
./spec/aircana/cli/commands/plan_spec.rb[1:1:3:1] | passed | 0.00059 seconds |
|
73
|
+
./spec/aircana/cli/commands/work_spec.rb[1:1:1:1] | passed | 0.0054 seconds |
|
74
|
+
./spec/aircana/cli/commands/work_spec.rb[1:1:2:1] | passed | 0.0049 seconds |
|
75
|
+
./spec/aircana/cli/commands/work_spec.rb[1:1:3:1] | passed | 0.0009 seconds |
|
76
|
+
./spec/aircana/configuration_spec.rb[1:1:1] | passed | 0.00027 seconds |
|
77
|
+
./spec/aircana/configuration_spec.rb[1:2:1] | passed | 0.00009 seconds |
|
78
|
+
./spec/aircana/configuration_spec.rb[1:3:1] | passed | 0.00008 seconds |
|
79
|
+
./spec/aircana/configuration_spec.rb[1:4:1] | passed | 0.00008 seconds |
|
80
|
+
./spec/aircana/configuration_spec.rb[1:5:1] | passed | 0.00016 seconds |
|
81
|
+
./spec/aircana/configuration_spec.rb[1:5:2] | passed | 0.00007 seconds |
|
82
|
+
./spec/aircana/contexts/confluence_spec.rb[1:1:1:1] | passed | 0.00224 seconds |
|
83
|
+
./spec/aircana/contexts/confluence_spec.rb[1:1:1:2] | passed | 0.00055 seconds |
|
84
|
+
./spec/aircana/contexts/confluence_spec.rb[1:1:1:3] | passed | 0.0011 seconds |
|
85
|
+
./spec/aircana/contexts/confluence_spec.rb[1:1:2:1] | passed | 0.00018 seconds |
|
86
|
+
./spec/aircana/contexts/confluence_spec.rb[1:1:2:2] | passed | 0.00014 seconds |
|
78
87
|
./spec/aircana/contexts/confluence_spec.rb[1:1:2:3] | passed | 0.00012 seconds |
|
79
|
-
./spec/aircana/contexts/confluence_spec.rb[1:1:3:1] | passed | 0.
|
80
|
-
./spec/aircana/contexts/confluence_spec.rb[1:1:3:2] | passed | 0.
|
81
|
-
./spec/aircana/contexts/local_spec.rb[1:1:1] | passed | 0.
|
82
|
-
./spec/aircana/contexts/local_spec.rb[1:1:2] | passed | 0.
|
83
|
-
./spec/aircana/contexts/local_spec.rb[1:1:3] | passed | 0.
|
84
|
-
./spec/aircana/contexts/local_spec.rb[1:1:4] | passed | 0.
|
85
|
-
./spec/aircana/contexts/local_spec.rb[1:1:5] | passed | 0.
|
86
|
-
./spec/aircana/contexts/local_spec.rb[1:1:6] | passed | 0.
|
87
|
-
./spec/aircana/contexts/local_spec.rb[1:1:7] | passed | 0.
|
88
|
-
./spec/aircana/contexts/local_spec.rb[1:1:8] | passed | 0.
|
89
|
-
./spec/aircana/contexts/manifest_spec.rb[1:1:1] | passed | 0.
|
90
|
-
./spec/aircana/contexts/manifest_spec.rb[1:1:2] | passed | 0.
|
91
|
-
./spec/aircana/contexts/manifest_spec.rb[1:2:1] | passed | 1.
|
92
|
-
./spec/aircana/contexts/manifest_spec.rb[1:2:2] | passed | 0.
|
93
|
-
./spec/aircana/contexts/manifest_spec.rb[1:3:1] | passed | 0.
|
94
|
-
./spec/aircana/contexts/manifest_spec.rb[1:3:2] | passed | 0.
|
95
|
-
./spec/aircana/contexts/manifest_spec.rb[1:3:3] | passed | 0.
|
96
|
-
./spec/aircana/contexts/manifest_spec.rb[1:3:4] | passed | 0.
|
97
|
-
./spec/aircana/contexts/manifest_spec.rb[1:4:1] | passed | 0.
|
98
|
-
./spec/aircana/contexts/manifest_spec.rb[1:4:2] | passed | 0.
|
99
|
-
./spec/aircana/contexts/manifest_spec.rb[1:5:1] | passed | 0.
|
100
|
-
./spec/aircana/contexts/manifest_spec.rb[1:5:2] | passed | 0.
|
88
|
+
./spec/aircana/contexts/confluence_spec.rb[1:1:3:1] | passed | 0.00041 seconds |
|
89
|
+
./spec/aircana/contexts/confluence_spec.rb[1:1:3:2] | passed | 0.00043 seconds |
|
90
|
+
./spec/aircana/contexts/local_spec.rb[1:1:1] | passed | 0.00148 seconds |
|
91
|
+
./spec/aircana/contexts/local_spec.rb[1:1:2] | passed | 0.00123 seconds |
|
92
|
+
./spec/aircana/contexts/local_spec.rb[1:1:3] | passed | 0.00144 seconds |
|
93
|
+
./spec/aircana/contexts/local_spec.rb[1:1:4] | passed | 0.00112 seconds |
|
94
|
+
./spec/aircana/contexts/local_spec.rb[1:1:5] | passed | 0.00135 seconds |
|
95
|
+
./spec/aircana/contexts/local_spec.rb[1:1:6] | passed | 0.00118 seconds |
|
96
|
+
./spec/aircana/contexts/local_spec.rb[1:1:7] | passed | 0.00203 seconds |
|
97
|
+
./spec/aircana/contexts/local_spec.rb[1:1:8] | passed | 0.00123 seconds |
|
98
|
+
./spec/aircana/contexts/manifest_spec.rb[1:1:1] | passed | 0.00088 seconds |
|
99
|
+
./spec/aircana/contexts/manifest_spec.rb[1:1:2] | passed | 0.00086 seconds |
|
100
|
+
./spec/aircana/contexts/manifest_spec.rb[1:2:1] | passed | 1.01 seconds |
|
101
|
+
./spec/aircana/contexts/manifest_spec.rb[1:2:2] | passed | 0.00168 seconds |
|
102
|
+
./spec/aircana/contexts/manifest_spec.rb[1:3:1] | passed | 0.00153 seconds |
|
103
|
+
./spec/aircana/contexts/manifest_spec.rb[1:3:2] | passed | 0.00052 seconds |
|
104
|
+
./spec/aircana/contexts/manifest_spec.rb[1:3:3] | passed | 0.00152 seconds |
|
105
|
+
./spec/aircana/contexts/manifest_spec.rb[1:3:4] | passed | 0.00156 seconds |
|
106
|
+
./spec/aircana/contexts/manifest_spec.rb[1:4:1] | passed | 0.00142 seconds |
|
107
|
+
./spec/aircana/contexts/manifest_spec.rb[1:4:2] | passed | 0.00058 seconds |
|
108
|
+
./spec/aircana/contexts/manifest_spec.rb[1:5:1] | passed | 0.00214 seconds |
|
109
|
+
./spec/aircana/contexts/manifest_spec.rb[1:5:2] | passed | 0.00039 seconds |
|
101
110
|
./spec/aircana/contexts/manifest_spec.rb[1:6:1] | passed | 0.00046 seconds |
|
102
|
-
./spec/aircana/contexts/manifest_spec.rb[1:6:2] | passed | 0.
|
103
|
-
./spec/aircana/contexts/manifest_spec.rb[1:6:3] | passed | 0.
|
104
|
-
./spec/aircana/
|
105
|
-
./spec/aircana/
|
106
|
-
./spec/aircana/
|
107
|
-
./spec/aircana/
|
108
|
-
./spec/aircana/
|
109
|
-
./spec/aircana/
|
110
|
-
./spec/aircana/
|
111
|
-
./spec/aircana/
|
112
|
-
./spec/aircana/
|
113
|
-
./spec/aircana/
|
114
|
-
./spec/aircana/
|
115
|
-
./spec/aircana/
|
116
|
-
./spec/aircana/
|
117
|
-
./spec/aircana/
|
118
|
-
./spec/aircana/
|
119
|
-
./spec/aircana/
|
120
|
-
./spec/aircana/
|
121
|
-
./spec/aircana/
|
122
|
-
./spec/aircana/
|
111
|
+
./spec/aircana/contexts/manifest_spec.rb[1:6:2] | passed | 0.00042 seconds |
|
112
|
+
./spec/aircana/contexts/manifest_spec.rb[1:6:3] | passed | 0.00042 seconds |
|
113
|
+
./spec/aircana/contexts/manifest_spec.rb[1:6:4] | passed | 0.00038 seconds |
|
114
|
+
./spec/aircana/contexts/manifest_spec.rb[1:6:5] | passed | 0.00038 seconds |
|
115
|
+
./spec/aircana/contexts/manifest_spec.rb[1:6:6] | passed | 0.00036 seconds |
|
116
|
+
./spec/aircana/contexts/manifest_spec.rb[1:6:7] | passed | 0.00036 seconds |
|
117
|
+
./spec/aircana/contexts/manifest_spec.rb[1:6:8] | passed | 0.00036 seconds |
|
118
|
+
./spec/aircana/contexts/manifest_spec.rb[1:6:9] | passed | 0.00151 seconds |
|
119
|
+
./spec/aircana/contexts/web_spec.rb[1:1:1] | passed | 0.00033 seconds |
|
120
|
+
./spec/aircana/contexts/web_spec.rb[1:2:1] | passed | 0.00032 seconds |
|
121
|
+
./spec/aircana/contexts/web_spec.rb[1:3:1:1] | passed | 0.00022 seconds |
|
122
|
+
./spec/aircana/contexts/web_spec.rb[1:3:1:2] | passed | 0.00019 seconds |
|
123
|
+
./spec/aircana/contexts/web_spec.rb[1:3:1:3] | passed | 0.00018 seconds |
|
124
|
+
./spec/aircana/contexts/web_spec.rb[1:3:2:1] | passed | 0.00041 seconds |
|
125
|
+
./spec/aircana/contexts/web_spec.rb[1:3:2:2] | passed | 0.00033 seconds |
|
126
|
+
./spec/aircana/contexts/web_spec.rb[1:3:3:1] | passed | 0.00029 seconds |
|
127
|
+
./spec/aircana/contexts/web_spec.rb[1:3:3:2] | passed | 0.0004 seconds |
|
128
|
+
./spec/aircana/contexts/web_spec.rb[1:3:3:3] | passed | 0.00036 seconds |
|
129
|
+
./spec/aircana/contexts/web_spec.rb[1:3:3:4] | passed | 0.00022 seconds |
|
130
|
+
./spec/aircana/contexts/web_spec.rb[1:3:3:5] | passed | 0.00394 seconds |
|
131
|
+
./spec/aircana/contexts/web_spec.rb[1:3:4:1] | passed | 0.00022 seconds |
|
132
|
+
./spec/aircana/contexts/web_spec.rb[1:3:4:2] | passed | 0.00017 seconds |
|
133
|
+
./spec/aircana/contexts/web_spec.rb[1:3:4:3] | passed | 0.00017 seconds |
|
134
|
+
./spec/aircana/contexts/web_spec.rb[1:3:5:1] | passed | 0.0006 seconds |
|
135
|
+
./spec/aircana/contexts/web_spec.rb[1:3:5:2] | passed | 0.00017 seconds |
|
136
|
+
./spec/aircana/contexts/web_spec.rb[1:3:5:3] | passed | 0.00092 seconds |
|
137
|
+
./spec/aircana/contexts/web_spec.rb[1:3:6:1] | passed | 0.00036 seconds |
|
138
|
+
./spec/aircana/contexts/web_spec.rb[1:3:6:2] | passed | 0.00035 seconds |
|
139
|
+
./spec/aircana/contexts/web_spec.rb[1:3:6:3] | passed | 0.00033 seconds |
|
140
|
+
./spec/aircana/contexts/web_spec.rb[1:3:7:1] | passed | 0.00016 seconds |
|
141
|
+
./spec/aircana/contexts/web_spec.rb[1:3:7:2] | passed | 0.00042 seconds |
|
142
|
+
./spec/aircana/fzf_helper_spec.rb[1:1:1:1] | passed | 0.00024 seconds |
|
143
|
+
./spec/aircana/fzf_helper_spec.rb[1:1:1:2] | passed | 0.00021 seconds |
|
144
|
+
./spec/aircana/fzf_helper_spec.rb[1:1:1:3] | passed | 0.00026 seconds |
|
145
|
+
./spec/aircana/fzf_helper_spec.rb[1:1:2:1] | passed | 0.00042 seconds |
|
146
|
+
./spec/aircana/fzf_helper_spec.rb[1:1:3:1] | passed | 0.00054 seconds |
|
147
|
+
./spec/aircana/fzf_helper_spec.rb[1:2:1:1] | passed | 0.00017 seconds |
|
148
|
+
./spec/aircana/fzf_helper_spec.rb[1:2:1:2] | passed | 0.00014 seconds |
|
149
|
+
./spec/aircana/fzf_helper_spec.rb[1:2:1:3] | passed | 0.00014 seconds |
|
150
|
+
./spec/aircana/fzf_helper_spec.rb[1:2:2:1] | passed | 0.00014 seconds |
|
151
|
+
./spec/aircana/fzf_helper_spec.rb[1:2:2:2] | passed | 0.00011 seconds |
|
152
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:1:1] | passed | 0.00005 seconds |
|
153
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:2:1] | passed | 0.00041 seconds |
|
154
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:2:2] | passed | 0.00042 seconds |
|
155
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:3:1:1] | passed | 0.00009 seconds |
|
156
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:3:1:2] | passed | 0.00005 seconds |
|
157
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:3:2:1] | passed | 0.00005 seconds |
|
158
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:3:2:2] | passed | 0.00005 seconds |
|
159
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:4:1:1] | passed | 0.00006 seconds |
|
160
|
+
./spec/aircana/generators/agents_generator_spec.rb[1:4:2:1] | passed | 0.00016 seconds |
|
123
161
|
./spec/aircana/generators/hooks_generator_spec.rb[1:1:1] | passed | 0.00006 seconds |
|
124
|
-
./spec/aircana/generators/hooks_generator_spec.rb[1:2:1] | passed | 0.
|
125
|
-
./spec/aircana/generators/hooks_generator_spec.rb[1:3:1] | passed | 0.
|
126
|
-
./spec/aircana/generators/hooks_generator_spec.rb[1:3:2] | passed | 0.
|
127
|
-
./spec/aircana/generators/hooks_generator_spec.rb[1:4:1] | passed | 0.
|
128
|
-
./spec/aircana/generators/hooks_generator_spec.rb[1:5:1:1] | passed | 0.
|
129
|
-
./spec/aircana/generators/hooks_generator_spec.rb[1:5:2:1] | passed | 0.
|
130
|
-
./spec/aircana/generators/hooks_generator_spec.rb[1:6:1] | passed | 0.
|
131
|
-
./spec/aircana/generators/hooks_generator_spec.rb[1:7:1] | passed | 0.
|
132
|
-
./spec/aircana/human_logger_spec.rb[1:1:1] | passed | 0.
|
133
|
-
./spec/aircana/human_logger_spec.rb[1:2:1] | passed | 0.
|
134
|
-
./spec/aircana/human_logger_spec.rb[1:3:1] | passed | 0.
|
135
|
-
./spec/aircana/human_logger_spec.rb[1:4:1] | passed | 0.
|
136
|
-
./spec/aircana/human_logger_spec.rb[1:5:1] | passed | 0.
|
137
|
-
./spec/aircana/human_logger_spec.rb[1:5:2] | passed | 0.
|
138
|
-
./spec/aircana/human_logger_spec.rb[1:5:3] | passed | 0.
|
139
|
-
./spec/aircana/human_logger_spec.rb[1:5:4] | passed | 0.
|
140
|
-
./spec/aircana/human_logger_spec.rb[1:5:5] | passed | 0.
|
141
|
-
./spec/aircana/human_logger_spec.rb[1:5:6] | passed | 0.
|
142
|
-
./spec/aircana/human_logger_spec.rb[1:5:7] | passed | 0.
|
143
|
-
./spec/aircana/human_logger_spec.rb[1:6:1] | passed | 0.
|
144
|
-
./spec/aircana/llm/claude_client_spec.rb[1:1:1:1] | passed | 0.
|
145
|
-
./spec/aircana/llm/claude_client_spec.rb[1:1:1:2] | passed | 0.
|
146
|
-
./spec/aircana/llm/claude_client_spec.rb[1:1:1:3] | passed | 0.
|
147
|
-
./spec/aircana/llm/claude_client_spec.rb[1:1:1:4] | passed | 0.
|
148
|
-
./spec/aircana/llm/claude_client_spec.rb[1:1:2:1] | passed | 0.
|
149
|
-
./spec/aircana/llm/claude_client_spec.rb[1:1:2:2] | passed | 0.
|
150
|
-
./spec/aircana/llm/claude_client_spec.rb[1:1:3:1] | passed | 0.
|
151
|
-
./spec/aircana/llm/claude_client_spec.rb[1:2:1] | passed | 0.
|
152
|
-
./spec/aircana/llm/claude_client_spec.rb[1:2:2] | passed | 0.
|
153
|
-
./spec/aircana/llm/claude_client_spec.rb[1:3:1:1] | passed | 0.
|
154
|
-
./spec/aircana/llm/claude_client_spec.rb[1:3:1:2] | passed | 0.
|
155
|
-
./spec/aircana/system_checker_spec.rb[1:1:1:1] | passed | 0.
|
156
|
-
./spec/aircana/system_checker_spec.rb[1:1:2:1] | passed | 0.
|
157
|
-
./spec/aircana/system_checker_spec.rb[1:1:3:1] | passed | 0.
|
158
|
-
./spec/aircana/system_checker_spec.rb[1:2:1] | passed | 0.
|
159
|
-
./spec/aircana/system_checker_spec.rb[1:2:2] | passed | 0.
|
160
|
-
./spec/aircana/system_checker_spec.rb[1:3:1] | passed | 0.
|
161
|
-
./spec/aircana/system_checker_spec.rb[1:3:2] | passed | 0.
|
162
|
-
./spec/aircana/system_checker_spec.rb[1:3:3] | passed | 0.
|
163
|
-
./spec/aircana/system_checker_spec.rb[1:3:4] | passed | 0.
|
164
|
-
./spec/aircana/system_checker_spec.rb[1:3:5] | passed | 0.
|
165
|
-
./spec/aircana_spec.rb[1:1] | passed | 0.
|
166
|
-
./spec/aircana_spec.rb[1:2] | passed | 0.
|
162
|
+
./spec/aircana/generators/hooks_generator_spec.rb[1:2:1] | passed | 0.00004 seconds |
|
163
|
+
./spec/aircana/generators/hooks_generator_spec.rb[1:3:1] | passed | 0.00065 seconds |
|
164
|
+
./spec/aircana/generators/hooks_generator_spec.rb[1:3:2] | passed | 0.00027 seconds |
|
165
|
+
./spec/aircana/generators/hooks_generator_spec.rb[1:4:1] | passed | 0.00012 seconds |
|
166
|
+
./spec/aircana/generators/hooks_generator_spec.rb[1:5:1:1] | passed | 0.00013 seconds |
|
167
|
+
./spec/aircana/generators/hooks_generator_spec.rb[1:5:2:1] | passed | 0.00005 seconds |
|
168
|
+
./spec/aircana/generators/hooks_generator_spec.rb[1:6:1] | passed | 0.00074 seconds |
|
169
|
+
./spec/aircana/generators/hooks_generator_spec.rb[1:7:1] | passed | 0.00022 seconds |
|
170
|
+
./spec/aircana/human_logger_spec.rb[1:1:1] | passed | 0.00008 seconds |
|
171
|
+
./spec/aircana/human_logger_spec.rb[1:2:1] | passed | 0.00017 seconds |
|
172
|
+
./spec/aircana/human_logger_spec.rb[1:3:1] | passed | 0.00005 seconds |
|
173
|
+
./spec/aircana/human_logger_spec.rb[1:4:1] | passed | 0.00005 seconds |
|
174
|
+
./spec/aircana/human_logger_spec.rb[1:5:1] | passed | 0.00004 seconds |
|
175
|
+
./spec/aircana/human_logger_spec.rb[1:5:2] | passed | 0.00004 seconds |
|
176
|
+
./spec/aircana/human_logger_spec.rb[1:5:3] | passed | 0.00004 seconds |
|
177
|
+
./spec/aircana/human_logger_spec.rb[1:5:4] | passed | 0.00004 seconds |
|
178
|
+
./spec/aircana/human_logger_spec.rb[1:5:5] | passed | 0.00004 seconds |
|
179
|
+
./spec/aircana/human_logger_spec.rb[1:5:6] | passed | 0.00004 seconds |
|
180
|
+
./spec/aircana/human_logger_spec.rb[1:5:7] | passed | 0.00004 seconds |
|
181
|
+
./spec/aircana/human_logger_spec.rb[1:6:1] | passed | 0.00004 seconds |
|
182
|
+
./spec/aircana/llm/claude_client_spec.rb[1:1:1:1] | passed | 0.00064 seconds |
|
183
|
+
./spec/aircana/llm/claude_client_spec.rb[1:1:1:2] | passed | 0.00068 seconds |
|
184
|
+
./spec/aircana/llm/claude_client_spec.rb[1:1:1:3] | passed | 0.00048 seconds |
|
185
|
+
./spec/aircana/llm/claude_client_spec.rb[1:1:1:4] | passed | 0.00044 seconds |
|
186
|
+
./spec/aircana/llm/claude_client_spec.rb[1:1:2:1] | passed | 0.00066 seconds |
|
187
|
+
./spec/aircana/llm/claude_client_spec.rb[1:1:2:2] | passed | 0.0004 seconds |
|
188
|
+
./spec/aircana/llm/claude_client_spec.rb[1:1:3:1] | passed | 0.00041 seconds |
|
189
|
+
./spec/aircana/llm/claude_client_spec.rb[1:2:1] | passed | 0.00037 seconds |
|
190
|
+
./spec/aircana/llm/claude_client_spec.rb[1:2:2] | passed | 0.00037 seconds |
|
191
|
+
./spec/aircana/llm/claude_client_spec.rb[1:3:1:1] | passed | 0.00026 seconds |
|
192
|
+
./spec/aircana/llm/claude_client_spec.rb[1:3:1:2] | passed | 0.00024 seconds |
|
193
|
+
./spec/aircana/system_checker_spec.rb[1:1:1:1] | passed | 0.00034 seconds |
|
194
|
+
./spec/aircana/system_checker_spec.rb[1:1:2:1] | passed | 0.0005 seconds |
|
195
|
+
./spec/aircana/system_checker_spec.rb[1:1:3:1] | passed | 0.00062 seconds |
|
196
|
+
./spec/aircana/system_checker_spec.rb[1:2:1] | passed | 0.00025 seconds |
|
197
|
+
./spec/aircana/system_checker_spec.rb[1:2:2] | passed | 0.00025 seconds |
|
198
|
+
./spec/aircana/system_checker_spec.rb[1:3:1] | passed | 0.00025 seconds |
|
199
|
+
./spec/aircana/system_checker_spec.rb[1:3:2] | passed | 0.00025 seconds |
|
200
|
+
./spec/aircana/system_checker_spec.rb[1:3:3] | passed | 0.0003 seconds |
|
201
|
+
./spec/aircana/system_checker_spec.rb[1:3:4] | passed | 0.00041 seconds |
|
202
|
+
./spec/aircana/system_checker_spec.rb[1:3:5] | passed | 0.0002 seconds |
|
203
|
+
./spec/aircana_spec.rb[1:1] | passed | 0.00004 seconds |
|
204
|
+
./spec/aircana_spec.rb[1:2] | passed | 0.0009 seconds |
|
data/CLAUDE.md
CHANGED
@@ -36,7 +36,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
36
36
|
- `aircana plan` - Launch Claude Code with planner agent (experimental)
|
37
37
|
- `aircana work` - Launch Claude Code with worker agent (experimental)
|
38
38
|
- `aircana agents create` - Create a new agent interactively
|
39
|
-
- `aircana agents refresh <agent>` - Refresh agent knowledge from Confluence
|
39
|
+
- `aircana agents refresh <agent>` - Refresh agent knowledge from Confluence and web sources
|
40
|
+
- `aircana agents add-url <agent> <url>` - Add a web URL to an agent's knowledge base
|
40
41
|
- `aircana hooks list` - List all available hooks
|
41
42
|
- `aircana hooks enable <hook>` - Enable a specific hook
|
42
43
|
- `aircana hooks disable <hook>` - Disable a specific hook
|
@@ -57,6 +58,7 @@ Aircana is a Ruby gem that provides context management and workflow utilities fo
|
|
57
58
|
- `relevant_files.rb`: Manages current working file set
|
58
59
|
- `confluence.rb`: Handles Confluence page fetching and caching
|
59
60
|
- `confluence_content.rb`: Processes Confluence content
|
61
|
+
- `web.rb`: Handles web URL fetching and HTML to Markdown conversion
|
60
62
|
- `local.rb`: Local file context management
|
61
63
|
- **Generators** (`lib/aircana/generators/`): Template generation system
|
62
64
|
- `base_generator.rb`: Base class for all generators
|
@@ -95,9 +97,21 @@ Aircana is a Ruby gem that provides context management and workflow utilities fo
|
|
95
97
|
- Rubocop configured with custom rules in `.rubocop.yml`
|
96
98
|
- String style enforced as double quotes
|
97
99
|
|
98
|
-
###
|
99
|
-
|
100
|
+
### Knowledge Sources
|
101
|
+
Agents can sync knowledge from multiple sources:
|
102
|
+
|
103
|
+
**Confluence Integration:**
|
100
104
|
- Set environment variables: `CONFLUENCE_BASE_URL`, `CONFLUENCE_USERNAME`, `CONFLUENCE_API_TOKEN`
|
101
105
|
- Pages are fetched based on agent name as label
|
102
106
|
- Content cached locally for offline access
|
103
|
-
|
107
|
+
|
108
|
+
**Web URL Integration:**
|
109
|
+
- Add URLs directly to agent knowledge bases
|
110
|
+
- HTML content is automatically converted to Markdown
|
111
|
+
- Interactive URL collection during agent creation
|
112
|
+
- Add individual URLs: `aircana agents add-url <agent> <url>`
|
113
|
+
|
114
|
+
**Unified Management:**
|
115
|
+
- Both sources tracked in manifest.json for each agent
|
116
|
+
- Refresh all sources with `aircana agents refresh <agent>`
|
117
|
+
- Content stored as Markdown files in `.aircana/agents/<agent>/knowledge/`
|
data/lib/aircana/cli/app.rb
CHANGED
@@ -4,6 +4,7 @@ require "json"
|
|
4
4
|
require "tty-prompt"
|
5
5
|
require_relative "../../generators/agents_generator"
|
6
6
|
require_relative "../../contexts/manifest"
|
7
|
+
require_relative "../../contexts/web"
|
7
8
|
|
8
9
|
module Aircana
|
9
10
|
module CLI
|
@@ -43,6 +44,9 @@ module Aircana
|
|
43
44
|
# Prompt for knowledge fetching
|
44
45
|
prompt_for_knowledge_fetch(prompt, normalized_agent_name)
|
45
46
|
|
47
|
+
# Prompt for web URL fetching
|
48
|
+
prompt_for_url_fetch(prompt, normalized_agent_name)
|
49
|
+
|
46
50
|
# Prompt for agent file review
|
47
51
|
prompt_for_agent_review(prompt, file)
|
48
52
|
|
@@ -59,6 +63,44 @@ module Aircana
|
|
59
63
|
print_agents_list(agent_folders)
|
60
64
|
end
|
61
65
|
|
66
|
+
def add_url(agent, url) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
67
|
+
normalized_agent = normalize_string(agent)
|
68
|
+
|
69
|
+
unless agent_exists?(normalized_agent)
|
70
|
+
Aircana.human_logger.error "Agent '#{agent}' not found. Use 'aircana agents list' to see available agents."
|
71
|
+
exit 1
|
72
|
+
end
|
73
|
+
|
74
|
+
web = Aircana::Contexts::Web.new
|
75
|
+
result = web.fetch_url_for(agent: normalized_agent, url: url)
|
76
|
+
|
77
|
+
if result
|
78
|
+
# Update manifest with the new URL
|
79
|
+
existing_sources = Aircana::Contexts::Manifest.sources_from_manifest(normalized_agent)
|
80
|
+
web_sources = existing_sources.select { |s| s["type"] == "web" }
|
81
|
+
other_sources = existing_sources.reject { |s| s["type"] == "web" }
|
82
|
+
|
83
|
+
if web_sources.any?
|
84
|
+
# Add to existing web source
|
85
|
+
web_sources.first["urls"] << result
|
86
|
+
else
|
87
|
+
# Create new web source
|
88
|
+
web_sources = [{ "type" => "web", "urls" => [result] }]
|
89
|
+
end
|
90
|
+
|
91
|
+
all_sources = other_sources + web_sources
|
92
|
+
Aircana::Contexts::Manifest.update_manifest(normalized_agent, all_sources)
|
93
|
+
|
94
|
+
Aircana.human_logger.success "Successfully added URL to agent '#{agent}'"
|
95
|
+
else
|
96
|
+
Aircana.human_logger.error "Failed to fetch URL: #{url}"
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
rescue Aircana::Error => e
|
100
|
+
Aircana.human_logger.error "Failed to add URL: #{e.message}"
|
101
|
+
exit 1
|
102
|
+
end
|
103
|
+
|
62
104
|
private
|
63
105
|
|
64
106
|
def perform_refresh(normalized_agent)
|
@@ -77,20 +119,38 @@ module Aircana
|
|
77
119
|
end
|
78
120
|
end
|
79
121
|
|
80
|
-
def perform_manifest_aware_refresh(normalized_agent)
|
81
|
-
|
122
|
+
def perform_manifest_aware_refresh(normalized_agent) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
123
|
+
total_pages = 0
|
124
|
+
all_sources = []
|
82
125
|
|
83
126
|
# Try manifest-based refresh first
|
84
127
|
if Aircana::Contexts::Manifest.manifest_exists?(normalized_agent)
|
85
128
|
Aircana.human_logger.info "Refreshing from knowledge manifest..."
|
86
|
-
|
129
|
+
|
130
|
+
# Refresh Confluence sources
|
131
|
+
confluence = Aircana::Contexts::Confluence.new
|
132
|
+
confluence_result = confluence.refresh_from_manifest(agent: normalized_agent)
|
133
|
+
total_pages += confluence_result[:pages_count]
|
134
|
+
all_sources.concat(confluence_result[:sources])
|
135
|
+
|
136
|
+
# Refresh web sources
|
137
|
+
web = Aircana::Contexts::Web.new
|
138
|
+
web_result = web.refresh_web_sources(agent: normalized_agent)
|
139
|
+
total_pages += web_result[:pages_count]
|
140
|
+
all_sources.concat(web_result[:sources])
|
87
141
|
else
|
88
142
|
Aircana.human_logger.info "No manifest found, falling back to label-based search..."
|
89
|
-
|
143
|
+
confluence = Aircana::Contexts::Confluence.new
|
144
|
+
confluence_result = confluence.fetch_pages_for(agent: normalized_agent)
|
145
|
+
total_pages += confluence_result[:pages_count]
|
146
|
+
all_sources.concat(confluence_result[:sources])
|
90
147
|
end
|
91
148
|
|
92
|
-
|
93
|
-
|
149
|
+
# Update manifest with all sources combined
|
150
|
+
Aircana::Contexts::Manifest.update_manifest(normalized_agent, all_sources) if all_sources.any?
|
151
|
+
|
152
|
+
log_refresh_result(normalized_agent, total_pages)
|
153
|
+
{ pages_count: total_pages, sources: all_sources }
|
94
154
|
end
|
95
155
|
|
96
156
|
def show_gitignore_recommendation
|
@@ -153,6 +213,43 @@ module Aircana
|
|
153
213
|
Aircana.human_logger.info "You can try again later with 'aircana agents refresh #{normalized_agent_name}'"
|
154
214
|
end
|
155
215
|
|
216
|
+
def prompt_for_url_fetch(prompt, normalized_agent_name) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
217
|
+
return unless prompt.yes?("Would you like to add web URLs for this agent's knowledge base?")
|
218
|
+
|
219
|
+
urls = []
|
220
|
+
loop do
|
221
|
+
url = prompt.ask("Enter URL (or press Enter to finish):")
|
222
|
+
break if url.nil? || url.strip.empty?
|
223
|
+
|
224
|
+
url = url.strip
|
225
|
+
if valid_url?(url)
|
226
|
+
urls << url
|
227
|
+
else
|
228
|
+
Aircana.human_logger.warn "Invalid URL format: #{url}. Please enter a valid HTTP or HTTPS URL."
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
return if urls.empty?
|
233
|
+
|
234
|
+
begin
|
235
|
+
Aircana.human_logger.info "Fetching #{urls.size} URL(s)..."
|
236
|
+
web = Aircana::Contexts::Web.new
|
237
|
+
result = web.fetch_urls_for(agent: normalized_agent_name, urls: urls)
|
238
|
+
|
239
|
+
if result[:pages_count].positive?
|
240
|
+
Aircana.human_logger.success "Successfully fetched #{result[:pages_count]} URL(s)"
|
241
|
+
show_gitignore_recommendation
|
242
|
+
else
|
243
|
+
Aircana.human_logger.warn "No URLs were successfully fetched"
|
244
|
+
end
|
245
|
+
rescue Aircana::Error => e
|
246
|
+
Aircana.human_logger.warn "Failed to fetch URLs: #{e.message}"
|
247
|
+
Aircana.human_logger.info(
|
248
|
+
"You can add URLs later with 'aircana agents add-url #{normalized_agent_name} <URL>'"
|
249
|
+
)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
156
253
|
def prompt_for_agent_review(prompt, file_path)
|
157
254
|
Aircana.human_logger.info "Agent file created at: #{file_path}"
|
158
255
|
|
@@ -214,6 +311,18 @@ module Aircana
|
|
214
311
|
config["description"] || "No description available"
|
215
312
|
end
|
216
313
|
|
314
|
+
def agent_exists?(agent_name)
|
315
|
+
agent_dir = File.join(Aircana.configuration.agent_knowledge_dir, agent_name)
|
316
|
+
Dir.exist?(agent_dir)
|
317
|
+
end
|
318
|
+
|
319
|
+
def valid_url?(url)
|
320
|
+
uri = URI.parse(url)
|
321
|
+
%w[http https].include?(uri.scheme) && !uri.host.nil?
|
322
|
+
rescue URI::InvalidURIError
|
323
|
+
false
|
324
|
+
end
|
325
|
+
|
217
326
|
def find_available_editor
|
218
327
|
%w[code subl atom nano vim vi].find { |cmd| system("which #{cmd} > /dev/null 2>&1") }
|
219
328
|
end
|
@@ -56,7 +56,6 @@ module Aircana
|
|
56
56
|
return { pages_count: 0, sources: [] } if all_pages.empty?
|
57
57
|
|
58
58
|
updated_sources = process_pages_with_manifest(all_pages, agent)
|
59
|
-
Manifest.update_manifest(agent, updated_sources)
|
60
59
|
|
61
60
|
{ pages_count: all_pages.size, sources: updated_sources }
|
62
61
|
end
|
@@ -128,6 +128,8 @@ module Aircana
|
|
128
128
|
case source["type"]
|
129
129
|
when "confluence"
|
130
130
|
validate_confluence_source(source)
|
131
|
+
when "web"
|
132
|
+
validate_web_source(source)
|
131
133
|
else
|
132
134
|
raise ManifestError, "Unknown source type: #{source["type"]}"
|
133
135
|
end
|
@@ -140,6 +142,24 @@ module Aircana
|
|
140
142
|
|
141
143
|
raise ManifestError, "Confluence pages must be an array"
|
142
144
|
end
|
145
|
+
|
146
|
+
def validate_web_source(source)
|
147
|
+
raise ManifestError, "Web source missing required field: urls" unless source.key?("urls")
|
148
|
+
|
149
|
+
raise ManifestError, "Web urls must be an array" unless source["urls"].is_a?(Array)
|
150
|
+
|
151
|
+
source["urls"].each do |url_entry|
|
152
|
+
validate_web_url_entry(url_entry)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def validate_web_url_entry(url_entry)
|
157
|
+
raise ManifestError, "Each URL entry must be a hash" unless url_entry.is_a?(Hash)
|
158
|
+
|
159
|
+
raise ManifestError, "URL entry missing required field: url" unless url_entry.key?("url")
|
160
|
+
|
161
|
+
raise ManifestError, "URL entry missing required field: title" unless url_entry.key?("title")
|
162
|
+
end
|
143
163
|
end
|
144
164
|
end
|
145
165
|
|
@@ -0,0 +1,341 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "httparty"
|
4
|
+
require "reverse_markdown"
|
5
|
+
require "uri"
|
6
|
+
require_relative "local"
|
7
|
+
require_relative "manifest"
|
8
|
+
require_relative "../progress_tracker"
|
9
|
+
require_relative "../version"
|
10
|
+
require_relative "../llm/claude_client"
|
11
|
+
|
12
|
+
module Aircana
|
13
|
+
module Contexts
|
14
|
+
class Web # rubocop:disable Metrics/ClassLength
|
15
|
+
include HTTParty
|
16
|
+
|
17
|
+
headers "User-Agent" => "Aircana/#{Aircana::VERSION} (+https://github.com/westonkd/aircana)"
|
18
|
+
default_timeout 30
|
19
|
+
follow_redirects true
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@local_storage = Local.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_url_for(agent:, url:)
|
26
|
+
validate_url!(url)
|
27
|
+
|
28
|
+
page_data = fetch_and_process_url(url)
|
29
|
+
store_page_as_markdown(page_data, agent)
|
30
|
+
|
31
|
+
build_url_metadata(page_data)
|
32
|
+
rescue StandardError => e
|
33
|
+
handle_fetch_error(url, e)
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch_urls_for(agent:, urls:) # rubocop:disable Metrics/MethodLength
|
38
|
+
return { pages_count: 0, sources: [] } if urls.empty?
|
39
|
+
|
40
|
+
pages_metadata = []
|
41
|
+
successful_urls = []
|
42
|
+
|
43
|
+
ProgressTracker.with_batch_progress(urls, "Fetching URLs") do |url, _index|
|
44
|
+
metadata = fetch_url_for(agent: agent, url: url)
|
45
|
+
if metadata
|
46
|
+
pages_metadata << metadata
|
47
|
+
successful_urls << url
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if successful_urls.any?
|
52
|
+
sources = build_sources_metadata(successful_urls, pages_metadata)
|
53
|
+
update_or_create_manifest(agent, sources)
|
54
|
+
{ pages_count: successful_urls.size, sources: sources }
|
55
|
+
else
|
56
|
+
{ pages_count: 0, sources: [] }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def refresh_web_sources(agent:) # rubocop:disable Metrics/CyclomaticComplexity
|
61
|
+
sources = Manifest.sources_from_manifest(agent)
|
62
|
+
web_sources = sources.select { |s| s["type"] == "web" }
|
63
|
+
|
64
|
+
return { pages_count: 0, sources: [] } if web_sources.empty?
|
65
|
+
|
66
|
+
all_urls = web_sources.flat_map { |source| source["urls"]&.map { |u| u["url"] } || [] }
|
67
|
+
return { pages_count: 0, sources: [] } if all_urls.empty?
|
68
|
+
|
69
|
+
fetch_urls_for(agent: agent, urls: all_urls)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def validate_url!(url)
|
75
|
+
uri = URI.parse(url)
|
76
|
+
raise Error, "URL must use HTTP or HTTPS protocol" unless %w[http https].include?(uri.scheme)
|
77
|
+
raise Error, "Invalid URL format" unless uri.host
|
78
|
+
rescue URI::InvalidURIError
|
79
|
+
raise Error, "Invalid URL format"
|
80
|
+
end
|
81
|
+
|
82
|
+
def fetch_and_process_url(url) # rubocop:disable Metrics/MethodLength
|
83
|
+
Aircana.human_logger.info("Fetching #{url}")
|
84
|
+
|
85
|
+
response = self.class.get(url)
|
86
|
+
|
87
|
+
raise Error, "Failed to fetch URL (#{response.code})" unless response.success?
|
88
|
+
|
89
|
+
html_title = extract_title(response.body)
|
90
|
+
content = convert_to_markdown(response.body)
|
91
|
+
title = generate_meaningful_title(html_title, content, url)
|
92
|
+
|
93
|
+
{
|
94
|
+
url: url,
|
95
|
+
title: title,
|
96
|
+
content: content,
|
97
|
+
last_fetched: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def extract_title(html) # rubocop:disable Metrics/MethodLength
|
102
|
+
title_match = html.match(%r{<title[^>]*>(.*?)</title>}im)
|
103
|
+
return nil unless title_match
|
104
|
+
|
105
|
+
title = title_match[1].strip
|
106
|
+
# Decode HTML entities
|
107
|
+
title.gsub(/&([a-zA-Z]+|#\d+);/) do |match|
|
108
|
+
case match
|
109
|
+
when "&" then "&"
|
110
|
+
when "<" then "<"
|
111
|
+
when ">" then ">"
|
112
|
+
when """ then '"'
|
113
|
+
when "'", "'" then "'"
|
114
|
+
else match
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def extract_title_from_url(url)
|
120
|
+
uri = URI.parse(url)
|
121
|
+
# Use the last path segment or host as fallback title
|
122
|
+
path_segments = uri.path.split("/").reject(&:empty?)
|
123
|
+
if path_segments.any?
|
124
|
+
path_segments.last.gsub(/[-_]/, " ").split.map(&:capitalize).join(" ")
|
125
|
+
else
|
126
|
+
uri.host
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def generate_meaningful_title(html_title, content, url) # rubocop:disable Metrics/CyclomaticComplexity
|
131
|
+
# If we have a good HTML title that's descriptive, use it
|
132
|
+
return html_title if html_title && html_title.length > 10 && !generic_title?(html_title)
|
133
|
+
|
134
|
+
# If content is too short, use fallback
|
135
|
+
return html_title || extract_title_from_url(url) if content.length < 50
|
136
|
+
|
137
|
+
# Use Claude to generate a meaningful title based on content
|
138
|
+
begin
|
139
|
+
generate_title_with_claude(content, url)
|
140
|
+
rescue StandardError => e
|
141
|
+
Aircana.human_logger.warn("Failed to generate title with Claude: #{e.message}")
|
142
|
+
html_title || extract_title_from_url(url)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def generic_title?(title)
|
147
|
+
generic_patterns = [
|
148
|
+
/^(home|index|welcome|untitled|document)$/i,
|
149
|
+
/^(page|default)$/i,
|
150
|
+
/^\s*$/,
|
151
|
+
# Truncated titles (contain ellipsis)
|
152
|
+
/\.\.\./,
|
153
|
+
# Titles with excessive metadata (site names, IDs, etc.)
|
154
|
+
/ - .+ - \d+$/,
|
155
|
+
# Question titles that are truncated
|
156
|
+
/^how do i .+\.\.\./i,
|
157
|
+
/^what is .+\.\.\./i
|
158
|
+
]
|
159
|
+
|
160
|
+
generic_patterns.any? { |pattern| title.match?(pattern) }
|
161
|
+
end
|
162
|
+
|
163
|
+
def generate_title_with_claude(content, url)
|
164
|
+
prompt = build_title_generation_prompt(content, url)
|
165
|
+
claude_client = LLM::ClaudeClient.new
|
166
|
+
claude_client.prompt(prompt).strip
|
167
|
+
end
|
168
|
+
|
169
|
+
def build_title_generation_prompt(content, url) # rubocop:disable Metrics/MethodLength
|
170
|
+
# Truncate content to avoid overly long prompts
|
171
|
+
truncated_content = content.length > 1000 ? "#{content[0..1000]}..." : content
|
172
|
+
|
173
|
+
<<~PROMPT
|
174
|
+
Based on the following web page content from #{url}, generate a concise, descriptive title
|
175
|
+
that would help an AI agent understand what this document contains and when it would be useful.
|
176
|
+
|
177
|
+
The title should be:
|
178
|
+
- 3-8 words long
|
179
|
+
- Focused on the main topic or purpose
|
180
|
+
- Helpful for knowledge retrieval
|
181
|
+
- Professional and clear
|
182
|
+
|
183
|
+
Content:
|
184
|
+
#{truncated_content}
|
185
|
+
|
186
|
+
Respond with only the title, no additional text or explanation.
|
187
|
+
PROMPT
|
188
|
+
end
|
189
|
+
|
190
|
+
def convert_to_markdown(html)
|
191
|
+
return "" if html.nil? || html.empty?
|
192
|
+
|
193
|
+
# Extract meaningful content by removing unwanted elements
|
194
|
+
cleaned_html = extract_main_content(html)
|
195
|
+
|
196
|
+
ReverseMarkdown.convert(cleaned_html, github_flavored: true)
|
197
|
+
rescue StandardError => e
|
198
|
+
Aircana.human_logger.warn "Failed to convert HTML to markdown: #{e.message}"
|
199
|
+
# Fallback to plain text extraction
|
200
|
+
extract_text_content(html)
|
201
|
+
end
|
202
|
+
|
203
|
+
def store_page_as_markdown(page_data, agent)
|
204
|
+
@local_storage.store_content(
|
205
|
+
title: page_data[:title],
|
206
|
+
content: page_data[:content],
|
207
|
+
agent: agent
|
208
|
+
)
|
209
|
+
end
|
210
|
+
|
211
|
+
def build_url_metadata(page_data)
|
212
|
+
{
|
213
|
+
"url" => page_data[:url],
|
214
|
+
"title" => page_data[:title],
|
215
|
+
"last_fetched" => page_data[:last_fetched]
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
def build_sources_metadata(_urls, pages_metadata)
|
220
|
+
[
|
221
|
+
{
|
222
|
+
"type" => "web",
|
223
|
+
"urls" => pages_metadata
|
224
|
+
}
|
225
|
+
]
|
226
|
+
end
|
227
|
+
|
228
|
+
def update_or_create_manifest(agent, new_sources)
|
229
|
+
existing_sources = Manifest.sources_from_manifest(agent)
|
230
|
+
|
231
|
+
# Remove existing web sources and add new ones
|
232
|
+
other_sources = existing_sources.reject { |s| s["type"] == "web" }
|
233
|
+
all_sources = other_sources + new_sources
|
234
|
+
|
235
|
+
if Manifest.manifest_exists?(agent)
|
236
|
+
Manifest.update_manifest(agent, all_sources)
|
237
|
+
else
|
238
|
+
Manifest.create_manifest(agent, all_sources)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def handle_fetch_error(url, error)
|
243
|
+
case error
|
244
|
+
when URI::InvalidURIError
|
245
|
+
Aircana.human_logger.error "Invalid URL format: #{url}"
|
246
|
+
when HTTParty::Error
|
247
|
+
Aircana.human_logger.error "HTTP error fetching #{url}: #{error.message}"
|
248
|
+
when Error
|
249
|
+
Aircana.human_logger.error "Error fetching #{url}: #{error.message}"
|
250
|
+
else
|
251
|
+
Aircana.human_logger.error "Unexpected error fetching #{url}: #{error.message}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def extract_main_content(html) # rubocop:disable Metrics/MethodLength
|
256
|
+
# Try to find the main content area using common selectors
|
257
|
+
content_patterns = [
|
258
|
+
# Common main content selectors
|
259
|
+
%r{<main[^>]*>(.*?)</main>}mi,
|
260
|
+
%r{<article[^>]*>(.*?)</article>}mi,
|
261
|
+
%r{<div[^>]*class="[^"]*content[^"]*"[^>]*>(.*?)</div>}mi,
|
262
|
+
%r{<div[^>]*id="content"[^>]*>(.*?)</div>}mi,
|
263
|
+
%r{<div[^>]*class="[^"]*post[^"]*"[^>]*>(.*?)</div>}mi,
|
264
|
+
# Documentation specific
|
265
|
+
%r{<div[^>]*class="[^"]*docs[^"]*"[^>]*>(.*?)</div>}mi,
|
266
|
+
%r{<div[^>]*class="[^"]*documentation[^"]*"[^>]*>(.*?)</div>}mi,
|
267
|
+
# Body content as fallback
|
268
|
+
%r{<body[^>]*>(.*?)</body>}mi
|
269
|
+
]
|
270
|
+
|
271
|
+
extracted_content = nil
|
272
|
+
content_patterns.each do |pattern|
|
273
|
+
match = html.match(pattern)
|
274
|
+
if match && match[1].strip.length > 100 # Ensure meaningful content
|
275
|
+
extracted_content = match[1]
|
276
|
+
break
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# If no pattern matched or content is too short, use the full HTML
|
281
|
+
content_to_clean = extracted_content || html
|
282
|
+
|
283
|
+
# Remove unwanted elements
|
284
|
+
clean_html_content(content_to_clean)
|
285
|
+
end
|
286
|
+
|
287
|
+
def clean_html_content(html) # rubocop:disable Metrics/MethodLength
|
288
|
+
cleaned = html.dup
|
289
|
+
|
290
|
+
# Remove script and style tags completely
|
291
|
+
cleaned = cleaned.gsub(%r{<script[^>]*>.*?</script>}mi, "")
|
292
|
+
cleaned = cleaned.gsub(%r{<style[^>]*>.*?</style>}mi, "")
|
293
|
+
|
294
|
+
# Remove navigation, header, footer, sidebar elements
|
295
|
+
navigation_selectors = %w[nav header footer aside sidebar menu breadcrumb]
|
296
|
+
navigation_selectors.each do |selector|
|
297
|
+
# Remove by tag name
|
298
|
+
cleaned = cleaned.gsub(%r{<#{selector}[^>]*>.*?</#{selector}>}mi, "")
|
299
|
+
# Remove by class name (common patterns)
|
300
|
+
cleaned = cleaned.gsub(%r{<[^>]+class="[^"]*#{selector}[^"]*"[^>]*>.*?</[^>]+>}mi, "")
|
301
|
+
cleaned = cleaned.gsub(%r{<[^>]+id="#{selector}"[^>]*>.*?</[^>]+>}mi, "")
|
302
|
+
end
|
303
|
+
|
304
|
+
# Remove common non-content elements
|
305
|
+
unwanted_patterns = [
|
306
|
+
%r{<div[^>]*class="[^"]*comment[^"]*"[^>]*>.*?</div>}mi,
|
307
|
+
%r{<div[^>]*class="[^"]*social[^"]*"[^>]*>.*?</div>}mi,
|
308
|
+
%r{<div[^>]*class="[^"]*share[^"]*"[^>]*>.*?</div>}mi,
|
309
|
+
%r{<div[^>]*class="[^"]*ad[^"]*"[^>]*>.*?</div>}mi,
|
310
|
+
%r{<div[^>]*class="[^"]*advertisement[^"]*"[^>]*>.*?</div>}mi,
|
311
|
+
%r{<div[^>]*class="[^"]*popup[^"]*"[^>]*>.*?</div>}mi,
|
312
|
+
%r{<div[^>]*class="[^"]*modal[^"]*"[^>]*>.*?</div>}mi
|
313
|
+
]
|
314
|
+
|
315
|
+
unwanted_patterns.each do |pattern|
|
316
|
+
cleaned = cleaned.gsub(pattern, "")
|
317
|
+
end
|
318
|
+
|
319
|
+
# Clean up whitespace
|
320
|
+
cleaned.gsub(/\n\s*\n\s*\n+/, "\n\n").strip
|
321
|
+
end
|
322
|
+
|
323
|
+
def extract_text_content(html) # rubocop:disable Metrics/MethodLength
|
324
|
+
# Fallback method for plain text extraction
|
325
|
+
text = html.gsub(%r{<script[^>]*>.*?</script>}mi, "")
|
326
|
+
.gsub(%r{<style[^>]*>.*?</style>}mi, "")
|
327
|
+
.gsub(/<[^>]+>/, "")
|
328
|
+
.gsub(" ", " ")
|
329
|
+
.gsub("&", "&")
|
330
|
+
.gsub("<", "<")
|
331
|
+
.gsub(">", ">")
|
332
|
+
.gsub(""", '"')
|
333
|
+
.gsub(/\s+/, " ")
|
334
|
+
.strip
|
335
|
+
|
336
|
+
# If the extracted text is very short, it might not be useful
|
337
|
+
text.length < 20 ? "Content could not be extracted from this page." : text
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
data/lib/aircana/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aircana
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Weston Dransfield
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 2025-09-26 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: httparty
|
@@ -145,6 +145,7 @@ files:
|
|
145
145
|
- lib/aircana/contexts/local.rb
|
146
146
|
- lib/aircana/contexts/manifest.rb
|
147
147
|
- lib/aircana/contexts/relevant_files.rb
|
148
|
+
- lib/aircana/contexts/web.rb
|
148
149
|
- lib/aircana/fzf_helper.rb
|
149
150
|
- lib/aircana/generators.rb
|
150
151
|
- lib/aircana/generators/agents_generator.rb
|
@@ -197,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
197
198
|
- !ruby/object:Gem::Version
|
198
199
|
version: '0'
|
199
200
|
requirements: []
|
200
|
-
rubygems_version: 3.6.
|
201
|
+
rubygems_version: 3.6.2
|
201
202
|
specification_version: 4
|
202
203
|
summary: Humble workflow and context utilities for engineering with agents
|
203
204
|
test_files: []
|